@gajae-code/coding-agent 0.18.7 → 0.18.8
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 +279 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +2 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +6 -1
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +31 -7
- package/dist/types/session/session-storage.d.ts +62 -2
- package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +17 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +2 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +28 -8
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +159 -20
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -9
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/internal-urls/docs-index.generated.ts +18 -17
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +88 -4
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +75 -6
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +711 -66
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +238 -43
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2647 -294
- package/src/sdk/host/session-runtime.ts +426 -109
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +75 -12
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +67 -38
- package/src/session/agent-session.ts +623 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +376 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +19 -3
- package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +417 -14
- package/src/session/task-artifact-owner-codec.ts +71 -8
- package/src/session/task-artifact-owner-retirement.ts +21 -19
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type AgentLoopConfig,
|
|
31
31
|
type AgentMessage,
|
|
32
32
|
type AgentState,
|
|
33
|
+
type AgentTerminalOwnerContext,
|
|
33
34
|
type AgentTool,
|
|
34
35
|
assertImagePlaceholdersHavePayload,
|
|
35
36
|
type ContextMaintenanceResult,
|
|
@@ -134,8 +135,8 @@ import {
|
|
|
134
135
|
classifyFallbackTrigger,
|
|
135
136
|
EMPTY_RESPONSE_PROVIDER_CODE,
|
|
136
137
|
type FallbackAttemptToken,
|
|
137
|
-
PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE,
|
|
138
138
|
type FallbackTriggerClass,
|
|
139
|
+
PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE,
|
|
139
140
|
SERVER_OVERLOADED_PROVIDER_CODE,
|
|
140
141
|
STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE,
|
|
141
142
|
} from "@gajae-code/ai/utils/fallback-transport";
|
|
@@ -218,6 +219,7 @@ import {
|
|
|
218
219
|
type FoldReason,
|
|
219
220
|
type JobFoldEvent,
|
|
220
221
|
type OwnerSubagentShutdownLease,
|
|
222
|
+
type SubagentLifecycle,
|
|
221
223
|
} from "../async";
|
|
222
224
|
import { reset as resetCapabilities } from "../capability";
|
|
223
225
|
import type { Rule } from "../capability/rule";
|
|
@@ -234,6 +236,15 @@ import {
|
|
|
234
236
|
UnknownModelProfileError,
|
|
235
237
|
validateModelProfileName,
|
|
236
238
|
} from "../config/model-profile-contract";
|
|
239
|
+
import {
|
|
240
|
+
commitDurableModelProfileOwnership,
|
|
241
|
+
type DurableModelProfileOwnership,
|
|
242
|
+
InvalidModelProfileOwnershipError,
|
|
243
|
+
ModelProfileOwnershipConflictError,
|
|
244
|
+
type ModelProfileOwnershipMarker,
|
|
245
|
+
modelProfileOwnershipMarkersEqual,
|
|
246
|
+
readDurableModelProfileOwnership,
|
|
247
|
+
} from "../config/model-profile-ownership";
|
|
237
248
|
import { resolveProfileBindings } from "../config/model-profiles";
|
|
238
249
|
import {
|
|
239
250
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
@@ -319,6 +330,7 @@ import {
|
|
|
319
330
|
buildSkillPromptMessage,
|
|
320
331
|
getSkillSlashCommandName,
|
|
321
332
|
isNamespacedSkillSlashCommandName,
|
|
333
|
+
type ParsedSkillInvocation,
|
|
322
334
|
parseSkillInvocations,
|
|
323
335
|
type Skill,
|
|
324
336
|
type SkillWarning,
|
|
@@ -408,11 +420,7 @@ import type { NotificationSessionController } from "../sdk/bus/session-control";
|
|
|
408
420
|
import { buildSyntheticModelId, syntheticNamespaceCollision } from "../sdk/model-profile-model";
|
|
409
421
|
import { sanitizePromptFailure } from "../sdk/prompt-failure";
|
|
410
422
|
import type { SecretObfuscator } from "../secrets/obfuscator";
|
|
411
|
-
import {
|
|
412
|
-
formatNoCredentialOnboardingError,
|
|
413
|
-
formatNoModelOnboardingError,
|
|
414
|
-
NoModelSelectedError,
|
|
415
|
-
} from "../setup/model-onboarding-guidance";
|
|
423
|
+
import { formatNoCredentialOnboardingError, NoModelSelectedError } from "../setup/model-onboarding-guidance";
|
|
416
424
|
import {
|
|
417
425
|
isCanonicalGjcWorkflowSkill,
|
|
418
426
|
isWorkflowContinuationInert,
|
|
@@ -527,6 +535,7 @@ import type {
|
|
|
527
535
|
SessionEntry,
|
|
528
536
|
SessionManagerCloseOutcome,
|
|
529
537
|
SessionMemoryStats,
|
|
538
|
+
SessionArtifactPublication,
|
|
530
539
|
} from "./session-manager";
|
|
531
540
|
import {
|
|
532
541
|
createReadonlySessionManager,
|
|
@@ -1218,6 +1227,11 @@ type SendUserMessageDispatchOptions = SendUserMessageOptions & {
|
|
|
1218
1227
|
};
|
|
1219
1228
|
|
|
1220
1229
|
type InternalPromptOptions = PromptOptions & { sdkRunToken?: string };
|
|
1230
|
+
interface SdkContinuationOwnership {
|
|
1231
|
+
scope: AttemptScope;
|
|
1232
|
+
token: string;
|
|
1233
|
+
cohort: string[];
|
|
1234
|
+
}
|
|
1221
1235
|
type InternalCustomMessageOptions = Pick<
|
|
1222
1236
|
PromptOptions,
|
|
1223
1237
|
| "streamingBehavior"
|
|
@@ -1427,8 +1441,7 @@ const PROVIDER_FIRST_EVENT_TIMEOUT_WITHOUT_ARTICLE_ERROR = "Provider stream time
|
|
|
1427
1441
|
const BARE_DEFAULT_CODEX_RETRYABLE_ERROR =
|
|
1428
1442
|
/^Codex error event(?:: .*)? \(code=(?:server_is_overloaded|server_error|internal_error)(?:, [^)]+)*\)$/;
|
|
1429
1443
|
const BARE_DEFAULT_CODEX_RETRYABLE_CODES = new Set(["server_is_overloaded", "server_error", "internal_error"]);
|
|
1430
|
-
const BARE_DEFAULT_CODEX_OVERLOAD_ERROR =
|
|
1431
|
-
/^Codex error event(?:: .*)? \(code=server_is_overloaded(?:, [^)]+)*\)$/;
|
|
1444
|
+
const BARE_DEFAULT_CODEX_OVERLOAD_ERROR = /^Codex error event(?:: .*)? \(code=server_is_overloaded(?:, [^)]+)*\)$/;
|
|
1432
1445
|
const BARE_DEFAULT_OVERLOAD_CODES = new Set([SERVER_OVERLOADED_PROVIDER_CODE]);
|
|
1433
1446
|
/** Anthropic's typed capacity-overload `error.type`, the only Anthropic overload code admitted below. */
|
|
1434
1447
|
const ANTHROPIC_OVERLOADED_ERROR_TYPE = "overloaded_error";
|
|
@@ -1541,14 +1554,7 @@ function isExactTypedOverloadFacts(
|
|
|
1541
1554
|
facts.retryMaxAttempts === undefined
|
|
1542
1555
|
);
|
|
1543
1556
|
}
|
|
1544
|
-
|
|
1545
|
-
* True when transport facts are exactly the provider's statusless typed
|
|
1546
|
-
* capacity-overload code (the provider code verbatim and, when present, the
|
|
1547
|
-
* OpenAI code verbatim, with no status). The agent loop must not treat the new
|
|
1548
|
-
* facts as managed transaction authority, and managed session fallback must
|
|
1549
|
-
* not gain retry/advance authority from them, so this predicate keeps both
|
|
1550
|
-
* guards reading only typed facts, never error prose.
|
|
1551
|
-
*/
|
|
1557
|
+
|
|
1552
1558
|
function isStatuslessTypedOverloadFacts(facts: TransportFailureFacts | undefined): boolean {
|
|
1553
1559
|
if (!facts) return false;
|
|
1554
1560
|
return (
|
|
@@ -1557,7 +1563,6 @@ function isStatuslessTypedOverloadFacts(facts: TransportFailureFacts | undefined
|
|
|
1557
1563
|
(facts.openaiErrorCode === undefined || facts.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE)
|
|
1558
1564
|
);
|
|
1559
1565
|
}
|
|
1560
|
-
|
|
1561
1566
|
// Deterministic auth/request/model diagnostics must surface even when a provider
|
|
1562
1567
|
// labels the failure with a transient code.
|
|
1563
1568
|
const TERMINAL_ERROR_MESSAGE =
|
|
@@ -1798,20 +1803,14 @@ function summarizeAgentBashArtifactSave(
|
|
|
1798
1803
|
};
|
|
1799
1804
|
}
|
|
1800
1805
|
|
|
1801
|
-
export interface AgentBashArtifactStore {
|
|
1802
|
-
saveArtifact(content: string, toolType: string): Promise<string | undefined>;
|
|
1803
|
-
getArtifactPath(id: string): Promise<string | null>;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
1806
|
export async function saveAgentBashOriginalArtifact(
|
|
1807
|
-
|
|
1807
|
+
publication: SessionArtifactPublication,
|
|
1808
1808
|
originalText: string,
|
|
1809
1809
|
): Promise<BashArtifactSaveResult> {
|
|
1810
1810
|
try {
|
|
1811
|
-
const artifactId = await
|
|
1812
|
-
if (!artifactId) return { status: "
|
|
1813
|
-
|
|
1814
|
-
return artifactPath ? summarizeAgentBashArtifactSave(artifactId, originalText) : { status: "unavailable" };
|
|
1811
|
+
const artifactId = await publication(originalText, "bash-original");
|
|
1812
|
+
if (!artifactId) return { status: "unavailable" };
|
|
1813
|
+
return summarizeAgentBashArtifactSave(artifactId, originalText);
|
|
1815
1814
|
} catch (error) {
|
|
1816
1815
|
return { status: "failed", diagnostic: boundAgentBashArtifactSaveDiagnostic(error) };
|
|
1817
1816
|
}
|
|
@@ -2764,6 +2763,7 @@ export class AgentSession {
|
|
|
2764
2763
|
#scopedModels: ScopedModelSelection[];
|
|
2765
2764
|
#thinkingLevel: ThinkingLevel | undefined;
|
|
2766
2765
|
#activeModelProfile: string | undefined;
|
|
2766
|
+
#unavailableModelProfile: string | undefined;
|
|
2767
2767
|
#activeProfileInstalledRoles = new Map<string, ModelSelectorValue | undefined>();
|
|
2768
2768
|
#activeProfileInstalledAgentOverrides = new Map<string, ModelSelectorValue | undefined>();
|
|
2769
2769
|
#preProfileModel: Model | undefined;
|
|
@@ -2789,6 +2789,9 @@ export class AgentSession {
|
|
|
2789
2789
|
#followUpReservationTransitionWaiters = new Set<() => void>();
|
|
2790
2790
|
#selectionFenceGeneration = 0;
|
|
2791
2791
|
#defaultModelSelectionMutationRevision = 0;
|
|
2792
|
+
#userModelSelectionRevision = 0;
|
|
2793
|
+
#userCanonicalVariantSelectionRevision = 0;
|
|
2794
|
+
#userCanonicalVariantSelection: string | undefined;
|
|
2792
2795
|
#thinkingLevelMutationRevision = 0;
|
|
2793
2796
|
#thinkingVisibilityMutationRevision = 0;
|
|
2794
2797
|
#thinkingLevelLiveMutationRevision = 0;
|
|
@@ -3439,8 +3442,17 @@ export class AgentSession {
|
|
|
3439
3442
|
this.#activeAttemptScope = undefined;
|
|
3440
3443
|
this.#activeLogicalRunId = undefined;
|
|
3441
3444
|
}
|
|
3442
|
-
#
|
|
3443
|
-
const
|
|
3445
|
+
#captureSdkContinuationOwnership(scope: AttemptScope | undefined): SdkContinuationOwnership | undefined {
|
|
3446
|
+
const token = scope ? this.#sdkRunTokensByAttemptScope.get(scope) : undefined;
|
|
3447
|
+
if (!scope || token === undefined) return undefined;
|
|
3448
|
+
return { scope, token, cohort: [...(this.#sdkRunCohortsByAttemptScope.get(scope) ?? [token])] };
|
|
3449
|
+
}
|
|
3450
|
+
#acceptSdkAttemptRun(
|
|
3451
|
+
handle: AttemptRunHandle,
|
|
3452
|
+
sdkRunToken?: string,
|
|
3453
|
+
sdkRunTokens?: string[],
|
|
3454
|
+
predecessorScope?: AttemptScope,
|
|
3455
|
+
): void {
|
|
3444
3456
|
const predecessorSdkRunToken =
|
|
3445
3457
|
predecessorScope === undefined ? undefined : this.#sdkRunTokensByAttemptScope.get(predecessorScope);
|
|
3446
3458
|
const carryRecoverySkip =
|
|
@@ -3450,14 +3462,23 @@ export class AgentSession {
|
|
|
3450
3462
|
this.#skipPostPromptRecoveryWaitByAttemptScope.delete(predecessorScope);
|
|
3451
3463
|
this.#acceptRunHandle(handle);
|
|
3452
3464
|
this.#settleTrackedOwnRunPromotions(handle);
|
|
3465
|
+
// Logical continuation lineage is causal even without an SDK submission.
|
|
3466
|
+
if (predecessorScope) {
|
|
3467
|
+
this.#lifecycleScopesByAttemptScope.set(
|
|
3468
|
+
handle.scope,
|
|
3469
|
+
this.#lifecycleScopesByAttemptScope.get(predecessorScope) ?? predecessorScope,
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3453
3472
|
if (sdkRunToken !== undefined) {
|
|
3454
3473
|
this.#activeSdkRunToken = sdkRunToken;
|
|
3455
3474
|
this.#sdkRunTokensByAttemptScope.set(handle.scope, sdkRunToken);
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3475
|
+
this.#sdkRunCohortsByAttemptScope.set(handle.scope, sdkRunTokens ?? [sdkRunToken]);
|
|
3476
|
+
for (const token of this.#sdkRunCohortsByAttemptScope.get(handle.scope) ?? []) {
|
|
3477
|
+
if (this.#sdkTerminalPublications.has(token)) continue;
|
|
3478
|
+
const publication = Promise.withResolvers<void>();
|
|
3479
|
+
void publication.promise.catch(() => undefined);
|
|
3480
|
+
this.#sdkTerminalPublications.set(token, publication);
|
|
3481
|
+
}
|
|
3461
3482
|
}
|
|
3462
3483
|
if (carryRecoverySkip) this.#skipPostPromptRecoveryWaitByAttemptScope.add(handle.scope);
|
|
3463
3484
|
}
|
|
@@ -3607,6 +3628,7 @@ export class AgentSession {
|
|
|
3607
3628
|
#restoreManagedFallbackGetApiKey: (() => void) | undefined;
|
|
3608
3629
|
// Todo completion reminder state
|
|
3609
3630
|
#todoReminderCount = 0;
|
|
3631
|
+
#todoReminderContinuationGeneration: number | undefined;
|
|
3610
3632
|
#deepInterviewUserIntentEpoch = 0;
|
|
3611
3633
|
#deepInterviewTurnOwnerEpoch = 0;
|
|
3612
3634
|
#deepInterviewGenuineUserMessageEpochs = new WeakMap<object, number>();
|
|
@@ -3800,6 +3822,7 @@ export class AgentSession {
|
|
|
3800
3822
|
#skipPostPromptRecoveryWaitByAttemptScope = new WeakSet<AttemptScope>();
|
|
3801
3823
|
#sdkRunTokensByAttemptScope = new WeakMap<AttemptScope, string>();
|
|
3802
3824
|
#sdkRunCohortsByAttemptScope = new WeakMap<AttemptScope, string[]>();
|
|
3825
|
+
#lifecycleScopesByAttemptScope = new WeakMap<AttemptScope, AttemptScope>();
|
|
3803
3826
|
#activeSdkRunToken: string | undefined;
|
|
3804
3827
|
#activeAttemptScope: AttemptScope | undefined;
|
|
3805
3828
|
#attemptAuthority!: AttemptScopeAuthority;
|
|
@@ -4025,6 +4048,8 @@ export class AgentSession {
|
|
|
4025
4048
|
// async event handlers settle. Subscribers treat agent_end as readiness, so
|
|
4026
4049
|
// publishing it earlier lets a successor corrupt the prior prompt's lifecycle.
|
|
4027
4050
|
#pendingAgentEndEmit: AgentSessionEvent | undefined;
|
|
4051
|
+
#agentEndContinuationDecisions = new WeakSet<AgentSessionEvent>();
|
|
4052
|
+
#pendingSdkAgentEnds = new Set<AgentSessionEvent>();
|
|
4028
4053
|
// A scheduled continuation owns this terminal boundary until it either starts
|
|
4029
4054
|
// the successor or proves it cannot. Holds prevent a false idle event while
|
|
4030
4055
|
// preserving the predecessor for cancellation and preflight failures.
|
|
@@ -4035,6 +4060,8 @@ export class AgentSession {
|
|
|
4035
4060
|
#agentEndPublicationInFlight = 0;
|
|
4036
4061
|
#agentEndPublicationPromise: Promise<void> = Promise.resolve();
|
|
4037
4062
|
#agentEndHandlingPromise: Promise<void> = Promise.resolve();
|
|
4063
|
+
#sdkTerminalPublications = new Map<string, PromiseWithResolvers<void>>();
|
|
4064
|
+
#publishingSdkTerminals = new Set<PromiseWithResolvers<void>>();
|
|
4038
4065
|
|
|
4039
4066
|
#obfuscator: SecretObfuscator | undefined;
|
|
4040
4067
|
#checkpointState: CheckpointState | undefined = undefined;
|
|
@@ -4073,11 +4100,7 @@ export class AgentSession {
|
|
|
4073
4100
|
readonly rawSseDebugBuffer: RawSseDebugBuffer;
|
|
4074
4101
|
|
|
4075
4102
|
#acquirePowerAssertion(): void {
|
|
4076
|
-
if (
|
|
4077
|
-
process.platform !== "darwin" &&
|
|
4078
|
-
process.platform !== "linux" &&
|
|
4079
|
-
process.platform !== "win32"
|
|
4080
|
-
) {
|
|
4103
|
+
if (process.platform !== "darwin" && process.platform !== "linux" && process.platform !== "win32") {
|
|
4081
4104
|
return;
|
|
4082
4105
|
}
|
|
4083
4106
|
if (this.#powerAssertion || this.#powerAssertionLoad) return;
|
|
@@ -4707,8 +4730,21 @@ export class AgentSession {
|
|
|
4707
4730
|
}
|
|
4708
4731
|
}
|
|
4709
4732
|
|
|
4710
|
-
#reserveDeferredAgentEndForContinuation(): symbol | undefined {
|
|
4711
|
-
const
|
|
4733
|
+
#reserveDeferredAgentEndForContinuation(predecessorScope?: AttemptScope): symbol | undefined {
|
|
4734
|
+
const resourceRunId = this.#runResourceLeaseContext.getStore()?.resourceRunId;
|
|
4735
|
+
const pending = predecessorScope
|
|
4736
|
+
? [...this.#pendingSdkAgentEnds].find(
|
|
4737
|
+
event => (event as AgentSessionEvent & { scope?: AttemptScope }).scope === predecessorScope,
|
|
4738
|
+
)
|
|
4739
|
+
: resourceRunId === undefined
|
|
4740
|
+
? this.#pendingAgentEndEmit
|
|
4741
|
+
: ([...this.#pendingSdkAgentEnds].find(
|
|
4742
|
+
event => getAgentTerminalOwnerContext(event)?.resourceRunId === resourceRunId,
|
|
4743
|
+
) ??
|
|
4744
|
+
(this.#pendingAgentEndEmit &&
|
|
4745
|
+
getAgentTerminalOwnerContext(this.#pendingAgentEndEmit)?.resourceRunId === resourceRunId
|
|
4746
|
+
? this.#pendingAgentEndEmit
|
|
4747
|
+
: undefined));
|
|
4712
4748
|
if (!pending) return undefined;
|
|
4713
4749
|
const hold = Symbol("deferred-agent-end-continuation");
|
|
4714
4750
|
this.#pendingAgentEndContinuationHolds.set(hold, pending);
|
|
@@ -4722,6 +4758,8 @@ export class AgentSession {
|
|
|
4722
4758
|
throw new Error("Cannot restore a deferred agent_end over a different pending terminal event");
|
|
4723
4759
|
}
|
|
4724
4760
|
this.#pendingAgentEndEmit = pending;
|
|
4761
|
+
const scope = (pending as AgentSessionEvent & { scope?: AttemptScope }).scope;
|
|
4762
|
+
if (scope && this.#sdkRunTokensByAttemptScope.has(scope)) this.#pendingSdkAgentEnds.add(pending);
|
|
4725
4763
|
const hold = Symbol("deferred-agent-end-continuation");
|
|
4726
4764
|
this.#pendingAgentEndContinuationHolds.set(hold, pending);
|
|
4727
4765
|
this.#deferredAgentEndWorkLeases.set(hold, this.#sessionWorkLease.acquire());
|
|
@@ -4732,6 +4770,7 @@ export class AgentSession {
|
|
|
4732
4770
|
if (!hold) return undefined;
|
|
4733
4771
|
const pending = this.#pendingAgentEndContinuationHolds.get(hold);
|
|
4734
4772
|
this.#pendingAgentEndContinuationHolds.delete(hold);
|
|
4773
|
+
if (pending) this.#pendingSdkAgentEnds.delete(pending);
|
|
4735
4774
|
this.#deferredAgentEndWorkLeases.get(hold)?.release();
|
|
4736
4775
|
this.#deferredAgentEndWorkLeases.delete(hold);
|
|
4737
4776
|
if (pending && this.#pendingAgentEndEmit === pending) {
|
|
@@ -4749,6 +4788,8 @@ export class AgentSession {
|
|
|
4749
4788
|
}
|
|
4750
4789
|
|
|
4751
4790
|
#restoreDeferredAgentEndAfterContinuationFailure(pending: AgentSessionEvent | undefined): void {
|
|
4791
|
+
const scope = (pending as (AgentSessionEvent & { scope?: AttemptScope }) | undefined)?.scope;
|
|
4792
|
+
if (pending && scope && this.#sdkRunTokensByAttemptScope.has(scope)) this.#pendingSdkAgentEnds.add(pending);
|
|
4752
4793
|
if (pending && !this.#pendingAgentEndEmit) {
|
|
4753
4794
|
this.#pendingAgentEndEmit = pending;
|
|
4754
4795
|
}
|
|
@@ -4761,6 +4802,8 @@ export class AgentSession {
|
|
|
4761
4802
|
): void {
|
|
4762
4803
|
if (!hold || !pending) return;
|
|
4763
4804
|
this.#pendingAgentEndEmit = pending;
|
|
4805
|
+
const scope = (pending as AgentSessionEvent & { scope?: AttemptScope }).scope;
|
|
4806
|
+
if (scope && this.#sdkRunTokensByAttemptScope.has(scope)) this.#pendingSdkAgentEnds.add(pending);
|
|
4764
4807
|
this.#pendingAgentEndContinuationHolds.set(hold, pending);
|
|
4765
4808
|
}
|
|
4766
4809
|
|
|
@@ -5063,11 +5106,15 @@ export class AgentSession {
|
|
|
5063
5106
|
this.#flushPendingAgentEnd();
|
|
5064
5107
|
return flushError;
|
|
5065
5108
|
}
|
|
5066
|
-
async #settleEndedInFlight(
|
|
5109
|
+
async #settleEndedInFlight(
|
|
5110
|
+
token: symbol,
|
|
5111
|
+
promptWait?: "publication" | "full",
|
|
5112
|
+
sdkPublication?: Promise<void>,
|
|
5113
|
+
): Promise<void> {
|
|
5067
5114
|
const flushError = this.#endInFlight(token);
|
|
5068
5115
|
const predecessorPromptStillInFlight = this.#livePromptsInFlight() > 0;
|
|
5069
5116
|
if (promptWait === "publication") {
|
|
5070
|
-
await this.#agentEndPublicationPromise;
|
|
5117
|
+
await (sdkPublication ?? this.#agentEndPublicationPromise);
|
|
5071
5118
|
} else if (promptWait === "full") {
|
|
5072
5119
|
await this.#agentEndHandlingPromise;
|
|
5073
5120
|
await this.#waitForPostPromptRecovery();
|
|
@@ -5107,13 +5154,23 @@ export class AgentSession {
|
|
|
5107
5154
|
}
|
|
5108
5155
|
|
|
5109
5156
|
#flushPendingAgentEnd(): void {
|
|
5157
|
+
for (const sdkPending of this.#pendingSdkAgentEnds) {
|
|
5158
|
+
if (!this.#agentEndContinuationDecisions.has(sdkPending)) continue;
|
|
5159
|
+
if ([...this.#pendingAgentEndContinuationHolds.values()].includes(sdkPending)) continue;
|
|
5160
|
+
this.#pendingSdkAgentEnds.delete(sdkPending);
|
|
5161
|
+
if (this.#pendingAgentEndEmit === sdkPending) this.#pendingAgentEndEmit = undefined;
|
|
5162
|
+
const sdkLease = this.#deferredAgentEndLeases.get(sdkPending);
|
|
5163
|
+
this.#deferredAgentEndLeases.delete(sdkPending);
|
|
5164
|
+
this.#startAgentEndPublication(sdkPending, sdkLease);
|
|
5165
|
+
}
|
|
5166
|
+
const pending = this.#pendingAgentEndEmit;
|
|
5167
|
+
if (pending && this.#pendingSdkAgentEnds.has(pending)) return;
|
|
5110
5168
|
if (
|
|
5111
5169
|
this.#livePromptsInFlight() > 0 ||
|
|
5112
5170
|
this.#agentEventHandlersInFlight > 0 ||
|
|
5113
5171
|
this.#pendingAgentEndContinuationHolds.size > 0
|
|
5114
5172
|
)
|
|
5115
5173
|
return;
|
|
5116
|
-
const pending = this.#pendingAgentEndEmit;
|
|
5117
5174
|
if (!pending) {
|
|
5118
5175
|
this.#resolveSessionSettlement();
|
|
5119
5176
|
return;
|
|
@@ -5125,13 +5182,16 @@ export class AgentSession {
|
|
|
5125
5182
|
}
|
|
5126
5183
|
|
|
5127
5184
|
#startAgentEndPublication(pending: AgentSessionEvent, lease?: RunResourceProducerLease): void {
|
|
5185
|
+
this.#agentEndContinuationDecisions.delete(pending);
|
|
5128
5186
|
this.#agentEndPublicationInFlight++;
|
|
5129
5187
|
const pendingScope = (pending as AgentSessionEvent & { scope?: AttemptScopeRef }).scope as
|
|
5130
5188
|
| AttemptScope
|
|
5131
5189
|
| undefined;
|
|
5132
5190
|
const sdkTerminal = pendingScope !== undefined && this.#sdkRunTokensByAttemptScope.has(pendingScope);
|
|
5133
5191
|
this.#agentEndPublicationPromise = this.#publishDeferredAgentEnd(pending, lease, sdkTerminal);
|
|
5134
|
-
void this.#agentEndPublicationPromise
|
|
5192
|
+
void this.#agentEndPublicationPromise.catch(error => {
|
|
5193
|
+
logger.warn("Agent terminal publication failed", { error: sanitizePromptFailure(error) });
|
|
5194
|
+
});
|
|
5135
5195
|
}
|
|
5136
5196
|
|
|
5137
5197
|
async #publishDeferredAgentEnd(
|
|
@@ -5147,6 +5207,13 @@ export class AgentSession {
|
|
|
5147
5207
|
const publicationCorrelationId = publicationScope
|
|
5148
5208
|
? this.#sdkRunTokensByAttemptScope.get(publicationScope)
|
|
5149
5209
|
: undefined;
|
|
5210
|
+
const sdkPublications = (publicationScope ? (this.#sdkRunCohortsByAttemptScope.get(publicationScope) ?? []) : [])
|
|
5211
|
+
.map(token => ({ token, publication: this.#sdkTerminalPublications.get(token) }))
|
|
5212
|
+
.filter(
|
|
5213
|
+
(entry): entry is { token: string; publication: PromiseWithResolvers<void> } =>
|
|
5214
|
+
entry.publication !== undefined,
|
|
5215
|
+
);
|
|
5216
|
+
for (const { publication } of sdkPublications) this.#publishingSdkTerminals.add(publication);
|
|
5150
5217
|
let extensionDelivery: Promise<void> | undefined;
|
|
5151
5218
|
const releaseLease = () => {
|
|
5152
5219
|
if (!lease) return;
|
|
@@ -5239,7 +5306,15 @@ export class AgentSession {
|
|
|
5239
5306
|
try {
|
|
5240
5307
|
if (lease) await this.#runResourceLeaseContext.run(lease, publish);
|
|
5241
5308
|
else await publish();
|
|
5309
|
+
for (const { publication } of sdkPublications) publication.resolve();
|
|
5310
|
+
} catch (error) {
|
|
5311
|
+
for (const { publication } of sdkPublications) publication.reject(error);
|
|
5312
|
+
throw error;
|
|
5242
5313
|
} finally {
|
|
5314
|
+
for (const { token, publication } of sdkPublications) {
|
|
5315
|
+
this.#publishingSdkTerminals.delete(publication);
|
|
5316
|
+
if (this.#sdkTerminalPublications.get(token) === publication) this.#sdkTerminalPublications.delete(token);
|
|
5317
|
+
}
|
|
5243
5318
|
if (extensionDelivery) void extensionDelivery.then(releaseLease, releaseLease);
|
|
5244
5319
|
else releaseLease();
|
|
5245
5320
|
this.#agentEndPublicationInFlight = Math.max(0, this.#agentEndPublicationInFlight - 1);
|
|
@@ -5498,12 +5573,7 @@ export class AgentSession {
|
|
|
5498
5573
|
controller !== undefined &&
|
|
5499
5574
|
controller.chain.entries.length > 1;
|
|
5500
5575
|
if (shouldCapture && modelKey) this.#managedFallbackActiveCredentialRows.delete(modelKey);
|
|
5501
|
-
if (
|
|
5502
|
-
override &&
|
|
5503
|
-
model &&
|
|
5504
|
-
storageProvider === override.storageProvider &&
|
|
5505
|
-
modelKey === override.modelKey
|
|
5506
|
-
) {
|
|
5576
|
+
if (override && model && storageProvider === override.storageProvider && modelKey === override.modelKey) {
|
|
5507
5577
|
this.#managedFallbackNextCredentialOverride = undefined;
|
|
5508
5578
|
const apiKey = await this.#modelRegistry.getApiKey(model, this.credentialSessionId, {
|
|
5509
5579
|
credentialSelector: { kind: "id", value: String(override.rowId) },
|
|
@@ -5513,10 +5583,7 @@ export class AgentSession {
|
|
|
5513
5583
|
return apiKey;
|
|
5514
5584
|
}
|
|
5515
5585
|
// The preselected credential is no longer available; try to resolve another one of the same kind.
|
|
5516
|
-
const fallbackApiKey = await this.#resolveManagedFallbackCredentialRow(
|
|
5517
|
-
model,
|
|
5518
|
-
override.credentialKind,
|
|
5519
|
-
);
|
|
5586
|
+
const fallbackApiKey = await this.#resolveManagedFallbackCredentialRow(model, override.credentialKind);
|
|
5520
5587
|
if (fallbackApiKey !== undefined) {
|
|
5521
5588
|
if (shouldCapture && model) this.#captureManagedFallbackActiveCredential(model);
|
|
5522
5589
|
return fallbackApiKey;
|
|
@@ -5524,8 +5591,7 @@ export class AgentSession {
|
|
|
5524
5591
|
throw new Error("Selected managed fallback credential is no longer available");
|
|
5525
5592
|
}
|
|
5526
5593
|
const apiKey = await invokeOriginalGetApiKey(provider);
|
|
5527
|
-
if (shouldCapture && model && isAuthenticated(apiKey))
|
|
5528
|
-
this.#captureManagedFallbackActiveCredential(model);
|
|
5594
|
+
if (shouldCapture && model && isAuthenticated(apiKey)) this.#captureManagedFallbackActiveCredential(model);
|
|
5529
5595
|
return apiKey;
|
|
5530
5596
|
};
|
|
5531
5597
|
this.agent.getApiKey = managedGetApiKey;
|
|
@@ -5612,12 +5678,14 @@ export class AgentSession {
|
|
|
5612
5678
|
void this.#queueFollowUpAfterReservation(message, displayText, {
|
|
5613
5679
|
createDisplayEntry: false,
|
|
5614
5680
|
trackExternalFollowUp: false,
|
|
5681
|
+
sdkRunToken: this.#activeSdkRunToken,
|
|
5615
5682
|
}).catch(error => {
|
|
5616
5683
|
this.#settleDeliveredOwnedRegistrations([message]);
|
|
5617
5684
|
logger.warn("Owned streaming follow-up was rejected", { error: String(error) });
|
|
5618
5685
|
});
|
|
5619
5686
|
},
|
|
5620
5687
|
injectIdle: async (messages, signal) => {
|
|
5688
|
+
const sdkRunToken = this.#activeSdkRunToken;
|
|
5621
5689
|
// Mandated boundary comment (corrected turn semantics): same origin
|
|
5622
5690
|
// split as the streaming injector — an allowed owned-completion
|
|
5623
5691
|
// delivery starts a fresh turn attempt/lineage and is not a
|
|
@@ -5652,14 +5720,14 @@ export class AgentSession {
|
|
|
5652
5720
|
await this.agent.prompt(first, {
|
|
5653
5721
|
...this.#managedFallbackPromptOptions(),
|
|
5654
5722
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
5655
|
-
if (handle) this.#acceptSdkAttemptRun(handle);
|
|
5723
|
+
if (handle) this.#acceptSdkAttemptRun(handle, sdkRunToken);
|
|
5656
5724
|
},
|
|
5657
5725
|
});
|
|
5658
5726
|
} else {
|
|
5659
5727
|
await this.agent.prompt(survivors, {
|
|
5660
5728
|
...this.#managedFallbackPromptOptions(),
|
|
5661
5729
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
5662
|
-
if (handle) this.#acceptSdkAttemptRun(handle);
|
|
5730
|
+
if (handle) this.#acceptSdkAttemptRun(handle, sdkRunToken);
|
|
5663
5731
|
},
|
|
5664
5732
|
});
|
|
5665
5733
|
}
|
|
@@ -5831,9 +5899,8 @@ export class AgentSession {
|
|
|
5831
5899
|
(largestPhase, phase) =>
|
|
5832
5900
|
Math.max(
|
|
5833
5901
|
largestPhase,
|
|
5834
|
-
phase.tasks.filter(
|
|
5835
|
-
|
|
5836
|
-
).length,
|
|
5902
|
+
phase.tasks.filter(task => task.status === "pending" || task.status === "in_progress")
|
|
5903
|
+
.length,
|
|
5837
5904
|
),
|
|
5838
5905
|
0,
|
|
5839
5906
|
);
|
|
@@ -6747,6 +6814,17 @@ export class AgentSession {
|
|
|
6747
6814
|
return { running, recent, delivery };
|
|
6748
6815
|
}
|
|
6749
6816
|
|
|
6817
|
+
/**
|
|
6818
|
+
* Lifecycle status of every subagent owned by this session, read from the
|
|
6819
|
+
* manager's stable control-plane records (they outlive AsyncJob eviction).
|
|
6820
|
+
*/
|
|
6821
|
+
getSubagentLifecycleStatuses(): SubagentLifecycle[] {
|
|
6822
|
+
const manager = this.#ownedAsyncJobManager ?? AsyncJobManager.instance();
|
|
6823
|
+
if (!manager) return [];
|
|
6824
|
+
const ownerFilter = this.#agentId ? { ownerId: this.#agentId } : undefined;
|
|
6825
|
+
return manager.getSubagentRecords(ownerFilter).map(record => record.status);
|
|
6826
|
+
}
|
|
6827
|
+
|
|
6750
6828
|
/**
|
|
6751
6829
|
* Cancel async jobs registered by *this* agent only. Used by lifecycle
|
|
6752
6830
|
* transitions (newSession, switchSession, handoff, dispose) so a subagent
|
|
@@ -6922,6 +7000,8 @@ export class AgentSession {
|
|
|
6922
7000
|
object,
|
|
6923
7001
|
{ scope?: AttemptScope; sdkRunToken?: string; persistGeneration: number; persistBarrier?: Promise<void> }
|
|
6924
7002
|
>();
|
|
7003
|
+
/** Extension handlers cannot mutate or replace the Agent-claimed run owner. */
|
|
7004
|
+
#terminalOwnerByExtensionEvent = new WeakMap<object, AgentTerminalOwnerContext>();
|
|
6925
7005
|
|
|
6926
7006
|
/**
|
|
6927
7007
|
* Capture what is true at the SYNCHRONOUS agent-event boundary, before any async work.
|
|
@@ -7075,8 +7155,8 @@ export class AgentSession {
|
|
|
7075
7155
|
const pendingAgentEnd =
|
|
7076
7156
|
event.type === "agent_end" &&
|
|
7077
7157
|
!maintenanceCheckpoint &&
|
|
7078
|
-
(this.#pendingAgentEndEmit === event || this.#
|
|
7079
|
-
?
|
|
7158
|
+
(this.#pendingAgentEndEmit === event || this.#pendingSdkAgentEnds.has(event))
|
|
7159
|
+
? event
|
|
7080
7160
|
: undefined;
|
|
7081
7161
|
if (pendingAgentEnd) {
|
|
7082
7162
|
this.#deferredAgentEndLeases.set(pendingAgentEnd, eventLease);
|
|
@@ -7093,6 +7173,7 @@ export class AgentSession {
|
|
|
7093
7173
|
}
|
|
7094
7174
|
}
|
|
7095
7175
|
this.#agentEventHandlersInFlight = Math.max(0, this.#agentEventHandlersInFlight - 1);
|
|
7176
|
+
if (event.type === "agent_end") this.#agentEndContinuationDecisions.add(event);
|
|
7096
7177
|
this.#flushPendingAgentEnd();
|
|
7097
7178
|
agentEndHandled?.resolve();
|
|
7098
7179
|
// Every other in-flight counter republishes settlement when it drops; this
|
|
@@ -7329,16 +7410,10 @@ export class AgentSession {
|
|
|
7329
7410
|
// have unwound. Subscribers treat this event as the ready signal; flushing it
|
|
7330
7411
|
// from abort while either barrier is active permits a successor to race the
|
|
7331
7412
|
// prior prompt's cleanup.
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
if (
|
|
7335
|
-
event.type === "agent_end" &&
|
|
7336
|
-
sdkTerminal &&
|
|
7337
|
-
this.#pendingAgentEndContinuationHolds.size === 0 &&
|
|
7338
|
-
this.#pendingAgentEndEmit === undefined
|
|
7339
|
-
) {
|
|
7413
|
+
if (event.type === "agent_end" && attemptScope && this.#sdkRunTokensByAttemptScope.has(attemptScope)) {
|
|
7414
|
+
this.#pendingSdkAgentEnds.add(event);
|
|
7340
7415
|
if (eventLease) this.#deferredAgentEndLeases.set(event, eventLease);
|
|
7341
|
-
this.#
|
|
7416
|
+
this.#pendingAgentEndEmit = event;
|
|
7342
7417
|
return;
|
|
7343
7418
|
}
|
|
7344
7419
|
if (event.type === "agent_end" && (this.#livePromptsInFlight() > 0 || this.#agentEventHandlersInFlight > 0)) {
|
|
@@ -7452,6 +7527,8 @@ export class AgentSession {
|
|
|
7452
7527
|
eventLease?: RunResourceProducerLease,
|
|
7453
7528
|
): Promise<void> => {
|
|
7454
7529
|
const attemptScope = (event as AgentEvent & { scope?: AttemptScope }).scope;
|
|
7530
|
+
const terminalSdkOwnership =
|
|
7531
|
+
event.type === "agent_end" ? this.#captureSdkContinuationOwnership(attemptScope) : undefined;
|
|
7455
7532
|
|
|
7456
7533
|
// These lifecycle boundaries can be delivered without awaiting this listener.
|
|
7457
7534
|
// Revoke streaming-edit cache generations before any admission, spill, or
|
|
@@ -8160,7 +8237,7 @@ export class AgentSession {
|
|
|
8160
8237
|
skipCompactionCheck: true,
|
|
8161
8238
|
resourceRunId: activePromptHandle,
|
|
8162
8239
|
maintenanceContinuation: true,
|
|
8163
|
-
|
|
8240
|
+
sdkOwnership: terminalSdkOwnership,
|
|
8164
8241
|
});
|
|
8165
8242
|
}
|
|
8166
8243
|
return;
|
|
@@ -8244,7 +8321,9 @@ export class AgentSession {
|
|
|
8244
8321
|
}
|
|
8245
8322
|
|
|
8246
8323
|
// Check for retryable errors first (overloaded, rate limit, server errors)
|
|
8247
|
-
|
|
8324
|
+
// Skip retry for todo reminder continuations - they should fail silently without retrying
|
|
8325
|
+
const isReminderContinuationError = this.#todoReminderContinuationGeneration === agentEndGeneration && agentEndGeneration !== undefined;
|
|
8326
|
+
if (!isReminderContinuationError && this.#isRetryableError(msg)) {
|
|
8248
8327
|
const transportFailure = (msg as AssistantMessage & { transportFailure?: TransportFailureFacts })
|
|
8249
8328
|
.transportFailure;
|
|
8250
8329
|
const messageScope = this.#assistantAttemptScopes.get(msg);
|
|
@@ -8257,6 +8336,10 @@ export class AgentSession {
|
|
|
8257
8336
|
);
|
|
8258
8337
|
if (didRetry) return; // Retry was initiated, don't proceed to compaction
|
|
8259
8338
|
}
|
|
8339
|
+
// Clear the reminder continuation flag after processing
|
|
8340
|
+
if (isReminderContinuationError) {
|
|
8341
|
+
this.#todoReminderContinuationGeneration = undefined;
|
|
8342
|
+
}
|
|
8260
8343
|
if (this.#retryAttempt > 0) {
|
|
8261
8344
|
// A prior retry ended on a non-retryable (terminal) message: emit
|
|
8262
8345
|
// the terminal retry-end and reset so observers clear retry state.
|
|
@@ -8275,7 +8358,7 @@ export class AgentSession {
|
|
|
8275
8358
|
const compactionTask = this.#schedulePostPromptTask(
|
|
8276
8359
|
async () => {
|
|
8277
8360
|
if (this.#isDisposed || this.#sessionAdmissionClosing) return;
|
|
8278
|
-
await this.#checkCompaction(msg, true, undefined, activePromptHandle);
|
|
8361
|
+
await this.#checkCompaction(msg, true, undefined, activePromptHandle, undefined, terminalSdkOwnership);
|
|
8279
8362
|
},
|
|
8280
8363
|
{ resourceRunId: activePromptHandle },
|
|
8281
8364
|
);
|
|
@@ -8501,10 +8584,12 @@ export class AgentSession {
|
|
|
8501
8584
|
/** Internal predecessor terminal event sequestered while waiting behind selection. */
|
|
8502
8585
|
deferredPredecessorAgentEnd?: AgentSessionEvent;
|
|
8503
8586
|
/** Internal causal SDK owner captured when this continuation was scheduled. */
|
|
8504
|
-
|
|
8587
|
+
sdkOwnership?: SdkContinuationOwnership;
|
|
8588
|
+
/** Disable managed fallback retries for this continuation (used for terminal server-initiated turns). */
|
|
8589
|
+
disableManagedFallback?: boolean;
|
|
8505
8590
|
}): Promise<void> {
|
|
8506
8591
|
const continuationAdmission = this.#captureScheduledContinuationAdmission();
|
|
8507
|
-
const
|
|
8592
|
+
const scheduledSdkOwnership = options?.sdkOwnership;
|
|
8508
8593
|
const selectionFenceGeneration =
|
|
8509
8594
|
options?.selectionFenceGeneration ??
|
|
8510
8595
|
this.#selectionFenceGenerationContext.getStore() ??
|
|
@@ -8526,7 +8611,7 @@ export class AgentSession {
|
|
|
8526
8611
|
generation: deferredPromptGeneration,
|
|
8527
8612
|
selectionFenceGeneration,
|
|
8528
8613
|
deferredPredecessorAgentEnd,
|
|
8529
|
-
|
|
8614
|
+
sdkOwnership: scheduledSdkOwnership,
|
|
8530
8615
|
});
|
|
8531
8616
|
} finally {
|
|
8532
8617
|
// The recursive call synchronously re-reserved its settlement
|
|
@@ -8646,14 +8731,21 @@ export class AgentSession {
|
|
|
8646
8731
|
}
|
|
8647
8732
|
const predecessorAgentEnd =
|
|
8648
8733
|
this.#claimDeferredAgentEndForContinuation(predecessorAgentEndHold);
|
|
8734
|
+
const predecessorScope = (
|
|
8735
|
+
predecessorAgentEnd as (AgentSessionEvent & { scope?: AttemptScope }) | undefined
|
|
8736
|
+
)?.scope;
|
|
8737
|
+
const predecessorSdkOwnership = this.#captureSdkContinuationOwnership(predecessorScope);
|
|
8649
8738
|
let predecessorAccepted = false;
|
|
8650
8739
|
const releasePredecessor = () => {
|
|
8651
8740
|
if (predecessorAccepted) return;
|
|
8652
8741
|
predecessorAccepted = true;
|
|
8653
|
-
|
|
8742
|
+
if (startsQueuedSuccessor)
|
|
8743
|
+
this.#restoreDeferredAgentEndAfterContinuationFailure(predecessorAgentEnd);
|
|
8744
|
+
else this.#releaseDeferredAgentEndLease(predecessorAgentEnd);
|
|
8654
8745
|
};
|
|
8655
8746
|
const hasQueuedMessages = this.agent.hasQueuedMessages();
|
|
8656
8747
|
const startsQueuedSuccessor =
|
|
8748
|
+
options?.maintenanceContinuation !== true &&
|
|
8657
8749
|
hasQueuedMessages &&
|
|
8658
8750
|
(options?.continueQueuedOnly === true ||
|
|
8659
8751
|
this.agent.state.messages.at(-1)?.role === "assistant");
|
|
@@ -8662,7 +8754,7 @@ export class AgentSession {
|
|
|
8662
8754
|
: this.agent.continue.bind(this.agent);
|
|
8663
8755
|
try {
|
|
8664
8756
|
await continueQueued({
|
|
8665
|
-
...this.#managedFallbackPromptOptions(),
|
|
8757
|
+
...(options?.disableManagedFallback ? { fallbackManaged: false } : this.#managedFallbackPromptOptions()),
|
|
8666
8758
|
maintenanceContinuation: options?.maintenanceContinuation,
|
|
8667
8759
|
// Reset only after continue() has claimed the queued turn. Skipped or stale
|
|
8668
8760
|
// continuations retain predecessor accounting, and resetAttemptBudget keeps
|
|
@@ -8672,13 +8764,21 @@ export class AgentSession {
|
|
|
8672
8764
|
// agent_start (review P1); their queued messages are in-run
|
|
8673
8765
|
// consumptions, not own-run promotions.
|
|
8674
8766
|
const startsOwn = options?.maintenanceContinuation !== true;
|
|
8675
|
-
const
|
|
8676
|
-
|
|
8677
|
-
|
|
8767
|
+
const inheritedSdkOwnership =
|
|
8768
|
+
options?.continueQueuedOnly || startsQueuedSuccessor
|
|
8769
|
+
? undefined
|
|
8770
|
+
: (scheduledSdkOwnership ?? predecessorSdkOwnership);
|
|
8771
|
+
const inheritedPredecessorScope =
|
|
8772
|
+
options?.continueQueuedOnly || startsQueuedSuccessor
|
|
8773
|
+
? undefined
|
|
8774
|
+
: (inheritedSdkOwnership?.scope ?? predecessorScope);
|
|
8678
8775
|
const consumedSdkRunTokens = acceptance.consumedQueuedMessages
|
|
8679
8776
|
.map(message => this.#sdkRunTokensByQueuedMessage.get(message))
|
|
8680
8777
|
.filter((token): token is string => token !== undefined);
|
|
8681
|
-
const sdkRunToken = consumedSdkRunTokens[0]
|
|
8778
|
+
const sdkRunToken = inheritedSdkOwnership?.token ?? consumedSdkRunTokens[0];
|
|
8779
|
+
const sdkRunTokens = inheritedSdkOwnership
|
|
8780
|
+
? [...new Set([...inheritedSdkOwnership.cohort, ...consumedSdkRunTokens])]
|
|
8781
|
+
: consumedSdkRunTokens;
|
|
8682
8782
|
this.#fireQueuedPromotionHooks(acceptance.consumedQueuedMessages, {
|
|
8683
8783
|
startsOwnRun: startsOwn,
|
|
8684
8784
|
});
|
|
@@ -8686,7 +8786,8 @@ export class AgentSession {
|
|
|
8686
8786
|
this.#acceptSdkAttemptRun(
|
|
8687
8787
|
handle,
|
|
8688
8788
|
sdkRunToken,
|
|
8689
|
-
|
|
8789
|
+
sdkRunTokens.length > 0 ? sdkRunTokens : undefined,
|
|
8790
|
+
inheritedPredecessorScope,
|
|
8690
8791
|
);
|
|
8691
8792
|
options?.onRunAccepted?.(handle);
|
|
8692
8793
|
// Keep the queued token available through the acceptance callback;
|
|
@@ -8813,7 +8914,11 @@ export class AgentSession {
|
|
|
8813
8914
|
return compactionSettings.autoContinue === false ? "auto_continue_disabled_non_resumable_tail" : undefined;
|
|
8814
8915
|
}
|
|
8815
8916
|
|
|
8816
|
-
async #scheduleOverflowRetryContinuation(
|
|
8917
|
+
async #scheduleOverflowRetryContinuation(
|
|
8918
|
+
generation: number,
|
|
8919
|
+
resourceRunId?: string,
|
|
8920
|
+
sdkOwnership?: SdkContinuationOwnership,
|
|
8921
|
+
): Promise<boolean> {
|
|
8817
8922
|
this.#stripOverflowFailedTurnForRetry();
|
|
8818
8923
|
const snapshot = await this.#compactionStateSnapshot();
|
|
8819
8924
|
if (
|
|
@@ -8830,6 +8935,7 @@ export class AgentSession {
|
|
|
8830
8935
|
generation,
|
|
8831
8936
|
suppressPredecessorAgentEnd: true,
|
|
8832
8937
|
resourceRunId,
|
|
8938
|
+
sdkOwnership,
|
|
8833
8939
|
onSkip: reason => this.#logCompactionContinuationSkipped("overflow_retry", reason),
|
|
8834
8940
|
onError: error => this.#logCompactionContinuationError("overflow_retry", error),
|
|
8835
8941
|
});
|
|
@@ -8838,7 +8944,7 @@ export class AgentSession {
|
|
|
8838
8944
|
|
|
8839
8945
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
8840
8946
|
if (compactionSettings.autoContinue !== false) {
|
|
8841
|
-
this.#scheduleAutoContinuePrompt(generation, false, resourceRunId);
|
|
8947
|
+
this.#scheduleAutoContinuePrompt(generation, false, resourceRunId, undefined, undefined, sdkOwnership);
|
|
8842
8948
|
return true;
|
|
8843
8949
|
}
|
|
8844
8950
|
|
|
@@ -8852,7 +8958,7 @@ export class AgentSession {
|
|
|
8852
8958
|
resourceRunId?: string,
|
|
8853
8959
|
deferredSelectionFenceGeneration?: number,
|
|
8854
8960
|
deferredPredecessorAgentEnd?: AgentSessionEvent,
|
|
8855
|
-
|
|
8961
|
+
sdkOwnership?: SdkContinuationOwnership,
|
|
8856
8962
|
): void {
|
|
8857
8963
|
if (this.#sessionTransitionKind !== undefined || this.#handoffTransitionActive) {
|
|
8858
8964
|
this.#deferredAutoContinueDuringTransition = () =>
|
|
@@ -8862,14 +8968,16 @@ export class AgentSession {
|
|
|
8862
8968
|
resourceRunId,
|
|
8863
8969
|
deferredSelectionFenceGeneration,
|
|
8864
8970
|
deferredPredecessorAgentEnd,
|
|
8865
|
-
|
|
8971
|
+
sdkOwnership,
|
|
8866
8972
|
);
|
|
8867
8973
|
return;
|
|
8868
8974
|
}
|
|
8869
8975
|
const scheduledGeneration = generation;
|
|
8870
|
-
const
|
|
8871
|
-
|
|
8872
|
-
|
|
8976
|
+
const scheduledSdkOwnership =
|
|
8977
|
+
sdkOwnership ??
|
|
8978
|
+
this.#captureSdkContinuationOwnership(
|
|
8979
|
+
(deferredPredecessorAgentEnd as (AgentSessionEvent & { scope?: AttemptScope }) | undefined)?.scope,
|
|
8980
|
+
);
|
|
8873
8981
|
const continuationAuthorized = async (
|
|
8874
8982
|
signal: AbortSignal,
|
|
8875
8983
|
hasPendingNextTurnMessages = false,
|
|
@@ -8926,7 +9034,7 @@ export class AgentSession {
|
|
|
8926
9034
|
resourceRunId,
|
|
8927
9035
|
selectionFenceGeneration,
|
|
8928
9036
|
predecessorAgentEnd,
|
|
8929
|
-
|
|
9037
|
+
scheduledSdkOwnership,
|
|
8930
9038
|
);
|
|
8931
9039
|
} finally {
|
|
8932
9040
|
this.#endSelectionFenceDeferralTracking(selectionFenceGeneration);
|
|
@@ -8974,7 +9082,9 @@ export class AgentSession {
|
|
|
8974
9082
|
promptText,
|
|
8975
9083
|
{
|
|
8976
9084
|
skipPostPromptRecoveryWait: true,
|
|
8977
|
-
sdkRunToken:
|
|
9085
|
+
sdkRunToken: scheduledSdkOwnership?.token,
|
|
9086
|
+
sdkRunTokens: scheduledSdkOwnership?.cohort,
|
|
9087
|
+
sdkPredecessorScope: scheduledSdkOwnership?.scope,
|
|
8978
9088
|
skipCompactionCheck: true,
|
|
8979
9089
|
predecessorAgentEndHold,
|
|
8980
9090
|
onFinalPreflight: ({ hasPendingNextTurnMessages }) =>
|
|
@@ -9910,7 +10020,12 @@ export class AgentSession {
|
|
|
9910
10020
|
type: "agent_start",
|
|
9911
10021
|
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
9912
10022
|
...(deliveryScope
|
|
9913
|
-
? {
|
|
10023
|
+
? {
|
|
10024
|
+
sdkRunTokens: this.#sdkRunCohortsByAttemptScope.get(deliveryScope as AttemptScope),
|
|
10025
|
+
lifecycleScope:
|
|
10026
|
+
this.#lifecycleScopesByAttemptScope.get(deliveryScope as AttemptScope) ??
|
|
10027
|
+
(deliveryScope as AttemptScope),
|
|
10028
|
+
}
|
|
9914
10029
|
: {}),
|
|
9915
10030
|
},
|
|
9916
10031
|
undefined,
|
|
@@ -9928,17 +10043,19 @@ export class AgentSession {
|
|
|
9928
10043
|
deliveryScope,
|
|
9929
10044
|
);
|
|
9930
10045
|
} else if (event.type === "agent_end") {
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
);
|
|
10046
|
+
const extensionEvent = {
|
|
10047
|
+
type: "agent_end" as const,
|
|
10048
|
+
messages: event.messages,
|
|
10049
|
+
stopReason: event.stopReason,
|
|
10050
|
+
maintenanceOutcome: event.maintenanceOutcome,
|
|
10051
|
+
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
10052
|
+
};
|
|
10053
|
+
// Keep the Agent-claimed owner OUTSIDE the public extension event and
|
|
10054
|
+
// its replaceable side channel. Only the owning session may attest
|
|
10055
|
+
// this exact projected event to the SDK bus.
|
|
10056
|
+
const terminalOwner = getAgentTerminalOwnerContext(event);
|
|
10057
|
+
if (terminalOwner) this.#terminalOwnerByExtensionEvent.set(extensionEvent, terminalOwner);
|
|
10058
|
+
await this.#extensionRunner.emit(extensionEvent, undefined, deliveryScope);
|
|
9942
10059
|
} else if (event.type === "turn_start") {
|
|
9943
10060
|
const hookEvent: TurnStartEvent = {
|
|
9944
10061
|
type: "turn_start",
|
|
@@ -10145,6 +10262,22 @@ export class AgentSession {
|
|
|
10145
10262
|
this.#abortActiveMidRunBarriers();
|
|
10146
10263
|
this.#compactionQueuedDeliveryArmed = false;
|
|
10147
10264
|
if (this.#unsubscribeAgent) {
|
|
10265
|
+
// This accepted cohort cannot observe its terminal after the bridge is
|
|
10266
|
+
// removed. Reject only that captured owner; queued roots and already
|
|
10267
|
+
// publishing terminals retain their independently owned publication.
|
|
10268
|
+
const ownership = this.#captureSdkContinuationOwnership(this.#activeAttemptScope);
|
|
10269
|
+
if (ownership) {
|
|
10270
|
+
const failure = Object.assign(
|
|
10271
|
+
new Error("Agent event bridge disconnected before SDK terminal publication."),
|
|
10272
|
+
{ code: "cancelled" },
|
|
10273
|
+
);
|
|
10274
|
+
for (const token of ownership.cohort) {
|
|
10275
|
+
const publication = this.#sdkTerminalPublications.get(token);
|
|
10276
|
+
if (!publication || this.#publishingSdkTerminals.has(publication)) continue;
|
|
10277
|
+
publication.reject(failure);
|
|
10278
|
+
this.#sdkTerminalPublications.delete(token);
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10148
10281
|
this.#unsubscribeAgent();
|
|
10149
10282
|
this.#unsubscribeAgent = undefined;
|
|
10150
10283
|
}
|
|
@@ -10302,6 +10435,11 @@ export class AgentSession {
|
|
|
10302
10435
|
// settle every accepted SDK handle now so consumed submissions cannot wait
|
|
10303
10436
|
// forever for an agent_end that teardown deliberately suppresses.
|
|
10304
10437
|
this.#terminalizeQueuedSdkWorkForSessionTransition(this.#queuedMessagesForSessionTransition());
|
|
10438
|
+
const publicationFailure = Object.assign(new Error("Session disposed before SDK terminal publication."), {
|
|
10439
|
+
code: "cancelled",
|
|
10440
|
+
});
|
|
10441
|
+
for (const publication of this.#sdkTerminalPublications.values()) publication.reject(publicationFailure);
|
|
10442
|
+
this.#sdkTerminalPublications.clear();
|
|
10305
10443
|
// Invalidate every coordinator event admitted before disposal. Handlers may
|
|
10306
10444
|
// still unwind, but their captured generation can no longer enqueue a write.
|
|
10307
10445
|
this.#coordinatorPersistGeneration += 1;
|
|
@@ -10671,6 +10809,11 @@ export class AgentSession {
|
|
|
10671
10809
|
* at `timeoutMs` so a wedged subprocess can't stall process exit.
|
|
10672
10810
|
*/
|
|
10673
10811
|
async disposeChildSubprocesses(timeoutMs = SIGNAL_TEARDOWN_TIMEOUT_MS): Promise<void> {
|
|
10812
|
+
this.#evalExecutionDisposing = true;
|
|
10813
|
+
this.abortEval();
|
|
10814
|
+
const evalExecutionsSettled = this.#waitForEvalExecutionsToSettle(timeoutMs).then(settled => {
|
|
10815
|
+
if (!settled) logger.warn("signal teardown: active eval executions remain unsettled");
|
|
10816
|
+
});
|
|
10674
10817
|
const sessionId = this.sessionManager.getSessionId();
|
|
10675
10818
|
const kernelOwnerId = this.#evalKernelOwnerId;
|
|
10676
10819
|
this.#unregisterResourceGc?.();
|
|
@@ -10680,6 +10823,7 @@ export class AgentSession {
|
|
|
10680
10823
|
this.#unregisterDelegationHintSettings?.();
|
|
10681
10824
|
this.#unregisterDelegationHintSettings = undefined;
|
|
10682
10825
|
const work = Promise.allSettled([
|
|
10826
|
+
evalExecutionsSettled,
|
|
10683
10827
|
// kill:true so a forced exit also reaps spawned-app Chrome we own (headless
|
|
10684
10828
|
// always closes; connected/attached browsers only disconnect — never killed).
|
|
10685
10829
|
releaseTabsForOwner(sessionId, { kill: true }).catch((error: unknown) =>
|
|
@@ -10708,7 +10852,8 @@ export class AgentSession {
|
|
|
10708
10852
|
logger.warn("signal teardown: tool session transition cleanups failed", { error }),
|
|
10709
10853
|
),
|
|
10710
10854
|
]);
|
|
10711
|
-
await Promise.race([work, Bun.sleep(timeoutMs)]);
|
|
10855
|
+
const completed = await Promise.race([work.then(() => true), Bun.sleep(timeoutMs).then(() => false)]);
|
|
10856
|
+
if (!completed) logger.warn("signal teardown: cleanup exceeded its bounded caller deadline");
|
|
10712
10857
|
}
|
|
10713
10858
|
|
|
10714
10859
|
#rebindProviderSessionState(providerSessionState: Map<string, ProviderSessionState>): void {
|
|
@@ -12357,7 +12502,9 @@ export class AgentSession {
|
|
|
12357
12502
|
*/
|
|
12358
12503
|
get messages(): AgentMessage[] {
|
|
12359
12504
|
const messages = this.agent.state.messages;
|
|
12360
|
-
return messages.some(
|
|
12505
|
+
return messages.some(
|
|
12506
|
+
message => message.role === "custom" && this.#isEphemeralCustomMessageType(message.customType),
|
|
12507
|
+
)
|
|
12361
12508
|
? this.#withoutEphemeralCustomMessages(messages)
|
|
12362
12509
|
: messages;
|
|
12363
12510
|
}
|
|
@@ -13498,6 +13645,36 @@ export class AgentSession {
|
|
|
13498
13645
|
}
|
|
13499
13646
|
}
|
|
13500
13647
|
|
|
13648
|
+
/**
|
|
13649
|
+
* The loaded skill a prompt's text dispatches to, or undefined when `prompt`
|
|
13650
|
+
* would submit it as ordinary text (unknown or disabled `/skill:` names,
|
|
13651
|
+
* chains, or attached images). Single source for dispatch and for callers
|
|
13652
|
+
* that must mirror it, such as startup title seeding.
|
|
13653
|
+
*/
|
|
13654
|
+
resolvePromptSkillInvocation(text: string, images?: readonly unknown[]): ParsedSkillInvocation | undefined {
|
|
13655
|
+
if (images?.length || !text.startsWith("/") || !isNamespacedSkillSlashCommandName(text.slice(1))) return;
|
|
13656
|
+
const skillCommands = new Map(this.skills.map(skill => [getSkillSlashCommandName(skill), skill]));
|
|
13657
|
+
const invocations = parseSkillInvocations(text, skillCommands);
|
|
13658
|
+
return invocations.length === 1 ? invocations[0] : undefined;
|
|
13659
|
+
}
|
|
13660
|
+
|
|
13661
|
+
/**
|
|
13662
|
+
* Whether `prompt` intercepts this slash text as an extension or custom/MCP
|
|
13663
|
+
* command. Used to keep command arguments away from the title model. Mirrors the
|
|
13664
|
+
* command lookup in `#tryExecuteExtensionCommand` / `#tryExecuteCustomCommand`
|
|
13665
|
+
* without executing anything.
|
|
13666
|
+
*/
|
|
13667
|
+
isLocallyHandledSlashCommand(text: string): boolean {
|
|
13668
|
+
if (!text.startsWith("/")) return false;
|
|
13669
|
+
const spaceIndex = text.indexOf(" ");
|
|
13670
|
+
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
13671
|
+
return (
|
|
13672
|
+
this.#extensionRunner?.getCommand(commandName) !== undefined ||
|
|
13673
|
+
this.#customCommands.some(c => c.command.name === commandName) ||
|
|
13674
|
+
this.#mcpPromptCommands.some(c => c.command.name === commandName)
|
|
13675
|
+
);
|
|
13676
|
+
}
|
|
13677
|
+
|
|
13501
13678
|
async #promptInternal(
|
|
13502
13679
|
text: string,
|
|
13503
13680
|
options: PromptOptions | undefined,
|
|
@@ -13519,38 +13696,29 @@ export class AgentSession {
|
|
|
13519
13696
|
if (owner && !owner.released) throw this.#sessionAdmissionBusyError();
|
|
13520
13697
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
13521
13698
|
|
|
13522
|
-
if (
|
|
13523
|
-
|
|
13524
|
-
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
? { onPreflightAcceptCommit: options.onPreflightAcceptCommit }
|
|
13546
|
-
: {}),
|
|
13547
|
-
...(options?.preflightSignal ? { preflightSignal: options.preflightSignal } : {}),
|
|
13548
|
-
...(options?.sdkRunCapability ? { sdkRunCapability: options.sdkRunCapability } : {}),
|
|
13549
|
-
}
|
|
13550
|
-
: undefined,
|
|
13551
|
-
);
|
|
13552
|
-
return;
|
|
13553
|
-
}
|
|
13699
|
+
if (expandPromptTemplates) {
|
|
13700
|
+
const invocation = this.resolvePromptSkillInvocation(text, options?.images);
|
|
13701
|
+
if (invocation) {
|
|
13702
|
+
await this.invokeSkill(
|
|
13703
|
+
invocation.skill.name,
|
|
13704
|
+
invocation.args,
|
|
13705
|
+
options?.onPreflightAccepted ||
|
|
13706
|
+
options?.onPreflightAcceptCommit ||
|
|
13707
|
+
options?.preflightSignal ||
|
|
13708
|
+
internalOptions?.sdkRunToken
|
|
13709
|
+
? {
|
|
13710
|
+
...(options?.onPreflightAccepted
|
|
13711
|
+
? { onPreflightAccepted: options.onPreflightAccepted }
|
|
13712
|
+
: {}),
|
|
13713
|
+
...(options?.onPreflightAcceptCommit
|
|
13714
|
+
? { onPreflightAcceptCommit: options.onPreflightAcceptCommit }
|
|
13715
|
+
: {}),
|
|
13716
|
+
...(options?.preflightSignal ? { preflightSignal: options.preflightSignal } : {}),
|
|
13717
|
+
...(options?.sdkRunCapability ? { sdkRunCapability: options.sdkRunCapability } : {}),
|
|
13718
|
+
}
|
|
13719
|
+
: undefined,
|
|
13720
|
+
);
|
|
13721
|
+
return;
|
|
13554
13722
|
}
|
|
13555
13723
|
}
|
|
13556
13724
|
|
|
@@ -13967,6 +14135,8 @@ export class AgentSession {
|
|
|
13967
14135
|
| "preflightSignal"
|
|
13968
14136
|
> & {
|
|
13969
14137
|
sdkRunToken?: string;
|
|
14138
|
+
sdkRunTokens?: string[];
|
|
14139
|
+
sdkPredecessorScope?: AttemptScope;
|
|
13970
14140
|
prependMessages?: AgentMessage[];
|
|
13971
14141
|
skipPostPromptRecoveryWait?: boolean;
|
|
13972
14142
|
predecessorAgentEndHold?: symbol;
|
|
@@ -14037,6 +14207,7 @@ export class AgentSession {
|
|
|
14037
14207
|
let skipPostPromptRecoveryWait =
|
|
14038
14208
|
options?.skipPostPromptRecoveryWait === true || options?.sdkRunToken !== undefined;
|
|
14039
14209
|
let promptAttemptScope: AttemptScope | undefined;
|
|
14210
|
+
let promptSdkPublication: Promise<void> | undefined;
|
|
14040
14211
|
let hindsightRecall: string | undefined;
|
|
14041
14212
|
try {
|
|
14042
14213
|
this.#throwIfPromptPreflightCancelled(generation, preflightSignal);
|
|
@@ -14337,10 +14508,26 @@ export class AgentSession {
|
|
|
14337
14508
|
...(options?.sdkRunToken ? { sdkRunToken: options.sdkRunToken } : {}),
|
|
14338
14509
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
14339
14510
|
promptAttemptScope = handle?.scope;
|
|
14340
|
-
|
|
14511
|
+
// A synthetic recovery prompt can make Agent retire the old managed
|
|
14512
|
+
// run at method entry. Suppress only that captured predecessor once
|
|
14513
|
+
// the replacement accepts; pre-acceptance failures still publish it.
|
|
14514
|
+
if (handle && options?.sdkPredecessorScope) {
|
|
14515
|
+
const predecessor = this.#claimDeferredAgentEndForContinuation(
|
|
14516
|
+
this.#reserveDeferredAgentEndForContinuation(options.sdkPredecessorScope),
|
|
14517
|
+
);
|
|
14518
|
+
this.#releaseDeferredAgentEndLease(predecessor);
|
|
14519
|
+
}
|
|
14520
|
+
if (handle)
|
|
14521
|
+
this.#acceptSdkAttemptRun(
|
|
14522
|
+
handle,
|
|
14523
|
+
options?.sdkRunToken,
|
|
14524
|
+
options?.sdkRunTokens,
|
|
14525
|
+
options?.sdkPredecessorScope,
|
|
14526
|
+
);
|
|
14341
14527
|
else this.#acceptRunHandle(handle);
|
|
14342
14528
|
if (handle && options?.sdkRunToken) {
|
|
14343
14529
|
this.#activeSdkRunToken = options.sdkRunToken;
|
|
14530
|
+
promptSdkPublication = this.#sdkTerminalPublications.get(options.sdkRunToken)?.promise;
|
|
14344
14531
|
}
|
|
14345
14532
|
options?.onRunAccepted?.(handle);
|
|
14346
14533
|
options?.admissionLease?.release();
|
|
@@ -14423,7 +14610,11 @@ export class AgentSession {
|
|
|
14423
14610
|
this.#releaseIrcRosterClaim(rosterClaim.token, rosterClaim.epoch);
|
|
14424
14611
|
}
|
|
14425
14612
|
this.#releaseDeferredAgentEndContinuation(predecessorAgentEndHold);
|
|
14426
|
-
await this.#settleEndedInFlight(
|
|
14613
|
+
await this.#settleEndedInFlight(
|
|
14614
|
+
inFlightPrompt,
|
|
14615
|
+
skipPostPromptRecoveryWait ? "publication" : "full",
|
|
14616
|
+
promptSdkPublication,
|
|
14617
|
+
);
|
|
14427
14618
|
}
|
|
14428
14619
|
}
|
|
14429
14620
|
|
|
@@ -14711,10 +14902,7 @@ export class AgentSession {
|
|
|
14711
14902
|
// prompt still in preflight, aborting) has no run to steer. Ordinary steers
|
|
14712
14903
|
// become a sequential follow-up owned by the next turn; SDK turn-bound
|
|
14713
14904
|
// steers fail closed rather than being delivered to a successor run.
|
|
14714
|
-
if (
|
|
14715
|
-
options?.expectedSdkRunToken !== undefined &&
|
|
14716
|
-
this.#activeSdkRunToken !== options.expectedSdkRunToken
|
|
14717
|
-
)
|
|
14905
|
+
if (options?.expectedSdkRunToken !== undefined && this.#activeSdkRunToken !== options.expectedSdkRunToken)
|
|
14718
14906
|
throw Object.assign(new Error("The expected SDK run is not active."), { code: "turn_not_active" });
|
|
14719
14907
|
const admission = this.agent.steer(message, options?.forceOneAtATime ? { forceOneAtATime: true } : undefined);
|
|
14720
14908
|
if (!admission.admitted) {
|
|
@@ -14771,8 +14959,8 @@ export class AgentSession {
|
|
|
14771
14959
|
/**
|
|
14772
14960
|
* One-shot preflight-abort binding: an invocation cancelled after its queue
|
|
14773
14961
|
* admission must cancel the submission it admitted, so an aborted dispatch
|
|
14774
|
-
* can never execute later.
|
|
14775
|
-
*
|
|
14962
|
+
* can never execute later. Explicit and implicitly diverted queue admissions
|
|
14963
|
+
* share this helper so their cancellation semantics cannot diverge
|
|
14776
14964
|
* (exact-head review P1).
|
|
14777
14965
|
*/
|
|
14778
14966
|
#bindPreflightAbortCancellation(
|
|
@@ -15453,6 +15641,7 @@ export class AgentSession {
|
|
|
15453
15641
|
forceOneAtATime: sequential !== undefined,
|
|
15454
15642
|
createDisplayEntry: false,
|
|
15455
15643
|
trackExternalFollowUp: false,
|
|
15644
|
+
sdkRunToken: this.#activeSdkRunToken,
|
|
15456
15645
|
});
|
|
15457
15646
|
if (this.#abortUnwind && !sequential) this.#abortUnwindSteerFallbacks.push(appMessage);
|
|
15458
15647
|
// The chip now describes follow-up work: keep its mode and identity
|
|
@@ -15483,6 +15672,7 @@ export class AgentSession {
|
|
|
15483
15672
|
forceOneAtATime: options?.followUpQueuePolicy === "sequential",
|
|
15484
15673
|
createDisplayEntry: false,
|
|
15485
15674
|
trackExternalFollowUp: false,
|
|
15675
|
+
sdkRunToken: this.#activeSdkRunToken,
|
|
15486
15676
|
});
|
|
15487
15677
|
this.#bindCustomDisplayEntry(appMessage, "followUp");
|
|
15488
15678
|
// The session can report streaming while no agent loop owns the queue
|
|
@@ -15904,12 +16094,13 @@ export class AgentSession {
|
|
|
15904
16094
|
if (this.#isLiveTurnBusy() && !waitedForAbortUnwind) {
|
|
15905
16095
|
if (options?.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
15906
16096
|
assertPreflightStillOpen();
|
|
15907
|
-
await this.#queueSteer(text, images, {
|
|
16097
|
+
const queuedSteer = await this.#queueSteer(text, images, {
|
|
15908
16098
|
claimsGenuineUserIntent: true,
|
|
15909
|
-
onPromoted:
|
|
16099
|
+
onPromoted: onQueuedPromoted,
|
|
15910
16100
|
external: true,
|
|
15911
16101
|
sdkRunToken: internalOptions?.sdkRunToken,
|
|
15912
16102
|
});
|
|
16103
|
+
this.#bindPreflightAbortCancellation(options?.preflightSignal, queuedSteer);
|
|
15913
16104
|
// Dispatch-race disposition (#4668 review P1): the SDK snapshot-decided
|
|
15914
16105
|
// this submission starts its own turn (idle at dispatch), but the
|
|
15915
16106
|
// session began streaming before sendUserMessage ran, so the message
|
|
@@ -15919,8 +16110,15 @@ export class AgentSession {
|
|
|
15919
16110
|
// would terminalize the accepted request as an own-run completion
|
|
15920
16111
|
// before it is consumed. Report the internal in-run disposition so the
|
|
15921
16112
|
// runtime attaches the correlation to the in-flight run instead.
|
|
15922
|
-
|
|
15923
|
-
|
|
16113
|
+
try {
|
|
16114
|
+
assertPreflightStillOpen();
|
|
16115
|
+
options?.onDispatchDisposition?.({ startsOwnRun: false });
|
|
16116
|
+
options?.onPreflightAccepted?.();
|
|
16117
|
+
assertPreflightStillOpen();
|
|
16118
|
+
} catch (error) {
|
|
16119
|
+
queuedSteer.cancel();
|
|
16120
|
+
throw error;
|
|
16121
|
+
}
|
|
15924
16122
|
return;
|
|
15925
16123
|
}
|
|
15926
16124
|
|
|
@@ -16851,6 +17049,12 @@ export class AgentSession {
|
|
|
16851
17049
|
if (!lineageIdHash) return undefined;
|
|
16852
17050
|
return this.#promptGeneration;
|
|
16853
17051
|
}
|
|
17052
|
+
getTerminalRunOwnerForEvent(event: object): AgentTerminalOwnerContext | undefined {
|
|
17053
|
+
return this.#terminalOwnerByExtensionEvent.get(event);
|
|
17054
|
+
}
|
|
17055
|
+
getRunOwnerDomain(handle: string): RunCancellationDomain | undefined {
|
|
17056
|
+
return this.agent.resourceLedger.lookupDomain(handle);
|
|
17057
|
+
}
|
|
16854
17058
|
/**
|
|
16855
17059
|
* Logical endpoint used to key owned-registration lineage bindings.
|
|
16856
17060
|
* Subagents inherit the parent's AsyncJobManager, whose completion callback
|
|
@@ -18009,8 +18213,12 @@ export class AgentSession {
|
|
|
18009
18213
|
}
|
|
18010
18214
|
|
|
18011
18215
|
options?.onMutationStarted?.();
|
|
18012
|
-
|
|
18216
|
+
const cause = options?.cause ?? "user-selection";
|
|
18217
|
+
if (cause === "user-selection") this.markUserModelSelection();
|
|
18218
|
+
this.#setModelAuthoritatively(model, cause);
|
|
18219
|
+
if (cause === "user-selection") this.#unavailableModelProfile = undefined;
|
|
18013
18220
|
this.#seedSessionCanonicalVariant(model);
|
|
18221
|
+
if (cause === "user-selection") this.#recordUserCanonicalVariantSelection();
|
|
18014
18222
|
this.sessionManager.appendModelChange(`${model.provider}/${model.id}`, role);
|
|
18015
18223
|
this.settings.setModelRole(
|
|
18016
18224
|
role,
|
|
@@ -18018,7 +18226,7 @@ export class AgentSession {
|
|
|
18018
18226
|
);
|
|
18019
18227
|
// Only an explicit user selection starts a new fallback epoch. Internal
|
|
18020
18228
|
// fallback switches must preserve the exhausted-model set while advancing.
|
|
18021
|
-
if (role === "default" &&
|
|
18229
|
+
if (role === "default" && cause === "user-selection") {
|
|
18022
18230
|
this.#fallbackTransitionGeneration++;
|
|
18023
18231
|
this.#defaultFallbackController = undefined;
|
|
18024
18232
|
this.#defaultFallbackExhaustedLastTurn = false;
|
|
@@ -18055,12 +18263,41 @@ export class AgentSession {
|
|
|
18055
18263
|
|
|
18056
18264
|
setActiveModelProfile(name: string | undefined): void {
|
|
18057
18265
|
this.#activeModelProfile = name;
|
|
18266
|
+
if (name !== undefined) {
|
|
18267
|
+
this.#unavailableModelProfile = undefined;
|
|
18268
|
+
}
|
|
18058
18269
|
}
|
|
18059
18270
|
|
|
18060
18271
|
getActiveModelProfile(): string | undefined {
|
|
18061
18272
|
return this.#activeModelProfile;
|
|
18062
18273
|
}
|
|
18063
18274
|
|
|
18275
|
+
setUnavailableModelProfile(name: string | undefined): void {
|
|
18276
|
+
this.#unavailableModelProfile = name;
|
|
18277
|
+
}
|
|
18278
|
+
|
|
18279
|
+
getUnavailableModelProfile(): string | undefined {
|
|
18280
|
+
return this.#unavailableModelProfile;
|
|
18281
|
+
}
|
|
18282
|
+
|
|
18283
|
+
/** Revision fence for deferred activation after a user model selection. */
|
|
18284
|
+
getUserModelSelectionRevision(): number {
|
|
18285
|
+
return this.#userModelSelectionRevision;
|
|
18286
|
+
}
|
|
18287
|
+
|
|
18288
|
+
/** Latest concrete user choice that seeded or cleared the session's sticky canonical variant. */
|
|
18289
|
+
getUserCanonicalVariantSelection(): { revision: number; canonicalVariant: string | undefined } {
|
|
18290
|
+
return {
|
|
18291
|
+
revision: this.#userCanonicalVariantSelectionRevision,
|
|
18292
|
+
canonicalVariant: this.#userCanonicalVariantSelection,
|
|
18293
|
+
};
|
|
18294
|
+
}
|
|
18295
|
+
|
|
18296
|
+
/** Fence deferred startup profile recovery before an explicit control-surface selection. */
|
|
18297
|
+
markUserModelSelection(): void {
|
|
18298
|
+
this.#userModelSelectionRevision++;
|
|
18299
|
+
}
|
|
18300
|
+
|
|
18064
18301
|
/**
|
|
18065
18302
|
* Re-apply vendor-separated delegation after a profile activation changed the
|
|
18066
18303
|
* role layer. `eagerTasks` is resolved at prompt build time, but under
|
|
@@ -18222,12 +18459,15 @@ export class AgentSession {
|
|
|
18222
18459
|
* Session-scoped only: does not persist `modelProfile.default`.
|
|
18223
18460
|
*/
|
|
18224
18461
|
async activateModelProfileForControl(profileName: string): Promise<boolean> {
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
|
|
18462
|
+
this.markUserModelSelection();
|
|
18463
|
+
await this.withSdkControlMutation(() =>
|
|
18464
|
+
activateModelProfile({
|
|
18465
|
+
session: this,
|
|
18466
|
+
modelRegistry: this.#modelRegistry,
|
|
18467
|
+
settings: this.settings,
|
|
18468
|
+
profileName,
|
|
18469
|
+
}),
|
|
18470
|
+
);
|
|
18231
18471
|
return this.getActiveModelProfile() === profileName;
|
|
18232
18472
|
}
|
|
18233
18473
|
|
|
@@ -18262,6 +18502,7 @@ export class AgentSession {
|
|
|
18262
18502
|
onAfterActivation?: () => void;
|
|
18263
18503
|
},
|
|
18264
18504
|
): Promise<{ changed: boolean; id: string }> {
|
|
18505
|
+
this.markUserModelSelection();
|
|
18265
18506
|
// Do not hold selection admission while waiting for a scheduled continuation:
|
|
18266
18507
|
// the continuation may need prompt admission to settle the current turn.
|
|
18267
18508
|
await this.waitForIdle();
|
|
@@ -18540,6 +18781,7 @@ export class AgentSession {
|
|
|
18540
18781
|
reason?: TemporaryModelReason;
|
|
18541
18782
|
providerSessionScope?: TemporaryProviderSessionScope;
|
|
18542
18783
|
signal?: AbortSignal;
|
|
18784
|
+
shouldMutate?: () => boolean;
|
|
18543
18785
|
onMutationStarted?: () => void;
|
|
18544
18786
|
},
|
|
18545
18787
|
// biome-ignore lint/suspicious/noConfusingVoidType: Existing session adapters return Promise<void>; a scope is optional.
|
|
@@ -18558,6 +18800,8 @@ export class AgentSession {
|
|
|
18558
18800
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
18559
18801
|
}
|
|
18560
18802
|
if (suppliedScope && this.#temporaryProviderSessionScopes.at(-1)?.token !== suppliedScope) return;
|
|
18803
|
+
if (options?.shouldMutate && !options.shouldMutate()) return;
|
|
18804
|
+
if (options?.cause === "user-selection") this.markUserModelSelection();
|
|
18561
18805
|
options?.onMutationStarted?.();
|
|
18562
18806
|
|
|
18563
18807
|
const isTemporaryOperation = options?.cause === undefined || options.cause === "temporary-operation";
|
|
@@ -18584,6 +18828,9 @@ export class AgentSession {
|
|
|
18584
18828
|
} else {
|
|
18585
18829
|
this.#setModelAuthoritatively(model, options?.cause ?? "temporary-operation");
|
|
18586
18830
|
}
|
|
18831
|
+
if (options?.cause === "user-selection") {
|
|
18832
|
+
this.#unavailableModelProfile = undefined;
|
|
18833
|
+
}
|
|
18587
18834
|
this.sessionManager.appendModelChange(
|
|
18588
18835
|
`${model.provider}/${model.id}`,
|
|
18589
18836
|
options?.persistAsSessionDefault ? "default" : "temporary",
|
|
@@ -18591,12 +18838,36 @@ export class AgentSession {
|
|
|
18591
18838
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
18592
18839
|
if (options?.persistAsSessionDefault) {
|
|
18593
18840
|
this.#seedSessionCanonicalVariant(model);
|
|
18841
|
+
if (options.cause === "user-selection") this.#recordUserCanonicalVariantSelection();
|
|
18594
18842
|
}
|
|
18595
18843
|
|
|
18596
18844
|
// Apply explicit thinking level if given; otherwise prefer the model's
|
|
18597
18845
|
// configured defaultLevel; otherwise re-clamp the current level.
|
|
18598
18846
|
this.setThinkingLevel(thinkingLevel ?? model.thinking?.defaultLevel ?? this.thinkingLevel);
|
|
18599
|
-
if (options?.persistAsSessionDefault === true
|
|
18847
|
+
if (options?.persistAsSessionDefault === true && options.cause !== "profile-activation") {
|
|
18848
|
+
// Concrete model selection clears session-scoped profile state (#5919).
|
|
18849
|
+
// Materialize durable profiles first (if persisted and active), then reset
|
|
18850
|
+
// without force to preserve durable semantics.
|
|
18851
|
+
if (this.model && this.settings.get("modelProfile.default") !== undefined) {
|
|
18852
|
+
this.materializeActiveDefaultModelProfileAssignment(this.model);
|
|
18853
|
+
}
|
|
18854
|
+
this.#resetSessionScopedModelProfileState({ preserveDefaultConfiguredChain: true });
|
|
18855
|
+
// For user-selection and startup-override causes, also clear stale persisted
|
|
18856
|
+
// defaults via the legacy path when there is no ownership record.
|
|
18857
|
+
if (options.cause === "user-selection" || options.cause === "startup-override") {
|
|
18858
|
+
const ownership = readDurableModelProfileOwnership(this.settings);
|
|
18859
|
+
if (ownership.version === 0) {
|
|
18860
|
+
this.#clearActiveModelProfileForConcreteDefault(options.cause);
|
|
18861
|
+
}
|
|
18862
|
+
}
|
|
18863
|
+
const origin = options.cause === "startup-override" ? "startup-override" : "model_selection";
|
|
18864
|
+
const effectiveLevel = thinkingLevel ?? model.thinking?.defaultLevel ?? this.thinkingLevel;
|
|
18865
|
+
this.setConfiguredModelChain(
|
|
18866
|
+
"default",
|
|
18867
|
+
[formatModelSelectorValue(`${model.provider}/${model.id}`, effectiveLevel)],
|
|
18868
|
+
origin,
|
|
18869
|
+
);
|
|
18870
|
+
}
|
|
18600
18871
|
await this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
18601
18872
|
} catch (error) {
|
|
18602
18873
|
if (ownsScope) await this.restoreTemporaryProviderSessionScope(scope);
|
|
@@ -18687,8 +18958,15 @@ export class AgentSession {
|
|
|
18687
18958
|
|
|
18688
18959
|
#publishDefaultModelSelection(model: Model, thinkingLevel: ThinkingLevel, systemPrompt: string[] | undefined): void {
|
|
18689
18960
|
this.#clearActiveRetryFallback();
|
|
18961
|
+
// Concrete model selection clears session-scoped profile state (#5919).
|
|
18962
|
+
// IMPORTANT: This is called AFTER durable persistence completes, so the
|
|
18963
|
+
// session-scoped updates occur during the promotion phase after ownership
|
|
18964
|
+
// is committed. Session ownership is captured in the promotion logic above.
|
|
18965
|
+
this.#resetSessionScopedModelProfileState({ preserveDefaultConfiguredChain: true });
|
|
18966
|
+
this.markUserModelSelection();
|
|
18690
18967
|
this.#setModelWithProviderSessionReset(model);
|
|
18691
18968
|
this.#seedSessionCanonicalVariant(model);
|
|
18969
|
+
this.#recordUserCanonicalVariantSelection();
|
|
18692
18970
|
const thinkingLevelChanged = this.#thinkingLevel !== thinkingLevel;
|
|
18693
18971
|
this.#thinkingLevelMutationRevision++;
|
|
18694
18972
|
this.#thinkingLevelLiveMutationRevision++;
|
|
@@ -18698,6 +18976,12 @@ export class AgentSession {
|
|
|
18698
18976
|
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
18699
18977
|
this.#thinkingLevel = thinkingLevel;
|
|
18700
18978
|
this.agent.setThinkingLevel(toReasoningEffort(thinkingLevel));
|
|
18979
|
+
// Materialize durable profiles after model and thinking level installation
|
|
18980
|
+
// so the persisted default selector matches both the newly installed model
|
|
18981
|
+
// and the selected thinking level.
|
|
18982
|
+
if (this.model && this.settings.get("modelProfile.default") !== undefined) {
|
|
18983
|
+
this.materializeActiveDefaultModelProfileAssignment(this.model);
|
|
18984
|
+
}
|
|
18701
18985
|
if (thinkingLevelChanged) {
|
|
18702
18986
|
const event: AgentSessionEvent = { type: "thinking_level_changed", thinkingLevel };
|
|
18703
18987
|
for (const listener of this.#eventListenerSnapshot) {
|
|
@@ -18883,7 +19167,40 @@ export class AgentSession {
|
|
|
18883
19167
|
});
|
|
18884
19168
|
}
|
|
18885
19169
|
}
|
|
18886
|
-
|
|
19170
|
+
// Concrete model selection clears durable profile ownership (#5919).
|
|
19171
|
+
// If there is an existing ownership record (version > 0), clear it.
|
|
19172
|
+
// If there is no ownership record (version === 0), call the legacy path to
|
|
19173
|
+
// clear stale persisted profiles that no longer match the selection.
|
|
19174
|
+
// The ownership clear is attempted atomically with other model role updates.
|
|
19175
|
+
// If it fails due to concurrent access, we log and continue: the model
|
|
19176
|
+
// selection has been promoted and committed, so the concrete pick is durable.
|
|
19177
|
+
// A stale profile ownership marker will be resolved on the next session startup.
|
|
19178
|
+
const currentOwnership = readDurableModelProfileOwnership(this.settings);
|
|
19179
|
+
if (currentOwnership.version > 0) {
|
|
19180
|
+
try {
|
|
19181
|
+
await commitDurableModelProfileOwnership(this.settings, { kind: "cleared" });
|
|
19182
|
+
} catch (error) {
|
|
19183
|
+
if (error instanceof ModelProfileOwnershipConflictError) {
|
|
19184
|
+
logger.info("Model selection ownership clear deferred due to concurrent access", {
|
|
19185
|
+
code: "default_model_selection_ownership_conflict",
|
|
19186
|
+
disposition: "continue",
|
|
19187
|
+
expectedVersion: error.expectedVersion,
|
|
19188
|
+
actualVersion: error.actualVersion,
|
|
19189
|
+
});
|
|
19190
|
+
} else if (error instanceof InvalidModelProfileOwnershipError) {
|
|
19191
|
+
logger.warn("Invalid model profile ownership marker during selection", {
|
|
19192
|
+
code: "default_model_selection_ownership_invalid",
|
|
19193
|
+
disposition: "continue",
|
|
19194
|
+
});
|
|
19195
|
+
} else {
|
|
19196
|
+
throw error;
|
|
19197
|
+
}
|
|
19198
|
+
}
|
|
19199
|
+
} else {
|
|
19200
|
+
// Legacy path: no ownership record exists. Clear stale persisted defaults
|
|
19201
|
+
// that no longer match the concrete selection.
|
|
19202
|
+
this.#clearActiveModelProfileForConcreteDefault("user-selection");
|
|
19203
|
+
}
|
|
18887
19204
|
options?.onAfterMutation?.();
|
|
18888
19205
|
return { provider: model.provider, modelId: model.id, thinkingLevel: effectiveLevel };
|
|
18889
19206
|
},
|
|
@@ -18989,6 +19306,7 @@ export class AgentSession {
|
|
|
18989
19306
|
const next = roleModels[nextIndex];
|
|
18990
19307
|
|
|
18991
19308
|
if (options?.temporary) {
|
|
19309
|
+
this.markUserModelSelection();
|
|
18992
19310
|
await this.setModelTemporary(next.model, next.explicitThinkingLevel ? next.thinkingLevel : undefined, {
|
|
18993
19311
|
cause: "temporary-operation",
|
|
18994
19312
|
reason: "temporary-cycle",
|
|
@@ -20638,6 +20956,7 @@ export class AgentSession {
|
|
|
20638
20956
|
onTerminalOverflowNoop?: () => void,
|
|
20639
20957
|
resourceRunId?: string,
|
|
20640
20958
|
ownershipSignal?: AbortSignal,
|
|
20959
|
+
sdkOwnership?: SdkContinuationOwnership,
|
|
20641
20960
|
): Promise<boolean> {
|
|
20642
20961
|
// Safety stops are terminal and must not trigger context maintenance.
|
|
20643
20962
|
if (
|
|
@@ -20692,6 +21011,7 @@ export class AgentSession {
|
|
|
20692
21011
|
generation,
|
|
20693
21012
|
suppressPredecessorAgentEnd: true,
|
|
20694
21013
|
resourceRunId,
|
|
21014
|
+
sdkOwnership,
|
|
20695
21015
|
});
|
|
20696
21016
|
|
|
20697
21017
|
return true;
|
|
@@ -20710,10 +21030,11 @@ export class AgentSession {
|
|
|
20710
21030
|
},
|
|
20711
21031
|
resourceRunId,
|
|
20712
21032
|
signal: ownershipSignal,
|
|
21033
|
+
sdkOwnership,
|
|
20713
21034
|
});
|
|
20714
21035
|
return "continuationScheduled" in status && status.continuationScheduled === true;
|
|
20715
21036
|
}
|
|
20716
|
-
return await this.#scheduleOverflowRetryContinuation(generation, resourceRunId);
|
|
21037
|
+
return await this.#scheduleOverflowRetryContinuation(generation, resourceRunId, sdkOwnership);
|
|
20717
21038
|
}
|
|
20718
21039
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
20719
21040
|
if (!compactionSettings.enabled || compactionSettings.strategy === "off") return false;
|
|
@@ -20759,7 +21080,11 @@ export class AgentSession {
|
|
|
20759
21080
|
const promoted = await this.#tryContextPromotion(assistantMessage, ownershipSignal);
|
|
20760
21081
|
if (ownershipSignal?.aborted) return false;
|
|
20761
21082
|
if (!promoted) {
|
|
20762
|
-
await this.#runAutoCompaction("threshold", false, false, {
|
|
21083
|
+
await this.#runAutoCompaction("threshold", false, false, {
|
|
21084
|
+
resourceRunId,
|
|
21085
|
+
signal: ownershipSignal,
|
|
21086
|
+
sdkOwnership,
|
|
21087
|
+
});
|
|
20763
21088
|
}
|
|
20764
21089
|
}
|
|
20765
21090
|
return true;
|
|
@@ -21113,10 +21438,7 @@ export class AgentSession {
|
|
|
21113
21438
|
const isContextPromoted = this.#temporaryProviderSessionScopes.some(
|
|
21114
21439
|
scope => scope.token.reason === "context-promotion",
|
|
21115
21440
|
);
|
|
21116
|
-
if (
|
|
21117
|
-
!isContextPromoted ||
|
|
21118
|
-
!isDefaultAutoThresholdCeilingApplied(contextWindow, settings)
|
|
21119
|
-
) {
|
|
21441
|
+
if (!isContextPromoted || !isDefaultAutoThresholdCeilingApplied(contextWindow, settings)) {
|
|
21120
21442
|
return settings;
|
|
21121
21443
|
}
|
|
21122
21444
|
|
|
@@ -21525,7 +21847,9 @@ export class AgentSession {
|
|
|
21525
21847
|
// The reminder continues the current prompt, so the predecessor `agent_end`
|
|
21526
21848
|
// must stay held until the continuation turn produces the real terminal.
|
|
21527
21849
|
// Publishing it here would settle the caller's prompt mid-reminder.
|
|
21528
|
-
|
|
21850
|
+
// Disable managed fallback to prevent indefinite retries for this server-initiated turn.
|
|
21851
|
+
this.#todoReminderContinuationGeneration = this.#promptGeneration;
|
|
21852
|
+
this.#scheduleAgentContinue({ skipCompactionCheck: true, suppressPredecessorAgentEnd: true, disableManagedFallback: true });
|
|
21529
21853
|
}
|
|
21530
21854
|
|
|
21531
21855
|
/**
|
|
@@ -21639,6 +21963,11 @@ export class AgentSession {
|
|
|
21639
21963
|
}
|
|
21640
21964
|
}
|
|
21641
21965
|
|
|
21966
|
+
#recordUserCanonicalVariantSelection(): void {
|
|
21967
|
+
this.#userCanonicalVariantSelectionRevision++;
|
|
21968
|
+
this.#userCanonicalVariantSelection = this.#modelRegistry.getSessionCanonicalVariant?.(this.sessionId);
|
|
21969
|
+
}
|
|
21970
|
+
|
|
21642
21971
|
#closeCodexProviderSessionsForHistoryRewrite(): void {
|
|
21643
21972
|
const currentModel = this.model;
|
|
21644
21973
|
if (currentModel?.api !== "openai-codex-responses") return;
|
|
@@ -22242,6 +22571,7 @@ export class AgentSession {
|
|
|
22242
22571
|
signal?: AbortSignal;
|
|
22243
22572
|
beforeTerminalOverflowNoop?: () => void;
|
|
22244
22573
|
resourceRunId?: string;
|
|
22574
|
+
sdkOwnership?: SdkContinuationOwnership;
|
|
22245
22575
|
},
|
|
22246
22576
|
): Promise<AutoCompactionTerminalStatus> {
|
|
22247
22577
|
if (this.#isDisposed || this.#sessionAdmissionClosing) return Promise.resolve({ kind: "skipped" });
|
|
@@ -22262,6 +22592,7 @@ export class AgentSession {
|
|
|
22262
22592
|
signal?: AbortSignal;
|
|
22263
22593
|
beforeTerminalOverflowNoop?: () => void;
|
|
22264
22594
|
resourceRunId?: string;
|
|
22595
|
+
sdkOwnership?: SdkContinuationOwnership;
|
|
22265
22596
|
},
|
|
22266
22597
|
): Promise<AutoCompactionTerminalStatus> {
|
|
22267
22598
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
@@ -22404,7 +22735,14 @@ export class AgentSession {
|
|
|
22404
22735
|
});
|
|
22405
22736
|
if (autoCompactionSignal.aborted) return { kind: "aborted", source: "signal" };
|
|
22406
22737
|
if (continueAfterMaintenance && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
22407
|
-
this.#scheduleAutoContinuePrompt(
|
|
22738
|
+
this.#scheduleAutoContinuePrompt(
|
|
22739
|
+
generation,
|
|
22740
|
+
true,
|
|
22741
|
+
options?.resourceRunId,
|
|
22742
|
+
undefined,
|
|
22743
|
+
undefined,
|
|
22744
|
+
options?.sdkOwnership,
|
|
22745
|
+
);
|
|
22408
22746
|
}
|
|
22409
22747
|
|
|
22410
22748
|
if (autoCompactionSignal.aborted) return { kind: "aborted", source: "signal" };
|
|
@@ -22459,7 +22797,11 @@ export class AgentSession {
|
|
|
22459
22797
|
}
|
|
22460
22798
|
const overflowContinuationScheduled =
|
|
22461
22799
|
!overflowNoopWouldReplay && willRetry && !continuationSkipReason
|
|
22462
|
-
? await this.#scheduleOverflowRetryContinuation(
|
|
22800
|
+
? await this.#scheduleOverflowRetryContinuation(
|
|
22801
|
+
generation,
|
|
22802
|
+
options?.resourceRunId,
|
|
22803
|
+
options?.sdkOwnership,
|
|
22804
|
+
)
|
|
22463
22805
|
: false;
|
|
22464
22806
|
await this.#emitSessionEvent({
|
|
22465
22807
|
type: "auto_compaction_end",
|
|
@@ -22509,7 +22851,14 @@ export class AgentSession {
|
|
|
22509
22851
|
resourceRunId: options?.resourceRunId,
|
|
22510
22852
|
});
|
|
22511
22853
|
} else if (continueAfterMaintenance && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
22512
|
-
this.#scheduleAutoContinuePrompt(
|
|
22854
|
+
this.#scheduleAutoContinuePrompt(
|
|
22855
|
+
generation,
|
|
22856
|
+
true,
|
|
22857
|
+
options?.resourceRunId,
|
|
22858
|
+
undefined,
|
|
22859
|
+
undefined,
|
|
22860
|
+
options?.sdkOwnership,
|
|
22861
|
+
);
|
|
22513
22862
|
}
|
|
22514
22863
|
return { kind: "skipped" };
|
|
22515
22864
|
}
|
|
@@ -22794,7 +23143,11 @@ export class AgentSession {
|
|
|
22794
23143
|
}
|
|
22795
23144
|
const overflowContinuationScheduled =
|
|
22796
23145
|
willRetry && !continuationSkipReason
|
|
22797
|
-
? await this.#scheduleOverflowRetryContinuation(
|
|
23146
|
+
? await this.#scheduleOverflowRetryContinuation(
|
|
23147
|
+
generation,
|
|
23148
|
+
options?.resourceRunId,
|
|
23149
|
+
options?.sdkOwnership,
|
|
23150
|
+
)
|
|
22798
23151
|
: false;
|
|
22799
23152
|
|
|
22800
23153
|
await this.#emitSessionEvent({
|
|
@@ -22828,7 +23181,14 @@ export class AgentSession {
|
|
|
22828
23181
|
resourceRunId: options?.resourceRunId,
|
|
22829
23182
|
});
|
|
22830
23183
|
} else if (continueAfterMaintenance && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
22831
|
-
this.#scheduleAutoContinuePrompt(
|
|
23184
|
+
this.#scheduleAutoContinuePrompt(
|
|
23185
|
+
generation,
|
|
23186
|
+
true,
|
|
23187
|
+
options?.resourceRunId,
|
|
23188
|
+
undefined,
|
|
23189
|
+
undefined,
|
|
23190
|
+
options?.sdkOwnership,
|
|
23191
|
+
);
|
|
22832
23192
|
}
|
|
22833
23193
|
return { kind: "compacted" };
|
|
22834
23194
|
} catch (error) {
|
|
@@ -23021,7 +23381,8 @@ export class AgentSession {
|
|
|
23021
23381
|
isAuthenticated(apiKey) &&
|
|
23022
23382
|
authStorage.getSessionCredentialRowId(model.provider, this.credentialSessionId) === credential.id &&
|
|
23023
23383
|
authStorage.getSessionCredentialType(model.provider, this.credentialSessionId) === credentialKind &&
|
|
23024
|
-
currentCredential &&
|
|
23384
|
+
currentCredential &&
|
|
23385
|
+
!currentCredential.disabled
|
|
23025
23386
|
)
|
|
23026
23387
|
return apiKey;
|
|
23027
23388
|
}
|
|
@@ -23191,6 +23552,7 @@ export class AgentSession {
|
|
|
23191
23552
|
if (message.errorKind === "local_snapshot_failure") return "local_snapshot";
|
|
23192
23553
|
if (message.errorKind === "local_buffer_overflow") return "local_buffer_overflow";
|
|
23193
23554
|
if (this.#isTypedFirstEventTimeout(message)) return "first_event_timeout";
|
|
23555
|
+
if (message.errorKind === "local_empty_response") return "empty_response";
|
|
23194
23556
|
if (this.#isTypedEmptyResponse(message)) return "empty_response";
|
|
23195
23557
|
if (this.#isCodexCredentialModelUnavailable(message)) {
|
|
23196
23558
|
return this.#canRotateCodexCredential(message) ? "unknown" : "terminal";
|
|
@@ -23445,6 +23807,7 @@ export class AgentSession {
|
|
|
23445
23807
|
}
|
|
23446
23808
|
|
|
23447
23809
|
async #handleManagedAttemptOutcome(outcome: ManagedAttemptOutcome): Promise<ManagedAttemptDecision> {
|
|
23810
|
+
const sdkOwnership = this.#captureSdkContinuationOwnership(outcome.scope);
|
|
23448
23811
|
const attemptAbortEpoch = this.#abortAdmissionEpoch;
|
|
23449
23812
|
const attemptCancelled = () =>
|
|
23450
23813
|
this.#isDisposed || this.#sessionAdmissionClosing || this.#abortAdmissionEpoch !== attemptAbortEpoch;
|
|
@@ -23494,6 +23857,8 @@ export class AgentSession {
|
|
|
23494
23857
|
return {
|
|
23495
23858
|
type: "retry",
|
|
23496
23859
|
continuation: async ownership => {
|
|
23860
|
+
const continuationSdkOwnership =
|
|
23861
|
+
sdkOwnership ?? this.#captureSdkContinuationOwnership(ownership.handle.scope);
|
|
23497
23862
|
const restoreOwnedTransition = async () => {
|
|
23498
23863
|
if (this.#fallbackTransitionGeneration !== transitionGeneration) return;
|
|
23499
23864
|
this.#fallbackTransitionGeneration++;
|
|
@@ -23580,7 +23945,12 @@ export class AgentSession {
|
|
|
23580
23945
|
...this.#managedFallbackPromptOptions(),
|
|
23581
23946
|
transientRecoveryMessage: this.#escapedNonAsciiRecoveryMessage(),
|
|
23582
23947
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
23583
|
-
this.#acceptSdkAttemptRun(
|
|
23948
|
+
this.#acceptSdkAttemptRun(
|
|
23949
|
+
handle,
|
|
23950
|
+
continuationSdkOwnership?.token,
|
|
23951
|
+
continuationSdkOwnership?.cohort,
|
|
23952
|
+
continuationSdkOwnership?.scope,
|
|
23953
|
+
);
|
|
23584
23954
|
},
|
|
23585
23955
|
});
|
|
23586
23956
|
if (
|
|
@@ -23619,11 +23989,18 @@ export class AgentSession {
|
|
|
23619
23989
|
type: "retry",
|
|
23620
23990
|
continuation: async ownership => {
|
|
23621
23991
|
if (attemptCancelled() || !ownership.isCurrent() || ownership.lease.signal.aborted) return;
|
|
23992
|
+
const continuationSdkOwnership =
|
|
23993
|
+
sdkOwnership ?? this.#captureSdkContinuationOwnership(ownership.handle.scope);
|
|
23622
23994
|
await this.agent.continue({
|
|
23623
23995
|
...this.#managedFallbackPromptOptions(),
|
|
23624
23996
|
transientRecoveryMessage: this.#escapedNonAsciiRecoveryMessage(),
|
|
23625
23997
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
23626
|
-
this.#acceptSdkAttemptRun(
|
|
23998
|
+
this.#acceptSdkAttemptRun(
|
|
23999
|
+
handle,
|
|
24000
|
+
continuationSdkOwnership?.token,
|
|
24001
|
+
continuationSdkOwnership?.cohort,
|
|
24002
|
+
continuationSdkOwnership?.scope,
|
|
24003
|
+
);
|
|
23627
24004
|
},
|
|
23628
24005
|
});
|
|
23629
24006
|
},
|
|
@@ -23638,6 +24015,10 @@ export class AgentSession {
|
|
|
23638
24015
|
type: "maintenance",
|
|
23639
24016
|
continuation: async ownership => {
|
|
23640
24017
|
if (attemptCancelled() || !ownership.isCurrent() || ownership.lease.signal.aborted) return;
|
|
24018
|
+
// A discarded in-loop attempt need not have published turn_start;
|
|
24019
|
+
// the Agent-supplied handle still identifies its accepted SDK cohort.
|
|
24020
|
+
const continuationSdkOwnership =
|
|
24021
|
+
sdkOwnership ?? this.#captureSdkContinuationOwnership(ownership.handle.scope);
|
|
23641
24022
|
const resourceRunId = String(ownership.logicalRunId);
|
|
23642
24023
|
const previousLease = this.#postPromptLeases.get(resourceRunId);
|
|
23643
24024
|
this.#postPromptLeases.set(resourceRunId, ownership.lease);
|
|
@@ -23655,6 +24036,7 @@ export class AgentSession {
|
|
|
23655
24036
|
},
|
|
23656
24037
|
resourceRunId,
|
|
23657
24038
|
ownership.lease.signal,
|
|
24039
|
+
continuationSdkOwnership,
|
|
23658
24040
|
);
|
|
23659
24041
|
if (
|
|
23660
24042
|
terminalized ||
|
|
@@ -23689,12 +24071,14 @@ export class AgentSession {
|
|
|
23689
24071
|
},
|
|
23690
24072
|
};
|
|
23691
24073
|
}
|
|
23692
|
-
//
|
|
23693
|
-
//
|
|
23694
|
-
//
|
|
23695
|
-
//
|
|
23696
|
-
|
|
23697
|
-
|
|
24074
|
+
// Content-free typed statusless overloads are safe for managed retry when
|
|
24075
|
+
// this discarded attempt is clean, even after earlier committed work in
|
|
24076
|
+
// the same prompt. Preserve the committed failure path when this attempt
|
|
24077
|
+
// itself is not replay-safe.
|
|
24078
|
+
if (
|
|
24079
|
+
isStatuslessTypedOverloadFacts(outcome.failure.transportFailure) &&
|
|
24080
|
+
!this.#isRetryScopeClean(outcome.scope)
|
|
24081
|
+
) {
|
|
23698
24082
|
this.#defaultFallbackChain().resetAttemptBudget();
|
|
23699
24083
|
return this.#managedFallbackExhaustionDecision(
|
|
23700
24084
|
outcome.failure.message,
|
|
@@ -23759,6 +24143,10 @@ export class AgentSession {
|
|
|
23759
24143
|
if (classification === "transient" || classification === "first_event_timeout") {
|
|
23760
24144
|
return { class: "server" };
|
|
23761
24145
|
}
|
|
24146
|
+
// A locally promoted empty response is retryable only when the managed
|
|
24147
|
+
// attempt stayed clean; authorize the configured fallback chain without
|
|
24148
|
+
// fabricating provider transport facts for the runtime-owned failure.
|
|
24149
|
+
if (classification === "empty_response") return { class: "server" };
|
|
23762
24150
|
if (classification === "unknown") return { class: "unknown" };
|
|
23763
24151
|
return undefined;
|
|
23764
24152
|
}
|
|
@@ -23938,9 +24326,7 @@ export class AgentSession {
|
|
|
23938
24326
|
!authStorage.isCredentialAvailable(resolvedModel.provider, credential.id)
|
|
23939
24327
|
)
|
|
23940
24328
|
continue;
|
|
23941
|
-
let credentialKeyResult:
|
|
23942
|
-
| { kind: "resolved"; value: string | undefined }
|
|
23943
|
-
| { kind: "aborted" };
|
|
24329
|
+
let credentialKeyResult: { kind: "resolved"; value: string | undefined } | { kind: "aborted" };
|
|
23944
24330
|
try {
|
|
23945
24331
|
credentialKeyResult = await awaitWithCancellation(
|
|
23946
24332
|
this.#modelRegistry.getApiKey(resolvedModel, this.credentialSessionId, {
|
|
@@ -24038,7 +24424,11 @@ export class AgentSession {
|
|
|
24038
24424
|
!transitionStillOwned()
|
|
24039
24425
|
)
|
|
24040
24426
|
return await rollbackCancelled();
|
|
24041
|
-
if (
|
|
24427
|
+
if (
|
|
24428
|
+
selectedCredentialRowId !== undefined &&
|
|
24429
|
+
selectedCredentialKind !== undefined &&
|
|
24430
|
+
this.#restoreManagedFallbackGetApiKey
|
|
24431
|
+
) {
|
|
24042
24432
|
this.#managedFallbackNextCredentialOverride = {
|
|
24043
24433
|
modelKey: canonicalModelKey,
|
|
24044
24434
|
storageProvider: resolveOAuthStorageProvider(resolvedModel.provider),
|
|
@@ -24332,6 +24722,10 @@ export class AgentSession {
|
|
|
24332
24722
|
scope?: AttemptScope,
|
|
24333
24723
|
scopeWasClean = this.#isRetryScopeClean(scope),
|
|
24334
24724
|
): Promise<boolean | ManagedAttemptDecision> {
|
|
24725
|
+
// Capture the SDK owner before the failed attempt retires its active scope.
|
|
24726
|
+
// Retry/continuation terminals must remain attributed to the prompt that
|
|
24727
|
+
// admitted the provider call, even when the retry starts after agent_end.
|
|
24728
|
+
const retrySdkOwnership = this.#captureSdkContinuationOwnership(scope);
|
|
24335
24729
|
const retryAbortEpoch = this.#abortAdmissionEpoch;
|
|
24336
24730
|
const retryCancelled = () =>
|
|
24337
24731
|
this.#isDisposed || this.#sessionAdmissionClosing || this.#abortAdmissionEpoch !== retryAbortEpoch;
|
|
@@ -24349,13 +24743,22 @@ export class AgentSession {
|
|
|
24349
24743
|
// Managed outcomes can reach retry handling without the ordinary admission
|
|
24350
24744
|
// check. Do not let retained transport facts bypass the same terminal veto.
|
|
24351
24745
|
if (isTerminalCodexProviderFailure(message)) {
|
|
24352
|
-
return managedOutcome
|
|
24353
|
-
? { type: "terminal", terminal: { stopReason: "error", messages: [message] } }
|
|
24354
|
-
: false;
|
|
24746
|
+
return managedOutcome ? { type: "terminal", terminal: { stopReason: "error", messages: [message] } } : false;
|
|
24355
24747
|
}
|
|
24356
24748
|
if (retryCancelled()) {
|
|
24357
24749
|
return managedOutcome ? { type: "terminal", terminal: { stopReason: "cancelled" } } : false;
|
|
24358
24750
|
}
|
|
24751
|
+
if (
|
|
24752
|
+
transportFailure?.status === undefined &&
|
|
24753
|
+
transportFailure?.providerCode === SERVER_OVERLOADED_PROVIDER_CODE &&
|
|
24754
|
+
(transportFailure.openaiErrorCode === undefined ||
|
|
24755
|
+
transportFailure.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE) &&
|
|
24756
|
+
(managedOutcome ? !scopeWasClean : !this.#hasCleanRetryReplaySafety)
|
|
24757
|
+
) {
|
|
24758
|
+
return managedOutcome
|
|
24759
|
+
? { type: "terminal", terminal: { stopReason: "error", messages: [message] } }
|
|
24760
|
+
: false;
|
|
24761
|
+
}
|
|
24359
24762
|
// A local machinery failure must never stay charged against the provider
|
|
24360
24763
|
// fallback budget, no matter which local exit follows (disabled retry,
|
|
24361
24764
|
// visible-content surface, bounded retry, exhaustion, or the immediate
|
|
@@ -24462,21 +24865,6 @@ export class AgentSession {
|
|
|
24462
24865
|
| undefined = canReplayCodexProviderOverload || canReplayBareDefaultCodexFailure
|
|
24463
24866
|
? { class: "server" }
|
|
24464
24867
|
: fallbackTrigger;
|
|
24465
|
-
// OpenAI's typed statusless capacity-overload code (issue #5018) must not
|
|
24466
|
-
// gain managed-chain retry/advance authority from its new facts. Before
|
|
24467
|
-
// the code survived transport, this failure reached the session as an
|
|
24468
|
-
// ordinary committed error and surfaced immediately, so mirror that
|
|
24469
|
-
// behavior with the existing exhaustion decision. However, when called from
|
|
24470
|
-
// the agent_end path (managedOutcome=false), we must return false to allow
|
|
24471
|
-
// proper session termination handling.
|
|
24472
|
-
if (managedFallback && isStatuslessTypedOverloadFacts(transportFailure)) {
|
|
24473
|
-
return managedOutcome
|
|
24474
|
-
? this.#managedFallbackExhaustionDecision(
|
|
24475
|
-
message,
|
|
24476
|
-
message.errorMessage || "Model fallback attempt failed",
|
|
24477
|
-
)
|
|
24478
|
-
: false;
|
|
24479
|
-
}
|
|
24480
24868
|
if (!trigger) {
|
|
24481
24869
|
return managedOutcome
|
|
24482
24870
|
? this.#managedFallbackExhaustionDecision(message, message.errorMessage || "Model fallback attempt failed")
|
|
@@ -24689,10 +25077,7 @@ export class AgentSession {
|
|
|
24689
25077
|
if (outcome === "exhausted") {
|
|
24690
25078
|
if (managedFallback) {
|
|
24691
25079
|
let errorMessage = this.#fallbackExhaustionError(controller);
|
|
24692
|
-
if (
|
|
24693
|
-
!providerRetryCeilingReached &&
|
|
24694
|
-
(trigger.class === "quota" || trigger.class === "rate_limit")
|
|
24695
|
-
) {
|
|
25080
|
+
if (!providerRetryCeilingReached && (trigger.class === "quota" || trigger.class === "rate_limit")) {
|
|
24696
25081
|
if (!assistantMessageHasVisibleOrToolContent(message)) {
|
|
24697
25082
|
const mark =
|
|
24698
25083
|
quotaCredentialMark ??
|
|
@@ -24737,6 +25122,8 @@ export class AgentSession {
|
|
|
24737
25122
|
: cappedExponentialWithFullJitter(retrySettings.baseDelayMs, retrySettings.maxDelayMs, attemptsUsed);
|
|
24738
25123
|
|
|
24739
25124
|
const retry = async (ownership?: ManagedAttemptContinuationOwnership): Promise<void> => {
|
|
25125
|
+
const continuationSdkOwnership =
|
|
25126
|
+
retrySdkOwnership ?? this.#captureSdkContinuationOwnership(ownership?.handle.scope);
|
|
24740
25127
|
const activePromptHandle = this.activePromptHandle;
|
|
24741
25128
|
const cancellationSignal =
|
|
24742
25129
|
ownership?.domain.signal ??
|
|
@@ -24951,7 +25338,12 @@ export class AgentSession {
|
|
|
24951
25338
|
await this.agent.continue({
|
|
24952
25339
|
...this.#managedFallbackPromptOptions(),
|
|
24953
25340
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
24954
|
-
this.#acceptSdkAttemptRun(
|
|
25341
|
+
this.#acceptSdkAttemptRun(
|
|
25342
|
+
handle,
|
|
25343
|
+
continuationSdkOwnership?.token,
|
|
25344
|
+
continuationSdkOwnership?.cohort,
|
|
25345
|
+
continuationSdkOwnership?.scope,
|
|
25346
|
+
);
|
|
24955
25347
|
},
|
|
24956
25348
|
});
|
|
24957
25349
|
return;
|
|
@@ -24979,6 +25371,7 @@ export class AgentSession {
|
|
|
24979
25371
|
allowDuringCancelAndSubmit: true,
|
|
24980
25372
|
suppressPredecessorAgentEnd: resourceRunId !== undefined,
|
|
24981
25373
|
resourceRunId,
|
|
25374
|
+
sdkOwnership: continuationSdkOwnership,
|
|
24982
25375
|
onError: () => this.#failRetryRecovery("Retry continuation failed to start"),
|
|
24983
25376
|
onSkip: () => this.#failRetryRecovery("Retry continuation was superseded"),
|
|
24984
25377
|
});
|
|
@@ -25206,10 +25599,6 @@ export class AgentSession {
|
|
|
25206
25599
|
// Bash Execution
|
|
25207
25600
|
// =========================================================================
|
|
25208
25601
|
|
|
25209
|
-
async #saveBashOriginalArtifact(originalText: string): Promise<BashArtifactSaveResult> {
|
|
25210
|
-
return saveAgentBashOriginalArtifact(this.sessionManager, originalText);
|
|
25211
|
-
}
|
|
25212
|
-
|
|
25213
25602
|
/**
|
|
25214
25603
|
* Execute a bash command.
|
|
25215
25604
|
* Adds result to agent context and session.
|
|
@@ -25224,6 +25613,7 @@ export class AgentSession {
|
|
|
25224
25613
|
onChunk?: (chunk: string) => void,
|
|
25225
25614
|
options?: { excludeFromContext?: boolean; onPersisted?: () => void },
|
|
25226
25615
|
): Promise<BashResult> {
|
|
25616
|
+
const publishArtifact = this.sessionManager.captureArtifactPublication();
|
|
25227
25617
|
const excludeFromContext = options?.excludeFromContext === true;
|
|
25228
25618
|
this.#markRetryReplayUnsafe();
|
|
25229
25619
|
|
|
@@ -25261,7 +25651,7 @@ export class AgentSession {
|
|
|
25261
25651
|
sessionId: this.sessionId,
|
|
25262
25652
|
cwd,
|
|
25263
25653
|
}),
|
|
25264
|
-
onMinimizedSave: originalText =>
|
|
25654
|
+
onMinimizedSave: originalText => saveAgentBashOriginalArtifact(publishArtifact, originalText),
|
|
25265
25655
|
});
|
|
25266
25656
|
|
|
25267
25657
|
this.recordBashResult(command, result, options);
|
|
@@ -25365,9 +25755,18 @@ export class AgentSession {
|
|
|
25365
25755
|
this.#markRetryReplayUnsafe();
|
|
25366
25756
|
const cwd = this.sessionManager.getCwd();
|
|
25367
25757
|
this.assertEvalExecutionAllowed();
|
|
25758
|
+
const sessionFile = this.sessionManager.getSessionFile();
|
|
25759
|
+
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${cwd}` : `cwd:${cwd}`;
|
|
25760
|
+
const kernelOwnerId = this.#evalKernelOwnerId;
|
|
25761
|
+
const kernelMode = this.settings.get("python.kernelMode");
|
|
25762
|
+
const settings = this.settings;
|
|
25368
25763
|
|
|
25369
25764
|
const abortController = new AbortController();
|
|
25370
|
-
const execution = (async (): Promise<PythonResult> => {
|
|
25765
|
+
const execution = Promise.resolve().then(async (): Promise<PythonResult> => {
|
|
25766
|
+
if (abortController.signal.aborted) {
|
|
25767
|
+
throw abortController.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
25768
|
+
}
|
|
25769
|
+
this.assertEvalExecutionAllowed();
|
|
25371
25770
|
if (this.#extensionRunner?.hasHandlers("user_python")) {
|
|
25372
25771
|
const hookResult = await this.#extensionRunner.emitUserPython({
|
|
25373
25772
|
type: "user_python",
|
|
@@ -25375,6 +25774,9 @@ export class AgentSession {
|
|
|
25375
25774
|
excludeFromContext,
|
|
25376
25775
|
cwd,
|
|
25377
25776
|
});
|
|
25777
|
+
if (abortController.signal.aborted) {
|
|
25778
|
+
throw abortController.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
25779
|
+
}
|
|
25378
25780
|
this.assertEvalExecutionAllowed();
|
|
25379
25781
|
if (hookResult?.result) {
|
|
25380
25782
|
this.recordPythonResult(code, hookResult.result, options);
|
|
@@ -25382,15 +25784,12 @@ export class AgentSession {
|
|
|
25382
25784
|
}
|
|
25383
25785
|
}
|
|
25384
25786
|
|
|
25385
|
-
// Use the same session ID as eval's Python backend for kernel sharing
|
|
25386
|
-
const sessionFile = this.sessionManager.getSessionFile();
|
|
25387
|
-
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${cwd}` : `cwd:${cwd}`;
|
|
25388
25787
|
const result = await executePythonCommand(code, {
|
|
25389
25788
|
cwd,
|
|
25390
25789
|
sessionId,
|
|
25391
|
-
kernelOwnerId
|
|
25392
|
-
kernelMode
|
|
25393
|
-
settings
|
|
25790
|
+
kernelOwnerId,
|
|
25791
|
+
kernelMode,
|
|
25792
|
+
settings,
|
|
25394
25793
|
onChunk,
|
|
25395
25794
|
signal: abortController.signal,
|
|
25396
25795
|
});
|
|
@@ -25399,7 +25798,7 @@ export class AgentSession {
|
|
|
25399
25798
|
// must not reopen a closing session to append history.
|
|
25400
25799
|
if (!this.#evalExecutionDisposing) this.recordPythonResult(code, result, options);
|
|
25401
25800
|
return result;
|
|
25402
|
-
})
|
|
25801
|
+
});
|
|
25403
25802
|
return await this.trackEvalExecution(execution, abortController);
|
|
25404
25803
|
}
|
|
25405
25804
|
|