fix: atomically interrupt+resume wedged managed sessions on human reply #SUPERLOG - #441
fix: atomically interrupt+resume wedged managed sessions on human reply #SUPERLOG#441superlog-app[bot] wants to merge 1 commit into
Conversation
…ly #SUPERLOG Delivery-Id: 955651bfad744b1a631065c1c79bece842f5f4149add0965e95d9c1cbb6ca034 Delivery-Base: main
| }), | ||
| classifyError: (err) => runner.classifyDeliveryError?.(err) ?? "unknown", | ||
| interruptOpenTurn: interrupt ? () => interrupt(sessionId) : null, | ||
| // Preferred over two-step interrupt + resume: atomically interrupt the |
There was a problem hiding this comment.
logs · blocking — Log error when atomic repairAttempt fails before returning failed result
Add an error log with sessionId, errorKind, and the caught error when repairAttempt throws, so operators can distinguish atomic-repair failures from two-step-repair failures without correlating raw traces. Without it, a permanent resume_failed caused by the new code path is silent at the log level.
Useful? React with 👍 / 👎.
| ? () => | ||
| resumeDurableAgentRun({ | ||
| sessionId, | ||
| inputs: resumeInputs, | ||
| runner: { |
There was a problem hiding this comment.
traces · warning — Preserve full runner context in the atomic repairAttempt span
The inline { resume: ..., steer: ... } object passed to resumeDurableAgentRun drops all other runner methods, including any OTel context carriers or span-enrichment hooks the full runner provides; pass the full runner with only resume overridden so the atomic path appears correctly in traces.
| ? () => | |
| resumeDurableAgentRun({ | |
| sessionId, | |
| inputs: resumeInputs, | |
| runner: { | |
| resumeDurableAgentRun({ | |
| sessionId, | |
| inputs: resumeInputs, | |
| runner: { | |
| ...runner, | |
| resume: (sid, msg) => interruptAndResume(sid, msg), | |
| }, | |
| transitionToRunning: () => agentRunLifecycle.resumeRunning(resumeRunningOpts), | |
| markProcessed, | |
| }) |
Useful? React with 👍 / 👎.
Summary
When an agent run in
awaiting_humanstate tries to resume, the Anthropic managed session may have an open tool event waiting for a response (a "wedged" state). The current repair path callsinterruptOpenTurn()(sendsuser.interrupt) and thenattempt()(sendsuser.message) as two separate API calls. Since Anthropic may not have finished processing the interrupt before the retry message arrives, the retry also fails with the same400 "waiting on responses to events"error, causing the run to permanently fail withresume_failed.This is the same race that
recoverManagedAgentRunexplicitly avoids with its comment: "Batch the interrupt and retry so there is no accepted-interrupt / lost-continuation gap between calls."Root cause
In
deliverResumeRepairingWedgedTurn, the two-step repair path:…creates a window where the Anthropic session may still be processing the interrupt when the retry message arrives.
Fix
Add an optional
repairAttemptparameter todeliverResumeRepairingWedgedTurn. When provided, it replaces the two-stepinterruptOpenTurn + attemptwith a single atomic callback — the closed-source backend implements this by batching[user.interrupt, user.message]in oneevents.sendcall.Add
interruptAndResume?(sessionId, message): Promise<void>toAgentRunnerBackendas the optional method that backends provide to enable this atomic path.The existing two-step
interruptOpenTurn + attemptpath is kept as a fallback for runtimes that don't implementinterruptAndResume.Incident: 4a97a9aa-e753-43fb-ba0c-0d1638911c04
Was this PR helpful? Leave feedback — goes straight to the Superlog team.
Summary by cubic
Make human-reply resumes atomic for wedged managed sessions to prevent 400 “waiting on responses to events” and avoid permanent
resume_failedruns. Uses a single batched provider call when available and falls back to the two-step path otherwise.interruptAndResume?(sessionId, message)toAgentRunnerBackendto batchuser.interrupt+user.message.deliverResumeRepairingWedgedTurnwithrepairAttemptand prefer it overinterruptOpenTurn + attempt.resumeAgentRunFromHumanInputto call the atomic path when the backend provides it; otherwise use the existing two-step repair.Written for commit 357d89e. Summary will update on new commits.