@gajae-code/coding-agent 0.11.10 → 0.12.0
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 +86 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- 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/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +86 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- 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/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -1
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- 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/runtime-mcp/smithery-auth.d.ts +5 -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 +7 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +18 -2
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -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 +2 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -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 +51 -19
- 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/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +59 -13
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/skill-state/active-state.d.ts +0 -4
- package/dist/types/skill-state/workflow-hud.d.ts +3 -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/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/package.json +9 -13
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +8 -0
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +49 -2
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +91 -7
- package/src/commands/team.ts +1 -1
- 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/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +84 -3
- package/src/config/settings.ts +254 -108
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +229 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +78 -26
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +26 -16
- package/src/eval/py/executor.ts +7 -2
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -68
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -921
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +979 -71
- package/src/gjc-runtime/state-runtime.ts +50 -12
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +34 -5
- package/src/gjc-runtime/team-runtime.ts +970 -33
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +13 -7
- package/src/gjc-runtime/workflow-manifest.generated.json +97 -350
- package/src/gjc-runtime/workflow-manifest.ts +34 -179
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +10 -11
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +47 -0
- package/src/modes/acp/acp-agent.ts +597 -67
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +76 -5
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- 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/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +62 -6
- package/src/modes/controllers/selector-controller.ts +83 -37
- package/src/modes/interactive-mode.ts +127 -29
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/read.md +4 -2
- 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/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- 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 +107 -7
- 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/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +503 -60
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/reconciliation-store.ts +224 -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 +7 -1
- package/src/sdk/host/control/operations.ts +2 -1
- package/src/sdk/host/host.ts +25 -3
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/protocol/operation-inventory.generated.json +76 -0
- package/src/sdk/protocol/operation-registry.ts +8 -2
- package/src/sdk/session.ts +25 -2
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +720 -190
- 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/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +1372 -42
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +0 -10
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +65 -7
- 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/ask.ts +89 -69
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1542 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/tool-result.ts +14 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
|
@@ -18,6 +18,7 @@ import * as crypto from "node:crypto";
|
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
19
|
import * as path from "node:path";
|
|
20
20
|
import { scheduler } from "node:timers/promises";
|
|
21
|
+
import * as util from "node:util";
|
|
21
22
|
import {
|
|
22
23
|
type AfterToolCallContext,
|
|
23
24
|
type AfterToolCallResult,
|
|
@@ -172,6 +173,7 @@ import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../a
|
|
|
172
173
|
import { reset as resetCapabilities } from "../capability";
|
|
173
174
|
import type { Rule } from "../capability/rule";
|
|
174
175
|
import type { CasReceipt } from "../config/atomic-yaml-patch";
|
|
176
|
+
import { activateModelProfile } from "../config/model-profile-activation";
|
|
175
177
|
import {
|
|
176
178
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
177
179
|
isAuthenticated,
|
|
@@ -233,7 +235,7 @@ import type {
|
|
|
233
235
|
TurnEndEvent,
|
|
234
236
|
TurnStartEvent,
|
|
235
237
|
} from "../extensibility/extensions";
|
|
236
|
-
import type { CompactOptions, ContextUsage } from "../extensibility/extensions/types";
|
|
238
|
+
import type { CompactOptions, ContextUsage, ExtensionTranscriptEntry } from "../extensibility/extensions/types";
|
|
237
239
|
import { ExtensionToolWrapper } from "../extensibility/extensions/wrapper";
|
|
238
240
|
import {
|
|
239
241
|
type LoadedSubskillActivation,
|
|
@@ -243,10 +245,22 @@ import { resolveCurrentPhaseForParent } from "../extensibility/gjc-plugins/injec
|
|
|
243
245
|
import { readActiveSubskillsForParent, toActiveSubskillEntry } from "../extensibility/gjc-plugins/state";
|
|
244
246
|
import { loadActiveSubskillTools } from "../extensibility/gjc-plugins/tools";
|
|
245
247
|
import type { HookCommandContext } from "../extensibility/hooks/types";
|
|
246
|
-
import
|
|
248
|
+
import type { SessionSwitchEvent } from "../extensibility/shared-events";
|
|
249
|
+
import {
|
|
250
|
+
buildSkillPromptMessage,
|
|
251
|
+
getSkillSlashCommandName,
|
|
252
|
+
parseSkillInvocations,
|
|
253
|
+
type Skill,
|
|
254
|
+
type SkillWarning,
|
|
255
|
+
} from "../extensibility/skills";
|
|
247
256
|
import { expandSlashCommand, type FileSlashCommand } from "../extensibility/slash-commands";
|
|
248
257
|
import { assertDeepInterviewIntentManifest } from "../gjc-runtime/deep-interview-state";
|
|
249
258
|
import { buildGjcRuntimeSessionEnv, consumePendingGoalModeRequest } from "../gjc-runtime/goal-mode-request";
|
|
259
|
+
import {
|
|
260
|
+
isMemoryGuardClaimsLease,
|
|
261
|
+
isMemoryGuardClaimsLeaseForStateDir,
|
|
262
|
+
type MemoryGuardClaimsLease,
|
|
263
|
+
} from "../gjc-runtime/memory-guard-owner-claims";
|
|
250
264
|
import {
|
|
251
265
|
assertNonEmptyGjcSessionId,
|
|
252
266
|
modeStatePath as sessionModeStatePath,
|
|
@@ -341,6 +355,7 @@ import {
|
|
|
341
355
|
import {
|
|
342
356
|
type ConfiguredFallbackChain,
|
|
343
357
|
cappedExponentialWithFullJitter,
|
|
358
|
+
compactionRetryDelay,
|
|
344
359
|
effectiveFallbackDelay,
|
|
345
360
|
FallbackChainController,
|
|
346
361
|
} from "./fallback-chain-controller";
|
|
@@ -360,6 +375,7 @@ import {
|
|
|
360
375
|
prepareContributionPrep,
|
|
361
376
|
} from "./contribution-prep";
|
|
362
377
|
import { pruneStaleFileMentions } from "./file-mention-pruning";
|
|
378
|
+
import type { MemoryGuardRestoreResult } from "./memory-guard-checkpoint-participant";
|
|
363
379
|
import {
|
|
364
380
|
type BashExecutionMessage,
|
|
365
381
|
type CompactionSummaryMessage,
|
|
@@ -379,6 +395,7 @@ import type {
|
|
|
379
395
|
CompactionEntry,
|
|
380
396
|
DefaultModelSelectionStage,
|
|
381
397
|
NewSessionOptions,
|
|
398
|
+
PreparedNewSession,
|
|
382
399
|
RecoveryHydrationContext,
|
|
383
400
|
RecoveryHydrationPromotionFence,
|
|
384
401
|
SessionContext,
|
|
@@ -604,6 +621,24 @@ export interface AgentSessionConfig {
|
|
|
604
621
|
providerCacheSessionId?: string;
|
|
605
622
|
}
|
|
606
623
|
|
|
624
|
+
export interface AgentSessionMemoryGuardRestoreInput
|
|
625
|
+
extends Omit<AgentSessionConfig, "sessionManager" | "recoveryHydrationContext"> {
|
|
626
|
+
staged: Extract<MemoryGuardRestoreResult, { kind: "staged" }>;
|
|
627
|
+
claimsLease: MemoryGuardClaimsLease;
|
|
628
|
+
claimsStateDir: string;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export interface AgentMemoryGuardPromotionFence extends RecoveryHydrationPromotionFence {
|
|
632
|
+
readonly claimsLease: MemoryGuardClaimsLease;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
export type AgentMemoryGuardRestoreResult =
|
|
636
|
+
| { kind: "staged"; session: AgentSession; promotionFence: AgentMemoryGuardPromotionFence }
|
|
637
|
+
| {
|
|
638
|
+
kind: "blocked";
|
|
639
|
+
reason: "transcript-mismatch" | "hydration-context-mismatch" | "claim-mismatch" | "agent-messages-mismatch";
|
|
640
|
+
};
|
|
641
|
+
|
|
607
642
|
type MidRunMaintenanceLifecycle = Parameters<NonNullable<AgentLoopConfig["maintainContext"]>>[1];
|
|
608
643
|
|
|
609
644
|
type AutoCompactionTerminalStatus =
|
|
@@ -633,8 +668,16 @@ export interface PromptOptions {
|
|
|
633
668
|
/**
|
|
634
669
|
* Invoked after all prompt preflight checks pass and immediately before agent execution begins.
|
|
635
670
|
* Cancellation before this callback rejects the prompt.
|
|
671
|
+
* Prefer `onPreflightAcceptCommit` for async durable acceptance (#3031/#3032).
|
|
636
672
|
*/
|
|
637
673
|
onPreflightAccepted?: () => void;
|
|
674
|
+
/**
|
|
675
|
+
* Awaitable durable-accept fence. Called after preflight and before queue mutation
|
|
676
|
+
* or `#promptAgentWithIdleRetry`. SDK bus installs a closure that fsyncs acceptance.
|
|
677
|
+
*/
|
|
678
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
679
|
+
/** Skill-only: prepared metadata before the durable fence (path/lineCount/cleanedArgs). */
|
|
680
|
+
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
638
681
|
}
|
|
639
682
|
|
|
640
683
|
function promptPreflightCancelledError(): Error {
|
|
@@ -798,8 +841,14 @@ const KIMI_CODE_FIRST_EVENT_TIMEOUT_MESSAGES = {
|
|
|
798
841
|
|
|
799
842
|
const ALIBABA_TOKEN_PLAN_PROVIDER = "alibaba-token-plan";
|
|
800
843
|
const ALIBABA_TOKEN_PLAN_FIRST_EVENT_TIMEOUT_MESSAGES = {
|
|
801
|
-
"openai-responses":
|
|
802
|
-
|
|
844
|
+
"openai-responses": new Set([
|
|
845
|
+
"Provider stream timed out while waiting for the first event",
|
|
846
|
+
"OpenAI responses stream timed out while waiting for the first event",
|
|
847
|
+
]),
|
|
848
|
+
"openai-completions": new Set([
|
|
849
|
+
"Provider stream timed out while waiting for the first event",
|
|
850
|
+
"OpenAI completions stream timed out while waiting for the first event",
|
|
851
|
+
]),
|
|
803
852
|
} as const;
|
|
804
853
|
|
|
805
854
|
function hasBareDefaultRetryDisqualifyingFacts(message: AssistantMessage): boolean {
|
|
@@ -1043,6 +1092,22 @@ function createHandoffContext(document: string): string {
|
|
|
1043
1092
|
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
1093
|
}
|
|
1045
1094
|
|
|
1095
|
+
async function discardPreparedNewSessionAfterFailure(
|
|
1096
|
+
sessionManager: SessionManager,
|
|
1097
|
+
prepared: PreparedNewSession,
|
|
1098
|
+
transitionError: unknown,
|
|
1099
|
+
): Promise<unknown> {
|
|
1100
|
+
try {
|
|
1101
|
+
await sessionManager.discardPreparedNewSession(prepared);
|
|
1102
|
+
} catch (cleanupError) {
|
|
1103
|
+
throw new AggregateError(
|
|
1104
|
+
[transitionError, cleanupError],
|
|
1105
|
+
"Session transition and staged successor cleanup both failed.",
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
return transitionError;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1046
1111
|
// ============================================================================
|
|
1047
1112
|
// ACP Permission Gate
|
|
1048
1113
|
// ============================================================================
|
|
@@ -1507,6 +1572,47 @@ export class AgentSession {
|
|
|
1507
1572
|
#sessionAdmissionClosed = false;
|
|
1508
1573
|
#sessionAdmissionContext = new AsyncLocalStorage<SessionAdmissionEntry>();
|
|
1509
1574
|
#defaultModelSelectionMutationRevision = 0;
|
|
1575
|
+
#thinkingLevelMutationRevision = 0;
|
|
1576
|
+
#thinkingVisibilityMutationRevision = 0;
|
|
1577
|
+
#thinkingLevelLiveMutationRevision = 0;
|
|
1578
|
+
#thinkingVisibilityLiveMutationRevision = 0;
|
|
1579
|
+
#reasoningControlContextGeneration = 0;
|
|
1580
|
+
#pendingThinkingLevelControlSuccess:
|
|
1581
|
+
| {
|
|
1582
|
+
level: ThinkingLevel;
|
|
1583
|
+
mutationRevision: number;
|
|
1584
|
+
sessionId: string;
|
|
1585
|
+
model: Model | undefined;
|
|
1586
|
+
contextGeneration: number;
|
|
1587
|
+
}
|
|
1588
|
+
| undefined;
|
|
1589
|
+
#pendingThinkingVisibilityControlSuccess:
|
|
1590
|
+
| {
|
|
1591
|
+
visibility: "visible" | "hidden";
|
|
1592
|
+
mutationRevision: number;
|
|
1593
|
+
sessionId: string;
|
|
1594
|
+
model: Model | undefined;
|
|
1595
|
+
contextGeneration: number;
|
|
1596
|
+
}
|
|
1597
|
+
| undefined;
|
|
1598
|
+
#pendingThinkingLevelControlFailure:
|
|
1599
|
+
| {
|
|
1600
|
+
mutationRevision: number;
|
|
1601
|
+
liveMutationRevision: number;
|
|
1602
|
+
sessionId: string;
|
|
1603
|
+
model: Model | undefined;
|
|
1604
|
+
contextGeneration: number;
|
|
1605
|
+
}
|
|
1606
|
+
| undefined;
|
|
1607
|
+
#pendingThinkingVisibilityControlFailure:
|
|
1608
|
+
| {
|
|
1609
|
+
mutationRevision: number;
|
|
1610
|
+
liveMutationRevision: number;
|
|
1611
|
+
sessionId: string;
|
|
1612
|
+
model: Model | undefined;
|
|
1613
|
+
contextGeneration: number;
|
|
1614
|
+
}
|
|
1615
|
+
| undefined;
|
|
1510
1616
|
#promptTemplates: PromptTemplate[];
|
|
1511
1617
|
#slashCommands: FileSlashCommand[];
|
|
1512
1618
|
|
|
@@ -1758,6 +1864,7 @@ export class AgentSession {
|
|
|
1758
1864
|
#constructorMCPToolSelection: string[] | undefined;
|
|
1759
1865
|
#constructorDiscoveredBuiltinToolSelection: string[] | undefined;
|
|
1760
1866
|
#recoveryHydrationContext: RecoveryHydrationContext | undefined;
|
|
1867
|
+
#memoryGuardClaimsLease: MemoryGuardClaimsLease | undefined;
|
|
1761
1868
|
|
|
1762
1869
|
// TTSR manager for time-traveling stream rules
|
|
1763
1870
|
#ttsrManager: TtsrManager | undefined = undefined;
|
|
@@ -2213,6 +2320,7 @@ export class AgentSession {
|
|
|
2213
2320
|
this.#unregisterResourceGc = registerResourceGcSession({
|
|
2214
2321
|
sessionId: resourceGcSessionId,
|
|
2215
2322
|
settings: this.settings,
|
|
2323
|
+
cwd: () => this.sessionManager.getCwd(),
|
|
2216
2324
|
});
|
|
2217
2325
|
}
|
|
2218
2326
|
this.#unregisterRuntimeStateFinalizer = registerCoordinatorRuntimeStateFinalizer({
|
|
@@ -2640,9 +2748,15 @@ export class AgentSession {
|
|
|
2640
2748
|
this.#defaultFallbackController = scope.fallbackController;
|
|
2641
2749
|
const previousEditMode = this.#resolveActiveEditMode();
|
|
2642
2750
|
if (scope.model) {
|
|
2643
|
-
this
|
|
2751
|
+
this.#setAgentModelWithReasoningContext(scope.model);
|
|
2644
2752
|
this.#syncAppendOnlyContext(scope.model);
|
|
2645
2753
|
}
|
|
2754
|
+
this.#thinkingLevelMutationRevision++;
|
|
2755
|
+
this.#thinkingLevelLiveMutationRevision++;
|
|
2756
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
2757
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
2758
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
2759
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
2646
2760
|
this.#thinkingLevel = scope.thinkingLevel;
|
|
2647
2761
|
this.agent.setThinkingLevel(toReasoningEffort(scope.thinkingLevel));
|
|
2648
2762
|
void this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
@@ -4008,8 +4122,21 @@ export class AgentSession {
|
|
|
4008
4122
|
return;
|
|
4009
4123
|
}
|
|
4010
4124
|
const predecessorAgentEnd = this.#claimDeferredAgentEndForContinuation(predecessorAgentEndHold);
|
|
4125
|
+
const startsQueuedSuccessor =
|
|
4126
|
+
this.agent.state.messages.at(-1)?.role === "assistant" && this.agent.hasQueuedMessages();
|
|
4011
4127
|
try {
|
|
4012
|
-
await this.agent.continue(
|
|
4128
|
+
await this.agent.continue({
|
|
4129
|
+
...this.#managedFallbackPromptOptions(),
|
|
4130
|
+
// Reset only after continue() has claimed the queued turn. Skipped or stale
|
|
4131
|
+
// continuations retain predecessor accounting, and resetAttemptBudget keeps
|
|
4132
|
+
// the sticky fallback cursor unchanged.
|
|
4133
|
+
onRunAccepted: startsQueuedSuccessor
|
|
4134
|
+
? () => {
|
|
4135
|
+
this.#defaultFallbackChain().resetAttemptBudget();
|
|
4136
|
+
this.#overflowMaintenanceAttempts = 0;
|
|
4137
|
+
}
|
|
4138
|
+
: undefined,
|
|
4139
|
+
});
|
|
4013
4140
|
} catch (error) {
|
|
4014
4141
|
this.#restoreDeferredAgentEndAfterContinuationFailure(predecessorAgentEnd);
|
|
4015
4142
|
throw error;
|
|
@@ -4612,13 +4739,13 @@ export class AgentSession {
|
|
|
4612
4739
|
return resolveToCwd(normalized, this.sessionManager.getCwd());
|
|
4613
4740
|
}
|
|
4614
4741
|
|
|
4615
|
-
#localProtocolOptions(): LocalProtocolOptions {
|
|
4742
|
+
#localProtocolOptions(prepared?: PreparedNewSession): LocalProtocolOptions {
|
|
4616
4743
|
return {
|
|
4617
|
-
getArtifactsDir: () => this.sessionManager.getArtifactsDir(),
|
|
4744
|
+
getArtifactsDir: () => prepared?.artifactsDir ?? this.sessionManager.getArtifactsDir(),
|
|
4618
4745
|
isManagedDestination: () => this.sessionManager.isManagedDestination(),
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
getSessionId: () => this.sessionManager.getSessionId(),
|
|
4746
|
+
getManagedLegacyLocalMigrationSource: () =>
|
|
4747
|
+
prepared?.managedLegacyLocalMigrationSource ?? this.sessionManager.getManagedLegacyLocalMigrationSource(),
|
|
4748
|
+
getSessionId: () => prepared?.sessionId ?? this.sessionManager.getSessionId(),
|
|
4622
4749
|
};
|
|
4623
4750
|
}
|
|
4624
4751
|
|
|
@@ -5073,6 +5200,7 @@ export class AgentSession {
|
|
|
5073
5200
|
* such event.
|
|
5074
5201
|
*/
|
|
5075
5202
|
#syncAgentSessionId(sessionId?: string): void {
|
|
5203
|
+
this.#reasoningControlContextGeneration++;
|
|
5076
5204
|
const sid = this.#providerSessionId ?? sessionId ?? this.sessionManager.getSessionId();
|
|
5077
5205
|
this.agent.sessionId = sid;
|
|
5078
5206
|
this.agent.providerSessionId = this.#providerCacheSessionId ?? sid;
|
|
@@ -6397,17 +6525,65 @@ export class AgentSession {
|
|
|
6397
6525
|
return this.#promptGeneration;
|
|
6398
6526
|
}
|
|
6399
6527
|
|
|
6528
|
+
static async restoreFromMemoryGuardCheckpoint(
|
|
6529
|
+
input: AgentSessionMemoryGuardRestoreInput,
|
|
6530
|
+
): Promise<AgentMemoryGuardRestoreResult> {
|
|
6531
|
+
const { staged, claimsLease, claimsStateDir, ...config } = input;
|
|
6532
|
+
if (
|
|
6533
|
+
staged.manager.getSessionId() !== staged.transcriptIdentity.sessionId ||
|
|
6534
|
+
staged.hydrationContext.identity.sessionId !== staged.transcriptIdentity.sessionId
|
|
6535
|
+
) {
|
|
6536
|
+
await staged.cleanup();
|
|
6537
|
+
return { kind: "blocked", reason: "transcript-mismatch" };
|
|
6538
|
+
}
|
|
6539
|
+
if (
|
|
6540
|
+
!isMemoryGuardClaimsLeaseForStateDir(claimsLease, claimsStateDir) ||
|
|
6541
|
+
claimsLease.owner.sessionId !== staged.transcriptIdentity.sessionId
|
|
6542
|
+
) {
|
|
6543
|
+
await staged.cleanup();
|
|
6544
|
+
return { kind: "blocked", reason: "claim-mismatch" };
|
|
6545
|
+
}
|
|
6546
|
+
const checkpointMessages = staged.manager.buildSessionContext().messages;
|
|
6547
|
+
if (!util.isDeepStrictEqual(config.agent.state.messages, checkpointMessages)) {
|
|
6548
|
+
await staged.cleanup();
|
|
6549
|
+
return { kind: "blocked", reason: "agent-messages-mismatch" };
|
|
6550
|
+
}
|
|
6551
|
+
const session = new AgentSession({
|
|
6552
|
+
...config,
|
|
6553
|
+
sessionManager: staged.manager,
|
|
6554
|
+
recoveryHydrationContext: staged.hydrationContext,
|
|
6555
|
+
});
|
|
6556
|
+
session.#memoryGuardClaimsLease = claimsLease;
|
|
6557
|
+
if (session.recoveryHydrationContext !== staged.hydrationContext) {
|
|
6558
|
+
await session.dispose();
|
|
6559
|
+
await staged.cleanup();
|
|
6560
|
+
return { kind: "blocked", reason: "hydration-context-mismatch" };
|
|
6561
|
+
}
|
|
6562
|
+
return {
|
|
6563
|
+
kind: "staged",
|
|
6564
|
+
session,
|
|
6565
|
+
promotionFence: Object.freeze({ ownershipReady: true, claimsLease }),
|
|
6566
|
+
};
|
|
6567
|
+
}
|
|
6568
|
+
|
|
6400
6569
|
/** The immutable recovery authority, present only before external ownership promotion. */
|
|
6401
6570
|
get recoveryHydrationContext(): RecoveryHydrationContext | undefined {
|
|
6402
6571
|
return this.#recoveryHydrationContext;
|
|
6403
6572
|
}
|
|
6404
6573
|
|
|
6405
6574
|
/** Enables normal session mutations after the owner has published its durable fence and writer lease. */
|
|
6406
|
-
async promoteRecoveryHydrationAfterOwnershipReadyFence(fence:
|
|
6575
|
+
async promoteRecoveryHydrationAfterOwnershipReadyFence(fence: AgentMemoryGuardPromotionFence): Promise<void> {
|
|
6407
6576
|
const context = this.#recoveryHydrationContext;
|
|
6408
6577
|
if (!context) throw new Error("Agent session is not awaiting recovery hydration promotion.");
|
|
6578
|
+
if (!isMemoryGuardClaimsLease(fence.claimsLease)) {
|
|
6579
|
+
throw new Error("Recovery hydration promotion requires a live memory-guard claims lease.");
|
|
6580
|
+
}
|
|
6581
|
+
if (this.#memoryGuardClaimsLease !== fence.claimsLease) {
|
|
6582
|
+
throw new Error("Recovery hydration promotion requires the acquired memory-guard claims lease.");
|
|
6583
|
+
}
|
|
6409
6584
|
await this.sessionManager.promoteRecoveryHydrationAfterOwnershipReadyFence(context, fence);
|
|
6410
6585
|
this.#recoveryHydrationContext = undefined;
|
|
6586
|
+
this.#memoryGuardClaimsLease = undefined;
|
|
6411
6587
|
}
|
|
6412
6588
|
|
|
6413
6589
|
/** Recovery hydration must not start a continuation before ownership promotion. */
|
|
@@ -6479,6 +6655,15 @@ export class AgentSession {
|
|
|
6479
6655
|
return { ...context, messages: this.#withoutEphemeralCustomMessages(context.messages) };
|
|
6480
6656
|
}
|
|
6481
6657
|
|
|
6658
|
+
/** Build display context from an unpublished successor without changing active session state. @internal */
|
|
6659
|
+
buildPreparedDisplaySessionContext(prepared: PreparedNewSession): SessionContext {
|
|
6660
|
+
const context = deobfuscateSessionContext(
|
|
6661
|
+
this.sessionManager.buildPreparedNewSessionContext(prepared),
|
|
6662
|
+
this.#obfuscator,
|
|
6663
|
+
);
|
|
6664
|
+
return { ...context, messages: this.#withoutEphemeralCustomMessages(context.messages) };
|
|
6665
|
+
}
|
|
6666
|
+
|
|
6482
6667
|
/** Convert session messages using the same pre-LLM pipeline as the active session. */
|
|
6483
6668
|
async convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]> {
|
|
6484
6669
|
const transformedMessages = await this.#transformContext(messages, signal);
|
|
@@ -6586,9 +6771,11 @@ export class AgentSession {
|
|
|
6586
6771
|
/** Live SDK configuration values exposed through the session query surface. */
|
|
6587
6772
|
getSdkConfigItems(): Record<string, string> {
|
|
6588
6773
|
const model = this.model;
|
|
6774
|
+
const modelPreset = this.getActiveModelProfile() ?? this.settings.get("modelProfile.default");
|
|
6589
6775
|
return {
|
|
6590
6776
|
mode: this.#planModeState?.enabled ? "plan" : "default",
|
|
6591
6777
|
...(model ? { model: `${model.provider}/${model.id}` } : {}),
|
|
6778
|
+
...(modelPreset ? { modelPreset } : {}),
|
|
6592
6779
|
thinking: this.#thinkingLevel ?? "off",
|
|
6593
6780
|
steeringMode: this.steeringMode,
|
|
6594
6781
|
followUpMode: this.followUpMode,
|
|
@@ -6607,6 +6794,7 @@ export class AgentSession {
|
|
|
6607
6794
|
async invokeSkill(
|
|
6608
6795
|
name: string,
|
|
6609
6796
|
args = "",
|
|
6797
|
+
options?: Pick<PromptOptions, "onPreflightAccepted" | "onPreflightAcceptCommit" | "onSkillPrepared">,
|
|
6610
6798
|
): Promise<{ name: string; path: string; args?: string; lineCount?: number }> {
|
|
6611
6799
|
const skillName = name.trim();
|
|
6612
6800
|
if (!skillName) throw Object.assign(new Error("skill.invoke requires a skill name."), { code: "invalid_input" });
|
|
@@ -6639,7 +6827,13 @@ export class AgentSession {
|
|
|
6639
6827
|
attribution: "user" as const,
|
|
6640
6828
|
};
|
|
6641
6829
|
this.#deepInterviewPreclaimedCustomInputEpochs.set(skillPromptMessage, deepInterviewUserIntentEpoch);
|
|
6642
|
-
|
|
6830
|
+
options?.onSkillPrepared?.({
|
|
6831
|
+
name: skill.name,
|
|
6832
|
+
path: skill.filePath,
|
|
6833
|
+
lineCount: built.details.lineCount,
|
|
6834
|
+
cleanedArgs: activation.cleanedArgs || undefined,
|
|
6835
|
+
});
|
|
6836
|
+
await this.promptCustomMessage(skillPromptMessage, options);
|
|
6643
6837
|
return {
|
|
6644
6838
|
name: skill.name,
|
|
6645
6839
|
path: skill.filePath,
|
|
@@ -6685,22 +6879,49 @@ export class AgentSession {
|
|
|
6685
6879
|
}
|
|
6686
6880
|
}
|
|
6687
6881
|
|
|
6688
|
-
getTranscript():
|
|
6882
|
+
getTranscript(): ExtensionTranscriptEntry[] {
|
|
6689
6883
|
return getEntriesForInternalRead(this.sessionManager).flatMap(entry => {
|
|
6690
6884
|
if (entry.type !== "message") return [];
|
|
6691
|
-
const message = entry.message as unknown as {
|
|
6885
|
+
const message = entry.message as unknown as {
|
|
6886
|
+
role?: unknown;
|
|
6887
|
+
content?: unknown;
|
|
6888
|
+
toolCallId?: unknown;
|
|
6889
|
+
toolName?: unknown;
|
|
6890
|
+
isError?: unknown;
|
|
6891
|
+
};
|
|
6892
|
+
const content: NonNullable<ExtensionTranscriptEntry["content"]> | undefined = Array.isArray(message.content)
|
|
6893
|
+
? message.content.reduce<NonNullable<ExtensionTranscriptEntry["content"]>>((blocks, part) => {
|
|
6894
|
+
if (typeof part !== "object" || part === null || !("type" in part)) return blocks;
|
|
6895
|
+
if (part.type === "text" && "text" in part && typeof part.text === "string") {
|
|
6896
|
+
blocks.push({ type: "text", text: part.text });
|
|
6897
|
+
return blocks;
|
|
6898
|
+
}
|
|
6899
|
+
if (part.type === "thinking" && "thinking" in part && typeof part.thinking === "string") {
|
|
6900
|
+
blocks.push({ type: "thinking", thinking: part.thinking });
|
|
6901
|
+
return blocks;
|
|
6902
|
+
}
|
|
6903
|
+
if (
|
|
6904
|
+
part.type === "toolCall" &&
|
|
6905
|
+
"id" in part &&
|
|
6906
|
+
typeof part.id === "string" &&
|
|
6907
|
+
"name" in part &&
|
|
6908
|
+
typeof part.name === "string"
|
|
6909
|
+
) {
|
|
6910
|
+
blocks.push({
|
|
6911
|
+
type: "toolCall",
|
|
6912
|
+
id: part.id,
|
|
6913
|
+
name: part.name,
|
|
6914
|
+
arguments: "arguments" in part ? part.arguments : {},
|
|
6915
|
+
});
|
|
6916
|
+
}
|
|
6917
|
+
return blocks;
|
|
6918
|
+
}, [])
|
|
6919
|
+
: undefined;
|
|
6692
6920
|
const body =
|
|
6693
6921
|
typeof message.content === "string"
|
|
6694
6922
|
? 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")
|
|
6923
|
+
: content
|
|
6924
|
+
? content.flatMap(part => (part.type === "text" ? [part.text] : [])).join("\n")
|
|
6704
6925
|
: "";
|
|
6705
6926
|
return [
|
|
6706
6927
|
{
|
|
@@ -6709,6 +6930,10 @@ export class AgentSession {
|
|
|
6709
6930
|
textSummary: body.slice(0, 500),
|
|
6710
6931
|
ts: entry.timestamp,
|
|
6711
6932
|
body,
|
|
6933
|
+
...(content ? { content } : {}),
|
|
6934
|
+
...(typeof message.toolCallId === "string" ? { toolCallId: message.toolCallId } : {}),
|
|
6935
|
+
...(typeof message.toolName === "string" ? { toolName: message.toolName } : {}),
|
|
6936
|
+
...(typeof message.isError === "boolean" ? { isError: message.isError } : {}),
|
|
6712
6937
|
},
|
|
6713
6938
|
];
|
|
6714
6939
|
});
|
|
@@ -6746,8 +6971,7 @@ export class AgentSession {
|
|
|
6746
6971
|
return getAskAnswerSourceFromRegistry(this.sessionId);
|
|
6747
6972
|
}
|
|
6748
6973
|
|
|
6749
|
-
#constructWorkflowGateEmitter(): WorkflowGateEmitter {
|
|
6750
|
-
const sessionId = this.sessionManager.getSessionId();
|
|
6974
|
+
#constructWorkflowGateEmitter(sessionId = this.sessionManager.getSessionId()): WorkflowGateEmitter {
|
|
6751
6975
|
assertNonEmptyGjcSessionId(sessionId, "AgentSession workflow-gate session");
|
|
6752
6976
|
const gateStore = this.sessionManager.isPersisted()
|
|
6753
6977
|
? new FileGateStore(path.join(sessionStateDir(this.sessionManager.getCwd(), sessionId), "workflow-gates.json"))
|
|
@@ -7264,8 +7488,32 @@ export class AgentSession {
|
|
|
7264
7488
|
* @throws Error if no model selected or no API key available (when not streaming)
|
|
7265
7489
|
*/
|
|
7266
7490
|
async prompt(text: string, options?: PromptOptions): Promise<void> {
|
|
7491
|
+
this.#assertRecoveryHydrationPromoted();
|
|
7267
7492
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
7268
7493
|
|
|
7494
|
+
if (expandPromptTemplates && text.startsWith("/skill:") && !options?.images?.length) {
|
|
7495
|
+
const skillCommands = new Map(this.skills.map(skill => [getSkillSlashCommandName(skill), skill]));
|
|
7496
|
+
const invocations = parseSkillInvocations(text, skillCommands);
|
|
7497
|
+
if (invocations.length === 1) {
|
|
7498
|
+
const invocation = invocations[0];
|
|
7499
|
+
if (invocation) {
|
|
7500
|
+
await this.invokeSkill(
|
|
7501
|
+
invocation.skill.name,
|
|
7502
|
+
invocation.args,
|
|
7503
|
+
options?.onPreflightAccepted || options?.onPreflightAcceptCommit
|
|
7504
|
+
? {
|
|
7505
|
+
...(options.onPreflightAccepted ? { onPreflightAccepted: options.onPreflightAccepted } : {}),
|
|
7506
|
+
...(options.onPreflightAcceptCommit
|
|
7507
|
+
? { onPreflightAcceptCommit: options.onPreflightAcceptCommit }
|
|
7508
|
+
: {}),
|
|
7509
|
+
}
|
|
7510
|
+
: undefined,
|
|
7511
|
+
);
|
|
7512
|
+
return;
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
}
|
|
7516
|
+
|
|
7269
7517
|
// Handle extension commands first (execute immediately, even during streaming)
|
|
7270
7518
|
if (expandPromptTemplates && text.startsWith("/")) {
|
|
7271
7519
|
const handled = await this.#tryExecuteExtensionCommand(text);
|
|
@@ -7313,7 +7561,8 @@ export class AgentSession {
|
|
|
7313
7561
|
if (workflowIntentDiff) {
|
|
7314
7562
|
this.sessionManager.appendCustomEntry(WORKFLOW_INTENT_DIFF_CUSTOM_TYPE, workflowIntentDiff);
|
|
7315
7563
|
}
|
|
7316
|
-
options?.
|
|
7564
|
+
if (options?.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
7565
|
+
else options?.onPreflightAccepted?.();
|
|
7317
7566
|
return;
|
|
7318
7567
|
}
|
|
7319
7568
|
|
|
@@ -7445,7 +7694,10 @@ export class AgentSession {
|
|
|
7445
7694
|
|
|
7446
7695
|
async promptCustomMessage<T = unknown>(
|
|
7447
7696
|
message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
|
|
7448
|
-
options?: Pick<
|
|
7697
|
+
options?: Pick<
|
|
7698
|
+
PromptOptions,
|
|
7699
|
+
"streamingBehavior" | "toolChoice" | "followUpQueuePolicy" | "onPreflightAccepted" | "onPreflightAcceptCommit"
|
|
7700
|
+
>,
|
|
7449
7701
|
): Promise<void> {
|
|
7450
7702
|
const textContent =
|
|
7451
7703
|
typeof message.content === "string"
|
|
@@ -7507,7 +7759,10 @@ export class AgentSession {
|
|
|
7507
7759
|
async #promptWithMessage(
|
|
7508
7760
|
message: AgentMessage,
|
|
7509
7761
|
expandedText: string,
|
|
7510
|
-
options?: Pick<
|
|
7762
|
+
options?: Pick<
|
|
7763
|
+
PromptOptions,
|
|
7764
|
+
"toolChoice" | "images" | "skipCompactionCheck" | "onPreflightAccepted" | "onPreflightAcceptCommit"
|
|
7765
|
+
> & {
|
|
7511
7766
|
prependMessages?: AgentMessage[];
|
|
7512
7767
|
skipPostPromptRecoveryWait?: boolean;
|
|
7513
7768
|
predecessorAgentEndHold?: symbol;
|
|
@@ -7614,7 +7869,7 @@ export class AgentSession {
|
|
|
7614
7869
|
// A newer abort/prompt cycle superseded this preflight. Callers awaiting
|
|
7615
7870
|
// acceptance (onPreflightAccepted) must be told it never ran; direct
|
|
7616
7871
|
// callers (e.g. prompt() aborted during a TTSR wait) resolve gracefully.
|
|
7617
|
-
if (options?.onPreflightAccepted) throw promptPreflightCancelledError();
|
|
7872
|
+
if (options?.onPreflightAccepted || options?.onPreflightAcceptCommit) throw promptPreflightCancelledError();
|
|
7618
7873
|
return;
|
|
7619
7874
|
}
|
|
7620
7875
|
|
|
@@ -7728,7 +7983,7 @@ export class AgentSession {
|
|
|
7728
7983
|
this.#resetInjectedContextSignatures();
|
|
7729
7984
|
// Ack-waiting callers are told the preflight never ran; direct callers
|
|
7730
7985
|
// (aborted after setup) resolve gracefully as before f24f46ff5.
|
|
7731
|
-
if (options?.onPreflightAccepted) throw promptPreflightCancelledError();
|
|
7986
|
+
if (options?.onPreflightAccepted || options?.onPreflightAcceptCommit) throw promptPreflightCancelledError();
|
|
7732
7987
|
return;
|
|
7733
7988
|
}
|
|
7734
7989
|
|
|
@@ -7741,7 +7996,8 @@ export class AgentSession {
|
|
|
7741
7996
|
if (hindsightRecall) this.getHindsightSessionState()?.markRecallSnippetInjected(hindsightRecall);
|
|
7742
7997
|
},
|
|
7743
7998
|
};
|
|
7744
|
-
options?.
|
|
7999
|
+
if (options?.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
8000
|
+
else options?.onPreflightAccepted?.();
|
|
7745
8001
|
this.#throwIfPromptPreflightCancelled(generation, preflightSignal);
|
|
7746
8002
|
await this.#promptAgentWithIdleRetry(messages, agentPromptOptions, predecessorAgentEndHold);
|
|
7747
8003
|
const terminalAssistant = this.#findLastAssistantMessage();
|
|
@@ -7760,7 +8016,12 @@ export class AgentSession {
|
|
|
7760
8016
|
// Session identity changes historically cancel local setup silently. Only SDK
|
|
7761
8017
|
// submissions provide an acceptance callback and require an explicit terminal
|
|
7762
8018
|
// preflight failure for their remote request authority.
|
|
7763
|
-
if (
|
|
8019
|
+
if (
|
|
8020
|
+
isPromptPreflightCancelledError(error) &&
|
|
8021
|
+
!options?.onPreflightAccepted &&
|
|
8022
|
+
!options?.onPreflightAcceptCommit
|
|
8023
|
+
)
|
|
8024
|
+
return;
|
|
7764
8025
|
throw error;
|
|
7765
8026
|
} finally {
|
|
7766
8027
|
this.#removeEphemeralCustomMessages();
|
|
@@ -7844,6 +8105,7 @@ export class AgentSession {
|
|
|
7844
8105
|
return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
|
|
7845
8106
|
},
|
|
7846
8107
|
cycleModel: () => this.cycleModel(),
|
|
8108
|
+
setModelProfile: name => this.activateModelProfileForControl(name),
|
|
7847
8109
|
cycleThinkingLevel: () => this.cycleThinkingLevel(),
|
|
7848
8110
|
setQueueMode: (kind, mode) => {
|
|
7849
8111
|
if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
|
|
@@ -7860,7 +8122,7 @@ export class AgentSession {
|
|
|
7860
8122
|
}
|
|
7861
8123
|
return false;
|
|
7862
8124
|
},
|
|
7863
|
-
invokeSkill: (name, args) => this.invokeSkill(name, args),
|
|
8125
|
+
invokeSkill: (name, args, options) => this.invokeSkill(name, args, options),
|
|
7864
8126
|
setPlanMode: on => this.setSdkPlanMode(on),
|
|
7865
8127
|
operateGoal: (op, objective) => this.operateGoal(op, objective),
|
|
7866
8128
|
getSkillState: () => this.skills.map(skill => ({ name: skill.name, description: skill.description })),
|
|
@@ -7871,6 +8133,7 @@ export class AgentSession {
|
|
|
7871
8133
|
getJobs: () => undefined,
|
|
7872
8134
|
sdkBindings: () => [
|
|
7873
8135
|
"cycleModel",
|
|
8136
|
+
"setModelProfile",
|
|
7874
8137
|
"cycleThinkingLevel",
|
|
7875
8138
|
"setQueueMode",
|
|
7876
8139
|
"getSkillState",
|
|
@@ -7974,6 +8237,7 @@ export class AgentSession {
|
|
|
7974
8237
|
* Queue a steering message to interrupt the agent mid-run.
|
|
7975
8238
|
*/
|
|
7976
8239
|
async steer(text: string, images?: ImageContent[]): Promise<void> {
|
|
8240
|
+
this.#assertRecoveryHydrationPromoted();
|
|
7977
8241
|
if (text.startsWith("/")) {
|
|
7978
8242
|
this.#throwIfExtensionCommand(text);
|
|
7979
8243
|
}
|
|
@@ -7991,6 +8255,7 @@ export class AgentSession {
|
|
|
7991
8255
|
images?: ImageContent[],
|
|
7992
8256
|
options?: Pick<PromptOptions, "followUpQueuePolicy">,
|
|
7993
8257
|
): Promise<void> {
|
|
8258
|
+
this.#assertRecoveryHydrationPromoted();
|
|
7994
8259
|
if (text.startsWith("/")) {
|
|
7995
8260
|
this.#throwIfExtensionCommand(text);
|
|
7996
8261
|
}
|
|
@@ -8225,6 +8490,7 @@ export class AgentSession {
|
|
|
8225
8490
|
followUpQueuePolicy?: "respect-mode" | "sequential";
|
|
8226
8491
|
},
|
|
8227
8492
|
): Promise<void> {
|
|
8493
|
+
this.#assertRecoveryHydrationPromoted();
|
|
8228
8494
|
const appMessage: CustomMessage<T> = {
|
|
8229
8495
|
role: "custom",
|
|
8230
8496
|
customType: message.customType,
|
|
@@ -8366,8 +8632,13 @@ export class AgentSession {
|
|
|
8366
8632
|
*/
|
|
8367
8633
|
async sendUserMessage(
|
|
8368
8634
|
content: string | (TextContent | ImageContent)[],
|
|
8369
|
-
options?: {
|
|
8635
|
+
options?: {
|
|
8636
|
+
deliverAs?: "steer" | "followUp";
|
|
8637
|
+
onPreflightAccepted?: () => void;
|
|
8638
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
8639
|
+
},
|
|
8370
8640
|
): Promise<void> {
|
|
8641
|
+
this.#assertRecoveryHydrationPromoted();
|
|
8371
8642
|
// Normalize content to text string + optional images
|
|
8372
8643
|
let text: string;
|
|
8373
8644
|
let images: ImageContent[] | undefined;
|
|
@@ -8389,11 +8660,13 @@ export class AgentSession {
|
|
|
8389
8660
|
}
|
|
8390
8661
|
|
|
8391
8662
|
if (options?.deliverAs === "followUp") {
|
|
8663
|
+
if (options.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
8392
8664
|
await this.#queueFollowUp(text, images, { claimsGenuineUserIntent: true });
|
|
8393
8665
|
options.onPreflightAccepted?.();
|
|
8394
8666
|
return;
|
|
8395
8667
|
}
|
|
8396
8668
|
if (options?.deliverAs === "steer") {
|
|
8669
|
+
if (options.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
8397
8670
|
await this.#queueSteer(text, images, { claimsGenuineUserIntent: true });
|
|
8398
8671
|
options.onPreflightAccepted?.();
|
|
8399
8672
|
return;
|
|
@@ -8405,6 +8678,7 @@ export class AgentSession {
|
|
|
8405
8678
|
// in-flight compaction internally, and #queueSteer would otherwise park
|
|
8406
8679
|
// the message in the steering queue with no turn to consume it.
|
|
8407
8680
|
if (this.isStreaming) {
|
|
8681
|
+
if (options?.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
8408
8682
|
await this.#queueSteer(text, images, { claimsGenuineUserIntent: true });
|
|
8409
8683
|
options?.onPreflightAccepted?.();
|
|
8410
8684
|
return;
|
|
@@ -8415,6 +8689,7 @@ export class AgentSession {
|
|
|
8415
8689
|
expandPromptTemplates: false,
|
|
8416
8690
|
images,
|
|
8417
8691
|
onPreflightAccepted: options?.onPreflightAccepted,
|
|
8692
|
+
onPreflightAcceptCommit: options?.onPreflightAcceptCommit,
|
|
8418
8693
|
});
|
|
8419
8694
|
}
|
|
8420
8695
|
|
|
@@ -9039,11 +9314,14 @@ export class AgentSession {
|
|
|
9039
9314
|
this.#rebindProviderSessionState(new Map());
|
|
9040
9315
|
this.agent.reset();
|
|
9041
9316
|
if (!options?.drop) await this.sessionManager.flush();
|
|
9042
|
-
await this.sessionManager.
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9317
|
+
const prepared = await this.sessionManager.prepareNewSession(options);
|
|
9318
|
+
try {
|
|
9319
|
+
// Last fallible gate while public getters still show the predecessor (#3138).
|
|
9320
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
9321
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
9322
|
+
} catch (error) {
|
|
9323
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
9324
|
+
}
|
|
9047
9325
|
this.setTodoPhases([]);
|
|
9048
9326
|
this.#syncAgentSessionId();
|
|
9049
9327
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|
|
@@ -9105,16 +9383,18 @@ export class AgentSession {
|
|
|
9105
9383
|
if (!(await manager.cancelAndSettleOwnerJobs(ownerId))) {
|
|
9106
9384
|
throw new Error("Owned async jobs did not settle before session replacement.");
|
|
9107
9385
|
}
|
|
9108
|
-
|
|
9386
|
+
const prepared = await this.sessionManager.prepareNewSession(options);
|
|
9387
|
+
try {
|
|
9388
|
+
// Last fallible gate while public getters still show the predecessor (#3138).
|
|
9389
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
9390
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
9391
|
+
} catch (error) {
|
|
9392
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
9393
|
+
}
|
|
9109
9394
|
this.#disconnectFromAgent();
|
|
9110
9395
|
this.#closeAllProviderSessions("new session");
|
|
9111
9396
|
this.#rebindProviderSessionState(new Map());
|
|
9112
9397
|
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
9398
|
this.setTodoPhases([]);
|
|
9119
9399
|
this.#syncAgentSessionId();
|
|
9120
9400
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|
|
@@ -9149,6 +9429,12 @@ export class AgentSession {
|
|
|
9149
9429
|
): Promise<void> {
|
|
9150
9430
|
this.#clearConstructorToolSelectionAuthority();
|
|
9151
9431
|
const inheritedThinkingLevel = resolveThinkingLevelForModel(this.model, this.#getInheritedThinkingLevel());
|
|
9432
|
+
this.#thinkingLevelMutationRevision++;
|
|
9433
|
+
this.#thinkingLevelLiveMutationRevision++;
|
|
9434
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
9435
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
9436
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
9437
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
9152
9438
|
this.#thinkingLevel = inheritedThinkingLevel;
|
|
9153
9439
|
this.agent.setThinkingLevel(toReasoningEffort(inheritedThinkingLevel));
|
|
9154
9440
|
this.sessionManager.appendThinkingLevelChange(ThinkingLevel.Inherit);
|
|
@@ -9157,10 +9443,17 @@ export class AgentSession {
|
|
|
9157
9443
|
}
|
|
9158
9444
|
this.sessionManager.appendServiceTierChange(this.serviceTier ?? null);
|
|
9159
9445
|
if (nextDiscoverySessionToolNames) {
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9446
|
+
// Avoid a full system-prompt rebuild when the active set is unchanged; rebuild
|
|
9447
|
+
// can block on shared workspace-tree work across repeated /new transitions.
|
|
9448
|
+
const current = this.getActiveToolNames().slice().sort().join("\0");
|
|
9449
|
+
const next = [...new Set(nextDiscoverySessionToolNames.map(n => n.toLowerCase()))].sort().join("\0");
|
|
9450
|
+
if (current !== next) {
|
|
9451
|
+
await this.#applyActiveToolsByName(nextDiscoverySessionToolNames, {
|
|
9452
|
+
persistMCPSelection: false,
|
|
9453
|
+
nextSelectedDiscoveredBuiltinToolNames: [],
|
|
9454
|
+
});
|
|
9455
|
+
} else {
|
|
9456
|
+
}
|
|
9164
9457
|
}
|
|
9165
9458
|
this.#todoReminderCount = 0;
|
|
9166
9459
|
this.#planReferenceSent = false;
|
|
@@ -9173,6 +9466,7 @@ export class AgentSession {
|
|
|
9173
9466
|
reason: "new",
|
|
9174
9467
|
previousSessionFile,
|
|
9175
9468
|
});
|
|
9469
|
+
} else {
|
|
9176
9470
|
}
|
|
9177
9471
|
}
|
|
9178
9472
|
|
|
@@ -9252,16 +9546,18 @@ export class AgentSession {
|
|
|
9252
9546
|
// Flush current session to ensure all entries are written
|
|
9253
9547
|
await this.sessionManager.flush();
|
|
9254
9548
|
|
|
9255
|
-
//
|
|
9256
|
-
|
|
9257
|
-
|
|
9549
|
+
// Prepare the copied successor and complete local:// readiness while all
|
|
9550
|
+
// public manager getters remain bound to the predecessor.
|
|
9551
|
+
const prepared = await this.sessionManager.prepareFork();
|
|
9552
|
+
if (!prepared) {
|
|
9258
9553
|
return false;
|
|
9259
9554
|
}
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9555
|
+
try {
|
|
9556
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
9557
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
9558
|
+
} catch (error) {
|
|
9559
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
9560
|
+
}
|
|
9265
9561
|
this.#syncAgentSessionId();
|
|
9266
9562
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|
|
9267
9563
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -9350,6 +9646,17 @@ export class AgentSession {
|
|
|
9350
9646
|
return this.#activeModelProfile;
|
|
9351
9647
|
}
|
|
9352
9648
|
|
|
9649
|
+
/** Activate a complete model profile through a nonvisual session control. */
|
|
9650
|
+
async activateModelProfileForControl(profileName: string): Promise<boolean> {
|
|
9651
|
+
await activateModelProfile({
|
|
9652
|
+
session: this,
|
|
9653
|
+
modelRegistry: this.#modelRegistry,
|
|
9654
|
+
settings: this.settings,
|
|
9655
|
+
profileName,
|
|
9656
|
+
});
|
|
9657
|
+
return this.getActiveModelProfile() === profileName;
|
|
9658
|
+
}
|
|
9659
|
+
|
|
9353
9660
|
/** Return the persisted configured fallback selectors for a model role. */
|
|
9354
9661
|
getConfiguredModelChain(role: string): readonly string[] | undefined {
|
|
9355
9662
|
return getSessionContextForInternalRead(this.sessionManager).configuredModelChains[role]?.entries;
|
|
@@ -9477,7 +9784,7 @@ export class AgentSession {
|
|
|
9477
9784
|
const ownsScope = scope !== undefined && !suppliedScope;
|
|
9478
9785
|
try {
|
|
9479
9786
|
if (isTemporaryOperation) {
|
|
9480
|
-
this
|
|
9787
|
+
this.#setAgentModelWithReasoningContext(model);
|
|
9481
9788
|
this.#syncAppendOnlyContext(model);
|
|
9482
9789
|
} else {
|
|
9483
9790
|
this.#setModelAuthoritatively(model, options?.cause ?? "temporary-operation");
|
|
@@ -9562,6 +9869,12 @@ export class AgentSession {
|
|
|
9562
9869
|
this.#clearActiveRetryFallback();
|
|
9563
9870
|
this.#setModelWithProviderSessionReset(model);
|
|
9564
9871
|
const thinkingLevelChanged = this.#thinkingLevel !== thinkingLevel;
|
|
9872
|
+
this.#thinkingLevelMutationRevision++;
|
|
9873
|
+
this.#thinkingLevelLiveMutationRevision++;
|
|
9874
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
9875
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
9876
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
9877
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
9565
9878
|
this.#thinkingLevel = thinkingLevel;
|
|
9566
9879
|
this.agent.setThinkingLevel(toReasoningEffort(thinkingLevel));
|
|
9567
9880
|
if (thinkingLevelChanged) {
|
|
@@ -9870,7 +10183,19 @@ export class AgentSession {
|
|
|
9870
10183
|
* Set thinking level.
|
|
9871
10184
|
* Saves the effective metadata-clamped level to the session, and to settings when requested.
|
|
9872
10185
|
*/
|
|
10186
|
+
#assertDurableSettingsWritable(): void {
|
|
10187
|
+
if (this.settings.canWriteDurableConfig()) return;
|
|
10188
|
+
throw new Error(
|
|
10189
|
+
"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
|
|
10190
|
+
);
|
|
10191
|
+
}
|
|
10192
|
+
|
|
9873
10193
|
setThinkingLevel(level: ThinkingLevel | undefined, persist: boolean = false): void {
|
|
10194
|
+
if (persist) this.#assertDurableSettingsWritable();
|
|
10195
|
+
this.#thinkingLevelMutationRevision++;
|
|
10196
|
+
this.#thinkingLevelLiveMutationRevision++;
|
|
10197
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
10198
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
9874
10199
|
const effectiveLevel = resolveThinkingLevelForModel(this.model, level);
|
|
9875
10200
|
const isChanging = effectiveLevel !== this.#thinkingLevel;
|
|
9876
10201
|
|
|
@@ -9890,11 +10215,10 @@ export class AgentSession {
|
|
|
9890
10215
|
}
|
|
9891
10216
|
|
|
9892
10217
|
/**
|
|
9893
|
-
* Set thinking level from a control surface. Global changes
|
|
10218
|
+
* Set thinking level from a control surface. Global changes commit before affecting live state.
|
|
9894
10219
|
*/
|
|
9895
10220
|
async setThinkingLevelForControl(level: ThinkingLevel, persist: boolean): Promise<void> {
|
|
9896
10221
|
const previousThinkingLevel = this.thinkingLevel;
|
|
9897
|
-
const previousScope = this.getThinkingScopeForControl();
|
|
9898
10222
|
if (!persist) {
|
|
9899
10223
|
this.setThinkingLevel(level === ThinkingLevel.Inherit ? this.#getInheritedThinkingLevel() : level);
|
|
9900
10224
|
if (level === ThinkingLevel.Inherit || this.thinkingLevel === previousThinkingLevel) {
|
|
@@ -9903,26 +10227,99 @@ export class AgentSession {
|
|
|
9903
10227
|
return;
|
|
9904
10228
|
}
|
|
9905
10229
|
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
this
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
10230
|
+
this.#assertDurableSettingsWritable();
|
|
10231
|
+
const effectiveLevel = resolveThinkingLevelForModel(
|
|
10232
|
+
this.model,
|
|
10233
|
+
level === ThinkingLevel.Inherit ? this.#getInheritedThinkingLevel() : level,
|
|
10234
|
+
);
|
|
10235
|
+
const persistedLevel = level === ThinkingLevel.Inherit ? getDefault("defaultThinkingLevel") : effectiveLevel;
|
|
10236
|
+
const mutationRevision = ++this.#thinkingLevelMutationRevision;
|
|
10237
|
+
const expectedLiveMutationRevision = this.#thinkingLevelLiveMutationRevision;
|
|
10238
|
+
const expectedSessionId = this.sessionManager.getSessionId();
|
|
10239
|
+
const expectedModel = this.model;
|
|
10240
|
+
const expectedContextGeneration = this.#reasoningControlContextGeneration;
|
|
9914
10241
|
try {
|
|
9915
|
-
await this.settings.
|
|
9916
|
-
this.sessionManager.appendThinkingLevelChange(ThinkingLevel.Inherit);
|
|
10242
|
+
await this.settings.commitAtomicBatch([{ path: "defaultThinkingLevel", op: "set", value: persistedLevel }]);
|
|
9917
10243
|
} catch {
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
10244
|
+
if (
|
|
10245
|
+
mutationRevision === this.#thinkingLevelMutationRevision &&
|
|
10246
|
+
this.#reasoningControlContextGeneration === expectedContextGeneration &&
|
|
10247
|
+
this.sessionManager.getSessionId() === expectedSessionId &&
|
|
10248
|
+
this.model === expectedModel
|
|
10249
|
+
) {
|
|
10250
|
+
const pending = this.#pendingThinkingLevelControlSuccess;
|
|
10251
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
10252
|
+
if (
|
|
10253
|
+
pending &&
|
|
10254
|
+
pending.contextGeneration === expectedContextGeneration &&
|
|
10255
|
+
this.sessionManager.getSessionId() === pending.sessionId &&
|
|
10256
|
+
this.model === pending.model
|
|
10257
|
+
) {
|
|
10258
|
+
this.setThinkingLevel(
|
|
10259
|
+
pending.level === ThinkingLevel.Inherit ? this.#getInheritedThinkingLevel() : pending.level,
|
|
10260
|
+
);
|
|
10261
|
+
this.sessionManager.appendThinkingLevelChange(ThinkingLevel.Inherit);
|
|
10262
|
+
} else {
|
|
10263
|
+
this.#pendingThinkingLevelControlFailure = {
|
|
10264
|
+
mutationRevision,
|
|
10265
|
+
liveMutationRevision: expectedLiveMutationRevision,
|
|
10266
|
+
sessionId: expectedSessionId,
|
|
10267
|
+
model: expectedModel,
|
|
10268
|
+
contextGeneration: expectedContextGeneration,
|
|
10269
|
+
};
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
9924
10272
|
throw new Error("Unable to persist reasoning settings.");
|
|
9925
10273
|
}
|
|
10274
|
+
|
|
10275
|
+
if (
|
|
10276
|
+
mutationRevision === this.#thinkingLevelMutationRevision &&
|
|
10277
|
+
this.#thinkingLevelLiveMutationRevision === expectedLiveMutationRevision
|
|
10278
|
+
) {
|
|
10279
|
+
this.setThinkingLevel(level === ThinkingLevel.Inherit ? this.#getInheritedThinkingLevel() : level);
|
|
10280
|
+
this.sessionManager.appendThinkingLevelChange(ThinkingLevel.Inherit);
|
|
10281
|
+
return;
|
|
10282
|
+
}
|
|
10283
|
+
if (
|
|
10284
|
+
mutationRevision !== this.#thinkingLevelMutationRevision ||
|
|
10285
|
+
this.#reasoningControlContextGeneration !== expectedContextGeneration ||
|
|
10286
|
+
this.sessionManager.getSessionId() !== expectedSessionId ||
|
|
10287
|
+
this.model !== expectedModel
|
|
10288
|
+
) {
|
|
10289
|
+
if (
|
|
10290
|
+
this.#thinkingLevelLiveMutationRevision === expectedLiveMutationRevision &&
|
|
10291
|
+
this.#reasoningControlContextGeneration === expectedContextGeneration &&
|
|
10292
|
+
this.sessionManager.getSessionId() === expectedSessionId &&
|
|
10293
|
+
this.model === expectedModel &&
|
|
10294
|
+
(this.#pendingThinkingLevelControlSuccess?.mutationRevision ?? -1) < mutationRevision
|
|
10295
|
+
) {
|
|
10296
|
+
this.#pendingThinkingLevelControlSuccess = {
|
|
10297
|
+
level,
|
|
10298
|
+
mutationRevision,
|
|
10299
|
+
sessionId: expectedSessionId,
|
|
10300
|
+
model: expectedModel,
|
|
10301
|
+
contextGeneration: expectedContextGeneration,
|
|
10302
|
+
};
|
|
10303
|
+
const failure = this.#pendingThinkingLevelControlFailure;
|
|
10304
|
+
if (
|
|
10305
|
+
failure &&
|
|
10306
|
+
failure.mutationRevision === this.#thinkingLevelMutationRevision &&
|
|
10307
|
+
failure.liveMutationRevision === expectedLiveMutationRevision &&
|
|
10308
|
+
failure.sessionId === expectedSessionId &&
|
|
10309
|
+
failure.model === expectedModel &&
|
|
10310
|
+
failure.contextGeneration === expectedContextGeneration
|
|
10311
|
+
) {
|
|
10312
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
10313
|
+
this.setThinkingLevel(
|
|
10314
|
+
level === ThinkingLevel.Inherit ? this.#getInheritedThinkingLevel() : effectiveLevel,
|
|
10315
|
+
);
|
|
10316
|
+
this.sessionManager.appendThinkingLevelChange(ThinkingLevel.Inherit);
|
|
10317
|
+
}
|
|
10318
|
+
}
|
|
10319
|
+
return;
|
|
10320
|
+
}
|
|
10321
|
+
this.setThinkingLevel(level === ThinkingLevel.Inherit ? this.#getInheritedThinkingLevel() : effectiveLevel);
|
|
10322
|
+
this.sessionManager.appendThinkingLevelChange(ThinkingLevel.Inherit);
|
|
9926
10323
|
}
|
|
9927
10324
|
|
|
9928
10325
|
getThinkingScopeForControl(): "session" | "global config" {
|
|
@@ -9938,6 +10335,11 @@ export class AgentSession {
|
|
|
9938
10335
|
}
|
|
9939
10336
|
|
|
9940
10337
|
setThinkingVisibility(visibility: "visible" | "hidden", persist: boolean = false): void {
|
|
10338
|
+
if (persist) this.#assertDurableSettingsWritable();
|
|
10339
|
+
this.#thinkingVisibilityMutationRevision++;
|
|
10340
|
+
this.#thinkingVisibilityLiveMutationRevision++;
|
|
10341
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
10342
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
9941
10343
|
this.agent.hideThinkingSummary = visibility === "hidden";
|
|
9942
10344
|
if (persist) {
|
|
9943
10345
|
this.settings.set("hideThinkingBlock", visibility === "hidden");
|
|
@@ -9945,7 +10347,7 @@ export class AgentSession {
|
|
|
9945
10347
|
}
|
|
9946
10348
|
|
|
9947
10349
|
/**
|
|
9948
|
-
* Set thinking visibility from a control surface. Global changes
|
|
10350
|
+
* Set thinking visibility from a control surface. Global changes commit before affecting live state.
|
|
9949
10351
|
*/
|
|
9950
10352
|
async setThinkingVisibilityForControl(visibility: "visible" | "hidden", persist: boolean): Promise<void> {
|
|
9951
10353
|
if (!persist) {
|
|
@@ -9953,17 +10355,88 @@ export class AgentSession {
|
|
|
9953
10355
|
return;
|
|
9954
10356
|
}
|
|
9955
10357
|
|
|
9956
|
-
|
|
9957
|
-
const
|
|
9958
|
-
this
|
|
10358
|
+
this.#assertDurableSettingsWritable();
|
|
10359
|
+
const mutationRevision = ++this.#thinkingVisibilityMutationRevision;
|
|
10360
|
+
const expectedLiveMutationRevision = this.#thinkingVisibilityLiveMutationRevision;
|
|
10361
|
+
const expectedSessionId = this.sessionManager.getSessionId();
|
|
10362
|
+
const expectedModel = this.model;
|
|
10363
|
+
const expectedContextGeneration = this.#reasoningControlContextGeneration;
|
|
9959
10364
|
try {
|
|
9960
|
-
await this.settings.
|
|
10365
|
+
await this.settings.commitAtomicBatch([
|
|
10366
|
+
{ path: "hideThinkingBlock", op: "set", value: visibility === "hidden" },
|
|
10367
|
+
]);
|
|
9961
10368
|
} catch {
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
10369
|
+
if (
|
|
10370
|
+
mutationRevision === this.#thinkingVisibilityMutationRevision &&
|
|
10371
|
+
this.#reasoningControlContextGeneration === expectedContextGeneration &&
|
|
10372
|
+
this.sessionManager.getSessionId() === expectedSessionId &&
|
|
10373
|
+
this.model === expectedModel
|
|
10374
|
+
) {
|
|
10375
|
+
const pending = this.#pendingThinkingVisibilityControlSuccess;
|
|
10376
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
10377
|
+
if (
|
|
10378
|
+
pending &&
|
|
10379
|
+
pending.contextGeneration === expectedContextGeneration &&
|
|
10380
|
+
this.sessionManager.getSessionId() === pending.sessionId &&
|
|
10381
|
+
this.model === pending.model
|
|
10382
|
+
) {
|
|
10383
|
+
this.setThinkingVisibility(pending.visibility);
|
|
10384
|
+
} else {
|
|
10385
|
+
this.#pendingThinkingVisibilityControlFailure = {
|
|
10386
|
+
mutationRevision,
|
|
10387
|
+
liveMutationRevision: expectedLiveMutationRevision,
|
|
10388
|
+
sessionId: expectedSessionId,
|
|
10389
|
+
model: expectedModel,
|
|
10390
|
+
contextGeneration: expectedContextGeneration,
|
|
10391
|
+
};
|
|
10392
|
+
}
|
|
10393
|
+
}
|
|
9965
10394
|
throw new Error("Unable to persist reasoning settings.");
|
|
9966
10395
|
}
|
|
10396
|
+
if (
|
|
10397
|
+
mutationRevision === this.#thinkingVisibilityMutationRevision &&
|
|
10398
|
+
this.#thinkingVisibilityLiveMutationRevision === expectedLiveMutationRevision
|
|
10399
|
+
) {
|
|
10400
|
+
this.setThinkingVisibility(visibility);
|
|
10401
|
+
return;
|
|
10402
|
+
}
|
|
10403
|
+
|
|
10404
|
+
if (
|
|
10405
|
+
mutationRevision !== this.#thinkingVisibilityMutationRevision ||
|
|
10406
|
+
this.#reasoningControlContextGeneration !== expectedContextGeneration ||
|
|
10407
|
+
this.sessionManager.getSessionId() !== expectedSessionId ||
|
|
10408
|
+
this.model !== expectedModel
|
|
10409
|
+
) {
|
|
10410
|
+
if (
|
|
10411
|
+
this.#thinkingVisibilityLiveMutationRevision === expectedLiveMutationRevision &&
|
|
10412
|
+
this.#reasoningControlContextGeneration === expectedContextGeneration &&
|
|
10413
|
+
this.sessionManager.getSessionId() === expectedSessionId &&
|
|
10414
|
+
this.model === expectedModel &&
|
|
10415
|
+
(this.#pendingThinkingVisibilityControlSuccess?.mutationRevision ?? -1) < mutationRevision
|
|
10416
|
+
) {
|
|
10417
|
+
this.#pendingThinkingVisibilityControlSuccess = {
|
|
10418
|
+
visibility,
|
|
10419
|
+
mutationRevision,
|
|
10420
|
+
sessionId: expectedSessionId,
|
|
10421
|
+
model: expectedModel,
|
|
10422
|
+
contextGeneration: expectedContextGeneration,
|
|
10423
|
+
};
|
|
10424
|
+
const failure = this.#pendingThinkingVisibilityControlFailure;
|
|
10425
|
+
if (
|
|
10426
|
+
failure &&
|
|
10427
|
+
failure.mutationRevision === this.#thinkingVisibilityMutationRevision &&
|
|
10428
|
+
failure.liveMutationRevision === expectedLiveMutationRevision &&
|
|
10429
|
+
failure.sessionId === expectedSessionId &&
|
|
10430
|
+
failure.model === expectedModel &&
|
|
10431
|
+
failure.contextGeneration === expectedContextGeneration
|
|
10432
|
+
) {
|
|
10433
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
10434
|
+
this.setThinkingVisibility(visibility);
|
|
10435
|
+
}
|
|
10436
|
+
}
|
|
10437
|
+
return;
|
|
10438
|
+
}
|
|
10439
|
+
this.setThinkingVisibility(visibility);
|
|
9967
10440
|
}
|
|
9968
10441
|
|
|
9969
10442
|
/**
|
|
@@ -10140,6 +10613,7 @@ export class AgentSession {
|
|
|
10140
10613
|
* Saves to settings.
|
|
10141
10614
|
*/
|
|
10142
10615
|
setSteeringMode(mode: "all" | "one-at-a-time"): void {
|
|
10616
|
+
this.#assertDurableSettingsWritable();
|
|
10143
10617
|
this.agent.setSteeringMode(mode);
|
|
10144
10618
|
this.settings.set("steeringMode", mode);
|
|
10145
10619
|
}
|
|
@@ -10149,6 +10623,7 @@ export class AgentSession {
|
|
|
10149
10623
|
* Saves to settings.
|
|
10150
10624
|
*/
|
|
10151
10625
|
setFollowUpMode(mode: "all" | "one-at-a-time"): void {
|
|
10626
|
+
this.#assertDurableSettingsWritable();
|
|
10152
10627
|
this.agent.setFollowUpMode(mode);
|
|
10153
10628
|
this.settings.set("followUpMode", mode);
|
|
10154
10629
|
}
|
|
@@ -10158,6 +10633,7 @@ export class AgentSession {
|
|
|
10158
10633
|
* Saves to settings.
|
|
10159
10634
|
*/
|
|
10160
10635
|
setInterruptMode(mode: "immediate" | "wait"): void {
|
|
10636
|
+
this.#assertDurableSettingsWritable();
|
|
10161
10637
|
this.agent.setInterruptMode(mode);
|
|
10162
10638
|
this.settings.set("interruptMode", mode);
|
|
10163
10639
|
}
|
|
@@ -10652,26 +11128,36 @@ export class AgentSession {
|
|
|
10652
11128
|
// user work that agent.reset() would otherwise clear.
|
|
10653
11129
|
const rollbackAgentSteeringQueue = this.agent.snapshotSteering();
|
|
10654
11130
|
const rollbackAgentFollowUpQueue = this.agent.snapshotFollowUp();
|
|
10655
|
-
let successorSessionFile: string | undefined;
|
|
10656
11131
|
let savedPath: string | undefined;
|
|
10657
11132
|
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;
|
|
11133
|
+
let prepared: PreparedNewSession | undefined;
|
|
10662
11134
|
try {
|
|
10663
|
-
|
|
10664
|
-
//
|
|
10665
|
-
//
|
|
10666
|
-
|
|
10667
|
-
await this.sessionManager.newSession(
|
|
11135
|
+
// Prepare successor entries, persistence, display state, and gate
|
|
11136
|
+
// construction without publishing manager identity. Managed local-root
|
|
11137
|
+
// readiness is the last fallible step before the sole commit (#3138).
|
|
11138
|
+
prepared = await this.sessionManager.prepareNewSession(
|
|
10668
11139
|
previousSessionFile ? { parentSession: previousSessionFile } : undefined,
|
|
10669
11140
|
);
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
11141
|
+
if (model) this.sessionManager.appendPreparedModelChange(prepared, `${model.provider}/${model.id}`);
|
|
11142
|
+
this.sessionManager.appendPreparedThinkingLevelChange(prepared, this.thinkingLevel);
|
|
11143
|
+
this.sessionManager.appendPreparedServiceTierChange(prepared, this.serviceTier ?? null);
|
|
11144
|
+
this.sessionManager.appendPreparedCustomMessageEntry(
|
|
11145
|
+
prepared,
|
|
11146
|
+
"handoff",
|
|
11147
|
+
createHandoffContext(handoffText),
|
|
11148
|
+
true,
|
|
11149
|
+
undefined,
|
|
11150
|
+
"agent",
|
|
11151
|
+
);
|
|
11152
|
+
await this.sessionManager.ensurePreparedNewSessionOnDisk(prepared);
|
|
11153
|
+
const sessionContext = this.buildPreparedDisplaySessionContext(prepared);
|
|
11154
|
+
const successorGateEmitter = this.#constructWorkflowGateEmitter(prepared.sessionId);
|
|
11155
|
+
// Last fallible action: verified local:// readiness from immutable staged options.
|
|
11156
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
11157
|
+
|
|
11158
|
+
// --- Commit boundary: synchronous adoption is the sole identity publication.
|
|
11159
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
11160
|
+
committed = true;
|
|
10675
11161
|
this.agent.reset();
|
|
10676
11162
|
this.#syncAgentSessionId();
|
|
10677
11163
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -10680,16 +11166,8 @@ export class AgentSession {
|
|
|
10680
11166
|
this.#pendingNextTurnMessages = [];
|
|
10681
11167
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
10682
11168
|
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();
|
|
11169
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
11170
|
+
this.#syncTodoPhasesFromBranch();
|
|
10693
11171
|
if (options?.autoTriggered && this.settings.get("compaction.handoffSaveToDisk")) {
|
|
10694
11172
|
try {
|
|
10695
11173
|
const artifactId = await this.sessionManager.saveArtifact(`${handoffText}\n`, "handoff");
|
|
@@ -10701,30 +11179,9 @@ export class AgentSession {
|
|
|
10701
11179
|
}
|
|
10702
11180
|
}
|
|
10703
11181
|
|
|
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;
|
|
11182
|
+
// The forward-only suffix starts only after the committed successor is live.
|
|
10722
11183
|
this.#resetInjectedContextSignatures();
|
|
10723
|
-
this.#publishWorkflowGateEmitter(
|
|
10724
|
-
successorGateEmitter,
|
|
10725
|
-
rollbackSessionState.sessionId,
|
|
10726
|
-
suspendedWorkflowGateEmitter,
|
|
10727
|
-
);
|
|
11184
|
+
this.#publishWorkflowGateEmitter(successorGateEmitter, rollbackSessionState.sessionId);
|
|
10728
11185
|
this.#closeAllProviderSessions("session handoff");
|
|
10729
11186
|
this.#rebindProviderSessionState(new Map());
|
|
10730
11187
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
@@ -10760,12 +11217,12 @@ export class AgentSession {
|
|
|
10760
11217
|
return { document: handoffText, savedPath };
|
|
10761
11218
|
} catch (switchError) {
|
|
10762
11219
|
if (committed) {
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
11220
|
+
throw Object.assign(new Error("Handoff committed but successor initialization was degraded."), {
|
|
11221
|
+
code: "handoff_committed_degraded",
|
|
11222
|
+
cause: switchError,
|
|
11223
|
+
handoffDocument: handoffText,
|
|
11224
|
+
savedPath,
|
|
10767
11225
|
});
|
|
10768
|
-
return { document: handoffText, savedPath };
|
|
10769
11226
|
}
|
|
10770
11227
|
// Reversible window: roll back to the pre-handoff session so the
|
|
10771
11228
|
// failure is non-destructive. Predecessor gate emitter, provider
|
|
@@ -10773,7 +11230,6 @@ export class AgentSession {
|
|
|
10773
11230
|
// were never mutated before commit, so they survive intact.
|
|
10774
11231
|
this.sessionManager.restoreState(rollbackSessionState);
|
|
10775
11232
|
this.#syncAgentSessionId(rollbackSessionState.sessionId);
|
|
10776
|
-
this.#restoreWorkflowGateEmitter(suspendedWorkflowGateEmitter);
|
|
10777
11233
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
10778
11234
|
this.agent.replaceMessages(rollbackAgentMessages);
|
|
10779
11235
|
this.agent.clearAllQueues();
|
|
@@ -10785,25 +11241,17 @@ export class AgentSession {
|
|
|
10785
11241
|
this.#scheduledHiddenNextTurnGeneration = rollbackScheduledHiddenNextTurnGeneration;
|
|
10786
11242
|
this.#todoReminderCount = rollbackTodoReminderCount;
|
|
10787
11243
|
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
|
-
}
|
|
11244
|
+
// Exact-discard only the staged successor; predecessor state was never adopted.
|
|
11245
|
+
const rollbackError = prepared
|
|
11246
|
+
? await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, switchError)
|
|
11247
|
+
: switchError;
|
|
10800
11248
|
// Map to cancellation only for a genuine handoff-signal abort; a
|
|
10801
11249
|
// downstream error keeps its cause and the generated document.
|
|
10802
11250
|
if (handoffSignal.aborted) {
|
|
10803
11251
|
throw new Error("Handoff cancelled");
|
|
10804
11252
|
}
|
|
10805
11253
|
// Preserve the generated handoff document for copy/retry.
|
|
10806
|
-
throw Object.assign(
|
|
11254
|
+
throw Object.assign(rollbackError instanceof Error ? rollbackError : new Error(String(rollbackError)), {
|
|
10807
11255
|
handoffDocument: handoffText,
|
|
10808
11256
|
});
|
|
10809
11257
|
}
|
|
@@ -10811,7 +11259,7 @@ export class AgentSession {
|
|
|
10811
11259
|
// Genuine handoff-signal cancellation maps to a cancellation error.
|
|
10812
11260
|
// Errors surfaced by the inner transaction (which already rolled back
|
|
10813
11261
|
// and, for non-aborts, attached the generated document) pass through.
|
|
10814
|
-
if (handoffSignal.aborted) {
|
|
11262
|
+
if (handoffSignal.aborted && !(error instanceof AggregateError)) {
|
|
10815
11263
|
throw new Error("Handoff cancelled");
|
|
10816
11264
|
}
|
|
10817
11265
|
throw error;
|
|
@@ -10853,7 +11301,11 @@ export class AgentSession {
|
|
|
10853
11301
|
* @param assistantMessage The assistant message to check
|
|
10854
11302
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
10855
11303
|
*/
|
|
10856
|
-
async #checkCompaction(
|
|
11304
|
+
async #checkCompaction(
|
|
11305
|
+
assistantMessage: AssistantMessage,
|
|
11306
|
+
skipAbortedCheck = true,
|
|
11307
|
+
onTerminalOverflowNoop?: () => void,
|
|
11308
|
+
): Promise<boolean> {
|
|
10857
11309
|
// Safety stops are terminal and must not trigger context maintenance.
|
|
10858
11310
|
if (
|
|
10859
11311
|
assistantMessage.errorKind === "provider_safety_stop" ||
|
|
@@ -10889,8 +11341,10 @@ export class AgentSession {
|
|
|
10889
11341
|
// Remove the error message from agent state (it IS saved to session for history,
|
|
10890
11342
|
// but we don't want it in context for the retry)
|
|
10891
11343
|
const messages = this.agent.state.messages;
|
|
11344
|
+
let removedOverflowAssistant = false;
|
|
10892
11345
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
10893
11346
|
this.agent.replaceMessages(messages.slice(0, -1));
|
|
11347
|
+
removedOverflowAssistant = true;
|
|
10894
11348
|
}
|
|
10895
11349
|
|
|
10896
11350
|
// Try context promotion first - switch to a larger model and retry without compacting
|
|
@@ -10905,7 +11359,15 @@ export class AgentSession {
|
|
|
10905
11359
|
// No promotion target available fall through to compaction
|
|
10906
11360
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
10907
11361
|
if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
|
|
10908
|
-
const status = await this.#runAutoCompaction("overflow", true
|
|
11362
|
+
const status = await this.#runAutoCompaction("overflow", true, false, {
|
|
11363
|
+
beforeTerminalOverflowNoop: () => {
|
|
11364
|
+
if (onTerminalOverflowNoop) {
|
|
11365
|
+
onTerminalOverflowNoop();
|
|
11366
|
+
} else if (removedOverflowAssistant) {
|
|
11367
|
+
this.agent.appendMessage(assistantMessage);
|
|
11368
|
+
}
|
|
11369
|
+
},
|
|
11370
|
+
});
|
|
10909
11371
|
return "continuationScheduled" in status && status.continuationScheduled === true;
|
|
10910
11372
|
}
|
|
10911
11373
|
return this.#scheduleOverflowRetryContinuation(generation);
|
|
@@ -11673,13 +12135,18 @@ export class AgentSession {
|
|
|
11673
12135
|
this.#defaultFallbackExhaustedLastTurn = false;
|
|
11674
12136
|
}
|
|
11675
12137
|
|
|
12138
|
+
#setAgentModelWithReasoningContext(model: Model | undefined): void {
|
|
12139
|
+
this.#reasoningControlContextGeneration++;
|
|
12140
|
+
this.agent.setModel(model);
|
|
12141
|
+
}
|
|
12142
|
+
|
|
11676
12143
|
#setModelWithProviderSessionReset(model: Model | undefined): void {
|
|
11677
12144
|
this.#defaultModelSelectionMutationRevision++;
|
|
11678
12145
|
const currentModel = this.model;
|
|
11679
12146
|
if (currentModel) {
|
|
11680
12147
|
this.#closeProviderSessionsForModelSwitch(currentModel, model);
|
|
11681
12148
|
}
|
|
11682
|
-
this
|
|
12149
|
+
this.#setAgentModelWithReasoningContext(model);
|
|
11683
12150
|
}
|
|
11684
12151
|
|
|
11685
12152
|
#setModelAuthoritatively(model: Model, cause: ModelChangeCause): void {
|
|
@@ -11688,7 +12155,7 @@ export class AgentSession {
|
|
|
11688
12155
|
if (cause !== "temporary-operation") this.#commitAllTemporaryProviderSessionScopes();
|
|
11689
12156
|
const currentModel = this.model;
|
|
11690
12157
|
if (currentModel) this.#closeProviderSessionsForModelSwitch(currentModel, model);
|
|
11691
|
-
this
|
|
12158
|
+
this.#setAgentModelWithReasoningContext(model);
|
|
11692
12159
|
this.#syncAppendOnlyContext(model);
|
|
11693
12160
|
}
|
|
11694
12161
|
|
|
@@ -12214,6 +12681,7 @@ export class AgentSession {
|
|
|
12214
12681
|
deferHandoffMaintenance?: boolean;
|
|
12215
12682
|
force?: boolean;
|
|
12216
12683
|
signal?: AbortSignal;
|
|
12684
|
+
beforeTerminalOverflowNoop?: () => void;
|
|
12217
12685
|
},
|
|
12218
12686
|
): Promise<AutoCompactionTerminalStatus> {
|
|
12219
12687
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
@@ -12351,16 +12819,42 @@ export class AgentSession {
|
|
|
12351
12819
|
if (autoCompactionSignal.aborted) return await emitAborted();
|
|
12352
12820
|
|
|
12353
12821
|
if (!preparation) {
|
|
12354
|
-
|
|
12822
|
+
// #checkCompaction removes the failed overflow assistant before entering
|
|
12823
|
+
// this path. A resumable tail would therefore resend the same oversized
|
|
12824
|
+
// request even though maintenance made no change. Non-resumable tails
|
|
12825
|
+
// retain the existing synthetic auto-continue recovery, which changes
|
|
12826
|
+
// the request and is covered by the continuation contract.
|
|
12827
|
+
const overflowNoopWouldReplay = reason === "overflow" && willRetry && this.#isResumableAgentTail();
|
|
12828
|
+
const continuationSkipReason =
|
|
12829
|
+
!overflowNoopWouldReplay && willRetry ? this.#detectOverflowRetryContinuationSkip() : undefined;
|
|
12830
|
+
if (overflowNoopWouldReplay) {
|
|
12831
|
+
options?.beforeTerminalOverflowNoop?.();
|
|
12832
|
+
}
|
|
12355
12833
|
await this.#emitSessionEvent({
|
|
12356
12834
|
type: "auto_compaction_end",
|
|
12357
12835
|
action,
|
|
12358
12836
|
result: undefined,
|
|
12359
12837
|
aborted: false,
|
|
12360
|
-
willRetry: willRetry && !continuationSkipReason,
|
|
12838
|
+
willRetry: overflowNoopWouldReplay ? false : willRetry && !continuationSkipReason,
|
|
12839
|
+
errorMessage: overflowNoopWouldReplay
|
|
12840
|
+
? "Context overflow recovery skipped: nothing eligible to compact. Run /clear to preserve this session ID, or switch to a larger-context model before retrying."
|
|
12841
|
+
: undefined,
|
|
12361
12842
|
skipped: true,
|
|
12362
12843
|
continuationSkipReason,
|
|
12363
12844
|
});
|
|
12845
|
+
if (overflowNoopWouldReplay) {
|
|
12846
|
+
if (continueAfterMaintenance && this.agent.hasQueuedMessages()) {
|
|
12847
|
+
this.#scheduleAgentContinue({
|
|
12848
|
+
delayMs: 100,
|
|
12849
|
+
generation,
|
|
12850
|
+
shouldContinue: () => this.agent.hasQueuedMessages(),
|
|
12851
|
+
onSkip: skipReason => this.#logCompactionContinuationSkipped("queued_continue", skipReason),
|
|
12852
|
+
onError: error => this.#logCompactionContinuationError("queued_continue", error),
|
|
12853
|
+
});
|
|
12854
|
+
return { kind: "skipped", continuationScheduled: true };
|
|
12855
|
+
}
|
|
12856
|
+
return { kind: "skipped" };
|
|
12857
|
+
}
|
|
12364
12858
|
if (willRetry) {
|
|
12365
12859
|
return { kind: "skipped", continuationScheduled: this.#scheduleOverflowRetryContinuation(generation) };
|
|
12366
12860
|
}
|
|
@@ -12501,8 +12995,14 @@ export class AgentSession {
|
|
|
12501
12995
|
break;
|
|
12502
12996
|
}
|
|
12503
12997
|
|
|
12504
|
-
|
|
12505
|
-
|
|
12998
|
+
// Legacy parsed Retry-After is capped at retry.maxDelayMs (see
|
|
12999
|
+
// compactionRetryDelay); only managed fallback is uncapped.
|
|
13000
|
+
const delayMs = compactionRetryDelay(
|
|
13001
|
+
retrySettings.baseDelayMs,
|
|
13002
|
+
retrySettings.maxDelayMs,
|
|
13003
|
+
attempt,
|
|
13004
|
+
retryAfterMs,
|
|
13005
|
+
);
|
|
12506
13006
|
|
|
12507
13007
|
// If retry delay is too long (>30s), try next candidate instead of waiting
|
|
12508
13008
|
const maxAcceptableDelayMs = 30_000;
|
|
@@ -12695,7 +13195,7 @@ export class AgentSession {
|
|
|
12695
13195
|
);
|
|
12696
13196
|
}
|
|
12697
13197
|
|
|
12698
|
-
#
|
|
13198
|
+
#alibabaTokenPlanTimeoutMessagesForApi(api: string): ReadonlySet<string> | undefined {
|
|
12699
13199
|
if (api === "openai-responses" || api === "openai-completions") {
|
|
12700
13200
|
return ALIBABA_TOKEN_PLAN_FIRST_EVENT_TIMEOUT_MESSAGES[api];
|
|
12701
13201
|
}
|
|
@@ -12704,18 +13204,23 @@ export class AgentSession {
|
|
|
12704
13204
|
|
|
12705
13205
|
#isAlibabaTokenPlanFirstEventTimeout(message: AssistantMessage): boolean {
|
|
12706
13206
|
if (message.stopReason !== "error" || message.provider !== ALIBABA_TOKEN_PLAN_PROVIDER) return false;
|
|
12707
|
-
const
|
|
12708
|
-
return
|
|
13207
|
+
const timeoutMessages = this.#alibabaTokenPlanTimeoutMessagesForApi(message.api);
|
|
13208
|
+
return timeoutMessages?.has(message.errorMessage ?? "") ?? false;
|
|
12709
13209
|
}
|
|
12710
13210
|
|
|
12711
13211
|
#isAlibabaTokenPlanCompactionTimeout(candidate: Model, errorMessage: string): boolean {
|
|
12712
13212
|
if (candidate.provider !== ALIBABA_TOKEN_PLAN_PROVIDER) return false;
|
|
12713
|
-
const
|
|
12714
|
-
if (
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
13213
|
+
const timeoutMessages = this.#alibabaTokenPlanTimeoutMessagesForApi(candidate.api);
|
|
13214
|
+
if (!timeoutMessages) return false;
|
|
13215
|
+
for (const timeoutMessage of timeoutMessages) {
|
|
13216
|
+
if (
|
|
13217
|
+
errorMessage === `Summarization failed: ${timeoutMessage}` ||
|
|
13218
|
+
errorMessage === `Turn prefix summarization failed: ${timeoutMessage}`
|
|
13219
|
+
) {
|
|
13220
|
+
return true;
|
|
13221
|
+
}
|
|
13222
|
+
}
|
|
13223
|
+
return false;
|
|
12719
13224
|
}
|
|
12720
13225
|
|
|
12721
13226
|
#isTerminalProviderFirstEventTimeout(message: AssistantMessage): boolean {
|
|
@@ -13056,8 +13561,15 @@ export class AgentSession {
|
|
|
13056
13561
|
type: "maintenance",
|
|
13057
13562
|
continuation: async ownership => {
|
|
13058
13563
|
if (!ownership.isCurrent()) return;
|
|
13059
|
-
|
|
13060
|
-
|
|
13564
|
+
let terminalized = false;
|
|
13565
|
+
const successorScheduled = await this.#checkCompaction(outcome.message, true, () => {
|
|
13566
|
+
terminalized = true;
|
|
13567
|
+
this.agent.requestRunTerminal(ownership.logicalRunId, {
|
|
13568
|
+
stopReason: "error",
|
|
13569
|
+
messages: [outcome.message],
|
|
13570
|
+
});
|
|
13571
|
+
});
|
|
13572
|
+
if (terminalized || successorScheduled || !ownership.isCurrent()) return;
|
|
13061
13573
|
this.agent.requestRunTerminal(ownership.logicalRunId, {
|
|
13062
13574
|
stopReason: "error",
|
|
13063
13575
|
messages: [outcome.message],
|
|
@@ -13067,7 +13579,7 @@ export class AgentSession {
|
|
|
13067
13579
|
}
|
|
13068
13580
|
if (this.#isTerminalProviderFirstEventTimeout(outcome.failure.message)) {
|
|
13069
13581
|
// The managed transport discarded this attempt before session policy saw it.
|
|
13070
|
-
// Remove its provisional
|
|
13582
|
+
// Remove its provisional charge without changing sticky fallback selection.
|
|
13071
13583
|
this.#defaultFallbackChain().discardStartedAttempt();
|
|
13072
13584
|
return {
|
|
13073
13585
|
type: "terminal",
|
|
@@ -14543,7 +15055,10 @@ export class AgentSession {
|
|
|
14543
15055
|
* Listeners are preserved and will continue receiving events.
|
|
14544
15056
|
* @returns true if switch completed, false if cancelled by hook
|
|
14545
15057
|
*/
|
|
14546
|
-
async switchSession(
|
|
15058
|
+
async switchSession(
|
|
15059
|
+
sessionPath: string,
|
|
15060
|
+
options?: { transition?: SessionSwitchEvent["transition"] },
|
|
15061
|
+
): Promise<boolean> {
|
|
14547
15062
|
this.#beginSessionTransition("switch-session");
|
|
14548
15063
|
try {
|
|
14549
15064
|
const previousSessionFile = this.sessionManager.getSessionFile();
|
|
@@ -14600,8 +15115,10 @@ export class AgentSession {
|
|
|
14600
15115
|
try {
|
|
14601
15116
|
await this.sessionManager.setSessionFile(sessionPath);
|
|
14602
15117
|
// The successor identity is already rotated in the manager but not yet
|
|
14603
|
-
// published; gate its local:// root before publication so
|
|
14604
|
-
//
|
|
15118
|
+
// published; gate its local:// root before publication so the agent,
|
|
15119
|
+
// workflow-gate emitter, and hooks cannot resolve against an ungated
|
|
15120
|
+
// root. The manager-rotation window itself is tracked in #3138
|
|
15121
|
+
// (#2797 / #2925).
|
|
14605
15122
|
if (switchingToDifferentSession) await initializeLocalRoot(this.#localProtocolOptions());
|
|
14606
15123
|
this.#syncAgentSessionId();
|
|
14607
15124
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
@@ -14667,6 +15184,12 @@ export class AgentSession {
|
|
|
14667
15184
|
? this.#getInheritedThinkingLevel()
|
|
14668
15185
|
: persistedThinkingLevel,
|
|
14669
15186
|
);
|
|
15187
|
+
this.#thinkingLevelMutationRevision++;
|
|
15188
|
+
this.#thinkingLevelLiveMutationRevision++;
|
|
15189
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
15190
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
15191
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
15192
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
14670
15193
|
this.#thinkingLevel = nextThinkingLevel;
|
|
14671
15194
|
this.agent.setThinkingLevel(toReasoningEffort(nextThinkingLevel));
|
|
14672
15195
|
this.agent.serviceTier = hasServiceTierEntry
|
|
@@ -14677,6 +15200,7 @@ export class AgentSession {
|
|
|
14677
15200
|
// Establish the successor's durable session identity only after every
|
|
14678
15201
|
// restored state facet is live. Identity-bound extension hooks run below.
|
|
14679
15202
|
await this.sessionManager.ensureOnDisk();
|
|
15203
|
+
if (!switchingToDifferentSession) await initializeLocalRoot(this.#localProtocolOptions());
|
|
14680
15204
|
|
|
14681
15205
|
if (switchingToDifferentSession) {
|
|
14682
15206
|
// The local:// migration gate for this successor already ran above,
|
|
@@ -14699,6 +15223,7 @@ export class AgentSession {
|
|
|
14699
15223
|
type: "session_switch",
|
|
14700
15224
|
reason: "resume",
|
|
14701
15225
|
previousSessionFile,
|
|
15226
|
+
...(options?.transition ? { transition: options.transition } : {}),
|
|
14702
15227
|
});
|
|
14703
15228
|
}
|
|
14704
15229
|
return true;
|
|
@@ -14734,8 +15259,14 @@ export class AgentSession {
|
|
|
14734
15259
|
this.agent.restoreSteering(previousAgentSteeringQueue);
|
|
14735
15260
|
this.agent.restoreFollowUp(previousAgentFollowUpQueue);
|
|
14736
15261
|
if (previousModel) {
|
|
14737
|
-
this
|
|
14738
|
-
}
|
|
15262
|
+
this.#setAgentModelWithReasoningContext(previousModel);
|
|
15263
|
+
}
|
|
15264
|
+
this.#thinkingLevelMutationRevision++;
|
|
15265
|
+
this.#thinkingLevelLiveMutationRevision++;
|
|
15266
|
+
this.#pendingThinkingLevelControlSuccess = undefined;
|
|
15267
|
+
this.#pendingThinkingLevelControlFailure = undefined;
|
|
15268
|
+
this.#pendingThinkingVisibilityControlSuccess = undefined;
|
|
15269
|
+
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
14739
15270
|
this.#thinkingLevel = previousThinkingLevel;
|
|
14740
15271
|
this.agent.setThinkingLevel(toReasoningEffort(previousThinkingLevel));
|
|
14741
15272
|
this.agent.serviceTier = previousServiceTier;
|
|
@@ -14799,22 +15330,21 @@ export class AgentSession {
|
|
|
14799
15330
|
skipConversationRestore = result?.skipConversationRestore ?? false;
|
|
14800
15331
|
}
|
|
14801
15332
|
|
|
14802
|
-
//
|
|
15333
|
+
// Flush pending writes before preparing the successor.
|
|
15334
|
+
await this.sessionManager.flush();
|
|
15335
|
+
const prepared = selectedEntry.parentId
|
|
15336
|
+
? await this.sessionManager.prepareBranchedSession(selectedEntry.parentId)
|
|
15337
|
+
: await this.sessionManager.prepareNewSession({ parentSession: previousSessionFile });
|
|
15338
|
+
try {
|
|
15339
|
+
await initializeLocalRoot(this.#localProtocolOptions(prepared));
|
|
15340
|
+
this.sessionManager.commitPreparedNewSession(prepared);
|
|
15341
|
+
} catch (error) {
|
|
15342
|
+
throw await discardPreparedNewSessionAfterFailure(this.sessionManager, prepared, error);
|
|
15343
|
+
}
|
|
14803
15344
|
this.#pendingNextTurnMessages = [];
|
|
14804
15345
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
14805
|
-
|
|
14806
|
-
// Flush pending writes before branching
|
|
14807
|
-
await this.sessionManager.flush();
|
|
14808
15346
|
this.#cancelOwnAsyncJobs();
|
|
14809
15347
|
|
|
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
15348
|
this.#syncTodoPhasesFromBranch();
|
|
14819
15349
|
this.#syncAgentSessionId();
|
|
14820
15350
|
this.#bindWorkflowGateEmitter(previousWorkflowGateSessionId);
|