Repository navigation
feat(think,ai-chat,agents): expose incident identity + enrich onExhausted payload (#1631) - #1636
Conversation
🦋 Changeset detectedLatest commit: 827e0d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
60cf123 to
fd8a39d
Compare
…sted payload (#1631) Lets products build a terminal-state policy without re-deriving anything: - ChatRecoveryContext (onChatRecovery) gains recoveryRootRequestId — the stable request id for the whole continuation chain, the right key for per-incident budget tracking / fresh-incident detection (no re-deriving from message IDs). - ChatRecoveryExhaustedContext (onExhausted) gains recoveryRootRequestId, terminalMessage (exact user-facing text), partialText/partialParts (what the turn produced before it was given up on), and streamId/createdAt — enough to render/persist a terminal banner AND emit correlated terminal telemetry (msSinceTurnStart, stream correlation) directly. streamId + createdAt were added after verifying the payload against the actual consumer (g3's _emitExhaustedRecovery): it reads both from the recovery context for telemetry, and they already exist on ChatRecoveryContext (the Pick source), so adding them to the exhausted context is additive and unblocks re-homing the exhaustion handler onto onExhausted with zero re-derivation (D4). Shared types in `agents`; wired through think + ai-chat (_exhaustChatRecovery now receives streamId + createdAt). Test agents capture the exhausted context; tests assert both contexts (incl. streamId + createdAt) in both packages. Rebased onto main (dropping the merged #1633/#1634/#1635 commits); adapted the exhausted-ctx test to N1/#1638's alarm-debounce and gave the think harness an explicit return shape (the context's MessagePart[] over-instantiates the RPC stub type). Co-authored-by: Cursor <cursoragent@cursor.com>
4e5d0cb to
827e0d5
Compare
Rebased onto
|
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
Stack
This is PR 4 of 4 (top) for #1631. Stacked; base is
feat/think-repair-interrupted-tool-part-hook(PR #1635). Retargets tomainas the stack merges. Diff shown is only this PR's commit.repairInterruptedToolParthook · minoronExhaustedpayload (this PR) · minorPublic-API addition to shared types — additive only. Touches
agents(sharedlifecycle.tstypes),@cloudflare/think, and@cloudflare/ai-chat.Problem (#1631, item 3)
A production agent needs a persisted, user-visible terminal signal when recovery gives up, and a stable way to key per-incident budget state — but the framework made products re-derive both:
onExhaustedpayload is too thin.ChatRecoveryExhaustedContextcarried onlyincidentId / requestId / attempt / maxAttempts / recoveryKind / reason. To render or persist a "this turn failed, send a new message" banner (especially when the client was disconnected at the moment recovery exhausted — exactly when this happens under deploy churn), products re-implemented it in a fullonChatRecoveryoverride.onChatRecoverygotincidentId(<root>:<latestUserMessageId>) but not therecoveryRootRequestIdon its own. SincerequestIdchanges on every chained continuation, products keyed budget/fresh-incident logic off message IDs — error-prone, and the source of premature exhaustion when a custom budget lacked the framework's progress-aware reset.Fix (additive)
In the shared
packages/agents/src/chat/lifecycle.ts:ChatRecoveryContextgainsrecoveryRootRequestId: string— the stable request ID for the whole continuation chain (constant across chained continuations). The right key for per-incident budget tracking / fresh-incident detection without touching message IDs.ChatRecoveryExhaustedContextgainsrecoveryRootRequestId,terminalMessage(the exact user-facing text from thechatRecoveryconfig), andpartialText/partialParts(what the turn produced before being given up on) — enough to render/persist a contextual terminal banner with zero re-derivation.Wired through both packages:
onChatRecoveryctx now includesrecoveryRootRequestId;_exhaustChatRecoverytakes the reconstructedpartialand builds the enriched ctx (ai-chat's localChatRecoveryIncidentgainedrecoveryRootRequestId, mirroring think).Safety of the required field
recoveryRootRequestIdis required onChatRecoveryContext, but that type is only ever constructed by the framework (think.ts,ai-chat/index.ts, and the test workers — all updated here). Consumers only overrideonChatRecovery(ctx)and read the context, so adding a field is backward-compatible for them. Verified there are no otherChatRecoveryContextconstructors in the repo.Files
packages/agents/src/chat/lifecycle.ts—recoveryRootRequestIdonChatRecoveryContext; enrichedChatRecoveryExhaustedContext.packages/think/src/think.ts— passrecoveryRootRequestIdinto theonChatRecoveryctx;_exhaustChatRecovery(incident, config, partial)builds the enriched ctx.packages/ai-chat/src/index.ts— same; localChatRecoveryIncidentgainsrecoveryRootRequestId(set in_beginChatRecoveryIncident).enableExhaustedCaptureForTest/getExhaustedContextsForTest) and the recovery ctx'srecoveryRootRequestId..changeset/chat-recovery-exhausted-context.md(minor · agents + think + ai-chat).Tests (both packages)
recoveryRootRequestIdon theonChatRecoverycontext.onExhaustedcontext carriesterminalMessage,recoveryRootRequestId, and the partial — drive an incident to exhaustion and assert the captured exhausted ctx.Verification
tsc --noEmitclean (think + ai-chat, after rebuildingagents);oxlintclean;oxfmtapplied.Test plan
ChatRecoveryContext/ChatRecoveryExhaustedContextfieldsChatRecoveryContextbreak (none in-repo)Made with Cursor