@code-yeongyu/senpi 2026.8.28 → 2026.8.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +5 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +22 -12
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts +6 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +22 -5
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/credential-pool/failover.d.ts +8 -0
- package/dist/core/credential-pool/failover.d.ts.map +1 -1
- package/dist/core/credential-pool/failover.js +24 -6
- package/dist/core/credential-pool/failover.js.map +1 -1
- package/dist/core/credential-pool/rotation-stream.d.ts +13 -2
- package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
- package/dist/core/credential-pool/rotation-stream.js +97 -10
- package/dist/core/credential-pool/rotation-stream.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +7 -0
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js +13 -0
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +4 -2
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/model-config-schema.d.ts +4 -4
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +74 -37
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/retry-fallback/chains.d.ts +23 -1
- package/dist/core/retry-fallback/chains.d.ts.map +1 -1
- package/dist/core/retry-fallback/chains.js +59 -3
- package/dist/core/retry-fallback/chains.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js +9 -0
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/core/retry-fallback/validate.js +1 -1
- package/dist/core/retry-fallback/validate.js.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +31 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/main.d.ts +7 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.js +2 -2
- package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +323 -33
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +276 -237
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +133 -24
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +14 -4
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +82 -13
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +85 -28
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +3 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +162 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +22 -0
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +147 -16
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +4 -0
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +7 -0
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +9 -0
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/docs/rpc.md +37 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +6 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -15,7 +15,11 @@ export class CredentialFailoverError extends Error {
|
|
|
15
15
|
function isAvailable(slot, now) {
|
|
16
16
|
if (slot.blockReason === "auth_error" || slot.blockReason === "account_disabled")
|
|
17
17
|
return false;
|
|
18
|
-
|
|
18
|
+
if (slot.blockedUntil !== undefined && slot.blockedUntil > now)
|
|
19
|
+
return false;
|
|
20
|
+
// A live lease marks the one caller admitted to run the half-open probe.
|
|
21
|
+
// listSlots excludes that lease for later callers; the holder must remain runnable.
|
|
22
|
+
return true;
|
|
19
23
|
}
|
|
20
24
|
function soonestRetryAt(slots, now) {
|
|
21
25
|
const deadlines = slots
|
|
@@ -75,29 +79,43 @@ export async function* runCredentialFailover(options) {
|
|
|
75
79
|
committedOutput ||= options.isCommittedOutput(event);
|
|
76
80
|
yield event;
|
|
77
81
|
}
|
|
82
|
+
await options.onSuccess?.(slot);
|
|
78
83
|
return;
|
|
79
84
|
}
|
|
80
85
|
catch (error) {
|
|
81
86
|
if (attemptStream)
|
|
82
87
|
await settle(attemptStream);
|
|
83
88
|
const failureCount = (slot.failureCount ?? 0) + (retriesBySlot.get(slot.name) ?? 0);
|
|
84
|
-
const action = classify(error, {
|
|
89
|
+
const action = classify(error, {
|
|
90
|
+
failureCount,
|
|
91
|
+
});
|
|
85
92
|
lastOriginal = error;
|
|
86
93
|
if (action.kind === "retry_same" && !committedOutput) {
|
|
87
94
|
const used = (retriesBySlot.get(slot.name) ?? 0) + 1;
|
|
88
95
|
retriesBySlot.set(slot.name, used);
|
|
89
96
|
if (used < action.maxAttempts)
|
|
90
97
|
continue;
|
|
91
|
-
throw new CredentialFailoverError(action, error, {
|
|
98
|
+
throw new CredentialFailoverError(action, error, {
|
|
99
|
+
suppressTurnRetry: false,
|
|
100
|
+
});
|
|
92
101
|
}
|
|
93
102
|
if (action.kind !== "failover") {
|
|
94
|
-
throw new CredentialFailoverError(action, error, {
|
|
103
|
+
throw new CredentialFailoverError(action, error, {
|
|
104
|
+
suppressTurnRetry: committedOutput,
|
|
105
|
+
});
|
|
95
106
|
}
|
|
96
107
|
await options.persistBlock(slot, action.block);
|
|
97
108
|
attempted.add(slot.name);
|
|
98
|
-
const failure = new CredentialFailoverError(action, error, {
|
|
109
|
+
const failure = new CredentialFailoverError(action, error, {
|
|
110
|
+
suppressTurnRetry: committedOutput,
|
|
111
|
+
});
|
|
99
112
|
lastError = failure;
|
|
100
|
-
await options.onRotate?.({
|
|
113
|
+
await options.onRotate?.({
|
|
114
|
+
slot,
|
|
115
|
+
block: action.block,
|
|
116
|
+
attempt: attempted.size,
|
|
117
|
+
committedOutput,
|
|
118
|
+
});
|
|
101
119
|
if (committedOutput)
|
|
102
120
|
throw failure;
|
|
103
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failover.js","sourceRoot":"","sources":["../../../src/core/credential-pool/failover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAA+C,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAEvG,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAmCzC,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAMjD,YAAY,MAAwB,EAAE,QAAiB,EAAE,OAAyD;QACjH,MAAM,MAAM,GAAG,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/E,KAAK,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzG,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,CAAC;CACD;AAED,SAAS,WAAW,CAAC,IAAa,EAAE,GAAW;IAC9C,IAAI,IAAI,CAAC,WAAW,KAAK,YAAY,IAAI,IAAI,CAAC,WAAW,KAAK,kBAAkB;QAAE,OAAO,KAAK,CAAC;IAC/F,OAAO,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,IAAI,GAAG,CAAC;AACpE,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB,EAAE,GAAW;IAC7D,MAAM,SAAS,GAAG,KAAK;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;SAChC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,GAAG,CAAC,CAAC;IACzE,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;AACpE,CAAC;AAED,KAAK,UAAU,MAAM,CAAS,MAA6B;IAC1D,yEAAyE;IACzE,oEAAoE;IACpE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;IAChD,IAAI,CAAC;QACJ,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,+DAA+D;IAChE,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,qBAAqB,CAC3C,OAAoD;IAEpD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,yBAAyB,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,IAAI,SAA8C,CAAC;IACnD,IAAI,YAAqB,CAAC;IAE1B,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACjG,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAC7C,MAAM,SAAS,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS;gBAC1D,CAAC,CAAC,IAAI,uBAAuB,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE;oBAC5D,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;oBAC9C,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;iBAC7C,CAAC;gBACH,CAAC,CAAC,IAAI,uBAAuB,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,EAAE;oBAClG,iBAAiB,EAAE,KAAK;oBACxB,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;iBAC7C,CAAC,CAAC;QACN,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,aAAgD,CAAC;QACrD,IAAI,CAAC;YACJ,aAAa,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;gBACzC,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC;gBAChD,IAAI,OAAO,KAAK,SAAS;oBAAE,MAAM,OAAO,CAAC;gBACzC,eAAe,KAAK,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBACrD,MAAM,KAAK,CAAC;YACb,CAAC;YACD,OAAO;QACR,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,aAAa;gBAAE,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YAC/C,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACpF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;YACjD,YAAY,GAAG,KAAK,CAAC;YAErB,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,eAAe,EAAE,CAAC;gBACtD,MAAM,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBACrD,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACnC,IAAI,IAAI,GAAG,MAAM,CAAC,WAAW;oBAAE,SAAS;gBACxC,MAAM,IAAI,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC;YAChF,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAChC,MAAM,IAAI,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC,CAAC;YAC1F,CAAC;YAED,MAAM,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/C,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC,CAAC;YACnG,SAAS,GAAG,OAAO,CAAC;YACpB,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;YAClG,IAAI,eAAe;gBAAE,MAAM,OAAO,CAAC;QACpC,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["import { TURN_RETRY_SUPPRESSION_PREFIX } from \"@earendil-works/pi-ai/auth/pool/failover\";\nimport { type CredentialAction, type CredentialBlock, classifyCredentialFailure } from \"./classify.ts\";\n\nexport { TURN_RETRY_SUPPRESSION_PREFIX };\n\nexport type RunSlot = {\n\tname: string;\n\tblockedUntil?: number;\n\tblockReason?: string;\n\tfailureCount?: number;\n};\n\nexport type CredentialFailoverEvent<TSlot extends RunSlot> = {\n\tslot: TSlot;\n\tblock: CredentialBlock;\n\tattempt: number;\n\tcommittedOutput: boolean;\n};\n\nexport type RunCredentialFailoverOptions<TEvent, TSlot extends RunSlot> = {\n\t/** Re-read before every distinct-credential attempt so a newly added slot participates. */\n\tlistSlots: () => Promise<readonly TSlot[]> | readonly TSlot[];\n\tselect: (slots: readonly TSlot[]) => TSlot;\n\trunAttempt: (slot: TSlot) => AsyncIterable<TEvent> | Promise<AsyncIterable<TEvent>>;\n\t/**\n\t * REQUIRED and default-DENY by contract: return false only for event types\n\t * explicitly known to be pre-commit bookkeeping. Any event this predicate\n\t * does not recognize must count as committed output.\n\t */\n\tisCommittedOutput: (event: TEvent) => boolean;\n\tclassify?: (error: unknown, context: { failureCount: number }) => CredentialAction;\n\terrorFromEvent?: (event: TEvent) => unknown | undefined;\n\t/** Persisted BEFORE a replacement slot is selected so a crash never forgets a block. */\n\tpersistBlock: (slot: TSlot, block: CredentialBlock) => void | Promise<void>;\n\tonRotate?: (event: CredentialFailoverEvent<TSlot>) => void | Promise<void>;\n\tnow?: () => number;\n};\n\nexport class CredentialFailoverError extends Error {\n\treadonly action: CredentialAction;\n\treadonly original: unknown;\n\treadonly suppressTurnRetry: boolean;\n\treadonly retryAt: number | undefined;\n\n\tconstructor(action: CredentialAction, original: unknown, options: { suppressTurnRetry: boolean; retryAt?: number }) {\n\t\tconst detail = original instanceof Error ? original.message : String(original);\n\t\tsuper(`${options.suppressTurnRetry ? TURN_RETRY_SUPPRESSION_PREFIX : \"\"}${detail}`, { cause: original });\n\t\tthis.name = \"CredentialFailoverError\";\n\t\tthis.action = action;\n\t\tthis.original = original;\n\t\tthis.suppressTurnRetry = options.suppressTurnRetry;\n\t\tthis.retryAt = options.retryAt;\n\t}\n}\n\nfunction isAvailable(slot: RunSlot, now: number): boolean {\n\tif (slot.blockReason === \"auth_error\" || slot.blockReason === \"account_disabled\") return false;\n\treturn slot.blockedUntil === undefined || slot.blockedUntil <= now;\n}\n\nfunction soonestRetryAt(slots: readonly RunSlot[], now: number): number | undefined {\n\tconst deadlines = slots\n\t\t.map((slot) => slot.blockedUntil)\n\t\t.filter((value): value is number => value !== undefined && value > now);\n\treturn deadlines.length === 0 ? undefined : Math.min(...deadlines);\n}\n\nasync function settle<TEvent>(stream: AsyncIterable<TEvent>): Promise<void> {\n\t// A failed attempt must fully release its transport before a replacement\n\t// starts, or two live streams can interleave provider-side effects.\n\tconst iterator = stream[Symbol.asyncIterator]();\n\ttry {\n\t\tawait iterator.return?.(undefined);\n\t} catch {\n\t\t// Settling a dead stream must never mask the original failure.\n\t}\n}\n\n/**\n * Generic in-lane credential failover. Runs at most one failover attempt per\n * slot per request, retries provider-scoped faults on the SAME slot up to the\n * classifier's bound without blocking it, and rotates only while no committed\n * output has reached the caller - afterwards the failure is rethrown with the\n * `senpi:no-turn-retry:` marker so the turn is never replayed.\n */\nexport async function* runCredentialFailover<TEvent, TSlot extends RunSlot>(\n\toptions: RunCredentialFailoverOptions<TEvent, TSlot>,\n): AsyncGenerator<TEvent> {\n\tconst now = options.now ?? Date.now;\n\tconst classify = options.classify ?? classifyCredentialFailure;\n\tconst attempted = new Set<string>();\n\tconst retriesBySlot = new Map<string, number>();\n\tlet lastError: CredentialFailoverError | undefined;\n\tlet lastOriginal: unknown;\n\n\twhile (true) {\n\t\tconst slots = await options.listSlots();\n\t\tconst candidates = slots.filter((slot) => !attempted.has(slot.name) && isAvailable(slot, now()));\n\t\tif (candidates.length === 0) {\n\t\t\tconst retryAt = soonestRetryAt(slots, now());\n\t\t\tthrow lastError !== undefined && lastOriginal !== undefined\n\t\t\t\t? new CredentialFailoverError(lastError.action, lastOriginal, {\n\t\t\t\t\t\tsuppressTurnRetry: lastError.suppressTurnRetry,\n\t\t\t\t\t\t...(retryAt === undefined ? {} : { retryAt }),\n\t\t\t\t\t})\n\t\t\t\t: new CredentialFailoverError({ kind: \"fail_request\" }, new Error(\"No credential slots available\"), {\n\t\t\t\t\t\tsuppressTurnRetry: false,\n\t\t\t\t\t\t...(retryAt === undefined ? {} : { retryAt }),\n\t\t\t\t\t});\n\t\t}\n\t\tconst slot = options.select(candidates);\n\t\tlet committedOutput = false;\n\t\tlet attemptStream: AsyncIterable<TEvent> | undefined;\n\t\ttry {\n\t\t\tattemptStream = await options.runAttempt(slot);\n\t\t\tfor await (const event of attemptStream) {\n\t\t\t\tconst failure = options.errorFromEvent?.(event);\n\t\t\t\tif (failure !== undefined) throw failure;\n\t\t\t\tcommittedOutput ||= options.isCommittedOutput(event);\n\t\t\t\tyield event;\n\t\t\t}\n\t\t\treturn;\n\t\t} catch (error) {\n\t\t\tif (attemptStream) await settle(attemptStream);\n\t\t\tconst failureCount = (slot.failureCount ?? 0) + (retriesBySlot.get(slot.name) ?? 0);\n\t\t\tconst action = classify(error, { failureCount });\n\t\t\tlastOriginal = error;\n\n\t\t\tif (action.kind === \"retry_same\" && !committedOutput) {\n\t\t\t\tconst used = (retriesBySlot.get(slot.name) ?? 0) + 1;\n\t\t\t\tretriesBySlot.set(slot.name, used);\n\t\t\t\tif (used < action.maxAttempts) continue;\n\t\t\t\tthrow new CredentialFailoverError(action, error, { suppressTurnRetry: false });\n\t\t\t}\n\t\t\tif (action.kind !== \"failover\") {\n\t\t\t\tthrow new CredentialFailoverError(action, error, { suppressTurnRetry: committedOutput });\n\t\t\t}\n\n\t\t\tawait options.persistBlock(slot, action.block);\n\t\t\tattempted.add(slot.name);\n\t\t\tconst failure = new CredentialFailoverError(action, error, { suppressTurnRetry: committedOutput });\n\t\t\tlastError = failure;\n\t\t\tawait options.onRotate?.({ slot, block: action.block, attempt: attempted.size, committedOutput });\n\t\t\tif (committedOutput) throw failure;\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"failover.js","sourceRoot":"","sources":["../../../src/core/credential-pool/failover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAA+C,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAEvG,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAyCzC,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAMjD,YAAY,MAAwB,EAAE,QAAiB,EAAE,OAAyD;QACjH,MAAM,MAAM,GAAG,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/E,KAAK,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzG,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,CAAC;CACD;AAED,SAAS,WAAW,CAAC,IAAa,EAAE,GAAW;IAC9C,IAAI,IAAI,CAAC,WAAW,KAAK,YAAY,IAAI,IAAI,CAAC,WAAW,KAAK,kBAAkB;QAAE,OAAO,KAAK,CAAC;IAC/F,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,GAAG,GAAG;QAAE,OAAO,KAAK,CAAC;IAC7E,yEAAyE;IACzE,oFAAoF;IACpF,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB,EAAE,GAAW;IAC7D,MAAM,SAAS,GAAG,KAAK;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;SAChC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,GAAG,CAAC,CAAC;IACzE,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;AACpE,CAAC;AAED,KAAK,UAAU,MAAM,CAAS,MAA6B;IAC1D,yEAAyE;IACzE,oEAAoE;IACpE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;IAChD,IAAI,CAAC;QACJ,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,+DAA+D;IAChE,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,qBAAqB,CAC3C,OAAoD;IAEpD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,yBAAyB,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,IAAI,SAA8C,CAAC;IACnD,IAAI,YAAqB,CAAC;IAE1B,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACjG,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAC7C,MAAM,SAAS,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS;gBAC1D,CAAC,CAAC,IAAI,uBAAuB,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE;oBAC5D,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;oBAC9C,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;iBAC7C,CAAC;gBACH,CAAC,CAAC,IAAI,uBAAuB,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,EAAE;oBAClG,iBAAiB,EAAE,KAAK;oBACxB,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;iBAC7C,CAAC,CAAC;QACN,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,aAAgD,CAAC;QACrD,IAAI,CAAC;YACJ,aAAa,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;gBACzC,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC;gBAChD,IAAI,OAAO,KAAK,SAAS;oBAAE,MAAM,OAAO,CAAC;gBACzC,eAAe,KAAK,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBACrD,MAAM,KAAK,CAAC;YACb,CAAC;YACD,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;YAChC,OAAO;QACR,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,aAAa;gBAAE,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YAC/C,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACpF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE;gBAC9B,YAAY;aACZ,CAAC,CAAC;YACH,YAAY,GAAG,KAAK,CAAC;YAErB,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,eAAe,EAAE,CAAC;gBACtD,MAAM,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBACrD,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACnC,IAAI,IAAI,GAAG,MAAM,CAAC,WAAW;oBAAE,SAAS;gBACxC,MAAM,IAAI,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE;oBAChD,iBAAiB,EAAE,KAAK;iBACxB,CAAC,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAChC,MAAM,IAAI,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE;oBAChD,iBAAiB,EAAE,eAAe;iBAClC,CAAC,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/C,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE;gBAC1D,iBAAiB,EAAE,eAAe;aAClC,CAAC,CAAC;YACH,SAAS,GAAG,OAAO,CAAC;YACpB,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACxB,IAAI;gBACJ,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,OAAO,EAAE,SAAS,CAAC,IAAI;gBACvB,eAAe;aACf,CAAC,CAAC;YACH,IAAI,eAAe;gBAAE,MAAM,OAAO,CAAC;QACpC,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["import { TURN_RETRY_SUPPRESSION_PREFIX } from \"@earendil-works/pi-ai/auth/pool/failover\";\nimport { type CredentialAction, type CredentialBlock, classifyCredentialFailure } from \"./classify.ts\";\n\nexport { TURN_RETRY_SUPPRESSION_PREFIX };\n\nexport type RunSlot = {\n\tname: string;\n\tblockedUntil?: number;\n\tblockReason?: string;\n\tfailureCount?: number;\n\tlease?: { id: string; expiresAt: number };\n\tpinned?: boolean;\n};\n\nexport type CredentialFailoverEvent<TSlot extends RunSlot> = {\n\tslot: TSlot;\n\tblock: CredentialBlock;\n\tattempt: number;\n\tcommittedOutput: boolean;\n};\n\nexport type RunCredentialFailoverOptions<TEvent, TSlot extends RunSlot> = {\n\t/** Re-read before every distinct-credential attempt so a newly added slot participates. */\n\tlistSlots: () => Promise<readonly TSlot[]> | readonly TSlot[];\n\tselect: (slots: readonly TSlot[]) => TSlot;\n\trunAttempt: (slot: TSlot) => AsyncIterable<TEvent> | Promise<AsyncIterable<TEvent>>;\n\t/**\n\t * REQUIRED and default-DENY by contract: return false only for event types\n\t * explicitly known to be pre-commit bookkeeping. Any event this predicate\n\t * does not recognize must count as committed output.\n\t */\n\tisCommittedOutput: (event: TEvent) => boolean;\n\tclassify?: (\n\t\terror: unknown,\n\t\tcontext: { failureCount: number; cooldownBaseMs?: number; cooldownCapMs?: number },\n\t) => CredentialAction;\n\terrorFromEvent?: (event: TEvent) => unknown | undefined;\n\t/** Persisted BEFORE a replacement slot is selected so a crash never forgets a block. */\n\tpersistBlock: (slot: TSlot, block: CredentialBlock) => void | Promise<void>;\n\tonRotate?: (event: CredentialFailoverEvent<TSlot>) => void | Promise<void>;\n\tonSuccess?: (slot: TSlot) => void | Promise<void>;\n\tnow?: () => number;\n};\n\nexport class CredentialFailoverError extends Error {\n\treadonly action: CredentialAction;\n\treadonly original: unknown;\n\treadonly suppressTurnRetry: boolean;\n\treadonly retryAt: number | undefined;\n\n\tconstructor(action: CredentialAction, original: unknown, options: { suppressTurnRetry: boolean; retryAt?: number }) {\n\t\tconst detail = original instanceof Error ? original.message : String(original);\n\t\tsuper(`${options.suppressTurnRetry ? TURN_RETRY_SUPPRESSION_PREFIX : \"\"}${detail}`, { cause: original });\n\t\tthis.name = \"CredentialFailoverError\";\n\t\tthis.action = action;\n\t\tthis.original = original;\n\t\tthis.suppressTurnRetry = options.suppressTurnRetry;\n\t\tthis.retryAt = options.retryAt;\n\t}\n}\n\nfunction isAvailable(slot: RunSlot, now: number): boolean {\n\tif (slot.blockReason === \"auth_error\" || slot.blockReason === \"account_disabled\") return false;\n\tif (slot.blockedUntil !== undefined && slot.blockedUntil > now) return false;\n\t// A live lease marks the one caller admitted to run the half-open probe.\n\t// listSlots excludes that lease for later callers; the holder must remain runnable.\n\treturn true;\n}\n\nfunction soonestRetryAt(slots: readonly RunSlot[], now: number): number | undefined {\n\tconst deadlines = slots\n\t\t.map((slot) => slot.blockedUntil)\n\t\t.filter((value): value is number => value !== undefined && value > now);\n\treturn deadlines.length === 0 ? undefined : Math.min(...deadlines);\n}\n\nasync function settle<TEvent>(stream: AsyncIterable<TEvent>): Promise<void> {\n\t// A failed attempt must fully release its transport before a replacement\n\t// starts, or two live streams can interleave provider-side effects.\n\tconst iterator = stream[Symbol.asyncIterator]();\n\ttry {\n\t\tawait iterator.return?.(undefined);\n\t} catch {\n\t\t// Settling a dead stream must never mask the original failure.\n\t}\n}\n\n/**\n * Generic in-lane credential failover. Runs at most one failover attempt per\n * slot per request, retries provider-scoped faults on the SAME slot up to the\n * classifier's bound without blocking it, and rotates only while no committed\n * output has reached the caller - afterwards the failure is rethrown with the\n * `senpi:no-turn-retry:` marker so the turn is never replayed.\n */\nexport async function* runCredentialFailover<TEvent, TSlot extends RunSlot>(\n\toptions: RunCredentialFailoverOptions<TEvent, TSlot>,\n): AsyncGenerator<TEvent> {\n\tconst now = options.now ?? Date.now;\n\tconst classify = options.classify ?? classifyCredentialFailure;\n\tconst attempted = new Set<string>();\n\tconst retriesBySlot = new Map<string, number>();\n\tlet lastError: CredentialFailoverError | undefined;\n\tlet lastOriginal: unknown;\n\n\twhile (true) {\n\t\tconst slots = await options.listSlots();\n\t\tconst candidates = slots.filter((slot) => !attempted.has(slot.name) && isAvailable(slot, now()));\n\t\tif (candidates.length === 0) {\n\t\t\tconst retryAt = soonestRetryAt(slots, now());\n\t\t\tthrow lastError !== undefined && lastOriginal !== undefined\n\t\t\t\t? new CredentialFailoverError(lastError.action, lastOriginal, {\n\t\t\t\t\t\tsuppressTurnRetry: lastError.suppressTurnRetry,\n\t\t\t\t\t\t...(retryAt === undefined ? {} : { retryAt }),\n\t\t\t\t\t})\n\t\t\t\t: new CredentialFailoverError({ kind: \"fail_request\" }, new Error(\"No credential slots available\"), {\n\t\t\t\t\t\tsuppressTurnRetry: false,\n\t\t\t\t\t\t...(retryAt === undefined ? {} : { retryAt }),\n\t\t\t\t\t});\n\t\t}\n\t\tconst slot = options.select(candidates);\n\t\tlet committedOutput = false;\n\t\tlet attemptStream: AsyncIterable<TEvent> | undefined;\n\t\ttry {\n\t\t\tattemptStream = await options.runAttempt(slot);\n\t\t\tfor await (const event of attemptStream) {\n\t\t\t\tconst failure = options.errorFromEvent?.(event);\n\t\t\t\tif (failure !== undefined) throw failure;\n\t\t\t\tcommittedOutput ||= options.isCommittedOutput(event);\n\t\t\t\tyield event;\n\t\t\t}\n\t\t\tawait options.onSuccess?.(slot);\n\t\t\treturn;\n\t\t} catch (error) {\n\t\t\tif (attemptStream) await settle(attemptStream);\n\t\t\tconst failureCount = (slot.failureCount ?? 0) + (retriesBySlot.get(slot.name) ?? 0);\n\t\t\tconst action = classify(error, {\n\t\t\t\tfailureCount,\n\t\t\t});\n\t\t\tlastOriginal = error;\n\n\t\t\tif (action.kind === \"retry_same\" && !committedOutput) {\n\t\t\t\tconst used = (retriesBySlot.get(slot.name) ?? 0) + 1;\n\t\t\t\tretriesBySlot.set(slot.name, used);\n\t\t\t\tif (used < action.maxAttempts) continue;\n\t\t\t\tthrow new CredentialFailoverError(action, error, {\n\t\t\t\t\tsuppressTurnRetry: false,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (action.kind !== \"failover\") {\n\t\t\t\tthrow new CredentialFailoverError(action, error, {\n\t\t\t\t\tsuppressTurnRetry: committedOutput,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait options.persistBlock(slot, action.block);\n\t\t\tattempted.add(slot.name);\n\t\t\tconst failure = new CredentialFailoverError(action, error, {\n\t\t\t\tsuppressTurnRetry: committedOutput,\n\t\t\t});\n\t\t\tlastError = failure;\n\t\t\tawait options.onRotate?.({\n\t\t\t\tslot,\n\t\t\t\tblock: action.block,\n\t\t\t\tattempt: attempted.size,\n\t\t\t\tcommittedOutput,\n\t\t\t});\n\t\t\tif (committedOutput) throw failure;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AssistantMessageEvent, Credential } from "@earendil-works/pi-ai";
|
|
2
2
|
import { type SlotHasher } from "@earendil-works/pi-ai/auth/pool/select";
|
|
3
3
|
import { type RunSlot } from "./failover.ts";
|
|
4
|
-
import type
|
|
4
|
+
import { type CredentialSlotRepository } from "./state-store.ts";
|
|
5
5
|
/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */
|
|
6
6
|
export declare const sha256SlotHasher: SlotHasher;
|
|
7
7
|
export type RotationLane = "stored" | "env";
|
|
@@ -16,6 +16,15 @@ export type RotationSources = {
|
|
|
16
16
|
credential: Credential | undefined;
|
|
17
17
|
env: (name: string) => string | undefined;
|
|
18
18
|
repository: CredentialSlotRepository;
|
|
19
|
+
policy?: {
|
|
20
|
+
affinity?: boolean;
|
|
21
|
+
cooldownBaseMs?: number;
|
|
22
|
+
cooldownCapMs?: number;
|
|
23
|
+
slots?: Record<string, {
|
|
24
|
+
env?: string;
|
|
25
|
+
value?: string;
|
|
26
|
+
}>;
|
|
27
|
+
};
|
|
19
28
|
now?: () => number;
|
|
20
29
|
};
|
|
21
30
|
/**
|
|
@@ -25,7 +34,9 @@ export type RotationSources = {
|
|
|
25
34
|
* persisted health applies only while its HMAC revision still matches the
|
|
26
35
|
* current env value, so rotating a key in place clears its own stale block.
|
|
27
36
|
*/
|
|
28
|
-
export declare function listRotationSlots(sources: RotationSources
|
|
37
|
+
export declare function listRotationSlots(sources: RotationSources, options?: {
|
|
38
|
+
acquireLeases?: boolean;
|
|
39
|
+
}): Promise<RotationSlot[]>;
|
|
29
40
|
export type CredentialRotationOptions = {
|
|
30
41
|
sources: RotationSources;
|
|
31
42
|
/** Stable session key keeps a session on its slot; absent, each request distributes. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation-stream.d.ts","sourceRoot":"","sources":["../../../src/core/credential-pool/rotation-stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"rotation-stream.d.ts","sourceRoot":"","sources":["../../../src/core/credential-pool/rotation-stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAK1F,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,eAAe,CAAC;AACpE,OAAO,EAAwB,KAAK,wBAAwB,EAA4B,MAAM,kBAAkB,CAAC;AAEjH,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,EAAE,UAAsF,CAAC;AAEtH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE5C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,wBAAwB,CAAC;IACrC,MAAM,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACzD,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB,CAAC;AAaF;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,YAAY,EAAE,CAAC,CA2DzB;AAiED,MAAM,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,eAAe,CAAC;IACzB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,CACX,IAAI,EAAE,YAAY,KACd,aAAa,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC;CAC1F,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,yBAAyB,GAChC,cAAc,CAAC,qBAAqB,CAAC,CAmDvC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { rendezvousOrder } from "@earendil-works/pi-ai/auth/pool/select";
|
|
3
3
|
import { listSlots as listCredentialSlots } from "@earendil-works/pi-ai/auth/pool/slots";
|
|
4
|
+
import { resolveConfigValue } from "../resolve-config-value.js";
|
|
4
5
|
import { classifyCredentialFailure } from "./classify.js";
|
|
5
6
|
import { discoverEnvSlots } from "./env-slots.js";
|
|
6
7
|
import { runCredentialFailover } from "./failover.js";
|
|
8
|
+
import { acquireHalfOpenLease } from "./state-store.js";
|
|
7
9
|
/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */
|
|
8
10
|
export const sha256SlotHasher = (input) => createHash("sha256").update(input).digest().readBigUInt64BE(0);
|
|
9
11
|
function overlayState(slot, state) {
|
|
@@ -14,6 +16,7 @@ function overlayState(slot, state) {
|
|
|
14
16
|
...(state.blockedUntil === undefined ? {} : { blockedUntil: state.blockedUntil }),
|
|
15
17
|
...(state.blockReason === undefined ? {} : { blockReason: state.blockReason }),
|
|
16
18
|
...(state.failureCount === undefined ? {} : { failureCount: state.failureCount }),
|
|
19
|
+
...(state.lease === undefined ? {} : { lease: state.lease }),
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
@@ -23,26 +26,87 @@ function overlayState(slot, state) {
|
|
|
23
26
|
* persisted health applies only while its HMAC revision still matches the
|
|
24
27
|
* current env value, so rotating a key in place clears its own stale block.
|
|
25
28
|
*/
|
|
26
|
-
export async function listRotationSlots(sources) {
|
|
29
|
+
export async function listRotationSlots(sources, options = {}) {
|
|
30
|
+
const acquireLeases = options.acquireLeases !== false;
|
|
27
31
|
const { providerId, credential, env, repository } = sources;
|
|
32
|
+
const policySlots = Object.entries(sources.policy?.slots ?? {}).flatMap(([name, ref]) => {
|
|
33
|
+
const envVarName = ref.env ?? `models.json:${name}`;
|
|
34
|
+
const key = ref.env !== undefined ? env(ref.env) : ref.value !== undefined ? resolveConfigValue(ref.value, {}) : undefined;
|
|
35
|
+
if (!key)
|
|
36
|
+
return [];
|
|
37
|
+
return [{ name, envVarName, key, source: "env" }];
|
|
38
|
+
});
|
|
28
39
|
if (credential) {
|
|
29
40
|
const state = await repository.listSlots(providerId, "stored");
|
|
30
|
-
|
|
41
|
+
const slots = [];
|
|
42
|
+
for (const slot of listCredentialSlots(credential)) {
|
|
43
|
+
const current = state[slot.name];
|
|
44
|
+
if (acquireLeases &&
|
|
45
|
+
current?.blockedUntil !== undefined &&
|
|
46
|
+
current.blockedUntil <= (sources.now ?? Date.now)()) {
|
|
47
|
+
const lease = await acquireHalfOpenLease(repository, providerId, "stored", slot.name, {
|
|
48
|
+
now: (sources.now ?? Date.now)(),
|
|
49
|
+
});
|
|
50
|
+
if (!lease)
|
|
51
|
+
continue;
|
|
52
|
+
const leased = await repository.listSlots(providerId, "stored");
|
|
53
|
+
slots.push(overlayState({
|
|
54
|
+
name: slot.name,
|
|
55
|
+
lane: "stored",
|
|
56
|
+
pinned: credential.pinned === slot.name,
|
|
57
|
+
}, leased[slot.name]));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
slots.push(overlayState({
|
|
61
|
+
name: slot.name,
|
|
62
|
+
lane: "stored",
|
|
63
|
+
pinned: credential.pinned === slot.name,
|
|
64
|
+
}, current));
|
|
65
|
+
}
|
|
66
|
+
if (policySlots.length === 0)
|
|
67
|
+
return slots;
|
|
68
|
+
const namedSources = {
|
|
69
|
+
...sources,
|
|
70
|
+
credential: undefined,
|
|
71
|
+
policy: { ...sources.policy, slots: {} },
|
|
72
|
+
};
|
|
73
|
+
const namedSlots = await listEnvRotationSlots(namedSources, policySlots, acquireLeases);
|
|
74
|
+
return [...slots, ...namedSlots];
|
|
31
75
|
}
|
|
32
|
-
const envSlots = discoverEnvSlots(providerId, env);
|
|
76
|
+
const envSlots = [...discoverEnvSlots(providerId, env), ...policySlots];
|
|
77
|
+
return listEnvRotationSlots(sources, envSlots, acquireLeases);
|
|
78
|
+
}
|
|
79
|
+
async function listEnvRotationSlots(sources, envSlots, acquireLeases = true) {
|
|
33
80
|
if (envSlots.length === 0)
|
|
34
81
|
return [];
|
|
82
|
+
const { providerId, repository } = sources;
|
|
35
83
|
const state = await repository.listSlots(providerId, "env");
|
|
36
84
|
const slots = [];
|
|
37
85
|
for (const slot of envSlots) {
|
|
38
86
|
const persisted = state[slot.name];
|
|
39
87
|
const revision = await repository.envCredentialRevision(slot.envVarName, slot.key);
|
|
40
|
-
|
|
41
|
-
|
|
88
|
+
let applicable = persisted?.credentialRevision === revision ? persisted : undefined;
|
|
89
|
+
if (acquireLeases &&
|
|
90
|
+
applicable?.blockedUntil !== undefined &&
|
|
91
|
+
applicable.blockedUntil <= (sources.now ?? Date.now)()) {
|
|
92
|
+
const lease = await acquireHalfOpenLease(repository, providerId, "env", slot.name, {
|
|
93
|
+
now: (sources.now ?? Date.now)(),
|
|
94
|
+
});
|
|
95
|
+
if (!lease)
|
|
96
|
+
continue;
|
|
97
|
+
const leased = await repository.listSlots(providerId, "env");
|
|
98
|
+
applicable = leased[slot.name];
|
|
99
|
+
}
|
|
100
|
+
slots.push(overlayState({
|
|
101
|
+
name: slot.name,
|
|
102
|
+
lane: "env",
|
|
103
|
+
envKey: slot.key,
|
|
104
|
+
envVarName: slot.envVarName,
|
|
105
|
+
}, applicable));
|
|
42
106
|
}
|
|
43
107
|
return slots;
|
|
44
108
|
}
|
|
45
|
-
function blockPatch(block, current, now, credentialRevision) {
|
|
109
|
+
function blockPatch(block, current, now, credentialRevision, policy) {
|
|
46
110
|
const failureCount = (current?.failureCount ?? 0) + 1;
|
|
47
111
|
const base = {
|
|
48
112
|
failureCount,
|
|
@@ -50,7 +114,11 @@ function blockPatch(block, current, now, credentialRevision) {
|
|
|
50
114
|
...(current?.lastSuccessAt === undefined ? {} : { lastSuccessAt: current.lastSuccessAt }),
|
|
51
115
|
};
|
|
52
116
|
if (block.reason === "rate_limit") {
|
|
53
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
...base,
|
|
119
|
+
blockedUntil: now + Math.min(policy?.cooldownCapMs ?? block.cooldownMs, block.cooldownMs),
|
|
120
|
+
blockReason: "rate_limit",
|
|
121
|
+
};
|
|
54
122
|
}
|
|
55
123
|
return { ...base, blockReason: block.reason };
|
|
56
124
|
}
|
|
@@ -70,11 +138,15 @@ export function streamWithCredentialRotation(options) {
|
|
|
70
138
|
const { sources, runAttempt } = options;
|
|
71
139
|
const hasher = options.hasher ?? sha256SlotHasher;
|
|
72
140
|
const affinityKey = options.affinityKey ?? randomUUID();
|
|
141
|
+
const useAffinity = sources.policy?.affinity !== false;
|
|
73
142
|
const now = sources.now ?? Date.now;
|
|
74
143
|
return runCredentialFailover({
|
|
75
144
|
listSlots: () => listRotationSlots(sources),
|
|
76
145
|
select: (candidates) => {
|
|
77
|
-
const
|
|
146
|
+
const pinned = candidates.find((candidate) => candidate.pinned === true);
|
|
147
|
+
if (pinned)
|
|
148
|
+
return pinned;
|
|
149
|
+
const ordered = useAffinity ? rendezvousOrder(affinityKey, candidates, hasher) : candidates;
|
|
78
150
|
const winner = ordered[0];
|
|
79
151
|
if (!winner)
|
|
80
152
|
throw new Error("credential rotation selected from an empty candidate set");
|
|
@@ -83,12 +155,27 @@ export function streamWithCredentialRotation(options) {
|
|
|
83
155
|
runAttempt,
|
|
84
156
|
isCommittedOutput: (event) => event.type !== "start",
|
|
85
157
|
errorFromEvent,
|
|
86
|
-
classify: classifyCredentialFailure,
|
|
158
|
+
classify: (error, context) => classifyCredentialFailure(error, {
|
|
159
|
+
...context,
|
|
160
|
+
cooldownBaseMs: sources.policy?.cooldownBaseMs,
|
|
161
|
+
cooldownCapMs: sources.policy?.cooldownCapMs,
|
|
162
|
+
}),
|
|
163
|
+
onSuccess: async (slot) => {
|
|
164
|
+
await sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) => current
|
|
165
|
+
? {
|
|
166
|
+
...current,
|
|
167
|
+
lastSuccessAt: now(),
|
|
168
|
+
lease: undefined,
|
|
169
|
+
blockedUntil: undefined,
|
|
170
|
+
blockReason: undefined,
|
|
171
|
+
}
|
|
172
|
+
: undefined);
|
|
173
|
+
},
|
|
87
174
|
persistBlock: async (slot, block) => {
|
|
88
175
|
const revision = slot.lane === "env" && slot.envVarName !== undefined && slot.envKey !== undefined
|
|
89
176
|
? await sources.repository.envCredentialRevision(slot.envVarName, slot.envKey)
|
|
90
177
|
: undefined;
|
|
91
|
-
await sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) => blockPatch(block, current, now(), revision));
|
|
178
|
+
await sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) => blockPatch(block, current, now(), revision, sources.policy));
|
|
92
179
|
},
|
|
93
180
|
now,
|
|
94
181
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation-stream.js","sourceRoot":"","sources":["../../../src/core/credential-pool/rotation-stream.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { AssistantMessageEvent, Credential } from \"@earendil-works/pi-ai\";\nimport { rendezvousOrder, type SlotHasher } from \"@earendil-works/pi-ai/auth/pool/select\";\nimport { listSlots as listCredentialSlots } from \"@earendil-works/pi-ai/auth/pool/slots\";\nimport { type CredentialBlock, classifyCredentialFailure } from \"./classify.ts\";\nimport { discoverEnvSlots } from \"./env-slots.ts\";\nimport { type RunSlot, runCredentialFailover } from \"./failover.ts\";\nimport type { CredentialSlotRepository, CredentialSlotState } from \"./state-store.ts\";\n\n/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */\nexport const sha256SlotHasher: SlotHasher = (input) => createHash(\"sha256\").update(input).digest().readBigUInt64BE(0);\n\nexport type RotationLane = \"stored\" | \"env\";\n\nexport type RotationSlot = RunSlot & {\n\tlane: RotationLane;\n\t/** Env-lane key material for the attempt; never serialized or persisted. */\n\tenvKey?: string;\n\tenvVarName?: string;\n};\n\nexport type RotationSources = {\n\tproviderId: string;\n\tcredential: Credential | undefined;\n\tenv: (name: string) => string | undefined;\n\trepository: CredentialSlotRepository;\n\tnow?: () => number;\n};\n\nfunction overlayState(slot: RotationSlot, state: CredentialSlotState | undefined): RotationSlot {\n\tif (!state) return slot;\n\treturn {\n\t\t...slot,\n\t\t...(state.blockedUntil === undefined ? {} : { blockedUntil: state.blockedUntil }),\n\t\t...(state.blockReason === undefined ? {} : { blockReason: state.blockReason }),\n\t\t...(state.failureCount === undefined ? {} : { failureCount: state.failureCount }),\n\t};\n}\n\n/**\n * Lists the provider's rotation slots with sidecar health overlaid. Stored\n * credentials own the lane when present; env slots participate only when\n * nothing is stored, preserving today's resolution precedence. An env slot's\n * persisted health applies only while its HMAC revision still matches the\n * current env value, so rotating a key in place clears its own stale block.\n */\nexport async function listRotationSlots(sources: RotationSources): Promise<RotationSlot[]> {\n\tconst { providerId, credential, env, repository } = sources;\n\tif (credential) {\n\t\tconst state = await repository.listSlots(providerId, \"stored\");\n\t\treturn listCredentialSlots(credential).map((slot) =>\n\t\t\toverlayState({ name: slot.name, lane: \"stored\" }, state[slot.name]),\n\t\t);\n\t}\n\tconst envSlots = discoverEnvSlots(providerId, env);\n\tif (envSlots.length === 0) return [];\n\tconst state = await repository.listSlots(providerId, \"env\");\n\tconst slots: RotationSlot[] = [];\n\tfor (const slot of envSlots) {\n\t\tconst persisted = state[slot.name];\n\t\tconst revision = await repository.envCredentialRevision(slot.envVarName, slot.key);\n\t\tconst applicable = persisted?.credentialRevision === revision ? persisted : undefined;\n\t\tslots.push(\n\t\t\toverlayState({ name: slot.name, lane: \"env\", envKey: slot.key, envVarName: slot.envVarName }, applicable),\n\t\t);\n\t}\n\treturn slots;\n}\n\nfunction blockPatch(\n\tblock: CredentialBlock,\n\tcurrent: CredentialSlotState | undefined,\n\tnow: number,\n\tcredentialRevision?: string,\n): Omit<CredentialSlotState, \"stateVersion\"> {\n\tconst failureCount = (current?.failureCount ?? 0) + 1;\n\tconst base = {\n\t\tfailureCount,\n\t\t...(credentialRevision === undefined ? {} : { credentialRevision }),\n\t\t...(current?.lastSuccessAt === undefined ? {} : { lastSuccessAt: current.lastSuccessAt }),\n\t};\n\tif (block.reason === \"rate_limit\") {\n\t\treturn { ...base, blockedUntil: now + block.cooldownMs, blockReason: \"rate_limit\" };\n\t}\n\treturn { ...base, blockReason: block.reason };\n}\n\nexport type CredentialRotationOptions = {\n\tsources: RotationSources;\n\t/** Stable session key keeps a session on its slot; absent, each request distributes. */\n\taffinityKey?: string;\n\thasher?: SlotHasher;\n\trunAttempt: (\n\t\tslot: RotationSlot,\n\t) => AsyncIterable<AssistantMessageEvent> | Promise<AsyncIterable<AssistantMessageEvent>>;\n};\n\nfunction errorFromEvent(event: AssistantMessageEvent): unknown {\n\tif (event.type !== \"error\") return undefined;\n\tconst message = event.error.errorMessage ?? \"provider stream error\";\n\treturn new Error(message);\n}\n\n/**\n * In-lane credential rotation for one provider request. Selection follows the\n * HRW order for the affinity key; only the `start` bookkeeping event counts as\n * pre-commit, so any delta bars silent rotation (default-DENY) and failures\n * after output carry the turn-retry suppression marker.\n */\nexport function streamWithCredentialRotation(\n\toptions: CredentialRotationOptions,\n): AsyncGenerator<AssistantMessageEvent> {\n\tconst { sources, runAttempt } = options;\n\tconst hasher = options.hasher ?? sha256SlotHasher;\n\tconst affinityKey = options.affinityKey ?? randomUUID();\n\tconst now = sources.now ?? Date.now;\n\n\treturn runCredentialFailover<AssistantMessageEvent, RotationSlot>({\n\t\tlistSlots: () => listRotationSlots(sources),\n\t\tselect: (candidates) => {\n\t\t\tconst ordered = rendezvousOrder(affinityKey, candidates, hasher);\n\t\t\tconst winner = ordered[0];\n\t\t\tif (!winner) throw new Error(\"credential rotation selected from an empty candidate set\");\n\t\t\treturn winner;\n\t\t},\n\t\trunAttempt,\n\t\tisCommittedOutput: (event) => event.type !== \"start\",\n\t\terrorFromEvent,\n\t\tclassify: classifyCredentialFailure,\n\t\tpersistBlock: async (slot, block) => {\n\t\t\tconst revision =\n\t\t\t\tslot.lane === \"env\" && slot.envVarName !== undefined && slot.envKey !== undefined\n\t\t\t\t\t? await sources.repository.envCredentialRevision(slot.envVarName, slot.envKey)\n\t\t\t\t\t: undefined;\n\t\t\tawait sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) =>\n\t\t\t\tblockPatch(block, current, now(), revision),\n\t\t\t);\n\t\t},\n\t\tnow,\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rotation-stream.js","sourceRoot":"","sources":["../../../src/core/credential-pool/rotation-stream.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { AssistantMessageEvent, Credential } from \"@earendil-works/pi-ai\";\nimport { rendezvousOrder, type SlotHasher } from \"@earendil-works/pi-ai/auth/pool/select\";\nimport { listSlots as listCredentialSlots, type PooledCredential } from \"@earendil-works/pi-ai/auth/pool/slots\";\nimport { resolveConfigValue } from \"../resolve-config-value.ts\";\nimport { type CredentialBlock, classifyCredentialFailure } from \"./classify.ts\";\nimport { discoverEnvSlots } from \"./env-slots.ts\";\nimport { type RunSlot, runCredentialFailover } from \"./failover.ts\";\nimport { acquireHalfOpenLease, type CredentialSlotRepository, type CredentialSlotState } from \"./state-store.ts\";\n\n/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */\nexport const sha256SlotHasher: SlotHasher = (input) => createHash(\"sha256\").update(input).digest().readBigUInt64BE(0);\n\nexport type RotationLane = \"stored\" | \"env\";\n\nexport type RotationSlot = RunSlot & {\n\tlane: RotationLane;\n\t/** Env-lane key material for the attempt; never serialized or persisted. */\n\tenvKey?: string;\n\tenvVarName?: string;\n};\n\nexport type RotationSources = {\n\tproviderId: string;\n\tcredential: Credential | undefined;\n\tenv: (name: string) => string | undefined;\n\trepository: CredentialSlotRepository;\n\tpolicy?: {\n\t\taffinity?: boolean;\n\t\tcooldownBaseMs?: number;\n\t\tcooldownCapMs?: number;\n\t\tslots?: Record<string, { env?: string; value?: string }>;\n\t};\n\tnow?: () => number;\n};\n\nfunction overlayState(slot: RotationSlot, state: CredentialSlotState | undefined): RotationSlot {\n\tif (!state) return slot;\n\treturn {\n\t\t...slot,\n\t\t...(state.blockedUntil === undefined ? {} : { blockedUntil: state.blockedUntil }),\n\t\t...(state.blockReason === undefined ? {} : { blockReason: state.blockReason }),\n\t\t...(state.failureCount === undefined ? {} : { failureCount: state.failureCount }),\n\t\t...(state.lease === undefined ? {} : { lease: state.lease }),\n\t};\n}\n\n/**\n * Lists the provider's rotation slots with sidecar health overlaid. Stored\n * credentials own the lane when present; env slots participate only when\n * nothing is stored, preserving today's resolution precedence. An env slot's\n * persisted health applies only while its HMAC revision still matches the\n * current env value, so rotating a key in place clears its own stale block.\n */\nexport async function listRotationSlots(\n\tsources: RotationSources,\n\toptions: { acquireLeases?: boolean } = {},\n): Promise<RotationSlot[]> {\n\tconst acquireLeases = options.acquireLeases !== false;\n\tconst { providerId, credential, env, repository } = sources;\n\tconst policySlots = Object.entries(sources.policy?.slots ?? {}).flatMap(([name, ref]) => {\n\t\tconst envVarName = ref.env ?? `models.json:${name}`;\n\t\tconst key =\n\t\t\tref.env !== undefined ? env(ref.env) : ref.value !== undefined ? resolveConfigValue(ref.value, {}) : undefined;\n\t\tif (!key) return [];\n\t\treturn [{ name, envVarName, key, source: \"env\" as const }];\n\t});\n\tif (credential) {\n\t\tconst state = await repository.listSlots(providerId, \"stored\");\n\t\tconst slots: RotationSlot[] = [];\n\t\tfor (const slot of listCredentialSlots(credential)) {\n\t\t\tconst current = state[slot.name];\n\t\t\tif (\n\t\t\t\tacquireLeases &&\n\t\t\t\tcurrent?.blockedUntil !== undefined &&\n\t\t\t\tcurrent.blockedUntil <= (sources.now ?? Date.now)()\n\t\t\t) {\n\t\t\t\tconst lease = await acquireHalfOpenLease(repository, providerId, \"stored\", slot.name, {\n\t\t\t\t\tnow: (sources.now ?? Date.now)(),\n\t\t\t\t});\n\t\t\t\tif (!lease) continue;\n\t\t\t\tconst leased = await repository.listSlots(providerId, \"stored\");\n\t\t\t\tslots.push(\n\t\t\t\t\toverlayState(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: slot.name,\n\t\t\t\t\t\t\tlane: \"stored\",\n\t\t\t\t\t\t\tpinned: (credential as PooledCredential).pinned === slot.name,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tleased[slot.name],\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tslots.push(\n\t\t\t\toverlayState(\n\t\t\t\t\t{\n\t\t\t\t\t\tname: slot.name,\n\t\t\t\t\t\tlane: \"stored\",\n\t\t\t\t\t\tpinned: (credential as PooledCredential).pinned === slot.name,\n\t\t\t\t\t},\n\t\t\t\t\tcurrent,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (policySlots.length === 0) return slots;\n\t\tconst namedSources: RotationSources = {\n\t\t\t...sources,\n\t\t\tcredential: undefined,\n\t\t\tpolicy: { ...sources.policy, slots: {} },\n\t\t};\n\t\tconst namedSlots = await listEnvRotationSlots(namedSources, policySlots, acquireLeases);\n\t\treturn [...slots, ...namedSlots];\n\t}\n\tconst envSlots = [...discoverEnvSlots(providerId, env), ...policySlots];\n\treturn listEnvRotationSlots(sources, envSlots, acquireLeases);\n}\n\nasync function listEnvRotationSlots(\n\tsources: RotationSources,\n\tenvSlots: readonly { name: string; envVarName: string; key: string }[],\n\tacquireLeases = true,\n): Promise<RotationSlot[]> {\n\tif (envSlots.length === 0) return [];\n\tconst { providerId, repository } = sources;\n\tconst state = await repository.listSlots(providerId, \"env\");\n\tconst slots: RotationSlot[] = [];\n\tfor (const slot of envSlots) {\n\t\tconst persisted = state[slot.name];\n\t\tconst revision = await repository.envCredentialRevision(slot.envVarName, slot.key);\n\t\tlet applicable = persisted?.credentialRevision === revision ? persisted : undefined;\n\t\tif (\n\t\t\tacquireLeases &&\n\t\t\tapplicable?.blockedUntil !== undefined &&\n\t\t\tapplicable.blockedUntil <= (sources.now ?? Date.now)()\n\t\t) {\n\t\t\tconst lease = await acquireHalfOpenLease(repository, providerId, \"env\", slot.name, {\n\t\t\t\tnow: (sources.now ?? Date.now)(),\n\t\t\t});\n\t\t\tif (!lease) continue;\n\t\t\tconst leased = await repository.listSlots(providerId, \"env\");\n\t\t\tapplicable = leased[slot.name];\n\t\t}\n\t\tslots.push(\n\t\t\toverlayState(\n\t\t\t\t{\n\t\t\t\t\tname: slot.name,\n\t\t\t\t\tlane: \"env\",\n\t\t\t\t\tenvKey: slot.key,\n\t\t\t\t\tenvVarName: slot.envVarName,\n\t\t\t\t},\n\t\t\t\tapplicable,\n\t\t\t),\n\t\t);\n\t}\n\treturn slots;\n}\n\nfunction blockPatch(\n\tblock: CredentialBlock,\n\tcurrent: CredentialSlotState | undefined,\n\tnow: number,\n\tcredentialRevision?: string,\n\tpolicy?: { cooldownBaseMs?: number; cooldownCapMs?: number },\n): Omit<CredentialSlotState, \"stateVersion\"> {\n\tconst failureCount = (current?.failureCount ?? 0) + 1;\n\tconst base = {\n\t\tfailureCount,\n\t\t...(credentialRevision === undefined ? {} : { credentialRevision }),\n\t\t...(current?.lastSuccessAt === undefined ? {} : { lastSuccessAt: current.lastSuccessAt }),\n\t};\n\tif (block.reason === \"rate_limit\") {\n\t\treturn {\n\t\t\t...base,\n\t\t\tblockedUntil: now + Math.min(policy?.cooldownCapMs ?? block.cooldownMs, block.cooldownMs),\n\t\t\tblockReason: \"rate_limit\",\n\t\t};\n\t}\n\treturn { ...base, blockReason: block.reason };\n}\n\nexport type CredentialRotationOptions = {\n\tsources: RotationSources;\n\t/** Stable session key keeps a session on its slot; absent, each request distributes. */\n\taffinityKey?: string;\n\thasher?: SlotHasher;\n\trunAttempt: (\n\t\tslot: RotationSlot,\n\t) => AsyncIterable<AssistantMessageEvent> | Promise<AsyncIterable<AssistantMessageEvent>>;\n};\n\nfunction errorFromEvent(event: AssistantMessageEvent): unknown {\n\tif (event.type !== \"error\") return undefined;\n\tconst message = event.error.errorMessage ?? \"provider stream error\";\n\treturn new Error(message);\n}\n\n/**\n * In-lane credential rotation for one provider request. Selection follows the\n * HRW order for the affinity key; only the `start` bookkeeping event counts as\n * pre-commit, so any delta bars silent rotation (default-DENY) and failures\n * after output carry the turn-retry suppression marker.\n */\nexport function streamWithCredentialRotation(\n\toptions: CredentialRotationOptions,\n): AsyncGenerator<AssistantMessageEvent> {\n\tconst { sources, runAttempt } = options;\n\tconst hasher = options.hasher ?? sha256SlotHasher;\n\tconst affinityKey = options.affinityKey ?? randomUUID();\n\tconst useAffinity = sources.policy?.affinity !== false;\n\tconst now = sources.now ?? Date.now;\n\n\treturn runCredentialFailover<AssistantMessageEvent, RotationSlot>({\n\t\tlistSlots: () => listRotationSlots(sources),\n\t\tselect: (candidates) => {\n\t\t\tconst pinned = candidates.find((candidate) => candidate.pinned === true);\n\t\t\tif (pinned) return pinned;\n\t\t\tconst ordered = useAffinity ? rendezvousOrder(affinityKey, candidates, hasher) : candidates;\n\n\t\t\tconst winner = ordered[0];\n\t\t\tif (!winner) throw new Error(\"credential rotation selected from an empty candidate set\");\n\t\t\treturn winner;\n\t\t},\n\t\trunAttempt,\n\t\tisCommittedOutput: (event) => event.type !== \"start\",\n\t\terrorFromEvent,\n\t\tclassify: (error, context) =>\n\t\t\tclassifyCredentialFailure(error, {\n\t\t\t\t...context,\n\t\t\t\tcooldownBaseMs: sources.policy?.cooldownBaseMs,\n\t\t\t\tcooldownCapMs: sources.policy?.cooldownCapMs,\n\t\t\t}),\n\t\tonSuccess: async (slot) => {\n\t\t\tawait sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) =>\n\t\t\t\tcurrent\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...current,\n\t\t\t\t\t\t\tlastSuccessAt: now(),\n\t\t\t\t\t\t\tlease: undefined,\n\t\t\t\t\t\t\tblockedUntil: undefined,\n\t\t\t\t\t\t\tblockReason: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t},\n\t\tpersistBlock: async (slot, block) => {\n\t\t\tconst revision =\n\t\t\t\tslot.lane === \"env\" && slot.envVarName !== undefined && slot.envKey !== undefined\n\t\t\t\t\t? await sources.repository.envCredentialRevision(slot.envVarName, slot.envKey)\n\t\t\t\t\t: undefined;\n\t\t\tawait sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) =>\n\t\t\t\tblockPatch(block, current, now(), revision, sources.policy),\n\t\t\t);\n\t\t},\n\t\tnow,\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAgCrE,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAgCrE,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8X5D"}
|
|
@@ -246,6 +246,13 @@ export default function goalExtension(pi) {
|
|
|
246
246
|
if (!isResumeOfStoppedGoal(ctx, sessionStartReason, goal)) {
|
|
247
247
|
return false;
|
|
248
248
|
}
|
|
249
|
+
// RPC switch_session cannot service a nested interactive UI request until the
|
|
250
|
+
// switch response completes. Keep the goal stopped and let the user resume it
|
|
251
|
+
// explicitly after the new session finishes binding.
|
|
252
|
+
if (ctx.mode === "rpc") {
|
|
253
|
+
ctx.ui.notify(`Goal remains ${goal.status} after session resume. Resume it explicitly after the session finishes loading.`, "info");
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
249
256
|
const choice = await ctx.ui.select(`Resume ${goal.status} goal?\nGoal: ${goal.objective}`, [
|
|
250
257
|
RESUME_GOAL_CHOICE,
|
|
251
258
|
LEAVE_GOAL_STOPPED_CHOICE,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AAGtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,iCAAiC,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAC5G,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAC7F,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAExE,MAAM,kBAAkB,GAAG,aAAa,CAAC;AACzC,MAAM,yBAAyB,GAAG,eAAe,CAAC;AAOlD,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAAgB;IACrD,IAAI,mBAAmB,GAAG,KAAK,CAAC;IAChC,IAAI,6BAA6B,GAAG,KAAK,CAAC;IAC1C,IAAI,mBAAmB,GAA+B,IAAI,CAAC;IAC3D,IAAI,qBAAqB,GAAkB,IAAI,CAAC;IAChD,IAAI,uBAAuB,GAAkB,IAAI,CAAC;IAClD,IAAI,mBAAmB,GAAG,KAAK,CAAC;IAChC,IAAI,aAA2C,CAAC;IAChD,MAAM,SAAS,GAAG,IAAI,gBAAgB,EAAE,CAAC;IACzC,6EAA6E;IAC7E,wEAAwE;IACxE,+DAA+D;IAC/D,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;QACzC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC;KACnF,CAAC,CAAC;IACH,MAAM,mBAAmB,GAAG,IAAI,4BAA4B,CAC3D,EAAE,EACF,GAAG,EAAE,CAAC,mBAAmB,EACzB,GAAG,EAAE;QACJ,mBAAmB,GAAG,IAAI,CAAC;IAC5B,CAAC,EACD,cAAc,CACd,CAAC;IACF,MAAM,oBAAoB,GAAG,IAAI,wBAAwB,CAAC;QACzD,OAAO,EAAE,mBAAmB;QAC5B,YAAY;QACZ,wBAAwB;QACxB,aAAa,EAAE,uBAAuB;QACtC,yBAAyB,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,sCAAsC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC;KAC3G,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC;QACxC,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC;KAClF,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,CAAC,4BAA4B,EAAE,0BAA0B,CAAC,CAAC;IACnF,iBAAiB,CAAC,EAAE,EAAE;QACrB,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC;QACrE,uBAAuB;QACvB,wBAAwB,EAAE,CAAC,IAAI,EAAE,EAAE;YAClC,mBAAmB,CAAC,uBAAuB,EAAE,CAAC;YAC9C,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QACD,uBAAuB;QACvB,yBAAyB;QACzB,aAAa;KACb,CAAC,CAAC;IACH,mBAAmB,CAAC,EAAE,EAAE;QACvB,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC;QACrE,uBAAuB;QACvB,wBAAwB;QACxB,uBAAuB;QACvB,wBAAwB;QACxB,qBAAqB,EAAE,CAAC,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE;YACzD,KAAK,sCAAsC,CAAC,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAC7E,CAAC;QACD,aAAa;KACb,CAAC,CAAC;IAEH,MAAM,2BAA2B,GAChC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,wBAAwB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;YAAE,OAAO;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,aAAa,CAAC;QACtC,IACC,GAAG,KAAK,SAAS;YACjB,GAAG,CAAC,cAAc,CAAC,YAAY,EAAE,KAAK,IAAI,CAAC,QAAQ;YACnD,CAAC,GAAG,CAAC,MAAM,EAAE;YACb,GAAG,CAAC,kBAAkB,EAAE,EACvB,CAAC;YACF,OAAO;QACR,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ;YAAE,OAAO;QACtC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACnC,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAC/B,uBAAuB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACnC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,iCAAiC,EAAE;YAClD,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,OAAO,EAAE,CAAC;YACV,kBAAkB,EAAE,CAAC;YACrB,WAAW,EAAE,EAAE;YACf,MAAM,EAAE,wBAAwB;SAChC,CAAC,CAAC;QACH,MAAM,sCAAsC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7D,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAElB,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC3C,aAAa,GAAG,GAAG,CAAC;QACpB,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,oBAAoB,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,qBAAqB,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACP,wBAAwB,EAAE,CAAC;QAC5B,CAAC;QACD,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,MAAM,4BAA4B,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;YACrE,OAAO;QACR,CAAC;QACD,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACvC,yEAAyE;YACzE,uEAAuE;YACvE,8DAA8D;YAC9D,MAAM,uBAAuB,CAAC;gBAC7B,GAAG;gBACH,IAAI;gBACJ,OAAO,EAAE,mBAAmB;gBAC5B,0BAA0B,EAAE,GAAG,EAAE,CAAC,oCAAoC,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;gBACtG,iBAAiB,EAAE,GAAG,EAAE,CAAC,sCAAsC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC;aAC9E,CAAC,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,EAAE,CAAC;YACjB,2EAA2E;YAC3E,2EAA2E;YAC3E,8EAA8E;YAC9E,MAAM,qBAAqB,GAAG,oCAAoC,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC;YACnG,IAAI,qBAAqB,IAAI,qBAAqB,EAAE,CAAC;gBACpD,uEAAuE;gBACvE,qEAAqE;gBACrE,oEAAoE;gBACpE,qEAAqE;gBACrE,iEAAiE;gBACjE,oBAAoB,CAAC,uBAAuB,EAAE,CAAC;gBAC/C,GAAG,CAAC,EAAE,CAAC,MAAM,CACZ,+DAA+D,qBAAqB,uDAAuD,EAC3I,MAAM,CACN,CAAC;YACH,CAAC;iBAAM,CAAC;gBACP,MAAM,sCAAsC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7D,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnC,MAAM,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC/C,MAAM,oBAAoB,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;QAC9B,6BAA6B,GAAG,KAAK,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,4EAA4E;IAC5E,6EAA6E;IAC7E,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,6BAA6B;YAAE,OAAO,SAAS,CAAC;QAClG,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,OAAO,SAAS,CAAC;QAC9D,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC7C,6BAA6B,GAAG,IAAI,CAAC;QACrC,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAC1C,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;QAChD,mBAAmB,GAAG,KAAK,CAAC;QAC5B,IAAI,mBAAmB;YAAE,mBAAmB,CAAC,uBAAuB,EAAE,CAAC;QACvE,mBAAmB,GAAG,IAAI,CAAC;QAC3B,qBAAqB,GAAG,IAAI,CAAC;QAC7B,uBAAuB,GAAG,IAAI,CAAC;QAC/B,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACP,mBAAmB,GAAG,IAAI,CAAC;QAC5B,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACpC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACvC,MAAM,IAAI,GACT,qBAAqB,KAAK,IAAI;YAC7B,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,uBAAuB,KAAK,IAAI;gBACjC,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,kBAAkB,CAAC;QACxB,IAAI,IAAI,GAAG,MAAM,uBAAuB,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpE,mBAAmB,GAAG,KAAK,CAAC;QAC5B,qBAAqB,GAAG,IAAI,CAAC;QAC7B,uBAAuB,GAAG,IAAI,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzF,IAAI,GAAG,MAAM,UAAU,CACtB,YAAY,CAAC,GAAG,CAAC,EACjB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,2BAA2B,EAAE,EAC1D,OAAO,CACP,CAAC;QACH,CAAC;QACD,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACP,wBAAwB,EAAE,CAAC;QAC5B,CAAC;QACD,uBAAuB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,gBAAgB,GAAG,MAAM,yBAAyB,CAAC,mBAAmB,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpG,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,GAAG,gBAAgB,CAAC;YACxB,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAC5C,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,iBAAiB,EAAE,CAAC;QAC3D,IAAI,IAAI,KAAK,SAAS;YAAE,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAC5C,mBAAmB,GAAG,KAAK,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ;YAAE,OAAO;QACtC,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC/D,IAAI,SAAS,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,MAAM,UAAU,CAC/B,YAAY,CAAC,GAAG,CAAC,EACjB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,2BAA2B,EAAE,EAC1D,OAAO,CACP,CAAC;YACF,wBAAwB,EAAE,CAAC;YAC3B,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAC/C,aAAa,GAAG,SAAS,CAAC;QAC1B,2BAA2B,EAAE,CAAC;QAC9B,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,uBAAuB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;QACD,wBAAwB,EAAE,CAAC;QAC3B,UAAU,CAAC,IAAI,EAAE,CAAC;QAClB,mBAAmB,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,KAAK,UAAU,4BAA4B,CAC1C,EAAgB,EAChB,GAAqB,EACrB,kBAA0B,EAC1B,IAAiB;QAEjB,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC;YAC3D,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,MAAM,iBAAiB,IAAI,CAAC,SAAS,EAAE,EAAE;YAC1F,kBAAkB;YAClB,yBAAyB;SACzB,CAAC,CAAC;QACH,IAAI,MAAM,KAAK,kBAAkB;YAAE,OAAO,IAAI,CAAC;QAE/C,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;QAClF,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAClC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAChG,MAAM,sCAAsC,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,UAAU,sCAAsC,CACpD,YAA0B,EAC1B,GAAqB,EACrB,IAAU;QAEV,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE;YAC1E,mBAAmB;YACnB,uBAAuB,EAAE,GAAG,EAAE;gBAC7B,mBAAmB,GAAG,IAAI,CAAC;YAC5B,CAAC;SACD,CAAC,CAAC;QACH,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC5B,wBAAwB,EAAE,CAAC;YAC3B,uBAAuB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACnC,OAAO;QACR,CAAC;QACD,IAAI,aAAa,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO;QACjD,IAAI,aAAa,CAAC,MAAM,KAAK,QAAQ;YAAE,wBAAwB,CAAC,aAAa,CAAC,CAAC;;YAC1E,wBAAwB,EAAE,CAAC;QAChC,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC7C,CAAC;IAED,SAAS,wBAAwB,CAAC,IAAU;QAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO;QACrC,IAAI,mBAAmB,EAAE,MAAM,KAAK,IAAI,CAAC,EAAE;YAAE,OAAO;QACpD,SAAS,CAAC,cAAc,EAAE,CAAC;QAC3B,mBAAmB,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,wBAAwB,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,SAAS,uBAAuB,CAAC,IAAU;QAC1C,IAAI,mBAAmB;YAAE,qBAAqB,GAAG,IAAI,CAAC,EAAE,CAAC;IAC1D,CAAC;IAED,SAAS,yBAAyB,CAAC,IAAU;QAC5C,IAAI,CAAC,mBAAmB;YAAE,OAAO;QACjC,uBAAuB,GAAG,IAAI,CAAC,EAAE,CAAC;QAClC,mBAAmB,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,wBAAwB,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,SAAS,uBAAuB,CAAC,MAAc;QAC9C,IAAI,mBAAmB,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5C,mBAAmB,GAAG,IAAI,CAAC;QAC5B,CAAC;QACD,IAAI,qBAAqB,KAAK,MAAM,EAAE,CAAC;YACtC,qBAAqB,GAAG,IAAI,CAAC;QAC9B,CAAC;QACD,IAAI,uBAAuB,KAAK,MAAM,EAAE,CAAC;YACxC,uBAAuB,GAAG,IAAI,CAAC;QAChC,CAAC;IACF,CAAC;IAED,SAAS,wBAAwB;QAChC,mBAAmB,GAAG,IAAI,CAAC;QAC3B,qBAAqB,GAAG,IAAI,CAAC;QAC7B,uBAAuB,GAAG,IAAI,CAAC;IAChC,CAAC;IAED,SAAS,oBAAoB,CAAC,GAAqB,EAAE,IAAiB;QACrE,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ;YAAE,wBAAwB,CAAC,IAAI,CAAC,CAAC;;YACzD,wBAAwB,EAAE,CAAC;QAChC,uBAAuB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,aAAa,CAAC,GAAqB,EAAE,IAAiB;QAC9D,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,UAAU,GAAG,mBAAmB,CAAC;QACvC,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ,IAAI,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YAC9E,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,wBAAwB,CAAC,CAAC;YAChE,OAAO;QACR,CAAC;QACD,UAAU,CAAC,IAAI,EAAE,CAAC;QAClB,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,SAAS,uBAAuB,CAAC,GAAqB,EAAE,IAAiB;QACxE,IAAI,CAAC;YACJ,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,4BAA4B,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzC,OAAO;YACR,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAED,KAAK,UAAU,uBAAuB,CACrC,GAAqB,EACrB,IAAwB,EACxB,gBAA4B;QAE5B,MAAM,UAAU,GAAG,mBAAmB,CAAC;QACvC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,UAAU,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;QAE9C,MAAM,KAAK,GACV,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;QACtG,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACnG,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACzF,IAAI,IAAI,EAAE,EAAE,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;YACpC,mBAAmB,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,wBAAwB,EAAE,GAAG,EAAE,CAAC;QACpF,CAAC;aAAM,CAAC;YACP,wBAAwB,EAAE,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,SAAS,oCAAoC,CAAC,OAAgC;IAC7E,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,KAAK,EAAE,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;YAAE,MAAM;QACtE,IAAI,KAAK,EAAE,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,UAAU,KAAK,8BAA8B;YAAE,KAAK,IAAI,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,GAAqB;IAC1C,OAAO,iBAAiB,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AACvD,CAAC","sourcesContent":["import { GOAL_CONTINUATION_MESSAGE_TYPE } from \"../../../messages.ts\";\nimport type { SessionEntry } from \"../../../session-manager.ts\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { continueGoalAfterAgentEnd } from \"./agent-end-continuation.ts\";\nimport { GOAL_CACHE_WARMUP_ENTRY_TYPE } from \"./cache-warm.ts\";\nimport { renderGoalCacheWarmupEntry } from \"./cache-warm-renderer.ts\";\nimport { registerGoalCommand } from \"./command-registration.ts\";\nimport { GOAL_CONTINUATION_CAP } from \"./continuation.ts\";\nimport { GoalDirectInputLifecycle } from \"./direct-input-lifecycle.ts\";\nimport { GoalElapsedTicker } from \"./elapsed-ticker.ts\";\nimport { formatGoalForTool, goalStatusLabel } from \"./format.ts\";\nimport { isResumeOfStoppedGoal, queueGoalContinuation } from \"./lifecycle-helpers.ts\";\nimport { GOAL_CONTINUATION_SCHEDULED_EVENT, MonitorAwareGoalContinuation } from \"./monitor-continuation.ts\";\nimport { migrateLegacyGoalFile } from \"./persistence.ts\";\nimport { reengageGoalAfterReload } from \"./reload-reengagement.ts\";\nimport { isStaleExtensionContextError } from \"./stale-context.ts\";\nimport { accountGoalUsage, readGoal, updateGoal } from \"./store.ts\";\nimport { GOAL_STORE_CHANGED_EVENT, isGoalStoreChangedEvent } from \"./store-changed-event.ts\";\nimport { goalStoreRef as buildGoalStoreRef } from \"./store-ref.ts\";\nimport { staleGoalTodoReminder, todoResultAddsOpenTasks } from \"./todo-gate.ts\";\nimport { registerGoalTools } from \"./tool-registration.ts\";\nimport { TurnUsageTracker } from \"./turn-usage.ts\";\nimport type { Goal, GoalAccountingMode, GoalStoreRef } from \"./types.ts\";\nimport { updateGoalUi } from \"./ui.ts\";\nimport { GOAL_WAIT_STATUS_KEY, GoalWaitTicker } from \"./wait-ticker.ts\";\n\nconst RESUME_GOAL_CHOICE = \"Resume goal\";\nconst LEAVE_GOAL_STOPPED_CHOICE = \"Leave stopped\";\n\ntype AgentGoalAccounting = {\n\tgoalId: string;\n\tmeasuredFromMilliseconds: number;\n};\n\nexport default function goalExtension(pi: ExtensionAPI): void {\n\tlet agentTurnInProgress = false;\n\tlet staleGoalReminderSentThisTurn = false;\n\tlet agentGoalAccounting: AgentGoalAccounting | null = null;\n\tlet blockedThisTurnGoalId: string | null = null;\n\tlet completedThisTurnGoalId: string | null = null;\n\tlet continuationPending = false;\n\tlet activeContext: ExtensionContext | undefined;\n\tconst turnUsage = new TurnUsageTracker();\n\t// No stale-ctx swallow here: GoalWaitTicker retires itself on a stale render\n\t// (a ticker that keeps ticking against a retired ctx freezes the footer\n\t// countdown forever); non-stale render errors still propagate.\n\tconst goalWaitTicker = new GoalWaitTicker({\n\t\trender: (renderCtx, status) => renderCtx.ui.setStatus(GOAL_WAIT_STATUS_KEY, status),\n\t});\n\tconst monitorContinuation = new MonitorAwareGoalContinuation(\n\t\tpi,\n\t\t() => continuationPending,\n\t\t() => {\n\t\t\tcontinuationPending = true;\n\t\t},\n\t\tgoalWaitTicker,\n\t);\n\tconst directInputLifecycle = new GoalDirectInputLifecycle({\n\t\tmonitor: monitorContinuation,\n\t\tgoalStoreRef,\n\t\tbeginAgentGoalAccounting,\n\t\trefreshGoalUi: refreshGoalUiBestEffort,\n\t\tresumeAfterSuppressedLoad: (resumeCtx, goal) => queueGoalContinuationForCurrentSession(pi, resumeCtx, goal),\n\t});\n\n\tconst goalTicker = new GoalElapsedTicker({\n\t\trender: (renderCtx, renderGoal, live) => updateGoalUi(renderCtx, renderGoal, live),\n\t});\n\n\tpi.registerEntryRenderer(GOAL_CACHE_WARMUP_ENTRY_TYPE, renderGoalCacheWarmupEntry);\n\tregisterGoalTools(pi, {\n\t\tgoalStoreRef: (ctx) => buildGoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\taccountCurrentAgentTurn,\n\t\tbeginAgentGoalAccounting: (goal) => {\n\t\t\tmonitorContinuation.noteContinuationStarted();\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t},\n\t\tmarkGoalBlockedThisTurn,\n\t\tmarkGoalCompletedThisTurn,\n\t\trefreshGoalUi,\n\t});\n\tregisterGoalCommand(pi, {\n\t\tgoalStoreRef: (ctx) => buildGoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\taccountCurrentAgentTurn,\n\t\tbeginAgentGoalAccounting,\n\t\tstopAgentGoalAccounting,\n\t\tclearAgentGoalAccounting,\n\t\tqueueGoalContinuation: (extensionApi, commandCtx, goal) => {\n\t\t\tvoid queueGoalContinuationForCurrentSession(extensionApi, commandCtx, goal);\n\t\t},\n\t\trefreshGoalUi,\n\t});\n\n\tconst unsubscribeGoalStoreChanged =\n\t\tpi.events?.on(GOAL_STORE_CHANGED_EVENT, async (data) => {\n\t\t\tif (!isGoalStoreChangedEvent(data)) return;\n\t\t\tconst ctx = data.ctx ?? activeContext;\n\t\t\tif (\n\t\t\t\tctx === undefined ||\n\t\t\t\tctx.sessionManager.getSessionId() !== data.threadId ||\n\t\t\t\t!ctx.isIdle() ||\n\t\t\t\tctx.hasPendingMessages()\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\t\tif (goal?.status !== \"active\") return;\n\t\t\tmonitorContinuation.syncGoal(goal);\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t\trefreshGoalUiBestEffort(ctx, goal);\n\t\t\tpi.events?.emit(GOAL_CONTINUATION_SCHEDULED_EVENT, {\n\t\t\t\tgoalId: goal.id,\n\t\t\t\tdelayMs: 0,\n\t\t\t\tactiveMonitorCount: 0,\n\t\t\t\twakeSources: {},\n\t\t\t\treason: GOAL_STORE_CHANGED_EVENT,\n\t\t\t});\n\t\t\tawait queueGoalContinuationForCurrentSession(pi, ctx, goal);\n\t\t}) ?? (() => {});\n\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tactiveContext = ctx;\n\t\tmonitorContinuation.start(ctx);\n\t\tdirectInputLifecycle.reset();\n\t\tconst ref = goalStoreRef(ctx);\n\t\tawait migrateLegacyGoalFile(ref);\n\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\tif (goal?.status === \"active\") {\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t} else {\n\t\t\tclearAgentGoalAccounting();\n\t\t}\n\t\trefreshGoalUi(ctx, goal);\n\t\tif (await maybePromptResumeStoppedGoal(pi, ctx, event.reason, goal)) {\n\t\t\treturn;\n\t\t}\n\t\tif (goal && event.reason === \"reload\") {\n\t\t\t// A reload retires the extension generation and every armed continuation\n\t\t\t// timer with it. Re-engage through the reload path: stopped goals stay\n\t\t\t// stopped, active goals resume the wait the reload tore down.\n\t\t\tawait reengageGoalAfterReload({\n\t\t\t\tctx,\n\t\t\t\tgoal,\n\t\t\t\tmonitor: monitorContinuation,\n\t\t\t\tcountTrailingContinuations: () => countTrailingGoalContinuationEntries(ctx.sessionManager.getBranch()),\n\t\t\t\tqueueContinuation: () => queueGoalContinuationForCurrentSession(pi, ctx, goal),\n\t\t\t});\n\t\t} else if (goal) {\n\t\t\t// Migration-lite admission: a resumed session carrying a trailing flood of\n\t\t\t// historical continuations must not reignite on load. Skip the auto-queue,\n\t\t\t// leave the goal active (no status rewrite), and tell the user how to resume.\n\t\t\tconst trailingContinuations = countTrailingGoalContinuationEntries(ctx.sessionManager.getBranch());\n\t\t\tif (trailingContinuations >= GOAL_CONTINUATION_CAP) {\n\t\t\t\t// The load-time flood suppression parks the goal without rewriting its\n\t\t\t\t// status, so the only resume signal left is the user's next message.\n\t\t\t\t// Arm the one-shot latch the direct-input lifecycle fires when that\n\t\t\t\t// message is accepted; without it the promise in the notice is a lie\n\t\t\t\t// and the resumed session sits idle until the goal is recreated.\n\t\t\t\tdirectInputLifecycle.armSuppressedLoadResume();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Goal auto-continuation suppressed for this resumed session (${trailingContinuations} historical continuations). Send a message to resume.`,\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait queueGoalContinuationForCurrentSession(pi, ctx, goal);\n\t\t\t}\n\t\t}\n\t});\n\n\tpi.on(\"input\", async (event, ctx) => {\n\t\tawait directInputLifecycle.onInput(event, ctx);\n\t});\n\n\tpi.on(\"input_disposition\", async (event, ctx) => {\n\t\tawait directInputLifecycle.onDisposition(event, ctx);\n\t});\n\n\tpi.on(\"turn_start\", async () => {\n\t\tstaleGoalReminderSentThisTurn = false;\n\t});\n\n\t// When the model starts tracking new open todo work while the thread has no\n\t// goal (or only a stale, already-complete one), append a system reminder to\n\t// the todo result so the model registers the goal when the work warrants it.\n\tpi.on(\"tool_result\", async (event, ctx) => {\n\t\tif (event.toolName !== \"todo\" || event.isError || staleGoalReminderSentThisTurn) return undefined;\n\t\tif (!todoResultAddsOpenTasks(event.details)) return undefined;\n\t\tconst reminder = staleGoalTodoReminder(await readGoal(goalStoreRef(ctx)));\n\t\tif (reminder === undefined) return undefined;\n\t\tstaleGoalReminderSentThisTurn = true;\n\t\treturn { content: [...event.content, { type: \"text\" as const, text: reminder }] };\n\t});\n\n\tpi.on(\"agent_start\", async (_event, ctx) => {\n\t\tconst continuationStarted = continuationPending;\n\t\tcontinuationPending = false;\n\t\tif (continuationStarted) monitorContinuation.noteContinuationStarted();\n\t\tagentTurnInProgress = true;\n\t\tblockedThisTurnGoalId = null;\n\t\tcompletedThisTurnGoalId = null;\n\t\tturnUsage.reset();\n\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\tif (goal?.status === \"active\") {\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t} else {\n\t\t\tagentGoalAccounting = null;\n\t\t}\n\t});\n\n\tpi.on(\"message_end\", async (event) => {\n\t\tturnUsage.noteMessageEnd(event.message);\n\t});\n\n\tpi.on(\"agent_end\", async (event, ctx) => {\n\t\tconst mode: GoalAccountingMode =\n\t\t\tblockedThisTurnGoalId !== null\n\t\t\t\t? \"activeOrBlocked\"\n\t\t\t\t: completedThisTurnGoalId === null\n\t\t\t\t\t? \"active\"\n\t\t\t\t\t: \"activeOrComplete\";\n\t\tlet goal = await accountCurrentAgentTurn(ctx, mode, event.messages);\n\t\tagentTurnInProgress = false;\n\t\tblockedThisTurnGoalId = null;\n\t\tcompletedThisTurnGoalId = null;\n\t\tif (event.aborted === true && event.abortSource === \"user\" && goal?.status === \"active\") {\n\t\t\tgoal = await updateGoal(\n\t\t\t\tgoalStoreRef(ctx),\n\t\t\t\t{ status: \"blocked\", reason: \"user interrupted the turn\" },\n\t\t\t\t\"model\",\n\t\t\t);\n\t\t}\n\t\tif (goal?.status === \"active\") {\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t} else {\n\t\t\tclearAgentGoalAccounting();\n\t\t}\n\t\trefreshGoalUiBestEffort(ctx, goal);\n\t\tconst continuationGoal = await continueGoalAfterAgentEnd(monitorContinuation, { ctx, event, goal });\n\t\tif (continuationGoal !== goal) {\n\t\t\tgoal = continuationGoal;\n\t\t\tsyncContinuationGoal(ctx, goal);\n\t\t}\n\t});\n\n\tpi.on(\"agent_settled\", async (_event, ctx) => {\n\t\tconst goal = await monitorContinuation.afterAgentSettled();\n\t\tif (goal !== undefined) syncContinuationGoal(ctx, goal);\n\t});\n\n\tpi.on(\"session_abort\", async (_event, ctx) => {\n\t\tcontinuationPending = false;\n\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\tif (goal?.status !== \"active\") return;\n\t\tconst accounted = await accountCurrentAgentTurn(ctx, \"active\");\n\t\tif (accounted?.status === \"active\") {\n\t\t\tconst blocked = await updateGoal(\n\t\t\t\tgoalStoreRef(ctx),\n\t\t\t\t{ status: \"blocked\", reason: \"user interrupted the turn\" },\n\t\t\t\t\"model\",\n\t\t\t);\n\t\t\tclearAgentGoalAccounting();\n\t\t\trefreshGoalUiBestEffort(ctx, blocked);\n\t\t}\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tactiveContext = undefined;\n\t\tunsubscribeGoalStoreChanged();\n\t\tif (agentGoalAccounting !== null) {\n\t\t\tawait accountCurrentAgentTurn(ctx, \"active\");\n\t\t}\n\t\tclearAgentGoalAccounting();\n\t\tgoalTicker.stop();\n\t\tmonitorContinuation.dispose();\n\t});\n\n\tasync function maybePromptResumeStoppedGoal(\n\t\tpi: ExtensionAPI,\n\t\tctx: ExtensionContext,\n\t\tsessionStartReason: string,\n\t\tgoal: Goal | null,\n\t): Promise<boolean> {\n\t\tif (!isResumeOfStoppedGoal(ctx, sessionStartReason, goal)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst choice = await ctx.ui.select(`Resume ${goal.status} goal?\\nGoal: ${goal.objective}`, [\n\t\t\tRESUME_GOAL_CHOICE,\n\t\t\tLEAVE_GOAL_STOPPED_CHOICE,\n\t\t]);\n\t\tif (choice !== RESUME_GOAL_CHOICE) return true;\n\n\t\tconst resumed = await updateGoal(goalStoreRef(ctx), { status: \"active\" }, \"user\");\n\t\tbeginAgentGoalAccounting(resumed);\n\t\trefreshGoalUi(ctx, resumed);\n\t\tctx.ui.notify(`Goal ${goalStatusLabel(resumed.status)}\\n${formatGoalForTool(resumed)}`, \"info\");\n\t\tawait queueGoalContinuationForCurrentSession(pi, ctx, resumed);\n\t\treturn true;\n\t}\n\n\tasync function queueGoalContinuationForCurrentSession(\n\t\textensionApi: ExtensionAPI,\n\t\tctx: ExtensionContext,\n\t\tgoal: Goal,\n\t): Promise<void> {\n\t\tconst continuedGoal = await queueGoalContinuation(extensionApi, ctx, goal, {\n\t\t\tcontinuationPending,\n\t\t\tmarkContinuationPending: () => {\n\t\t\t\tcontinuationPending = true;\n\t\t\t},\n\t\t});\n\t\tif (continuedGoal === null) {\n\t\t\tclearAgentGoalAccounting();\n\t\t\trefreshGoalUiBestEffort(ctx, null);\n\t\t\treturn;\n\t\t}\n\t\tif (continuedGoal.status === goal.status) return;\n\t\tif (continuedGoal.status === \"active\") beginAgentGoalAccounting(continuedGoal);\n\t\telse clearAgentGoalAccounting();\n\t\trefreshGoalUiBestEffort(ctx, continuedGoal);\n\t}\n\n\tfunction beginAgentGoalAccounting(goal: Goal): void {\n\t\tif (goal.status !== \"active\") return;\n\t\tif (agentGoalAccounting?.goalId === goal.id) return;\n\t\tturnUsage.discardPending();\n\t\tagentGoalAccounting = { goalId: goal.id, measuredFromMilliseconds: Date.now() };\n\t}\n\n\tfunction markGoalBlockedThisTurn(goal: Goal): void {\n\t\tif (agentTurnInProgress) blockedThisTurnGoalId = goal.id;\n\t}\n\n\tfunction markGoalCompletedThisTurn(goal: Goal): void {\n\t\tif (!agentTurnInProgress) return;\n\t\tcompletedThisTurnGoalId = goal.id;\n\t\tagentGoalAccounting = { goalId: goal.id, measuredFromMilliseconds: Date.now() };\n\t}\n\n\tfunction stopAgentGoalAccounting(goalId: string): void {\n\t\tif (agentGoalAccounting?.goalId === goalId) {\n\t\t\tagentGoalAccounting = null;\n\t\t}\n\t\tif (blockedThisTurnGoalId === goalId) {\n\t\t\tblockedThisTurnGoalId = null;\n\t\t}\n\t\tif (completedThisTurnGoalId === goalId) {\n\t\t\tcompletedThisTurnGoalId = null;\n\t\t}\n\t}\n\n\tfunction clearAgentGoalAccounting(): void {\n\t\tagentGoalAccounting = null;\n\t\tblockedThisTurnGoalId = null;\n\t\tcompletedThisTurnGoalId = null;\n\t}\n\n\tfunction syncContinuationGoal(ctx: ExtensionContext, goal: Goal | null): void {\n\t\tif (goal?.status === \"active\") beginAgentGoalAccounting(goal);\n\t\telse clearAgentGoalAccounting();\n\t\trefreshGoalUiBestEffort(ctx, goal);\n\t}\n\n\tfunction refreshGoalUi(ctx: ExtensionContext, goal: Goal | null): void {\n\t\tmonitorContinuation.syncGoal(goal);\n\t\tconst accounting = agentGoalAccounting;\n\t\tif (ctx.hasUI && goal?.status === \"active\" && accounting?.goalId === goal.id) {\n\t\t\tgoalTicker.sync(ctx, goal, accounting.measuredFromMilliseconds);\n\t\t\treturn;\n\t\t}\n\t\tgoalTicker.stop();\n\t\tupdateGoalUi(ctx, goal);\n\t}\n\n\tfunction refreshGoalUiBestEffort(ctx: ExtensionContext, goal: Goal | null): void {\n\t\ttry {\n\t\t\trefreshGoalUi(ctx, goal);\n\t\t} catch (error) {\n\t\t\tif (isStaleExtensionContextError(error)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync function accountCurrentAgentTurn(\n\t\tctx: ExtensionContext,\n\t\tmode: GoalAccountingMode,\n\t\tagentRunMessages?: unknown[],\n\t): Promise<Goal | null> {\n\t\tconst accounting = agentGoalAccounting;\n\t\tconst ref = goalStoreRef(ctx);\n\t\tif (accounting === null) return readGoal(ref);\n\n\t\tconst usage =\n\t\t\tagentRunMessages === undefined ? turnUsage.takePending() : turnUsage.takeRemaining(agentRunMessages);\n\t\tconst now = Date.now();\n\t\tconst elapsedSeconds = Math.max(0, Math.round((now - accounting.measuredFromMilliseconds) / 1000));\n\t\tconst goal = await accountGoalUsage(ref, usage, elapsedSeconds, mode, accounting.goalId);\n\t\tif (goal?.id === accounting.goalId) {\n\t\t\tagentGoalAccounting = { goalId: accounting.goalId, measuredFromMilliseconds: now };\n\t\t} else {\n\t\t\tclearAgentGoalAccounting();\n\t\t}\n\t\treturn goal;\n\t}\n}\n\n/**\n * Counts goal-continuation entries queued since the most recent real user message\n * in the current branch (mirrors the todo-gate / todo-bridge backward branch read).\n * A real user message is the only reset: the assistant turns in between are exactly\n * the unattended loop this admission guard exists to stop.\n */\nfunction countTrailingGoalContinuationEntries(entries: readonly SessionEntry[]): number {\n\tlet count = 0;\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"message\" && entry.message.role === \"user\") break;\n\t\tif (entry?.type === \"custom_message\" && entry.customType === GOAL_CONTINUATION_MESSAGE_TYPE) count += 1;\n\t}\n\treturn count;\n}\n\nfunction goalStoreRef(ctx: ExtensionContext): GoalStoreRef {\n\treturn buildGoalStoreRef(ctx.sessionManager, ctx.cwd);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { GOAL_CONTINUATION_MESSAGE_TYPE } from \"../../../messages.ts\";\nimport type { SessionEntry } from \"../../../session-manager.ts\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { continueGoalAfterAgentEnd } from \"./agent-end-continuation.ts\";\nimport { GOAL_CACHE_WARMUP_ENTRY_TYPE } from \"./cache-warm.ts\";\nimport { renderGoalCacheWarmupEntry } from \"./cache-warm-renderer.ts\";\nimport { registerGoalCommand } from \"./command-registration.ts\";\nimport { GOAL_CONTINUATION_CAP } from \"./continuation.ts\";\nimport { GoalDirectInputLifecycle } from \"./direct-input-lifecycle.ts\";\nimport { GoalElapsedTicker } from \"./elapsed-ticker.ts\";\nimport { formatGoalForTool, goalStatusLabel } from \"./format.ts\";\nimport { isResumeOfStoppedGoal, queueGoalContinuation } from \"./lifecycle-helpers.ts\";\nimport { GOAL_CONTINUATION_SCHEDULED_EVENT, MonitorAwareGoalContinuation } from \"./monitor-continuation.ts\";\nimport { migrateLegacyGoalFile } from \"./persistence.ts\";\nimport { reengageGoalAfterReload } from \"./reload-reengagement.ts\";\nimport { isStaleExtensionContextError } from \"./stale-context.ts\";\nimport { accountGoalUsage, readGoal, updateGoal } from \"./store.ts\";\nimport { GOAL_STORE_CHANGED_EVENT, isGoalStoreChangedEvent } from \"./store-changed-event.ts\";\nimport { goalStoreRef as buildGoalStoreRef } from \"./store-ref.ts\";\nimport { staleGoalTodoReminder, todoResultAddsOpenTasks } from \"./todo-gate.ts\";\nimport { registerGoalTools } from \"./tool-registration.ts\";\nimport { TurnUsageTracker } from \"./turn-usage.ts\";\nimport type { Goal, GoalAccountingMode, GoalStoreRef } from \"./types.ts\";\nimport { updateGoalUi } from \"./ui.ts\";\nimport { GOAL_WAIT_STATUS_KEY, GoalWaitTicker } from \"./wait-ticker.ts\";\n\nconst RESUME_GOAL_CHOICE = \"Resume goal\";\nconst LEAVE_GOAL_STOPPED_CHOICE = \"Leave stopped\";\n\ntype AgentGoalAccounting = {\n\tgoalId: string;\n\tmeasuredFromMilliseconds: number;\n};\n\nexport default function goalExtension(pi: ExtensionAPI): void {\n\tlet agentTurnInProgress = false;\n\tlet staleGoalReminderSentThisTurn = false;\n\tlet agentGoalAccounting: AgentGoalAccounting | null = null;\n\tlet blockedThisTurnGoalId: string | null = null;\n\tlet completedThisTurnGoalId: string | null = null;\n\tlet continuationPending = false;\n\tlet activeContext: ExtensionContext | undefined;\n\tconst turnUsage = new TurnUsageTracker();\n\t// No stale-ctx swallow here: GoalWaitTicker retires itself on a stale render\n\t// (a ticker that keeps ticking against a retired ctx freezes the footer\n\t// countdown forever); non-stale render errors still propagate.\n\tconst goalWaitTicker = new GoalWaitTicker({\n\t\trender: (renderCtx, status) => renderCtx.ui.setStatus(GOAL_WAIT_STATUS_KEY, status),\n\t});\n\tconst monitorContinuation = new MonitorAwareGoalContinuation(\n\t\tpi,\n\t\t() => continuationPending,\n\t\t() => {\n\t\t\tcontinuationPending = true;\n\t\t},\n\t\tgoalWaitTicker,\n\t);\n\tconst directInputLifecycle = new GoalDirectInputLifecycle({\n\t\tmonitor: monitorContinuation,\n\t\tgoalStoreRef,\n\t\tbeginAgentGoalAccounting,\n\t\trefreshGoalUi: refreshGoalUiBestEffort,\n\t\tresumeAfterSuppressedLoad: (resumeCtx, goal) => queueGoalContinuationForCurrentSession(pi, resumeCtx, goal),\n\t});\n\n\tconst goalTicker = new GoalElapsedTicker({\n\t\trender: (renderCtx, renderGoal, live) => updateGoalUi(renderCtx, renderGoal, live),\n\t});\n\n\tpi.registerEntryRenderer(GOAL_CACHE_WARMUP_ENTRY_TYPE, renderGoalCacheWarmupEntry);\n\tregisterGoalTools(pi, {\n\t\tgoalStoreRef: (ctx) => buildGoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\taccountCurrentAgentTurn,\n\t\tbeginAgentGoalAccounting: (goal) => {\n\t\t\tmonitorContinuation.noteContinuationStarted();\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t},\n\t\tmarkGoalBlockedThisTurn,\n\t\tmarkGoalCompletedThisTurn,\n\t\trefreshGoalUi,\n\t});\n\tregisterGoalCommand(pi, {\n\t\tgoalStoreRef: (ctx) => buildGoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\taccountCurrentAgentTurn,\n\t\tbeginAgentGoalAccounting,\n\t\tstopAgentGoalAccounting,\n\t\tclearAgentGoalAccounting,\n\t\tqueueGoalContinuation: (extensionApi, commandCtx, goal) => {\n\t\t\tvoid queueGoalContinuationForCurrentSession(extensionApi, commandCtx, goal);\n\t\t},\n\t\trefreshGoalUi,\n\t});\n\n\tconst unsubscribeGoalStoreChanged =\n\t\tpi.events?.on(GOAL_STORE_CHANGED_EVENT, async (data) => {\n\t\t\tif (!isGoalStoreChangedEvent(data)) return;\n\t\t\tconst ctx = data.ctx ?? activeContext;\n\t\t\tif (\n\t\t\t\tctx === undefined ||\n\t\t\t\tctx.sessionManager.getSessionId() !== data.threadId ||\n\t\t\t\t!ctx.isIdle() ||\n\t\t\t\tctx.hasPendingMessages()\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\t\tif (goal?.status !== \"active\") return;\n\t\t\tmonitorContinuation.syncGoal(goal);\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t\trefreshGoalUiBestEffort(ctx, goal);\n\t\t\tpi.events?.emit(GOAL_CONTINUATION_SCHEDULED_EVENT, {\n\t\t\t\tgoalId: goal.id,\n\t\t\t\tdelayMs: 0,\n\t\t\t\tactiveMonitorCount: 0,\n\t\t\t\twakeSources: {},\n\t\t\t\treason: GOAL_STORE_CHANGED_EVENT,\n\t\t\t});\n\t\t\tawait queueGoalContinuationForCurrentSession(pi, ctx, goal);\n\t\t}) ?? (() => {});\n\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tactiveContext = ctx;\n\t\tmonitorContinuation.start(ctx);\n\t\tdirectInputLifecycle.reset();\n\t\tconst ref = goalStoreRef(ctx);\n\t\tawait migrateLegacyGoalFile(ref);\n\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\tif (goal?.status === \"active\") {\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t} else {\n\t\t\tclearAgentGoalAccounting();\n\t\t}\n\t\trefreshGoalUi(ctx, goal);\n\t\tif (await maybePromptResumeStoppedGoal(pi, ctx, event.reason, goal)) {\n\t\t\treturn;\n\t\t}\n\t\tif (goal && event.reason === \"reload\") {\n\t\t\t// A reload retires the extension generation and every armed continuation\n\t\t\t// timer with it. Re-engage through the reload path: stopped goals stay\n\t\t\t// stopped, active goals resume the wait the reload tore down.\n\t\t\tawait reengageGoalAfterReload({\n\t\t\t\tctx,\n\t\t\t\tgoal,\n\t\t\t\tmonitor: monitorContinuation,\n\t\t\t\tcountTrailingContinuations: () => countTrailingGoalContinuationEntries(ctx.sessionManager.getBranch()),\n\t\t\t\tqueueContinuation: () => queueGoalContinuationForCurrentSession(pi, ctx, goal),\n\t\t\t});\n\t\t} else if (goal) {\n\t\t\t// Migration-lite admission: a resumed session carrying a trailing flood of\n\t\t\t// historical continuations must not reignite on load. Skip the auto-queue,\n\t\t\t// leave the goal active (no status rewrite), and tell the user how to resume.\n\t\t\tconst trailingContinuations = countTrailingGoalContinuationEntries(ctx.sessionManager.getBranch());\n\t\t\tif (trailingContinuations >= GOAL_CONTINUATION_CAP) {\n\t\t\t\t// The load-time flood suppression parks the goal without rewriting its\n\t\t\t\t// status, so the only resume signal left is the user's next message.\n\t\t\t\t// Arm the one-shot latch the direct-input lifecycle fires when that\n\t\t\t\t// message is accepted; without it the promise in the notice is a lie\n\t\t\t\t// and the resumed session sits idle until the goal is recreated.\n\t\t\t\tdirectInputLifecycle.armSuppressedLoadResume();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Goal auto-continuation suppressed for this resumed session (${trailingContinuations} historical continuations). Send a message to resume.`,\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait queueGoalContinuationForCurrentSession(pi, ctx, goal);\n\t\t\t}\n\t\t}\n\t});\n\n\tpi.on(\"input\", async (event, ctx) => {\n\t\tawait directInputLifecycle.onInput(event, ctx);\n\t});\n\n\tpi.on(\"input_disposition\", async (event, ctx) => {\n\t\tawait directInputLifecycle.onDisposition(event, ctx);\n\t});\n\n\tpi.on(\"turn_start\", async () => {\n\t\tstaleGoalReminderSentThisTurn = false;\n\t});\n\n\t// When the model starts tracking new open todo work while the thread has no\n\t// goal (or only a stale, already-complete one), append a system reminder to\n\t// the todo result so the model registers the goal when the work warrants it.\n\tpi.on(\"tool_result\", async (event, ctx) => {\n\t\tif (event.toolName !== \"todo\" || event.isError || staleGoalReminderSentThisTurn) return undefined;\n\t\tif (!todoResultAddsOpenTasks(event.details)) return undefined;\n\t\tconst reminder = staleGoalTodoReminder(await readGoal(goalStoreRef(ctx)));\n\t\tif (reminder === undefined) return undefined;\n\t\tstaleGoalReminderSentThisTurn = true;\n\t\treturn { content: [...event.content, { type: \"text\" as const, text: reminder }] };\n\t});\n\n\tpi.on(\"agent_start\", async (_event, ctx) => {\n\t\tconst continuationStarted = continuationPending;\n\t\tcontinuationPending = false;\n\t\tif (continuationStarted) monitorContinuation.noteContinuationStarted();\n\t\tagentTurnInProgress = true;\n\t\tblockedThisTurnGoalId = null;\n\t\tcompletedThisTurnGoalId = null;\n\t\tturnUsage.reset();\n\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\tif (goal?.status === \"active\") {\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t} else {\n\t\t\tagentGoalAccounting = null;\n\t\t}\n\t});\n\n\tpi.on(\"message_end\", async (event) => {\n\t\tturnUsage.noteMessageEnd(event.message);\n\t});\n\n\tpi.on(\"agent_end\", async (event, ctx) => {\n\t\tconst mode: GoalAccountingMode =\n\t\t\tblockedThisTurnGoalId !== null\n\t\t\t\t? \"activeOrBlocked\"\n\t\t\t\t: completedThisTurnGoalId === null\n\t\t\t\t\t? \"active\"\n\t\t\t\t\t: \"activeOrComplete\";\n\t\tlet goal = await accountCurrentAgentTurn(ctx, mode, event.messages);\n\t\tagentTurnInProgress = false;\n\t\tblockedThisTurnGoalId = null;\n\t\tcompletedThisTurnGoalId = null;\n\t\tif (event.aborted === true && event.abortSource === \"user\" && goal?.status === \"active\") {\n\t\t\tgoal = await updateGoal(\n\t\t\t\tgoalStoreRef(ctx),\n\t\t\t\t{ status: \"blocked\", reason: \"user interrupted the turn\" },\n\t\t\t\t\"model\",\n\t\t\t);\n\t\t}\n\t\tif (goal?.status === \"active\") {\n\t\t\tbeginAgentGoalAccounting(goal);\n\t\t} else {\n\t\t\tclearAgentGoalAccounting();\n\t\t}\n\t\trefreshGoalUiBestEffort(ctx, goal);\n\t\tconst continuationGoal = await continueGoalAfterAgentEnd(monitorContinuation, { ctx, event, goal });\n\t\tif (continuationGoal !== goal) {\n\t\t\tgoal = continuationGoal;\n\t\t\tsyncContinuationGoal(ctx, goal);\n\t\t}\n\t});\n\n\tpi.on(\"agent_settled\", async (_event, ctx) => {\n\t\tconst goal = await monitorContinuation.afterAgentSettled();\n\t\tif (goal !== undefined) syncContinuationGoal(ctx, goal);\n\t});\n\n\tpi.on(\"session_abort\", async (_event, ctx) => {\n\t\tcontinuationPending = false;\n\t\tconst goal = await readGoal(goalStoreRef(ctx));\n\t\tif (goal?.status !== \"active\") return;\n\t\tconst accounted = await accountCurrentAgentTurn(ctx, \"active\");\n\t\tif (accounted?.status === \"active\") {\n\t\t\tconst blocked = await updateGoal(\n\t\t\t\tgoalStoreRef(ctx),\n\t\t\t\t{ status: \"blocked\", reason: \"user interrupted the turn\" },\n\t\t\t\t\"model\",\n\t\t\t);\n\t\t\tclearAgentGoalAccounting();\n\t\t\trefreshGoalUiBestEffort(ctx, blocked);\n\t\t}\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tactiveContext = undefined;\n\t\tunsubscribeGoalStoreChanged();\n\t\tif (agentGoalAccounting !== null) {\n\t\t\tawait accountCurrentAgentTurn(ctx, \"active\");\n\t\t}\n\t\tclearAgentGoalAccounting();\n\t\tgoalTicker.stop();\n\t\tmonitorContinuation.dispose();\n\t});\n\n\tasync function maybePromptResumeStoppedGoal(\n\t\tpi: ExtensionAPI,\n\t\tctx: ExtensionContext,\n\t\tsessionStartReason: string,\n\t\tgoal: Goal | null,\n\t): Promise<boolean> {\n\t\tif (!isResumeOfStoppedGoal(ctx, sessionStartReason, goal)) {\n\t\t\treturn false;\n\t\t}\n\t\t// RPC switch_session cannot service a nested interactive UI request until the\n\t\t// switch response completes. Keep the goal stopped and let the user resume it\n\t\t// explicitly after the new session finishes binding.\n\t\tif (ctx.mode === \"rpc\") {\n\t\t\tctx.ui.notify(\n\t\t\t\t`Goal remains ${goal.status} after session resume. Resume it explicitly after the session finishes loading.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst choice = await ctx.ui.select(`Resume ${goal.status} goal?\\nGoal: ${goal.objective}`, [\n\t\t\tRESUME_GOAL_CHOICE,\n\t\t\tLEAVE_GOAL_STOPPED_CHOICE,\n\t\t]);\n\t\tif (choice !== RESUME_GOAL_CHOICE) return true;\n\n\t\tconst resumed = await updateGoal(goalStoreRef(ctx), { status: \"active\" }, \"user\");\n\t\tbeginAgentGoalAccounting(resumed);\n\t\trefreshGoalUi(ctx, resumed);\n\t\tctx.ui.notify(`Goal ${goalStatusLabel(resumed.status)}\\n${formatGoalForTool(resumed)}`, \"info\");\n\t\tawait queueGoalContinuationForCurrentSession(pi, ctx, resumed);\n\t\treturn true;\n\t}\n\n\tasync function queueGoalContinuationForCurrentSession(\n\t\textensionApi: ExtensionAPI,\n\t\tctx: ExtensionContext,\n\t\tgoal: Goal,\n\t): Promise<void> {\n\t\tconst continuedGoal = await queueGoalContinuation(extensionApi, ctx, goal, {\n\t\t\tcontinuationPending,\n\t\t\tmarkContinuationPending: () => {\n\t\t\t\tcontinuationPending = true;\n\t\t\t},\n\t\t});\n\t\tif (continuedGoal === null) {\n\t\t\tclearAgentGoalAccounting();\n\t\t\trefreshGoalUiBestEffort(ctx, null);\n\t\t\treturn;\n\t\t}\n\t\tif (continuedGoal.status === goal.status) return;\n\t\tif (continuedGoal.status === \"active\") beginAgentGoalAccounting(continuedGoal);\n\t\telse clearAgentGoalAccounting();\n\t\trefreshGoalUiBestEffort(ctx, continuedGoal);\n\t}\n\n\tfunction beginAgentGoalAccounting(goal: Goal): void {\n\t\tif (goal.status !== \"active\") return;\n\t\tif (agentGoalAccounting?.goalId === goal.id) return;\n\t\tturnUsage.discardPending();\n\t\tagentGoalAccounting = { goalId: goal.id, measuredFromMilliseconds: Date.now() };\n\t}\n\n\tfunction markGoalBlockedThisTurn(goal: Goal): void {\n\t\tif (agentTurnInProgress) blockedThisTurnGoalId = goal.id;\n\t}\n\n\tfunction markGoalCompletedThisTurn(goal: Goal): void {\n\t\tif (!agentTurnInProgress) return;\n\t\tcompletedThisTurnGoalId = goal.id;\n\t\tagentGoalAccounting = { goalId: goal.id, measuredFromMilliseconds: Date.now() };\n\t}\n\n\tfunction stopAgentGoalAccounting(goalId: string): void {\n\t\tif (agentGoalAccounting?.goalId === goalId) {\n\t\t\tagentGoalAccounting = null;\n\t\t}\n\t\tif (blockedThisTurnGoalId === goalId) {\n\t\t\tblockedThisTurnGoalId = null;\n\t\t}\n\t\tif (completedThisTurnGoalId === goalId) {\n\t\t\tcompletedThisTurnGoalId = null;\n\t\t}\n\t}\n\n\tfunction clearAgentGoalAccounting(): void {\n\t\tagentGoalAccounting = null;\n\t\tblockedThisTurnGoalId = null;\n\t\tcompletedThisTurnGoalId = null;\n\t}\n\n\tfunction syncContinuationGoal(ctx: ExtensionContext, goal: Goal | null): void {\n\t\tif (goal?.status === \"active\") beginAgentGoalAccounting(goal);\n\t\telse clearAgentGoalAccounting();\n\t\trefreshGoalUiBestEffort(ctx, goal);\n\t}\n\n\tfunction refreshGoalUi(ctx: ExtensionContext, goal: Goal | null): void {\n\t\tmonitorContinuation.syncGoal(goal);\n\t\tconst accounting = agentGoalAccounting;\n\t\tif (ctx.hasUI && goal?.status === \"active\" && accounting?.goalId === goal.id) {\n\t\t\tgoalTicker.sync(ctx, goal, accounting.measuredFromMilliseconds);\n\t\t\treturn;\n\t\t}\n\t\tgoalTicker.stop();\n\t\tupdateGoalUi(ctx, goal);\n\t}\n\n\tfunction refreshGoalUiBestEffort(ctx: ExtensionContext, goal: Goal | null): void {\n\t\ttry {\n\t\t\trefreshGoalUi(ctx, goal);\n\t\t} catch (error) {\n\t\t\tif (isStaleExtensionContextError(error)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync function accountCurrentAgentTurn(\n\t\tctx: ExtensionContext,\n\t\tmode: GoalAccountingMode,\n\t\tagentRunMessages?: unknown[],\n\t): Promise<Goal | null> {\n\t\tconst accounting = agentGoalAccounting;\n\t\tconst ref = goalStoreRef(ctx);\n\t\tif (accounting === null) return readGoal(ref);\n\n\t\tconst usage =\n\t\t\tagentRunMessages === undefined ? turnUsage.takePending() : turnUsage.takeRemaining(agentRunMessages);\n\t\tconst now = Date.now();\n\t\tconst elapsedSeconds = Math.max(0, Math.round((now - accounting.measuredFromMilliseconds) / 1000));\n\t\tconst goal = await accountGoalUsage(ref, usage, elapsedSeconds, mode, accounting.goalId);\n\t\tif (goal?.id === accounting.goalId) {\n\t\t\tagentGoalAccounting = { goalId: accounting.goalId, measuredFromMilliseconds: now };\n\t\t} else {\n\t\t\tclearAgentGoalAccounting();\n\t\t}\n\t\treturn goal;\n\t}\n}\n\n/**\n * Counts goal-continuation entries queued since the most recent real user message\n * in the current branch (mirrors the todo-gate / todo-bridge backward branch read).\n * A real user message is the only reset: the assistant turns in between are exactly\n * the unattended loop this admission guard exists to stop.\n */\nfunction countTrailingGoalContinuationEntries(entries: readonly SessionEntry[]): number {\n\tlet count = 0;\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"message\" && entry.message.role === \"user\") break;\n\t\tif (entry?.type === \"custom_message\" && entry.customType === GOAL_CONTINUATION_MESSAGE_TYPE) count += 1;\n\t}\n\treturn count;\n}\n\nfunction goalStoreRef(ctx: ExtensionContext): GoalStoreRef {\n\treturn buildGoalStoreRef(ctx.sessionManager, ctx.cwd);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-tier.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/builtin/service-tier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAA2B,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtF,YAAY,EAAE,WAAW,EAAE,CAAC;AAK5B,eAAO,MAAM,mBAAmB,2BAA6B,CAAC;AAa9D,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAalH;AAkCD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAEzG;AAED,kFAAkF;AAClF,wBAAgB,wBAAwB,CACvC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GACf,WAAW,GAAG,SAAS,CAGzB;AAED,4FAA4F;AAC5F,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,oFAAoF;IACpF,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,gBAAgB,IAAI,OAAO,CAAC;IAC5B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;IACnE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,6FAA6F;IAC7F,OAAO,EAAE,OAAO,CAAC;IACjB,kGAAkG;IAClG,YAAY,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CAChB;AAeD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAsCnG;AAqBD,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"service-tier.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/builtin/service-tier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAA2B,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtF,YAAY,EAAE,WAAW,EAAE,CAAC;AAK5B,eAAO,MAAM,mBAAmB,2BAA6B,CAAC;AAa9D,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAalH;AAkCD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAEzG;AAED,kFAAkF;AAClF,wBAAgB,wBAAwB,CACvC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GACf,WAAW,GAAG,SAAS,CAGzB;AAED,4FAA4F;AAC5F,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,oFAAoF;IACpF,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,gBAAgB,IAAI,OAAO,CAAC;IAC5B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;IACnE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,6FAA6F;IAC7F,OAAO,EAAE,OAAO,CAAC;IACjB,kGAAkG;IAClG,YAAY,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CAChB;AAeD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAsCnG;AAqBD,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAiJnE"}
|
|
@@ -197,6 +197,19 @@ export default function serviceTierExtension(pi) {
|
|
|
197
197
|
const memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, event.model);
|
|
198
198
|
liveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;
|
|
199
199
|
liveMemoryTier = getRememberedServiceTier(settingsManager, ctx.modelRegistry, event.model);
|
|
200
|
+
// `service_tier` is an OpenAI-family request field, so hopping to a non-Codex model leaves the
|
|
201
|
+
// intent with nothing to act on: `before_provider_request` already refuses to emit the tier
|
|
202
|
+
// there, but the session flag kept `isFastModeActive()` (and with it the RPC `fastMode` and the
|
|
203
|
+
// lightning indicator) claiming fast for a model that can never be served at that tier.
|
|
204
|
+
//
|
|
205
|
+
// Codex -> Codex is deliberately untouched: fast mode is a SESSION intent that survives a
|
|
206
|
+
// mid-session Codex switch (see service-tier-extension.test.ts "keeps session fast mode on
|
|
207
|
+
// across a mid-session switch to another Codex model"), and an incoming model's remembered
|
|
208
|
+
// "auto" is honored on the wire by `liveMemoryTier` below, not by clearing the flag here.
|
|
209
|
+
if (sessionFastMode && event.model.api !== OPENAI_CODEX_RESPONSES_API) {
|
|
210
|
+
sessionFastMode = false;
|
|
211
|
+
pi.setSessionFastMode(false);
|
|
212
|
+
}
|
|
200
213
|
});
|
|
201
214
|
pi.registerCommand("fast", {
|
|
202
215
|
description: "Turn OpenAI Codex fast mode on or off for the current model",
|