-
Notifications
You must be signed in to change notification settings - Fork 91
feat(recovery): add session fallback restore and manual compact #1268
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,7 +173,7 @@ import { expandPromptTemplateWithMetadata, type PromptTemplate } from "./prompt- | |
| import { createProviderTimeoutRetryPlan, runBoundedRetryContinuation } from "./provider-timeout-retry.ts"; | ||
| import type { ResourceExtensionPaths, ResourceLoader } from "./resource-loader.ts"; | ||
| import { isBillingErrorMessage } from "./retry-fallback/billing.ts"; | ||
| import { formatSelector } from "./retry-fallback/chains.ts"; | ||
| import { formatSelector, parseFallbackSelector } from "./retry-fallback/chains.ts"; | ||
| import { RetryFallbackController } from "./retry-fallback/controller.ts"; | ||
| import { SelectorCooldowns } from "./retry-fallback/cooldown.ts"; | ||
| import { | ||
|
|
@@ -6880,6 +6880,17 @@ export class AgentSession { | |
| pinned: active.pinned, | ||
| }; | ||
| }, | ||
| restoreFallbackPrimary: async () => { | ||
| const active = this._retryFallback.activeState; | ||
| if (!active) return false; | ||
| const selector = parseFallbackSelector(active.originalSelector, this._modelRegistry); | ||
| const model = selector ? this._modelRegistry.find(selector.provider, selector.id) : undefined; | ||
| if (!model) return false; | ||
| const originalThinkingLevel = active.originalThinkingLevel; | ||
| await this.setSessionModel(model); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When the model-select step fails, Prompt for AI agentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Restore bypasses the retry controller's revert semantics. It clears state only via the side effect of Prompt for AI agents |
||
| if (originalThinkingLevel !== undefined) this.setSessionThinkingLevel(originalThinkingLevel); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: A restore interrupted after Prompt for AI agents |
||
| return true; | ||
| }, | ||
| }, | ||
| compact: (options) => { | ||
| const admission = this._claimPendingCompactionAdmission(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ import { | |
| CLAUDE_SDK_OAUTH_COMPACT_ENTRY_TYPE, | ||
| collectCompactBoundaryEntries, | ||
| createCompactionLanePolicy, | ||
| isLaneOverrideReason, | ||
| SDK_NATIVE_LANE_REJECTION_REASON, | ||
| } from "./lane-policy.ts"; | ||
| import { type CompactionLogger, createCompactionLogger } from "./log.ts"; | ||
|
|
@@ -556,7 +557,11 @@ export default function compactionExtension( | |
| let warmJobConsumed = false; | ||
| invalidateSpeculativeCompaction(ctx); | ||
| try { | ||
| if (lanePolicy.disablesSenpiCompaction(ctx)) { | ||
| // The lane owns senpi's AUTOMATIC compaction only. An explicitly requested | ||
| // compaction overrides the delegation: senpi cannot observe that the SDK | ||
| // failed to compact, so `/compact` is the only way back under the limit | ||
| // once the delegated owner has not delivered. | ||
| if (!isLaneOverrideReason(event.reason) && lanePolicy.disablesSenpiCompaction(ctx)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When Prompt for AI agents |
||
| return { | ||
| cancel: true, | ||
| rejectionCause: "external-owner", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -178,10 +178,10 @@ function beforeAgentStartEvent(): BeforeAgentStartEvent { | |||||||
| } as BeforeAgentStartEvent; | ||||||||
| } | ||||||||
|
|
||||||||
| function beforeCompactEvent(): SessionBeforeCompactEvent { | ||||||||
| function beforeCompactEvent(reason: SessionBeforeCompactEvent["reason"] = "threshold"): SessionBeforeCompactEvent { | ||||||||
| return { | ||||||||
| type: "session_before_compact", | ||||||||
| reason: "threshold", | ||||||||
| reason, | ||||||||
| willRetry: false, | ||||||||
| requestId: "request-1", | ||||||||
| preparation: { | ||||||||
|
|
@@ -227,6 +227,17 @@ describe("claude-sdk-oauth lane: senpi compaction stands down", () => { | |||||||
| }); | ||||||||
| }); | ||||||||
|
|
||||||||
| it("never delegates an explicitly requested manual compaction to the SDK", async () => { | ||||||||
| const harness = createHarness({ provider: "claude-sdk-oauth" }); | ||||||||
|
|
||||||||
| const result = await harness.sessionBeforeCompact(beforeCompactEvent("manual"), harness.ctx); | ||||||||
|
|
||||||||
| // The delegation covers senpi's AUTOMATIC compaction only. `/compact` is the | ||||||||
| // user's escape hatch for the case the lane cannot detect: the SDK did not | ||||||||
| // compact and the session has no other way back under the limit. | ||||||||
| expect(result?.rejectionCause).not.toBe("external-owner"); | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The manual-compaction test only asserts that the result is not rejected as Prompt for AI agents
Suggested change
|
||||||||
| }); | ||||||||
|
|
||||||||
| it("leaves context messages untouched while the same load reduces them for other providers", () => { | ||||||||
| const reductionMessages = () => [ | ||||||||
| { role: "user" as const, content: [{ type: "text" as const, text: "u1" }], timestamp: 1 }, | ||||||||
|
|
||||||||
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.
P2: When the original model disappears from the registry,
/fallback restorereports that no fallback is active while leaving the fallback state active. Return a distinct unavailable result or surface the unresolved original model instead of mapping this case to inactive.Prompt for AI agents