Runtime: compose typed hosted external-context tools - #386
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f54e795b5f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return LlmProviderRuntimeService.resolve({ | ||
| ...input, | ||
| credential, | ||
| }); |
There was a problem hiding this comment.
Remove the ignored API key before resolving the OAuth token
When a caller supplies a provider-tagged API key (for example, apiKeyProvider: 'openai') while stored OAuth is available and preferApiKey is false, the initial resolution correctly selects OAuth, but this second resolution passes both the original apiKey and the newly acquired credential. RuntimeCredentialService.assertRuntimeCredential rejects that combination, so a previously valid run now fails with “Provide either apiKey or credential” instead of honoring OAuth precedence.
Useful? React with 👍 / 👎.
| const credential = await RuntimeCredentialService.acquireRequestScopedCredentialForModel( | ||
| input.model, | ||
| { | ||
| storePath: input.credentialStorePath, | ||
| ...(signal ? { signal } : {}), | ||
| }, |
There was a problem hiding this comment.
Preserve interrupted results during credential acquisition
For a stored-OAuth run, aborting before or during request-credential acquisition makes throwIfAborted() reject through this awaited call, so AgentLoopRuntimeService.run() throws without producing its normal outcome: 'interrupted' result or terminal loop event. This differs from cancellation after acquisition, which is normalized by AgentRunService; handle the abort at this preflight boundary so callers retain one cancellation contract.
Useful? React with 👍 / 👎.
| }; | ||
| }); | ||
|
|
||
| return await Promise.all([...pathFiles, ...referenceFiles]); |
There was a problem hiding this comment.
Cancel sibling image streams when one input fails
When a call contains multiple opaque references and one resolver or stream rejects, Promise.all returns immediately but does not cancel the other already-started reads. Since those reads only observe the external tool signal—and the tool timeout is cleared once the failed call returns—a slow or unbounded sibling stream can continue consuming host I/O and memory after tool completion; use an internal abort signal for the batch or read the bounded resources sequentially.
Useful? React with 👍 / 👎.
Outcome
Hosted adopters can now compose an exact run-scoped tool surface from Heddle's
built-in external-context tools without copying schemas, resolving provider
credentials twice, or materializing authorized image content as a temporary
file.
Closes #384.
Public contract
ToolDefinition<Input, Output>/ToolResult<Output>retain typed schema andresult information while tool execution still validates raw
unknowninput.WebSearchInputSchema,WebSearchOutputSchema,ViewImageInputSchema,ViewImageOutputSchema, and their inferred types areexported from
@heddleagent/runtime/advanced.RunAgentLoopOptions.toolkitsconstructs host toolkits after providerresolution and works with
includeDefaultTools: false.Heddle-created runs/request-scoped toolkits;
credentialStorePathcan beindependent from runtime state.
view_imageaccepts generic opaquereference/referencesthrough a hostresourceResolver, with bounded streaming reads, supported-media checks,cancellation, optional byte-count/SHA-256 verification, and no temp file.
Existing local paths remain supported.
Product authorization, object lookup/availability, provenance persistence,
database transactions, and job policy remain adopter-owned.
Verification
yarn typecheckyarn lintcase: 48 files, 425 tests passed
yarn buildThe unfiltered local integration run has one unrelated environment-dependent
failure because
/Users/roackb2/.agents/skills/orchestration/SKILL.mdcurrentlyexceeds Heddle's 1,024-character skill-description limit. No skill discovery
code is changed here; clean-environment CI remains the authoritative full-suite
check.