@gajae-code/coding-agent 0.15.3 → 0.15.5
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 +26 -1
- package/dist/types/cli/update-cli.d.ts +25 -5
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +52 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -0
- package/dist/types/extensibility/shared-events.d.ts +2 -0
- package/dist/types/gjc-runtime/workflow-placeholder.d.ts +6 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -1
- package/dist/types/sdk/cli/session-cli.d.ts +42 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/session-runtime.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +4 -3
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/path-utils.d.ts +5 -6
- package/dist/types/tools/tmux-self-injection-guard.d.ts +15 -0
- package/package.json +7 -7
- package/src/cli/update-cli.ts +102 -1
- package/src/commands/sdk.ts +5 -0
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +10 -4
- package/src/config/settings-schema.ts +53 -0
- package/src/config/settings.ts +1 -1
- package/src/coordinator-mcp/policy.ts +1 -1
- package/src/deep-interview/render-middleware.ts +6 -2
- package/src/extensibility/extensions/types.ts +5 -0
- package/src/extensibility/shared-events.ts +2 -0
- package/src/gjc-runtime/launch-worktree.ts +44 -7
- package/src/gjc-runtime/ralplan-runtime.ts +20 -2
- package/src/gjc-runtime/tmux-sessions.ts +145 -48
- package/src/gjc-runtime/ultragoal-runtime.ts +5 -0
- package/src/gjc-runtime/workflow-placeholder.ts +33 -0
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/modes/components/hook-selector.ts +2 -1
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/prompt-action-autocomplete.ts +26 -47
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/kind-aware-reconciliation.ts +10 -2
- package/src/sdk/bus/prompt-reconciliation.ts +5 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
- package/src/sdk/cli/session-cli.ts +149 -4
- package/src/sdk/host/host.ts +7 -0
- package/src/sdk/host/session-runtime.test.ts +821 -22
- package/src/sdk/host/session-runtime.ts +669 -67
- package/src/sdk/router/session-router.ts +69 -15
- package/src/session/agent-session.ts +302 -25
- package/src/session/internal/managed-session-storage.ts +64 -26
- package/src/session/session-manager.ts +26 -8
- package/src/tools/ask-contract.ts +54 -3
- package/src/tools/ask.ts +46 -6
- package/src/tools/bash.ts +14 -0
- package/src/tools/path-utils.ts +77 -29
- package/src/tools/tmux-self-injection-guard.ts +359 -0
|
@@ -20,6 +20,7 @@ import * as path from "node:path";
|
|
|
20
20
|
import { scheduler } from "node:timers/promises";
|
|
21
21
|
import * as util from "node:util";
|
|
22
22
|
import {
|
|
23
|
+
AdaptiveCompactionTracker,
|
|
23
24
|
type AfterToolCallContext,
|
|
24
25
|
type AfterToolCallResult,
|
|
25
26
|
type Agent,
|
|
@@ -123,6 +124,7 @@ import {
|
|
|
123
124
|
EMPTY_RESPONSE_PROVIDER_CODE,
|
|
124
125
|
type FallbackAttemptToken,
|
|
125
126
|
type FallbackTriggerClass,
|
|
127
|
+
SERVER_OVERLOADED_PROVIDER_CODE,
|
|
126
128
|
STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE,
|
|
127
129
|
} from "@gajae-code/ai/utils/fallback-transport";
|
|
128
130
|
import { AttemptRecordStore } from "./attempt-record-store";
|
|
@@ -1046,6 +1048,8 @@ export interface PromptOptions {
|
|
|
1046
1048
|
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
1047
1049
|
/** Optional invocation-scoped cancellation fence used before an accepted skill starts execution. */
|
|
1048
1050
|
preflightSignal?: AbortSignal;
|
|
1051
|
+
/** Internal SDK lifecycle owner token. */
|
|
1052
|
+
sdkRunToken?: string;
|
|
1049
1053
|
}
|
|
1050
1054
|
|
|
1051
1055
|
function promptPreflightCancelledError(): Error {
|
|
@@ -1296,11 +1300,74 @@ function isBareDefaultMessageOnlyFirstEventTimeout(message: AssistantMessage): b
|
|
|
1296
1300
|
BARE_DEFAULT_WATCHDOG_ERROR.test(message.errorMessage ?? ""))
|
|
1297
1301
|
);
|
|
1298
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* True when the attempt's structured evidence is exactly the provider's typed
|
|
1305
|
+
* capacity-overload code and nothing else: no status, no retained retry header,
|
|
1306
|
+
* no second typed code, no error kind, and none of the transport's other
|
|
1307
|
+
* observations (request size, first-event timing, endpoint class, provider retry
|
|
1308
|
+
* ceiling). The code arrives statuslessly (a Codex error event, an HTTP 200
|
|
1309
|
+
* terminal Responses envelope), so these facts ARE the overload rather than
|
|
1310
|
+
* evidence that the attempt did observable work; any additional fact means the
|
|
1311
|
+
* attempt is no longer provably that bare capacity rejection. The code is
|
|
1312
|
+
* compared case-sensitively, matching the parser and the transport gate.
|
|
1313
|
+
*/
|
|
1314
|
+
function isExactTypedOverloadFacts(message: AssistantMessage): boolean {
|
|
1315
|
+
if (message.errorKind !== undefined || message.errorStatus !== undefined) return false;
|
|
1316
|
+
const facts = message.transportFailure;
|
|
1317
|
+
if (!facts) return false;
|
|
1318
|
+
return (
|
|
1319
|
+
facts.providerCode === SERVER_OVERLOADED_PROVIDER_CODE &&
|
|
1320
|
+
facts.status === undefined &&
|
|
1321
|
+
facts.anthropicErrorType === undefined &&
|
|
1322
|
+
(facts.openaiErrorCode === undefined || facts.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE) &&
|
|
1323
|
+
(facts.headers === undefined || Object.keys(facts.headers).length === 0) &&
|
|
1324
|
+
facts.requestBytes === undefined &&
|
|
1325
|
+
facts.firstEventElapsedMs === undefined &&
|
|
1326
|
+
facts.firstEventTimeoutMs === undefined &&
|
|
1327
|
+
facts.endpointClass === undefined &&
|
|
1328
|
+
facts.retryMaxAttempts === undefined
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* True when transport facts are exactly the provider's statusless typed
|
|
1333
|
+
* capacity-overload code (the provider code verbatim and, when present, the
|
|
1334
|
+
* OpenAI code verbatim, with no status). The agent loop must not treat the new
|
|
1335
|
+
* facts as managed transaction authority, and managed session fallback must
|
|
1336
|
+
* not gain retry/advance authority from them, so this predicate keeps both
|
|
1337
|
+
* guards reading only typed facts, never error prose.
|
|
1338
|
+
*/
|
|
1339
|
+
function isStatuslessTypedOverloadFacts(facts: TransportFailureFacts | undefined): boolean {
|
|
1340
|
+
if (!facts) return false;
|
|
1341
|
+
return (
|
|
1342
|
+
facts.status === undefined &&
|
|
1343
|
+
facts.providerCode === SERVER_OVERLOADED_PROVIDER_CODE &&
|
|
1344
|
+
(facts.openaiErrorCode === undefined || facts.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE)
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1299
1348
|
function isBareDefaultCodexOverload(message: AssistantMessage): boolean {
|
|
1300
1349
|
return (
|
|
1301
1350
|
message.api === "openai-codex-responses" &&
|
|
1302
1351
|
BARE_DEFAULT_CODEX_OVERLOAD_ERROR.test(message.errorMessage ?? "") &&
|
|
1303
|
-
!hasBareDefaultRetryDisqualifyingFacts(message) &&
|
|
1352
|
+
(isExactTypedOverloadFacts(message) || !hasBareDefaultRetryDisqualifyingFacts(message)) &&
|
|
1353
|
+
!assistantMessageHasVisibleOrToolContent(message)
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* True when a generic OpenAI Responses turn failed with exactly OpenAI's typed
|
|
1359
|
+
* capacity-overload code. Unlike the Codex and Anthropic admissions this reads
|
|
1360
|
+
* no prose at all: the terminal Responses envelope carries the code in typed
|
|
1361
|
+
* transport facts, so the code plus a content-free attempt is the whole
|
|
1362
|
+
* admission. Both `openaiErrorCode` and the derived `providerCode` must be the
|
|
1363
|
+
* exact code, so a different transport's overload facts cannot enter through
|
|
1364
|
+
* this path.
|
|
1365
|
+
*/
|
|
1366
|
+
function isBareDefaultOpenAIResponsesOverload(message: AssistantMessage): boolean {
|
|
1367
|
+
return (
|
|
1368
|
+
message.api === "openai-responses" &&
|
|
1369
|
+
message.transportFailure?.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE &&
|
|
1370
|
+
isExactTypedOverloadFacts(message) &&
|
|
1304
1371
|
!assistantMessageHasVisibleOrToolContent(message)
|
|
1305
1372
|
);
|
|
1306
1373
|
}
|
|
@@ -2364,6 +2431,8 @@ export class AgentSession {
|
|
|
2364
2431
|
this.#followUpPromotionHooks.delete(message);
|
|
2365
2432
|
followUpHook({ startsOwnRun: true, removed: true });
|
|
2366
2433
|
}
|
|
2434
|
+
this.#sdkRunTokensByQueuedMessage.delete(message);
|
|
2435
|
+
this.#deepInterviewGenuineUserMessageEpochs.delete(message);
|
|
2367
2436
|
}
|
|
2368
2437
|
}
|
|
2369
2438
|
#fireQueuedPromotionHooks(messages: readonly AgentMessage[], promotion?: { startsOwnRun?: boolean }): void {
|
|
@@ -2383,6 +2452,29 @@ export class AgentSession {
|
|
|
2383
2452
|
}
|
|
2384
2453
|
}
|
|
2385
2454
|
}
|
|
2455
|
+
#queuedMessagesForSessionTransition(): AgentMessage[] {
|
|
2456
|
+
return [
|
|
2457
|
+
...new Set([
|
|
2458
|
+
...this.agent.snapshotSteering(),
|
|
2459
|
+
...this.agent.snapshotFollowUp(),
|
|
2460
|
+
...this.#deferredSdkFollowUps,
|
|
2461
|
+
]),
|
|
2462
|
+
];
|
|
2463
|
+
}
|
|
2464
|
+
/** Drop queued SDK work when the session identity is replaced. The old
|
|
2465
|
+
* promotion hooks belong to the predecessor runtime; retaining the message
|
|
2466
|
+
* would let it execute later under the successor without an owner. */
|
|
2467
|
+
#terminalizeQueuedSdkWorkForSessionTransition(messages: readonly AgentMessage[]): void {
|
|
2468
|
+
if (messages.length === 0) return;
|
|
2469
|
+
this.#fireQueuedRemovalHooks(messages);
|
|
2470
|
+
for (const message of messages) this.#sdkRunTokensByQueuedMessage.delete(message);
|
|
2471
|
+
this.#deferredSdkFollowUps = this.#deferredSdkFollowUps.filter(message => !messages.includes(message));
|
|
2472
|
+
}
|
|
2473
|
+
#resetActiveSdkRunOwnership(): void {
|
|
2474
|
+
this.#activeSdkRunToken = undefined;
|
|
2475
|
+
this.#activeAttemptScope = undefined;
|
|
2476
|
+
this.#activeLogicalRunId = undefined;
|
|
2477
|
+
}
|
|
2386
2478
|
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
2387
2479
|
#pendingNextTurnMessages: Array<{ message: CustomMessage; origin: "turn" | "external" }> = [];
|
|
2388
2480
|
#scheduledHiddenNextTurnGeneration: number | undefined = undefined;
|
|
@@ -2430,6 +2522,8 @@ export class AgentSession {
|
|
|
2430
2522
|
// required to re-trigger. A content signature (not object identity) is used so
|
|
2431
2523
|
// it survives the message-array rebuild that compaction/prune perform.
|
|
2432
2524
|
#lastMidRunMaintenanceAnchorSignature: string | undefined = undefined;
|
|
2525
|
+
#adaptiveCompactionTracker = new AdaptiveCompactionTracker();
|
|
2526
|
+
#adaptiveCompactionRecordedMessageKey: string | undefined;
|
|
2433
2527
|
#resourceSampler: () => EmergencyCompactionSample = () => this.#defaultResourceSample();
|
|
2434
2528
|
#retainedMemorySampler: (() => RetainedMemorySample) | undefined;
|
|
2435
2529
|
|
|
@@ -2565,6 +2659,8 @@ export class AgentSession {
|
|
|
2565
2659
|
/** SDK follow-up ownership by queued message and the attempt that dequeues it. */
|
|
2566
2660
|
#sdkRunTokensByQueuedMessage = new WeakMap<AgentMessage, string>();
|
|
2567
2661
|
#sdkRunTokensByAttemptScope = new WeakMap<AttemptScope, string>();
|
|
2662
|
+
#activeSdkRunToken: string | undefined;
|
|
2663
|
+
#activeAttemptScope: AttemptScope | undefined;
|
|
2568
2664
|
#attemptAuthority!: AttemptScopeAuthority;
|
|
2569
2665
|
#attemptRecordStore!: AttemptRecordStore;
|
|
2570
2666
|
#activeLogicalRunId: AttemptRunHandle["logicalRunId"] | undefined;
|
|
@@ -2573,6 +2669,7 @@ export class AgentSession {
|
|
|
2573
2669
|
// previously recorded run id rather than throwing inside the callback.
|
|
2574
2670
|
if (!handle) return;
|
|
2575
2671
|
this.#activeLogicalRunId = handle.logicalRunId;
|
|
2672
|
+
this.#activeAttemptScope = handle.scope;
|
|
2576
2673
|
}
|
|
2577
2674
|
|
|
2578
2675
|
#turnIndex = 0;
|
|
@@ -3972,7 +4069,20 @@ export class AgentSession {
|
|
|
3972
4069
|
// when the batch is drained by a continuation the message did not
|
|
3973
4070
|
// schedule (a skipped continuation must never discard the correlation
|
|
3974
4071
|
// of work that is still consumed; review thread P2).
|
|
4072
|
+
const consumedSdkRunToken =
|
|
4073
|
+
promotion.startsOwnRun === true
|
|
4074
|
+
? [...messages, ...dropped]
|
|
4075
|
+
.map(message => this.#sdkRunTokensByQueuedMessage.get(message))
|
|
4076
|
+
.find((token): token is string => token !== undefined)
|
|
4077
|
+
: undefined;
|
|
3975
4078
|
this.#fireQueuedPromotionHooks(messages, promotion);
|
|
4079
|
+
if (consumedSdkRunToken !== undefined) this.#activeSdkRunToken = consumedSdkRunToken;
|
|
4080
|
+
// Dropped or in-run follow-ups have no later own-run acceptance callback;
|
|
4081
|
+
// delete those tokens now. Own-run messages stay mapped until
|
|
4082
|
+
// #scheduleAgentContinue.onRunAccepted binds the new attempt scope.
|
|
4083
|
+
for (const message of dropped) this.#sdkRunTokensByQueuedMessage.delete(message);
|
|
4084
|
+
if (promotion.startsOwnRun !== true)
|
|
4085
|
+
for (const message of messages) this.#sdkRunTokensByQueuedMessage.delete(message);
|
|
3976
4086
|
};
|
|
3977
4087
|
// Steering consumed mid-run never starts its own run: fire the stored
|
|
3978
4088
|
// promotion hook at the REAL dequeue boundary so the SDK attaches the
|
|
@@ -6365,18 +6475,23 @@ export class AgentSession {
|
|
|
6365
6475
|
// agent_start (review P1); their queued messages are in-run
|
|
6366
6476
|
// consumptions, not own-run promotions.
|
|
6367
6477
|
const startsOwn = options?.maintenanceContinuation !== true;
|
|
6478
|
+
const consumedSdkRunToken = startsOwn
|
|
6479
|
+
? acceptance.consumedQueuedMessages
|
|
6480
|
+
.map(message => this.#sdkRunTokensByQueuedMessage.get(message))
|
|
6481
|
+
.find((token): token is string => token !== undefined)
|
|
6482
|
+
: undefined;
|
|
6368
6483
|
this.#fireQueuedPromotionHooks(acceptance.consumedQueuedMessages, {
|
|
6369
6484
|
startsOwnRun: startsOwn,
|
|
6370
6485
|
});
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
this.#sdkRunTokensByAttemptScope.set(handle.scope, sdkRunToken);
|
|
6375
|
-
break;
|
|
6376
|
-
}
|
|
6377
|
-
}
|
|
6486
|
+
if (consumedSdkRunToken !== undefined)
|
|
6487
|
+
this.#sdkRunTokensByAttemptScope.set(handle.scope, consumedSdkRunToken);
|
|
6488
|
+
if (startsOwn) this.#activeSdkRunToken = consumedSdkRunToken;
|
|
6378
6489
|
this.#acceptRunHandle(handle);
|
|
6379
6490
|
options?.onRunAccepted?.(handle);
|
|
6491
|
+
// Keep the queued token available through the acceptance callback;
|
|
6492
|
+
// SDK follow-up owners bind it to the new attempt scope there.
|
|
6493
|
+
for (const message of acceptance.consumedQueuedMessages)
|
|
6494
|
+
this.#sdkRunTokensByQueuedMessage.delete(message);
|
|
6380
6495
|
settleLease();
|
|
6381
6496
|
releasePredecessor();
|
|
6382
6497
|
if (startsQueuedSuccessor) {
|
|
@@ -7438,12 +7553,30 @@ export class AgentSession {
|
|
|
7438
7553
|
await this.#flushWorkerIntegrationForAgentEnd();
|
|
7439
7554
|
}
|
|
7440
7555
|
const deliveryScope = scope ?? (event as AgentSessionEvent & { scope?: AttemptScopeRef }).scope;
|
|
7556
|
+
const eventToken = (event as AgentSessionEvent & { sdkRunToken?: unknown }).sdkRunToken;
|
|
7557
|
+
const sdkRunToken =
|
|
7558
|
+
typeof eventToken === "string"
|
|
7559
|
+
? eventToken
|
|
7560
|
+
: deliveryScope
|
|
7561
|
+
? this.#sdkRunTokensByAttemptScope.get(deliveryScope as AttemptScope)
|
|
7562
|
+
: this.#activeSdkRunToken;
|
|
7441
7563
|
const isTerminalAgentEnd =
|
|
7442
7564
|
event.type === "agent_end" && !(event.stopReason === "maintenance" && event.maintenanceOutcome !== "aborted");
|
|
7443
7565
|
const finishAttempt = () => {
|
|
7444
7566
|
if (!isTerminalAgentEnd) return;
|
|
7445
|
-
|
|
7446
|
-
|
|
7567
|
+
const isActiveAttempt =
|
|
7568
|
+
deliveryScope === undefined ||
|
|
7569
|
+
this.#activeAttemptScope === undefined ||
|
|
7570
|
+
deliveryScope === this.#activeAttemptScope;
|
|
7571
|
+
if (deliveryScope) {
|
|
7572
|
+
this.#attemptRecordStore.retire(deliveryScope as AttemptScope);
|
|
7573
|
+
this.#sdkRunTokensByAttemptScope.delete(deliveryScope as AttemptScope);
|
|
7574
|
+
}
|
|
7575
|
+
if (isActiveAttempt && sdkRunToken === this.#activeSdkRunToken) this.#activeSdkRunToken = undefined;
|
|
7576
|
+
if (isActiveAttempt) {
|
|
7577
|
+
this.#activeAttemptScope = undefined;
|
|
7578
|
+
this.#activeLogicalRunId = undefined;
|
|
7579
|
+
}
|
|
7447
7580
|
};
|
|
7448
7581
|
if (!this.#extensionRunner) {
|
|
7449
7582
|
finishAttempt();
|
|
@@ -7456,9 +7589,7 @@ export class AgentSession {
|
|
|
7456
7589
|
await this.#extensionRunner.emit(
|
|
7457
7590
|
{
|
|
7458
7591
|
type: "agent_start",
|
|
7459
|
-
...(
|
|
7460
|
-
? { sdkRunToken: this.#sdkRunTokensByAttemptScope.get(deliveryScope as AttemptScope) }
|
|
7461
|
-
: {}),
|
|
7592
|
+
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
7462
7593
|
},
|
|
7463
7594
|
undefined,
|
|
7464
7595
|
deliveryScope,
|
|
@@ -7469,6 +7600,7 @@ export class AgentSession {
|
|
|
7469
7600
|
type: "agent_failed",
|
|
7470
7601
|
error: sanitizePromptFailure(event.error),
|
|
7471
7602
|
scope: event.scope,
|
|
7603
|
+
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
7472
7604
|
},
|
|
7473
7605
|
undefined,
|
|
7474
7606
|
deliveryScope,
|
|
@@ -7480,6 +7612,7 @@ export class AgentSession {
|
|
|
7480
7612
|
messages: event.messages,
|
|
7481
7613
|
stopReason: event.stopReason,
|
|
7482
7614
|
maintenanceOutcome: event.maintenanceOutcome,
|
|
7615
|
+
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
7483
7616
|
},
|
|
7484
7617
|
undefined,
|
|
7485
7618
|
deliveryScope,
|
|
@@ -7702,6 +7835,7 @@ export class AgentSession {
|
|
|
7702
7835
|
* such event.
|
|
7703
7836
|
*/
|
|
7704
7837
|
#syncAgentSessionId(sessionId?: string): void {
|
|
7838
|
+
this.#resetAdaptiveCompactionState();
|
|
7705
7839
|
this.#reasoningControlContextGeneration++;
|
|
7706
7840
|
const sid = this.#providerSessionId ?? sessionId ?? this.sessionManager.getSessionId();
|
|
7707
7841
|
this.agent.sessionId = sid;
|
|
@@ -9567,7 +9701,7 @@ export class AgentSession {
|
|
|
9567
9701
|
args = "",
|
|
9568
9702
|
options?: Pick<
|
|
9569
9703
|
PromptOptions,
|
|
9570
|
-
"onPreflightAccepted" | "onPreflightAcceptCommit" | "onSkillPrepared" | "preflightSignal"
|
|
9704
|
+
"onPreflightAccepted" | "onPreflightAcceptCommit" | "onSkillPrepared" | "preflightSignal" | "sdkRunToken"
|
|
9571
9705
|
>,
|
|
9572
9706
|
): Promise<{ name: string; path: string; args?: string; lineCount?: number }> {
|
|
9573
9707
|
if (options?.preflightSignal?.aborted) throw promptPreflightCancelledError();
|
|
@@ -10326,6 +10460,7 @@ export class AgentSession {
|
|
|
10326
10460
|
? { onPreflightAcceptCommit: options.onPreflightAcceptCommit }
|
|
10327
10461
|
: {}),
|
|
10328
10462
|
...(options.preflightSignal ? { preflightSignal: options.preflightSignal } : {}),
|
|
10463
|
+
...(options.sdkRunToken ? { sdkRunToken: options.sdkRunToken } : {}),
|
|
10329
10464
|
}
|
|
10330
10465
|
: undefined,
|
|
10331
10466
|
);
|
|
@@ -10579,6 +10714,7 @@ export class AgentSession {
|
|
|
10579
10714
|
| "onPreflightAccepted"
|
|
10580
10715
|
| "onPreflightAcceptCommit"
|
|
10581
10716
|
| "preflightSignal"
|
|
10717
|
+
| "sdkRunToken"
|
|
10582
10718
|
>,
|
|
10583
10719
|
): Promise<void> {
|
|
10584
10720
|
if (options?.preflightSignal?.aborted) throw promptPreflightCancelledError();
|
|
@@ -10680,6 +10816,7 @@ export class AgentSession {
|
|
|
10680
10816
|
| "onPreflightAccepted"
|
|
10681
10817
|
| "onPreflightAcceptCommit"
|
|
10682
10818
|
| "preflightSignal"
|
|
10819
|
+
| "sdkRunToken"
|
|
10683
10820
|
> & {
|
|
10684
10821
|
prependMessages?: AgentMessage[];
|
|
10685
10822
|
skipPostPromptRecoveryWait?: boolean;
|
|
@@ -11013,8 +11150,13 @@ export class AgentSession {
|
|
|
11013
11150
|
const agentPromptOptions = {
|
|
11014
11151
|
...(options?.toolChoice ? { toolChoice: options.toolChoice } : undefined),
|
|
11015
11152
|
...this.#managedFallbackPromptOptions(),
|
|
11153
|
+
...(options?.sdkRunToken ? { sdkRunToken: options.sdkRunToken } : {}),
|
|
11016
11154
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
11017
11155
|
this.#acceptRunHandle(handle);
|
|
11156
|
+
if (options?.sdkRunToken) {
|
|
11157
|
+
this.#sdkRunTokensByAttemptScope.set(handle.scope, options.sdkRunToken);
|
|
11158
|
+
this.#activeSdkRunToken = options.sdkRunToken;
|
|
11159
|
+
}
|
|
11018
11160
|
options?.onRunAccepted?.(handle);
|
|
11019
11161
|
options?.admissionLease?.release();
|
|
11020
11162
|
// R3.3: the accepted-run wrapper is the exact acceptance boundary —
|
|
@@ -12106,6 +12248,7 @@ export class AgentSession {
|
|
|
12106
12248
|
await this.prompt(text, {
|
|
12107
12249
|
expandPromptTemplates: false,
|
|
12108
12250
|
images,
|
|
12251
|
+
sdkRunToken: options?.sdkRunToken,
|
|
12109
12252
|
onPreflightAccepted: () => {
|
|
12110
12253
|
options?.onPreflightAccepted?.();
|
|
12111
12254
|
fireQueuedPromotion();
|
|
@@ -13457,6 +13600,8 @@ export class AgentSession {
|
|
|
13457
13600
|
this.#cancelOwnAsyncJobs();
|
|
13458
13601
|
this.#closeAllProviderSessions("new session");
|
|
13459
13602
|
this.#rebindProviderSessionState(new Map());
|
|
13603
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition(this.#queuedMessagesForSessionTransition());
|
|
13604
|
+
this.#resetActiveSdkRunOwnership();
|
|
13460
13605
|
this.agent.reset();
|
|
13461
13606
|
if (!options?.drop) await this.sessionManager.flush();
|
|
13462
13607
|
const noLeasePreviousSessionIdentity = this.sessionManager.getSessionId();
|
|
@@ -13554,6 +13699,8 @@ export class AgentSession {
|
|
|
13554
13699
|
this.#disconnectFromAgent();
|
|
13555
13700
|
this.#closeAllProviderSessions("new session");
|
|
13556
13701
|
this.#rebindProviderSessionState(new Map());
|
|
13702
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition(this.#queuedMessagesForSessionTransition());
|
|
13703
|
+
this.#resetActiveSdkRunOwnership();
|
|
13557
13704
|
this.agent.reset();
|
|
13558
13705
|
this.setTodoPhases([]);
|
|
13559
13706
|
this.#syncAgentSessionId();
|
|
@@ -13676,6 +13823,8 @@ export class AgentSession {
|
|
|
13676
13823
|
this.yieldQueue.clear();
|
|
13677
13824
|
this.#pendingBackgroundExchanges = [];
|
|
13678
13825
|
this.#closeAllProviderSessions("context clear");
|
|
13826
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition(this.#queuedMessagesForSessionTransition());
|
|
13827
|
+
this.#resetActiveSdkRunOwnership();
|
|
13679
13828
|
this.agent.reset();
|
|
13680
13829
|
await this.sessionManager.flush();
|
|
13681
13830
|
this.sessionManager.appendContextClearEntry({ sessionId });
|
|
@@ -15857,6 +16006,7 @@ export class AgentSession {
|
|
|
15857
16006
|
fromExtension,
|
|
15858
16007
|
preserveData,
|
|
15859
16008
|
);
|
|
16009
|
+
this.#recordAdaptiveCompactionReset(tokensBefore);
|
|
15860
16010
|
await this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
|
|
15861
16011
|
|
|
15862
16012
|
const compactionResult: CompactionResult = {
|
|
@@ -16099,6 +16249,7 @@ export class AgentSession {
|
|
|
16099
16249
|
const rollbackPendingNextTurnMessages = [...this.#pendingNextTurnMessages];
|
|
16100
16250
|
const rollbackScheduledHiddenNextTurnGeneration = this.#scheduledHiddenNextTurnGeneration;
|
|
16101
16251
|
const rollbackTodoReminderCount = this.#todoReminderCount;
|
|
16252
|
+
const rollbackDeferredSdkFollowUps = [...this.#deferredSdkFollowUps];
|
|
16102
16253
|
// Snapshot the agent's executable queues so a rollback restores queued
|
|
16103
16254
|
// user work that agent.reset() would otherwise clear.
|
|
16104
16255
|
const rollbackAgentSteeringQueue = this.agent.snapshotSteering();
|
|
@@ -16139,6 +16290,12 @@ export class AgentSession {
|
|
|
16139
16290
|
this.#rekeyJobManagerForSessionIdentity(rollbackSessionState.sessionId, rollbackSessionState.sessionFile);
|
|
16140
16291
|
committed = true;
|
|
16141
16292
|
await this.#runToolSessionTransitionCleanups();
|
|
16293
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition([
|
|
16294
|
+
...rollbackAgentSteeringQueue,
|
|
16295
|
+
...rollbackAgentFollowUpQueue,
|
|
16296
|
+
...rollbackDeferredSdkFollowUps,
|
|
16297
|
+
]);
|
|
16298
|
+
this.#resetActiveSdkRunOwnership();
|
|
16142
16299
|
this.agent.reset();
|
|
16143
16300
|
this.#syncAgentSessionId();
|
|
16144
16301
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -16222,6 +16379,7 @@ export class AgentSession {
|
|
|
16222
16379
|
this.#pendingNextTurnMessages = rollbackPendingNextTurnMessages;
|
|
16223
16380
|
this.#scheduledHiddenNextTurnGeneration = rollbackScheduledHiddenNextTurnGeneration;
|
|
16224
16381
|
this.#todoReminderCount = rollbackTodoReminderCount;
|
|
16382
|
+
this.#deferredSdkFollowUps = rollbackDeferredSdkFollowUps;
|
|
16225
16383
|
this.#syncTodoPhasesFromBranch();
|
|
16226
16384
|
// Exact-discard only the staged successor; predecessor state was never adopted.
|
|
16227
16385
|
const rollbackError = prepared
|
|
@@ -16380,7 +16538,12 @@ export class AgentSession {
|
|
|
16380
16538
|
// which breaks the provider prompt-cache prefix mid-epoch. Only prune at a
|
|
16381
16539
|
// sanctioned maintenance boundary, i.e. when the un-pruned context already
|
|
16382
16540
|
// crosses the compaction threshold. Pruning may then avert full compaction.
|
|
16383
|
-
|
|
16541
|
+
const adaptiveCompactionSettings = this.#recordAdaptiveCompactionCall(
|
|
16542
|
+
contextTokens,
|
|
16543
|
+
compactionSettings,
|
|
16544
|
+
assistantMessage,
|
|
16545
|
+
);
|
|
16546
|
+
if (!shouldCompact(contextTokens, contextWindow, adaptiveCompactionSettings, autoCompactionOutputReserveTokens))
|
|
16384
16547
|
return true;
|
|
16385
16548
|
const pruneEstimate = estimateToolOutputPruneSavings(this.sessionManager.getBranch(), DEFAULT_PRUNE_CONFIG, {
|
|
16386
16549
|
relaxedMinimum: 0,
|
|
@@ -16391,7 +16554,7 @@ export class AgentSession {
|
|
|
16391
16554
|
!shouldCompact(
|
|
16392
16555
|
Math.max(0, contextTokens - pruneEstimate.tokensSaved),
|
|
16393
16556
|
contextWindow,
|
|
16394
|
-
|
|
16557
|
+
adaptiveCompactionSettings,
|
|
16395
16558
|
autoCompactionOutputReserveTokens,
|
|
16396
16559
|
)
|
|
16397
16560
|
) {
|
|
@@ -16399,7 +16562,8 @@ export class AgentSession {
|
|
|
16399
16562
|
if (ownershipSignal?.aborted) return false;
|
|
16400
16563
|
if (pruneResult) contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
|
|
16401
16564
|
}
|
|
16402
|
-
|
|
16565
|
+
const prunedCompactionSettings = this.#compactionSettingsWithAdaptiveState(compactionSettings);
|
|
16566
|
+
if (shouldCompact(contextTokens, contextWindow, prunedCompactionSettings, autoCompactionOutputReserveTokens)) {
|
|
16403
16567
|
// Try promotion first — if a larger model is available, switch instead of compacting
|
|
16404
16568
|
const promoted = await this.#tryContextPromotion(assistantMessage, ownershipSignal);
|
|
16405
16569
|
if (ownershipSignal?.aborted) return false;
|
|
@@ -16453,7 +16617,7 @@ export class AgentSession {
|
|
|
16453
16617
|
}
|
|
16454
16618
|
if (isAborted()) return result("aborted");
|
|
16455
16619
|
|
|
16456
|
-
//
|
|
16620
|
+
// context-full maintenance. "off" defers entirely; "handoff"
|
|
16457
16621
|
// keeps its existing agent_end / pre-prompt boundaries (a mid-tool-loop
|
|
16458
16622
|
// session swap would be far more disruptive than the overflow it avoids).
|
|
16459
16623
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
@@ -16469,7 +16633,12 @@ export class AgentSession {
|
|
|
16469
16633
|
const autoCompactionOutputReserveTokens = 0;
|
|
16470
16634
|
const anchor = this.#findMidRunUsageAnchor(context.messages);
|
|
16471
16635
|
let contextTokens = this.#estimateMidRunContextTokens(context.messages);
|
|
16472
|
-
|
|
16636
|
+
const adaptiveCompactionSettings = anchor
|
|
16637
|
+
? this.#recordAdaptiveCompactionCall(contextTokens, compactionSettings, anchor.message)
|
|
16638
|
+
: this.#compactionSettingsWithAdaptiveState(compactionSettings);
|
|
16639
|
+
if (
|
|
16640
|
+
!shouldCompact(contextTokens, contextWindow, adaptiveCompactionSettings, autoCompactionOutputReserveTokens)
|
|
16641
|
+
) {
|
|
16473
16642
|
return result("not-needed");
|
|
16474
16643
|
}
|
|
16475
16644
|
// Anti-loop (#1662): a given provider response anchors at most one
|
|
@@ -16503,7 +16672,7 @@ export class AgentSession {
|
|
|
16503
16672
|
!shouldCompact(
|
|
16504
16673
|
Math.max(0, contextTokens - pruneEstimate.tokensSaved),
|
|
16505
16674
|
contextWindow,
|
|
16506
|
-
|
|
16675
|
+
adaptiveCompactionSettings,
|
|
16507
16676
|
autoCompactionOutputReserveTokens,
|
|
16508
16677
|
)
|
|
16509
16678
|
) {
|
|
@@ -16512,7 +16681,9 @@ export class AgentSession {
|
|
|
16512
16681
|
if (pruneResult?.failure === "artifact_persistence") return result("failed");
|
|
16513
16682
|
if (pruneResult) contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
|
|
16514
16683
|
}
|
|
16515
|
-
if (
|
|
16684
|
+
if (
|
|
16685
|
+
!shouldCompact(contextTokens, contextWindow, adaptiveCompactionSettings, autoCompactionOutputReserveTokens)
|
|
16686
|
+
) {
|
|
16516
16687
|
return pruneResult?.committed ? result("pruned", true) : result("not-needed");
|
|
16517
16688
|
}
|
|
16518
16689
|
|
|
@@ -16675,7 +16846,8 @@ export class AgentSession {
|
|
|
16675
16846
|
// Model maxTokens is a capability ceiling, not a per-turn reservation.
|
|
16676
16847
|
// Auto maintenance should track actual context fullness.
|
|
16677
16848
|
const autoCompactionOutputReserveTokens = 0;
|
|
16678
|
-
|
|
16849
|
+
const adaptiveCompactionSettings = this.#compactionSettingsWithAdaptiveState(compactionSettings);
|
|
16850
|
+
if (!shouldCompact(contextTokens, contextWindow, adaptiveCompactionSettings, autoCompactionOutputReserveTokens)) {
|
|
16679
16851
|
// Below the compaction threshold: optionally run evidence-gated maintenance
|
|
16680
16852
|
// pruning (opt-in, high savings + cache-epoch payback required).
|
|
16681
16853
|
await this.#maybeRunBelowThresholdMaintenancePrune();
|
|
@@ -16691,14 +16863,15 @@ export class AgentSession {
|
|
|
16691
16863
|
!shouldCompact(
|
|
16692
16864
|
Math.max(0, contextTokens - pruneEstimate.tokensSaved),
|
|
16693
16865
|
contextWindow,
|
|
16694
|
-
|
|
16866
|
+
adaptiveCompactionSettings,
|
|
16695
16867
|
autoCompactionOutputReserveTokens,
|
|
16696
16868
|
)
|
|
16697
16869
|
) {
|
|
16698
16870
|
const pruneResult = await this.#pruneToolOutputs(undefined, true);
|
|
16699
16871
|
if (pruneResult) contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
|
|
16700
16872
|
}
|
|
16701
|
-
|
|
16873
|
+
const prunedCompactionSettings = this.#compactionSettingsWithAdaptiveState(compactionSettings);
|
|
16874
|
+
if (shouldCompact(contextTokens, contextWindow, prunedCompactionSettings, autoCompactionOutputReserveTokens)) {
|
|
16702
16875
|
await this.#runAutoCompaction("threshold", false, false, {
|
|
16703
16876
|
continueAfterMaintenance: false,
|
|
16704
16877
|
deferHandoffMaintenance: false,
|
|
@@ -16706,6 +16879,45 @@ export class AgentSession {
|
|
|
16706
16879
|
}
|
|
16707
16880
|
}
|
|
16708
16881
|
|
|
16882
|
+
#recordAdaptiveCompactionCall<T extends { adaptive?: { enabled: boolean; turnWindow: number } }>(
|
|
16883
|
+
contextTokens: number,
|
|
16884
|
+
settings: T,
|
|
16885
|
+
assistantMessage: AssistantMessage,
|
|
16886
|
+
): T {
|
|
16887
|
+
if (!settings.adaptive?.enabled) return settings;
|
|
16888
|
+
this.#adaptiveCompactionTracker.setWindowMs(settings.adaptive.turnWindow * 60_000);
|
|
16889
|
+
const messageKey = this.#adaptiveCompactionMessageKey(assistantMessage);
|
|
16890
|
+
if (this.#adaptiveCompactionRecordedMessageKey === messageKey) {
|
|
16891
|
+
return this.#compactionSettingsWithAdaptiveState(settings);
|
|
16892
|
+
}
|
|
16893
|
+
this.#adaptiveCompactionRecordedMessageKey = messageKey;
|
|
16894
|
+
this.#adaptiveCompactionTracker.recordCall(contextTokens);
|
|
16895
|
+
return this.#compactionSettingsWithAdaptiveState(settings);
|
|
16896
|
+
}
|
|
16897
|
+
|
|
16898
|
+
#adaptiveCompactionMessageKey(message: AssistantMessage): string {
|
|
16899
|
+
return `${message.provider}\u0000${message.model}\u0000${message.timestamp}\u0000${message.usage?.totalTokens ?? ""}`;
|
|
16900
|
+
}
|
|
16901
|
+
|
|
16902
|
+
#compactionSettingsWithAdaptiveState<T extends { adaptive?: { enabled: boolean } }>(settings: T): T {
|
|
16903
|
+
if (!settings.adaptive?.enabled) return settings;
|
|
16904
|
+
return {
|
|
16905
|
+
...settings,
|
|
16906
|
+
adaptiveState: this.#adaptiveCompactionTracker.decisionState(),
|
|
16907
|
+
};
|
|
16908
|
+
}
|
|
16909
|
+
|
|
16910
|
+
#resetAdaptiveCompactionState(): void {
|
|
16911
|
+
this.#adaptiveCompactionTracker.reset();
|
|
16912
|
+
this.#adaptiveCompactionRecordedMessageKey = undefined;
|
|
16913
|
+
}
|
|
16914
|
+
|
|
16915
|
+
#recordAdaptiveCompactionReset(contextTokens: number): void {
|
|
16916
|
+
this.#adaptiveCompactionTracker.reset();
|
|
16917
|
+
this.#adaptiveCompactionTracker.recordCompact(contextTokens);
|
|
16918
|
+
this.#adaptiveCompactionRecordedMessageKey = undefined;
|
|
16919
|
+
}
|
|
16920
|
+
|
|
16709
16921
|
#assistantEndedWithSuccessfulYield(assistantMessage: AssistantMessage): boolean {
|
|
16710
16922
|
const toolCallId = this.#lastSuccessfulYieldToolCallId;
|
|
16711
16923
|
if (!toolCallId) return false;
|
|
@@ -18156,6 +18368,7 @@ export class AgentSession {
|
|
|
18156
18368
|
fromExtension,
|
|
18157
18369
|
preserveData,
|
|
18158
18370
|
);
|
|
18371
|
+
this.#recordAdaptiveCompactionReset(tokensBefore);
|
|
18159
18372
|
await this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
|
|
18160
18373
|
if (autoCompactionSignal.aborted) return await emitAborted();
|
|
18161
18374
|
|
|
@@ -18935,6 +19148,18 @@ export class AgentSession {
|
|
|
18935
19148
|
},
|
|
18936
19149
|
};
|
|
18937
19150
|
}
|
|
19151
|
+
// Issue #5018 preserves managed behavior for the typed statusless
|
|
19152
|
+
// Responses overload: before the code survived transport, this failure
|
|
19153
|
+
// reached the session as an ordinary committed error, so the chain never
|
|
19154
|
+
// discarded or advanced on it. Route it to the exhaustion decision
|
|
19155
|
+
// directly, before the retryable path can classify its new facts.
|
|
19156
|
+
if (isStatuslessTypedOverloadFacts(outcome.failure.transportFailure)) {
|
|
19157
|
+
this.#defaultFallbackChain().resetAttemptBudget();
|
|
19158
|
+
return this.#managedFallbackExhaustionDecision(
|
|
19159
|
+
outcome.failure.message,
|
|
19160
|
+
outcome.failure.message.errorMessage || "Model fallback attempt failed",
|
|
19161
|
+
);
|
|
19162
|
+
}
|
|
18938
19163
|
return this.#handleRetryableError(
|
|
18939
19164
|
outcome.failure.message,
|
|
18940
19165
|
true,
|
|
@@ -19393,7 +19618,10 @@ export class AgentSession {
|
|
|
19393
19618
|
}
|
|
19394
19619
|
const firstEventTimeout = classification === "first_event_timeout";
|
|
19395
19620
|
const emptyResponse = classification === "empty_response";
|
|
19396
|
-
const canReplayProviderOverload =
|
|
19621
|
+
const canReplayProviderOverload =
|
|
19622
|
+
isBareDefaultCodexOverload(message) ||
|
|
19623
|
+
isBareDefaultAnthropicOverload(message) ||
|
|
19624
|
+
isBareDefaultOpenAIResponsesOverload(message);
|
|
19397
19625
|
const reportedRetryMaxAttempts = transportFailure?.retryMaxAttempts;
|
|
19398
19626
|
if (reportedRetryMaxAttempts !== undefined) {
|
|
19399
19627
|
this.#providerRetryMaxAttempts = Math.min(
|
|
@@ -19436,6 +19664,17 @@ export class AgentSession {
|
|
|
19436
19664
|
const trigger:
|
|
19437
19665
|
| { class: FallbackTriggerClass; retryAfterMs?: number; authDisposition?: AuthDisposition }
|
|
19438
19666
|
| undefined = this.#fallbackTriggerFor(message, !managedFallback, transportFailure);
|
|
19667
|
+
// OpenAI's typed statusless capacity-overload code (issue #5018) must not
|
|
19668
|
+
// gain managed-chain retry/advance authority from its new facts. Before
|
|
19669
|
+
// the code survived transport, this failure reached the session as an
|
|
19670
|
+
// ordinary committed error and surfaced immediately, so mirror that
|
|
19671
|
+
// behavior with the existing exhaustion decision.
|
|
19672
|
+
if (managedFallback && isStatuslessTypedOverloadFacts(transportFailure)) {
|
|
19673
|
+
return this.#managedFallbackExhaustionDecision(
|
|
19674
|
+
message,
|
|
19675
|
+
message.errorMessage || "Model fallback attempt failed",
|
|
19676
|
+
);
|
|
19677
|
+
}
|
|
19439
19678
|
if (!trigger) {
|
|
19440
19679
|
return managedOutcome
|
|
19441
19680
|
? this.#managedFallbackExhaustionDecision(message, message.errorMessage || "Model fallback attempt failed")
|
|
@@ -19874,6 +20113,7 @@ export class AgentSession {
|
|
|
19874
20113
|
preSubmit: PreSubmitBuilder,
|
|
19875
20114
|
options?: {
|
|
19876
20115
|
toolChoice?: ToolChoice;
|
|
20116
|
+
sdkRunToken?: string;
|
|
19877
20117
|
fallbackManaged?: boolean;
|
|
19878
20118
|
onRunAccepted?: (handle: AttemptRunHandle) => void;
|
|
19879
20119
|
},
|
|
@@ -21173,6 +21413,10 @@ export class AgentSession {
|
|
|
21173
21413
|
const previousSystemPrompt = this.agent.state.systemPrompt;
|
|
21174
21414
|
const previousAgentSteeringQueue = this.agent.snapshotSteering();
|
|
21175
21415
|
const previousAgentFollowUpQueue = this.agent.snapshotFollowUp();
|
|
21416
|
+
const previousDeferredSdkFollowUps = [...this.#deferredSdkFollowUps];
|
|
21417
|
+
const previousActiveSdkRunToken = this.#activeSdkRunToken;
|
|
21418
|
+
const previousActiveAttemptScope = this.#activeAttemptScope;
|
|
21419
|
+
const previousActiveLogicalRunId = this.#activeLogicalRunId;
|
|
21176
21420
|
|
|
21177
21421
|
this.#steeringMessages = [];
|
|
21178
21422
|
this.#followUpMessages = [];
|
|
@@ -21220,6 +21464,7 @@ export class AgentSession {
|
|
|
21220
21464
|
// The target session is loaded and MCP selections are restored: discard
|
|
21221
21465
|
// pre-switch delivery queues before completing the restored agent state.
|
|
21222
21466
|
this.agent.clearAllQueues();
|
|
21467
|
+
this.#resetActiveSdkRunOwnership();
|
|
21223
21468
|
|
|
21224
21469
|
if (historyRewriteReason) {
|
|
21225
21470
|
this.agent.replaceMessages(sessionContext.messages, {
|
|
@@ -21404,6 +21649,12 @@ export class AgentSession {
|
|
|
21404
21649
|
...(options?.transition ? { transition: options.transition } : {}),
|
|
21405
21650
|
});
|
|
21406
21651
|
}
|
|
21652
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition([
|
|
21653
|
+
...previousAgentSteeringQueue,
|
|
21654
|
+
...previousAgentFollowUpQueue,
|
|
21655
|
+
...previousDeferredSdkFollowUps,
|
|
21656
|
+
]);
|
|
21657
|
+
this.#deferredSdkFollowUps = [];
|
|
21407
21658
|
return true;
|
|
21408
21659
|
} catch (error) {
|
|
21409
21660
|
if (transitionCleanupCommitted) throw error;
|
|
@@ -21469,6 +21720,10 @@ export class AgentSession {
|
|
|
21469
21720
|
this.#followUpMessages = previousFollowUpMessages;
|
|
21470
21721
|
this.#pendingNextTurnMessages = previousPendingNextTurnMessages;
|
|
21471
21722
|
this.#scheduledHiddenNextTurnGeneration = previousScheduledHiddenNextTurnGeneration;
|
|
21723
|
+
this.#deferredSdkFollowUps = previousDeferredSdkFollowUps;
|
|
21724
|
+
this.#activeSdkRunToken = previousActiveSdkRunToken;
|
|
21725
|
+
this.#activeAttemptScope = previousActiveAttemptScope;
|
|
21726
|
+
this.#activeLogicalRunId = previousActiveLogicalRunId;
|
|
21472
21727
|
this.agent.clearAllQueues();
|
|
21473
21728
|
this.agent.restoreSteering(previousAgentSteeringQueue);
|
|
21474
21729
|
this.agent.restoreFollowUp(previousAgentFollowUpQueue);
|
|
@@ -21535,6 +21790,9 @@ export class AgentSession {
|
|
|
21535
21790
|
}
|
|
21536
21791
|
|
|
21537
21792
|
const selectedText = this.#extractUserMessageText(selectedEntry.message.content);
|
|
21793
|
+
const previousAgentSteeringQueue = this.agent.snapshotSteering();
|
|
21794
|
+
const previousAgentFollowUpQueue = this.agent.snapshotFollowUp();
|
|
21795
|
+
const previousDeferredSdkFollowUps = [...this.#deferredSdkFollowUps];
|
|
21538
21796
|
|
|
21539
21797
|
let skipConversationRestore = false;
|
|
21540
21798
|
|
|
@@ -21570,6 +21828,16 @@ export class AgentSession {
|
|
|
21570
21828
|
}
|
|
21571
21829
|
this.#pendingNextTurnMessages = [];
|
|
21572
21830
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
21831
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition([
|
|
21832
|
+
...previousAgentSteeringQueue,
|
|
21833
|
+
...previousAgentFollowUpQueue,
|
|
21834
|
+
...previousDeferredSdkFollowUps,
|
|
21835
|
+
]);
|
|
21836
|
+
this.#deferredSdkFollowUps = [];
|
|
21837
|
+
this.#resetActiveSdkRunOwnership();
|
|
21838
|
+
this.agent.clearAllQueues();
|
|
21839
|
+
this.#steeringMessages = [];
|
|
21840
|
+
this.#followUpMessages = [];
|
|
21573
21841
|
|
|
21574
21842
|
this.#syncTodoPhasesFromBranch();
|
|
21575
21843
|
this.#syncAgentSessionId();
|
|
@@ -21775,6 +22043,15 @@ export class AgentSession {
|
|
|
21775
22043
|
this.sessionManager.branch(newLeafId);
|
|
21776
22044
|
}
|
|
21777
22045
|
|
|
22046
|
+
// The history rewrite is now committed. Drop predecessor-owned queued SDK
|
|
22047
|
+
// work at this boundary; cancelled or failed preparation above preserves it.
|
|
22048
|
+
const queuedSdkWork = this.#queuedMessagesForSessionTransition();
|
|
22049
|
+
this.#terminalizeQueuedSdkWorkForSessionTransition(queuedSdkWork);
|
|
22050
|
+
this.#deferredSdkFollowUps = [];
|
|
22051
|
+
this.agent.clearAllQueues();
|
|
22052
|
+
this.#steeringMessages = [];
|
|
22053
|
+
this.#followUpMessages = [];
|
|
22054
|
+
|
|
21778
22055
|
// Update agent state through the canonical filtered display context so legacy
|
|
21779
22056
|
// request-scoped entries cannot re-enter live history after tree navigation.
|
|
21780
22057
|
const displayContext = this.buildDisplaySessionContext();
|