@gajae-code/coding-agent 0.11.10 → 0.11.11
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 +38 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-profile-contract.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +57 -2
- package/dist/types/config/settings.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +60 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/modes/acp/acp-agent.d.ts +14 -9
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/manager.d.ts +8 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +3 -1
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +23 -3
- package/dist/types/sdk/host/control/operations.d.ts +1 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +9 -11
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +27 -0
- package/dist/types/session/session-manager.d.ts +34 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/types.d.ts +36 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/resource-gc.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli.ts +48 -2
- package/src/commands/sdk.ts +91 -7
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +24 -0
- package/src/config/settings-schema.ts +61 -2
- package/src/config/settings.ts +8 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +14 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +16 -2
- package/src/gjc-runtime/deep-interview-state.ts +25 -3
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/ralplan-runtime.ts +252 -1
- package/src/gjc-runtime/state-runtime.ts +48 -11
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/modes/acp/acp-agent.ts +554 -60
- package/src/modes/acp/acp-event-mapper.ts +93 -6
- package/src/modes/components/model-selector.ts +15 -1
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/tool-status-header.ts +1 -4
- package/src/modes/components/welcome.ts +18 -3
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +44 -3
- package/src/modes/interactive-mode.ts +65 -26
- package/src/modes/runtime-init.ts +1 -0
- package/src/prompts/agents/executor.md +1 -1
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/manager.ts +26 -6
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/lifecycle.ts +82 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/config.ts +1 -1
- package/src/sdk/bus/index.ts +162 -22
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -4
- package/src/sdk/bus/telegram-daemon.ts +531 -53
- package/src/sdk/host/control/dispatch.ts +2 -0
- package/src/sdk/host/control/operations.ts +1 -0
- package/src/sdk/host/host.ts +2 -2
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/protocol/operation-inventory.generated.json +47 -0
- package/src/sdk/protocol/operation-registry.ts +1 -0
- package/src/sdk/session.ts +12 -1
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +256 -142
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +40 -10
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/session-manager.ts +608 -25
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +2 -0
- package/src/task/types.ts +22 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/resource-gc.ts +496 -120
- package/src/workflow/workflow-intent-diff.ts +4 -1
|
@@ -172,6 +172,7 @@ import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../a
|
|
|
172
172
|
import { reset as resetCapabilities } from "../capability";
|
|
173
173
|
import type { Rule } from "../capability/rule";
|
|
174
174
|
import type { CasReceipt } from "../config/atomic-yaml-patch";
|
|
175
|
+
import { activateModelProfile } from "../config/model-profile-activation";
|
|
175
176
|
import {
|
|
176
177
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
177
178
|
isAuthenticated,
|
|
@@ -233,7 +234,7 @@ import type {
|
|
|
233
234
|
TurnEndEvent,
|
|
234
235
|
TurnStartEvent,
|
|
235
236
|
} from "../extensibility/extensions";
|
|
236
|
-
import type { CompactOptions, ContextUsage } from "../extensibility/extensions/types";
|
|
237
|
+
import type { CompactOptions, ContextUsage, ExtensionTranscriptEntry } from "../extensibility/extensions/types";
|
|
237
238
|
import { ExtensionToolWrapper } from "../extensibility/extensions/wrapper";
|
|
238
239
|
import {
|
|
239
240
|
type LoadedSubskillActivation,
|
|
@@ -243,7 +244,13 @@ import { resolveCurrentPhaseForParent } from "../extensibility/gjc-plugins/injec
|
|
|
243
244
|
import { readActiveSubskillsForParent, toActiveSubskillEntry } from "../extensibility/gjc-plugins/state";
|
|
244
245
|
import { loadActiveSubskillTools } from "../extensibility/gjc-plugins/tools";
|
|
245
246
|
import type { HookCommandContext } from "../extensibility/hooks/types";
|
|
246
|
-
import {
|
|
247
|
+
import {
|
|
248
|
+
buildSkillPromptMessage,
|
|
249
|
+
getSkillSlashCommandName,
|
|
250
|
+
parseSkillInvocations,
|
|
251
|
+
type Skill,
|
|
252
|
+
type SkillWarning,
|
|
253
|
+
} from "../extensibility/skills";
|
|
247
254
|
import { expandSlashCommand, type FileSlashCommand } from "../extensibility/slash-commands";
|
|
248
255
|
import { assertDeepInterviewIntentManifest } from "../gjc-runtime/deep-interview-state";
|
|
249
256
|
import { buildGjcRuntimeSessionEnv, consumePendingGoalModeRequest } from "../gjc-runtime/goal-mode-request";
|
|
@@ -341,6 +348,7 @@ import {
|
|
|
341
348
|
import {
|
|
342
349
|
type ConfiguredFallbackChain,
|
|
343
350
|
cappedExponentialWithFullJitter,
|
|
351
|
+
compactionRetryDelay,
|
|
344
352
|
effectiveFallbackDelay,
|
|
345
353
|
FallbackChainController,
|
|
346
354
|
} from "./fallback-chain-controller";
|
|
@@ -379,6 +387,7 @@ import type {
|
|
|
379
387
|
CompactionEntry,
|
|
380
388
|
DefaultModelSelectionStage,
|
|
381
389
|
NewSessionOptions,
|
|
390
|
+
PreparedNewSession,
|
|
382
391
|
RecoveryHydrationContext,
|
|
383
392
|
RecoveryHydrationPromotionFence,
|
|
384
393
|
SessionContext,
|
|
@@ -798,8 +807,14 @@ const KIMI_CODE_FIRST_EVENT_TIMEOUT_MESSAGES = {
|
|
|
798
807
|
|
|
799
808
|
const ALIBABA_TOKEN_PLAN_PROVIDER = "alibaba-token-plan";
|
|
800
809
|
const ALIBABA_TOKEN_PLAN_FIRST_EVENT_TIMEOUT_MESSAGES = {
|
|
801
|
-
"openai-responses":
|
|
802
|
-
|
|
810
|
+
"openai-responses": new Set([
|
|
811
|
+
"Provider stream timed out while waiting for the first event",
|
|
812
|
+
"OpenAI responses stream timed out while waiting for the first event",
|
|
813
|
+
]),
|
|
814
|
+
"openai-completions": new Set([
|
|
815
|
+
"Provider stream timed out while waiting for the first event",
|
|
816
|
+
"OpenAI completions stream timed out while waiting for the first event",
|
|
817
|
+
]),
|
|
803
818
|
} as const;
|
|
804
819
|
|
|
805
820
|
function hasBareDefaultRetryDisqualifyingFacts(message: AssistantMessage): boolean {
|
|
@@ -1043,6 +1058,22 @@ function createHandoffContext(document: string): string {
|
|
|
1043
1058
|
return `<handoff-context>\n${document}\n</handoff-context>\n\nThe above is a handoff document from a previous session. Use this context to continue the work seamlessly.`;
|
|
1044
1059
|
}
|
|
1045
1060
|
|
|
1061
|
+
async function discardPreparedNewSessionAfterFailure(
|
|
1062
|
+
sessionManager: SessionManager,
|
|
1063
|
+
prepared: PreparedNewSession,
|
|
1064
|
+
transitionError: unknown,
|
|
1065
|
+
): Promise<unknown> {
|
|
1066
|
+
try {
|
|
1067
|
+
await sessionManager.discardPreparedNewSession(prepared);
|
|
1068
|
+
} catch (cleanupError) {
|
|
1069
|
+
throw new AggregateError(
|
|
1070
|
+
[transitionError, cleanupError],
|
|
1071
|
+
"Session transition and staged successor cleanup both failed.",
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
return transitionError;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1046
1077
|
// ============================================================================
|
|
1047
1078
|
// ACP Permission Gate
|
|
1048
1079
|
// ============================================================================
|
|
@@ -4008,8 +4039,21 @@ export class AgentSession {
|
|
|
4008
4039
|
return;
|
|
4009
4040
|
}
|
|
4010
4041
|
const predecessorAgentEnd = this.#claimDeferredAgentEndForContinuation(predecessorAgentEndHold);
|
|
4042
|
+
const startsQueuedSuccessor =
|
|
4043
|
+
this.agent.state.messages.at(-1)?.role === "assistant" && this.agent.hasQueuedMessages();
|
|
4011
4044
|
try {
|
|
4012
|
-
await this.agent.continue(
|
|
4045
|
+
await this.agent.continue({
|
|
4046
|
+
...this.#managedFallbackPromptOptions(),
|
|
4047
|
+
// Reset only after continue() has claimed the queued turn. Skipped or stale
|
|
4048
|
+
// continuations retain predecessor accounting, and resetAttemptBudget keeps
|
|
4049
|
+
// the sticky fallback cursor unchanged.
|
|
4050
|
+
onRunAccepted: startsQueuedSuccessor
|
|
4051
|
+
? () => {
|
|
4052
|
+
this.#defaultFallbackChain().resetAttemptBudget();
|
|
4053
|
+
this.#overflowMaintenanceAttempts = 0;
|
|
4054
|
+
}
|
|
4055
|
+
: undefined,
|
|
4056
|
+
});
|
|
4013
4057
|
} catch (error) {
|
|
4014
4058
|
this.#restoreDeferredAgentEndAfterContinuationFailure(predecessorAgentEnd);
|
|
4015
4059
|
throw error;
|
|
@@ -4612,13 +4656,13 @@ export class AgentSession {
|
|
|
4612
4656
|
return resolveToCwd(normalized, this.sessionManager.getCwd());
|
|
4613
4657
|
}
|
|
4614
4658
|
|
|
4615
|
-
#localProtocolOptions(): LocalProtocolOptions {
|
|
4659
|
+
#localProtocolOptions(prepared?: PreparedNewSession): LocalProtocolOptions {
|
|
4616
4660
|
return {
|
|
4617
|
-
getArtifactsDir: () => this.sessionManager.getArtifactsDir(),
|
|
4661
|
+
getArtifactsDir: () => prepared?.artifactsDir ?? this.sessionManager.getArtifactsDir(),
|
|
4618
4662
|
isManagedDestination: () => this.sessionManager.isManagedDestination(),
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
getSessionId: () => this.sessionManager.getSessionId(),
|
|
4663
|
+
getManagedLegacyLocalMigrationSource: () =>
|
|
4664
|
+
prepared?.managedLegacyLocalMigrationSource ?? this.sessionManager.getManagedLegacyLocalMigrationSource(),
|
|
4665
|
+
getSessionId: () => prepared?.sessionId ?? this.sessionManager.getSessionId(),
|
|
4622
4666
|
};
|
|
4623
4667
|
}
|
|
4624
4668
|
|
|
@@ -6479,6 +6523,15 @@ export class AgentSession {
|
|
|
6479
6523
|
return { ...context, messages: this.#withoutEphemeralCustomMessages(context.messages) };
|
|
6480
6524
|
}
|
|
6481
6525
|
|
|
6526
|
+
/** Build display context from an unpublished successor without changing active session state. @internal */
|
|
6527
|
+
buildPreparedDisplaySessionContext(prepared: PreparedNewSession): SessionContext {
|
|
6528
|
+
const context = deobfuscateSessionContext(
|
|
6529
|
+
this.sessionManager.buildPreparedNewSessionContext(prepared),
|
|
6530
|
+
this.#obfuscator,
|
|
6531
|
+
);
|
|
6532
|
+
return { ...context, messages: this.#withoutEphemeralCustomMessages(context.messages) };
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6482
6535
|
/** Convert session messages using the same pre-LLM pipeline as the active session. */
|
|
6483
6536
|
async convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]> {
|
|
6484
6537
|
const transformedMessages = await this.#transformContext(messages, signal);
|
|
@@ -6586,9 +6639,11 @@ export class AgentSession {
|
|
|
6586
6639
|
/** Live SDK configuration values exposed through the session query surface. */
|
|
6587
6640
|
getSdkConfigItems(): Record<string, string> {
|
|
6588
6641
|
const model = this.model;
|
|
6642
|
+
const modelPreset = this.getActiveModelProfile() ?? this.settings.get("modelProfile.default");
|
|
6589
6643
|
return {
|
|
6590
6644
|
mode: this.#planModeState?.enabled ? "plan" : "default",
|
|
6591
6645
|
...(model ? { model: `${model.provider}/${model.id}` } : {}),
|
|
6646
|
+
...(modelPreset ? { modelPreset } : {}),
|
|
6592
6647
|
thinking: this.#thinkingLevel ?? "off",
|
|
6593
6648
|
steeringMode: this.steeringMode,
|
|
6594
6649
|
followUpMode: this.followUpMode,
|
|
@@ -6607,6 +6662,7 @@ export class AgentSession {
|
|
|
6607
6662
|
async invokeSkill(
|
|
6608
6663
|
name: string,
|
|
6609
6664
|
args = "",
|
|
6665
|
+
options?: Pick<PromptOptions, "onPreflightAccepted">,
|
|
6610
6666
|
): Promise<{ name: string; path: string; args?: string; lineCount?: number }> {
|
|
6611
6667
|
const skillName = name.trim();
|
|
6612
6668
|
if (!skillName) throw Object.assign(new Error("skill.invoke requires a skill name."), { code: "invalid_input" });
|
|
@@ -6639,7 +6695,7 @@ export class AgentSession {
|
|
|
6639
6695
|
attribution: "user" as const,
|
|
6640
6696
|
};
|
|
6641
6697
|
this.#deepInterviewPreclaimedCustomInputEpochs.set(skillPromptMessage, deepInterviewUserIntentEpoch);
|
|
6642
|
-
await this.promptCustomMessage(skillPromptMessage);
|
|
6698
|
+
await this.promptCustomMessage(skillPromptMessage, options);
|
|
6643
6699
|
return {
|
|
6644
6700
|
name: skill.name,
|
|
6645
6701
|
path: skill.filePath,
|
|
@@ -6685,22 +6741,49 @@ export class AgentSession {
|
|
|
6685
6741
|
}
|
|
6686
6742
|
}
|
|
6687
6743
|
|
|
6688
|
-
getTranscript():
|
|
6744
|
+
getTranscript(): ExtensionTranscriptEntry[] {
|
|
6689
6745
|
return getEntriesForInternalRead(this.sessionManager).flatMap(entry => {
|
|
6690
6746
|
if (entry.type !== "message") return [];
|
|
6691
|
-
const message = entry.message as unknown as {
|
|
6747
|
+
const message = entry.message as unknown as {
|
|
6748
|
+
role?: unknown;
|
|
6749
|
+
content?: unknown;
|
|
6750
|
+
toolCallId?: unknown;
|
|
6751
|
+
toolName?: unknown;
|
|
6752
|
+
isError?: unknown;
|
|
6753
|
+
};
|
|
6754
|
+
const content: NonNullable<ExtensionTranscriptEntry["content"]> | undefined = Array.isArray(message.content)
|
|
6755
|
+
? message.content.reduce<NonNullable<ExtensionTranscriptEntry["content"]>>((blocks, part) => {
|
|
6756
|
+
if (typeof part !== "object" || part === null || !("type" in part)) return blocks;
|
|
6757
|
+
if (part.type === "text" && "text" in part && typeof part.text === "string") {
|
|
6758
|
+
blocks.push({ type: "text", text: part.text });
|
|
6759
|
+
return blocks;
|
|
6760
|
+
}
|
|
6761
|
+
if (part.type === "thinking" && "thinking" in part && typeof part.thinking === "string") {
|
|
6762
|
+
blocks.push({ type: "thinking", thinking: part.thinking });
|
|
6763
|
+
return blocks;
|
|
6764
|
+
}
|
|
6765
|
+
if (
|
|
6766
|
+
part.type === "toolCall" &&
|
|
6767
|
+
"id" in part &&
|
|
6768
|
+
typeof part.id === "string" &&
|
|
6769
|
+
"name" in part &&
|
|
6770
|
+
typeof part.name === "string"
|
|
6771
|
+
) {
|
|
6772
|
+
blocks.push({
|
|
6773
|
+
type: "toolCall",
|
|
6774
|
+
id: part.id,
|
|
6775
|
+
name: part.name,
|
|
6776
|
+
arguments: "arguments" in part ? part.arguments : {},
|
|
6777
|
+
});
|
|
6778
|
+
}
|
|
6779
|
+
return blocks;
|
|
6780
|
+
}, [])
|
|
6781
|
+
: undefined;
|
|
6692
6782
|
const body =
|
|
6693
6783
|
typeof message.content === "string"
|
|
6694
6784
|
? message.content
|
|
6695
|
-
:
|
|
6696
|
-
?
|
|
6697
|
-
.map(part =>
|
|
6698
|
-
typeof part === "object" && part !== null && "text" in part && typeof part.text === "string"
|
|
6699
|
-
? part.text
|
|
6700
|
-
: "",
|
|
6701
|
-
)
|
|
6702
|
-
.filter(Boolean)
|
|
6703
|
-
.join("\n")
|
|
6785
|
+
: content
|
|
6786
|
+
? content.flatMap(part => (part.type === "text" ? [part.text] : [])).join("\n")
|
|
6704
6787
|
: "";
|
|
6705
6788
|
return [
|
|
6706
6789
|
{
|
|
@@ -6709,6 +6792,10 @@ export class AgentSession {
|
|
|
6709
6792
|
textSummary: body.slice(0, 500),
|
|
6710
6793
|
ts: entry.timestamp,
|
|
6711
6794
|
body,
|
|
6795
|
+
...(content ? { content } : {}),
|
|
6796
|
+
...(typeof message.toolCallId === "string" ? { toolCallId: message.toolCallId } : {}),
|
|
6797
|
+
...(typeof message.toolName === "string" ? { toolName: message.toolName } : {}),
|
|
6798
|
+
...(typeof message.isError === "boolean" ? { isError: message.isError } : {}),
|
|
6712
6799
|
},
|
|
6713
6800
|
];
|
|
6714
6801
|
});
|
|
@@ -6746,8 +6833,7 @@ export class AgentSession {
|
|
|
6746
6833
|
return getAskAnswerSourceFromRegistry(this.sessionId);
|
|
6747
6834
|
}
|
|
6748
6835
|
|
|
6749
|
-
#constructWorkflowGateEmitter(): WorkflowGateEmitter {
|
|
6750
|
-
const sessionId = this.sessionManager.getSessionId();
|
|
6836
|
+
#constructWorkflowGateEmitter(sessionId = this.sessionManager.getSessionId()): WorkflowGateEmitter {
|
|
6751
6837
|
assertNonEmptyGjcSessionId(sessionId, "AgentSession workflow-gate session");
|
|
6752
6838
|
const gateStore = this.sessionManager.isPersisted()
|
|
6753
6839
|
? new FileGateStore(path.join(sessionStateDir(this.sessionManager.getCwd(), sessionId), "workflow-gates.json"))
|
|
@@ -7266,6 +7352,22 @@ export class AgentSession {
|
|
|
7266
7352
|
async prompt(text: string, options?: PromptOptions): Promise<void> {
|
|
7267
7353
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
7268
7354
|
|
|
7355
|
+
if (expandPromptTemplates && text.startsWith("/skill:") && !options?.images?.length) {
|
|
7356
|
+
const skillCommands = new Map(this.skills.map(skill => [getSkillSlashCommandName(skill), skill]));
|
|
7357
|
+
const invocations = parseSkillInvocations(text, skillCommands);
|
|
7358
|
+
if (invocations.length === 1) {
|
|
7359
|
+
const invocation = invocations[0];
|
|
7360
|
+
if (invocation) {
|
|
7361
|
+
await this.invokeSkill(
|
|
7362
|
+
invocation.skill.name,
|
|
7363
|
+
invocation.args,
|
|
7364
|
+
options?.onPreflightAccepted ? { onPreflightAccepted: options.onPreflightAccepted } : undefined,
|
|
7365
|
+
);
|
|
7366
|
+
return;
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
|
|
7269
7371
|
// Handle extension commands first (execute immediately, even during streaming)
|
|
7270
7372
|
if (expandPromptTemplates && text.startsWith("/")) {
|
|
7271
7373
|
const handled = await this.#tryExecuteExtensionCommand(text);
|
|
@@ -7445,7 +7547,7 @@ export class AgentSession {
|
|
|
7445
7547
|
|
|
7446
7548
|
async promptCustomMessage<T = unknown>(
|
|
7447
7549
|
message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
|
|
7448
|
-
options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice" | "followUpQueuePolicy">,
|
|
7550
|
+
options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice" | "followUpQueuePolicy" | "onPreflightAccepted">,
|
|
7449
7551
|
): Promise<void> {
|
|
7450
7552
|
const textContent =
|
|
7451
7553
|
typeof message.content === "string"
|
|
@@ -7844,6 +7946,7 @@ export class AgentSession {
|
|
|
7844
7946
|
return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
|
|
7845
7947
|
},
|
|
7846
7948
|
cycleModel: () => this.cycleModel(),
|
|
7949
|
+
setModelProfile: name => this.activateModelProfileForControl(name),
|
|
7847
7950
|
cycleThinkingLevel: () => this.cycleThinkingLevel(),
|
|
7848
7951
|
setQueueMode: (kind, mode) => {
|
|
7849
7952
|
if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
|
|
@@ -7871,6 +7974,7 @@ export class AgentSession {
|
|
|
7871
7974
|
getJobs: () => undefined,
|
|
7872
7975
|
sdkBindings: () => [
|
|
7873
7976
|
"cycleModel",
|
|
7977
|
+
"setModelProfile",
|
|
7874
7978
|
"cycleThinkingLevel",
|
|
7875
7979
|
"setQueueMode",
|
|
7876
7980
|
"getSkillState",
|
|
@@ -9039,11 +9143,14 @@ export class AgentSession {
|
|
|
9039
9143
|
this.#rebindProviderSessionState(new Map());
|
|
9040
9144
|
this.agent.reset();
|
|
9041
9145
|
if (!options?.drop) await this.sessionManager.flush();
|
|
9042
|
-
await this.sessionManager.
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9146
|
+
const prepared = await this.sessionManager.prepareNewSession(options);
|
|
9147
|
+
try {
|
|
9148
|
+
// Last fallible gate while public getters still show the predecessor (#3138).
|
|
9149
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
9150
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
9151
|
+
} catch (error) {
|
|
9152
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
9153
|
+
}
|
|
9047
9154
|
this.setTodoPhases([]);
|
|
9048
9155
|
this.#syncAgentSessionId();
|
|
9049
9156
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|
|
@@ -9105,16 +9212,18 @@ export class AgentSession {
|
|
|
9105
9212
|
if (!(await manager.cancelAndSettleOwnerJobs(ownerId))) {
|
|
9106
9213
|
throw new Error("Owned async jobs did not settle before session replacement.");
|
|
9107
9214
|
}
|
|
9108
|
-
|
|
9215
|
+
const prepared = await this.sessionManager.prepareNewSession(options);
|
|
9216
|
+
try {
|
|
9217
|
+
// Last fallible gate while public getters still show the predecessor (#3138).
|
|
9218
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
9219
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
9220
|
+
} catch (error) {
|
|
9221
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
9222
|
+
}
|
|
9109
9223
|
this.#disconnectFromAgent();
|
|
9110
9224
|
this.#closeAllProviderSessions("new session");
|
|
9111
9225
|
this.#rebindProviderSessionState(new Map());
|
|
9112
9226
|
this.agent.reset();
|
|
9113
|
-
await this.sessionManager.newSession(options);
|
|
9114
|
-
// Gate the successor local:// root before the successor identity is
|
|
9115
|
-
// published to the agent, the workflow-gate emitter, hooks, or any
|
|
9116
|
-
// synchronous resolver (#2797 / #2925).
|
|
9117
|
-
await initializeLocalRoot(this.#localProtocolOptions());
|
|
9118
9227
|
this.setTodoPhases([]);
|
|
9119
9228
|
this.#syncAgentSessionId();
|
|
9120
9229
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|
|
@@ -9157,10 +9266,17 @@ export class AgentSession {
|
|
|
9157
9266
|
}
|
|
9158
9267
|
this.sessionManager.appendServiceTierChange(this.serviceTier ?? null);
|
|
9159
9268
|
if (nextDiscoverySessionToolNames) {
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9269
|
+
// Avoid a full system-prompt rebuild when the active set is unchanged; rebuild
|
|
9270
|
+
// can block on shared workspace-tree work across repeated /new transitions.
|
|
9271
|
+
const current = this.getActiveToolNames().slice().sort().join("\0");
|
|
9272
|
+
const next = [...new Set(nextDiscoverySessionToolNames.map(n => n.toLowerCase()))].sort().join("\0");
|
|
9273
|
+
if (current !== next) {
|
|
9274
|
+
await this.#applyActiveToolsByName(nextDiscoverySessionToolNames, {
|
|
9275
|
+
persistMCPSelection: false,
|
|
9276
|
+
nextSelectedDiscoveredBuiltinToolNames: [],
|
|
9277
|
+
});
|
|
9278
|
+
} else {
|
|
9279
|
+
}
|
|
9164
9280
|
}
|
|
9165
9281
|
this.#todoReminderCount = 0;
|
|
9166
9282
|
this.#planReferenceSent = false;
|
|
@@ -9173,6 +9289,7 @@ export class AgentSession {
|
|
|
9173
9289
|
reason: "new",
|
|
9174
9290
|
previousSessionFile,
|
|
9175
9291
|
});
|
|
9292
|
+
} else {
|
|
9176
9293
|
}
|
|
9177
9294
|
}
|
|
9178
9295
|
|
|
@@ -9252,16 +9369,18 @@ export class AgentSession {
|
|
|
9252
9369
|
// Flush current session to ensure all entries are written
|
|
9253
9370
|
await this.sessionManager.flush();
|
|
9254
9371
|
|
|
9255
|
-
//
|
|
9256
|
-
|
|
9257
|
-
|
|
9372
|
+
// Prepare the copied successor and complete local:// readiness while all
|
|
9373
|
+
// public manager getters remain bound to the predecessor.
|
|
9374
|
+
const prepared = await this.sessionManager.prepareFork();
|
|
9375
|
+
if (!prepared) {
|
|
9258
9376
|
return false;
|
|
9259
9377
|
}
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9378
|
+
try {
|
|
9379
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
9380
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
9381
|
+
} catch (error) {
|
|
9382
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
9383
|
+
}
|
|
9265
9384
|
this.#syncAgentSessionId();
|
|
9266
9385
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|
|
9267
9386
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -9350,6 +9469,17 @@ export class AgentSession {
|
|
|
9350
9469
|
return this.#activeModelProfile;
|
|
9351
9470
|
}
|
|
9352
9471
|
|
|
9472
|
+
/** Activate a complete model profile through a nonvisual session control. */
|
|
9473
|
+
async activateModelProfileForControl(profileName: string): Promise<boolean> {
|
|
9474
|
+
await activateModelProfile({
|
|
9475
|
+
session: this,
|
|
9476
|
+
modelRegistry: this.#modelRegistry,
|
|
9477
|
+
settings: this.settings,
|
|
9478
|
+
profileName,
|
|
9479
|
+
});
|
|
9480
|
+
return this.getActiveModelProfile() === profileName;
|
|
9481
|
+
}
|
|
9482
|
+
|
|
9353
9483
|
/** Return the persisted configured fallback selectors for a model role. */
|
|
9354
9484
|
getConfiguredModelChain(role: string): readonly string[] | undefined {
|
|
9355
9485
|
return getSessionContextForInternalRead(this.sessionManager).configuredModelChains[role]?.entries;
|
|
@@ -10652,26 +10782,36 @@ export class AgentSession {
|
|
|
10652
10782
|
// user work that agent.reset() would otherwise clear.
|
|
10653
10783
|
const rollbackAgentSteeringQueue = this.agent.snapshotSteering();
|
|
10654
10784
|
const rollbackAgentFollowUpQueue = this.agent.snapshotFollowUp();
|
|
10655
|
-
let successorSessionFile: string | undefined;
|
|
10656
10785
|
let savedPath: string | undefined;
|
|
10657
10786
|
let committed = false;
|
|
10658
|
-
|
|
10659
|
-
// commit or resumed on rollback. Suspension runs inside the transaction so a
|
|
10660
|
-
// listener fault during suspension is rolled back like any other prepare step.
|
|
10661
|
-
let suspendedWorkflowGateEmitter: WorkflowGateEmitter | undefined;
|
|
10787
|
+
let prepared: PreparedNewSession | undefined;
|
|
10662
10788
|
try {
|
|
10663
|
-
|
|
10664
|
-
//
|
|
10665
|
-
//
|
|
10666
|
-
|
|
10667
|
-
await this.sessionManager.newSession(
|
|
10789
|
+
// Prepare successor entries, persistence, display state, and gate
|
|
10790
|
+
// construction without publishing manager identity. Managed local-root
|
|
10791
|
+
// readiness is the last fallible step before the sole commit (#3138).
|
|
10792
|
+
prepared = await this.sessionManager.prepareNewSession(
|
|
10668
10793
|
previousSessionFile ? { parentSession: previousSessionFile } : undefined,
|
|
10669
10794
|
);
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
10795
|
+
if (model) this.sessionManager.appendPreparedModelChange(prepared, `${model.provider}/${model.id}`);
|
|
10796
|
+
this.sessionManager.appendPreparedThinkingLevelChange(prepared, this.thinkingLevel);
|
|
10797
|
+
this.sessionManager.appendPreparedServiceTierChange(prepared, this.serviceTier ?? null);
|
|
10798
|
+
this.sessionManager.appendPreparedCustomMessageEntry(
|
|
10799
|
+
prepared,
|
|
10800
|
+
"handoff",
|
|
10801
|
+
createHandoffContext(handoffText),
|
|
10802
|
+
true,
|
|
10803
|
+
undefined,
|
|
10804
|
+
"agent",
|
|
10805
|
+
);
|
|
10806
|
+
await this.sessionManager.ensurePreparedNewSessionOnDisk(prepared);
|
|
10807
|
+
const sessionContext = this.buildPreparedDisplaySessionContext(prepared);
|
|
10808
|
+
const successorGateEmitter = this.#constructWorkflowGateEmitter(prepared.sessionId);
|
|
10809
|
+
// Last fallible action: verified local:// readiness from immutable staged options.
|
|
10810
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
10811
|
+
|
|
10812
|
+
// --- Commit boundary: synchronous adoption is the sole identity publication.
|
|
10813
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
10814
|
+
committed = true;
|
|
10675
10815
|
this.agent.reset();
|
|
10676
10816
|
this.#syncAgentSessionId();
|
|
10677
10817
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -10680,16 +10820,8 @@ export class AgentSession {
|
|
|
10680
10820
|
this.#pendingNextTurnMessages = [];
|
|
10681
10821
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
10682
10822
|
this.#todoReminderCount = 0;
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
}
|
|
10686
|
-
this.sessionManager.appendThinkingLevelChange(this.thinkingLevel);
|
|
10687
|
-
this.sessionManager.appendServiceTierChange(this.serviceTier ?? null);
|
|
10688
|
-
|
|
10689
|
-
// Inject the handoff document as a custom message
|
|
10690
|
-
const handoffContent = createHandoffContext(handoffText);
|
|
10691
|
-
this.sessionManager.appendCustomMessageEntry("handoff", handoffContent, true, undefined, "agent");
|
|
10692
|
-
await this.sessionManager.ensureOnDisk();
|
|
10823
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
10824
|
+
this.#syncTodoPhasesFromBranch();
|
|
10693
10825
|
if (options?.autoTriggered && this.settings.get("compaction.handoffSaveToDisk")) {
|
|
10694
10826
|
try {
|
|
10695
10827
|
const artifactId = await this.sessionManager.saveArtifact(`${handoffText}\n`, "handoff");
|
|
@@ -10701,30 +10833,9 @@ export class AgentSession {
|
|
|
10701
10833
|
}
|
|
10702
10834
|
}
|
|
10703
10835
|
|
|
10704
|
-
//
|
|
10705
|
-
const sessionContext = this.buildDisplaySessionContext();
|
|
10706
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
10707
|
-
this.#syncTodoPhasesFromBranch();
|
|
10708
|
-
|
|
10709
|
-
// Construct the successor workflow-gate emitter in the reversible window:
|
|
10710
|
-
// FileGateStore load / broker init can throw, and a failure here must roll
|
|
10711
|
-
// back rather than corrupt an already-committed switch. Publication is
|
|
10712
|
-
// deferred to the (no-throw) commit step below.
|
|
10713
|
-
const successorGateEmitter = this.#constructWorkflowGateEmitter();
|
|
10714
|
-
|
|
10715
|
-
// --- Commit boundary: the successor is durably persisted and every
|
|
10716
|
-
// reversible, fallible step above has succeeded. Cross the irreversible
|
|
10717
|
-
// commit BEFORE any identity rotation. Fencing the predecessor gate
|
|
10718
|
-
// emitter and rotating provider sessions cannot be undone, so once we
|
|
10719
|
-
// begin them a failure must RETAIN the committed successor rather than
|
|
10720
|
-
// attempt a (now-impossible) rollback that would resume a fenced emitter.
|
|
10721
|
-
committed = true;
|
|
10836
|
+
// The forward-only suffix starts only after the committed successor is live.
|
|
10722
10837
|
this.#resetInjectedContextSignatures();
|
|
10723
|
-
this.#publishWorkflowGateEmitter(
|
|
10724
|
-
successorGateEmitter,
|
|
10725
|
-
rollbackSessionState.sessionId,
|
|
10726
|
-
suspendedWorkflowGateEmitter,
|
|
10727
|
-
);
|
|
10838
|
+
this.#publishWorkflowGateEmitter(successorGateEmitter, rollbackSessionState.sessionId);
|
|
10728
10839
|
this.#closeAllProviderSessions("session handoff");
|
|
10729
10840
|
this.#rebindProviderSessionState(new Map());
|
|
10730
10841
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
@@ -10760,12 +10871,12 @@ export class AgentSession {
|
|
|
10760
10871
|
return { document: handoffText, savedPath };
|
|
10761
10872
|
} catch (switchError) {
|
|
10762
10873
|
if (committed) {
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
10874
|
+
throw Object.assign(new Error("Handoff committed but successor initialization was degraded."), {
|
|
10875
|
+
code: "handoff_committed_degraded",
|
|
10876
|
+
cause: switchError,
|
|
10877
|
+
handoffDocument: handoffText,
|
|
10878
|
+
savedPath,
|
|
10767
10879
|
});
|
|
10768
|
-
return { document: handoffText, savedPath };
|
|
10769
10880
|
}
|
|
10770
10881
|
// Reversible window: roll back to the pre-handoff session so the
|
|
10771
10882
|
// failure is non-destructive. Predecessor gate emitter, provider
|
|
@@ -10773,7 +10884,6 @@ export class AgentSession {
|
|
|
10773
10884
|
// were never mutated before commit, so they survive intact.
|
|
10774
10885
|
this.sessionManager.restoreState(rollbackSessionState);
|
|
10775
10886
|
this.#syncAgentSessionId(rollbackSessionState.sessionId);
|
|
10776
|
-
this.#restoreWorkflowGateEmitter(suspendedWorkflowGateEmitter);
|
|
10777
10887
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
10778
10888
|
this.agent.replaceMessages(rollbackAgentMessages);
|
|
10779
10889
|
this.agent.clearAllQueues();
|
|
@@ -10785,25 +10895,17 @@ export class AgentSession {
|
|
|
10785
10895
|
this.#scheduledHiddenNextTurnGeneration = rollbackScheduledHiddenNextTurnGeneration;
|
|
10786
10896
|
this.#todoReminderCount = rollbackTodoReminderCount;
|
|
10787
10897
|
this.#syncTodoPhasesFromBranch();
|
|
10788
|
-
//
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
await this.sessionManager.discardUncommittedSession(successorSessionFile);
|
|
10793
|
-
} catch (cleanupError) {
|
|
10794
|
-
logger.warn("Failed to remove orphaned handoff session after rollback", {
|
|
10795
|
-
path: successorSessionFile,
|
|
10796
|
-
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
|
|
10797
|
-
});
|
|
10798
|
-
}
|
|
10799
|
-
}
|
|
10898
|
+
// Exact-discard only the staged successor; predecessor state was never adopted.
|
|
10899
|
+
const rollbackError = prepared
|
|
10900
|
+
? await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, switchError)
|
|
10901
|
+
: switchError;
|
|
10800
10902
|
// Map to cancellation only for a genuine handoff-signal abort; a
|
|
10801
10903
|
// downstream error keeps its cause and the generated document.
|
|
10802
10904
|
if (handoffSignal.aborted) {
|
|
10803
10905
|
throw new Error("Handoff cancelled");
|
|
10804
10906
|
}
|
|
10805
10907
|
// Preserve the generated handoff document for copy/retry.
|
|
10806
|
-
throw Object.assign(
|
|
10908
|
+
throw Object.assign(rollbackError instanceof Error ? rollbackError : new Error(String(rollbackError)), {
|
|
10807
10909
|
handoffDocument: handoffText,
|
|
10808
10910
|
});
|
|
10809
10911
|
}
|
|
@@ -10811,7 +10913,7 @@ export class AgentSession {
|
|
|
10811
10913
|
// Genuine handoff-signal cancellation maps to a cancellation error.
|
|
10812
10914
|
// Errors surfaced by the inner transaction (which already rolled back
|
|
10813
10915
|
// and, for non-aborts, attached the generated document) pass through.
|
|
10814
|
-
if (handoffSignal.aborted) {
|
|
10916
|
+
if (handoffSignal.aborted && !(error instanceof AggregateError)) {
|
|
10815
10917
|
throw new Error("Handoff cancelled");
|
|
10816
10918
|
}
|
|
10817
10919
|
throw error;
|
|
@@ -12501,8 +12603,14 @@ export class AgentSession {
|
|
|
12501
12603
|
break;
|
|
12502
12604
|
}
|
|
12503
12605
|
|
|
12504
|
-
|
|
12505
|
-
|
|
12606
|
+
// Legacy parsed Retry-After is capped at retry.maxDelayMs (see
|
|
12607
|
+
// compactionRetryDelay); only managed fallback is uncapped.
|
|
12608
|
+
const delayMs = compactionRetryDelay(
|
|
12609
|
+
retrySettings.baseDelayMs,
|
|
12610
|
+
retrySettings.maxDelayMs,
|
|
12611
|
+
attempt,
|
|
12612
|
+
retryAfterMs,
|
|
12613
|
+
);
|
|
12506
12614
|
|
|
12507
12615
|
// If retry delay is too long (>30s), try next candidate instead of waiting
|
|
12508
12616
|
const maxAcceptableDelayMs = 30_000;
|
|
@@ -12695,7 +12803,7 @@ export class AgentSession {
|
|
|
12695
12803
|
);
|
|
12696
12804
|
}
|
|
12697
12805
|
|
|
12698
|
-
#
|
|
12806
|
+
#alibabaTokenPlanTimeoutMessagesForApi(api: string): ReadonlySet<string> | undefined {
|
|
12699
12807
|
if (api === "openai-responses" || api === "openai-completions") {
|
|
12700
12808
|
return ALIBABA_TOKEN_PLAN_FIRST_EVENT_TIMEOUT_MESSAGES[api];
|
|
12701
12809
|
}
|
|
@@ -12704,18 +12812,23 @@ export class AgentSession {
|
|
|
12704
12812
|
|
|
12705
12813
|
#isAlibabaTokenPlanFirstEventTimeout(message: AssistantMessage): boolean {
|
|
12706
12814
|
if (message.stopReason !== "error" || message.provider !== ALIBABA_TOKEN_PLAN_PROVIDER) return false;
|
|
12707
|
-
const
|
|
12708
|
-
return
|
|
12815
|
+
const timeoutMessages = this.#alibabaTokenPlanTimeoutMessagesForApi(message.api);
|
|
12816
|
+
return timeoutMessages?.has(message.errorMessage ?? "") ?? false;
|
|
12709
12817
|
}
|
|
12710
12818
|
|
|
12711
12819
|
#isAlibabaTokenPlanCompactionTimeout(candidate: Model, errorMessage: string): boolean {
|
|
12712
12820
|
if (candidate.provider !== ALIBABA_TOKEN_PLAN_PROVIDER) return false;
|
|
12713
|
-
const
|
|
12714
|
-
if (
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12821
|
+
const timeoutMessages = this.#alibabaTokenPlanTimeoutMessagesForApi(candidate.api);
|
|
12822
|
+
if (!timeoutMessages) return false;
|
|
12823
|
+
for (const timeoutMessage of timeoutMessages) {
|
|
12824
|
+
if (
|
|
12825
|
+
errorMessage === `Summarization failed: ${timeoutMessage}` ||
|
|
12826
|
+
errorMessage === `Turn prefix summarization failed: ${timeoutMessage}`
|
|
12827
|
+
) {
|
|
12828
|
+
return true;
|
|
12829
|
+
}
|
|
12830
|
+
}
|
|
12831
|
+
return false;
|
|
12719
12832
|
}
|
|
12720
12833
|
|
|
12721
12834
|
#isTerminalProviderFirstEventTimeout(message: AssistantMessage): boolean {
|
|
@@ -13067,7 +13180,7 @@ export class AgentSession {
|
|
|
13067
13180
|
}
|
|
13068
13181
|
if (this.#isTerminalProviderFirstEventTimeout(outcome.failure.message)) {
|
|
13069
13182
|
// The managed transport discarded this attempt before session policy saw it.
|
|
13070
|
-
// Remove its provisional
|
|
13183
|
+
// Remove its provisional charge without changing sticky fallback selection.
|
|
13071
13184
|
this.#defaultFallbackChain().discardStartedAttempt();
|
|
13072
13185
|
return {
|
|
13073
13186
|
type: "terminal",
|
|
@@ -14600,8 +14713,10 @@ export class AgentSession {
|
|
|
14600
14713
|
try {
|
|
14601
14714
|
await this.sessionManager.setSessionFile(sessionPath);
|
|
14602
14715
|
// The successor identity is already rotated in the manager but not yet
|
|
14603
|
-
// published; gate its local:// root before publication so
|
|
14604
|
-
//
|
|
14716
|
+
// published; gate its local:// root before publication so the agent,
|
|
14717
|
+
// workflow-gate emitter, and hooks cannot resolve against an ungated
|
|
14718
|
+
// root. The manager-rotation window itself is tracked in #3138
|
|
14719
|
+
// (#2797 / #2925).
|
|
14605
14720
|
if (switchingToDifferentSession) await initializeLocalRoot(this.#localProtocolOptions());
|
|
14606
14721
|
this.#syncAgentSessionId();
|
|
14607
14722
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -14799,22 +14914,21 @@ export class AgentSession {
|
|
|
14799
14914
|
skipConversationRestore = result?.skipConversationRestore ?? false;
|
|
14800
14915
|
}
|
|
14801
14916
|
|
|
14802
|
-
//
|
|
14917
|
+
// Flush pending writes before preparing the successor.
|
|
14918
|
+
await this.sessionManager.flush();
|
|
14919
|
+
const prepared = selectedEntry.parentId
|
|
14920
|
+
? await this.sessionManager.prepareBranchedSession(selectedEntry.parentId)
|
|
14921
|
+
: await this.sessionManager.prepareNewSession({ parentSession: previousSessionFile });
|
|
14922
|
+
try {
|
|
14923
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
14924
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
14925
|
+
} catch (error) {
|
|
14926
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
14927
|
+
}
|
|
14803
14928
|
this.#pendingNextTurnMessages = [];
|
|
14804
14929
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
14805
|
-
|
|
14806
|
-
// Flush pending writes before branching
|
|
14807
|
-
await this.sessionManager.flush();
|
|
14808
14930
|
this.#cancelOwnAsyncJobs();
|
|
14809
14931
|
|
|
14810
|
-
if (!selectedEntry.parentId) {
|
|
14811
|
-
await this.sessionManager.newSession({ parentSession: previousSessionFile });
|
|
14812
|
-
} else {
|
|
14813
|
-
this.sessionManager.createBranchedSession(selectedEntry.parentId);
|
|
14814
|
-
}
|
|
14815
|
-
// Branch/tree-jump establishes a new session identity; gate the successor
|
|
14816
|
-
// local:// root before that identity is published (#2797 / #2925).
|
|
14817
|
-
await initializeLocalRoot(this.#localProtocolOptions());
|
|
14818
14932
|
this.#syncTodoPhasesFromBranch();
|
|
14819
14933
|
this.#syncAgentSessionId();
|
|
14820
14934
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|