-
Notifications
You must be signed in to change notification settings - Fork 0
fix(gateway): soft-reset pre-turn gap recovery (v0.5.38) #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
6150a7d
design: soft-reset pre-turn gap recovery (v0.5.38)
bfdb388
fix(gateway): classify and recover from context overflow in agent.pro…
d09d764
test: gateway and helper-level overflow recovery tests (v0.5.38)
662f032
fix(streaming): escalate model_error overflow to gateway recovery (F1…
royosherove 4df2d3d
fix(gateway): drop dead 'cron' TurnSource and clarify unsupported-ove…
royosherove 71c674e
refactor: extract telemetry, dedupe MAX_ERROR_PREVIEW (F4+F5+F6)
royosherove 9797640
docs(changelog): v0.5.38 streaming-path fix + F2-F6 polish
royosherove File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /** | ||
| * agents/shared/overflow-recovery.ts — Reactive context-overflow recovery helper | ||
| * | ||
| * Used by: | ||
| * - src/memory/lifecycle.ts: catch in flushMemoryThenCompact (compact itself overflowed) | ||
| * - src/gateway/gateway.ts: catch around agent.prompt/agent.promptStream | ||
| * (the live session was already past the limit before this turn even started — typically | ||
| * after idle background growth via cron/boot/sub-agents) | ||
| * | ||
| * Pure agent-error → agent-action helper. Memory-state effects | ||
| * (forceInjectReason="after-soft-reset", clearing pendingCompact, arming | ||
| * pendingCompact="emergency") are the CALLER's responsibility, because the two | ||
| * call sites need different fallback semantics: | ||
| * - lifecycle: re-arms pendingCompact at whatever level was failing | ||
| * - gateway: only arms pendingCompact="emergency" when agent.compact exists | ||
| * and softReset didn't recover (so the next pre-turn branch fires) | ||
| * | ||
| * Returns a discriminated outcome rather than {attempted, succeeded} so callers | ||
| * can branch precisely. | ||
| */ | ||
|
|
||
| import type { AgentAdapter } from "../../types"; | ||
| import type { SoftResetReport } from "./session-soft-reset"; | ||
| import { isContextOverflowError } from "./error-classifiers"; | ||
|
|
||
| export type OverflowRecoveryOutcome = | ||
| | { kind: "not-overflow" } | ||
| | { kind: "unsupported" } // agent.softReset undefined | ||
| | { kind: "recovered"; report: SoftResetReport } // softReset returned reset:true | ||
| | { kind: "noop"; reason: string } // softReset returned reset:false | ||
| | { kind: "failed"; error: string }; // softReset itself threw | ||
|
|
||
| /** Max bytes of resetErr.message we surface in `failed.error` and onProgress. */ | ||
| const MAX_RESET_ERROR_PREVIEW = 200; | ||
|
|
||
| /** | ||
| * Classify err and, on context-overflow, run agent.softReset to trim the | ||
| * on-disk session jsonl. | ||
| * | ||
| * Emits onProgress("♻️ Session overflowed — soft-resetting to recent turns...") | ||
| * when entering recovery, and one of the v0.5.32 trio (✅/⚠️/❌) on outcome. | ||
| * | ||
| * Does NOT mutate memory state. Caller is responsible for state writes. | ||
| */ | ||
| export async function recoverFromContextOverflow( | ||
| err: unknown, | ||
| threadId: string, | ||
| agent: AgentAdapter, | ||
| onProgress?: (step: string) => void | Promise<void>, | ||
| ): Promise<OverflowRecoveryOutcome> { | ||
| if (!isContextOverflowError(err)) { | ||
| return { kind: "not-overflow" }; | ||
| } | ||
|
|
||
| if (!agent.softReset) { | ||
| return { kind: "unsupported" }; | ||
| } | ||
|
|
||
| try { | ||
| await onProgress?.("♻️ Session overflowed — soft-resetting to recent turns..."); | ||
| const report = await agent.softReset(threadId); | ||
|
|
||
| if (report?.reset) { | ||
| console.warn(`[overflow-recovery] soft-reset recovered ${threadId} from overflow`); | ||
| const { entriesBefore, entriesAfter } = report as SoftResetReport; | ||
| const detail = typeof entriesBefore === "number" && typeof entriesAfter === "number" | ||
| ? ` (${entriesBefore} → ${entriesAfter} entries)` | ||
| : ""; | ||
| await onProgress?.(`✅ Soft-reset complete${detail}. Durable memory will re-inject on next turn.`); | ||
| return { kind: "recovered", report: report as SoftResetReport }; | ||
| } | ||
|
|
||
| const reason = (report as { reason?: string } | null)?.reason ?? "unknown"; | ||
| console.warn(`[overflow-recovery] soft-reset returned no-op for ${threadId} (${reason})`); | ||
| await onProgress?.(`⚠️ Soft-reset no-op (${reason}). Will retry compact next turn.`); | ||
| return { kind: "noop", reason }; | ||
| } catch (resetErr) { | ||
| const msg = resetErr instanceof Error ? resetErr.message : String(resetErr); | ||
| console.error(`[overflow-recovery] soft-reset failed for ${threadId}:`, msg); | ||
| await onProgress?.(`❌ Soft-reset failed: ${msg.slice(0, MAX_RESET_ERROR_PREVIEW)}. Will retry next turn.`); | ||
| return { kind: "failed", error: msg }; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
streamHadVisibleTextis only updated fromstreamResultafterhandleStreaming(...)returns, buthandleStreamingnow throwsStreamModelOverflowErroron overflowmodel_errorevents. In the common case where sometext_deltawas already emitted and then overflow occurs, this assignment never runs, so the catch path always receiveshadVisibleText: falseand posts the "Please resend your last message" copy. That is misleading after partial output and can prompt duplicate side effects if the user resends a message whose tools already started running.Useful? React with 👍 / 👎.