@gajae-code/coding-agent 0.18.11 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +760 -0
- package/dist/types/async/job-manager.d.ts +7 -3
- package/dist/types/async/result-formatting.d.ts +2 -0
- package/dist/types/autoresearch/git.d.ts +1 -1
- package/dist/types/capability/fs.d.ts +2 -2
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/accounts-cli.d.ts +2 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
- package/dist/types/cli/install-activation.d.ts +10 -4
- package/dist/types/cli/public-command-errors.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +21 -3
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/cli-ordinary.d.ts +2 -2
- package/dist/types/commands/accounts.d.ts +2 -1
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +13 -0
- package/dist/types/commit/changelog/index.d.ts +1 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +53 -1
- package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
- package/dist/types/config/config-hot-reload.d.ts +44 -0
- package/dist/types/config/file-lock.d.ts +52 -3
- package/dist/types/config/model-discovery-manager.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +79 -9
- package/dist/types/config/model-registry.d.ts +72 -8
- package/dist/types/config/model-resolver.d.ts +7 -4
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +57 -4
- package/dist/types/config/settings.d.ts +33 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/config.d.ts +8 -4
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/debug/system-info.d.ts +6 -1
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/discovery/mcp-compat.d.ts +3 -1
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +14 -0
- package/dist/types/edit/modes/replace.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +12 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +2 -1
- package/dist/types/migrate/adapters/index.d.ts +42 -1
- package/dist/types/migrate/executor.d.ts +3 -1
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +82 -3
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -0
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
- package/dist/types/runtime-mcp/manager.d.ts +7 -0
- package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
- package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
- package/dist/types/sdk/broker/broker.d.ts +29 -2
- package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
- package/dist/types/sdk/broker/ensure.d.ts +141 -9
- package/dist/types/sdk/broker/hop.d.ts +45 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
- package/dist/types/sdk/broker/session-index.d.ts +6 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +44 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/cli/session-cli.d.ts +0 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +40 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
- package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +107 -9
- package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
- package/dist/types/session/messages.d.ts +5 -1
- package/dist/types/session/session-manager.d.ts +29 -3
- package/dist/types/session/startup-auth-config.d.ts +24 -2
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/session/terminal-abort.d.ts +3 -1
- package/dist/types/session-import/service.d.ts +1 -1
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/executor.d.ts +19 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
- package/dist/types/tools/atomic-file-write.d.ts +9 -7
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +15 -2
- package/dist/types/tools/fetch.d.ts +7 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/python.d.ts +1 -1
- package/dist/types/tools/report-issue.d.ts +41 -0
- package/dist/types/tools/subagent.d.ts +23 -0
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/scrapers/types.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/dist/types/web/search/providers/insane.d.ts +7 -1
- package/dist/types/web/search/providers/searxng.d.ts +5 -0
- package/package.json +10 -9
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/build-binary.ts +2 -1
- package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
- package/scripts/generate-docs-index.ts +8 -1
- package/scripts/generate-sdk-operation-inventory.ts +38 -4
- package/src/async/job-manager.ts +356 -56
- package/src/async/result-formatting.ts +40 -0
- package/src/autoresearch/git.ts +9 -2
- package/src/autoresearch/prompts/prompt-setup.md +1 -1
- package/src/capability/fs.ts +58 -22
- package/src/capability/index.ts +2 -2
- package/src/capability/rule.ts +11 -2
- package/src/cli/accounts-cli.ts +176 -8
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/auth-broker-cli.ts +21 -11
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/doctor/install-repairs.ts +35 -5
- package/src/cli/fast-help.ts +1 -3
- package/src/cli/install-activation.ts +47 -4
- package/src/cli/migrate-cli.ts +19 -6
- package/src/cli/public-command-errors.ts +6 -0
- package/src/cli/root-flags.ts +2 -4
- package/src/cli/stats-cli.ts +9 -5
- package/src/cli/update-cli.ts +471 -13
- package/src/cli-ordinary.ts +7 -2
- package/src/commands/accounts.ts +10 -5
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +277 -50
- package/src/commands/ultragoal.ts +16 -8
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/changelog/index.ts +39 -1
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/message.ts +10 -3
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +50 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload-trust.ts +148 -0
- package/src/config/config-hot-reload.ts +742 -0
- package/src/config/file-lock.ts +1615 -328
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-bindings-applier.ts +85 -47
- package/src/config/model-discovery-manager.ts +10 -0
- package/src/config/model-profile-activation.ts +889 -148
- package/src/config/model-registry.ts +1706 -205
- package/src/config/model-resolver.ts +52 -22
- package/src/config/models-config-schema.ts +6 -0
- package/src/config/settings-schema.ts +73 -4
- package/src/config/settings.ts +460 -24
- package/src/coordinator-mcp/policy.ts +26 -1
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +7 -1
- package/src/customization/import.ts +279 -8
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/config.ts +31 -11
- package/src/dap/defaults.json +1 -1
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +89 -6
- package/src/debug/report-bundle.ts +90 -2
- package/src/debug/system-info.ts +8 -3
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +25 -16
- package/src/discovery/mcp-compat.ts +120 -17
- package/src/edit/apply-patch/index.ts +12 -1
- package/src/edit/index.ts +204 -22
- package/src/edit/modes/patch.ts +214 -8
- package/src/edit/modes/replace.ts +136 -0
- package/src/eval/js/context-manager.ts +29 -3
- package/src/eval/js/worker-entry.ts +51 -18
- package/src/eval/py/executor.ts +14 -4
- package/src/eval/py/runner.py +144 -5
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/bash-executor.ts +81 -4
- package/src/exec/bash-shell-guardian.ts +4 -1
- package/src/exec/bash-shell-supervisor.ts +8 -4
- package/src/exec/isolated-shell.ts +5 -1
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +86 -6
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/custom-tools/wrapper.ts +3 -1
- package/src/extensibility/extensions/loader.ts +10 -1
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +37 -7
- package/src/gjc-runtime/launch-tmux.ts +30 -15
- package/src/gjc-runtime/launch-worktree.test.ts +148 -0
- package/src/gjc-runtime/launch-worktree.ts +101 -14
- package/src/gjc-runtime/managed-owner-admission.ts +46 -2
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/session-resolution.ts +38 -6
- package/src/gjc-runtime/session-state-lock.ts +59 -14
- package/src/gjc-runtime/state-runtime.ts +23 -19
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +17 -12
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +23 -22
- package/src/internal-urls/local-protocol.ts +167 -8
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/lsp/index.ts +29 -0
- package/src/lsp/workspace-path.ts +23 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +574 -2
- package/src/migrate/adapters/claude-code.ts +162 -5
- package/src/migrate/adapters/codex.ts +77 -7
- package/src/migrate/adapters/index.ts +258 -5
- package/src/migrate/adapters/opencode.ts +10 -1
- package/src/migrate/executor.ts +222 -7
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +63 -2
- package/src/migrate/types.ts +84 -4
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/acp/permission-mode.ts +18 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/assistant-message.ts +55 -1
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/settings-selector.ts +103 -37
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +300 -9
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +66 -11
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +76 -7
- package/src/modes/interactive-mode.ts +123 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/theme/theme.ts +15 -0
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/report-issue.md +13 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-credential-selector.ts +2 -2
- package/src/runtime-mcp/content-limits.ts +7 -0
- package/src/runtime-mcp/manager-pool.test.ts +5 -4
- package/src/runtime-mcp/manager.ts +40 -3
- package/src/runtime-mcp/oauth-flow.ts +32 -6
- package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/render.ts +16 -1
- package/src/runtime-mcp/tool-bridge.ts +194 -21
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +10 -5
- package/src/sdk/broker/broker.ts +484 -85
- package/src/sdk/broker/daemon-entry.ts +338 -116
- package/src/sdk/broker/doctor-restart.ts +2 -1
- package/src/sdk/broker/ensure.ts +1214 -268
- package/src/sdk/broker/hop.ts +286 -0
- package/src/sdk/broker/lifecycle-ledger.ts +38 -11
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/lock-host-identity.ts +29 -0
- package/src/sdk/broker/session-index.ts +91 -16
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-command-policy.ts +16 -3
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +460 -35
- package/src/sdk/bus/reconciliation-store.ts +201 -8
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +23 -6
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1737 -129
- package/src/sdk/host/session-runtime.ts +684 -83
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +15 -1
- package/src/sdk/prompt-deadline-flush.ts +78 -5
- package/src/sdk/prompt-deadline-manager.ts +68 -14
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +81 -12
- package/src/sdk/protocol/operation-inventory.generated.json +261 -2
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/reconciliation-extensions.ts +1 -0
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +237 -97
- package/src/session/agent-session.ts +2726 -479
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +173 -57
- package/src/session/internal/managed-session-storage.ts +85 -12
- package/src/session/internal/native-publish-outcome.ts +5 -1
- package/src/session/messages.ts +47 -6
- package/src/session/session-manager.ts +326 -19
- package/src/session/startup-auth-config.ts +116 -8
- package/src/session/streaming-output.ts +262 -4
- package/src/session/terminal-abort.ts +14 -2
- package/src/session-import/redact.ts +53 -2
- package/src/session-import/service.ts +1 -0
- package/src/skill-state/workflow-mutation-guard.ts +2145 -206
- package/src/slash-commands/builtin-registry.ts +21 -6
- package/src/slash-commands/helpers/credential-switch.ts +10 -5
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +115 -20
- package/src/task/index.ts +389 -175
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-edit-write-path.ts +64 -0
- package/src/tools/ast-edit.ts +18 -1
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/atomic-file-write.ts +46 -15
- package/src/tools/auto-generated-guard.ts +15 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +214 -6
- package/src/tools/bash-interactive.ts +14 -2
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +113 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +16 -8
- package/src/tools/descriptors.test.ts +44 -0
- package/src/tools/descriptors.ts +50 -6
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +93 -4
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/index.ts +2 -0
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +74 -5
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +26 -37
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/report-issue.ts +184 -0
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent-render.ts +114 -12
- package/src/tools/subagent.ts +118 -5
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/todo-write.ts +41 -3
- package/src/tools/tool-catalog.generated.ts +32 -4
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +42 -3
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +12 -4
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +75 -7
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +72 -22
- package/src/web/scrapers/index.ts +3 -1
- package/src/web/scrapers/types.ts +8 -0
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
- package/src/web/search/providers/insane.ts +91 -28
- package/src/web/search/providers/searxng.ts +62 -10
|
@@ -4,7 +4,12 @@ import * as crypto from "node:crypto";
|
|
|
4
4
|
import * as fs from "node:fs/promises";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
isContinuingMidRunMaintenanceOutcome,
|
|
9
|
+
isNonDispatchedToolEvent,
|
|
10
|
+
type RunSettlementProof,
|
|
11
|
+
ThinkingLevel,
|
|
12
|
+
} from "@gajae-code/agent-core";
|
|
8
13
|
import type { AttemptScope } from "@gajae-code/agent-core/attempt-scope";
|
|
9
14
|
import type { Api, ImageContent, Model, ProviderDiagnostic } from "@gajae-code/ai/core";
|
|
10
15
|
|
|
@@ -39,7 +44,7 @@ import { tagSdkLifecycleObserver } from "../../extensibility/extensions/function
|
|
|
39
44
|
import type { AgentEndEvent } from "../../extensibility/shared-events";
|
|
40
45
|
import { normalizeGoal } from "../../goals/state";
|
|
41
46
|
import { toAgentWireEventPayload } from "../../modes/shared/agent-wire/event-envelope";
|
|
42
|
-
import type { AgentSessionEvent } from "../../session/agent-session";
|
|
47
|
+
import type { AgentSessionEvent, QueuedRunStartFailure } from "../../session/agent-session";
|
|
43
48
|
import type { SdkRunCapability } from "../../session/sdk-run-capability";
|
|
44
49
|
import {
|
|
45
50
|
boundTerminalRetentionState,
|
|
@@ -73,7 +78,7 @@ import {
|
|
|
73
78
|
syntheticNamespaceCollision,
|
|
74
79
|
} from "../model-profile-model";
|
|
75
80
|
import { projectQ10Models } from "../models.js";
|
|
76
|
-
import {
|
|
81
|
+
import { autosaveWorktreeOnPromptDeadline } from "../prompt-deadline-flush";
|
|
77
82
|
import {
|
|
78
83
|
PromptDeadlineManager,
|
|
79
84
|
type PromptDeadlinePublicationResult,
|
|
@@ -103,6 +108,7 @@ import {
|
|
|
103
108
|
createKindAwareReconciliation,
|
|
104
109
|
createReconciliationStore,
|
|
105
110
|
type KindAwareReconciliation,
|
|
111
|
+
type ReconciliationStoreInstrumentationEvent,
|
|
106
112
|
resolveReconciliationSessionFile,
|
|
107
113
|
} from "../reconciliation-extensions";
|
|
108
114
|
import {
|
|
@@ -124,6 +130,7 @@ import {
|
|
|
124
130
|
TURN_STREAM_CAPABILITY,
|
|
125
131
|
} from "./host";
|
|
126
132
|
import { clearAutoroutingInactive, isAutoroutingInactive, markAutoroutingInactive } from "./internal-autorouting-state";
|
|
133
|
+
import { PromptImageUploadStore, retirePromptImageReservation } from "./prompt-image-upload";
|
|
127
134
|
import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } from "./query";
|
|
128
135
|
import { createSdkRunCapability } from "./sdk-run-capability";
|
|
129
136
|
import {
|
|
@@ -349,6 +356,8 @@ export interface SessionSdkTransport {
|
|
|
349
356
|
stop(): Promise<void>;
|
|
350
357
|
broadcastFrame?(frame: SdkFrame): void;
|
|
351
358
|
onConnectionClose?(handler: (connectionId: string) => void): undefined | (() => void);
|
|
359
|
+
/** Authoritative live socket membership, not a cache of previously observed frames. */
|
|
360
|
+
isConnectionOpen?(connectionId: string): boolean;
|
|
352
361
|
onNegotiatedCapabilities?(
|
|
353
362
|
handler: (connectionId: string, capabilities: readonly string[]) => void,
|
|
354
363
|
): undefined | (() => void);
|
|
@@ -356,6 +365,7 @@ export interface SessionSdkTransport {
|
|
|
356
365
|
|
|
357
366
|
export interface SessionSdkRuntimeOptions
|
|
358
367
|
extends Omit<SessionSdkHostOptions, "sessionId" | "stateRoot" | "token" | "sendFrame" | "onFrame"> {
|
|
368
|
+
onDisconnected?: (connectionId: string) => void;
|
|
359
369
|
transport: SessionSdkTransport;
|
|
360
370
|
/** Session settings; enables `config.patch` application on this runtime. */
|
|
361
371
|
settings?: Settings;
|
|
@@ -442,9 +452,13 @@ export interface SdkOnlyReconciliationStore {
|
|
|
442
452
|
}
|
|
443
453
|
|
|
444
454
|
export interface SdkOnlyTerminalAbortSeams {
|
|
455
|
+
/** Definitive event-driven observation: never abort, seal or quarantine a run to reclaim images. */
|
|
456
|
+
waitForRunSettlement?: (handle: string) => Promise<RunSettlementProof>;
|
|
445
457
|
getReconciliationStore?: () => SdkOnlyReconciliationStore | undefined;
|
|
446
458
|
getTerminalTurnEpoch: () => number | undefined;
|
|
447
459
|
getActivePromptHandle: () => string | undefined;
|
|
460
|
+
onDeadlineRecoveryCheckpointForTests?: (checkpoint: SdkOnlyDeadlineRecoveryCheckpoint) => void;
|
|
461
|
+
onPersistenceInstrumentationForTests?: (event: PersistenceInstrumentationEvent) => void;
|
|
448
462
|
/** Re-read the active prompt's owning SDK connection for the owner-mismatch
|
|
449
463
|
* recheck; falls back to the runtime-tracked owner when absent (review
|
|
450
464
|
* thread P1). */
|
|
@@ -481,6 +495,35 @@ export interface SdkOnlyTerminalAbortSeams {
|
|
|
481
495
|
maxDurableTerminalReservationsForTests?: number;
|
|
482
496
|
}
|
|
483
497
|
|
|
498
|
+
export interface SdkOnlyDeadlineRecoveryCheckpoint {
|
|
499
|
+
at: number;
|
|
500
|
+
stage:
|
|
501
|
+
| "terminalization-start"
|
|
502
|
+
| "terminalization-result"
|
|
503
|
+
| "publication-start"
|
|
504
|
+
| "publication-result"
|
|
505
|
+
| "note-transition-start"
|
|
506
|
+
| "note-transition-awaiting-finalization"
|
|
507
|
+
| "note-transition-finalization-ready"
|
|
508
|
+
| "note-transition-before-persist"
|
|
509
|
+
| "note-transition-persisted"
|
|
510
|
+
| "note-transition-persist-failed"
|
|
511
|
+
| "deadline-recovery-retired";
|
|
512
|
+
correlation: InvocationCorrelation;
|
|
513
|
+
result?: "settled" | "uncertain" | "published" | "not-published" | "rejected";
|
|
514
|
+
reason?: string;
|
|
515
|
+
recordStatus?: string;
|
|
516
|
+
recordRevision?: number;
|
|
517
|
+
recordTerminalAt?: number;
|
|
518
|
+
pendingFinalization?: boolean;
|
|
519
|
+
eventCaptured?: boolean;
|
|
520
|
+
eventPrepared?: boolean;
|
|
521
|
+
terminalCommitted?: boolean;
|
|
522
|
+
leaseCurrent?: boolean;
|
|
523
|
+
pendingToolCount?: number;
|
|
524
|
+
errorCode?: string;
|
|
525
|
+
}
|
|
526
|
+
|
|
484
527
|
/**
|
|
485
528
|
* The transport-neutral SDK session runtime.
|
|
486
529
|
*
|
|
@@ -489,6 +532,10 @@ export interface SdkOnlyTerminalAbortSeams {
|
|
|
489
532
|
* dispatch, replay/event publication, and reverse-provider lifecycle without
|
|
490
533
|
* importing any notification adapter or native notification class.
|
|
491
534
|
*/
|
|
535
|
+
function isStringArray(value: unknown): value is string[] {
|
|
536
|
+
return Array.isArray(value) && value.every((item: unknown) => typeof item === "string");
|
|
537
|
+
}
|
|
538
|
+
|
|
492
539
|
export class SessionSdkSessionRuntime {
|
|
493
540
|
readonly host: SessionSdkHost;
|
|
494
541
|
readonly transport: SessionSdkTransport;
|
|
@@ -519,18 +566,32 @@ export class SessionSdkSessionRuntime {
|
|
|
519
566
|
onFrame: handler =>
|
|
520
567
|
options.transport.onFrame((connectionId, frame) => {
|
|
521
568
|
this.#connectionIds.add(connectionId);
|
|
569
|
+
if (
|
|
570
|
+
!options.connectionCapabilities &&
|
|
571
|
+
frame.type === "event_replay" &&
|
|
572
|
+
isStringArray(frame.capabilities)
|
|
573
|
+
) {
|
|
574
|
+
this.#connectionCapabilities.set(
|
|
575
|
+
connectionId,
|
|
576
|
+
new Set([...(this.#connectionCapabilities.get(connectionId) ?? []), ...frame.capabilities]),
|
|
577
|
+
);
|
|
578
|
+
}
|
|
522
579
|
handler(connectionId, frame);
|
|
523
580
|
}),
|
|
524
581
|
});
|
|
525
582
|
this.#connectionDisposer = options.transport.onConnectionClose?.(connectionId => {
|
|
526
583
|
this.#connectionIds.delete(connectionId);
|
|
527
584
|
this.#connectionCapabilities.delete(connectionId);
|
|
585
|
+
options.onDisconnected?.(connectionId);
|
|
528
586
|
this.host.handleDisconnect(connectionId);
|
|
529
587
|
});
|
|
530
588
|
this.#capabilitiesDisposer = options.transport.onNegotiatedCapabilities?.((connectionId, negotiated) => {
|
|
531
589
|
if (options.connectionCapabilities && this.#connectionCapabilitiesProvider(connectionId) === undefined) return;
|
|
532
590
|
this.#connectionIds.add(connectionId);
|
|
533
|
-
this.#connectionCapabilities.set(
|
|
591
|
+
this.#connectionCapabilities.set(
|
|
592
|
+
connectionId,
|
|
593
|
+
new Set([...(this.#connectionCapabilities.get(connectionId) ?? []), ...negotiated]),
|
|
594
|
+
);
|
|
534
595
|
});
|
|
535
596
|
this.#malformedDisposer = options.transport.onMalformedFrame?.((connectionId, message) => {
|
|
536
597
|
this.host.handleMalformedFrame(connectionId, message);
|
|
@@ -1024,8 +1085,26 @@ function canonicalizeHydratedDiagnostics(record: InvocationRecord): InvocationRe
|
|
|
1024
1085
|
return canonical;
|
|
1025
1086
|
}
|
|
1026
1087
|
|
|
1088
|
+
export interface PersistenceInstrumentationEvent {
|
|
1089
|
+
at: number;
|
|
1090
|
+
phase: "enqueued" | "started" | "completed" | "failed";
|
|
1091
|
+
operation?: string;
|
|
1092
|
+
substep?: string;
|
|
1093
|
+
error?: string;
|
|
1094
|
+
/** Detailed information about lock acquisition, promise identity, db state, etc. */
|
|
1095
|
+
detail?: string;
|
|
1096
|
+
/** Time spent waiting (ms) before the operation started. */
|
|
1097
|
+
waitMs?: number;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1027
1100
|
export function createInvocationReconciliation(
|
|
1028
|
-
options: {
|
|
1101
|
+
options: {
|
|
1102
|
+
stateRoot?: string;
|
|
1103
|
+
sessionId?: string;
|
|
1104
|
+
store?: SdkOnlyReconciliationStore;
|
|
1105
|
+
onDeadlineRecoveryCheckpointForTests?: (checkpoint: SdkOnlyDeadlineRecoveryCheckpoint) => void;
|
|
1106
|
+
onPersistenceInstrumentationForTests?: (event: PersistenceInstrumentationEvent) => void;
|
|
1107
|
+
} = {},
|
|
1029
1108
|
): InvocationReconciliation {
|
|
1030
1109
|
const ACTIVE_CAPACITY = 256;
|
|
1031
1110
|
const TERMINAL_CAPACITY = 512;
|
|
@@ -1063,6 +1142,17 @@ export function createInvocationReconciliation(
|
|
|
1063
1142
|
}
|
|
1064
1143
|
>();
|
|
1065
1144
|
const pendingTerminalVisibility = new Map<string, { visibleRecord: InvocationRecord; writes: number }>();
|
|
1145
|
+
const recordDeadlineRecoveryCheckpoint = (
|
|
1146
|
+
stage: SdkOnlyDeadlineRecoveryCheckpoint["stage"],
|
|
1147
|
+
correlation: InvocationCorrelation,
|
|
1148
|
+
details: Omit<SdkOnlyDeadlineRecoveryCheckpoint, "at" | "correlation" | "stage"> = {},
|
|
1149
|
+
): void => {
|
|
1150
|
+
try {
|
|
1151
|
+
options.onDeadlineRecoveryCheckpointForTests?.({ at: Date.now(), stage, correlation, ...details });
|
|
1152
|
+
} catch {
|
|
1153
|
+
// A test observer must never participate in durable reconciliation.
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1066
1156
|
const retainPendingTerminalVisibility = (recordKey: string, visibleRecord: InvocationRecord): (() => void) => {
|
|
1067
1157
|
const existing = pendingTerminalVisibility.get(recordKey);
|
|
1068
1158
|
if (existing) existing.writes += 1;
|
|
@@ -1079,36 +1169,120 @@ export function createInvocationReconciliation(
|
|
|
1079
1169
|
};
|
|
1080
1170
|
const persist = async (): Promise<void> => {
|
|
1081
1171
|
const run = async (): Promise<void> => {
|
|
1172
|
+
try {
|
|
1173
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "enqueued" });
|
|
1174
|
+
} catch {
|
|
1175
|
+
// A test observer must never participate in durable reconciliation.
|
|
1176
|
+
}
|
|
1177
|
+
try {
|
|
1178
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started" });
|
|
1179
|
+
} catch {
|
|
1180
|
+
// Ignore observer errors.
|
|
1181
|
+
}
|
|
1082
1182
|
// Construct the candidate only when this serialized write starts. A
|
|
1083
1183
|
// pre-await full snapshot lets a later agent_start/agent_end transition
|
|
1084
1184
|
// be overwritten on disk by an older queued write even though the live
|
|
1085
1185
|
// map has already converged.
|
|
1086
1186
|
const snapshot = [...records.values()].map(record => ({ ...record }));
|
|
1087
1187
|
if (store) {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1188
|
+
const promiseId = `transact-${crypto.randomUUID().slice(0, 8)}`;
|
|
1189
|
+
const lockWaitStart = Date.now();
|
|
1190
|
+
try {
|
|
1191
|
+
options.onPersistenceInstrumentationForTests?.({
|
|
1192
|
+
at: lockWaitStart,
|
|
1193
|
+
phase: "started",
|
|
1194
|
+
operation: "store.transact",
|
|
1195
|
+
detail: `promise=${promiseId} awaiting-lock`,
|
|
1196
|
+
});
|
|
1197
|
+
} catch {}
|
|
1198
|
+
const lockAcquiredTime = Date.now();
|
|
1199
|
+
try {
|
|
1200
|
+
await store.transact(current => [
|
|
1201
|
+
...current.filter(record => record.kind !== "prompt" && record.kind !== "skill"),
|
|
1202
|
+
...snapshot.map(record => ({ ...record })),
|
|
1203
|
+
]);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
try {
|
|
1206
|
+
options.onPersistenceInstrumentationForTests?.({
|
|
1207
|
+
at: Date.now(),
|
|
1208
|
+
phase: "failed",
|
|
1209
|
+
operation: "store.transact",
|
|
1210
|
+
detail: `promise=${promiseId} lock-wait=${Date.now() - lockWaitStart}ms`,
|
|
1211
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1212
|
+
});
|
|
1213
|
+
} catch {}
|
|
1214
|
+
throw error;
|
|
1215
|
+
}
|
|
1216
|
+
try {
|
|
1217
|
+
options.onPersistenceInstrumentationForTests?.({
|
|
1218
|
+
at: Date.now(),
|
|
1219
|
+
phase: "completed",
|
|
1220
|
+
operation: "store.transact",
|
|
1221
|
+
detail: `promise=${promiseId} lock-acquire=${lockAcquiredTime - lockWaitStart}ms`,
|
|
1222
|
+
});
|
|
1223
|
+
} catch {}
|
|
1092
1224
|
return;
|
|
1093
1225
|
}
|
|
1094
1226
|
if (!reconciliationFile) return;
|
|
1095
1227
|
const directory = path.dirname(reconciliationFile);
|
|
1096
1228
|
const temporary = `${reconciliationFile}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
1229
|
+
try {
|
|
1230
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "mkdir" });
|
|
1231
|
+
} catch {}
|
|
1097
1232
|
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
|
|
1233
|
+
try {
|
|
1234
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed", substep: "mkdir" });
|
|
1235
|
+
} catch {}
|
|
1236
|
+
try {
|
|
1237
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "writeFile" });
|
|
1238
|
+
} catch {}
|
|
1098
1239
|
await fs.writeFile(
|
|
1099
1240
|
temporary,
|
|
1100
1241
|
JSON.stringify({ version: 1, sessionId: options.sessionId, records: [...snapshot] }),
|
|
1101
1242
|
{ encoding: "utf8", mode: 0o600 },
|
|
1102
1243
|
);
|
|
1244
|
+
try {
|
|
1245
|
+
options.onPersistenceInstrumentationForTests?.({
|
|
1246
|
+
at: Date.now(),
|
|
1247
|
+
phase: "completed",
|
|
1248
|
+
substep: "writeFile",
|
|
1249
|
+
});
|
|
1250
|
+
} catch {}
|
|
1251
|
+
try {
|
|
1252
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "chmod" });
|
|
1253
|
+
} catch {}
|
|
1103
1254
|
await fs.chmod(temporary, 0o600);
|
|
1255
|
+
try {
|
|
1256
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed", substep: "chmod" });
|
|
1257
|
+
} catch {}
|
|
1258
|
+
try {
|
|
1259
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "rename" });
|
|
1260
|
+
} catch {}
|
|
1104
1261
|
await fs.rename(temporary, reconciliationFile);
|
|
1262
|
+
try {
|
|
1263
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed", substep: "rename" });
|
|
1264
|
+
} catch {}
|
|
1105
1265
|
};
|
|
1106
1266
|
const pending = persistenceChain.then(run, run);
|
|
1107
1267
|
persistenceChain = pending.then(
|
|
1108
1268
|
() => undefined,
|
|
1109
1269
|
() => undefined,
|
|
1110
1270
|
);
|
|
1111
|
-
|
|
1271
|
+
try {
|
|
1272
|
+
await pending;
|
|
1273
|
+
try {
|
|
1274
|
+
options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed" });
|
|
1275
|
+
} catch {}
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
try {
|
|
1278
|
+
options.onPersistenceInstrumentationForTests?.({
|
|
1279
|
+
at: Date.now(),
|
|
1280
|
+
phase: "failed",
|
|
1281
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1282
|
+
});
|
|
1283
|
+
} catch {}
|
|
1284
|
+
throw error;
|
|
1285
|
+
}
|
|
1112
1286
|
};
|
|
1113
1287
|
// Retention contract (#4547): terminal records are never age-evicted; only
|
|
1114
1288
|
// the per-kind oldest-terminal-first capacity trim removes them, so a
|
|
@@ -1257,7 +1431,22 @@ export function createInvocationReconciliation(
|
|
|
1257
1431
|
let record = records.get(recordKey);
|
|
1258
1432
|
if (!record) return;
|
|
1259
1433
|
const pending = pendingFinalizations.get(recordKey);
|
|
1434
|
+
const isPromptEnd = kind === "prompt" && frame.type === "agent_end";
|
|
1435
|
+
if (isPromptEnd)
|
|
1436
|
+
recordDeadlineRecoveryCheckpoint("note-transition-start", correlation, {
|
|
1437
|
+
recordStatus: record.status,
|
|
1438
|
+
recordRevision: record.revision,
|
|
1439
|
+
recordTerminalAt: record.terminalAt,
|
|
1440
|
+
pendingFinalization: pending?.finalizedRecord === record,
|
|
1441
|
+
});
|
|
1260
1442
|
if (pending?.finalizedRecord === record) {
|
|
1443
|
+
if (isPromptEnd)
|
|
1444
|
+
recordDeadlineRecoveryCheckpoint("note-transition-awaiting-finalization", correlation, {
|
|
1445
|
+
recordStatus: record.status,
|
|
1446
|
+
recordRevision: record.revision,
|
|
1447
|
+
recordTerminalAt: record.terminalAt,
|
|
1448
|
+
pendingFinalization: true,
|
|
1449
|
+
});
|
|
1261
1450
|
const upgrade = Promise.withResolvers<void>();
|
|
1262
1451
|
pending.upgrades.add(upgrade);
|
|
1263
1452
|
let upgradeError: unknown;
|
|
@@ -1269,6 +1458,13 @@ export function createInvocationReconciliation(
|
|
|
1269
1458
|
// so a real lifecycle event is never swallowed.
|
|
1270
1459
|
}
|
|
1271
1460
|
const current = records.get(recordKey);
|
|
1461
|
+
if (isPromptEnd)
|
|
1462
|
+
recordDeadlineRecoveryCheckpoint("note-transition-finalization-ready", correlation, {
|
|
1463
|
+
recordStatus: current?.status,
|
|
1464
|
+
recordRevision: current?.revision,
|
|
1465
|
+
recordTerminalAt: current?.terminalAt,
|
|
1466
|
+
pendingFinalization: pendingFinalizations.get(recordKey) === pending,
|
|
1467
|
+
});
|
|
1272
1468
|
const incomingOutcome = frame.type === "agent_end" ? canonicalTerminalOutcome(frame.outcome) : undefined;
|
|
1273
1469
|
const providerFailure =
|
|
1274
1470
|
current?.error !== undefined && current.error.code !== "prompt_deadline_exceeded";
|
|
@@ -1650,10 +1846,31 @@ export function createInvocationReconciliation(
|
|
|
1650
1846
|
record.terminalAt === undefined && next.terminalAt !== undefined
|
|
1651
1847
|
? retainPendingTerminalVisibility(recordKey, record)
|
|
1652
1848
|
: undefined;
|
|
1849
|
+
if (isPromptEnd)
|
|
1850
|
+
recordDeadlineRecoveryCheckpoint("note-transition-before-persist", correlation, {
|
|
1851
|
+
recordStatus: next.status,
|
|
1852
|
+
recordRevision: next.revision,
|
|
1853
|
+
recordTerminalAt: next.terminalAt,
|
|
1854
|
+
pendingFinalization: pendingFinalizations.has(recordKey),
|
|
1855
|
+
});
|
|
1653
1856
|
records.set(recordKey, next);
|
|
1654
1857
|
try {
|
|
1655
1858
|
await persist();
|
|
1859
|
+
if (isPromptEnd)
|
|
1860
|
+
recordDeadlineRecoveryCheckpoint("note-transition-persisted", correlation, {
|
|
1861
|
+
recordStatus: next.status,
|
|
1862
|
+
recordRevision: next.revision,
|
|
1863
|
+
recordTerminalAt: next.terminalAt,
|
|
1864
|
+
pendingFinalization: pendingFinalizations.has(recordKey),
|
|
1865
|
+
});
|
|
1656
1866
|
} catch (error) {
|
|
1867
|
+
if (isPromptEnd)
|
|
1868
|
+
recordDeadlineRecoveryCheckpoint("note-transition-persist-failed", correlation, {
|
|
1869
|
+
recordStatus: next.status,
|
|
1870
|
+
recordRevision: next.revision,
|
|
1871
|
+
recordTerminalAt: next.terminalAt,
|
|
1872
|
+
pendingFinalization: pendingFinalizations.has(recordKey),
|
|
1873
|
+
});
|
|
1657
1874
|
if (records.get(recordKey) === next) records.set(recordKey, record);
|
|
1658
1875
|
throw error;
|
|
1659
1876
|
} finally {
|
|
@@ -2027,6 +2244,8 @@ export interface SdkSurfaceFactoryOptions {
|
|
|
2027
2244
|
api: ExtensionAPI;
|
|
2028
2245
|
/** Startup surface that owns host-level permission and lifecycle authority. */
|
|
2029
2246
|
primaryControlSurface?: "cli" | "sdk";
|
|
2247
|
+
/** Private staging controls are advertised only with authoritative transport liveness. */
|
|
2248
|
+
imageUploadsAvailable?: boolean;
|
|
2030
2249
|
policy?: SdkSurfacePolicy;
|
|
2031
2250
|
getInstalledDefinitions?: (capability: string) => unknown | undefined;
|
|
2032
2251
|
getLiveState?: () => { isStreaming: boolean; steeringQueueDepth: number; followupQueueDepth: number };
|
|
@@ -2110,7 +2329,8 @@ function createQuerySurface(
|
|
|
2110
2329
|
};
|
|
2111
2330
|
const getProfileCredentialSessionId = () => ctx.credentialSessionId ?? id;
|
|
2112
2331
|
const profileSettings = (options.settings ?? ctx.settings) as Pick<Settings, "get"> | undefined;
|
|
2113
|
-
const getProfileAvailableModels = (): Model<Api>[] =>
|
|
2332
|
+
const getProfileAvailableModels = (): Model<Api>[] =>
|
|
2333
|
+
ctx.modelRegistry.getAvailable(getProfileCredentialSessionId());
|
|
2114
2334
|
const resolveProfileAvailability = async (
|
|
2115
2335
|
profile: ModelProfileDefinition,
|
|
2116
2336
|
authenticatedProviders: ReadonlySet<string>,
|
|
@@ -2331,7 +2551,7 @@ function createQuerySurface(
|
|
|
2331
2551
|
getDiff,
|
|
2332
2552
|
getUsage: () => ctx.sessionManager.getUsageStatistics(),
|
|
2333
2553
|
getModels: async () => {
|
|
2334
|
-
const models = ctx.modelRegistry.getAll();
|
|
2554
|
+
const models = ctx.modelRegistry.getAll(getProfileCredentialSessionId());
|
|
2335
2555
|
const currentModel = ctx.model;
|
|
2336
2556
|
const currentThinkingLevel = api.getThinkingLevel();
|
|
2337
2557
|
const activeProfile =
|
|
@@ -2454,7 +2674,9 @@ function createQuerySurface(
|
|
|
2454
2674
|
getBranchCandidates: () => ctx.getBranchCandidates(),
|
|
2455
2675
|
getLastAssistant: lastAssistant,
|
|
2456
2676
|
getCapabilities: () => createSdkCapabilities(policy, hasHostTools(), options.primaryControlSurface),
|
|
2457
|
-
getAuthProviders: () => [
|
|
2677
|
+
getAuthProviders: () => [
|
|
2678
|
+
...new Set(ctx.modelRegistry.getAll(getProfileCredentialSessionId()).map(model => model.provider)),
|
|
2679
|
+
],
|
|
2458
2680
|
getActiveProviders: () => ctx.modelRegistry.getActiveProviders(),
|
|
2459
2681
|
getTools: () => {
|
|
2460
2682
|
const tools = typeof (ctx as Partial<ExtensionContext>).getAllTools === "function" ? ctx.getAllTools() : [];
|
|
@@ -2532,7 +2754,11 @@ export function createSdkSurfaceFactory(
|
|
|
2532
2754
|
): SdkSurfaceFactory {
|
|
2533
2755
|
const policy =
|
|
2534
2756
|
options.policy ??
|
|
2535
|
-
createSdkSurfacePolicyForContext(
|
|
2757
|
+
createSdkSurfacePolicyForContext(
|
|
2758
|
+
options.ctx,
|
|
2759
|
+
hasSdkWorkflowGateCapability(options.ctx.workflowGate),
|
|
2760
|
+
options.imageUploadsAvailable,
|
|
2761
|
+
);
|
|
2536
2762
|
const reconciliation =
|
|
2537
2763
|
options.reconciliation ??
|
|
2538
2764
|
createInvocationReconciliation({
|
|
@@ -2918,6 +3144,7 @@ function createControlSurface(
|
|
|
2918
3144
|
) => void,
|
|
2919
3145
|
armPromptDeadline: (correlation: InvocationCorrelation) => void,
|
|
2920
3146
|
steerReconciliation: KindAwareReconciliation,
|
|
3147
|
+
imageUploads: PromptImageUploadStore | undefined,
|
|
2921
3148
|
onPromotedTurn?: (
|
|
2922
3149
|
kind: InvocationKind,
|
|
2923
3150
|
correlation: InvocationCorrelation,
|
|
@@ -2945,7 +3172,15 @@ function createControlSurface(
|
|
|
2945
3172
|
trackGateResolution: <T>(resolution: Promise<T>) => Promise<T> = async resolution => await resolution,
|
|
2946
3173
|
onInvocationCompletionReconciledForTests?: (kind: InvocationKind, correlation: InvocationCorrelation) => void,
|
|
2947
3174
|
publishLifecycleFrame?: (frame: SdkFrame) => void,
|
|
3175
|
+
retainAcceptedImage?: (correlation: InvocationCorrelation, release: () => void) => void,
|
|
3176
|
+
settleAcceptedImageSubmission?: (correlation: InvocationCorrelation) => void,
|
|
3177
|
+
releaseAcceptedImage?: (correlation: InvocationCorrelation) => void,
|
|
2948
3178
|
acceptedQueueCancellations: Map<string, AcceptedQueueCancellation> = new Map(),
|
|
3179
|
+
onQueuedRunStartFailed?: (
|
|
3180
|
+
kind: InvocationKind,
|
|
3181
|
+
correlation: InvocationCorrelation,
|
|
3182
|
+
failure: QueuedRunStartFailure,
|
|
3183
|
+
) => boolean,
|
|
2949
3184
|
): ControlSurface {
|
|
2950
3185
|
const normalizePromptImages = (value: unknown): ImageContent[] => {
|
|
2951
3186
|
if (!Array.isArray(value)) return [];
|
|
@@ -2966,6 +3201,7 @@ function createControlSurface(
|
|
|
2966
3201
|
type InternalSendOptions = NonNullable<Parameters<ExtensionAPI["sendUserMessage"]>[1]> & {
|
|
2967
3202
|
sdkRunCapability?: SdkRunCapability;
|
|
2968
3203
|
expectedSdkRunToken?: string;
|
|
3204
|
+
onQueuedRunStartFailed?: (failure: QueuedRunStartFailure) => void;
|
|
2969
3205
|
};
|
|
2970
3206
|
type InternalSdkApi = Omit<ExtensionAPI, "sendUserMessage"> & {
|
|
2971
3207
|
sendUserMessage: (
|
|
@@ -2976,7 +3212,7 @@ function createControlSurface(
|
|
|
2976
3212
|
const internalApi = api as InternalSdkApi;
|
|
2977
3213
|
const sendSdkUserMessage = (
|
|
2978
3214
|
content: Parameters<ExtensionAPI["sendUserMessage"]>[0],
|
|
2979
|
-
options?:
|
|
3215
|
+
options?: InternalSendOptions,
|
|
2980
3216
|
): Promise<void> => internalApi.sendUserMessage(content, options);
|
|
2981
3217
|
const surfacePolicy =
|
|
2982
3218
|
policy ?? createSdkSurfacePolicyForContext(ctx, hasSdkWorkflowGateCapability(ctx.workflowGate));
|
|
@@ -2986,8 +3222,14 @@ function createControlSurface(
|
|
|
2986
3222
|
const [provider, ...modelId] = id.split("/");
|
|
2987
3223
|
const model =
|
|
2988
3224
|
modelId.length > 0
|
|
2989
|
-
? ctx.modelRegistry.find(
|
|
2990
|
-
|
|
3225
|
+
? ctx.modelRegistry.find(
|
|
3226
|
+
provider,
|
|
3227
|
+
modelId.join("/"),
|
|
3228
|
+
ctx.credentialSessionId ?? ctx.sessionManager.getSessionId(),
|
|
3229
|
+
)
|
|
3230
|
+
: ctx.modelRegistry
|
|
3231
|
+
.getAll(ctx.credentialSessionId ?? ctx.sessionManager.getSessionId())
|
|
3232
|
+
.find(candidate => candidate.id === id);
|
|
2991
3233
|
if (!model) throw Object.assign(new Error(`Model ${id} was not found.`), { code: "invalid_input" });
|
|
2992
3234
|
return model;
|
|
2993
3235
|
};
|
|
@@ -3113,11 +3355,13 @@ function createControlSurface(
|
|
|
3113
3355
|
onDispatchDisposition: (promotion: { startsOwnRun: boolean }) => void;
|
|
3114
3356
|
/** Fired when a queued submission (steering or follow-up) is promoted to its own run (SDK ownership correlation). */
|
|
3115
3357
|
onQueuedPromoted: (promotion?: { startsOwnRun?: boolean; removed?: boolean }) => void;
|
|
3358
|
+
onQueuedRunStartFailed: (failure: QueuedRunStartFailure) => void;
|
|
3116
3359
|
queuedAtDispatch: boolean;
|
|
3117
3360
|
}) => Promise<unknown>,
|
|
3118
3361
|
acceptedFields?: () => Record<string, unknown>,
|
|
3119
3362
|
allowCompletionFallback = false,
|
|
3120
3363
|
alwaysQueued = false,
|
|
3364
|
+
onCreated?: (correlation: InvocationCorrelation) => void,
|
|
3121
3365
|
): Promise<unknown> => {
|
|
3122
3366
|
// Capture the REQUESTING connection at admission: a terminal abort from
|
|
3123
3367
|
// another SDK connection must never stop the prompt this one accepts
|
|
@@ -3126,9 +3370,11 @@ function createControlSurface(
|
|
|
3126
3370
|
const retainedClientRef = normalizeClientRef(clientRef);
|
|
3127
3371
|
reconciliation.admit(kind, retainedClientRef);
|
|
3128
3372
|
const correlation = newCorrelation();
|
|
3373
|
+
onCreated?.(correlation);
|
|
3129
3374
|
const sdkRunToken = `${correlation.commandId}:${correlation.turnId}`;
|
|
3130
3375
|
const sdkRunCapability = createSdkRunCapability(sdkRunToken);
|
|
3131
3376
|
const publishTerminal = (outcome: InvocationOutcome): void => {
|
|
3377
|
+
releaseAcceptedImage?.(correlation);
|
|
3132
3378
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
3133
3379
|
publishLifecycleFrame?.({
|
|
3134
3380
|
type: "agent_end",
|
|
@@ -3162,6 +3408,7 @@ function createControlSurface(
|
|
|
3162
3408
|
};
|
|
3163
3409
|
let accepted = false;
|
|
3164
3410
|
let settled = false;
|
|
3411
|
+
let submission: Promise<unknown> | undefined;
|
|
3165
3412
|
const cancelPreflight = () => {
|
|
3166
3413
|
preflightController.abort();
|
|
3167
3414
|
if (settled) return;
|
|
@@ -3221,14 +3468,16 @@ function createControlSurface(
|
|
|
3221
3468
|
if (kind === "prompt")
|
|
3222
3469
|
acceptedQueueCancellations.set(`${correlation.commandId}:${correlation.turnId}`, queueCancellation);
|
|
3223
3470
|
let promotionStartsOwnRun: boolean | undefined;
|
|
3471
|
+
let queuedRunStartRefused = false;
|
|
3224
3472
|
try {
|
|
3225
|
-
|
|
3473
|
+
submission = Promise.resolve(
|
|
3226
3474
|
run({
|
|
3227
3475
|
onPreflightAccepted: () => void accept().catch(() => undefined),
|
|
3228
3476
|
preflightSignal: preflightController.signal,
|
|
3229
3477
|
sdkRunCapability,
|
|
3230
3478
|
onPreflightAcceptCommit: accept,
|
|
3231
3479
|
onDispatchDisposition: promotion => {
|
|
3480
|
+
if (queuedRunStartRefused) return;
|
|
3232
3481
|
promotionStartsOwnRun = promotion.startsOwnRun;
|
|
3233
3482
|
if (promotion.startsOwnRun === false) {
|
|
3234
3483
|
queueCancellation.queueCandidate = true;
|
|
@@ -3248,6 +3497,7 @@ function createControlSurface(
|
|
|
3248
3497
|
// created at promotion so the submitting connection can
|
|
3249
3498
|
// terminal-abort that turn (review threads P1/P2).
|
|
3250
3499
|
onQueuedPromoted: (promotion?: { startsOwnRun?: boolean; removed?: boolean }) => {
|
|
3500
|
+
if (queuedRunStartRefused) return;
|
|
3251
3501
|
promotionStartsOwnRun = promotion?.startsOwnRun;
|
|
3252
3502
|
if (promotion?.removed) {
|
|
3253
3503
|
queueCancellation.disposition = "removed";
|
|
@@ -3281,11 +3531,29 @@ function createControlSurface(
|
|
|
3281
3531
|
}
|
|
3282
3532
|
void onPromotedTurn?.(kind, correlation, requesterConnectionId, sdkRunToken, promotion);
|
|
3283
3533
|
},
|
|
3534
|
+
onQueuedRunStartFailed: failure => {
|
|
3535
|
+
if (
|
|
3536
|
+
queuedRunStartRefused ||
|
|
3537
|
+
!accepted ||
|
|
3538
|
+
!queueCancellation.queueCandidate ||
|
|
3539
|
+
failure.disposition !== "not-started"
|
|
3540
|
+
)
|
|
3541
|
+
return;
|
|
3542
|
+
// Only the private Core decision for this selected queue entry is proof.
|
|
3543
|
+
// The lifecycle owner rejects contradictory decisions after adoption.
|
|
3544
|
+
queuedRunStartRefused = onQueuedRunStartFailed?.(kind, correlation, failure) === true;
|
|
3545
|
+
if (queuedRunStartRefused) {
|
|
3546
|
+
queueCancellation.disposition = "consumed";
|
|
3547
|
+
queueCancellation.resolveDisposition("consumed");
|
|
3548
|
+
}
|
|
3549
|
+
},
|
|
3284
3550
|
queuedAtDispatch,
|
|
3285
3551
|
}),
|
|
3286
3552
|
);
|
|
3287
3553
|
void submission.then(
|
|
3288
3554
|
result => {
|
|
3555
|
+
settleAcceptedImageSubmission?.(correlation);
|
|
3556
|
+
if (queuedRunStartRefused) return;
|
|
3289
3557
|
if (settled) {
|
|
3290
3558
|
// A resolved submission after preflight acceptance means the work is over
|
|
3291
3559
|
// for every kind. `noteTransition` ignores an already-terminal record, so
|
|
@@ -3296,7 +3564,9 @@ function createControlSurface(
|
|
|
3296
3564
|
// a delayed preflight diverted to steering fires onQueuedPromoted
|
|
3297
3565
|
// with startsOwnRun:false and is now attached to the in-flight run.
|
|
3298
3566
|
// Do not terminalize from the stale snapshot — the run will.
|
|
3299
|
-
|
|
3567
|
+
// Remember actual diversion even after own-run promotion changes
|
|
3568
|
+
// promotionStartsOwnRun: enqueue completion is still not run completion.
|
|
3569
|
+
if (!queueCancellation.queueCandidate && promotionStartsOwnRun !== false) {
|
|
3300
3570
|
// The accepted work settled without its own run still pending:
|
|
3301
3571
|
// retire the pending ownership entry (and with it the
|
|
3302
3572
|
// acceptance-anchored deadline lease, #4668 review) BEFORE
|
|
@@ -3365,7 +3635,10 @@ function createControlSurface(
|
|
|
3365
3635
|
);
|
|
3366
3636
|
},
|
|
3367
3637
|
error => {
|
|
3638
|
+
settleAcceptedImageSubmission?.(correlation);
|
|
3639
|
+
if (queuedRunStartRefused) return;
|
|
3368
3640
|
if (settled) {
|
|
3641
|
+
releaseAcceptedImage?.(correlation);
|
|
3369
3642
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
3370
3643
|
// The submission promise rejects after preflight acceptance only when the
|
|
3371
3644
|
// work itself is over (provider stream interrupt, abort, queue failure).
|
|
@@ -3487,6 +3760,9 @@ function createControlSurface(
|
|
|
3487
3760
|
...(acceptedFields?.() ?? {}),
|
|
3488
3761
|
};
|
|
3489
3762
|
} catch (error) {
|
|
3763
|
+
if (!accepted) preflightController.abort();
|
|
3764
|
+
if (!submission) settleAcceptedImageSubmission?.(correlation);
|
|
3765
|
+
releaseAcceptedImage?.(correlation);
|
|
3490
3766
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
3491
3767
|
if (!accepted) reconciliation.release(kind, retainedClientRef);
|
|
3492
3768
|
throw error;
|
|
@@ -4405,28 +4681,57 @@ function createControlSurface(
|
|
|
4405
4681
|
}
|
|
4406
4682
|
};
|
|
4407
4683
|
return {
|
|
4408
|
-
prompt: async (text, images, clientRef) => {
|
|
4409
|
-
const invalid = validateRequiredPromptText("turn.prompt", { text, images });
|
|
4684
|
+
prompt: async (text, images, clientRef, stagedImages) => {
|
|
4685
|
+
const invalid = validateRequiredPromptText("turn.prompt", { text, images, stagedImages });
|
|
4410
4686
|
if (invalid) throw Object.assign(new Error(invalid.message), { code: invalid.code });
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
//
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4687
|
+
if (stagedImages !== undefined && images !== undefined)
|
|
4688
|
+
throw Object.assign(new Error("Direct and staged images cannot be mixed."), { code: "invalid_input" });
|
|
4689
|
+
if (stagedImages !== undefined && !imageUploads)
|
|
4690
|
+
throw Object.assign(new Error("Image uploads are unavailable."), { code: "operation_prohibited" });
|
|
4691
|
+
let reservation: { images: ImageContent[]; release: () => void } | undefined;
|
|
4692
|
+
let imageCorrelation: InvocationCorrelation | undefined;
|
|
4693
|
+
try {
|
|
4694
|
+
return await submit(
|
|
4695
|
+
"prompt",
|
|
4696
|
+
clientRef,
|
|
4697
|
+
({ queuedAtDispatch, sdkRunCapability, ...options }) => {
|
|
4698
|
+
// submit has reserved reconciliation/clientRef before consuming staged IDs.
|
|
4699
|
+
if (stagedImages !== undefined) {
|
|
4700
|
+
reservation = imageUploads!.redeem(sdkControlRequesterContext.getStore(), stagedImages);
|
|
4701
|
+
if (imageCorrelation && retainAcceptedImage)
|
|
4702
|
+
retainAcceptedImage(imageCorrelation, reservation.release);
|
|
4703
|
+
}
|
|
4704
|
+
return sendSdkUserMessage(
|
|
4705
|
+
typeof images === "undefined" && !reservation
|
|
4706
|
+
? text
|
|
4707
|
+
: ([{ type: "text", text }, ...(reservation?.images ?? normalizePromptImages(images))] as [
|
|
4708
|
+
{ type: "text"; text: string },
|
|
4709
|
+
...ImageContent[],
|
|
4710
|
+
]),
|
|
4711
|
+
{ ...options, sdkRunCapability, ...(queuedAtDispatch ? { queuedAtDispatch: true } : {}) },
|
|
4712
|
+
);
|
|
4426
4713
|
},
|
|
4427
|
-
|
|
4428
|
-
|
|
4714
|
+
undefined,
|
|
4715
|
+
false,
|
|
4716
|
+
false,
|
|
4717
|
+
correlation => {
|
|
4718
|
+
if (retainAcceptedImage) imageCorrelation = correlation;
|
|
4719
|
+
},
|
|
4720
|
+
);
|
|
4721
|
+
} catch (error) {
|
|
4722
|
+
if (imageCorrelation) releaseAcceptedImage?.(imageCorrelation);
|
|
4723
|
+
else reservation?.release();
|
|
4724
|
+
throw error;
|
|
4725
|
+
}
|
|
4429
4726
|
},
|
|
4727
|
+
...(imageUploads
|
|
4728
|
+
? {
|
|
4729
|
+
imageBegin: (input: unknown) => imageUploads.begin(sdkControlRequesterContext.getStore(), input),
|
|
4730
|
+
imageAppend: (input: unknown) => imageUploads.append(sdkControlRequesterContext.getStore(), input),
|
|
4731
|
+
imageFinish: (input: unknown) => imageUploads.finish(sdkControlRequesterContext.getStore(), input),
|
|
4732
|
+
imageDiscard: (input: unknown) => imageUploads.discard(sdkControlRequesterContext.getStore(), input),
|
|
4733
|
+
}
|
|
4734
|
+
: {}),
|
|
4430
4735
|
steer: async (text, clientRef, expectedSdkRunToken) => {
|
|
4431
4736
|
const invalid = validateRequiredPromptText("turn.steer", { text });
|
|
4432
4737
|
if (invalid) throw Object.assign(new Error(invalid.message), { code: invalid.code });
|
|
@@ -4681,7 +4986,9 @@ function createControlSurface(
|
|
|
4681
4986
|
retryLast: () => typed("retry.last"),
|
|
4682
4987
|
retryNow: () => typed("retry.now"),
|
|
4683
4988
|
backgroundBash: () => typed("bash.background"),
|
|
4684
|
-
installedOperations:
|
|
4989
|
+
installedOperations: imageUploads
|
|
4990
|
+
? surfacePolicy.installedControls
|
|
4991
|
+
: new Set([...surfacePolicy.installedControls].filter(operation => !operation.startsWith("turn.image."))),
|
|
4685
4992
|
revisionProvider: resource => (resource === "config" ? String(configRevision.current) : undefined),
|
|
4686
4993
|
};
|
|
4687
4994
|
}
|
|
@@ -4831,6 +5138,89 @@ function quiescingFrame(frame: Record<string, unknown>): Record<string, unknown>
|
|
|
4831
5138
|
|
|
4832
5139
|
/** Install a complete SDK host for a session when notifications are inactive. */
|
|
4833
5140
|
export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: CreateSdkSessionRuntimeOptions): void {
|
|
5141
|
+
const acceptedImages = new Map<
|
|
5142
|
+
string,
|
|
5143
|
+
{
|
|
5144
|
+
store: PromptImageUploadStore;
|
|
5145
|
+
release: () => void;
|
|
5146
|
+
handle?: string;
|
|
5147
|
+
executionStarted?: boolean;
|
|
5148
|
+
submissionPending?: boolean;
|
|
5149
|
+
awaitingAdoption?: boolean;
|
|
5150
|
+
releaseRequested?: boolean;
|
|
5151
|
+
retiring?: boolean;
|
|
5152
|
+
}
|
|
5153
|
+
>();
|
|
5154
|
+
const imageKey = (correlation: InvocationCorrelation): string => `${correlation.commandId}:${correlation.turnId}`;
|
|
5155
|
+
const retireAcceptedImage = (key: string): void => {
|
|
5156
|
+
const reservation = acceptedImages.get(key);
|
|
5157
|
+
if (!reservation) return;
|
|
5158
|
+
if ((reservation.submissionPending || reservation.awaitingAdoption) && !reservation.executionStarted) {
|
|
5159
|
+
reservation.releaseRequested = true;
|
|
5160
|
+
return;
|
|
5161
|
+
}
|
|
5162
|
+
if (reservation.retiring) return;
|
|
5163
|
+
const release = (): void => {
|
|
5164
|
+
if (acceptedImages.get(key) !== reservation) return;
|
|
5165
|
+
acceptedImages.delete(key);
|
|
5166
|
+
reservation.release();
|
|
5167
|
+
};
|
|
5168
|
+
if (!reservation.handle) {
|
|
5169
|
+
if (reservation.executionStarted) {
|
|
5170
|
+
reservation.retiring = true;
|
|
5171
|
+
logger.warn("SDK image capacity retained for execution without an exact resource handle");
|
|
5172
|
+
return;
|
|
5173
|
+
}
|
|
5174
|
+
release();
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
const waitForSettlement = options.terminalAbortSeams?.waitForRunSettlement;
|
|
5178
|
+
if (!waitForSettlement) return;
|
|
5179
|
+
reservation.retiring = true;
|
|
5180
|
+
retirePromptImageReservation(
|
|
5181
|
+
reservation.handle,
|
|
5182
|
+
waitForSettlement,
|
|
5183
|
+
() => acceptedImages.get(key) === reservation,
|
|
5184
|
+
release,
|
|
5185
|
+
);
|
|
5186
|
+
};
|
|
5187
|
+
const releaseAcceptedImage = (correlation: InvocationCorrelation): void =>
|
|
5188
|
+
retireAcceptedImage(imageKey(correlation));
|
|
5189
|
+
const settleAcceptedImageSubmission = (correlation: InvocationCorrelation): void => {
|
|
5190
|
+
const key = imageKey(correlation);
|
|
5191
|
+
const reservation = acceptedImages.get(key);
|
|
5192
|
+
if (!reservation) return;
|
|
5193
|
+
reservation.submissionPending = false;
|
|
5194
|
+
if (reservation.releaseRequested) {
|
|
5195
|
+
reservation.releaseRequested = false;
|
|
5196
|
+
retireAcceptedImage(key);
|
|
5197
|
+
}
|
|
5198
|
+
};
|
|
5199
|
+
const bindAcceptedImage = (correlation: InvocationCorrelation, handle: string | undefined): void => {
|
|
5200
|
+
const reservation = acceptedImages.get(imageKey(correlation));
|
|
5201
|
+
if (!reservation) return;
|
|
5202
|
+
reservation.awaitingAdoption = false;
|
|
5203
|
+
reservation.executionStarted = true;
|
|
5204
|
+
if (handle && reservation.handle === undefined) reservation.handle = handle;
|
|
5205
|
+
if (reservation.releaseRequested) retireAcceptedImage(imageKey(correlation));
|
|
5206
|
+
};
|
|
5207
|
+
// Promotion commits a queued image to its next own-run before agent_start supplies the exact resource handle.
|
|
5208
|
+
const awaitAcceptedImageAdoption = (correlation: InvocationCorrelation): void => {
|
|
5209
|
+
const reservation = acceptedImages.get(imageKey(correlation));
|
|
5210
|
+
if (reservation) reservation.awaitingAdoption = true;
|
|
5211
|
+
};
|
|
5212
|
+
const releaseImagesForStore = (store: PromptImageUploadStore | undefined): void => {
|
|
5213
|
+
for (const [key, reservation] of acceptedImages) {
|
|
5214
|
+
if (reservation.store !== store) continue;
|
|
5215
|
+
retireAcceptedImage(key);
|
|
5216
|
+
}
|
|
5217
|
+
};
|
|
5218
|
+
const pendingImageCleanups = new Set<PromptImageUploadStore>();
|
|
5219
|
+
const completeImageCleanups = (): void => {
|
|
5220
|
+
for (const store of pendingImageCleanups) releaseImagesForStore(store);
|
|
5221
|
+
pendingImageCleanups.clear();
|
|
5222
|
+
};
|
|
5223
|
+
let currentImageUploads: PromptImageUploadStore | undefined;
|
|
4834
5224
|
const acceptedQueueCancellations = new Map<string, AcceptedQueueCancellation>();
|
|
4835
5225
|
const disposeAcceptedQueueCancellations = async (): Promise<void> => {
|
|
4836
5226
|
const cancellations = [...acceptedQueueCancellations.values()];
|
|
@@ -5361,6 +5751,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5361
5751
|
): Promise<void> => {
|
|
5362
5752
|
const current = lifecycleOwner?.state ?? active;
|
|
5363
5753
|
if (!current) return;
|
|
5754
|
+
const imageRunHandle =
|
|
5755
|
+
type === "agent_start" && acceptedImages.size > 0
|
|
5756
|
+
? options.terminalAbortSeams?.getActivePromptHandle()
|
|
5757
|
+
: undefined;
|
|
5364
5758
|
const failureBatch = lifecycleOwner?.batch;
|
|
5365
5759
|
const adoptLifecycleBatch = (batch: LifecycleBatch | undefined): void => {
|
|
5366
5760
|
current.activeInvocation = batch?.invocations[0];
|
|
@@ -5478,6 +5872,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5478
5872
|
adoptLifecycleBatch(batch);
|
|
5479
5873
|
for (const entry of drained) {
|
|
5480
5874
|
if (entry.kind !== "prompt") continue;
|
|
5875
|
+
bindAcceptedImage(entry.correlation, imageRunHandle);
|
|
5481
5876
|
if (current.deadlineManager.isExpiring(entry.correlation))
|
|
5482
5877
|
current.deadlineManager.captureExpiringRun(entry.correlation);
|
|
5483
5878
|
else current.deadlineManager.onRunStarted(entry.correlation);
|
|
@@ -5987,8 +6382,13 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5987
6382
|
}
|
|
5988
6383
|
if (type === "agent_end" && isContinuingMidRunMaintenanceOutcome(maintenanceOutcome)) return;
|
|
5989
6384
|
if (type === "agent_end") {
|
|
5990
|
-
for (const invocation of transitions)
|
|
6385
|
+
for (const invocation of transitions) {
|
|
6386
|
+
// A deadline can capture this event while the exact run still owns
|
|
6387
|
+
// pending tools. Its settlement owner, not event capture, releases quota.
|
|
6388
|
+
if (!current.deadlineManager.shouldDeferTerminalTransition(invocation.correlation))
|
|
6389
|
+
releaseAcceptedImage(invocation.correlation);
|
|
5991
6390
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, invocation.correlation);
|
|
6391
|
+
}
|
|
5992
6392
|
if (current.lifecycleEpoch !== eventLifecycleEpoch) {
|
|
5993
6393
|
// A successor agent_start won the lifecycle race while this event's
|
|
5994
6394
|
// durable transitions were awaiting persistence. Retire the ended
|
|
@@ -6408,6 +6808,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6408
6808
|
const startRuntime = async (ctx: ExtensionContext): Promise<void> => {
|
|
6409
6809
|
if (active) return;
|
|
6410
6810
|
if (acceptedQueueCancellations.size > 0) await disposeAcceptedQueueCancellations();
|
|
6811
|
+
completeImageCleanups();
|
|
6411
6812
|
if (active) return;
|
|
6412
6813
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
6413
6814
|
const stateRoot = path.join(ctx.cwd, ".gjc", "state");
|
|
@@ -6419,16 +6820,38 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6419
6820
|
(typeof ctx.sessionManager.getSessionFile === "function" ? ctx.sessionManager.getSessionFile() : undefined) ??
|
|
6420
6821
|
resolveReconciliationSessionFile(undefined, stateRoot, sessionId);
|
|
6421
6822
|
const sessionIdentity = `${sessionId}\u0000${sessionFile}`;
|
|
6823
|
+
const reconciliationInstrumentation = options.terminalAbortSeams?.onPersistenceInstrumentationForTests
|
|
6824
|
+
? (event: ReconciliationStoreInstrumentationEvent) => {
|
|
6825
|
+
try {
|
|
6826
|
+
options.terminalAbortSeams?.onPersistenceInstrumentationForTests?.(event as never);
|
|
6827
|
+
} catch {}
|
|
6828
|
+
}
|
|
6829
|
+
: undefined;
|
|
6422
6830
|
const reconciliationStore =
|
|
6423
6831
|
options.terminalAbortSeams?.getReconciliationStore?.() ??
|
|
6424
|
-
createReconciliationStore({
|
|
6425
|
-
|
|
6832
|
+
createReconciliationStore({
|
|
6833
|
+
sessionFile,
|
|
6834
|
+
sessionId,
|
|
6835
|
+
onInstrumentationForTests: reconciliationInstrumentation,
|
|
6836
|
+
});
|
|
6837
|
+
const reconciliation = createInvocationReconciliation({
|
|
6838
|
+
store: reconciliationStore,
|
|
6839
|
+
onDeadlineRecoveryCheckpointForTests: options.terminalAbortSeams?.onDeadlineRecoveryCheckpointForTests,
|
|
6840
|
+
onPersistenceInstrumentationForTests: options.terminalAbortSeams?.onPersistenceInstrumentationForTests,
|
|
6841
|
+
});
|
|
6426
6842
|
await reconciliation.hydrate();
|
|
6427
6843
|
const steerReconciliation = createKindAwareReconciliation({
|
|
6428
6844
|
store: reconciliationStore as never,
|
|
6429
6845
|
ownedKinds: ["steer"],
|
|
6430
6846
|
});
|
|
6431
6847
|
await steerReconciliation.hydrateFromStore();
|
|
6848
|
+
const recordDeadlineRecoveryCheckpoint = (checkpoint: Omit<SdkOnlyDeadlineRecoveryCheckpoint, "at">): void => {
|
|
6849
|
+
try {
|
|
6850
|
+
options.terminalAbortSeams?.onDeadlineRecoveryCheckpointForTests?.({ ...checkpoint, at: Date.now() });
|
|
6851
|
+
} catch {
|
|
6852
|
+
// A test observer must never participate in deadline correctness.
|
|
6853
|
+
}
|
|
6854
|
+
};
|
|
6432
6855
|
const deadlineManager = new PromptDeadlineManager({
|
|
6433
6856
|
reconciliation,
|
|
6434
6857
|
getLeaseMs: () => resolveSdkPromptDeadlineMs(options.settings?.get("sdk.promptDeadlineMs" as never)),
|
|
@@ -6448,6 +6871,9 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6448
6871
|
const seams = options.terminalAbortSeams;
|
|
6449
6872
|
const handle = seams?.getActivePromptHandle();
|
|
6450
6873
|
const epoch = seams?.getTerminalTurnEpoch();
|
|
6874
|
+
// Capture lifecycle ownership independently of settlement proof. Without
|
|
6875
|
+
// a tool-set observer the later terminalization hook still fails closed,
|
|
6876
|
+
// but a matching real end must remain private and recoverable meanwhile.
|
|
6451
6877
|
if (!seams || !handle || epoch === undefined) return;
|
|
6452
6878
|
const existing = deadlineTerminalizationObservations.get(key);
|
|
6453
6879
|
if (existing) return () => cleanupDeadlineTerminalizationObservation(key, existing);
|
|
@@ -6495,8 +6921,36 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6495
6921
|
const observation = deadlineTerminalizationObservations.get(target);
|
|
6496
6922
|
const seams = options.terminalAbortSeams;
|
|
6497
6923
|
const pendingToolExecutions = seams?.pendingToolExecutions;
|
|
6498
|
-
|
|
6499
|
-
|
|
6924
|
+
recordDeadlineRecoveryCheckpoint({
|
|
6925
|
+
stage: "terminalization-start",
|
|
6926
|
+
correlation,
|
|
6927
|
+
eventCaptured: observation?.eventCaptured,
|
|
6928
|
+
eventPrepared: observation?.eventPrepared,
|
|
6929
|
+
terminalCommitted: observation?.terminalCommitted,
|
|
6930
|
+
});
|
|
6931
|
+
let pendingToolCount: number | undefined;
|
|
6932
|
+
const finish = (result: PromptDeadlineTerminalization, reason: string): PromptDeadlineTerminalization => {
|
|
6933
|
+
recordDeadlineRecoveryCheckpoint({
|
|
6934
|
+
stage: "terminalization-result",
|
|
6935
|
+
correlation,
|
|
6936
|
+
result,
|
|
6937
|
+
reason,
|
|
6938
|
+
eventCaptured: observation?.eventCaptured,
|
|
6939
|
+
eventPrepared: observation?.eventPrepared,
|
|
6940
|
+
terminalCommitted: observation?.terminalCommitted,
|
|
6941
|
+
pendingToolCount,
|
|
6942
|
+
});
|
|
6943
|
+
// Notify deadline manager about termination result to adjust retry strategy
|
|
6944
|
+
deadlineManager.notifyTerminationResult(correlation, result, reason);
|
|
6945
|
+
return result;
|
|
6946
|
+
};
|
|
6947
|
+
if (!observation || !seams || !pendingToolExecutions)
|
|
6948
|
+
return finish("uncertain", "missing-observation-or-tool-observer");
|
|
6949
|
+
const pendingTools = () => {
|
|
6950
|
+
const pending = pendingToolExecutions(observation.handle);
|
|
6951
|
+
pendingToolCount = pending.length;
|
|
6952
|
+
return pending;
|
|
6953
|
+
};
|
|
6500
6954
|
const awaitEventPreparation = async (): Promise<boolean> => {
|
|
6501
6955
|
if (observation.eventPrepared) return true;
|
|
6502
6956
|
const captured = await Promise.race([
|
|
@@ -6507,7 +6961,15 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6507
6961
|
};
|
|
6508
6962
|
if (observation.eventCaptured) {
|
|
6509
6963
|
const prepared = await awaitEventPreparation();
|
|
6510
|
-
|
|
6964
|
+
pendingToolCount = pendingTools().length;
|
|
6965
|
+
return finish(
|
|
6966
|
+
prepared && pendingToolCount === 0 ? "settled" : "uncertain",
|
|
6967
|
+
!prepared
|
|
6968
|
+
? "captured-event-not-prepared"
|
|
6969
|
+
: pendingToolCount > 0
|
|
6970
|
+
? "tools-still-pending"
|
|
6971
|
+
: "captured-event-and-tools-settled",
|
|
6972
|
+
);
|
|
6511
6973
|
}
|
|
6512
6974
|
|
|
6513
6975
|
if (!observation.eventCaptured) {
|
|
@@ -6521,13 +6983,13 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6521
6983
|
entry => lifecycleCorrelationKey(entry.correlation) === target,
|
|
6522
6984
|
)
|
|
6523
6985
|
)
|
|
6524
|
-
return "uncertain";
|
|
6986
|
+
return finish("uncertain", "lifecycle-batch-no-longer-owns-correlation");
|
|
6525
6987
|
if (
|
|
6526
6988
|
!isCurrent() ||
|
|
6527
6989
|
seams.getActivePromptHandle() !== observation.handle ||
|
|
6528
6990
|
seams.getTerminalTurnEpoch() !== observation.epoch
|
|
6529
6991
|
)
|
|
6530
|
-
return "uncertain";
|
|
6992
|
+
return finish("uncertain", "lease-or-active-run-mismatch");
|
|
6531
6993
|
}
|
|
6532
6994
|
await waitForToolCallBoundary({
|
|
6533
6995
|
pending: pendingTools,
|
|
@@ -6536,14 +6998,22 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6536
6998
|
});
|
|
6537
6999
|
if (observation.eventCaptured) {
|
|
6538
7000
|
const prepared = await awaitEventPreparation();
|
|
6539
|
-
|
|
7001
|
+
pendingToolCount = pendingTools().length;
|
|
7002
|
+
return finish(
|
|
7003
|
+
prepared && pendingToolCount === 0 ? "settled" : "uncertain",
|
|
7004
|
+
!prepared
|
|
7005
|
+
? "captured-event-not-prepared"
|
|
7006
|
+
: pendingToolCount > 0
|
|
7007
|
+
? "tools-still-pending"
|
|
7008
|
+
: "captured-event-and-tools-settled",
|
|
7009
|
+
);
|
|
6540
7010
|
}
|
|
6541
7011
|
if (
|
|
6542
7012
|
!isCurrent() ||
|
|
6543
7013
|
seams.getActivePromptHandle() !== observation.handle ||
|
|
6544
7014
|
seams.getTerminalTurnEpoch() !== observation.epoch
|
|
6545
7015
|
)
|
|
6546
|
-
return "uncertain";
|
|
7016
|
+
return finish("uncertain", "lease-or-active-run-mismatch-after-tool-boundary");
|
|
6547
7017
|
|
|
6548
7018
|
let steeringSnapshotToken: number | undefined;
|
|
6549
7019
|
let proof: { status: string; terminalScope?: unknown } | undefined;
|
|
@@ -6560,7 +7030,15 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6560
7030
|
});
|
|
6561
7031
|
if (proof.status !== "settled") {
|
|
6562
7032
|
const prepared = await awaitEventPreparation();
|
|
6563
|
-
|
|
7033
|
+
pendingToolCount = pendingTools().length;
|
|
7034
|
+
return finish(
|
|
7035
|
+
prepared && pendingToolCount === 0 ? "settled" : "uncertain",
|
|
7036
|
+
!prepared
|
|
7037
|
+
? "abort-proof-not-settled-and-event-not-prepared"
|
|
7038
|
+
: pendingToolCount > 0
|
|
7039
|
+
? "tools-still-pending"
|
|
7040
|
+
: "captured-event-and-tools-settled",
|
|
7041
|
+
);
|
|
6564
7042
|
}
|
|
6565
7043
|
const terminalScope = proof.terminalScope as
|
|
6566
7044
|
| { abortedAttemptEpoch?: unknown; lineageIdHash?: unknown }
|
|
@@ -6570,17 +7048,26 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6570
7048
|
typeof terminalScope.lineageIdHash !== "string" ||
|
|
6571
7049
|
isOwnedAttemptRegistrationIncomplete(terminalScope.lineageIdHash, observation.epoch)
|
|
6572
7050
|
)
|
|
6573
|
-
return "uncertain";
|
|
7051
|
+
return finish("uncertain", "terminal-scope-mismatch");
|
|
6574
7052
|
const exactJobs = findOwnedRegistrationsForTurn(terminalScope.lineageIdHash, observation.epoch);
|
|
6575
7053
|
if (exactJobs.length > 0) {
|
|
6576
7054
|
const endpointId = exactJobs[0]?.endpointId;
|
|
6577
7055
|
const manager = AsyncJobManager.forEndpoint(endpointId) ?? AsyncJobManager.instance();
|
|
6578
|
-
if (!manager || (await settleOwnedWork(manager, exactJobs, 500)) !== "stopped")
|
|
7056
|
+
if (!manager || (await settleOwnedWork(manager, exactJobs, 500)) !== "stopped")
|
|
7057
|
+
return finish("uncertain", "owned-work-not-stopped");
|
|
6579
7058
|
}
|
|
6580
7059
|
const prepared = await awaitEventPreparation();
|
|
6581
|
-
|
|
7060
|
+
pendingToolCount = pendingTools().length;
|
|
7061
|
+
return finish(
|
|
7062
|
+
prepared && pendingToolCount === 0 ? "settled" : "uncertain",
|
|
7063
|
+
!prepared
|
|
7064
|
+
? "captured-event-not-prepared"
|
|
7065
|
+
: pendingToolCount > 0
|
|
7066
|
+
? "tools-still-pending"
|
|
7067
|
+
: "captured-event-and-tools-settled",
|
|
7068
|
+
);
|
|
6582
7069
|
} catch {
|
|
6583
|
-
return "uncertain";
|
|
7070
|
+
return finish("uncertain", "terminal-abort-error");
|
|
6584
7071
|
} finally {
|
|
6585
7072
|
if (steeringSnapshotToken !== undefined && proof?.status !== "settled")
|
|
6586
7073
|
seams.discardTerminalAbortSteeringSnapshot?.(steeringSnapshotToken);
|
|
@@ -6592,21 +7079,57 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6592
7079
|
const terminalOutcome = observation
|
|
6593
7080
|
? deadlineOutcomeAfterAbort(correlation, observation.terminalOutcome, observation.terminalHasActivity)
|
|
6594
7081
|
: undefined;
|
|
6595
|
-
|
|
7082
|
+
let terminalIsCurrent: boolean | undefined;
|
|
7083
|
+
let pendingToolCount = -1;
|
|
7084
|
+
const recordPublicationResult = (
|
|
7085
|
+
result: SdkOnlyDeadlineRecoveryCheckpoint["result"],
|
|
7086
|
+
reason: string,
|
|
7087
|
+
errorCodeValue?: string,
|
|
7088
|
+
): void => {
|
|
7089
|
+
recordDeadlineRecoveryCheckpoint({
|
|
7090
|
+
stage: "publication-result",
|
|
7091
|
+
correlation,
|
|
7092
|
+
result,
|
|
7093
|
+
reason,
|
|
7094
|
+
eventCaptured: observation?.eventCaptured,
|
|
7095
|
+
eventPrepared: observation?.eventPrepared,
|
|
7096
|
+
terminalCommitted: observation?.terminalCommitted,
|
|
7097
|
+
leaseCurrent: terminalIsCurrent,
|
|
7098
|
+
pendingToolCount,
|
|
7099
|
+
errorCode: errorCodeValue,
|
|
7100
|
+
});
|
|
7101
|
+
};
|
|
7102
|
+
recordDeadlineRecoveryCheckpoint({
|
|
7103
|
+
stage: "publication-start",
|
|
7104
|
+
correlation,
|
|
7105
|
+
eventCaptured: observation?.eventCaptured,
|
|
7106
|
+
eventPrepared: observation?.eventPrepared,
|
|
7107
|
+
terminalCommitted: observation?.terminalCommitted,
|
|
7108
|
+
});
|
|
7109
|
+
if (!observation?.eventCaptured || !observation.eventPrepared || terminalOutcome === undefined) {
|
|
7110
|
+
recordPublicationResult("rejected", "missing-captured-terminal-outcome");
|
|
6596
7111
|
return false;
|
|
7112
|
+
}
|
|
6597
7113
|
if (!observation.terminalCommitted) {
|
|
6598
7114
|
const pendingTools = options.terminalAbortSeams?.pendingToolExecutions;
|
|
6599
|
-
let terminalIsCurrent = false;
|
|
6600
|
-
let pendingToolCount = -1;
|
|
6601
7115
|
try {
|
|
6602
7116
|
terminalIsCurrent = isCurrent();
|
|
6603
7117
|
if (pendingTools) pendingToolCount = pendingTools(observation.handle).length;
|
|
6604
|
-
} catch {
|
|
7118
|
+
} catch (error) {
|
|
7119
|
+
recordPublicationResult("rejected", "publication-precheck-threw", errorCode(error));
|
|
7120
|
+
return false;
|
|
7121
|
+
}
|
|
7122
|
+
if (!terminalIsCurrent || !pendingTools || pendingToolCount > 0) {
|
|
7123
|
+
recordPublicationResult("rejected", "publication-precheck-rejected");
|
|
6605
7124
|
return false;
|
|
6606
7125
|
}
|
|
6607
|
-
if (!terminalIsCurrent || !pendingTools || pendingToolCount > 0) return false;
|
|
6608
7126
|
try {
|
|
6609
|
-
|
|
7127
|
+
terminalIsCurrent = isCurrent();
|
|
7128
|
+
pendingToolCount = pendingTools(observation.handle).length;
|
|
7129
|
+
if (!terminalIsCurrent || pendingToolCount > 0) {
|
|
7130
|
+
recordPublicationResult("rejected", "publication-recheck-rejected");
|
|
7131
|
+
return false;
|
|
7132
|
+
}
|
|
6610
7133
|
// Commit the authoritative Q26 result before exposing its terminal
|
|
6611
7134
|
// boundary; a crash between publication and this write would strand
|
|
6612
7135
|
// restart recovery with only a hidden pending claim.
|
|
@@ -6619,40 +7142,39 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6619
7142
|
observation.terminalOutcome = terminalOutcome;
|
|
6620
7143
|
observation.terminalCommitted = true;
|
|
6621
7144
|
observation.clearUnrecordedFailure?.();
|
|
6622
|
-
} catch {
|
|
7145
|
+
} catch (error) {
|
|
7146
|
+
recordPublicationResult("rejected", "terminal-transition-persist-failed", errorCode(error));
|
|
6623
7147
|
return false;
|
|
6624
7148
|
}
|
|
6625
7149
|
}
|
|
6626
7150
|
const published = publishDeadlineTerminalBoundary(observation, correlation, terminalOutcome);
|
|
7151
|
+
recordPublicationResult(published ? "published" : "not-published", "terminal-boundary-result");
|
|
6627
7152
|
return { outcome: terminalOutcome, published } satisfies PromptDeadlinePublicationResult;
|
|
6628
7153
|
},
|
|
6629
7154
|
// Persist the agent's uncommitted work before the retirement below tears
|
|
6630
7155
|
// the session down (#5583). Best effort by contract: failures are logged
|
|
6631
7156
|
// inside the flush and the deadline outcome is unaffected.
|
|
6632
7157
|
onDeadlineExceeded: async (_correlation, signal, isCurrent) => {
|
|
6633
|
-
|
|
6634
|
-
//
|
|
6635
|
-
|
|
6636
|
-
// honours the default inside a linked worktree the session owns.
|
|
6637
|
-
const configured = options.settings?.get("sdk.flushWorktreeOnDeadline" as never);
|
|
6638
|
-
const hasExplicitSetting =
|
|
6639
|
-
typeof options.settings?.has === "function"
|
|
6640
|
-
? options.settings.has("sdk.flushWorktreeOnDeadline" as never)
|
|
6641
|
-
: configured === true;
|
|
6642
|
-
const explicitOptIn = hasExplicitSetting === true && configured === true;
|
|
6643
|
-
await flushWorktreeOnPromptDeadline(ctx.cwd, {
|
|
6644
|
-
agentDir: options.settings?.getAgentDir(),
|
|
6645
|
-
explicitOptIn,
|
|
6646
|
-
isCurrent,
|
|
6647
|
-
signal,
|
|
6648
|
-
});
|
|
7158
|
+
// Project `.gjc` files are not an opt-in. The shared helper reads the
|
|
7159
|
+
// user/global layer and runtime overrides only (#5583).
|
|
7160
|
+
await autosaveWorktreeOnPromptDeadline(ctx.cwd, options.settings, { isCurrent, signal });
|
|
6649
7161
|
},
|
|
6650
7162
|
onExpired: correlation => {
|
|
7163
|
+
// The manager invokes retirement only after confirmed settlement and
|
|
7164
|
+
// durable terminalization (including recovered queue removals), never
|
|
7165
|
+
// merely because an inactivity timer fired or execution is uncertain.
|
|
7166
|
+
releaseAcceptedImage(correlation);
|
|
6651
7167
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation, true);
|
|
6652
7168
|
const owner = lifecycleOwnerHolder.state;
|
|
6653
|
-
if (
|
|
6654
|
-
|
|
6655
|
-
|
|
7169
|
+
if (owner) {
|
|
7170
|
+
removeLifecycleReferences(owner, correlation);
|
|
7171
|
+
maybeRetireLifecycleOwner(owner);
|
|
7172
|
+
}
|
|
7173
|
+
recordDeadlineRecoveryCheckpoint({
|
|
7174
|
+
stage: "deadline-recovery-retired",
|
|
7175
|
+
correlation,
|
|
7176
|
+
terminalCommitted: true,
|
|
7177
|
+
});
|
|
6656
7178
|
},
|
|
6657
7179
|
});
|
|
6658
7180
|
const pending: Array<{
|
|
@@ -6706,6 +7228,9 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6706
7228
|
ctx,
|
|
6707
7229
|
id: sessionId,
|
|
6708
7230
|
api,
|
|
7231
|
+
imageUploadsAvailable:
|
|
7232
|
+
typeof transport.isConnectionOpen === "function" &&
|
|
7233
|
+
typeof options.terminalAbortSeams?.waitForRunSettlement === "function",
|
|
6709
7234
|
reconciliation,
|
|
6710
7235
|
turnResultLookup: selector => reconciliation.lookupResult(selector.kind, selector),
|
|
6711
7236
|
steerStatusLookup: selector => steerReconciliation.lookupSteer(selector),
|
|
@@ -6836,7 +7361,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6836
7361
|
});
|
|
6837
7362
|
// 3. Release recovery ownership ONLY after durable terminalization.
|
|
6838
7363
|
deadlineManager.clear(correlation);
|
|
6839
|
-
if (kind === "prompt")
|
|
7364
|
+
if (kind === "prompt") {
|
|
7365
|
+
releaseAcceptedImage(correlation);
|
|
7366
|
+
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
7367
|
+
}
|
|
6840
7368
|
return true;
|
|
6841
7369
|
} catch (transitionError) {
|
|
6842
7370
|
// Keep prompt recovery leased; skill recovery has no prompt lease.
|
|
@@ -6854,6 +7382,14 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6854
7382
|
return attempt(3);
|
|
6855
7383
|
};
|
|
6856
7384
|
|
|
7385
|
+
// This is a private SDK transport facility. A frame-observer cache cannot
|
|
7386
|
+
// establish that the authenticated connection still exists at redemption.
|
|
7387
|
+
const imageUploads =
|
|
7388
|
+
typeof transport.isConnectionOpen === "function" &&
|
|
7389
|
+
typeof options.terminalAbortSeams?.waitForRunSettlement === "function"
|
|
7390
|
+
? new PromptImageUploadStore(connectionId => transport.isConnectionOpen?.(connectionId) === true)
|
|
7391
|
+
: undefined;
|
|
7392
|
+
currentImageUploads = imageUploads;
|
|
6857
7393
|
const controlSurface = createControlSurface(
|
|
6858
7394
|
ctx,
|
|
6859
7395
|
api,
|
|
@@ -6890,7 +7426,13 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6890
7426
|
},
|
|
6891
7427
|
correlation => deadlineManager.onAccepted(correlation),
|
|
6892
7428
|
steerReconciliation,
|
|
7429
|
+
imageUploads,
|
|
6893
7430
|
(kind, correlation, connectionId, sdkRunToken, promotion) => {
|
|
7431
|
+
if (!promotion?.removed) {
|
|
7432
|
+
if (promotion?.startsOwnRun === false)
|
|
7433
|
+
bindAcceptedImage(correlation, options.terminalAbortSeams?.getActivePromptHandle());
|
|
7434
|
+
else awaitAcceptedImageAdoption(correlation);
|
|
7435
|
+
}
|
|
6894
7436
|
const bindPromotedToken = (batch?: LifecycleBatch): void => {
|
|
6895
7437
|
const owner = lifecycleOwnerHolder.state;
|
|
6896
7438
|
if (sdkRunToken && owner)
|
|
@@ -6946,6 +7488,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6946
7488
|
// with it.
|
|
6947
7489
|
const current = active;
|
|
6948
7490
|
if (current?.lifecycleActive) {
|
|
7491
|
+
bindAcceptedImage(correlation, options.terminalAbortSeams?.getActivePromptHandle());
|
|
6949
7492
|
// If this correlation was already admitted as own-run via the
|
|
6950
7493
|
// idle snapshot (onAccepted -> pending) but later diverted to
|
|
6951
7494
|
// steering and consumed in-run, move it from pending to the
|
|
@@ -7106,7 +7649,47 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7106
7649
|
throw error;
|
|
7107
7650
|
}
|
|
7108
7651
|
},
|
|
7652
|
+
(correlation, release) => {
|
|
7653
|
+
if (imageUploads)
|
|
7654
|
+
acceptedImages.set(imageKey(correlation), {
|
|
7655
|
+
store: imageUploads,
|
|
7656
|
+
release,
|
|
7657
|
+
submissionPending: true,
|
|
7658
|
+
});
|
|
7659
|
+
},
|
|
7660
|
+
settleAcceptedImageSubmission,
|
|
7661
|
+
releaseAcceptedImage,
|
|
7109
7662
|
acceptedQueueCancellations,
|
|
7663
|
+
(kind, correlation, failure) => {
|
|
7664
|
+
const record = reconciliation.lookup(kind, correlation) as { status?: string };
|
|
7665
|
+
const reservation = acceptedImages.get(imageKey(correlation));
|
|
7666
|
+
const terminal = record.status === "terminal_ok" || record.status === "failed";
|
|
7667
|
+
if (record.status !== "accepted" && !(terminal && reservation)) return false;
|
|
7668
|
+
// A known execution/handle retains exact-ledger authority even if a
|
|
7669
|
+
// contradictory callback arrives later. Never downgrade adoption.
|
|
7670
|
+
if (reservation?.executionStarted || reservation?.handle) return false;
|
|
7671
|
+
const adopted = active?.openLifecycleBatches.some(batch =>
|
|
7672
|
+
[...batch.invocations, ...batch.attachedInvocations].some(
|
|
7673
|
+
entry => entry.kind === kind && imageKey(entry.correlation) === imageKey(correlation),
|
|
7674
|
+
),
|
|
7675
|
+
);
|
|
7676
|
+
if (adopted) return false;
|
|
7677
|
+
const index = pending.findIndex(entry => entry.kind === kind && entry.correlation === correlation);
|
|
7678
|
+
if (index >= 0) {
|
|
7679
|
+
const [removed] = pending.splice(index, 1);
|
|
7680
|
+
if (removed) lifecycleRunOwners.delete(removed.sdkRunToken);
|
|
7681
|
+
}
|
|
7682
|
+
if (reservation) {
|
|
7683
|
+
reservation.awaitingAdoption = false;
|
|
7684
|
+
// Ordinary retirement still waits for an outstanding enqueue producer.
|
|
7685
|
+
retireAcceptedImage(imageKey(correlation));
|
|
7686
|
+
}
|
|
7687
|
+
// Keep the existing durable-first failure/recovery owner; this is not
|
|
7688
|
+
// queue removal, and cannot publish cancellation or synthetic success.
|
|
7689
|
+
if (!terminal)
|
|
7690
|
+
void terminalizeAbandonedSubmission(kind, correlation, rejectionRecoveryIntent(failure.error));
|
|
7691
|
+
return true;
|
|
7692
|
+
},
|
|
7110
7693
|
);
|
|
7111
7694
|
const installProviderDefinitions = (capability: string, definitions: unknown): void => {
|
|
7112
7695
|
if (capability === "permission") {
|
|
@@ -7174,6 +7757,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7174
7757
|
};
|
|
7175
7758
|
runtime = new SessionSdkSessionRuntime({
|
|
7176
7759
|
transport,
|
|
7760
|
+
onDisconnected: connectionId => imageUploads?.disconnect(connectionId),
|
|
7177
7761
|
eventRevision: () =>
|
|
7178
7762
|
typeof (ctx as Partial<ExtensionContext>).getTranscript === "function"
|
|
7179
7763
|
? ctx.getTranscript().length
|
|
@@ -7624,6 +8208,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7624
8208
|
} catch (error) {
|
|
7625
8209
|
runtimeOwner.quiesceInput();
|
|
7626
8210
|
runtimeOwner.fenceGateResolutions();
|
|
8211
|
+
imageUploads?.close();
|
|
7627
8212
|
active = undefined;
|
|
7628
8213
|
let queueCancellationFailure: unknown;
|
|
7629
8214
|
try {
|
|
@@ -7682,7 +8267,8 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7682
8267
|
}
|
|
7683
8268
|
cursors.close();
|
|
7684
8269
|
await revisions.close().catch(() => undefined);
|
|
7685
|
-
if (queueCancellationFailure !== undefined)
|
|
8270
|
+
if (queueCancellationFailure !== undefined) {
|
|
8271
|
+
if (imageUploads) pendingImageCleanups.add(imageUploads);
|
|
7686
8272
|
throw Object.assign(
|
|
7687
8273
|
new AggregateError(
|
|
7688
8274
|
[error, queueCancellationFailure],
|
|
@@ -7690,14 +8276,25 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7690
8276
|
),
|
|
7691
8277
|
{ code: "sdk_reconciliation_teardown_failed" },
|
|
7692
8278
|
);
|
|
8279
|
+
}
|
|
8280
|
+
if (currentImageUploads === imageUploads) currentImageUploads = undefined;
|
|
8281
|
+
releaseImagesForStore(imageUploads);
|
|
7693
8282
|
throw error;
|
|
7694
8283
|
}
|
|
7695
8284
|
};
|
|
7696
8285
|
const stopActive = async (cancelSkillRecovery = false, unregisterReason?: "detached_idle"): Promise<void> => {
|
|
7697
8286
|
const current = active;
|
|
7698
|
-
if (!current)
|
|
8287
|
+
if (!current) {
|
|
8288
|
+
if (pendingImageCleanups.size > 0) {
|
|
8289
|
+
await disposeAcceptedQueueCancellations();
|
|
8290
|
+
completeImageCleanups();
|
|
8291
|
+
}
|
|
8292
|
+
return;
|
|
8293
|
+
}
|
|
7699
8294
|
current.quiesceInput();
|
|
7700
8295
|
current.fenceGateResolutions();
|
|
8296
|
+
const imageUploads = currentImageUploads;
|
|
8297
|
+
imageUploads?.close();
|
|
7701
8298
|
let queueCancellationFailure: unknown;
|
|
7702
8299
|
try {
|
|
7703
8300
|
await disposeAcceptedQueueCancellations();
|
|
@@ -7763,6 +8360,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7763
8360
|
allowLockContention: cancelSkillRecovery,
|
|
7764
8361
|
...(unregisterReason === undefined ? {} : { unregisterReason }),
|
|
7765
8362
|
});
|
|
8363
|
+
if (queueCancellationFailure === undefined) {
|
|
8364
|
+
if (currentImageUploads === imageUploads) currentImageUploads = undefined;
|
|
8365
|
+
releaseImagesForStore(imageUploads);
|
|
8366
|
+
} else if (imageUploads) pendingImageCleanups.add(imageUploads);
|
|
7766
8367
|
} catch (error) {
|
|
7767
8368
|
// Keep the immutable owner available for a retry when transport teardown
|
|
7768
8369
|
// fails after quiescing. Clearing `active` before stop prevents a second
|