@gajae-code/coding-agent 0.18.7 → 0.18.9
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 +377 -1
- package/dist/types/async/endpoint-id.d.ts +16 -0
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/async/support.d.ts +0 -14
- 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/commands/mcp-serve.d.ts +4 -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/discovery/mcp-json.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +18 -0
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/html-export-destination.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
- 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 +4 -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/psmux-detect.d.ts +3 -3
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
- 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 +14 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
- 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/config.d.ts +8 -5
- 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/manager.d.ts +2 -1
- package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +27 -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 +24 -2
- package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
- package/dist/types/sdk/broker/ensure.d.ts +14 -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 +69 -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/skill-state/workflow-mutation-guard.d.ts +3 -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/task/isolation-dir.d.ts +26 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/browser/cdp-url.d.ts +2 -0
- package/dist/types/tools/github-side-effect.d.ts +22 -0
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/monitor-mutation.d.ts +9 -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/endpoint-id.ts +26 -0
- package/src/async/job-manager.ts +247 -132
- package/src/async/support.ts +0 -25
- package/src/capability/index.ts +15 -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/doctor/repairs.ts +4 -1
- package/src/cli/doctor-supervisor.ts +3 -0
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/public-command-registry.ts +28 -6
- 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/mcp-serve.ts +7 -0
- package/src/commands/sdk.ts +455 -397
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +316 -79
- 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/coordinator-mcp/server.ts +2 -0
- 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 +4 -13
- package/src/dap/config.ts +2 -2
- 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/register.ts +10 -6
- 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 +13 -1
- package/src/discovery/helpers.ts +171 -29
- package/src/discovery/mcp-json.ts +26 -12
- 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 +796 -154
- package/src/eval/py/kernel.ts +79 -13
- package/src/eval/py/tool-bridge.ts +4 -0
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/html-export-destination.ts +16 -0
- package/src/export/html/index.ts +7 -10
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
- package/src/extensibility/extensions/runner.ts +15 -1
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -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 +9 -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 +70 -9
- package/src/gjc-runtime/psmux-detect.ts +28 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/session-state-sidecar.ts +3 -2
- package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
- 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 +105 -16
- package/src/gjc-runtime/tmux-common.ts +8 -5
- 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 +20 -19
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/config.ts +12 -63
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/memories/index.ts +88 -7
- 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/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +1 -1
- 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 +76 -31
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
- package/src/modes/controllers/selector-controller.ts +24 -15
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -1
- 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/client.ts +4 -2
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/json-rpc.ts +2 -1
- package/src/runtime-mcp/manager.ts +29 -11
- package/src/runtime-mcp/mcp-redirect.ts +73 -0
- package/src/runtime-mcp/oauth-flow.ts +14 -2
- package/src/runtime-mcp/oauth-public-url.ts +14 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/pool.ts +6 -0
- package/src/runtime-mcp/project-host-values.ts +46 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +4 -3
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +95 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +81 -6
- package/src/sdk/broker/doctor-restart.ts +4 -0
- package/src/sdk/broker/ensure.ts +31 -2
- 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 +2705 -303
- package/src/sdk/host/session-runtime.ts +490 -143
- 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 +82 -18
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +69 -45
- package/src/session/agent-session.ts +679 -238
- package/src/session/artifacts.ts +176 -71
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3779 -462
- package/src/session/internal/managed-session-storage.ts +501 -78
- 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 +951 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +447 -15
- 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 +145 -2
- package/src/skill-state/workflow-mutation-guard.ts +102 -10
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/mcp.ts +8 -4
- 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/task/isolation-dir.ts +143 -0
- package/src/task/worktree.ts +16 -7
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit-write-path.ts +68 -0
- package/src/tools/ast-edit.ts +10 -1
- 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/browser/cdp-url.ts +18 -0
- package/src/tools/browser.ts +8 -1
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/gh.ts +10 -0
- package/src/tools/github-side-effect.ts +112 -0
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/monitor-mutation.ts +27 -0
- package/src/tools/monitor.ts +7 -0
- package/src/tools/python.ts +188 -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/external-editor.ts +3 -2
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/insane/url-guard.ts +2 -1
- 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";
|
|
@@ -214,11 +215,12 @@ import {
|
|
|
214
215
|
type AsyncJob,
|
|
215
216
|
type AsyncJobDeliveryState,
|
|
216
217
|
AsyncJobManager,
|
|
217
|
-
asyncJobEndpointId as deriveAsyncJobEndpointId,
|
|
218
218
|
type FoldReason,
|
|
219
219
|
type JobFoldEvent,
|
|
220
220
|
type OwnerSubagentShutdownLease,
|
|
221
|
+
type SubagentLifecycle,
|
|
221
222
|
} from "../async";
|
|
223
|
+
import { asyncJobEndpointId as deriveAsyncJobEndpointId } from "../async/endpoint-id";
|
|
222
224
|
import { reset as resetCapabilities } from "../capability";
|
|
223
225
|
import type { Rule } from "../capability/rule";
|
|
224
226
|
import type { CasReceipt } from "../config/atomic-yaml-patch";
|
|
@@ -234,6 +236,12 @@ import {
|
|
|
234
236
|
UnknownModelProfileError,
|
|
235
237
|
validateModelProfileName,
|
|
236
238
|
} from "../config/model-profile-contract";
|
|
239
|
+
import {
|
|
240
|
+
commitDurableModelProfileOwnership,
|
|
241
|
+
InvalidModelProfileOwnershipError,
|
|
242
|
+
ModelProfileOwnershipConflictError,
|
|
243
|
+
readDurableModelProfileOwnership,
|
|
244
|
+
} from "../config/model-profile-ownership";
|
|
237
245
|
import { resolveProfileBindings } from "../config/model-profiles";
|
|
238
246
|
import {
|
|
239
247
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
@@ -319,6 +327,7 @@ import {
|
|
|
319
327
|
buildSkillPromptMessage,
|
|
320
328
|
getSkillSlashCommandName,
|
|
321
329
|
isNamespacedSkillSlashCommandName,
|
|
330
|
+
type ParsedSkillInvocation,
|
|
322
331
|
parseSkillInvocations,
|
|
323
332
|
type Skill,
|
|
324
333
|
type SkillWarning,
|
|
@@ -408,18 +417,14 @@ import type { NotificationSessionController } from "../sdk/bus/session-control";
|
|
|
408
417
|
import { buildSyntheticModelId, syntheticNamespaceCollision } from "../sdk/model-profile-model";
|
|
409
418
|
import { sanitizePromptFailure } from "../sdk/prompt-failure";
|
|
410
419
|
import type { SecretObfuscator } from "../secrets/obfuscator";
|
|
411
|
-
import {
|
|
412
|
-
formatNoCredentialOnboardingError,
|
|
413
|
-
formatNoModelOnboardingError,
|
|
414
|
-
NoModelSelectedError,
|
|
415
|
-
} from "../setup/model-onboarding-guidance";
|
|
420
|
+
import { formatNoCredentialOnboardingError, NoModelSelectedError } from "../setup/model-onboarding-guidance";
|
|
416
421
|
import {
|
|
417
422
|
isCanonicalGjcWorkflowSkill,
|
|
418
423
|
isWorkflowContinuationInert,
|
|
419
424
|
readVisibleSkillActiveState,
|
|
420
425
|
syncSkillActiveState,
|
|
421
426
|
} from "../skill-state/active-state";
|
|
422
|
-
import { assertWorkflowMutationAllowed } from "../skill-state/workflow-mutation-guard";
|
|
427
|
+
import { assertWorkflowMutationAllowed, isWorkflowMutationTool } from "../skill-state/workflow-mutation-guard";
|
|
423
428
|
import { invalidateHostMetadata } from "../ssh/connection-manager";
|
|
424
429
|
import { buildVolatileProjectContext } from "../system-prompt";
|
|
425
430
|
import { DelegationHintController } from "../task/delegation-hint";
|
|
@@ -444,6 +449,7 @@ import {
|
|
|
444
449
|
import { assertEditableFile } from "../tools/auto-generated-guard";
|
|
445
450
|
import { releaseTabsForOwner } from "../tools/browser/tab-supervisor";
|
|
446
451
|
import type { CheckpointState } from "../tools/checkpoint";
|
|
452
|
+
import { githubPermissionIntent } from "../tools/github-side-effect";
|
|
447
453
|
import { outputMeta, wrapToolWithMetaNotice } from "../tools/output-meta";
|
|
448
454
|
import { normalizeLocalScheme, resolveReadPath, resolveToCwd } from "../tools/path-utils";
|
|
449
455
|
import { registerResourceGcSession } from "../tools/resource-gc";
|
|
@@ -527,6 +533,7 @@ import type {
|
|
|
527
533
|
SessionEntry,
|
|
528
534
|
SessionManagerCloseOutcome,
|
|
529
535
|
SessionMemoryStats,
|
|
536
|
+
SessionArtifactPublication,
|
|
530
537
|
} from "./session-manager";
|
|
531
538
|
import {
|
|
532
539
|
createReadonlySessionManager,
|
|
@@ -913,6 +920,28 @@ export interface QueuedInputSubmission {
|
|
|
913
920
|
|
|
914
921
|
/** Listener function for agent session events */
|
|
915
922
|
export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Deliver one event to one listener, containing both synchronous throws and
|
|
926
|
+
* async rejections. Listeners are `void`-typed but interactive mode subscribes
|
|
927
|
+
* with an `async` one whose handlers can persist (plan approval calls
|
|
928
|
+
* `session.prompt()`); a rejection there, e.g. the managed append fence after
|
|
929
|
+
* another process took over the transcript, must not escape as an unhandled
|
|
930
|
+
* rejection and terminate the process.
|
|
931
|
+
*/
|
|
932
|
+
function deliverSessionEvent(
|
|
933
|
+
listener: AgentSessionEventListener,
|
|
934
|
+
event: AgentSessionEvent,
|
|
935
|
+
onError: (error: unknown) => void,
|
|
936
|
+
): void {
|
|
937
|
+
try {
|
|
938
|
+
const result: unknown = listener(event);
|
|
939
|
+
if (result instanceof Promise) result.catch(onError);
|
|
940
|
+
} catch (error) {
|
|
941
|
+
onError(error);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
916
945
|
export type AsyncJobSnapshotItem = Pick<
|
|
917
946
|
AsyncJob,
|
|
918
947
|
"id" | "type" | "status" | "label" | "startTime" | "endTime" | "metadata"
|
|
@@ -1218,6 +1247,11 @@ type SendUserMessageDispatchOptions = SendUserMessageOptions & {
|
|
|
1218
1247
|
};
|
|
1219
1248
|
|
|
1220
1249
|
type InternalPromptOptions = PromptOptions & { sdkRunToken?: string };
|
|
1250
|
+
interface SdkContinuationOwnership {
|
|
1251
|
+
scope: AttemptScope;
|
|
1252
|
+
token: string;
|
|
1253
|
+
cohort: string[];
|
|
1254
|
+
}
|
|
1221
1255
|
type InternalCustomMessageOptions = Pick<
|
|
1222
1256
|
PromptOptions,
|
|
1223
1257
|
| "streamingBehavior"
|
|
@@ -1427,8 +1461,7 @@ const PROVIDER_FIRST_EVENT_TIMEOUT_WITHOUT_ARTICLE_ERROR = "Provider stream time
|
|
|
1427
1461
|
const BARE_DEFAULT_CODEX_RETRYABLE_ERROR =
|
|
1428
1462
|
/^Codex error event(?:: .*)? \(code=(?:server_is_overloaded|server_error|internal_error)(?:, [^)]+)*\)$/;
|
|
1429
1463
|
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(?:, [^)]+)*\)$/;
|
|
1464
|
+
const BARE_DEFAULT_CODEX_OVERLOAD_ERROR = /^Codex error event(?:: .*)? \(code=server_is_overloaded(?:, [^)]+)*\)$/;
|
|
1432
1465
|
const BARE_DEFAULT_OVERLOAD_CODES = new Set([SERVER_OVERLOADED_PROVIDER_CODE]);
|
|
1433
1466
|
/** Anthropic's typed capacity-overload `error.type`, the only Anthropic overload code admitted below. */
|
|
1434
1467
|
const ANTHROPIC_OVERLOADED_ERROR_TYPE = "overloaded_error";
|
|
@@ -1541,14 +1574,7 @@ function isExactTypedOverloadFacts(
|
|
|
1541
1574
|
facts.retryMaxAttempts === undefined
|
|
1542
1575
|
);
|
|
1543
1576
|
}
|
|
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
|
-
*/
|
|
1577
|
+
|
|
1552
1578
|
function isStatuslessTypedOverloadFacts(facts: TransportFailureFacts | undefined): boolean {
|
|
1553
1579
|
if (!facts) return false;
|
|
1554
1580
|
return (
|
|
@@ -1557,7 +1583,6 @@ function isStatuslessTypedOverloadFacts(facts: TransportFailureFacts | undefined
|
|
|
1557
1583
|
(facts.openaiErrorCode === undefined || facts.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE)
|
|
1558
1584
|
);
|
|
1559
1585
|
}
|
|
1560
|
-
|
|
1561
1586
|
// Deterministic auth/request/model diagnostics must surface even when a provider
|
|
1562
1587
|
// labels the failure with a transient code.
|
|
1563
1588
|
const TERMINAL_ERROR_MESSAGE =
|
|
@@ -1798,20 +1823,14 @@ function summarizeAgentBashArtifactSave(
|
|
|
1798
1823
|
};
|
|
1799
1824
|
}
|
|
1800
1825
|
|
|
1801
|
-
export interface AgentBashArtifactStore {
|
|
1802
|
-
saveArtifact(content: string, toolType: string): Promise<string | undefined>;
|
|
1803
|
-
getArtifactPath(id: string): Promise<string | null>;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
1826
|
export async function saveAgentBashOriginalArtifact(
|
|
1807
|
-
|
|
1827
|
+
publication: SessionArtifactPublication,
|
|
1808
1828
|
originalText: string,
|
|
1809
1829
|
): Promise<BashArtifactSaveResult> {
|
|
1810
1830
|
try {
|
|
1811
|
-
const artifactId = await
|
|
1812
|
-
if (!artifactId) return { status: "
|
|
1813
|
-
|
|
1814
|
-
return artifactPath ? summarizeAgentBashArtifactSave(artifactId, originalText) : { status: "unavailable" };
|
|
1831
|
+
const artifactId = await publication(originalText, "bash-original");
|
|
1832
|
+
if (!artifactId) return { status: "unavailable" };
|
|
1833
|
+
return summarizeAgentBashArtifactSave(artifactId, originalText);
|
|
1815
1834
|
} catch (error) {
|
|
1816
1835
|
return { status: "failed", diagnostic: boundAgentBashArtifactSaveDiagnostic(error) };
|
|
1817
1836
|
}
|
|
@@ -1991,7 +2010,7 @@ async function discardPreparedNewSessionAfterFailure(
|
|
|
1991
2010
|
// ============================================================================
|
|
1992
2011
|
|
|
1993
2012
|
/** Tools that require user permission before execution when an ACP client is connected. */
|
|
1994
|
-
const PERMISSION_REQUIRED_TOOLS = new Set(["bash", "monitor", "eval", "edit", "delete", "move"]);
|
|
2013
|
+
const PERMISSION_REQUIRED_TOOLS = new Set(["bash", "monitor", "eval", "edit", "delete", "move", "github"]);
|
|
1995
2014
|
|
|
1996
2015
|
function isShellExecutionPermissionTool(toolName: string): boolean {
|
|
1997
2016
|
return toolName === "bash" || toolName === "monitor";
|
|
@@ -2115,6 +2134,7 @@ function getPermissionIntent(
|
|
|
2115
2134
|
cacheKey: "edit:move",
|
|
2116
2135
|
};
|
|
2117
2136
|
}
|
|
2137
|
+
if (toolName === "github") return githubPermissionIntent(args);
|
|
2118
2138
|
return undefined;
|
|
2119
2139
|
}
|
|
2120
2140
|
|
|
@@ -2764,6 +2784,7 @@ export class AgentSession {
|
|
|
2764
2784
|
#scopedModels: ScopedModelSelection[];
|
|
2765
2785
|
#thinkingLevel: ThinkingLevel | undefined;
|
|
2766
2786
|
#activeModelProfile: string | undefined;
|
|
2787
|
+
#unavailableModelProfile: string | undefined;
|
|
2767
2788
|
#activeProfileInstalledRoles = new Map<string, ModelSelectorValue | undefined>();
|
|
2768
2789
|
#activeProfileInstalledAgentOverrides = new Map<string, ModelSelectorValue | undefined>();
|
|
2769
2790
|
#preProfileModel: Model | undefined;
|
|
@@ -2789,6 +2810,9 @@ export class AgentSession {
|
|
|
2789
2810
|
#followUpReservationTransitionWaiters = new Set<() => void>();
|
|
2790
2811
|
#selectionFenceGeneration = 0;
|
|
2791
2812
|
#defaultModelSelectionMutationRevision = 0;
|
|
2813
|
+
#userModelSelectionRevision = 0;
|
|
2814
|
+
#userCanonicalVariantSelectionRevision = 0;
|
|
2815
|
+
#userCanonicalVariantSelection: string | undefined;
|
|
2792
2816
|
#thinkingLevelMutationRevision = 0;
|
|
2793
2817
|
#thinkingVisibilityMutationRevision = 0;
|
|
2794
2818
|
#thinkingLevelLiveMutationRevision = 0;
|
|
@@ -3439,8 +3463,17 @@ export class AgentSession {
|
|
|
3439
3463
|
this.#activeAttemptScope = undefined;
|
|
3440
3464
|
this.#activeLogicalRunId = undefined;
|
|
3441
3465
|
}
|
|
3442
|
-
#
|
|
3443
|
-
const
|
|
3466
|
+
#captureSdkContinuationOwnership(scope: AttemptScope | undefined): SdkContinuationOwnership | undefined {
|
|
3467
|
+
const token = scope ? this.#sdkRunTokensByAttemptScope.get(scope) : undefined;
|
|
3468
|
+
if (!scope || token === undefined) return undefined;
|
|
3469
|
+
return { scope, token, cohort: [...(this.#sdkRunCohortsByAttemptScope.get(scope) ?? [token])] };
|
|
3470
|
+
}
|
|
3471
|
+
#acceptSdkAttemptRun(
|
|
3472
|
+
handle: AttemptRunHandle,
|
|
3473
|
+
sdkRunToken?: string,
|
|
3474
|
+
sdkRunTokens?: string[],
|
|
3475
|
+
predecessorScope?: AttemptScope,
|
|
3476
|
+
): void {
|
|
3444
3477
|
const predecessorSdkRunToken =
|
|
3445
3478
|
predecessorScope === undefined ? undefined : this.#sdkRunTokensByAttemptScope.get(predecessorScope);
|
|
3446
3479
|
const carryRecoverySkip =
|
|
@@ -3450,14 +3483,23 @@ export class AgentSession {
|
|
|
3450
3483
|
this.#skipPostPromptRecoveryWaitByAttemptScope.delete(predecessorScope);
|
|
3451
3484
|
this.#acceptRunHandle(handle);
|
|
3452
3485
|
this.#settleTrackedOwnRunPromotions(handle);
|
|
3486
|
+
// Logical continuation lineage is causal even without an SDK submission.
|
|
3487
|
+
if (predecessorScope) {
|
|
3488
|
+
this.#lifecycleScopesByAttemptScope.set(
|
|
3489
|
+
handle.scope,
|
|
3490
|
+
this.#lifecycleScopesByAttemptScope.get(predecessorScope) ?? predecessorScope,
|
|
3491
|
+
);
|
|
3492
|
+
}
|
|
3453
3493
|
if (sdkRunToken !== undefined) {
|
|
3454
3494
|
this.#activeSdkRunToken = sdkRunToken;
|
|
3455
3495
|
this.#sdkRunTokensByAttemptScope.set(handle.scope, sdkRunToken);
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3496
|
+
this.#sdkRunCohortsByAttemptScope.set(handle.scope, sdkRunTokens ?? [sdkRunToken]);
|
|
3497
|
+
for (const token of this.#sdkRunCohortsByAttemptScope.get(handle.scope) ?? []) {
|
|
3498
|
+
if (this.#sdkTerminalPublications.has(token)) continue;
|
|
3499
|
+
const publication = Promise.withResolvers<void>();
|
|
3500
|
+
void publication.promise.catch(() => undefined);
|
|
3501
|
+
this.#sdkTerminalPublications.set(token, publication);
|
|
3502
|
+
}
|
|
3461
3503
|
}
|
|
3462
3504
|
if (carryRecoverySkip) this.#skipPostPromptRecoveryWaitByAttemptScope.add(handle.scope);
|
|
3463
3505
|
}
|
|
@@ -3607,6 +3649,7 @@ export class AgentSession {
|
|
|
3607
3649
|
#restoreManagedFallbackGetApiKey: (() => void) | undefined;
|
|
3608
3650
|
// Todo completion reminder state
|
|
3609
3651
|
#todoReminderCount = 0;
|
|
3652
|
+
#todoReminderContinuationGeneration: number | undefined;
|
|
3610
3653
|
#deepInterviewUserIntentEpoch = 0;
|
|
3611
3654
|
#deepInterviewTurnOwnerEpoch = 0;
|
|
3612
3655
|
#deepInterviewGenuineUserMessageEpochs = new WeakMap<object, number>();
|
|
@@ -3800,6 +3843,7 @@ export class AgentSession {
|
|
|
3800
3843
|
#skipPostPromptRecoveryWaitByAttemptScope = new WeakSet<AttemptScope>();
|
|
3801
3844
|
#sdkRunTokensByAttemptScope = new WeakMap<AttemptScope, string>();
|
|
3802
3845
|
#sdkRunCohortsByAttemptScope = new WeakMap<AttemptScope, string[]>();
|
|
3846
|
+
#lifecycleScopesByAttemptScope = new WeakMap<AttemptScope, AttemptScope>();
|
|
3803
3847
|
#activeSdkRunToken: string | undefined;
|
|
3804
3848
|
#activeAttemptScope: AttemptScope | undefined;
|
|
3805
3849
|
#attemptAuthority!: AttemptScopeAuthority;
|
|
@@ -4025,6 +4069,8 @@ export class AgentSession {
|
|
|
4025
4069
|
// async event handlers settle. Subscribers treat agent_end as readiness, so
|
|
4026
4070
|
// publishing it earlier lets a successor corrupt the prior prompt's lifecycle.
|
|
4027
4071
|
#pendingAgentEndEmit: AgentSessionEvent | undefined;
|
|
4072
|
+
#agentEndContinuationDecisions = new WeakSet<AgentSessionEvent>();
|
|
4073
|
+
#pendingSdkAgentEnds = new Set<AgentSessionEvent>();
|
|
4028
4074
|
// A scheduled continuation owns this terminal boundary until it either starts
|
|
4029
4075
|
// the successor or proves it cannot. Holds prevent a false idle event while
|
|
4030
4076
|
// preserving the predecessor for cancellation and preflight failures.
|
|
@@ -4035,6 +4081,8 @@ export class AgentSession {
|
|
|
4035
4081
|
#agentEndPublicationInFlight = 0;
|
|
4036
4082
|
#agentEndPublicationPromise: Promise<void> = Promise.resolve();
|
|
4037
4083
|
#agentEndHandlingPromise: Promise<void> = Promise.resolve();
|
|
4084
|
+
#sdkTerminalPublications = new Map<string, PromiseWithResolvers<void>>();
|
|
4085
|
+
#publishingSdkTerminals = new Set<PromiseWithResolvers<void>>();
|
|
4038
4086
|
|
|
4039
4087
|
#obfuscator: SecretObfuscator | undefined;
|
|
4040
4088
|
#checkpointState: CheckpointState | undefined = undefined;
|
|
@@ -4073,11 +4121,7 @@ export class AgentSession {
|
|
|
4073
4121
|
readonly rawSseDebugBuffer: RawSseDebugBuffer;
|
|
4074
4122
|
|
|
4075
4123
|
#acquirePowerAssertion(): void {
|
|
4076
|
-
if (
|
|
4077
|
-
process.platform !== "darwin" &&
|
|
4078
|
-
process.platform !== "linux" &&
|
|
4079
|
-
process.platform !== "win32"
|
|
4080
|
-
) {
|
|
4124
|
+
if (process.platform !== "darwin" && process.platform !== "linux" && process.platform !== "win32") {
|
|
4081
4125
|
return;
|
|
4082
4126
|
}
|
|
4083
4127
|
if (this.#powerAssertion || this.#powerAssertionLoad) return;
|
|
@@ -4707,8 +4751,21 @@ export class AgentSession {
|
|
|
4707
4751
|
}
|
|
4708
4752
|
}
|
|
4709
4753
|
|
|
4710
|
-
#reserveDeferredAgentEndForContinuation(): symbol | undefined {
|
|
4711
|
-
const
|
|
4754
|
+
#reserveDeferredAgentEndForContinuation(predecessorScope?: AttemptScope): symbol | undefined {
|
|
4755
|
+
const resourceRunId = this.#runResourceLeaseContext.getStore()?.resourceRunId;
|
|
4756
|
+
const pending = predecessorScope
|
|
4757
|
+
? [...this.#pendingSdkAgentEnds].find(
|
|
4758
|
+
event => (event as AgentSessionEvent & { scope?: AttemptScope }).scope === predecessorScope,
|
|
4759
|
+
)
|
|
4760
|
+
: resourceRunId === undefined
|
|
4761
|
+
? this.#pendingAgentEndEmit
|
|
4762
|
+
: ([...this.#pendingSdkAgentEnds].find(
|
|
4763
|
+
event => getAgentTerminalOwnerContext(event)?.resourceRunId === resourceRunId,
|
|
4764
|
+
) ??
|
|
4765
|
+
(this.#pendingAgentEndEmit &&
|
|
4766
|
+
getAgentTerminalOwnerContext(this.#pendingAgentEndEmit)?.resourceRunId === resourceRunId
|
|
4767
|
+
? this.#pendingAgentEndEmit
|
|
4768
|
+
: undefined));
|
|
4712
4769
|
if (!pending) return undefined;
|
|
4713
4770
|
const hold = Symbol("deferred-agent-end-continuation");
|
|
4714
4771
|
this.#pendingAgentEndContinuationHolds.set(hold, pending);
|
|
@@ -4722,6 +4779,8 @@ export class AgentSession {
|
|
|
4722
4779
|
throw new Error("Cannot restore a deferred agent_end over a different pending terminal event");
|
|
4723
4780
|
}
|
|
4724
4781
|
this.#pendingAgentEndEmit = pending;
|
|
4782
|
+
const scope = (pending as AgentSessionEvent & { scope?: AttemptScope }).scope;
|
|
4783
|
+
if (scope && this.#sdkRunTokensByAttemptScope.has(scope)) this.#pendingSdkAgentEnds.add(pending);
|
|
4725
4784
|
const hold = Symbol("deferred-agent-end-continuation");
|
|
4726
4785
|
this.#pendingAgentEndContinuationHolds.set(hold, pending);
|
|
4727
4786
|
this.#deferredAgentEndWorkLeases.set(hold, this.#sessionWorkLease.acquire());
|
|
@@ -4732,6 +4791,7 @@ export class AgentSession {
|
|
|
4732
4791
|
if (!hold) return undefined;
|
|
4733
4792
|
const pending = this.#pendingAgentEndContinuationHolds.get(hold);
|
|
4734
4793
|
this.#pendingAgentEndContinuationHolds.delete(hold);
|
|
4794
|
+
if (pending) this.#pendingSdkAgentEnds.delete(pending);
|
|
4735
4795
|
this.#deferredAgentEndWorkLeases.get(hold)?.release();
|
|
4736
4796
|
this.#deferredAgentEndWorkLeases.delete(hold);
|
|
4737
4797
|
if (pending && this.#pendingAgentEndEmit === pending) {
|
|
@@ -4749,6 +4809,8 @@ export class AgentSession {
|
|
|
4749
4809
|
}
|
|
4750
4810
|
|
|
4751
4811
|
#restoreDeferredAgentEndAfterContinuationFailure(pending: AgentSessionEvent | undefined): void {
|
|
4812
|
+
const scope = (pending as (AgentSessionEvent & { scope?: AttemptScope }) | undefined)?.scope;
|
|
4813
|
+
if (pending && scope && this.#sdkRunTokensByAttemptScope.has(scope)) this.#pendingSdkAgentEnds.add(pending);
|
|
4752
4814
|
if (pending && !this.#pendingAgentEndEmit) {
|
|
4753
4815
|
this.#pendingAgentEndEmit = pending;
|
|
4754
4816
|
}
|
|
@@ -4761,6 +4823,8 @@ export class AgentSession {
|
|
|
4761
4823
|
): void {
|
|
4762
4824
|
if (!hold || !pending) return;
|
|
4763
4825
|
this.#pendingAgentEndEmit = pending;
|
|
4826
|
+
const scope = (pending as AgentSessionEvent & { scope?: AttemptScope }).scope;
|
|
4827
|
+
if (scope && this.#sdkRunTokensByAttemptScope.has(scope)) this.#pendingSdkAgentEnds.add(pending);
|
|
4764
4828
|
this.#pendingAgentEndContinuationHolds.set(hold, pending);
|
|
4765
4829
|
}
|
|
4766
4830
|
|
|
@@ -5063,11 +5127,15 @@ export class AgentSession {
|
|
|
5063
5127
|
this.#flushPendingAgentEnd();
|
|
5064
5128
|
return flushError;
|
|
5065
5129
|
}
|
|
5066
|
-
async #settleEndedInFlight(
|
|
5130
|
+
async #settleEndedInFlight(
|
|
5131
|
+
token: symbol,
|
|
5132
|
+
promptWait?: "publication" | "full",
|
|
5133
|
+
sdkPublication?: Promise<void>,
|
|
5134
|
+
): Promise<void> {
|
|
5067
5135
|
const flushError = this.#endInFlight(token);
|
|
5068
5136
|
const predecessorPromptStillInFlight = this.#livePromptsInFlight() > 0;
|
|
5069
5137
|
if (promptWait === "publication") {
|
|
5070
|
-
await this.#agentEndPublicationPromise;
|
|
5138
|
+
await (sdkPublication ?? this.#agentEndPublicationPromise);
|
|
5071
5139
|
} else if (promptWait === "full") {
|
|
5072
5140
|
await this.#agentEndHandlingPromise;
|
|
5073
5141
|
await this.#waitForPostPromptRecovery();
|
|
@@ -5107,13 +5175,23 @@ export class AgentSession {
|
|
|
5107
5175
|
}
|
|
5108
5176
|
|
|
5109
5177
|
#flushPendingAgentEnd(): void {
|
|
5178
|
+
for (const sdkPending of this.#pendingSdkAgentEnds) {
|
|
5179
|
+
if (!this.#agentEndContinuationDecisions.has(sdkPending)) continue;
|
|
5180
|
+
if ([...this.#pendingAgentEndContinuationHolds.values()].includes(sdkPending)) continue;
|
|
5181
|
+
this.#pendingSdkAgentEnds.delete(sdkPending);
|
|
5182
|
+
if (this.#pendingAgentEndEmit === sdkPending) this.#pendingAgentEndEmit = undefined;
|
|
5183
|
+
const sdkLease = this.#deferredAgentEndLeases.get(sdkPending);
|
|
5184
|
+
this.#deferredAgentEndLeases.delete(sdkPending);
|
|
5185
|
+
this.#startAgentEndPublication(sdkPending, sdkLease);
|
|
5186
|
+
}
|
|
5187
|
+
const pending = this.#pendingAgentEndEmit;
|
|
5188
|
+
if (pending && this.#pendingSdkAgentEnds.has(pending)) return;
|
|
5110
5189
|
if (
|
|
5111
5190
|
this.#livePromptsInFlight() > 0 ||
|
|
5112
5191
|
this.#agentEventHandlersInFlight > 0 ||
|
|
5113
5192
|
this.#pendingAgentEndContinuationHolds.size > 0
|
|
5114
5193
|
)
|
|
5115
5194
|
return;
|
|
5116
|
-
const pending = this.#pendingAgentEndEmit;
|
|
5117
5195
|
if (!pending) {
|
|
5118
5196
|
this.#resolveSessionSettlement();
|
|
5119
5197
|
return;
|
|
@@ -5125,13 +5203,16 @@ export class AgentSession {
|
|
|
5125
5203
|
}
|
|
5126
5204
|
|
|
5127
5205
|
#startAgentEndPublication(pending: AgentSessionEvent, lease?: RunResourceProducerLease): void {
|
|
5206
|
+
this.#agentEndContinuationDecisions.delete(pending);
|
|
5128
5207
|
this.#agentEndPublicationInFlight++;
|
|
5129
5208
|
const pendingScope = (pending as AgentSessionEvent & { scope?: AttemptScopeRef }).scope as
|
|
5130
5209
|
| AttemptScope
|
|
5131
5210
|
| undefined;
|
|
5132
5211
|
const sdkTerminal = pendingScope !== undefined && this.#sdkRunTokensByAttemptScope.has(pendingScope);
|
|
5133
5212
|
this.#agentEndPublicationPromise = this.#publishDeferredAgentEnd(pending, lease, sdkTerminal);
|
|
5134
|
-
void this.#agentEndPublicationPromise
|
|
5213
|
+
void this.#agentEndPublicationPromise.catch(error => {
|
|
5214
|
+
logger.warn("Agent terminal publication failed", { error: sanitizePromptFailure(error) });
|
|
5215
|
+
});
|
|
5135
5216
|
}
|
|
5136
5217
|
|
|
5137
5218
|
async #publishDeferredAgentEnd(
|
|
@@ -5147,6 +5228,13 @@ export class AgentSession {
|
|
|
5147
5228
|
const publicationCorrelationId = publicationScope
|
|
5148
5229
|
? this.#sdkRunTokensByAttemptScope.get(publicationScope)
|
|
5149
5230
|
: undefined;
|
|
5231
|
+
const sdkPublications = (publicationScope ? (this.#sdkRunCohortsByAttemptScope.get(publicationScope) ?? []) : [])
|
|
5232
|
+
.map(token => ({ token, publication: this.#sdkTerminalPublications.get(token) }))
|
|
5233
|
+
.filter(
|
|
5234
|
+
(entry): entry is { token: string; publication: PromiseWithResolvers<void> } =>
|
|
5235
|
+
entry.publication !== undefined,
|
|
5236
|
+
);
|
|
5237
|
+
for (const { publication } of sdkPublications) this.#publishingSdkTerminals.add(publication);
|
|
5150
5238
|
let extensionDelivery: Promise<void> | undefined;
|
|
5151
5239
|
const releaseLease = () => {
|
|
5152
5240
|
if (!lease) return;
|
|
@@ -5239,7 +5327,15 @@ export class AgentSession {
|
|
|
5239
5327
|
try {
|
|
5240
5328
|
if (lease) await this.#runResourceLeaseContext.run(lease, publish);
|
|
5241
5329
|
else await publish();
|
|
5330
|
+
for (const { publication } of sdkPublications) publication.resolve();
|
|
5331
|
+
} catch (error) {
|
|
5332
|
+
for (const { publication } of sdkPublications) publication.reject(error);
|
|
5333
|
+
throw error;
|
|
5242
5334
|
} finally {
|
|
5335
|
+
for (const { token, publication } of sdkPublications) {
|
|
5336
|
+
this.#publishingSdkTerminals.delete(publication);
|
|
5337
|
+
if (this.#sdkTerminalPublications.get(token) === publication) this.#sdkTerminalPublications.delete(token);
|
|
5338
|
+
}
|
|
5243
5339
|
if (extensionDelivery) void extensionDelivery.then(releaseLease, releaseLease);
|
|
5244
5340
|
else releaseLease();
|
|
5245
5341
|
this.#agentEndPublicationInFlight = Math.max(0, this.#agentEndPublicationInFlight - 1);
|
|
@@ -5498,12 +5594,7 @@ export class AgentSession {
|
|
|
5498
5594
|
controller !== undefined &&
|
|
5499
5595
|
controller.chain.entries.length > 1;
|
|
5500
5596
|
if (shouldCapture && modelKey) this.#managedFallbackActiveCredentialRows.delete(modelKey);
|
|
5501
|
-
if (
|
|
5502
|
-
override &&
|
|
5503
|
-
model &&
|
|
5504
|
-
storageProvider === override.storageProvider &&
|
|
5505
|
-
modelKey === override.modelKey
|
|
5506
|
-
) {
|
|
5597
|
+
if (override && model && storageProvider === override.storageProvider && modelKey === override.modelKey) {
|
|
5507
5598
|
this.#managedFallbackNextCredentialOverride = undefined;
|
|
5508
5599
|
const apiKey = await this.#modelRegistry.getApiKey(model, this.credentialSessionId, {
|
|
5509
5600
|
credentialSelector: { kind: "id", value: String(override.rowId) },
|
|
@@ -5513,10 +5604,7 @@ export class AgentSession {
|
|
|
5513
5604
|
return apiKey;
|
|
5514
5605
|
}
|
|
5515
5606
|
// 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
|
-
);
|
|
5607
|
+
const fallbackApiKey = await this.#resolveManagedFallbackCredentialRow(model, override.credentialKind);
|
|
5520
5608
|
if (fallbackApiKey !== undefined) {
|
|
5521
5609
|
if (shouldCapture && model) this.#captureManagedFallbackActiveCredential(model);
|
|
5522
5610
|
return fallbackApiKey;
|
|
@@ -5524,8 +5612,7 @@ export class AgentSession {
|
|
|
5524
5612
|
throw new Error("Selected managed fallback credential is no longer available");
|
|
5525
5613
|
}
|
|
5526
5614
|
const apiKey = await invokeOriginalGetApiKey(provider);
|
|
5527
|
-
if (shouldCapture && model && isAuthenticated(apiKey))
|
|
5528
|
-
this.#captureManagedFallbackActiveCredential(model);
|
|
5615
|
+
if (shouldCapture && model && isAuthenticated(apiKey)) this.#captureManagedFallbackActiveCredential(model);
|
|
5529
5616
|
return apiKey;
|
|
5530
5617
|
};
|
|
5531
5618
|
this.agent.getApiKey = managedGetApiKey;
|
|
@@ -5612,12 +5699,14 @@ export class AgentSession {
|
|
|
5612
5699
|
void this.#queueFollowUpAfterReservation(message, displayText, {
|
|
5613
5700
|
createDisplayEntry: false,
|
|
5614
5701
|
trackExternalFollowUp: false,
|
|
5702
|
+
sdkRunToken: this.#activeSdkRunToken,
|
|
5615
5703
|
}).catch(error => {
|
|
5616
5704
|
this.#settleDeliveredOwnedRegistrations([message]);
|
|
5617
5705
|
logger.warn("Owned streaming follow-up was rejected", { error: String(error) });
|
|
5618
5706
|
});
|
|
5619
5707
|
},
|
|
5620
5708
|
injectIdle: async (messages, signal) => {
|
|
5709
|
+
const sdkRunToken = this.#activeSdkRunToken;
|
|
5621
5710
|
// Mandated boundary comment (corrected turn semantics): same origin
|
|
5622
5711
|
// split as the streaming injector — an allowed owned-completion
|
|
5623
5712
|
// delivery starts a fresh turn attempt/lineage and is not a
|
|
@@ -5652,14 +5741,14 @@ export class AgentSession {
|
|
|
5652
5741
|
await this.agent.prompt(first, {
|
|
5653
5742
|
...this.#managedFallbackPromptOptions(),
|
|
5654
5743
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
5655
|
-
if (handle) this.#acceptSdkAttemptRun(handle);
|
|
5744
|
+
if (handle) this.#acceptSdkAttemptRun(handle, sdkRunToken);
|
|
5656
5745
|
},
|
|
5657
5746
|
});
|
|
5658
5747
|
} else {
|
|
5659
5748
|
await this.agent.prompt(survivors, {
|
|
5660
5749
|
...this.#managedFallbackPromptOptions(),
|
|
5661
5750
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
5662
|
-
if (handle) this.#acceptSdkAttemptRun(handle);
|
|
5751
|
+
if (handle) this.#acceptSdkAttemptRun(handle, sdkRunToken);
|
|
5663
5752
|
},
|
|
5664
5753
|
});
|
|
5665
5754
|
}
|
|
@@ -5831,9 +5920,8 @@ export class AgentSession {
|
|
|
5831
5920
|
(largestPhase, phase) =>
|
|
5832
5921
|
Math.max(
|
|
5833
5922
|
largestPhase,
|
|
5834
|
-
phase.tasks.filter(
|
|
5835
|
-
|
|
5836
|
-
).length,
|
|
5923
|
+
phase.tasks.filter(task => task.status === "pending" || task.status === "in_progress")
|
|
5924
|
+
.length,
|
|
5837
5925
|
),
|
|
5838
5926
|
0,
|
|
5839
5927
|
);
|
|
@@ -6747,6 +6835,17 @@ export class AgentSession {
|
|
|
6747
6835
|
return { running, recent, delivery };
|
|
6748
6836
|
}
|
|
6749
6837
|
|
|
6838
|
+
/**
|
|
6839
|
+
* Lifecycle status of every subagent owned by this session, read from the
|
|
6840
|
+
* manager's stable control-plane records (they outlive AsyncJob eviction).
|
|
6841
|
+
*/
|
|
6842
|
+
getSubagentLifecycleStatuses(): SubagentLifecycle[] {
|
|
6843
|
+
const manager = this.#ownedAsyncJobManager ?? AsyncJobManager.instance();
|
|
6844
|
+
if (!manager) return [];
|
|
6845
|
+
const ownerFilter = this.#agentId ? { ownerId: this.#agentId } : undefined;
|
|
6846
|
+
return manager.getSubagentRecords(ownerFilter).map(record => record.status);
|
|
6847
|
+
}
|
|
6848
|
+
|
|
6750
6849
|
/**
|
|
6751
6850
|
* Cancel async jobs registered by *this* agent only. Used by lifecycle
|
|
6752
6851
|
* transitions (newSession, switchSession, handoff, dispose) so a subagent
|
|
@@ -6853,11 +6952,9 @@ export class AgentSession {
|
|
|
6853
6952
|
/** Emit an event to all listeners without letting one subscriber poison lifecycle settlement. */
|
|
6854
6953
|
#emit(event: AgentSessionEvent): void {
|
|
6855
6954
|
for (const listener of this.#eventListenerSnapshot) {
|
|
6856
|
-
|
|
6857
|
-
listener(event);
|
|
6858
|
-
} catch (error) {
|
|
6955
|
+
deliverSessionEvent(listener, event, error => {
|
|
6859
6956
|
logger.warn("Agent session event subscriber failed", { event: event.type, error: String(error) });
|
|
6860
|
-
}
|
|
6957
|
+
});
|
|
6861
6958
|
}
|
|
6862
6959
|
}
|
|
6863
6960
|
|
|
@@ -6922,6 +7019,8 @@ export class AgentSession {
|
|
|
6922
7019
|
object,
|
|
6923
7020
|
{ scope?: AttemptScope; sdkRunToken?: string; persistGeneration: number; persistBarrier?: Promise<void> }
|
|
6924
7021
|
>();
|
|
7022
|
+
/** Extension handlers cannot mutate or replace the Agent-claimed run owner. */
|
|
7023
|
+
#terminalOwnerByExtensionEvent = new WeakMap<object, AgentTerminalOwnerContext>();
|
|
6925
7024
|
|
|
6926
7025
|
/**
|
|
6927
7026
|
* Capture what is true at the SYNCHRONOUS agent-event boundary, before any async work.
|
|
@@ -7075,8 +7174,8 @@ export class AgentSession {
|
|
|
7075
7174
|
const pendingAgentEnd =
|
|
7076
7175
|
event.type === "agent_end" &&
|
|
7077
7176
|
!maintenanceCheckpoint &&
|
|
7078
|
-
(this.#pendingAgentEndEmit === event || this.#
|
|
7079
|
-
?
|
|
7177
|
+
(this.#pendingAgentEndEmit === event || this.#pendingSdkAgentEnds.has(event))
|
|
7178
|
+
? event
|
|
7080
7179
|
: undefined;
|
|
7081
7180
|
if (pendingAgentEnd) {
|
|
7082
7181
|
this.#deferredAgentEndLeases.set(pendingAgentEnd, eventLease);
|
|
@@ -7093,6 +7192,7 @@ export class AgentSession {
|
|
|
7093
7192
|
}
|
|
7094
7193
|
}
|
|
7095
7194
|
this.#agentEventHandlersInFlight = Math.max(0, this.#agentEventHandlersInFlight - 1);
|
|
7195
|
+
if (event.type === "agent_end") this.#agentEndContinuationDecisions.add(event);
|
|
7096
7196
|
this.#flushPendingAgentEnd();
|
|
7097
7197
|
agentEndHandled?.resolve();
|
|
7098
7198
|
// Every other in-flight counter republishes settlement when it drops; this
|
|
@@ -7329,16 +7429,10 @@ export class AgentSession {
|
|
|
7329
7429
|
// have unwound. Subscribers treat this event as the ready signal; flushing it
|
|
7330
7430
|
// from abort while either barrier is active permits a successor to race the
|
|
7331
7431
|
// 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
|
-
) {
|
|
7432
|
+
if (event.type === "agent_end" && attemptScope && this.#sdkRunTokensByAttemptScope.has(attemptScope)) {
|
|
7433
|
+
this.#pendingSdkAgentEnds.add(event);
|
|
7340
7434
|
if (eventLease) this.#deferredAgentEndLeases.set(event, eventLease);
|
|
7341
|
-
this.#
|
|
7435
|
+
this.#pendingAgentEndEmit = event;
|
|
7342
7436
|
return;
|
|
7343
7437
|
}
|
|
7344
7438
|
if (event.type === "agent_end" && (this.#livePromptsInFlight() > 0 || this.#agentEventHandlersInFlight > 0)) {
|
|
@@ -7452,6 +7546,8 @@ export class AgentSession {
|
|
|
7452
7546
|
eventLease?: RunResourceProducerLease,
|
|
7453
7547
|
): Promise<void> => {
|
|
7454
7548
|
const attemptScope = (event as AgentEvent & { scope?: AttemptScope }).scope;
|
|
7549
|
+
const terminalSdkOwnership =
|
|
7550
|
+
event.type === "agent_end" ? this.#captureSdkContinuationOwnership(attemptScope) : undefined;
|
|
7455
7551
|
|
|
7456
7552
|
// These lifecycle boundaries can be delivered without awaiting this listener.
|
|
7457
7553
|
// Revoke streaming-edit cache generations before any admission, spill, or
|
|
@@ -8160,7 +8256,7 @@ export class AgentSession {
|
|
|
8160
8256
|
skipCompactionCheck: true,
|
|
8161
8257
|
resourceRunId: activePromptHandle,
|
|
8162
8258
|
maintenanceContinuation: true,
|
|
8163
|
-
|
|
8259
|
+
sdkOwnership: terminalSdkOwnership,
|
|
8164
8260
|
});
|
|
8165
8261
|
}
|
|
8166
8262
|
return;
|
|
@@ -8244,7 +8340,9 @@ export class AgentSession {
|
|
|
8244
8340
|
}
|
|
8245
8341
|
|
|
8246
8342
|
// Check for retryable errors first (overloaded, rate limit, server errors)
|
|
8247
|
-
|
|
8343
|
+
// Skip retry for todo reminder continuations - they should fail silently without retrying
|
|
8344
|
+
const isReminderContinuationError = this.#todoReminderContinuationGeneration === agentEndGeneration && agentEndGeneration !== undefined;
|
|
8345
|
+
if (!isReminderContinuationError && this.#isRetryableError(msg)) {
|
|
8248
8346
|
const transportFailure = (msg as AssistantMessage & { transportFailure?: TransportFailureFacts })
|
|
8249
8347
|
.transportFailure;
|
|
8250
8348
|
const messageScope = this.#assistantAttemptScopes.get(msg);
|
|
@@ -8257,6 +8355,10 @@ export class AgentSession {
|
|
|
8257
8355
|
);
|
|
8258
8356
|
if (didRetry) return; // Retry was initiated, don't proceed to compaction
|
|
8259
8357
|
}
|
|
8358
|
+
// Clear the reminder continuation flag after processing
|
|
8359
|
+
if (isReminderContinuationError) {
|
|
8360
|
+
this.#todoReminderContinuationGeneration = undefined;
|
|
8361
|
+
}
|
|
8260
8362
|
if (this.#retryAttempt > 0) {
|
|
8261
8363
|
// A prior retry ended on a non-retryable (terminal) message: emit
|
|
8262
8364
|
// the terminal retry-end and reset so observers clear retry state.
|
|
@@ -8275,7 +8377,7 @@ export class AgentSession {
|
|
|
8275
8377
|
const compactionTask = this.#schedulePostPromptTask(
|
|
8276
8378
|
async () => {
|
|
8277
8379
|
if (this.#isDisposed || this.#sessionAdmissionClosing) return;
|
|
8278
|
-
await this.#checkCompaction(msg, true, undefined, activePromptHandle);
|
|
8380
|
+
await this.#checkCompaction(msg, true, undefined, activePromptHandle, undefined, terminalSdkOwnership);
|
|
8279
8381
|
},
|
|
8280
8382
|
{ resourceRunId: activePromptHandle },
|
|
8281
8383
|
);
|
|
@@ -8501,10 +8603,12 @@ export class AgentSession {
|
|
|
8501
8603
|
/** Internal predecessor terminal event sequestered while waiting behind selection. */
|
|
8502
8604
|
deferredPredecessorAgentEnd?: AgentSessionEvent;
|
|
8503
8605
|
/** Internal causal SDK owner captured when this continuation was scheduled. */
|
|
8504
|
-
|
|
8606
|
+
sdkOwnership?: SdkContinuationOwnership;
|
|
8607
|
+
/** Disable managed fallback retries for this continuation (used for terminal server-initiated turns). */
|
|
8608
|
+
disableManagedFallback?: boolean;
|
|
8505
8609
|
}): Promise<void> {
|
|
8506
8610
|
const continuationAdmission = this.#captureScheduledContinuationAdmission();
|
|
8507
|
-
const
|
|
8611
|
+
const scheduledSdkOwnership = options?.sdkOwnership;
|
|
8508
8612
|
const selectionFenceGeneration =
|
|
8509
8613
|
options?.selectionFenceGeneration ??
|
|
8510
8614
|
this.#selectionFenceGenerationContext.getStore() ??
|
|
@@ -8526,7 +8630,7 @@ export class AgentSession {
|
|
|
8526
8630
|
generation: deferredPromptGeneration,
|
|
8527
8631
|
selectionFenceGeneration,
|
|
8528
8632
|
deferredPredecessorAgentEnd,
|
|
8529
|
-
|
|
8633
|
+
sdkOwnership: scheduledSdkOwnership,
|
|
8530
8634
|
});
|
|
8531
8635
|
} finally {
|
|
8532
8636
|
// The recursive call synchronously re-reserved its settlement
|
|
@@ -8646,14 +8750,21 @@ export class AgentSession {
|
|
|
8646
8750
|
}
|
|
8647
8751
|
const predecessorAgentEnd =
|
|
8648
8752
|
this.#claimDeferredAgentEndForContinuation(predecessorAgentEndHold);
|
|
8753
|
+
const predecessorScope = (
|
|
8754
|
+
predecessorAgentEnd as (AgentSessionEvent & { scope?: AttemptScope }) | undefined
|
|
8755
|
+
)?.scope;
|
|
8756
|
+
const predecessorSdkOwnership = this.#captureSdkContinuationOwnership(predecessorScope);
|
|
8649
8757
|
let predecessorAccepted = false;
|
|
8650
8758
|
const releasePredecessor = () => {
|
|
8651
8759
|
if (predecessorAccepted) return;
|
|
8652
8760
|
predecessorAccepted = true;
|
|
8653
|
-
|
|
8761
|
+
if (startsQueuedSuccessor)
|
|
8762
|
+
this.#restoreDeferredAgentEndAfterContinuationFailure(predecessorAgentEnd);
|
|
8763
|
+
else this.#releaseDeferredAgentEndLease(predecessorAgentEnd);
|
|
8654
8764
|
};
|
|
8655
8765
|
const hasQueuedMessages = this.agent.hasQueuedMessages();
|
|
8656
8766
|
const startsQueuedSuccessor =
|
|
8767
|
+
options?.maintenanceContinuation !== true &&
|
|
8657
8768
|
hasQueuedMessages &&
|
|
8658
8769
|
(options?.continueQueuedOnly === true ||
|
|
8659
8770
|
this.agent.state.messages.at(-1)?.role === "assistant");
|
|
@@ -8662,7 +8773,7 @@ export class AgentSession {
|
|
|
8662
8773
|
: this.agent.continue.bind(this.agent);
|
|
8663
8774
|
try {
|
|
8664
8775
|
await continueQueued({
|
|
8665
|
-
...this.#managedFallbackPromptOptions(),
|
|
8776
|
+
...(options?.disableManagedFallback ? { fallbackManaged: false } : this.#managedFallbackPromptOptions()),
|
|
8666
8777
|
maintenanceContinuation: options?.maintenanceContinuation,
|
|
8667
8778
|
// Reset only after continue() has claimed the queued turn. Skipped or stale
|
|
8668
8779
|
// continuations retain predecessor accounting, and resetAttemptBudget keeps
|
|
@@ -8672,13 +8783,21 @@ export class AgentSession {
|
|
|
8672
8783
|
// agent_start (review P1); their queued messages are in-run
|
|
8673
8784
|
// consumptions, not own-run promotions.
|
|
8674
8785
|
const startsOwn = options?.maintenanceContinuation !== true;
|
|
8675
|
-
const
|
|
8676
|
-
|
|
8677
|
-
|
|
8786
|
+
const inheritedSdkOwnership =
|
|
8787
|
+
options?.continueQueuedOnly || startsQueuedSuccessor
|
|
8788
|
+
? undefined
|
|
8789
|
+
: (scheduledSdkOwnership ?? predecessorSdkOwnership);
|
|
8790
|
+
const inheritedPredecessorScope =
|
|
8791
|
+
options?.continueQueuedOnly || startsQueuedSuccessor
|
|
8792
|
+
? undefined
|
|
8793
|
+
: (inheritedSdkOwnership?.scope ?? predecessorScope);
|
|
8678
8794
|
const consumedSdkRunTokens = acceptance.consumedQueuedMessages
|
|
8679
8795
|
.map(message => this.#sdkRunTokensByQueuedMessage.get(message))
|
|
8680
8796
|
.filter((token): token is string => token !== undefined);
|
|
8681
|
-
const sdkRunToken = consumedSdkRunTokens[0]
|
|
8797
|
+
const sdkRunToken = inheritedSdkOwnership?.token ?? consumedSdkRunTokens[0];
|
|
8798
|
+
const sdkRunTokens = inheritedSdkOwnership
|
|
8799
|
+
? [...new Set([...inheritedSdkOwnership.cohort, ...consumedSdkRunTokens])]
|
|
8800
|
+
: consumedSdkRunTokens;
|
|
8682
8801
|
this.#fireQueuedPromotionHooks(acceptance.consumedQueuedMessages, {
|
|
8683
8802
|
startsOwnRun: startsOwn,
|
|
8684
8803
|
});
|
|
@@ -8686,7 +8805,8 @@ export class AgentSession {
|
|
|
8686
8805
|
this.#acceptSdkAttemptRun(
|
|
8687
8806
|
handle,
|
|
8688
8807
|
sdkRunToken,
|
|
8689
|
-
|
|
8808
|
+
sdkRunTokens.length > 0 ? sdkRunTokens : undefined,
|
|
8809
|
+
inheritedPredecessorScope,
|
|
8690
8810
|
);
|
|
8691
8811
|
options?.onRunAccepted?.(handle);
|
|
8692
8812
|
// Keep the queued token available through the acceptance callback;
|
|
@@ -8813,7 +8933,11 @@ export class AgentSession {
|
|
|
8813
8933
|
return compactionSettings.autoContinue === false ? "auto_continue_disabled_non_resumable_tail" : undefined;
|
|
8814
8934
|
}
|
|
8815
8935
|
|
|
8816
|
-
async #scheduleOverflowRetryContinuation(
|
|
8936
|
+
async #scheduleOverflowRetryContinuation(
|
|
8937
|
+
generation: number,
|
|
8938
|
+
resourceRunId?: string,
|
|
8939
|
+
sdkOwnership?: SdkContinuationOwnership,
|
|
8940
|
+
): Promise<boolean> {
|
|
8817
8941
|
this.#stripOverflowFailedTurnForRetry();
|
|
8818
8942
|
const snapshot = await this.#compactionStateSnapshot();
|
|
8819
8943
|
if (
|
|
@@ -8830,6 +8954,7 @@ export class AgentSession {
|
|
|
8830
8954
|
generation,
|
|
8831
8955
|
suppressPredecessorAgentEnd: true,
|
|
8832
8956
|
resourceRunId,
|
|
8957
|
+
sdkOwnership,
|
|
8833
8958
|
onSkip: reason => this.#logCompactionContinuationSkipped("overflow_retry", reason),
|
|
8834
8959
|
onError: error => this.#logCompactionContinuationError("overflow_retry", error),
|
|
8835
8960
|
});
|
|
@@ -8838,7 +8963,7 @@ export class AgentSession {
|
|
|
8838
8963
|
|
|
8839
8964
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
8840
8965
|
if (compactionSettings.autoContinue !== false) {
|
|
8841
|
-
this.#scheduleAutoContinuePrompt(generation, false, resourceRunId);
|
|
8966
|
+
this.#scheduleAutoContinuePrompt(generation, false, resourceRunId, undefined, undefined, sdkOwnership);
|
|
8842
8967
|
return true;
|
|
8843
8968
|
}
|
|
8844
8969
|
|
|
@@ -8852,7 +8977,7 @@ export class AgentSession {
|
|
|
8852
8977
|
resourceRunId?: string,
|
|
8853
8978
|
deferredSelectionFenceGeneration?: number,
|
|
8854
8979
|
deferredPredecessorAgentEnd?: AgentSessionEvent,
|
|
8855
|
-
|
|
8980
|
+
sdkOwnership?: SdkContinuationOwnership,
|
|
8856
8981
|
): void {
|
|
8857
8982
|
if (this.#sessionTransitionKind !== undefined || this.#handoffTransitionActive) {
|
|
8858
8983
|
this.#deferredAutoContinueDuringTransition = () =>
|
|
@@ -8862,14 +8987,16 @@ export class AgentSession {
|
|
|
8862
8987
|
resourceRunId,
|
|
8863
8988
|
deferredSelectionFenceGeneration,
|
|
8864
8989
|
deferredPredecessorAgentEnd,
|
|
8865
|
-
|
|
8990
|
+
sdkOwnership,
|
|
8866
8991
|
);
|
|
8867
8992
|
return;
|
|
8868
8993
|
}
|
|
8869
8994
|
const scheduledGeneration = generation;
|
|
8870
|
-
const
|
|
8871
|
-
|
|
8872
|
-
|
|
8995
|
+
const scheduledSdkOwnership =
|
|
8996
|
+
sdkOwnership ??
|
|
8997
|
+
this.#captureSdkContinuationOwnership(
|
|
8998
|
+
(deferredPredecessorAgentEnd as (AgentSessionEvent & { scope?: AttemptScope }) | undefined)?.scope,
|
|
8999
|
+
);
|
|
8873
9000
|
const continuationAuthorized = async (
|
|
8874
9001
|
signal: AbortSignal,
|
|
8875
9002
|
hasPendingNextTurnMessages = false,
|
|
@@ -8926,7 +9053,7 @@ export class AgentSession {
|
|
|
8926
9053
|
resourceRunId,
|
|
8927
9054
|
selectionFenceGeneration,
|
|
8928
9055
|
predecessorAgentEnd,
|
|
8929
|
-
|
|
9056
|
+
scheduledSdkOwnership,
|
|
8930
9057
|
);
|
|
8931
9058
|
} finally {
|
|
8932
9059
|
this.#endSelectionFenceDeferralTracking(selectionFenceGeneration);
|
|
@@ -8974,7 +9101,9 @@ export class AgentSession {
|
|
|
8974
9101
|
promptText,
|
|
8975
9102
|
{
|
|
8976
9103
|
skipPostPromptRecoveryWait: true,
|
|
8977
|
-
sdkRunToken:
|
|
9104
|
+
sdkRunToken: scheduledSdkOwnership?.token,
|
|
9105
|
+
sdkRunTokens: scheduledSdkOwnership?.cohort,
|
|
9106
|
+
sdkPredecessorScope: scheduledSdkOwnership?.scope,
|
|
8978
9107
|
skipCompactionCheck: true,
|
|
8979
9108
|
predecessorAgentEndHold,
|
|
8980
9109
|
onFinalPreflight: ({ hasPendingNextTurnMessages }) =>
|
|
@@ -9910,7 +10039,12 @@ export class AgentSession {
|
|
|
9910
10039
|
type: "agent_start",
|
|
9911
10040
|
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
9912
10041
|
...(deliveryScope
|
|
9913
|
-
? {
|
|
10042
|
+
? {
|
|
10043
|
+
sdkRunTokens: this.#sdkRunCohortsByAttemptScope.get(deliveryScope as AttemptScope),
|
|
10044
|
+
lifecycleScope:
|
|
10045
|
+
this.#lifecycleScopesByAttemptScope.get(deliveryScope as AttemptScope) ??
|
|
10046
|
+
(deliveryScope as AttemptScope),
|
|
10047
|
+
}
|
|
9914
10048
|
: {}),
|
|
9915
10049
|
},
|
|
9916
10050
|
undefined,
|
|
@@ -9928,17 +10062,19 @@ export class AgentSession {
|
|
|
9928
10062
|
deliveryScope,
|
|
9929
10063
|
);
|
|
9930
10064
|
} else if (event.type === "agent_end") {
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
);
|
|
10065
|
+
const extensionEvent = {
|
|
10066
|
+
type: "agent_end" as const,
|
|
10067
|
+
messages: event.messages,
|
|
10068
|
+
stopReason: event.stopReason,
|
|
10069
|
+
maintenanceOutcome: event.maintenanceOutcome,
|
|
10070
|
+
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
10071
|
+
};
|
|
10072
|
+
// Keep the Agent-claimed owner OUTSIDE the public extension event and
|
|
10073
|
+
// its replaceable side channel. Only the owning session may attest
|
|
10074
|
+
// this exact projected event to the SDK bus.
|
|
10075
|
+
const terminalOwner = getAgentTerminalOwnerContext(event);
|
|
10076
|
+
if (terminalOwner) this.#terminalOwnerByExtensionEvent.set(extensionEvent, terminalOwner);
|
|
10077
|
+
await this.#extensionRunner.emit(extensionEvent, undefined, deliveryScope);
|
|
9942
10078
|
} else if (event.type === "turn_start") {
|
|
9943
10079
|
const hookEvent: TurnStartEvent = {
|
|
9944
10080
|
type: "turn_start",
|
|
@@ -10145,6 +10281,22 @@ export class AgentSession {
|
|
|
10145
10281
|
this.#abortActiveMidRunBarriers();
|
|
10146
10282
|
this.#compactionQueuedDeliveryArmed = false;
|
|
10147
10283
|
if (this.#unsubscribeAgent) {
|
|
10284
|
+
// This accepted cohort cannot observe its terminal after the bridge is
|
|
10285
|
+
// removed. Reject only that captured owner; queued roots and already
|
|
10286
|
+
// publishing terminals retain their independently owned publication.
|
|
10287
|
+
const ownership = this.#captureSdkContinuationOwnership(this.#activeAttemptScope);
|
|
10288
|
+
if (ownership) {
|
|
10289
|
+
const failure = Object.assign(
|
|
10290
|
+
new Error("Agent event bridge disconnected before SDK terminal publication."),
|
|
10291
|
+
{ code: "cancelled" },
|
|
10292
|
+
);
|
|
10293
|
+
for (const token of ownership.cohort) {
|
|
10294
|
+
const publication = this.#sdkTerminalPublications.get(token);
|
|
10295
|
+
if (!publication || this.#publishingSdkTerminals.has(publication)) continue;
|
|
10296
|
+
publication.reject(failure);
|
|
10297
|
+
this.#sdkTerminalPublications.delete(token);
|
|
10298
|
+
}
|
|
10299
|
+
}
|
|
10148
10300
|
this.#unsubscribeAgent();
|
|
10149
10301
|
this.#unsubscribeAgent = undefined;
|
|
10150
10302
|
}
|
|
@@ -10302,6 +10454,11 @@ export class AgentSession {
|
|
|
10302
10454
|
// settle every accepted SDK handle now so consumed submissions cannot wait
|
|
10303
10455
|
// forever for an agent_end that teardown deliberately suppresses.
|
|
10304
10456
|
this.#terminalizeQueuedSdkWorkForSessionTransition(this.#queuedMessagesForSessionTransition());
|
|
10457
|
+
const publicationFailure = Object.assign(new Error("Session disposed before SDK terminal publication."), {
|
|
10458
|
+
code: "cancelled",
|
|
10459
|
+
});
|
|
10460
|
+
for (const publication of this.#sdkTerminalPublications.values()) publication.reject(publicationFailure);
|
|
10461
|
+
this.#sdkTerminalPublications.clear();
|
|
10305
10462
|
// Invalidate every coordinator event admitted before disposal. Handlers may
|
|
10306
10463
|
// still unwind, but their captured generation can no longer enqueue a write.
|
|
10307
10464
|
this.#coordinatorPersistGeneration += 1;
|
|
@@ -10671,6 +10828,11 @@ export class AgentSession {
|
|
|
10671
10828
|
* at `timeoutMs` so a wedged subprocess can't stall process exit.
|
|
10672
10829
|
*/
|
|
10673
10830
|
async disposeChildSubprocesses(timeoutMs = SIGNAL_TEARDOWN_TIMEOUT_MS): Promise<void> {
|
|
10831
|
+
this.#evalExecutionDisposing = true;
|
|
10832
|
+
this.abortEval();
|
|
10833
|
+
const evalExecutionsSettled = this.#waitForEvalExecutionsToSettle(timeoutMs).then(settled => {
|
|
10834
|
+
if (!settled) logger.warn("signal teardown: active eval executions remain unsettled");
|
|
10835
|
+
});
|
|
10674
10836
|
const sessionId = this.sessionManager.getSessionId();
|
|
10675
10837
|
const kernelOwnerId = this.#evalKernelOwnerId;
|
|
10676
10838
|
this.#unregisterResourceGc?.();
|
|
@@ -10680,6 +10842,7 @@ export class AgentSession {
|
|
|
10680
10842
|
this.#unregisterDelegationHintSettings?.();
|
|
10681
10843
|
this.#unregisterDelegationHintSettings = undefined;
|
|
10682
10844
|
const work = Promise.allSettled([
|
|
10845
|
+
evalExecutionsSettled,
|
|
10683
10846
|
// kill:true so a forced exit also reaps spawned-app Chrome we own (headless
|
|
10684
10847
|
// always closes; connected/attached browsers only disconnect — never killed).
|
|
10685
10848
|
releaseTabsForOwner(sessionId, { kill: true }).catch((error: unknown) =>
|
|
@@ -10708,7 +10871,8 @@ export class AgentSession {
|
|
|
10708
10871
|
logger.warn("signal teardown: tool session transition cleanups failed", { error }),
|
|
10709
10872
|
),
|
|
10710
10873
|
]);
|
|
10711
|
-
await Promise.race([work, Bun.sleep(timeoutMs)]);
|
|
10874
|
+
const completed = await Promise.race([work.then(() => true), Bun.sleep(timeoutMs).then(() => false)]);
|
|
10875
|
+
if (!completed) logger.warn("signal teardown: cleanup exceeded its bounded caller deadline");
|
|
10712
10876
|
}
|
|
10713
10877
|
|
|
10714
10878
|
#rebindProviderSessionState(providerSessionState: Map<string, ProviderSessionState>): void {
|
|
@@ -11670,7 +11834,9 @@ export class AgentSession {
|
|
|
11670
11834
|
: undefined;
|
|
11671
11835
|
const commandContent = command
|
|
11672
11836
|
? [{ type: "content" as const, content: { type: "text" as const, text: `$ ${command}` } }]
|
|
11673
|
-
:
|
|
11837
|
+
: target.name === "github"
|
|
11838
|
+
? [{ type: "content" as const, content: { type: "text" as const, text: permissionIntent.title } }]
|
|
11839
|
+
: undefined;
|
|
11674
11840
|
if (this.#sdkPermissionMode === "allow") {
|
|
11675
11841
|
return await target.execute(toolCallId, args as never, signal, onUpdate, ctx);
|
|
11676
11842
|
}
|
|
@@ -11783,7 +11949,7 @@ export class AgentSession {
|
|
|
11783
11949
|
/**
|
|
11784
11950
|
/** Wrap a tool with the workflow mutation guard before permissions or execution. */
|
|
11785
11951
|
#wrapToolForWorkflowMutationGuard<T extends AgentTool>(tool: T): T {
|
|
11786
|
-
if (!
|
|
11952
|
+
if (!isWorkflowMutationTool(tool.name)) return tool;
|
|
11787
11953
|
return new Proxy(tool, {
|
|
11788
11954
|
get: (target, prop) => {
|
|
11789
11955
|
if (prop !== "execute") return Reflect.get(target, prop, target);
|
|
@@ -12357,7 +12523,9 @@ export class AgentSession {
|
|
|
12357
12523
|
*/
|
|
12358
12524
|
get messages(): AgentMessage[] {
|
|
12359
12525
|
const messages = this.agent.state.messages;
|
|
12360
|
-
return messages.some(
|
|
12526
|
+
return messages.some(
|
|
12527
|
+
message => message.role === "custom" && this.#isEphemeralCustomMessageType(message.customType),
|
|
12528
|
+
)
|
|
12361
12529
|
? this.#withoutEphemeralCustomMessages(messages)
|
|
12362
12530
|
: messages;
|
|
12363
12531
|
}
|
|
@@ -13498,6 +13666,36 @@ export class AgentSession {
|
|
|
13498
13666
|
}
|
|
13499
13667
|
}
|
|
13500
13668
|
|
|
13669
|
+
/**
|
|
13670
|
+
* The loaded skill a prompt's text dispatches to, or undefined when `prompt`
|
|
13671
|
+
* would submit it as ordinary text (unknown or disabled `/skill:` names,
|
|
13672
|
+
* chains, or attached images). Single source for dispatch and for callers
|
|
13673
|
+
* that must mirror it, such as startup title seeding.
|
|
13674
|
+
*/
|
|
13675
|
+
resolvePromptSkillInvocation(text: string, images?: readonly unknown[]): ParsedSkillInvocation | undefined {
|
|
13676
|
+
if (images?.length || !text.startsWith("/") || !isNamespacedSkillSlashCommandName(text.slice(1))) return;
|
|
13677
|
+
const skillCommands = new Map(this.skills.map(skill => [getSkillSlashCommandName(skill), skill]));
|
|
13678
|
+
const invocations = parseSkillInvocations(text, skillCommands);
|
|
13679
|
+
return invocations.length === 1 ? invocations[0] : undefined;
|
|
13680
|
+
}
|
|
13681
|
+
|
|
13682
|
+
/**
|
|
13683
|
+
* Whether `prompt` intercepts this slash text as an extension or custom/MCP
|
|
13684
|
+
* command. Used to keep command arguments away from the title model. Mirrors the
|
|
13685
|
+
* command lookup in `#tryExecuteExtensionCommand` / `#tryExecuteCustomCommand`
|
|
13686
|
+
* without executing anything.
|
|
13687
|
+
*/
|
|
13688
|
+
isLocallyHandledSlashCommand(text: string): boolean {
|
|
13689
|
+
if (!text.startsWith("/")) return false;
|
|
13690
|
+
const spaceIndex = text.indexOf(" ");
|
|
13691
|
+
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
13692
|
+
return (
|
|
13693
|
+
this.#extensionRunner?.getCommand(commandName) !== undefined ||
|
|
13694
|
+
this.#customCommands.some(c => c.command.name === commandName) ||
|
|
13695
|
+
this.#mcpPromptCommands.some(c => c.command.name === commandName)
|
|
13696
|
+
);
|
|
13697
|
+
}
|
|
13698
|
+
|
|
13501
13699
|
async #promptInternal(
|
|
13502
13700
|
text: string,
|
|
13503
13701
|
options: PromptOptions | undefined,
|
|
@@ -13519,38 +13717,29 @@ export class AgentSession {
|
|
|
13519
13717
|
if (owner && !owner.released) throw this.#sessionAdmissionBusyError();
|
|
13520
13718
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
13521
13719
|
|
|
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
|
-
}
|
|
13720
|
+
if (expandPromptTemplates) {
|
|
13721
|
+
const invocation = this.resolvePromptSkillInvocation(text, options?.images);
|
|
13722
|
+
if (invocation) {
|
|
13723
|
+
await this.invokeSkill(
|
|
13724
|
+
invocation.skill.name,
|
|
13725
|
+
invocation.args,
|
|
13726
|
+
options?.onPreflightAccepted ||
|
|
13727
|
+
options?.onPreflightAcceptCommit ||
|
|
13728
|
+
options?.preflightSignal ||
|
|
13729
|
+
internalOptions?.sdkRunToken
|
|
13730
|
+
? {
|
|
13731
|
+
...(options?.onPreflightAccepted
|
|
13732
|
+
? { onPreflightAccepted: options.onPreflightAccepted }
|
|
13733
|
+
: {}),
|
|
13734
|
+
...(options?.onPreflightAcceptCommit
|
|
13735
|
+
? { onPreflightAcceptCommit: options.onPreflightAcceptCommit }
|
|
13736
|
+
: {}),
|
|
13737
|
+
...(options?.preflightSignal ? { preflightSignal: options.preflightSignal } : {}),
|
|
13738
|
+
...(options?.sdkRunCapability ? { sdkRunCapability: options.sdkRunCapability } : {}),
|
|
13739
|
+
}
|
|
13740
|
+
: undefined,
|
|
13741
|
+
);
|
|
13742
|
+
return;
|
|
13554
13743
|
}
|
|
13555
13744
|
}
|
|
13556
13745
|
|
|
@@ -13967,6 +14156,8 @@ export class AgentSession {
|
|
|
13967
14156
|
| "preflightSignal"
|
|
13968
14157
|
> & {
|
|
13969
14158
|
sdkRunToken?: string;
|
|
14159
|
+
sdkRunTokens?: string[];
|
|
14160
|
+
sdkPredecessorScope?: AttemptScope;
|
|
13970
14161
|
prependMessages?: AgentMessage[];
|
|
13971
14162
|
skipPostPromptRecoveryWait?: boolean;
|
|
13972
14163
|
predecessorAgentEndHold?: symbol;
|
|
@@ -14037,6 +14228,7 @@ export class AgentSession {
|
|
|
14037
14228
|
let skipPostPromptRecoveryWait =
|
|
14038
14229
|
options?.skipPostPromptRecoveryWait === true || options?.sdkRunToken !== undefined;
|
|
14039
14230
|
let promptAttemptScope: AttemptScope | undefined;
|
|
14231
|
+
let promptSdkPublication: Promise<void> | undefined;
|
|
14040
14232
|
let hindsightRecall: string | undefined;
|
|
14041
14233
|
try {
|
|
14042
14234
|
this.#throwIfPromptPreflightCancelled(generation, preflightSignal);
|
|
@@ -14337,10 +14529,26 @@ export class AgentSession {
|
|
|
14337
14529
|
...(options?.sdkRunToken ? { sdkRunToken: options.sdkRunToken } : {}),
|
|
14338
14530
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
14339
14531
|
promptAttemptScope = handle?.scope;
|
|
14340
|
-
|
|
14532
|
+
// A synthetic recovery prompt can make Agent retire the old managed
|
|
14533
|
+
// run at method entry. Suppress only that captured predecessor once
|
|
14534
|
+
// the replacement accepts; pre-acceptance failures still publish it.
|
|
14535
|
+
if (handle && options?.sdkPredecessorScope) {
|
|
14536
|
+
const predecessor = this.#claimDeferredAgentEndForContinuation(
|
|
14537
|
+
this.#reserveDeferredAgentEndForContinuation(options.sdkPredecessorScope),
|
|
14538
|
+
);
|
|
14539
|
+
this.#releaseDeferredAgentEndLease(predecessor);
|
|
14540
|
+
}
|
|
14541
|
+
if (handle)
|
|
14542
|
+
this.#acceptSdkAttemptRun(
|
|
14543
|
+
handle,
|
|
14544
|
+
options?.sdkRunToken,
|
|
14545
|
+
options?.sdkRunTokens,
|
|
14546
|
+
options?.sdkPredecessorScope,
|
|
14547
|
+
);
|
|
14341
14548
|
else this.#acceptRunHandle(handle);
|
|
14342
14549
|
if (handle && options?.sdkRunToken) {
|
|
14343
14550
|
this.#activeSdkRunToken = options.sdkRunToken;
|
|
14551
|
+
promptSdkPublication = this.#sdkTerminalPublications.get(options.sdkRunToken)?.promise;
|
|
14344
14552
|
}
|
|
14345
14553
|
options?.onRunAccepted?.(handle);
|
|
14346
14554
|
options?.admissionLease?.release();
|
|
@@ -14423,7 +14631,11 @@ export class AgentSession {
|
|
|
14423
14631
|
this.#releaseIrcRosterClaim(rosterClaim.token, rosterClaim.epoch);
|
|
14424
14632
|
}
|
|
14425
14633
|
this.#releaseDeferredAgentEndContinuation(predecessorAgentEndHold);
|
|
14426
|
-
await this.#settleEndedInFlight(
|
|
14634
|
+
await this.#settleEndedInFlight(
|
|
14635
|
+
inFlightPrompt,
|
|
14636
|
+
skipPostPromptRecoveryWait ? "publication" : "full",
|
|
14637
|
+
promptSdkPublication,
|
|
14638
|
+
);
|
|
14427
14639
|
}
|
|
14428
14640
|
}
|
|
14429
14641
|
|
|
@@ -14711,10 +14923,7 @@ export class AgentSession {
|
|
|
14711
14923
|
// prompt still in preflight, aborting) has no run to steer. Ordinary steers
|
|
14712
14924
|
// become a sequential follow-up owned by the next turn; SDK turn-bound
|
|
14713
14925
|
// steers fail closed rather than being delivered to a successor run.
|
|
14714
|
-
if (
|
|
14715
|
-
options?.expectedSdkRunToken !== undefined &&
|
|
14716
|
-
this.#activeSdkRunToken !== options.expectedSdkRunToken
|
|
14717
|
-
)
|
|
14926
|
+
if (options?.expectedSdkRunToken !== undefined && this.#activeSdkRunToken !== options.expectedSdkRunToken)
|
|
14718
14927
|
throw Object.assign(new Error("The expected SDK run is not active."), { code: "turn_not_active" });
|
|
14719
14928
|
const admission = this.agent.steer(message, options?.forceOneAtATime ? { forceOneAtATime: true } : undefined);
|
|
14720
14929
|
if (!admission.admitted) {
|
|
@@ -14771,8 +14980,8 @@ export class AgentSession {
|
|
|
14771
14980
|
/**
|
|
14772
14981
|
* One-shot preflight-abort binding: an invocation cancelled after its queue
|
|
14773
14982
|
* admission must cancel the submission it admitted, so an aborted dispatch
|
|
14774
|
-
* can never execute later.
|
|
14775
|
-
*
|
|
14983
|
+
* can never execute later. Explicit and implicitly diverted queue admissions
|
|
14984
|
+
* share this helper so their cancellation semantics cannot diverge
|
|
14776
14985
|
* (exact-head review P1).
|
|
14777
14986
|
*/
|
|
14778
14987
|
#bindPreflightAbortCancellation(
|
|
@@ -15453,6 +15662,7 @@ export class AgentSession {
|
|
|
15453
15662
|
forceOneAtATime: sequential !== undefined,
|
|
15454
15663
|
createDisplayEntry: false,
|
|
15455
15664
|
trackExternalFollowUp: false,
|
|
15665
|
+
sdkRunToken: this.#activeSdkRunToken,
|
|
15456
15666
|
});
|
|
15457
15667
|
if (this.#abortUnwind && !sequential) this.#abortUnwindSteerFallbacks.push(appMessage);
|
|
15458
15668
|
// The chip now describes follow-up work: keep its mode and identity
|
|
@@ -15483,6 +15693,7 @@ export class AgentSession {
|
|
|
15483
15693
|
forceOneAtATime: options?.followUpQueuePolicy === "sequential",
|
|
15484
15694
|
createDisplayEntry: false,
|
|
15485
15695
|
trackExternalFollowUp: false,
|
|
15696
|
+
sdkRunToken: this.#activeSdkRunToken,
|
|
15486
15697
|
});
|
|
15487
15698
|
this.#bindCustomDisplayEntry(appMessage, "followUp");
|
|
15488
15699
|
// The session can report streaming while no agent loop owns the queue
|
|
@@ -15904,12 +16115,13 @@ export class AgentSession {
|
|
|
15904
16115
|
if (this.#isLiveTurnBusy() && !waitedForAbortUnwind) {
|
|
15905
16116
|
if (options?.onPreflightAcceptCommit) await options.onPreflightAcceptCommit();
|
|
15906
16117
|
assertPreflightStillOpen();
|
|
15907
|
-
await this.#queueSteer(text, images, {
|
|
16118
|
+
const queuedSteer = await this.#queueSteer(text, images, {
|
|
15908
16119
|
claimsGenuineUserIntent: true,
|
|
15909
|
-
onPromoted:
|
|
16120
|
+
onPromoted: onQueuedPromoted,
|
|
15910
16121
|
external: true,
|
|
15911
16122
|
sdkRunToken: internalOptions?.sdkRunToken,
|
|
15912
16123
|
});
|
|
16124
|
+
this.#bindPreflightAbortCancellation(options?.preflightSignal, queuedSteer);
|
|
15913
16125
|
// Dispatch-race disposition (#4668 review P1): the SDK snapshot-decided
|
|
15914
16126
|
// this submission starts its own turn (idle at dispatch), but the
|
|
15915
16127
|
// session began streaming before sendUserMessage ran, so the message
|
|
@@ -15919,8 +16131,15 @@ export class AgentSession {
|
|
|
15919
16131
|
// would terminalize the accepted request as an own-run completion
|
|
15920
16132
|
// before it is consumed. Report the internal in-run disposition so the
|
|
15921
16133
|
// runtime attaches the correlation to the in-flight run instead.
|
|
15922
|
-
|
|
15923
|
-
|
|
16134
|
+
try {
|
|
16135
|
+
assertPreflightStillOpen();
|
|
16136
|
+
options?.onDispatchDisposition?.({ startsOwnRun: false });
|
|
16137
|
+
options?.onPreflightAccepted?.();
|
|
16138
|
+
assertPreflightStillOpen();
|
|
16139
|
+
} catch (error) {
|
|
16140
|
+
queuedSteer.cancel();
|
|
16141
|
+
throw error;
|
|
16142
|
+
}
|
|
15924
16143
|
return;
|
|
15925
16144
|
}
|
|
15926
16145
|
|
|
@@ -16851,6 +17070,12 @@ export class AgentSession {
|
|
|
16851
17070
|
if (!lineageIdHash) return undefined;
|
|
16852
17071
|
return this.#promptGeneration;
|
|
16853
17072
|
}
|
|
17073
|
+
getTerminalRunOwnerForEvent(event: object): AgentTerminalOwnerContext | undefined {
|
|
17074
|
+
return this.#terminalOwnerByExtensionEvent.get(event);
|
|
17075
|
+
}
|
|
17076
|
+
getRunOwnerDomain(handle: string): RunCancellationDomain | undefined {
|
|
17077
|
+
return this.agent.resourceLedger.lookupDomain(handle);
|
|
17078
|
+
}
|
|
16854
17079
|
/**
|
|
16855
17080
|
* Logical endpoint used to key owned-registration lineage bindings.
|
|
16856
17081
|
* Subagents inherit the parent's AsyncJobManager, whose completion callback
|
|
@@ -18009,8 +18234,12 @@ export class AgentSession {
|
|
|
18009
18234
|
}
|
|
18010
18235
|
|
|
18011
18236
|
options?.onMutationStarted?.();
|
|
18012
|
-
|
|
18237
|
+
const cause = options?.cause ?? "user-selection";
|
|
18238
|
+
if (cause === "user-selection") this.markUserModelSelection();
|
|
18239
|
+
this.#setModelAuthoritatively(model, cause);
|
|
18240
|
+
if (cause === "user-selection") this.#unavailableModelProfile = undefined;
|
|
18013
18241
|
this.#seedSessionCanonicalVariant(model);
|
|
18242
|
+
if (cause === "user-selection") this.#recordUserCanonicalVariantSelection();
|
|
18014
18243
|
this.sessionManager.appendModelChange(`${model.provider}/${model.id}`, role);
|
|
18015
18244
|
this.settings.setModelRole(
|
|
18016
18245
|
role,
|
|
@@ -18018,7 +18247,7 @@ export class AgentSession {
|
|
|
18018
18247
|
);
|
|
18019
18248
|
// Only an explicit user selection starts a new fallback epoch. Internal
|
|
18020
18249
|
// fallback switches must preserve the exhausted-model set while advancing.
|
|
18021
|
-
if (role === "default" &&
|
|
18250
|
+
if (role === "default" && cause === "user-selection") {
|
|
18022
18251
|
this.#fallbackTransitionGeneration++;
|
|
18023
18252
|
this.#defaultFallbackController = undefined;
|
|
18024
18253
|
this.#defaultFallbackExhaustedLastTurn = false;
|
|
@@ -18055,12 +18284,41 @@ export class AgentSession {
|
|
|
18055
18284
|
|
|
18056
18285
|
setActiveModelProfile(name: string | undefined): void {
|
|
18057
18286
|
this.#activeModelProfile = name;
|
|
18287
|
+
if (name !== undefined) {
|
|
18288
|
+
this.#unavailableModelProfile = undefined;
|
|
18289
|
+
}
|
|
18058
18290
|
}
|
|
18059
18291
|
|
|
18060
18292
|
getActiveModelProfile(): string | undefined {
|
|
18061
18293
|
return this.#activeModelProfile;
|
|
18062
18294
|
}
|
|
18063
18295
|
|
|
18296
|
+
setUnavailableModelProfile(name: string | undefined): void {
|
|
18297
|
+
this.#unavailableModelProfile = name;
|
|
18298
|
+
}
|
|
18299
|
+
|
|
18300
|
+
getUnavailableModelProfile(): string | undefined {
|
|
18301
|
+
return this.#unavailableModelProfile;
|
|
18302
|
+
}
|
|
18303
|
+
|
|
18304
|
+
/** Revision fence for deferred activation after a user model selection. */
|
|
18305
|
+
getUserModelSelectionRevision(): number {
|
|
18306
|
+
return this.#userModelSelectionRevision;
|
|
18307
|
+
}
|
|
18308
|
+
|
|
18309
|
+
/** Latest concrete user choice that seeded or cleared the session's sticky canonical variant. */
|
|
18310
|
+
getUserCanonicalVariantSelection(): { revision: number; canonicalVariant: string | undefined } {
|
|
18311
|
+
return {
|
|
18312
|
+
revision: this.#userCanonicalVariantSelectionRevision,
|
|
18313
|
+
canonicalVariant: this.#userCanonicalVariantSelection,
|
|
18314
|
+
};
|
|
18315
|
+
}
|
|
18316
|
+
|
|
18317
|
+
/** Fence deferred startup profile recovery before an explicit control-surface selection. */
|
|
18318
|
+
markUserModelSelection(): void {
|
|
18319
|
+
this.#userModelSelectionRevision++;
|
|
18320
|
+
}
|
|
18321
|
+
|
|
18064
18322
|
/**
|
|
18065
18323
|
* Re-apply vendor-separated delegation after a profile activation changed the
|
|
18066
18324
|
* role layer. `eagerTasks` is resolved at prompt build time, but under
|
|
@@ -18222,12 +18480,15 @@ export class AgentSession {
|
|
|
18222
18480
|
* Session-scoped only: does not persist `modelProfile.default`.
|
|
18223
18481
|
*/
|
|
18224
18482
|
async activateModelProfileForControl(profileName: string): Promise<boolean> {
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
|
|
18483
|
+
this.markUserModelSelection();
|
|
18484
|
+
await this.withSdkControlMutation(() =>
|
|
18485
|
+
activateModelProfile({
|
|
18486
|
+
session: this,
|
|
18487
|
+
modelRegistry: this.#modelRegistry,
|
|
18488
|
+
settings: this.settings,
|
|
18489
|
+
profileName,
|
|
18490
|
+
}),
|
|
18491
|
+
);
|
|
18231
18492
|
return this.getActiveModelProfile() === profileName;
|
|
18232
18493
|
}
|
|
18233
18494
|
|
|
@@ -18262,6 +18523,7 @@ export class AgentSession {
|
|
|
18262
18523
|
onAfterActivation?: () => void;
|
|
18263
18524
|
},
|
|
18264
18525
|
): Promise<{ changed: boolean; id: string }> {
|
|
18526
|
+
this.markUserModelSelection();
|
|
18265
18527
|
// Do not hold selection admission while waiting for a scheduled continuation:
|
|
18266
18528
|
// the continuation may need prompt admission to settle the current turn.
|
|
18267
18529
|
await this.waitForIdle();
|
|
@@ -18540,6 +18802,7 @@ export class AgentSession {
|
|
|
18540
18802
|
reason?: TemporaryModelReason;
|
|
18541
18803
|
providerSessionScope?: TemporaryProviderSessionScope;
|
|
18542
18804
|
signal?: AbortSignal;
|
|
18805
|
+
shouldMutate?: () => boolean;
|
|
18543
18806
|
onMutationStarted?: () => void;
|
|
18544
18807
|
},
|
|
18545
18808
|
// biome-ignore lint/suspicious/noConfusingVoidType: Existing session adapters return Promise<void>; a scope is optional.
|
|
@@ -18558,6 +18821,8 @@ export class AgentSession {
|
|
|
18558
18821
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
18559
18822
|
}
|
|
18560
18823
|
if (suppliedScope && this.#temporaryProviderSessionScopes.at(-1)?.token !== suppliedScope) return;
|
|
18824
|
+
if (options?.shouldMutate && !options.shouldMutate()) return;
|
|
18825
|
+
if (options?.cause === "user-selection") this.markUserModelSelection();
|
|
18561
18826
|
options?.onMutationStarted?.();
|
|
18562
18827
|
|
|
18563
18828
|
const isTemporaryOperation = options?.cause === undefined || options.cause === "temporary-operation";
|
|
@@ -18584,6 +18849,9 @@ export class AgentSession {
|
|
|
18584
18849
|
} else {
|
|
18585
18850
|
this.#setModelAuthoritatively(model, options?.cause ?? "temporary-operation");
|
|
18586
18851
|
}
|
|
18852
|
+
if (options?.cause === "user-selection") {
|
|
18853
|
+
this.#unavailableModelProfile = undefined;
|
|
18854
|
+
}
|
|
18587
18855
|
this.sessionManager.appendModelChange(
|
|
18588
18856
|
`${model.provider}/${model.id}`,
|
|
18589
18857
|
options?.persistAsSessionDefault ? "default" : "temporary",
|
|
@@ -18591,12 +18859,36 @@ export class AgentSession {
|
|
|
18591
18859
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
18592
18860
|
if (options?.persistAsSessionDefault) {
|
|
18593
18861
|
this.#seedSessionCanonicalVariant(model);
|
|
18862
|
+
if (options.cause === "user-selection") this.#recordUserCanonicalVariantSelection();
|
|
18594
18863
|
}
|
|
18595
18864
|
|
|
18596
18865
|
// Apply explicit thinking level if given; otherwise prefer the model's
|
|
18597
18866
|
// configured defaultLevel; otherwise re-clamp the current level.
|
|
18598
18867
|
this.setThinkingLevel(thinkingLevel ?? model.thinking?.defaultLevel ?? this.thinkingLevel);
|
|
18599
|
-
if (options?.persistAsSessionDefault === true
|
|
18868
|
+
if (options?.persistAsSessionDefault === true && options.cause !== "profile-activation") {
|
|
18869
|
+
// Concrete model selection clears session-scoped profile state (#5919).
|
|
18870
|
+
// Materialize durable profiles first (if persisted and active), then reset
|
|
18871
|
+
// without force to preserve durable semantics.
|
|
18872
|
+
if (this.model && this.settings.get("modelProfile.default") !== undefined) {
|
|
18873
|
+
this.materializeActiveDefaultModelProfileAssignment(this.model);
|
|
18874
|
+
}
|
|
18875
|
+
this.#resetSessionScopedModelProfileState({ preserveDefaultConfiguredChain: true });
|
|
18876
|
+
// For user-selection and startup-override causes, also clear stale persisted
|
|
18877
|
+
// defaults via the legacy path when there is no ownership record.
|
|
18878
|
+
if (options.cause === "user-selection" || options.cause === "startup-override") {
|
|
18879
|
+
const ownership = readDurableModelProfileOwnership(this.settings);
|
|
18880
|
+
if (ownership.version === 0) {
|
|
18881
|
+
this.#clearActiveModelProfileForConcreteDefault(options.cause);
|
|
18882
|
+
}
|
|
18883
|
+
}
|
|
18884
|
+
const origin = options.cause === "startup-override" ? "startup-override" : "model_selection";
|
|
18885
|
+
const effectiveLevel = thinkingLevel ?? model.thinking?.defaultLevel ?? this.thinkingLevel;
|
|
18886
|
+
this.setConfiguredModelChain(
|
|
18887
|
+
"default",
|
|
18888
|
+
[formatModelSelectorValue(`${model.provider}/${model.id}`, effectiveLevel)],
|
|
18889
|
+
origin,
|
|
18890
|
+
);
|
|
18891
|
+
}
|
|
18600
18892
|
await this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
18601
18893
|
} catch (error) {
|
|
18602
18894
|
if (ownsScope) await this.restoreTemporaryProviderSessionScope(scope);
|
|
@@ -18687,8 +18979,15 @@ export class AgentSession {
|
|
|
18687
18979
|
|
|
18688
18980
|
#publishDefaultModelSelection(model: Model, thinkingLevel: ThinkingLevel, systemPrompt: string[] | undefined): void {
|
|
18689
18981
|
this.#clearActiveRetryFallback();
|
|
18982
|
+
// Concrete model selection clears session-scoped profile state (#5919).
|
|
18983
|
+
// IMPORTANT: This is called AFTER durable persistence completes, so the
|
|
18984
|
+
// session-scoped updates occur during the promotion phase after ownership
|
|
18985
|
+
// is committed. Session ownership is captured in the promotion logic above.
|
|
18986
|
+
this.#resetSessionScopedModelProfileState({ preserveDefaultConfiguredChain: true });
|
|
18987
|
+
this.markUserModelSelection();
|
|
18690
18988
|
this.#setModelWithProviderSessionReset(model);
|
|
18691
18989
|
this.#seedSessionCanonicalVariant(model);
|
|
18990
|
+
this.#recordUserCanonicalVariantSelection();
|
|
18692
18991
|
const thinkingLevelChanged = this.#thinkingLevel !== thinkingLevel;
|
|
18693
18992
|
this.#thinkingLevelMutationRevision++;
|
|
18694
18993
|
this.#thinkingLevelLiveMutationRevision++;
|
|
@@ -18698,17 +18997,21 @@ export class AgentSession {
|
|
|
18698
18997
|
this.#pendingThinkingVisibilityControlFailure = undefined;
|
|
18699
18998
|
this.#thinkingLevel = thinkingLevel;
|
|
18700
18999
|
this.agent.setThinkingLevel(toReasoningEffort(thinkingLevel));
|
|
19000
|
+
// Materialize durable profiles after model and thinking level installation
|
|
19001
|
+
// so the persisted default selector matches both the newly installed model
|
|
19002
|
+
// and the selected thinking level.
|
|
19003
|
+
if (this.model && this.settings.get("modelProfile.default") !== undefined) {
|
|
19004
|
+
this.materializeActiveDefaultModelProfileAssignment(this.model);
|
|
19005
|
+
}
|
|
18701
19006
|
if (thinkingLevelChanged) {
|
|
18702
19007
|
const event: AgentSessionEvent = { type: "thinking_level_changed", thinkingLevel };
|
|
18703
19008
|
for (const listener of this.#eventListenerSnapshot) {
|
|
18704
|
-
|
|
18705
|
-
listener(event);
|
|
18706
|
-
} catch {
|
|
19009
|
+
deliverSessionEvent(listener, event, () => {
|
|
18707
19010
|
logger.warn("Default model selection event listener failed", {
|
|
18708
19011
|
code: "default_model_selection_listener_failed",
|
|
18709
19012
|
disposition: "continue",
|
|
18710
19013
|
});
|
|
18711
|
-
}
|
|
19014
|
+
});
|
|
18712
19015
|
}
|
|
18713
19016
|
}
|
|
18714
19017
|
this.#applyPreparedDefaultModelSelectionPrompt(systemPrompt);
|
|
@@ -18883,7 +19186,40 @@ export class AgentSession {
|
|
|
18883
19186
|
});
|
|
18884
19187
|
}
|
|
18885
19188
|
}
|
|
18886
|
-
|
|
19189
|
+
// Concrete model selection clears durable profile ownership (#5919).
|
|
19190
|
+
// If there is an existing ownership record (version > 0), clear it.
|
|
19191
|
+
// If there is no ownership record (version === 0), call the legacy path to
|
|
19192
|
+
// clear stale persisted profiles that no longer match the selection.
|
|
19193
|
+
// The ownership clear is attempted atomically with other model role updates.
|
|
19194
|
+
// If it fails due to concurrent access, we log and continue: the model
|
|
19195
|
+
// selection has been promoted and committed, so the concrete pick is durable.
|
|
19196
|
+
// A stale profile ownership marker will be resolved on the next session startup.
|
|
19197
|
+
const currentOwnership = readDurableModelProfileOwnership(this.settings);
|
|
19198
|
+
if (currentOwnership.version > 0) {
|
|
19199
|
+
try {
|
|
19200
|
+
await commitDurableModelProfileOwnership(this.settings, { kind: "cleared" });
|
|
19201
|
+
} catch (error) {
|
|
19202
|
+
if (error instanceof ModelProfileOwnershipConflictError) {
|
|
19203
|
+
logger.info("Model selection ownership clear deferred due to concurrent access", {
|
|
19204
|
+
code: "default_model_selection_ownership_conflict",
|
|
19205
|
+
disposition: "continue",
|
|
19206
|
+
expectedVersion: error.expectedVersion,
|
|
19207
|
+
actualVersion: error.actualVersion,
|
|
19208
|
+
});
|
|
19209
|
+
} else if (error instanceof InvalidModelProfileOwnershipError) {
|
|
19210
|
+
logger.warn("Invalid model profile ownership marker during selection", {
|
|
19211
|
+
code: "default_model_selection_ownership_invalid",
|
|
19212
|
+
disposition: "continue",
|
|
19213
|
+
});
|
|
19214
|
+
} else {
|
|
19215
|
+
throw error;
|
|
19216
|
+
}
|
|
19217
|
+
}
|
|
19218
|
+
} else {
|
|
19219
|
+
// Legacy path: no ownership record exists. Clear stale persisted defaults
|
|
19220
|
+
// that no longer match the concrete selection.
|
|
19221
|
+
this.#clearActiveModelProfileForConcreteDefault("user-selection");
|
|
19222
|
+
}
|
|
18887
19223
|
options?.onAfterMutation?.();
|
|
18888
19224
|
return { provider: model.provider, modelId: model.id, thinkingLevel: effectiveLevel };
|
|
18889
19225
|
},
|
|
@@ -18989,6 +19325,7 @@ export class AgentSession {
|
|
|
18989
19325
|
const next = roleModels[nextIndex];
|
|
18990
19326
|
|
|
18991
19327
|
if (options?.temporary) {
|
|
19328
|
+
this.markUserModelSelection();
|
|
18992
19329
|
await this.setModelTemporary(next.model, next.explicitThinkingLevel ? next.thinkingLevel : undefined, {
|
|
18993
19330
|
cause: "temporary-operation",
|
|
18994
19331
|
reason: "temporary-cycle",
|
|
@@ -20638,6 +20975,7 @@ export class AgentSession {
|
|
|
20638
20975
|
onTerminalOverflowNoop?: () => void,
|
|
20639
20976
|
resourceRunId?: string,
|
|
20640
20977
|
ownershipSignal?: AbortSignal,
|
|
20978
|
+
sdkOwnership?: SdkContinuationOwnership,
|
|
20641
20979
|
): Promise<boolean> {
|
|
20642
20980
|
// Safety stops are terminal and must not trigger context maintenance.
|
|
20643
20981
|
if (
|
|
@@ -20664,7 +21002,28 @@ export class AgentSession {
|
|
|
20664
21002
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
20665
21003
|
const errorIsFromBeforeCompaction =
|
|
20666
21004
|
compactionEntry !== null && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime();
|
|
21005
|
+
// A successful empty stop with nonzero usage is not overflow evidence by usage
|
|
21006
|
+
// alone. Zero-usage empty stops stay on the legacy proxy-overflow path, and typed
|
|
21007
|
+
// overflow classification is preserved; only the empty-stop heuristic is disabled.
|
|
21008
|
+
const hasOverflowEvidenceBeyondEmptyStopHeuristic =
|
|
21009
|
+
assistantMessage.stopReason === "stop" &&
|
|
21010
|
+
assistantMessage.content.length === 0 &&
|
|
21011
|
+
classifyContextOverflow(
|
|
21012
|
+
{ ...assistantMessage, content: [{ type: "text", text: "" }] },
|
|
21013
|
+
assistantMessage.transportFailure,
|
|
21014
|
+
contextWindow,
|
|
21015
|
+
);
|
|
21016
|
+
const emptyStopPromptTokens =
|
|
21017
|
+
assistantMessage.usage.input + assistantMessage.usage.cacheRead + assistantMessage.usage.cacheWrite;
|
|
21018
|
+
const successfulEmptyStop =
|
|
21019
|
+
assistantMessage.stopReason === "stop" &&
|
|
21020
|
+
assistantMessage.content.length === 0 &&
|
|
21021
|
+
(emptyStopPromptTokens + assistantMessage.usage.output > 0 ||
|
|
21022
|
+
assistantMessage.usage.totalTokens > 0) &&
|
|
21023
|
+
emptyStopPromptTokens <= contextWindow &&
|
|
21024
|
+
!hasOverflowEvidenceBeyondEmptyStopHeuristic;
|
|
20667
21025
|
if (
|
|
21026
|
+
!successfulEmptyStop &&
|
|
20668
21027
|
sameModel &&
|
|
20669
21028
|
!errorIsFromBeforeCompaction &&
|
|
20670
21029
|
classifyContextOverflow(assistantMessage, assistantMessage.transportFailure, contextWindow)
|
|
@@ -20692,6 +21051,7 @@ export class AgentSession {
|
|
|
20692
21051
|
generation,
|
|
20693
21052
|
suppressPredecessorAgentEnd: true,
|
|
20694
21053
|
resourceRunId,
|
|
21054
|
+
sdkOwnership,
|
|
20695
21055
|
});
|
|
20696
21056
|
|
|
20697
21057
|
return true;
|
|
@@ -20710,10 +21070,11 @@ export class AgentSession {
|
|
|
20710
21070
|
},
|
|
20711
21071
|
resourceRunId,
|
|
20712
21072
|
signal: ownershipSignal,
|
|
21073
|
+
sdkOwnership,
|
|
20713
21074
|
});
|
|
20714
21075
|
return "continuationScheduled" in status && status.continuationScheduled === true;
|
|
20715
21076
|
}
|
|
20716
|
-
return await this.#scheduleOverflowRetryContinuation(generation, resourceRunId);
|
|
21077
|
+
return await this.#scheduleOverflowRetryContinuation(generation, resourceRunId, sdkOwnership);
|
|
20717
21078
|
}
|
|
20718
21079
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
20719
21080
|
if (!compactionSettings.enabled || compactionSettings.strategy === "off") return false;
|
|
@@ -20759,7 +21120,11 @@ export class AgentSession {
|
|
|
20759
21120
|
const promoted = await this.#tryContextPromotion(assistantMessage, ownershipSignal);
|
|
20760
21121
|
if (ownershipSignal?.aborted) return false;
|
|
20761
21122
|
if (!promoted) {
|
|
20762
|
-
await this.#runAutoCompaction("threshold", false, false, {
|
|
21123
|
+
await this.#runAutoCompaction("threshold", false, false, {
|
|
21124
|
+
resourceRunId,
|
|
21125
|
+
signal: ownershipSignal,
|
|
21126
|
+
sdkOwnership,
|
|
21127
|
+
});
|
|
20763
21128
|
}
|
|
20764
21129
|
}
|
|
20765
21130
|
return true;
|
|
@@ -21113,10 +21478,7 @@ export class AgentSession {
|
|
|
21113
21478
|
const isContextPromoted = this.#temporaryProviderSessionScopes.some(
|
|
21114
21479
|
scope => scope.token.reason === "context-promotion",
|
|
21115
21480
|
);
|
|
21116
|
-
if (
|
|
21117
|
-
!isContextPromoted ||
|
|
21118
|
-
!isDefaultAutoThresholdCeilingApplied(contextWindow, settings)
|
|
21119
|
-
) {
|
|
21481
|
+
if (!isContextPromoted || !isDefaultAutoThresholdCeilingApplied(contextWindow, settings)) {
|
|
21120
21482
|
return settings;
|
|
21121
21483
|
}
|
|
21122
21484
|
|
|
@@ -21525,7 +21887,9 @@ export class AgentSession {
|
|
|
21525
21887
|
// The reminder continues the current prompt, so the predecessor `agent_end`
|
|
21526
21888
|
// must stay held until the continuation turn produces the real terminal.
|
|
21527
21889
|
// Publishing it here would settle the caller's prompt mid-reminder.
|
|
21528
|
-
|
|
21890
|
+
// Disable managed fallback to prevent indefinite retries for this server-initiated turn.
|
|
21891
|
+
this.#todoReminderContinuationGeneration = this.#promptGeneration;
|
|
21892
|
+
this.#scheduleAgentContinue({ skipCompactionCheck: true, suppressPredecessorAgentEnd: true, disableManagedFallback: true });
|
|
21529
21893
|
}
|
|
21530
21894
|
|
|
21531
21895
|
/**
|
|
@@ -21639,6 +22003,11 @@ export class AgentSession {
|
|
|
21639
22003
|
}
|
|
21640
22004
|
}
|
|
21641
22005
|
|
|
22006
|
+
#recordUserCanonicalVariantSelection(): void {
|
|
22007
|
+
this.#userCanonicalVariantSelectionRevision++;
|
|
22008
|
+
this.#userCanonicalVariantSelection = this.#modelRegistry.getSessionCanonicalVariant?.(this.sessionId);
|
|
22009
|
+
}
|
|
22010
|
+
|
|
21642
22011
|
#closeCodexProviderSessionsForHistoryRewrite(): void {
|
|
21643
22012
|
const currentModel = this.model;
|
|
21644
22013
|
if (currentModel?.api !== "openai-codex-responses") return;
|
|
@@ -22242,6 +22611,7 @@ export class AgentSession {
|
|
|
22242
22611
|
signal?: AbortSignal;
|
|
22243
22612
|
beforeTerminalOverflowNoop?: () => void;
|
|
22244
22613
|
resourceRunId?: string;
|
|
22614
|
+
sdkOwnership?: SdkContinuationOwnership;
|
|
22245
22615
|
},
|
|
22246
22616
|
): Promise<AutoCompactionTerminalStatus> {
|
|
22247
22617
|
if (this.#isDisposed || this.#sessionAdmissionClosing) return Promise.resolve({ kind: "skipped" });
|
|
@@ -22262,6 +22632,7 @@ export class AgentSession {
|
|
|
22262
22632
|
signal?: AbortSignal;
|
|
22263
22633
|
beforeTerminalOverflowNoop?: () => void;
|
|
22264
22634
|
resourceRunId?: string;
|
|
22635
|
+
sdkOwnership?: SdkContinuationOwnership;
|
|
22265
22636
|
},
|
|
22266
22637
|
): Promise<AutoCompactionTerminalStatus> {
|
|
22267
22638
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
@@ -22404,7 +22775,14 @@ export class AgentSession {
|
|
|
22404
22775
|
});
|
|
22405
22776
|
if (autoCompactionSignal.aborted) return { kind: "aborted", source: "signal" };
|
|
22406
22777
|
if (continueAfterMaintenance && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
22407
|
-
this.#scheduleAutoContinuePrompt(
|
|
22778
|
+
this.#scheduleAutoContinuePrompt(
|
|
22779
|
+
generation,
|
|
22780
|
+
true,
|
|
22781
|
+
options?.resourceRunId,
|
|
22782
|
+
undefined,
|
|
22783
|
+
undefined,
|
|
22784
|
+
options?.sdkOwnership,
|
|
22785
|
+
);
|
|
22408
22786
|
}
|
|
22409
22787
|
|
|
22410
22788
|
if (autoCompactionSignal.aborted) return { kind: "aborted", source: "signal" };
|
|
@@ -22459,7 +22837,11 @@ export class AgentSession {
|
|
|
22459
22837
|
}
|
|
22460
22838
|
const overflowContinuationScheduled =
|
|
22461
22839
|
!overflowNoopWouldReplay && willRetry && !continuationSkipReason
|
|
22462
|
-
? await this.#scheduleOverflowRetryContinuation(
|
|
22840
|
+
? await this.#scheduleOverflowRetryContinuation(
|
|
22841
|
+
generation,
|
|
22842
|
+
options?.resourceRunId,
|
|
22843
|
+
options?.sdkOwnership,
|
|
22844
|
+
)
|
|
22463
22845
|
: false;
|
|
22464
22846
|
await this.#emitSessionEvent({
|
|
22465
22847
|
type: "auto_compaction_end",
|
|
@@ -22509,7 +22891,14 @@ export class AgentSession {
|
|
|
22509
22891
|
resourceRunId: options?.resourceRunId,
|
|
22510
22892
|
});
|
|
22511
22893
|
} else if (continueAfterMaintenance && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
22512
|
-
this.#scheduleAutoContinuePrompt(
|
|
22894
|
+
this.#scheduleAutoContinuePrompt(
|
|
22895
|
+
generation,
|
|
22896
|
+
true,
|
|
22897
|
+
options?.resourceRunId,
|
|
22898
|
+
undefined,
|
|
22899
|
+
undefined,
|
|
22900
|
+
options?.sdkOwnership,
|
|
22901
|
+
);
|
|
22513
22902
|
}
|
|
22514
22903
|
return { kind: "skipped" };
|
|
22515
22904
|
}
|
|
@@ -22794,7 +23183,11 @@ export class AgentSession {
|
|
|
22794
23183
|
}
|
|
22795
23184
|
const overflowContinuationScheduled =
|
|
22796
23185
|
willRetry && !continuationSkipReason
|
|
22797
|
-
? await this.#scheduleOverflowRetryContinuation(
|
|
23186
|
+
? await this.#scheduleOverflowRetryContinuation(
|
|
23187
|
+
generation,
|
|
23188
|
+
options?.resourceRunId,
|
|
23189
|
+
options?.sdkOwnership,
|
|
23190
|
+
)
|
|
22798
23191
|
: false;
|
|
22799
23192
|
|
|
22800
23193
|
await this.#emitSessionEvent({
|
|
@@ -22828,7 +23221,14 @@ export class AgentSession {
|
|
|
22828
23221
|
resourceRunId: options?.resourceRunId,
|
|
22829
23222
|
});
|
|
22830
23223
|
} else if (continueAfterMaintenance && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
22831
|
-
this.#scheduleAutoContinuePrompt(
|
|
23224
|
+
this.#scheduleAutoContinuePrompt(
|
|
23225
|
+
generation,
|
|
23226
|
+
true,
|
|
23227
|
+
options?.resourceRunId,
|
|
23228
|
+
undefined,
|
|
23229
|
+
undefined,
|
|
23230
|
+
options?.sdkOwnership,
|
|
23231
|
+
);
|
|
22832
23232
|
}
|
|
22833
23233
|
return { kind: "compacted" };
|
|
22834
23234
|
} catch (error) {
|
|
@@ -23021,7 +23421,8 @@ export class AgentSession {
|
|
|
23021
23421
|
isAuthenticated(apiKey) &&
|
|
23022
23422
|
authStorage.getSessionCredentialRowId(model.provider, this.credentialSessionId) === credential.id &&
|
|
23023
23423
|
authStorage.getSessionCredentialType(model.provider, this.credentialSessionId) === credentialKind &&
|
|
23024
|
-
currentCredential &&
|
|
23424
|
+
currentCredential &&
|
|
23425
|
+
!currentCredential.disabled
|
|
23025
23426
|
)
|
|
23026
23427
|
return apiKey;
|
|
23027
23428
|
}
|
|
@@ -23191,6 +23592,7 @@ export class AgentSession {
|
|
|
23191
23592
|
if (message.errorKind === "local_snapshot_failure") return "local_snapshot";
|
|
23192
23593
|
if (message.errorKind === "local_buffer_overflow") return "local_buffer_overflow";
|
|
23193
23594
|
if (this.#isTypedFirstEventTimeout(message)) return "first_event_timeout";
|
|
23595
|
+
if (message.errorKind === "local_empty_response") return "empty_response";
|
|
23194
23596
|
if (this.#isTypedEmptyResponse(message)) return "empty_response";
|
|
23195
23597
|
if (this.#isCodexCredentialModelUnavailable(message)) {
|
|
23196
23598
|
return this.#canRotateCodexCredential(message) ? "unknown" : "terminal";
|
|
@@ -23315,6 +23717,7 @@ export class AgentSession {
|
|
|
23315
23717
|
|
|
23316
23718
|
#managedFallbackPromptOptions(): {
|
|
23317
23719
|
fallbackManaged?: boolean;
|
|
23720
|
+
contextOverflowManaged?: boolean;
|
|
23318
23721
|
nextFallbackAttempt?: (model: Model) => FallbackAttemptToken;
|
|
23319
23722
|
onManagedAttemptAccepted?: () => void;
|
|
23320
23723
|
onManagedAttemptOutcome?: (
|
|
@@ -23322,9 +23725,10 @@ export class AgentSession {
|
|
|
23322
23725
|
) => ManagedAttemptDecision | Promise<ManagedAttemptDecision>;
|
|
23323
23726
|
} {
|
|
23324
23727
|
const controller = this.#defaultFallbackChain();
|
|
23325
|
-
if (controller.chain.entries.length < 2) return {};
|
|
23728
|
+
if (controller.chain.entries.length < 2) return { contextOverflowManaged: true };
|
|
23326
23729
|
return {
|
|
23327
23730
|
fallbackManaged: true,
|
|
23731
|
+
contextOverflowManaged: true,
|
|
23328
23732
|
nextFallbackAttempt: model => {
|
|
23329
23733
|
controller.onAttemptStarted();
|
|
23330
23734
|
this.#managedFallbackProviderAttemptCount++;
|
|
@@ -23445,6 +23849,7 @@ export class AgentSession {
|
|
|
23445
23849
|
}
|
|
23446
23850
|
|
|
23447
23851
|
async #handleManagedAttemptOutcome(outcome: ManagedAttemptOutcome): Promise<ManagedAttemptDecision> {
|
|
23852
|
+
const sdkOwnership = this.#captureSdkContinuationOwnership(outcome.scope);
|
|
23448
23853
|
const attemptAbortEpoch = this.#abortAdmissionEpoch;
|
|
23449
23854
|
const attemptCancelled = () =>
|
|
23450
23855
|
this.#isDisposed || this.#sessionAdmissionClosing || this.#abortAdmissionEpoch !== attemptAbortEpoch;
|
|
@@ -23494,6 +23899,8 @@ export class AgentSession {
|
|
|
23494
23899
|
return {
|
|
23495
23900
|
type: "retry",
|
|
23496
23901
|
continuation: async ownership => {
|
|
23902
|
+
const continuationSdkOwnership =
|
|
23903
|
+
sdkOwnership ?? this.#captureSdkContinuationOwnership(ownership.handle.scope);
|
|
23497
23904
|
const restoreOwnedTransition = async () => {
|
|
23498
23905
|
if (this.#fallbackTransitionGeneration !== transitionGeneration) return;
|
|
23499
23906
|
this.#fallbackTransitionGeneration++;
|
|
@@ -23580,7 +23987,12 @@ export class AgentSession {
|
|
|
23580
23987
|
...this.#managedFallbackPromptOptions(),
|
|
23581
23988
|
transientRecoveryMessage: this.#escapedNonAsciiRecoveryMessage(),
|
|
23582
23989
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
23583
|
-
this.#acceptSdkAttemptRun(
|
|
23990
|
+
this.#acceptSdkAttemptRun(
|
|
23991
|
+
handle,
|
|
23992
|
+
continuationSdkOwnership?.token,
|
|
23993
|
+
continuationSdkOwnership?.cohort,
|
|
23994
|
+
continuationSdkOwnership?.scope,
|
|
23995
|
+
);
|
|
23584
23996
|
},
|
|
23585
23997
|
});
|
|
23586
23998
|
if (
|
|
@@ -23619,11 +24031,18 @@ export class AgentSession {
|
|
|
23619
24031
|
type: "retry",
|
|
23620
24032
|
continuation: async ownership => {
|
|
23621
24033
|
if (attemptCancelled() || !ownership.isCurrent() || ownership.lease.signal.aborted) return;
|
|
24034
|
+
const continuationSdkOwnership =
|
|
24035
|
+
sdkOwnership ?? this.#captureSdkContinuationOwnership(ownership.handle.scope);
|
|
23622
24036
|
await this.agent.continue({
|
|
23623
24037
|
...this.#managedFallbackPromptOptions(),
|
|
23624
24038
|
transientRecoveryMessage: this.#escapedNonAsciiRecoveryMessage(),
|
|
23625
24039
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
23626
|
-
this.#acceptSdkAttemptRun(
|
|
24040
|
+
this.#acceptSdkAttemptRun(
|
|
24041
|
+
handle,
|
|
24042
|
+
continuationSdkOwnership?.token,
|
|
24043
|
+
continuationSdkOwnership?.cohort,
|
|
24044
|
+
continuationSdkOwnership?.scope,
|
|
24045
|
+
);
|
|
23627
24046
|
},
|
|
23628
24047
|
});
|
|
23629
24048
|
},
|
|
@@ -23638,6 +24057,10 @@ export class AgentSession {
|
|
|
23638
24057
|
type: "maintenance",
|
|
23639
24058
|
continuation: async ownership => {
|
|
23640
24059
|
if (attemptCancelled() || !ownership.isCurrent() || ownership.lease.signal.aborted) return;
|
|
24060
|
+
// A discarded in-loop attempt need not have published turn_start;
|
|
24061
|
+
// the Agent-supplied handle still identifies its accepted SDK cohort.
|
|
24062
|
+
const continuationSdkOwnership =
|
|
24063
|
+
sdkOwnership ?? this.#captureSdkContinuationOwnership(ownership.handle.scope);
|
|
23641
24064
|
const resourceRunId = String(ownership.logicalRunId);
|
|
23642
24065
|
const previousLease = this.#postPromptLeases.get(resourceRunId);
|
|
23643
24066
|
this.#postPromptLeases.set(resourceRunId, ownership.lease);
|
|
@@ -23655,6 +24078,7 @@ export class AgentSession {
|
|
|
23655
24078
|
},
|
|
23656
24079
|
resourceRunId,
|
|
23657
24080
|
ownership.lease.signal,
|
|
24081
|
+
continuationSdkOwnership,
|
|
23658
24082
|
);
|
|
23659
24083
|
if (
|
|
23660
24084
|
terminalized ||
|
|
@@ -23689,12 +24113,14 @@ export class AgentSession {
|
|
|
23689
24113
|
},
|
|
23690
24114
|
};
|
|
23691
24115
|
}
|
|
23692
|
-
//
|
|
23693
|
-
//
|
|
23694
|
-
//
|
|
23695
|
-
//
|
|
23696
|
-
|
|
23697
|
-
|
|
24116
|
+
// Content-free typed statusless overloads are safe for managed retry when
|
|
24117
|
+
// this discarded attempt is clean, even after earlier committed work in
|
|
24118
|
+
// the same prompt. Preserve the committed failure path when this attempt
|
|
24119
|
+
// itself is not replay-safe.
|
|
24120
|
+
if (
|
|
24121
|
+
isStatuslessTypedOverloadFacts(outcome.failure.transportFailure) &&
|
|
24122
|
+
!this.#isRetryScopeClean(outcome.scope)
|
|
24123
|
+
) {
|
|
23698
24124
|
this.#defaultFallbackChain().resetAttemptBudget();
|
|
23699
24125
|
return this.#managedFallbackExhaustionDecision(
|
|
23700
24126
|
outcome.failure.message,
|
|
@@ -23759,6 +24185,10 @@ export class AgentSession {
|
|
|
23759
24185
|
if (classification === "transient" || classification === "first_event_timeout") {
|
|
23760
24186
|
return { class: "server" };
|
|
23761
24187
|
}
|
|
24188
|
+
// A locally promoted empty response is retryable only when the managed
|
|
24189
|
+
// attempt stayed clean; authorize the configured fallback chain without
|
|
24190
|
+
// fabricating provider transport facts for the runtime-owned failure.
|
|
24191
|
+
if (classification === "empty_response") return { class: "server" };
|
|
23762
24192
|
if (classification === "unknown") return { class: "unknown" };
|
|
23763
24193
|
return undefined;
|
|
23764
24194
|
}
|
|
@@ -23938,9 +24368,7 @@ export class AgentSession {
|
|
|
23938
24368
|
!authStorage.isCredentialAvailable(resolvedModel.provider, credential.id)
|
|
23939
24369
|
)
|
|
23940
24370
|
continue;
|
|
23941
|
-
let credentialKeyResult:
|
|
23942
|
-
| { kind: "resolved"; value: string | undefined }
|
|
23943
|
-
| { kind: "aborted" };
|
|
24371
|
+
let credentialKeyResult: { kind: "resolved"; value: string | undefined } | { kind: "aborted" };
|
|
23944
24372
|
try {
|
|
23945
24373
|
credentialKeyResult = await awaitWithCancellation(
|
|
23946
24374
|
this.#modelRegistry.getApiKey(resolvedModel, this.credentialSessionId, {
|
|
@@ -24038,7 +24466,11 @@ export class AgentSession {
|
|
|
24038
24466
|
!transitionStillOwned()
|
|
24039
24467
|
)
|
|
24040
24468
|
return await rollbackCancelled();
|
|
24041
|
-
if (
|
|
24469
|
+
if (
|
|
24470
|
+
selectedCredentialRowId !== undefined &&
|
|
24471
|
+
selectedCredentialKind !== undefined &&
|
|
24472
|
+
this.#restoreManagedFallbackGetApiKey
|
|
24473
|
+
) {
|
|
24042
24474
|
this.#managedFallbackNextCredentialOverride = {
|
|
24043
24475
|
modelKey: canonicalModelKey,
|
|
24044
24476
|
storageProvider: resolveOAuthStorageProvider(resolvedModel.provider),
|
|
@@ -24332,6 +24764,10 @@ export class AgentSession {
|
|
|
24332
24764
|
scope?: AttemptScope,
|
|
24333
24765
|
scopeWasClean = this.#isRetryScopeClean(scope),
|
|
24334
24766
|
): Promise<boolean | ManagedAttemptDecision> {
|
|
24767
|
+
// Capture the SDK owner before the failed attempt retires its active scope.
|
|
24768
|
+
// Retry/continuation terminals must remain attributed to the prompt that
|
|
24769
|
+
// admitted the provider call, even when the retry starts after agent_end.
|
|
24770
|
+
const retrySdkOwnership = this.#captureSdkContinuationOwnership(scope);
|
|
24335
24771
|
const retryAbortEpoch = this.#abortAdmissionEpoch;
|
|
24336
24772
|
const retryCancelled = () =>
|
|
24337
24773
|
this.#isDisposed || this.#sessionAdmissionClosing || this.#abortAdmissionEpoch !== retryAbortEpoch;
|
|
@@ -24349,13 +24785,22 @@ export class AgentSession {
|
|
|
24349
24785
|
// Managed outcomes can reach retry handling without the ordinary admission
|
|
24350
24786
|
// check. Do not let retained transport facts bypass the same terminal veto.
|
|
24351
24787
|
if (isTerminalCodexProviderFailure(message)) {
|
|
24352
|
-
return managedOutcome
|
|
24353
|
-
? { type: "terminal", terminal: { stopReason: "error", messages: [message] } }
|
|
24354
|
-
: false;
|
|
24788
|
+
return managedOutcome ? { type: "terminal", terminal: { stopReason: "error", messages: [message] } } : false;
|
|
24355
24789
|
}
|
|
24356
24790
|
if (retryCancelled()) {
|
|
24357
24791
|
return managedOutcome ? { type: "terminal", terminal: { stopReason: "cancelled" } } : false;
|
|
24358
24792
|
}
|
|
24793
|
+
if (
|
|
24794
|
+
transportFailure?.status === undefined &&
|
|
24795
|
+
transportFailure?.providerCode === SERVER_OVERLOADED_PROVIDER_CODE &&
|
|
24796
|
+
(transportFailure.openaiErrorCode === undefined ||
|
|
24797
|
+
transportFailure.openaiErrorCode === SERVER_OVERLOADED_PROVIDER_CODE) &&
|
|
24798
|
+
(managedOutcome ? !scopeWasClean : !this.#hasCleanRetryReplaySafety)
|
|
24799
|
+
) {
|
|
24800
|
+
return managedOutcome
|
|
24801
|
+
? { type: "terminal", terminal: { stopReason: "error", messages: [message] } }
|
|
24802
|
+
: false;
|
|
24803
|
+
}
|
|
24359
24804
|
// A local machinery failure must never stay charged against the provider
|
|
24360
24805
|
// fallback budget, no matter which local exit follows (disabled retry,
|
|
24361
24806
|
// visible-content surface, bounded retry, exhaustion, or the immediate
|
|
@@ -24462,21 +24907,6 @@ export class AgentSession {
|
|
|
24462
24907
|
| undefined = canReplayCodexProviderOverload || canReplayBareDefaultCodexFailure
|
|
24463
24908
|
? { class: "server" }
|
|
24464
24909
|
: 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
24910
|
if (!trigger) {
|
|
24481
24911
|
return managedOutcome
|
|
24482
24912
|
? this.#managedFallbackExhaustionDecision(message, message.errorMessage || "Model fallback attempt failed")
|
|
@@ -24689,10 +25119,7 @@ export class AgentSession {
|
|
|
24689
25119
|
if (outcome === "exhausted") {
|
|
24690
25120
|
if (managedFallback) {
|
|
24691
25121
|
let errorMessage = this.#fallbackExhaustionError(controller);
|
|
24692
|
-
if (
|
|
24693
|
-
!providerRetryCeilingReached &&
|
|
24694
|
-
(trigger.class === "quota" || trigger.class === "rate_limit")
|
|
24695
|
-
) {
|
|
25122
|
+
if (!providerRetryCeilingReached && (trigger.class === "quota" || trigger.class === "rate_limit")) {
|
|
24696
25123
|
if (!assistantMessageHasVisibleOrToolContent(message)) {
|
|
24697
25124
|
const mark =
|
|
24698
25125
|
quotaCredentialMark ??
|
|
@@ -24737,6 +25164,8 @@ export class AgentSession {
|
|
|
24737
25164
|
: cappedExponentialWithFullJitter(retrySettings.baseDelayMs, retrySettings.maxDelayMs, attemptsUsed);
|
|
24738
25165
|
|
|
24739
25166
|
const retry = async (ownership?: ManagedAttemptContinuationOwnership): Promise<void> => {
|
|
25167
|
+
const continuationSdkOwnership =
|
|
25168
|
+
retrySdkOwnership ?? this.#captureSdkContinuationOwnership(ownership?.handle.scope);
|
|
24740
25169
|
const activePromptHandle = this.activePromptHandle;
|
|
24741
25170
|
const cancellationSignal =
|
|
24742
25171
|
ownership?.domain.signal ??
|
|
@@ -24951,7 +25380,12 @@ export class AgentSession {
|
|
|
24951
25380
|
await this.agent.continue({
|
|
24952
25381
|
...this.#managedFallbackPromptOptions(),
|
|
24953
25382
|
onRunAccepted: (handle: AttemptRunHandle) => {
|
|
24954
|
-
this.#acceptSdkAttemptRun(
|
|
25383
|
+
this.#acceptSdkAttemptRun(
|
|
25384
|
+
handle,
|
|
25385
|
+
continuationSdkOwnership?.token,
|
|
25386
|
+
continuationSdkOwnership?.cohort,
|
|
25387
|
+
continuationSdkOwnership?.scope,
|
|
25388
|
+
);
|
|
24955
25389
|
},
|
|
24956
25390
|
});
|
|
24957
25391
|
return;
|
|
@@ -24979,6 +25413,7 @@ export class AgentSession {
|
|
|
24979
25413
|
allowDuringCancelAndSubmit: true,
|
|
24980
25414
|
suppressPredecessorAgentEnd: resourceRunId !== undefined,
|
|
24981
25415
|
resourceRunId,
|
|
25416
|
+
sdkOwnership: continuationSdkOwnership,
|
|
24982
25417
|
onError: () => this.#failRetryRecovery("Retry continuation failed to start"),
|
|
24983
25418
|
onSkip: () => this.#failRetryRecovery("Retry continuation was superseded"),
|
|
24984
25419
|
});
|
|
@@ -25206,10 +25641,6 @@ export class AgentSession {
|
|
|
25206
25641
|
// Bash Execution
|
|
25207
25642
|
// =========================================================================
|
|
25208
25643
|
|
|
25209
|
-
async #saveBashOriginalArtifact(originalText: string): Promise<BashArtifactSaveResult> {
|
|
25210
|
-
return saveAgentBashOriginalArtifact(this.sessionManager, originalText);
|
|
25211
|
-
}
|
|
25212
|
-
|
|
25213
25644
|
/**
|
|
25214
25645
|
* Execute a bash command.
|
|
25215
25646
|
* Adds result to agent context and session.
|
|
@@ -25224,6 +25655,7 @@ export class AgentSession {
|
|
|
25224
25655
|
onChunk?: (chunk: string) => void,
|
|
25225
25656
|
options?: { excludeFromContext?: boolean; onPersisted?: () => void },
|
|
25226
25657
|
): Promise<BashResult> {
|
|
25658
|
+
const publishArtifact = this.sessionManager.captureArtifactPublication();
|
|
25227
25659
|
const excludeFromContext = options?.excludeFromContext === true;
|
|
25228
25660
|
this.#markRetryReplayUnsafe();
|
|
25229
25661
|
|
|
@@ -25261,7 +25693,7 @@ export class AgentSession {
|
|
|
25261
25693
|
sessionId: this.sessionId,
|
|
25262
25694
|
cwd,
|
|
25263
25695
|
}),
|
|
25264
|
-
onMinimizedSave: originalText =>
|
|
25696
|
+
onMinimizedSave: originalText => saveAgentBashOriginalArtifact(publishArtifact, originalText),
|
|
25265
25697
|
});
|
|
25266
25698
|
|
|
25267
25699
|
this.recordBashResult(command, result, options);
|
|
@@ -25365,9 +25797,18 @@ export class AgentSession {
|
|
|
25365
25797
|
this.#markRetryReplayUnsafe();
|
|
25366
25798
|
const cwd = this.sessionManager.getCwd();
|
|
25367
25799
|
this.assertEvalExecutionAllowed();
|
|
25800
|
+
const sessionFile = this.sessionManager.getSessionFile();
|
|
25801
|
+
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${cwd}` : `cwd:${cwd}`;
|
|
25802
|
+
const kernelOwnerId = this.#evalKernelOwnerId;
|
|
25803
|
+
const kernelMode = this.settings.get("python.kernelMode");
|
|
25804
|
+
const settings = this.settings;
|
|
25368
25805
|
|
|
25369
25806
|
const abortController = new AbortController();
|
|
25370
|
-
const execution = (async (): Promise<PythonResult> => {
|
|
25807
|
+
const execution = Promise.resolve().then(async (): Promise<PythonResult> => {
|
|
25808
|
+
if (abortController.signal.aborted) {
|
|
25809
|
+
throw abortController.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
25810
|
+
}
|
|
25811
|
+
this.assertEvalExecutionAllowed();
|
|
25371
25812
|
if (this.#extensionRunner?.hasHandlers("user_python")) {
|
|
25372
25813
|
const hookResult = await this.#extensionRunner.emitUserPython({
|
|
25373
25814
|
type: "user_python",
|
|
@@ -25375,6 +25816,9 @@ export class AgentSession {
|
|
|
25375
25816
|
excludeFromContext,
|
|
25376
25817
|
cwd,
|
|
25377
25818
|
});
|
|
25819
|
+
if (abortController.signal.aborted) {
|
|
25820
|
+
throw abortController.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
25821
|
+
}
|
|
25378
25822
|
this.assertEvalExecutionAllowed();
|
|
25379
25823
|
if (hookResult?.result) {
|
|
25380
25824
|
this.recordPythonResult(code, hookResult.result, options);
|
|
@@ -25382,15 +25826,12 @@ export class AgentSession {
|
|
|
25382
25826
|
}
|
|
25383
25827
|
}
|
|
25384
25828
|
|
|
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
25829
|
const result = await executePythonCommand(code, {
|
|
25389
25830
|
cwd,
|
|
25390
25831
|
sessionId,
|
|
25391
|
-
kernelOwnerId
|
|
25392
|
-
kernelMode
|
|
25393
|
-
settings
|
|
25832
|
+
kernelOwnerId,
|
|
25833
|
+
kernelMode,
|
|
25834
|
+
settings,
|
|
25394
25835
|
onChunk,
|
|
25395
25836
|
signal: abortController.signal,
|
|
25396
25837
|
});
|
|
@@ -25399,7 +25840,7 @@ export class AgentSession {
|
|
|
25399
25840
|
// must not reopen a closing session to append history.
|
|
25400
25841
|
if (!this.#evalExecutionDisposing) this.recordPythonResult(code, result, options);
|
|
25401
25842
|
return result;
|
|
25402
|
-
})
|
|
25843
|
+
});
|
|
25403
25844
|
return await this.trackEvalExecution(execution, abortController);
|
|
25404
25845
|
}
|
|
25405
25846
|
|