feat(opencode): per-tool execution timeout with abort + session recovery#36869
feat(opencode): per-tool execution timeout with abort + session recovery#36869FahadBinHussain wants to merge 1 commit into
Conversation
Tools (built-in and MCP) can now hang the agent loop indefinitely when they wedge on a long-running or unresponsive process. This adds a configurable per-tool execution timeout that aborts the call and synthesizes a tool-result so the agent can continue instead of staying stuck on a status=running part forever. Config surface (all optional, in ms, 0 disables): - experimental.tool_timeout (global default, default 600000 / 10 min) - experimental.task_timeout (overrides tool_timeout for the task tool) - agent.tool_timeout (per-agent override, highest precedence) Precedence: agent.tool_timeout > task_timeout (task only) > tool_timeout > DEFAULT_TOOL_TIMEOUT_MS. On timeout the runner fires an AbortController that is composed with the user's existing abort signal via AbortSignal.any, so both Esc interrupts and timeouts surface through the same ctx.abort path. The catch block synthesizes a tool-result with metadata.timeout=true and a message guiding the agent to retry differently. See anomalyco#20096, anomalyco#34888, anomalyco#20216, anomalyco#34022, anomalyco#34960.
|
The following comment was made by an LLM, it may be inaccurate: Potential Duplicate/Related PRs Found:
The most likely duplicates are #36755 and #20103, as they directly address the same feature (configurable tool execution timeouts). You should verify if #36755 is still open and whether this PR supersedes it or builds on top of it. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
|
Nice work on this PR — the I took a close look at the code. A couple of observations:
|
|
I pulled the branch and did a deeper review. The One real improvement I found: the AbortController + setTimeout + composeSignals + clearTimeout pattern is duplicated identically at two call sites (~lines 106-116/159-161 and ~427-439/540-542). I extracted it into Submitted as a stacked PR: FahadBinHussain#2 Changes:
All 15 tool-timeout tests pass, typecheck clean. The existing ~30 lines of config schema + resolve logic are untouched — this is purely a refactor of the execution plumbing. |
Issue for this PR
Related: #20096, #34888, #20216, #34022, #34960
Type of change
What does this PR do?
Tools (built-in and MCP) can hang the agent loop indefinitely when they wedge on a long-running or unresponsive process. This adds a configurable per-tool execution timeout that aborts the call and synthesizes a tool-result so the agent can continue instead of staying stuck on a
status=runningpart forever.Config surface (all optional, in milliseconds,
0disables):experimental.tool_timeout600000(10 min)experimental.task_timeouttasktool onlytool_timeoutagent.tool_timeoutPrecedence:
agent.tool_timeout>experimental.task_timeout(task only) >experimental.tool_timeout>DEFAULT_TOOL_TIMEOUT_MSOn timeout the runner fires an
AbortControllercomposed with the user's existing abort signal viaAbortSignal.any, so both Esc interrupts and timeouts surface through the samectx.abortpath. The catch block synthesizes a tool-result withmetadata.timeout=trueand a message guiding the agent to retry differently. The underlying process may still be alive — the abort is a best-effort cleanup nudge, not a hard kill.Related PRs: #20103 covers the same feature area with the same config keys. That PR is from a different contributor and has been open for a while; this one is an independent implementation with a different approach (centralized
ToolTimeout.resolvemodule,composeSignalshelper, per-agent override surfaced onAgent.Info). Happy to collaborate or defer if maintainers prefer consolidating.How did you verify your code works?
test/session/tool-timeout.test.ts— 10 tests covering: default fallback, global override, per-agent override,0disable, task tool precedence, task_timeout fallback, non-task tools ignoring task_timeout, agent override beating task_timeout, and ToolTimeoutError message shape.bun typecheckinpackages/opencode— clean.bun test test/session/retry.test.ts test/tool/task.test.ts— 49 pass, 0 fail.bun test test/session/llm.test.tsfailures are pre-existing (mock LLM server route mismatches causing 5s timeouts), confirmed by running the same suite on cleandev.Screenshots / recordings
N/A.
Checklist