fix(agent): Stop turn timeouts looking like cancelled failures - #1331
fix(agent): Stop turn timeouts looking like cancelled failures#1331sentry-junior[bot] wants to merge 7 commits into
Conversation
Timeout resumes were still feeding the model cancelled/aborted tool text, so it treated an internal execution-slice boundary as a user-visible failure. Rewrite the aborted tool payload into an active-task continuation boundary. Co-Authored-By: David Cramer <david@sentry.io>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Stepping back on shape: this still looks like a prompt patch on a runtime boundary, not a durable contract.
The model-facing payload after this change still contains isError: true, aborted: true, exit_code: 130, cause: "turn_deadline", and scope: "execution_slice", plus instructional prose telling the model not to report the deadline. That keeps teaching the model our internal slice mechanics. The observed failure was the model narrating runtime vocabulary; this mostly swaps "cancelled" for a longer explanation that still names the deadline.
Compare the MCP auth pause path, which parks with a short placeholder (Authorization pending.) and does not expose runtime jargon. Timeout recovery is already owned by session state (resumeReason: "timeout"), so recovery does not need the tool result to look like a failed command or to include "don't tell the user" instructions.
I'd rather replace the aborted deadline tool result with a small canonical unknown-outcome projection, for example:
- keep only what the model needs to know the prior attempt is unconfirmed (
target, maybeoutcome: "unconfirmed") - drop deadline/slice jargon from model-visible content
- avoid instructional user-facing policy text in tool payloads
- set
isErroronly if something outside model interpretation actually depends on it; if nothing does, prefer not marking a recoverable park as an error
Also, the new unit test locks the instructional string (still active, without reporting the deadline). Prefer asserting the contract: no cancelled/deadline jargon in model-visible content, and the existing component/lifecycle continuation behavior still finishes the task.
I'm fine keeping the fix local to annotateTurnDeadlineToolResult, but the projection itself should be a clean boundary object, not a rewritten cancelled failure plus coaching text.
Replace cancelled/aborted tool text and runtime deadline jargon with a small model-safe unconfirmed projection. Timeout recovery stays in session state; tests assert the projection contract and continuation behavior. Co-Authored-By: David Cramer <david@sentry.io>
Call the model-facing boundary what it is: project an unconfirmed tool result. Drop the old annotate/turn-deadline naming and simplify the afterToolCall path so timeout recovery ownership is obvious. Co-Authored-By: David Cramer <david@sentry.io>
Access aborted/target through a Record cast after the object guard so prepare/typecheck no longer fails on Property 'aborted' does not exist.
There was a problem hiding this comment.
Final pass looks good to me.
The boundary is small and honest now: aborted deadline tool results become { outcome: "unconfirmed", target? } with isError: false, recovery stays on resumeReason: "timeout", and the old annotate/turn-deadline path is fully gone.
Naming and call site read cleanly, no leftover fallbacks or coaching prose, and the unit/component split matches the contract. I'd ship it.
Co-Authored-By: David Cramer <david@sentry.io>
Record mid-slice tool aborts as a plain timed_out attempt outcome so the model knows the call did not finish, while host continuity stays on session resume and automatic continuation. Co-Authored-By: David Cramer <david@sentry.io>
Drop the invented outcome: "timed_out" dialect. Host-preempted tool attempts now project onto the same timed_out boolean bash already uses, documented on the shared tool output schema. Co-Authored-By: David Cramer <david@sentry.io>
When a long turn hits the serverless time limit mid-tool, Junior already parks and auto-continues on the next function. The bug was the tool result left in history: it looked cancelled (
isError, abort text), so the resumed model quit and narrated the timeout to the user.What changed
Host-preempted tool attempts now use the normal tool-result field bash already has:
{ "target": "pnpm test", "timed_out": true }timed_outis documented on the shared tool output schema. No inventedoutcomefield, no cancelled/deadline jargon,isError: false.What did not change
resumeReason: "timeout")The model sees “this attempt timed out” the same way it already understands bash command timeouts, then keeps working.