fix(mothership): explain a Chat turn the worker ends without a reason - #8478
Conversation
When the worker rebuilds an ended run from its log (a resume or reattach that reaches a run that already ended, for example at its deadline), it sends an error terminal with no error event. Sim then fell back to the generic "An unexpected error occurred while processing the response." The turn now says the run had already ended and can be continued by sending a message. A reason the worker reports, a replay refusal, and a Stop all still take precedence. Also: - Log the Go stream's error text under errorMessage/detail so it no longer overwrites the log line's own message (stream.ts, buffer.ts). - Rename STREAM_TIMEOUT_MS to CHAT_RUN_DEADLINE_MS and document it as the worker's default run deadline, now only the base of USAGE_SETTLE_MS. - Update the byte-budget doc: a reader behind the ring trim is re-synced from the worker log; replay_gap is only the fallback.
… a replay refusal The fallback is shared by Chat, workflow execute and inbox, so it no longer tells the reader to send a message. A replay refusal now wins by guard rather than by spread order, with a test that covers the combination.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
|
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
…e stream-abort test helper
…-terminal-message
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
Summary
Problem. When a resume or reattach reaches a Chat run that has already ended (for example at its deadline), the turn failed with the generic "An unexpected error occurred while processing the response." and gave no hint of what happened.
Root cause. When the worker replays an ended run from its log, it sends an error terminal with no
errorevent, because that replay does not carry the run's stored reason.runCopilotLifecyclesawcompletionStatus === errorwith an emptycontext.errors, so no specific message was set and callers fell back to the generic one.Fix.
runCopilotLifecyclenow detects that exact shape (error terminal, no reported error, no replay refusal, not aborted) and sets a surface-neutral message: "This run had already ended before it could continue." A reason the worker reports, a replay refusal, and a user Stop all still take precedence. The refusal wins by an explicit guard, not by spread order.Also:
errorMessage/detail, so it no longer overwrites the log line's ownmessagefield (go/stream.ts,session/buffer.ts).STREAM_TIMEOUT_MSis renamedCHAT_RUN_DEADLINE_MSand documented as the worker's default run deadline. Sim does not enforce it; it is only the base ofUSAGE_SETTLE_MS.replay_gaponly when that log cannot serve it.Behaviour changes
message→errorMessage/detail).USAGE_SETTLE_MSkeeps the same number.Test plan
lib/mothership/request/lifecycle/run.test.ts: new cases for an error terminal with no reason (gets the ended-run message), a replay refusal combined with a reasonless error terminal (refusal wins), and a Stop with a reasonless error terminal (stays a cancellation). Removing the fallback turns the ended-run case red; restoring it turns it green.lib/mothership/request/lifecycleandlib/mothership/request/gosuites pass (185 tests).bun run type-checkinapps/sim