@gajae-code/coding-agent 0.13.3 → 0.14.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 +184 -2
- package/README.md +2 -0
- package/bin/gajaessi.js +4 -0
- package/dist/types/async/job-manager.d.ts +55 -2
- package/dist/types/async/support.d.ts +14 -0
- package/dist/types/capability/mcp.d.ts +6 -0
- package/dist/types/capability/types.d.ts +6 -0
- package/dist/types/cli/accounts-cli.d.ts +19 -0
- package/dist/types/cli/args.d.ts +3 -0
- package/dist/types/cli/config-cli.d.ts +6 -0
- package/dist/types/cli/crash-cli.d.ts +10 -0
- package/dist/types/cli/customize-doctor.d.ts +145 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/mcp-cli.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/root-flags.d.ts +12 -0
- package/dist/types/cli/setup-cli.d.ts +3 -1
- package/dist/types/cli/skills-cli.d.ts +2 -1
- package/dist/types/cli/update-cli.d.ts +19 -1
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/accounts.d.ts +40 -0
- package/dist/types/commands/crash.d.ts +24 -0
- package/dist/types/commands/customize.d.ts +22 -0
- package/dist/types/commands/daemon.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/commands/setup.d.ts +6 -0
- package/dist/types/commands/skills.d.ts +6 -1
- package/dist/types/commands/update.d.ts +7 -0
- package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
- package/dist/types/config/config-schema-version.d.ts +10 -0
- package/dist/types/config/file-lock.d.ts +18 -0
- package/dist/types/config/model-profile-activation.d.ts +3 -1
- package/dist/types/config/model-registry.d.ts +9 -1
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +70 -6
- package/dist/types/config/settings.d.ts +63 -0
- package/dist/types/config/skill-settings-defaults.d.ts +26 -0
- package/dist/types/config/task-delegation.d.ts +39 -0
- package/dist/types/config/update-channel.d.ts +10 -0
- package/dist/types/coordinator-mcp/server.d.ts +5 -1
- package/dist/types/crash/index-store.d.ts +87 -0
- package/dist/types/crash/nudge.d.ts +50 -0
- package/dist/types/crash/record-loader.d.ts +17 -0
- package/dist/types/crash/report.d.ts +134 -0
- package/dist/types/crash/sanitize.d.ts +25 -0
- package/dist/types/cursor.d.ts +13 -2
- package/dist/types/customization/import.d.ts +37 -0
- package/dist/types/customization/index.d.ts +4 -0
- package/dist/types/customization/inventory.d.ts +25 -0
- package/dist/types/customization/mutations.d.ts +32 -0
- package/dist/types/customization/types.d.ts +115 -0
- package/dist/types/daemon/control-types.d.ts +8 -1
- package/dist/types/daemon/operator-contract.d.ts +4 -4
- package/dist/types/discovery/claude.d.ts +39 -1
- package/dist/types/discovery/codex.d.ts +39 -1
- package/dist/types/discovery/index.d.ts +2 -0
- package/dist/types/discovery/mcp-compat.d.ts +30 -0
- package/dist/types/edit/renderer.d.ts +23 -0
- package/dist/types/extensibility/extensions/runner.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -4
- package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
- package/dist/types/extensibility/hooks/loader.d.ts +19 -5
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
- package/dist/types/extensibility/skill-management.d.ts +107 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
- package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
- package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
- package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
- package/dist/types/hooks/events.d.ts +60 -0
- package/dist/types/hooks/normalize.d.ts +83 -0
- package/dist/types/hooks/skill-state.d.ts +9 -2
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/acp/abort-scope.d.ts +13 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -4
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
- package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
- package/dist/types/modes/components/customization/index.d.ts +1 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +8 -30
- package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
- package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
- package/dist/types/modes/components/oauth-selector.d.ts +10 -4
- package/dist/types/modes/components/pet-capability.d.ts +11 -25
- package/dist/types/modes/components/status-line/gh.d.ts +6 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
- package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
- package/dist/types/runtime-mcp/config.d.ts +9 -0
- package/dist/types/runtime-mcp/manager.d.ts +26 -1
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
- package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
- package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
- package/dist/types/runtime-mcp/protocol.d.ts +278 -0
- package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
- package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
- package/dist/types/runtime-mcp/types.d.ts +102 -10
- package/dist/types/sdk/acp/adapter.d.ts +26 -5
- package/dist/types/sdk/broker/broker.d.ts +3 -55
- package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
- package/dist/types/sdk/broker/session-index.d.ts +14 -5
- package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
- package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
- package/dist/types/sdk/bus/index.d.ts +85 -99
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
- package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
- package/dist/types/sdk/bus/notification-service.d.ts +52 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
- package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
- package/dist/types/sdk/bus/session-control.d.ts +25 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
- package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
- package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
- package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
- package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
- package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
- package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
- package/dist/types/sdk/cli/rows.d.ts +2 -11
- package/dist/types/sdk/cli/session-cli.d.ts +15 -25
- package/dist/types/sdk/client/client.d.ts +97 -1
- package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +18 -5
- package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
- package/dist/types/sdk/host/host.d.ts +15 -1
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/host/session-runtime.d.ts +127 -7
- package/dist/types/sdk/host/types.d.ts +1 -1
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +2 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
- package/dist/types/sdk/lifecycle/client.d.ts +59 -0
- package/dist/types/sdk/lifecycle/index.d.ts +3 -0
- package/dist/types/sdk/lifecycle/service.d.ts +232 -0
- package/dist/types/sdk/mcp/server.d.ts +9 -6
- package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
- package/dist/types/sdk/prompt-status.d.ts +10 -3
- package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
- package/dist/types/sdk/receipt-state.d.ts +17 -0
- package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
- package/dist/types/sdk/router/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +152 -0
- package/dist/types/sdk/session-list.d.ts +22 -0
- package/dist/types/sdk/session-reconnect.d.ts +43 -1
- package/dist/types/sdk/session.d.ts +34 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
- package/dist/types/session/account-inventory.d.ts +86 -0
- package/dist/types/session/agent-session.d.ts +145 -6
- package/dist/types/session/agent-storage.d.ts +10 -0
- package/dist/types/session/coordinator-tool-label.d.ts +28 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/secure-token-file.d.ts +3 -0
- package/dist/types/session/session-manager.d.ts +40 -1
- package/dist/types/session/startup-auth-config.d.ts +44 -0
- package/dist/types/session/terminal-abort.d.ts +378 -0
- package/dist/types/session/yield-queue.d.ts +7 -0
- package/dist/types/session-import/claude.d.ts +26 -0
- package/dist/types/session-import/codex.d.ts +88 -0
- package/dist/types/session-import/command.d.ts +35 -0
- package/dist/types/session-import/detect.d.ts +22 -0
- package/dist/types/session-import/index.d.ts +7 -0
- package/dist/types/session-import/provider-codex.d.ts +22 -0
- package/dist/types/session-import/provider-service.d.ts +38 -0
- package/dist/types/session-import/redact.d.ts +23 -0
- package/dist/types/session-import/service.d.ts +47 -0
- package/dist/types/session-import/types.d.ts +120 -0
- package/dist/types/setup/paseo/check.d.ts +16 -0
- package/dist/types/setup/paseo/install-saga.d.ts +115 -0
- package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
- package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
- package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
- package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
- package/dist/types/setup/paseo/provider-config.d.ts +35 -0
- package/dist/types/setup/paseo/remove.d.ts +14 -0
- package/dist/types/setup/paseo/result-types.d.ts +59 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
- package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
- package/dist/types/skill-state/active-state.d.ts +5 -1
- package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
- package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
- package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
- package/dist/types/slash-commands/types.d.ts +7 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/tools/ask-answer-registry.d.ts +6 -3
- package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
- package/dist/types/tools/bash.d.ts +2 -6
- package/dist/types/tools/browser/attach.d.ts +11 -0
- package/dist/types/tools/browser/launch.d.ts +20 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +9 -2
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/monitor.d.ts +1 -1
- package/dist/types/tools/skill-discovery.d.ts +8 -1
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
- package/dist/types/utils/herdr-pane.d.ts +76 -0
- package/package.json +19 -9
- package/scripts/build-binary.ts +2 -1
- package/scripts/build-sdk-package-smoke.ts +2 -13
- package/scripts/capture-sticky-viewport-showcase.ts +0 -1
- package/scripts/compile-args.ts +2 -0
- package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
- package/scripts/generate-sdk-operation-inventory.ts +28 -0
- package/scripts/generate-telegram-baseline-manifest.ts +1 -0
- package/scripts/generate-tool-catalog.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
- package/src/async/job-manager.ts +352 -17
- package/src/async/support.ts +25 -0
- package/src/autoresearch/dashboard.ts +28 -13
- package/src/capability/index.ts +3 -1
- package/src/capability/mcp.ts +6 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/accounts-cli.ts +395 -0
- package/src/cli/args.ts +23 -0
- package/src/cli/auth-broker-cli.ts +136 -66
- package/src/cli/auth-gateway-cli.ts +94 -70
- package/src/cli/config-cli.ts +27 -2
- package/src/cli/crash-cli.ts +78 -0
- package/src/cli/customize-doctor.ts +1605 -0
- package/src/cli/daemon-cli.ts +12 -3
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/mcp-cli.ts +128 -60
- package/src/cli/notify-cli.ts +8 -14
- package/src/cli/root-flags.ts +8 -0
- package/src/cli/setup-cli.ts +145 -0
- package/src/cli/skills-cli.ts +38 -2
- package/src/cli/update-cli.ts +167 -12
- package/src/cli.ts +4 -0
- package/src/commands/accounts.ts +71 -0
- package/src/commands/crash.ts +35 -0
- package/src/commands/customize.ts +84 -0
- package/src/commands/daemon.ts +2 -2
- package/src/commands/deep-interview.ts +2 -0
- package/src/commands/launch.ts +1 -0
- package/src/commands/mcp.ts +12 -7
- package/src/commands/ralplan.ts +2 -0
- package/src/commands/sdk.ts +65 -32
- package/src/commands/setup.ts +5 -0
- package/src/commands/skills.ts +14 -4
- package/src/commands/ultragoal.ts +10 -1
- package/src/commands/update.ts +26 -15
- package/src/config/atomic-yaml-patch-worker.ts +57 -0
- package/src/config/atomic-yaml-patch.ts +769 -29
- package/src/config/config-schema-version.ts +10 -0
- package/src/config/file-lock.ts +70 -1
- package/src/config/mcp-schema.json +52 -14
- package/src/config/model-profile-activation.ts +20 -1
- package/src/config/model-profiles.ts +29 -19
- package/src/config/model-registry.ts +132 -21
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +76 -6
- package/src/config/settings.ts +4038 -199
- package/src/config/skill-settings-defaults.ts +41 -3
- package/src/config/task-delegation.ts +87 -0
- package/src/config/update-channel.ts +23 -0
- package/src/coordinator-mcp/server.ts +377 -308
- package/src/crash/index-store.ts +723 -0
- package/src/crash/nudge.ts +89 -0
- package/src/crash/record-loader.ts +149 -0
- package/src/crash/report.ts +551 -0
- package/src/crash/sanitize.ts +129 -0
- package/src/cursor.ts +36 -10
- package/src/customization/import.ts +946 -0
- package/src/customization/index.ts +4 -0
- package/src/customization/inventory.ts +335 -0
- package/src/customization/mutations.ts +141 -0
- package/src/customization/types.ts +223 -0
- package/src/daemon/control-types.ts +8 -1
- package/src/daemon/operator-contract.ts +6 -6
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
- package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
- package/src/discovery/claude-hooks.ts +38 -0
- package/src/discovery/claude.ts +142 -209
- package/src/discovery/codex-hooks.ts +38 -0
- package/src/discovery/codex.ts +132 -198
- package/src/discovery/helpers.ts +8 -1
- package/src/discovery/index.ts +2 -0
- package/src/discovery/mcp-compat.ts +234 -0
- package/src/discovery/mcp-json.ts +6 -0
- package/src/edit/renderer.ts +40 -0
- package/src/edit/streaming.ts +4 -2
- package/src/exec/bash-executor.ts +24 -20
- package/src/export/html/index.ts +51 -2
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +17 -4
- package/src/extensibility/extensions/types.ts +31 -6
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
- package/src/extensibility/gjc-plugins/installer.ts +9 -5
- package/src/extensibility/gjc-plugins/observability.ts +8 -2
- package/src/extensibility/gjc-plugins/registry.ts +13 -2
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
- package/src/extensibility/gjc-plugins/schema.ts +222 -39
- package/src/extensibility/hooks/loader.ts +154 -10
- package/src/extensibility/runtime-skill-discovery.ts +257 -60
- package/src/extensibility/skill-management.ts +368 -0
- package/src/extensibility/skills.ts +52 -16
- package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
- package/src/gjc-runtime/launch-worktree.ts +56 -2
- package/src/gjc-runtime/linux-proc.ts +7 -1
- package/src/gjc-runtime/ralplan-runtime.ts +100 -164
- package/src/gjc-runtime/session-state-lock.ts +992 -0
- package/src/gjc-runtime/session-state-sidecar.ts +644 -142
- package/src/gjc-runtime/state-writer.ts +39 -14
- package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
- package/src/gjc-runtime/ultragoal-guard.ts +11 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
- package/src/gjc-runtime/workflow-settings.ts +596 -0
- package/src/goals/tools/goal-tool.ts +6 -1
- package/src/hooks/events.ts +291 -0
- package/src/hooks/native-skill-hook.ts +4 -0
- package/src/hooks/normalize.ts +369 -0
- package/src/hooks/skill-state.ts +165 -46
- package/src/internal-urls/docs-index.generated.ts +40 -32
- package/src/main.ts +32 -5
- package/src/migrate/mcp-mapper.ts +25 -2
- package/src/modes/acp/abort-scope.ts +29 -0
- package/src/modes/acp/acp-agent.ts +423 -98
- package/src/modes/components/assistant-message.ts +6 -1
- package/src/modes/components/customization/customization-dashboard.ts +443 -0
- package/src/modes/components/customization/import-wizard.ts +372 -0
- package/src/modes/components/customization/index.ts +1 -0
- package/src/modes/components/gajae-pet-widget.ts +539 -213
- package/src/modes/components/irc-sidebar.ts +11 -0
- package/src/modes/components/iterm-pet-transport.ts +585 -0
- package/src/modes/components/oauth-selector.ts +277 -86
- package/src/modes/components/pet-capability.ts +39 -40
- package/src/modes/components/settings-selector.ts +9 -0
- package/src/modes/components/status-line/gh.ts +45 -1
- package/src/modes/components/status-line/segments.ts +16 -1
- package/src/modes/components/tool-status-header.ts +13 -3
- package/src/modes/controllers/command-controller.ts +20 -8
- package/src/modes/controllers/extension-ui-controller.ts +56 -257
- package/src/modes/controllers/input-controller.ts +10 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
- package/src/modes/controllers/selector-controller.ts +177 -72
- package/src/modes/controllers/stt-controller.ts +3 -3
- package/src/modes/interactive-mode.ts +88 -16
- package/src/modes/print-mode.ts +35 -6
- package/src/modes/runtime-init.ts +21 -68
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/injected-user-submission.ts +25 -0
- package/src/modes/utils/terminal-bell.ts +15 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/critic.md +1 -1
- package/src/prompts/system/rlm-research.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -2
- package/src/prompts/tools/bash.md +7 -1
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/cron.md +6 -6
- package/src/prompts/tools/read.md +0 -2
- package/src/prompts/tools/skill-discovery.md +4 -2
- package/src/runtime-mcp/client.ts +427 -23
- package/src/runtime-mcp/config.ts +37 -5
- package/src/runtime-mcp/manager-pool.test.ts +56 -0
- package/src/runtime-mcp/manager.ts +52 -2
- package/src/runtime-mcp/oauth-discovery.ts +13 -0
- package/src/runtime-mcp/oauth-flow.ts +46 -1
- package/src/runtime-mcp/pool-key.ts +5 -1
- package/src/runtime-mcp/pool.test.ts +9 -0
- package/src/runtime-mcp/protocol.test.ts +353 -0
- package/src/runtime-mcp/protocol.ts +624 -0
- package/src/runtime-mcp/scope-config.ts +78 -0
- package/src/runtime-mcp/tool-bridge.ts +44 -10
- package/src/runtime-mcp/transports/http.ts +84 -13
- package/src/runtime-mcp/types.ts +123 -12
- package/src/sdk/acp/adapter.ts +328 -97
- package/src/sdk/broker/broker.ts +160 -741
- package/src/sdk/broker/ensure.ts +9 -1
- package/src/sdk/broker/lifecycle.ts +510 -116
- package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
- package/src/sdk/broker/process-incarnation.ts +19 -0
- package/src/sdk/broker/session-index.ts +516 -117
- package/src/sdk/broker/startup-failure.ts +28 -5
- package/src/sdk/broker/transport.ts +1 -13
- package/src/sdk/bus/chat-daemon-cli.ts +4 -2
- package/src/sdk/bus/chat-daemon-control.ts +67 -6
- package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
- package/src/sdk/bus/config.ts +13 -0
- package/src/sdk/bus/conversation-store.ts +59 -171
- package/src/sdk/bus/daemon-paths.ts +20 -0
- package/src/sdk/bus/discord-conversation.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +524 -126
- package/src/sdk/bus/discord-live-provider.ts +206 -79
- package/src/sdk/bus/existing-thread-readiness.ts +4 -97
- package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
- package/src/sdk/bus/index.ts +2480 -456
- package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
- package/src/sdk/bus/lifecycle-commands.ts +2 -2
- package/src/sdk/bus/managed-daemon.ts +2 -145
- package/src/sdk/bus/notification-service.ts +196 -0
- package/src/sdk/bus/operator-runtime.ts +1 -1
- package/src/sdk/bus/prompt-reconciliation.ts +24 -15
- package/src/sdk/bus/reconciliation-store.ts +553 -24
- package/src/sdk/bus/session-control.ts +49 -3
- package/src/sdk/bus/slack-conversation.ts +4 -0
- package/src/sdk/bus/slack-daemon.ts +733 -134
- package/src/sdk/bus/slack-live-provider.ts +65 -15
- package/src/sdk/bus/slack-provider.ts +4 -0
- package/src/sdk/bus/slack-thread-binding.ts +9 -104
- package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
- package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
- package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
- package/src/sdk/bus/telegram-daemon-control.ts +3 -13
- package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
- package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
- package/src/sdk/bus/telegram-daemon.ts +2552 -2280
- package/src/sdk/bus/telegram-reference.ts +1 -226
- package/src/sdk/bus/telegram-setup.ts +2 -2
- package/src/sdk/bus/threaded-render.ts +1 -0
- package/src/sdk/bus/topic-registry.ts +279 -146
- package/src/sdk/cli/rows.ts +2 -35
- package/src/sdk/cli/session-cli.ts +776 -700
- package/src/sdk/client/client.ts +892 -1
- package/src/sdk/host/control/dispatch.ts +93 -58
- package/src/sdk/host/control/index.ts +1 -1
- package/src/sdk/host/control/operations.ts +18 -10
- package/src/sdk/host/control/runtime-gate.ts +40 -0
- package/src/sdk/host/host.ts +63 -12
- package/src/sdk/host/query/handlers.ts +66 -7
- package/src/sdk/host/reverse-leases.ts +2 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +2787 -34
- package/src/sdk/host/session-runtime.ts +1489 -101
- package/src/sdk/host/surface-policy.ts +12 -8
- package/src/sdk/host/types.ts +1 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
- package/src/sdk/host/websocket-transport.ts +85 -50
- package/src/sdk/index.ts +2 -1
- package/src/sdk/lifecycle/broker-client.ts +53 -0
- package/src/sdk/lifecycle/client.ts +234 -0
- package/src/sdk/lifecycle/index.ts +3 -0
- package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
- package/src/sdk/lifecycle/service.ts +720 -0
- package/src/sdk/mcp/server.ts +226 -188
- package/src/sdk/prompt-deadline-lease.ts +55 -0
- package/src/sdk/prompt-deadline-manager.ts +148 -0
- package/src/sdk/prompt-status.ts +11 -3
- package/src/sdk/protocol/operation-inventory.generated.json +272 -129
- package/src/sdk/protocol/operation-registry.ts +71 -8
- package/src/sdk/receipt-state.ts +43 -0
- package/src/sdk/reconciliation-extensions.ts +14 -0
- package/src/sdk/router/index.ts +1 -0
- package/src/sdk/router/session-router.ts +1658 -0
- package/src/sdk/session-list.ts +93 -0
- package/src/sdk/session-reconnect.ts +45 -1
- package/src/sdk/session.ts +716 -120
- package/src/sdk/transport/serve-cli.ts +18 -34
- package/src/session/account-inventory.ts +603 -0
- package/src/session/agent-session.ts +2857 -582
- package/src/session/agent-storage.ts +35 -0
- package/src/session/blob-store.ts +32 -1
- package/src/session/coordinator-tool-label.ts +40 -0
- package/src/session/internal/managed-session-scope.ts +4 -0
- package/src/session/internal/managed-session-storage.ts +173 -12
- package/src/session/messages.ts +1 -1
- package/src/session/secure-token-file.ts +80 -0
- package/src/session/session-manager.ts +730 -58
- package/src/session/session-storage.ts +25 -16
- package/src/session/startup-auth-config.ts +297 -0
- package/src/session/terminal-abort.ts +1253 -0
- package/src/session/yield-queue.ts +57 -15
- package/src/session-import/claude.ts +334 -0
- package/src/session-import/codex.ts +1033 -0
- package/src/session-import/command.ts +77 -0
- package/src/session-import/detect.ts +133 -0
- package/src/session-import/index.ts +7 -0
- package/src/session-import/provider-codex.ts +327 -0
- package/src/session-import/provider-service.ts +457 -0
- package/src/session-import/redact.ts +128 -0
- package/src/session-import/service.ts +1264 -0
- package/src/session-import/types.ts +148 -0
- package/src/setup/paseo/check.ts +213 -0
- package/src/setup/paseo/install-saga.ts +293 -0
- package/src/setup/paseo/json-publisher.ts +263 -0
- package/src/setup/paseo/orchestration-preferences.ts +86 -0
- package/src/setup/paseo/paseo-ownership.ts +224 -0
- package/src/setup/paseo/paseo-setup.ts +253 -0
- package/src/setup/paseo/provider-config.ts +139 -0
- package/src/setup/paseo/remove.ts +183 -0
- package/src/setup/paseo/result-types.ts +79 -0
- package/src/setup/paseo/setup-deps.ts +175 -0
- package/src/setup/paseo/skills-bridge.ts +260 -0
- package/src/skill-state/active-state.ts +21 -11
- package/src/slash-commands/acp-builtins.ts +9 -1
- package/src/slash-commands/builtin-registry.ts +98 -16
- package/src/slash-commands/helpers/credential-switch.ts +143 -0
- package/src/slash-commands/helpers/mcp.ts +9 -3
- package/src/slash-commands/helpers/usage-report.ts +61 -56
- package/src/slash-commands/types.ts +7 -2
- package/src/task/executor.ts +30 -11
- package/src/task/index.ts +66 -7
- package/src/task/render.ts +23 -9
- package/src/tools/ask-answer-registry.ts +23 -7
- package/src/tools/ask.ts +8 -4
- package/src/tools/bash-sleep-advisory.ts +115 -0
- package/src/tools/bash.ts +98 -18
- package/src/tools/browser/attach.ts +71 -3
- package/src/tools/browser/launch.ts +86 -13
- package/src/tools/browser/profile-discovery.ts +57 -7
- package/src/tools/browser/render.ts +8 -1
- package/src/tools/browser.ts +130 -21
- package/src/tools/computer.enforcement.test.ts +1 -1
- package/src/tools/computer.ts +3 -2
- package/src/tools/cron.ts +12 -1
- package/src/tools/image-gen.ts +27 -16
- package/src/tools/index.ts +8 -0
- package/src/tools/job.ts +33 -6
- package/src/tools/monitor.ts +41 -2
- package/src/tools/skill-discovery.ts +21 -29
- package/src/tools/subagent.ts +142 -24
- package/src/tools/tool-catalog.generated.ts +6 -6
- package/src/tools/ultragoal-ask-guard.ts +10 -3
- package/src/utils/herdr-pane.ts +345 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/scrapers/index.ts +6 -4
- package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
- package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
- package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
- package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
- package/dist/types/runtime/lazy-service.test.d.ts +0 -1
- package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
- package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
- package/dist/types/runtime-mcp/config.test.d.ts +0 -1
- package/dist/types/runtime-mcp/http.test.d.ts +0 -1
- package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
- package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
- package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
- package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
- package/dist/types/sdk/elevation/audit.d.ts +0 -93
- package/dist/types/sdk/elevation/capability.d.ts +0 -3
- package/dist/types/sdk/elevation/digest.d.ts +0 -72
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
- package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
- package/dist/types/sdk/elevation/owner.d.ts +0 -48
- package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
- package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
- package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
- package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
- package/dist/types/session/auth-broker-config.d.ts +0 -13
- package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
- package/dist/types/skills/skills.test.d.ts +0 -1
- package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
- package/dist/types/task/token-log.test.d.ts +0 -1
- package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
- package/dist/types/tools/descriptors.test.d.ts +0 -1
- package/scripts/g005-daemon-smoke.ts +0 -180
- package/scripts/g011-daemon-path-smoke.ts +0 -153
- package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
- package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
- package/src/sdk/bus/telegram-cli.ts +0 -212
- package/src/sdk/elevation/allowlist.ts +0 -54
- package/src/sdk/elevation/audit.ts +0 -208
- package/src/sdk/elevation/capability.ts +0 -41
- package/src/sdk/elevation/digest.ts +0 -129
- package/src/sdk/elevation/dispatch-receipt.ts +0 -184
- package/src/sdk/elevation/grant-ledger.ts +0 -1142
- package/src/sdk/elevation/owner.ts +0 -109
- package/src/session/auth-broker-config.ts +0 -109
- /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
- /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
- /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type AuthenticateResponse,
|
|
8
8
|
type AuthMethod,
|
|
9
9
|
type AvailableCommand,
|
|
10
|
+
type CancelNotification,
|
|
10
11
|
type ClientCapabilities,
|
|
11
12
|
type CloseSessionRequest,
|
|
12
13
|
type CloseSessionResponse,
|
|
@@ -49,9 +50,13 @@ import { resolveAcpFinalText } from "../../sdk/acp/final-text";
|
|
|
49
50
|
import { ACP_MCP_LIFECYCLE_TIMEOUT_MS, type SessionLifecycleMcpServer } from "../../sdk/acp/mcp";
|
|
50
51
|
import { ensureBroker } from "../../sdk/broker/ensure";
|
|
51
52
|
import { readSdkBrokerDiscovery, SdkClient, SdkClientError } from "../../sdk/client";
|
|
53
|
+
import type { AbortScope } from "../../sdk/host/control/operations";
|
|
52
54
|
import { SYNTHETIC_PROVIDER_ID } from "../../sdk/model-profile-namespace";
|
|
53
55
|
import type { SdkPromptTerminalOutcome } from "../../sdk/prompt-status";
|
|
54
56
|
import { PromptActivity, type PromptWatchdogClock, systemPromptWatchdogClock } from "../../sdk/prompt-watchdog";
|
|
57
|
+
import { type SessionAttachment, SessionRouter, type SessionRouterFrame } from "../../sdk/router";
|
|
58
|
+
import { SessionListTraversalError, sessionListPageFromResponse, traverseSessionList } from "../../sdk/session-list";
|
|
59
|
+
import { resolveAcpAbortScope } from "./abort-scope";
|
|
55
60
|
import {
|
|
56
61
|
buildToolCallStartUpdate,
|
|
57
62
|
mapAgentSessionEventToAcpSessionUpdates,
|
|
@@ -70,7 +75,6 @@ const ACP_CUSTOM_MODEL_PRESET = "__custom__";
|
|
|
70
75
|
const THINKING_CONFIG_ID = "thinking";
|
|
71
76
|
const SESSION_PAGE_SIZE = 50;
|
|
72
77
|
const MAX_ACP_REPLAY_PAGES = 10_000;
|
|
73
|
-
const MAX_ACP_SESSION_LIST_PAGES = 10_000;
|
|
74
78
|
/** Bounded retention of settled prompt correlations so late duplicates stay closed. */
|
|
75
79
|
const SETTLED_PROMPT_CORRELATION_RETENTION = 16;
|
|
76
80
|
/**
|
|
@@ -117,6 +121,19 @@ interface PromptWaiter {
|
|
|
117
121
|
cancelWatchdog?: () => void;
|
|
118
122
|
/** What the host is observably doing — a tool running, a model call unanswered — and the bound that follows from it. */
|
|
119
123
|
activity: PromptActivity;
|
|
124
|
+
/** Coordinates a prompt-control rejection racing an acknowledged ACP cancellation. */
|
|
125
|
+
cancelAttempt?: Promise<boolean>;
|
|
126
|
+
/** Whether ANY overlapping cancel attempt for this prompt was acknowledged:
|
|
127
|
+
* a later failed attempt must not erase an earlier success (review thread P2). */
|
|
128
|
+
cancelAcknowledged?: boolean;
|
|
129
|
+
/** In-flight cancel attempts for this prompt: cancellation intent must stay
|
|
130
|
+
* set while any attempt can still acknowledge, so a failure only clears
|
|
131
|
+
* the shared flag when no attempt remains pending (review thread P2). */
|
|
132
|
+
pendingCancelAttempts?: number;
|
|
133
|
+
/** Shared resolver for cancelAttempt: ANY successful attempt resolves it
|
|
134
|
+
* immediately (no request-order serialization), and the LAST failing
|
|
135
|
+
* attempt resolves false (review thread P2). */
|
|
136
|
+
cancelAttemptResolve?: (acknowledged: boolean) => void;
|
|
120
137
|
resolve: (response: PromptResponse) => void;
|
|
121
138
|
reject: (error: Error) => void;
|
|
122
139
|
}
|
|
@@ -129,6 +146,7 @@ type PendingAttachment = { epoch: number; task: Promise<void> };
|
|
|
129
146
|
type SessionRecord = {
|
|
130
147
|
cwd: string;
|
|
131
148
|
adapter: AcpSdkAdapter;
|
|
149
|
+
attachment: SessionAttachment;
|
|
132
150
|
closeIdempotencyKey: string;
|
|
133
151
|
unsubscribe: () => void;
|
|
134
152
|
reconnectUnsubscribe: () => void;
|
|
@@ -156,13 +174,10 @@ function promptWaiterRetired(record: SessionRecord, waiter: PromptWaiter): boole
|
|
|
156
174
|
return waiter.settled || record.activePrompt !== waiter;
|
|
157
175
|
}
|
|
158
176
|
|
|
159
|
-
type Endpoint = { url: string; token: string };
|
|
160
|
-
|
|
161
177
|
type BrokerSession = {
|
|
162
178
|
sessionId: string;
|
|
163
179
|
locator?: { repo?: string };
|
|
164
180
|
live?: boolean;
|
|
165
|
-
terminal?: boolean;
|
|
166
181
|
endpointGeneration?: number;
|
|
167
182
|
endpointMtimeMs?: number;
|
|
168
183
|
title?: string;
|
|
@@ -196,40 +211,44 @@ function object(value: unknown): JsonObject | undefined {
|
|
|
196
211
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? (value as JsonObject) : undefined;
|
|
197
212
|
}
|
|
198
213
|
|
|
214
|
+
function isAcpUnavailableSlashCommand(text: string): boolean {
|
|
215
|
+
return /^\/import-session(?:\s|:|$)/u.test(text);
|
|
216
|
+
}
|
|
217
|
+
|
|
199
218
|
async function collectAcpSessionList(
|
|
200
219
|
request: (input: JsonObject) => Promise<unknown>,
|
|
201
220
|
input: JsonObject = {},
|
|
202
221
|
): Promise<JsonObject> {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
222
|
+
try {
|
|
223
|
+
const pages = await traverseSessionList(
|
|
224
|
+
input,
|
|
225
|
+
async pageInput => {
|
|
226
|
+
const response = await request(pageInput);
|
|
227
|
+
const envelope = object(response);
|
|
228
|
+
if (envelope?.ok === false) {
|
|
229
|
+
const failure = object(envelope.error);
|
|
230
|
+
throw new AcpSdkAdapterError(
|
|
231
|
+
typeof failure?.code === "string" ? failure.code : "broker_error",
|
|
232
|
+
typeof failure?.message === "string" ? failure.message : "session.list failed",
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return response;
|
|
236
|
+
},
|
|
237
|
+
response => sessionListPageFromResponse(response),
|
|
238
|
+
);
|
|
239
|
+
const aggregate: JsonObject = {};
|
|
240
|
+
const sessions: unknown[] = [];
|
|
241
|
+
for (const { page } of pages) {
|
|
242
|
+
for (const [key, value] of Object.entries(page)) {
|
|
218
243
|
if (key !== "sessions" && key !== "continuationCursor") aggregate[key] = value;
|
|
219
244
|
}
|
|
220
|
-
|
|
221
|
-
const nextCursor =
|
|
222
|
-
typeof listing.continuationCursor === "string" && listing.continuationCursor.length > 0
|
|
223
|
-
? listing.continuationCursor
|
|
224
|
-
: undefined;
|
|
225
|
-
if (nextCursor) {
|
|
226
|
-
cursor = nextCursor;
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
245
|
+
sessions.push(...page.sessions);
|
|
229
246
|
}
|
|
230
247
|
return { ...aggregate, sessions };
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof SessionListTraversalError) throw new AcpSdkAdapterError("protocol_error", error.message);
|
|
250
|
+
throw error;
|
|
231
251
|
}
|
|
232
|
-
throw new AcpSdkAdapterError("protocol_error", "session.list exceeded the page budget.");
|
|
233
252
|
}
|
|
234
253
|
|
|
235
254
|
function aggregateAcpFailure(code: string, message: string, failures: unknown[]): AcpSdkAdapterError {
|
|
@@ -253,11 +272,7 @@ export function paginateAcpSessions(
|
|
|
253
272
|
(value): value is BrokerSession & { locator: { repo: string } } =>
|
|
254
273
|
typeof value?.sessionId === "string" && typeof value.locator?.repo === "string",
|
|
255
274
|
)
|
|
256
|
-
.filter(value => !cwd || value.locator.repo === cwd)
|
|
257
|
-
// DR-1 retains closed/unregistered rows in the broker index for inspect
|
|
258
|
-
// and offline tail; ACP's session/list advertises only loadable sessions,
|
|
259
|
-
// so terminal rows are excluded here (matching the pre-DR-1 wire shape).
|
|
260
|
-
.filter(value => value.terminal !== true);
|
|
275
|
+
.filter(value => !cwd || value.locator.repo === cwd);
|
|
261
276
|
const sessions = filtered.slice(offset, offset + SESSION_PAGE_SIZE).map(value => {
|
|
262
277
|
const metadata = sessionMetadata.get(value.sessionId);
|
|
263
278
|
const updatedAt =
|
|
@@ -286,15 +301,6 @@ export function paginateAcpSessions(
|
|
|
286
301
|
};
|
|
287
302
|
}
|
|
288
303
|
|
|
289
|
-
function endpoint(value: unknown): Endpoint {
|
|
290
|
-
const candidate = object(value);
|
|
291
|
-
const result = object(candidate?.result) ?? candidate;
|
|
292
|
-
const nested = object(result?.endpoint) ?? result;
|
|
293
|
-
if (typeof nested?.url !== "string" || typeof nested.token !== "string")
|
|
294
|
-
throw new AcpSdkAdapterError("unavailable", "SDK lifecycle response omitted a session endpoint.");
|
|
295
|
-
return { url: nested.url, token: nested.token };
|
|
296
|
-
}
|
|
297
|
-
|
|
298
304
|
function sessionId(value: unknown): string {
|
|
299
305
|
const candidate = object(value);
|
|
300
306
|
const result = object(candidate?.result) ?? candidate;
|
|
@@ -383,6 +389,26 @@ function promptAcknowledgement(value: unknown): PromptCorrelation | undefined {
|
|
|
383
389
|
if (typeof payload.turnId !== "string" || payload.turnId.trim().length === 0) return undefined;
|
|
384
390
|
return { commandId: payload.commandId, turnId: payload.turnId };
|
|
385
391
|
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* A `session/cancel` is acknowledged ONLY by a matching-scope C04 `stopped`
|
|
395
|
+
* disposition (`{ok:true, turn:"stopped", selection: <requested scope>, ...}`)
|
|
396
|
+
* or by the legacy `{aborted:true}` plain-abort ack from a broker that predates
|
|
397
|
+
* terminal mode. The no-effect dispositions (`no_active_turn`, `no_effect`,
|
|
398
|
+
* `no_store`) and `uncertain` explicitly provide NO proof the worker was
|
|
399
|
+
* stopped — the agent turn may keep running and executing tools — so accepting
|
|
400
|
+
* them would settle the ACP prompt as cancelled against a live worker (review
|
|
401
|
+
* thread P1). `no_active_turn` can also be a requester-ownership no-op after an
|
|
402
|
+
* SDK reconnect. Anything else means the SDK did not confirm the stop and the
|
|
403
|
+
* client must see an error rather than a settled turn.
|
|
404
|
+
*/
|
|
405
|
+
function isAbortAcknowledged(value: unknown, scope: AbortScope): boolean {
|
|
406
|
+
const candidate = object(value);
|
|
407
|
+
const result = object(candidate?.result) ?? candidate;
|
|
408
|
+
if (result === undefined) return false;
|
|
409
|
+
if (result.aborted === true) return true;
|
|
410
|
+
return result.ok === true && result.turn === "stopped" && result.selection === scope;
|
|
411
|
+
}
|
|
386
412
|
function strictCorrelationFrom(...values: unknown[]): PromptCorrelation | undefined {
|
|
387
413
|
const correlation: PromptCorrelation = {};
|
|
388
414
|
let malformed = false;
|
|
@@ -602,6 +628,36 @@ function receivedSdkEvent(frame: JsonObject): ReceivedSdkEvent | undefined {
|
|
|
602
628
|
};
|
|
603
629
|
}
|
|
604
630
|
|
|
631
|
+
const ROUTER_PASSTHROUGH_FRAME_TYPES = new Set([
|
|
632
|
+
"hello",
|
|
633
|
+
"server_hello",
|
|
634
|
+
"reverse_request",
|
|
635
|
+
"reverse_cancel",
|
|
636
|
+
"reverse_request_cancel",
|
|
637
|
+
"reverse_request_cancelled",
|
|
638
|
+
]);
|
|
639
|
+
|
|
640
|
+
function acpFrameFromRouted(frame: SessionRouterFrame): JsonObject {
|
|
641
|
+
if (
|
|
642
|
+
frame.body.type === "activity" ||
|
|
643
|
+
frame.body.type === "agent_start" ||
|
|
644
|
+
frame.body.type === "agent_end" ||
|
|
645
|
+
frame.body.type === "agent_failed"
|
|
646
|
+
)
|
|
647
|
+
return frame.body;
|
|
648
|
+
if (typeof frame.body.type === "string" && ROUTER_PASSTHROUGH_FRAME_TYPES.has(frame.body.type)) return frame.body;
|
|
649
|
+
const connectionId = typeof frame.body.connectionId === "string" ? frame.body.connectionId : undefined;
|
|
650
|
+
return {
|
|
651
|
+
type: "event",
|
|
652
|
+
...(frame.name === undefined ? {} : { kind: frame.name }),
|
|
653
|
+
...(frame.sessionId === undefined ? {} : { sessionId: frame.sessionId }),
|
|
654
|
+
...(frame.commandId === undefined ? {} : { commandId: frame.commandId }),
|
|
655
|
+
...(frame.turnId === undefined ? {} : { turnId: frame.turnId }),
|
|
656
|
+
...(connectionId === undefined ? {} : { connectionId }),
|
|
657
|
+
payload: frame.body,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
605
661
|
/**
|
|
606
662
|
* Author of the message a `message_start`/`message_end` frame carries. The host echoes the
|
|
607
663
|
* user prompt and every tool result back through the same events, so only `"assistant"`
|
|
@@ -864,6 +920,14 @@ export function acpSessionStateFromConfig(
|
|
|
864
920
|
};
|
|
865
921
|
}
|
|
866
922
|
|
|
923
|
+
/** Recognize a canonical ACP skill command only when it is the complete, single text prompt. */
|
|
924
|
+
export function acpSkillInvocation(blocks: PromptRequest["prompt"]): { name: string; args: string } | undefined {
|
|
925
|
+
if (blocks.length !== 1 || blocks[0]?.type !== "text") return undefined;
|
|
926
|
+
const match = /^\/skill:([^\s]+)(?:\s+([\s\S]*))?$/.exec(blocks[0].text.trim());
|
|
927
|
+
if (!match?.[1]) return undefined;
|
|
928
|
+
return { name: match[1], args: match[2]?.trim() ?? "" };
|
|
929
|
+
}
|
|
930
|
+
|
|
867
931
|
/** Convert every ACP prompt block the agent advertises without silently discarding context. */
|
|
868
932
|
export function acpPromptPayload(blocks: PromptRequest["prompt"]): {
|
|
869
933
|
text: string;
|
|
@@ -1061,10 +1125,15 @@ export async function applyAcpStartupOptions(
|
|
|
1061
1125
|
export class AcpAgent implements Agent {
|
|
1062
1126
|
readonly #connection: AgentSideConnection;
|
|
1063
1127
|
readonly #agentDir: string;
|
|
1128
|
+
readonly #router: SessionRouter;
|
|
1129
|
+
readonly #pendingRouterAdapters = new Map<string, AcpSdkAdapter>();
|
|
1130
|
+
readonly #pendingRouterFrames = new Map<string, Record<string, unknown>[]>();
|
|
1131
|
+
#routerStartPromise: Promise<void> | undefined;
|
|
1064
1132
|
readonly #sessions = new Map<string, SessionRecord>();
|
|
1065
1133
|
readonly #attaching = new Map<string, PendingAttachment>();
|
|
1066
1134
|
readonly #resolvingExisting = new Map<string, PendingAttachment>();
|
|
1067
1135
|
readonly #knownSessionCwds = new Map<string, string>();
|
|
1136
|
+
readonly #knownSessionMcpServers = new Map<string, SessionLifecycleMcpServer[]>();
|
|
1068
1137
|
readonly #knownSessionMetadata = new Map<string, { title?: string; updatedAt?: string }>();
|
|
1069
1138
|
readonly #pendingDeleteLocators = new Map<string, { cwd: string; path: string }>();
|
|
1070
1139
|
readonly #pendingCloseIdempotencyKeys = new Map<string, string>();
|
|
@@ -1093,6 +1162,39 @@ export class AcpAgent implements Agent {
|
|
|
1093
1162
|
this.#connection = connection;
|
|
1094
1163
|
const candidate = object(options);
|
|
1095
1164
|
this.#agentDir = typeof candidate?.agentDir === "string" ? candidate.agentDir : getAgentDir();
|
|
1165
|
+
this.#router = new SessionRouter({
|
|
1166
|
+
agentDir: this.#agentDir,
|
|
1167
|
+
deps: {
|
|
1168
|
+
onAttachment: attachment => {
|
|
1169
|
+
const record = this.#sessions.get(attachment.sessionId);
|
|
1170
|
+
const adapter = record?.adapter ?? this.#pendingRouterAdapters.get(attachment.sessionId);
|
|
1171
|
+
if (!adapter) return;
|
|
1172
|
+
if (record) record.attachment = attachment;
|
|
1173
|
+
adapter.acceptAttachment(attachment);
|
|
1174
|
+
},
|
|
1175
|
+
onAttachmentReady: async attachment => {
|
|
1176
|
+
const record = this.#sessions.get(attachment.sessionId);
|
|
1177
|
+
const adapter = record?.adapter ?? this.#pendingRouterAdapters.get(attachment.sessionId);
|
|
1178
|
+
if (!adapter) return;
|
|
1179
|
+
if (record) record.attachment = attachment;
|
|
1180
|
+
await adapter.attachmentReady(attachment);
|
|
1181
|
+
},
|
|
1182
|
+
onFrame: (attachment, frame) => {
|
|
1183
|
+
const acpFrame = acpFrameFromRouted(frame);
|
|
1184
|
+
const adapter =
|
|
1185
|
+
this.#sessions.get(attachment.sessionId)?.adapter ??
|
|
1186
|
+
this.#pendingRouterAdapters.get(attachment.sessionId);
|
|
1187
|
+
if (adapter) adapter.acceptFrame(acpFrame);
|
|
1188
|
+
else this.#pendingRouterFrames.get(attachment.sessionId)?.push(acpFrame);
|
|
1189
|
+
},
|
|
1190
|
+
onSessionRemoved: attachment => {
|
|
1191
|
+
const adapter =
|
|
1192
|
+
this.#sessions.get(attachment.sessionId)?.adapter ??
|
|
1193
|
+
this.#pendingRouterAdapters.get(attachment.sessionId);
|
|
1194
|
+
adapter?.revokeAttachment(attachment);
|
|
1195
|
+
},
|
|
1196
|
+
},
|
|
1197
|
+
});
|
|
1096
1198
|
this.#startupOptions = parseAcpStartupOptions(candidate?.startupOptions);
|
|
1097
1199
|
this.#cancelSettlementGraceMs =
|
|
1098
1200
|
typeof candidate?.cancelSettlementGraceMs === "number" &&
|
|
@@ -1174,8 +1276,9 @@ export class AcpAgent implements Agent {
|
|
|
1174
1276
|
);
|
|
1175
1277
|
const id = sessionId(result);
|
|
1176
1278
|
this.#knownSessionCwds.set(id, params.cwd);
|
|
1279
|
+
this.#knownSessionMcpServers.set(id, mcpServers);
|
|
1177
1280
|
try {
|
|
1178
|
-
await this.#attach(id, params.cwd,
|
|
1281
|
+
await this.#attach(id, params.cwd, undefined, result);
|
|
1179
1282
|
await applyAcpStartupOptions(this.#adapter(id), this.#startupOptions);
|
|
1180
1283
|
const response = { sessionId: id, ...(await this.#sessionState(id, true)) };
|
|
1181
1284
|
this.#scheduleBootstrap(id);
|
|
@@ -1190,6 +1293,7 @@ export class AcpAgent implements Agent {
|
|
|
1190
1293
|
const mcpServers = this.#mcpServers(params);
|
|
1191
1294
|
this.#assertAbsoluteCwd(params.cwd);
|
|
1192
1295
|
this.#assertNoAdditionalDirectories(params.additionalDirectories);
|
|
1296
|
+
if (mcpServers.length > 0) this.#knownSessionMcpServers.set(params.sessionId, mcpServers);
|
|
1193
1297
|
await this.#attachExisting(params.sessionId, params.cwd, mcpServers);
|
|
1194
1298
|
await this.#replaySession(params.sessionId);
|
|
1195
1299
|
const response = await this.#sessionState(params.sessionId);
|
|
@@ -1201,6 +1305,7 @@ export class AcpAgent implements Agent {
|
|
|
1201
1305
|
const mcpServers = this.#mcpServers(params);
|
|
1202
1306
|
this.#assertAbsoluteCwd(params.cwd);
|
|
1203
1307
|
this.#assertNoAdditionalDirectories(params.additionalDirectories);
|
|
1308
|
+
if (mcpServers.length > 0) this.#knownSessionMcpServers.set(params.sessionId, mcpServers);
|
|
1204
1309
|
await this.#attachExisting(params.sessionId, params.cwd, mcpServers);
|
|
1205
1310
|
const response = await this.#sessionState(params.sessionId);
|
|
1206
1311
|
this.#scheduleBootstrap(params.sessionId);
|
|
@@ -1226,8 +1331,9 @@ export class AcpAgent implements Agent {
|
|
|
1226
1331
|
);
|
|
1227
1332
|
const id = sessionId(result);
|
|
1228
1333
|
this.#knownSessionCwds.set(id, params.cwd);
|
|
1334
|
+
this.#knownSessionMcpServers.set(id, mcpServers);
|
|
1229
1335
|
try {
|
|
1230
|
-
await this.#attach(id, params.cwd,
|
|
1336
|
+
await this.#attach(id, params.cwd, undefined, result);
|
|
1231
1337
|
const response = { sessionId: id, ...(await this.#sessionState(id)) };
|
|
1232
1338
|
this.#scheduleBootstrap(id);
|
|
1233
1339
|
return response;
|
|
@@ -1250,10 +1356,7 @@ export class AcpAgent implements Agent {
|
|
|
1250
1356
|
if (
|
|
1251
1357
|
typeof candidate?.sessionId !== "string" ||
|
|
1252
1358
|
typeof candidate.locator?.repo !== "string" ||
|
|
1253
|
-
path.resolve(candidate.locator.repo) !== path.resolve(params.cwd)
|
|
1254
|
-
// A DR-1 terminal row is not a loadable session: adopting its cwd
|
|
1255
|
-
// here would re-arm teardown authority for a host that is gone.
|
|
1256
|
-
candidate.terminal === true
|
|
1359
|
+
path.resolve(candidate.locator.repo) !== path.resolve(params.cwd)
|
|
1257
1360
|
)
|
|
1258
1361
|
continue;
|
|
1259
1362
|
if (discovered.has(candidate.sessionId))
|
|
@@ -1309,7 +1412,11 @@ export class AcpAgent implements Agent {
|
|
|
1309
1412
|
}
|
|
1310
1413
|
this.#beginTeardown(params.sessionId);
|
|
1311
1414
|
try {
|
|
1312
|
-
|
|
1415
|
+
// A retained delete locator proves the prior attempt already completed
|
|
1416
|
+
// connection/process teardown and reached durable artifact cleanup. Re-closing
|
|
1417
|
+
// that terminal session can only replace the authoritative cleanup_pending
|
|
1418
|
+
// result with unrelated close uncertainty, so retries resume deletion directly.
|
|
1419
|
+
await this.#teardownSession(params.sessionId, "deleted", pendingLocator === undefined || record !== undefined);
|
|
1313
1420
|
let saved = pendingLocator?.cwd === cwd ? pendingLocator.path : undefined;
|
|
1314
1421
|
if (!saved) {
|
|
1315
1422
|
try {
|
|
@@ -1317,6 +1424,7 @@ export class AcpAgent implements Agent {
|
|
|
1317
1424
|
} catch (error) {
|
|
1318
1425
|
if (error instanceof AcpSdkAdapterError && error.code === "not_found") {
|
|
1319
1426
|
this.#knownSessionCwds.delete(params.sessionId);
|
|
1427
|
+
this.#knownSessionMcpServers.delete(params.sessionId);
|
|
1320
1428
|
this.#knownSessionMetadata.delete(params.sessionId);
|
|
1321
1429
|
return {};
|
|
1322
1430
|
}
|
|
@@ -1330,6 +1438,7 @@ export class AcpAgent implements Agent {
|
|
|
1330
1438
|
this.#lifecycleIdempotencyKey(params.sessionId, "session.delete"),
|
|
1331
1439
|
);
|
|
1332
1440
|
this.#knownSessionCwds.delete(params.sessionId);
|
|
1441
|
+
this.#knownSessionMcpServers.delete(params.sessionId);
|
|
1333
1442
|
this.#knownSessionMetadata.delete(params.sessionId);
|
|
1334
1443
|
this.#pendingDeleteLocators.delete(params.sessionId);
|
|
1335
1444
|
return {};
|
|
@@ -1392,6 +1501,21 @@ export class AcpAgent implements Agent {
|
|
|
1392
1501
|
// A new turn starts uncancelled; a stale flag must never settle it as `cancelled`.
|
|
1393
1502
|
record.cancelRequested = false;
|
|
1394
1503
|
const payload = acpPromptPayload(params.prompt);
|
|
1504
|
+
const skillInvocation = acpSkillInvocation(params.prompt);
|
|
1505
|
+
if (isAcpUnavailableSlashCommand(payload.text)) {
|
|
1506
|
+
await this.#publishSessionUpdate(
|
|
1507
|
+
params.sessionId,
|
|
1508
|
+
{
|
|
1509
|
+
sessionId: params.sessionId,
|
|
1510
|
+
update: {
|
|
1511
|
+
sessionUpdate: "agent_message_chunk",
|
|
1512
|
+
content: { type: "text", text: "Slash command /import-session is unavailable over ACP." },
|
|
1513
|
+
},
|
|
1514
|
+
},
|
|
1515
|
+
record.adapter,
|
|
1516
|
+
);
|
|
1517
|
+
return { stopReason: "end_turn" };
|
|
1518
|
+
}
|
|
1395
1519
|
// The SDK transport hard-caps a single request frame at 256 KiB and answers an
|
|
1396
1520
|
// oversize frame by closing the socket (CloseCode::Size, crates/gjc-sdk/src/server.rs),
|
|
1397
1521
|
// which surfaces to the client as an opaque `connection_closed` mid-turn. Reject
|
|
@@ -1402,8 +1526,11 @@ export class AcpAgent implements Agent {
|
|
|
1402
1526
|
const promptFrameBytes = Buffer.byteLength(
|
|
1403
1527
|
JSON.stringify({
|
|
1404
1528
|
type: "control_request",
|
|
1405
|
-
operation: "turn.prompt",
|
|
1406
|
-
input: { text: payload.text, images: payload.images },
|
|
1529
|
+
operation: skillInvocation ? "skill.invoke" : "turn.prompt",
|
|
1530
|
+
input: skillInvocation ?? { text: payload.text, images: payload.images },
|
|
1531
|
+
// AcpSdkAdapter.control passes `confirm: false` to SdkClient, which serializes
|
|
1532
|
+
// the field even though it is not part of the skill input payload.
|
|
1533
|
+
...(skillInvocation ? { confirm: false } : {}),
|
|
1407
1534
|
id: PROMPT_FRAME_ID_PLACEHOLDER,
|
|
1408
1535
|
}),
|
|
1409
1536
|
);
|
|
@@ -1457,10 +1584,12 @@ export class AcpAgent implements Agent {
|
|
|
1457
1584
|
);
|
|
1458
1585
|
}
|
|
1459
1586
|
const acknowledgementTask = (async (): Promise<PromptResponse> => {
|
|
1460
|
-
const acknowledgement =
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1587
|
+
const acknowledgement = skillInvocation
|
|
1588
|
+
? await record.adapter.control("skill.invoke", skillInvocation)
|
|
1589
|
+
: await record.adapter.prompt({
|
|
1590
|
+
text: payload.text,
|
|
1591
|
+
...(payload.images.length ? { images: payload.images } : {}),
|
|
1592
|
+
});
|
|
1464
1593
|
const acknowledgementCorrelation = promptAcknowledgement(acknowledgement);
|
|
1465
1594
|
if (!acknowledgementCorrelation)
|
|
1466
1595
|
throw new AcpSdkAdapterError(
|
|
@@ -1546,12 +1675,21 @@ export class AcpAgent implements Agent {
|
|
|
1546
1675
|
try {
|
|
1547
1676
|
return await Promise.race([response, acknowledgementTask]);
|
|
1548
1677
|
} catch (error) {
|
|
1678
|
+
if (waiter.cancelAttempt && (await waiter.cancelAttempt) && waiter.settled) return await response;
|
|
1549
1679
|
waiter.deferredFrames.length = 0;
|
|
1550
1680
|
waiter.deferredActivityFrames.length = 0;
|
|
1551
1681
|
clearPromptWatchdog(waiter);
|
|
1552
1682
|
waiter.terminal = undefined;
|
|
1553
1683
|
waiter.settled = true;
|
|
1554
1684
|
if (record.activePrompt === waiter) record.activePrompt = undefined;
|
|
1685
|
+
// Keep a late terminal for this (cancelled) turn closed, exactly like the
|
|
1686
|
+
// other settlement paths, so an aborted run's trailing frame can never
|
|
1687
|
+
// publish over a later prompt that reuses the identity.
|
|
1688
|
+
if (hasCompleteCorrelation(waiter.correlation)) {
|
|
1689
|
+
record.settledPromptCorrelations.push(waiter.correlation);
|
|
1690
|
+
while (record.settledPromptCorrelations.length > SETTLED_PROMPT_CORRELATION_RETENTION)
|
|
1691
|
+
record.settledPromptCorrelations.shift();
|
|
1692
|
+
}
|
|
1555
1693
|
// A prompt cancelled before the SDK acknowledged it still ends this turn by
|
|
1556
1694
|
// client request, and ACP is explicit: "Agents MUST catch these errors and
|
|
1557
1695
|
// return the semantically meaningful `cancelled` stop reason, so that Clients
|
|
@@ -1559,28 +1697,110 @@ export class AcpAgent implements Agent {
|
|
|
1559
1697
|
// rejection instead would show the user a spurious error for their own cancel.
|
|
1560
1698
|
if (record.cancelRequested) {
|
|
1561
1699
|
record.cancelRequested = false;
|
|
1700
|
+
// The client's turn is settled by the return; the advisory idle publication
|
|
1701
|
+
// must not gate it and must still be attempted so the running phase is
|
|
1702
|
+
// released (gjcRunning:false) instead of spinning behind a settled cancel.
|
|
1703
|
+
void this.#publishPromptPhaseIdle(params.sessionId, record.adapter);
|
|
1562
1704
|
return { stopReason: "cancelled" };
|
|
1563
1705
|
}
|
|
1564
1706
|
throw error;
|
|
1565
1707
|
}
|
|
1566
1708
|
}
|
|
1567
1709
|
|
|
1568
|
-
async cancel(params:
|
|
1710
|
+
async cancel(params: CancelNotification): Promise<void> {
|
|
1569
1711
|
const record = this.#sessions.get(params.sessionId);
|
|
1570
1712
|
if (!record) throw new AcpSdkAdapterError("not_found", `Unknown session, not found: ${params.sessionId}`);
|
|
1571
1713
|
// Record the client's intent before awaiting the SDK so a prompt that rejects
|
|
1572
1714
|
// mid-cancel (e.g. preflight `busy`) can still settle as `cancelled`.
|
|
1573
1715
|
record.cancelRequested = true;
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1716
|
+
// C04 terminal abort: an external client cancel stops the current turn
|
|
1717
|
+
// (`scope:"turn"`, the default, matching the SDK `turn.abort` default and
|
|
1718
|
+
// other ACP clients' cancel behavior). A client that also wants exact owned
|
|
1719
|
+
// subagents and background tasks stopped opts in with
|
|
1720
|
+
// `_meta.gjc.abortScope: "owned"` (or `GJC_ACP_ABORT_SCOPE=owned`).
|
|
1721
|
+
const scope = resolveAcpAbortScope(params._meta, process.env);
|
|
1722
|
+
const waiter = record.activePrompt;
|
|
1723
|
+
if (waiter) {
|
|
1724
|
+
// Overlapping cancels must not lose an earlier successful
|
|
1725
|
+
// acknowledgement: ANY successful attempt resolves the shared
|
|
1726
|
+
// waiter promise IMMEDIATELY (aggregation, not request-order
|
|
1727
|
+
// serialization — an unanswered first attempt must not delay the
|
|
1728
|
+
// second attempt's acknowledged success, or a prompt rejection
|
|
1729
|
+
// awaiting cancelAttempt could hang past the grace bound; review
|
|
1730
|
+
// thread P2). The LAST failing attempt resolves false. In-flight
|
|
1731
|
+
// attempts are counted so a failure only clears the shared intent
|
|
1732
|
+
// when no earlier attempt can still acknowledge.
|
|
1733
|
+
waiter.pendingCancelAttempts = (waiter.pendingCancelAttempts ?? 0) + 1;
|
|
1734
|
+
if (!waiter.cancelAttemptResolve) {
|
|
1735
|
+
const deferred = Promise.withResolvers<boolean>();
|
|
1736
|
+
waiter.cancelAttempt = deferred.promise;
|
|
1737
|
+
waiter.cancelAttemptResolve = deferred.resolve;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
try {
|
|
1741
|
+
const acknowledgement = await record.adapter.cancel(scope);
|
|
1742
|
+
const result = object(object(acknowledgement)?.result) ?? object(acknowledgement);
|
|
1743
|
+
if (!isAbortAcknowledged(acknowledgement, scope))
|
|
1744
|
+
throw new AcpSdkAdapterError(
|
|
1745
|
+
"abort_unacknowledged",
|
|
1746
|
+
"SDK did not acknowledge cancellation of the active prompt.",
|
|
1747
|
+
);
|
|
1748
|
+
if (waiter) waiter.cancelAcknowledged = true;
|
|
1749
|
+
if (waiter && record.activePrompt !== waiter) {
|
|
1750
|
+
waiter.cancelAttemptResolve?.(true);
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
if (
|
|
1754
|
+
result?.disposition === "preflight_cancelled" &&
|
|
1755
|
+
waiter &&
|
|
1756
|
+
record.activePrompt === waiter &&
|
|
1757
|
+
!waiter.acknowledged &&
|
|
1758
|
+
!waiter.settled
|
|
1759
|
+
) {
|
|
1760
|
+
record.activePrompt = undefined;
|
|
1761
|
+
waiter.settled = true;
|
|
1762
|
+
waiter.deferredFrames.length = 0;
|
|
1763
|
+
waiter.terminal = undefined;
|
|
1764
|
+
waiter.resolve({ stopReason: "cancelled" });
|
|
1765
|
+
// Release the running phase for consistency with the resolved cancel; the
|
|
1766
|
+
// publication is advisory and must never gate the settlement above.
|
|
1767
|
+
void this.#publishPromptPhaseIdle(params.sessionId, record.adapter);
|
|
1768
|
+
} else {
|
|
1769
|
+
// The acknowledgement proves the run was aborted, not that its terminal was
|
|
1770
|
+
// published. Arm the bounded settlement so the turn cannot outlive the cancel.
|
|
1771
|
+
this.#scheduleCancelSettlement(params.sessionId, record);
|
|
1772
|
+
}
|
|
1773
|
+
waiter?.cancelAttemptResolve?.(true);
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
if (!waiter) record.cancelRequested = false;
|
|
1776
|
+
// Only the LAST in-flight attempt resolves the shared promise false;
|
|
1777
|
+
// an earlier attempt may still acknowledge (review thread P2). After
|
|
1778
|
+
// every attempt of this wave failed, RE-ARM the aggregate: a later
|
|
1779
|
+
// cancellation wave must get a fresh resolver — a stale resolved-false
|
|
1780
|
+
// promise would let the prompt path observe the old failure
|
|
1781
|
+
// immediately and report cancelled while the retry is still pending
|
|
1782
|
+
// (review thread P2).
|
|
1783
|
+
// Only clear and re-arm the aggregate when the LAST in-flight
|
|
1784
|
+
// attempt fails AND the entire wave was unacknowledged: a
|
|
1785
|
+
// failing attempt that follows an acknowledged one must not
|
|
1786
|
+
// erase the already-resolved successful aggregate, or a later
|
|
1787
|
+
// cancel installs a fresh unresolved cancelAttempt that a
|
|
1788
|
+
// concurrent prompt-preflight rejection awaits past the grace
|
|
1789
|
+
// bound (review thread P2).
|
|
1790
|
+
if (waiter && (waiter.pendingCancelAttempts ?? 0) <= 1 && !waiter.cancelAcknowledged) {
|
|
1791
|
+
waiter.cancelAttemptResolve?.(false);
|
|
1792
|
+
waiter.cancelAttempt = undefined;
|
|
1793
|
+
waiter.cancelAttemptResolve = undefined;
|
|
1794
|
+
}
|
|
1795
|
+
throw error;
|
|
1796
|
+
} finally {
|
|
1797
|
+
if (waiter) {
|
|
1798
|
+
waiter.pendingCancelAttempts = Math.max(0, (waiter.pendingCancelAttempts ?? 1) - 1);
|
|
1799
|
+
if (waiter.pendingCancelAttempts === 0 && !waiter.cancelAcknowledged && record.activePrompt === waiter) {
|
|
1800
|
+
record.cancelRequested = false;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1584
1804
|
}
|
|
1585
1805
|
|
|
1586
1806
|
/**
|
|
@@ -1615,8 +1835,14 @@ export class AcpAgent implements Agent {
|
|
|
1615
1835
|
while (record.settledPromptCorrelations.length > SETTLED_PROMPT_CORRELATION_RETENTION)
|
|
1616
1836
|
record.settledPromptCorrelations.shift();
|
|
1617
1837
|
}
|
|
1618
|
-
|
|
1838
|
+
// The client's turn ends with the resolution, not with the advisory idle update.
|
|
1839
|
+
// Publishing the phase transition first made a backpressured ACP transport hold
|
|
1840
|
+
// the settlement hostage: `sessionUpdate` awaits the stream write, a client that
|
|
1841
|
+
// stops reading (force-cancel in progress) blocks it, and `waiter.resolve` never
|
|
1842
|
+
// ran — the acknowledged cancel left the prompt pending forever, the client
|
|
1843
|
+
// force-cancelled, and the next prompt collided with the stale foreground turn.
|
|
1619
1844
|
waiter.resolve({ stopReason: "cancelled" });
|
|
1845
|
+
void this.#publishPromptPhaseIdle(id, record.adapter);
|
|
1620
1846
|
}
|
|
1621
1847
|
|
|
1622
1848
|
async extMethod(method: string, params: JsonObject): Promise<JsonObject> {
|
|
@@ -1707,6 +1933,18 @@ export class AcpAgent implements Agent {
|
|
|
1707
1933
|
return !["terminal_uncertain", "cleanup_pending", "broker_restarting", "unavailable"].includes(error.code);
|
|
1708
1934
|
}
|
|
1709
1935
|
|
|
1936
|
+
async #ensureRouterReady(): Promise<void> {
|
|
1937
|
+
if (!this.#routerStartPromise) {
|
|
1938
|
+
let pending!: Promise<void>;
|
|
1939
|
+
pending = this.#router.start().catch(error => {
|
|
1940
|
+
if (this.#routerStartPromise === pending) this.#routerStartPromise = undefined;
|
|
1941
|
+
throw error;
|
|
1942
|
+
});
|
|
1943
|
+
this.#routerStartPromise = pending;
|
|
1944
|
+
}
|
|
1945
|
+
await this.#routerStartPromise;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1710
1948
|
async #attachExisting(id: string, cwd: string, mcpServers: SessionLifecycleMcpServer[] = []): Promise<void> {
|
|
1711
1949
|
const epoch = this.#sessionEpoch(id);
|
|
1712
1950
|
const attached = this.#sessions.get(id);
|
|
@@ -1716,6 +1954,7 @@ export class AcpAgent implements Agent {
|
|
|
1716
1954
|
// ACP clients replay their declared MCP servers when reconnecting. The live
|
|
1717
1955
|
// session host remains authoritative for its immutable configuration, so
|
|
1718
1956
|
// attachment must not reinterpret the replay as a mutation request.
|
|
1957
|
+
this.#pendingDeleteLocators.delete(id);
|
|
1719
1958
|
return;
|
|
1720
1959
|
}
|
|
1721
1960
|
const knownCwd = this.#knownSessionCwds.get(id);
|
|
@@ -1755,9 +1994,7 @@ export class AcpAgent implements Agent {
|
|
|
1755
1994
|
if (indexed?.live) {
|
|
1756
1995
|
// A reconnect may repeat the client's MCP declaration. Attaching to the
|
|
1757
1996
|
// existing endpoint preserves the live host's immutable configuration.
|
|
1758
|
-
|
|
1759
|
-
this.#assertSessionEpoch(id, epoch);
|
|
1760
|
-
await this.#attach(id, cwd, endpoint(result), epoch);
|
|
1997
|
+
await this.#attach(id, cwd, epoch);
|
|
1761
1998
|
return;
|
|
1762
1999
|
}
|
|
1763
2000
|
|
|
@@ -1776,7 +2013,7 @@ export class AcpAgent implements Agent {
|
|
|
1776
2013
|
mcpServers,
|
|
1777
2014
|
);
|
|
1778
2015
|
this.#assertSessionEpoch(id, epoch);
|
|
1779
|
-
await this.#attach(id, cwd,
|
|
2016
|
+
await this.#attach(id, cwd, epoch, result);
|
|
1780
2017
|
}
|
|
1781
2018
|
|
|
1782
2019
|
async #scopedBrokerSession(id: string, cwd: string): Promise<BrokerSession | undefined> {
|
|
@@ -1795,7 +2032,7 @@ export class AcpAgent implements Agent {
|
|
|
1795
2032
|
return matches[0];
|
|
1796
2033
|
}
|
|
1797
2034
|
|
|
1798
|
-
async #attach(id: string, cwd: string,
|
|
2035
|
+
async #attach(id: string, cwd: string, epoch = this.#sessionEpoch(id), lifecycleResult?: unknown): Promise<void> {
|
|
1799
2036
|
this.#assertSessionEpoch(id, epoch);
|
|
1800
2037
|
const existing = this.#sessions.get(id);
|
|
1801
2038
|
if (existing) {
|
|
@@ -1814,7 +2051,7 @@ export class AcpAgent implements Agent {
|
|
|
1814
2051
|
return;
|
|
1815
2052
|
}
|
|
1816
2053
|
|
|
1817
|
-
const task = this.#attachEndpoint(id, cwd,
|
|
2054
|
+
const task = this.#attachEndpoint(id, cwd, epoch, lifecycleResult);
|
|
1818
2055
|
const pending = { epoch, task };
|
|
1819
2056
|
this.#attaching.set(id, pending);
|
|
1820
2057
|
try {
|
|
@@ -1825,15 +2062,37 @@ export class AcpAgent implements Agent {
|
|
|
1825
2062
|
}
|
|
1826
2063
|
}
|
|
1827
2064
|
|
|
1828
|
-
async #attachEndpoint(id: string, cwd: string,
|
|
2065
|
+
async #attachEndpoint(id: string, cwd: string, epoch: number, lifecycleResult?: unknown): Promise<void> {
|
|
1829
2066
|
let adapter: AcpSdkAdapter | undefined;
|
|
2067
|
+
const bufferedFrames: Record<string, unknown>[] = [];
|
|
2068
|
+
const pendingAdapterFrames: Record<string, unknown>[] = [];
|
|
2069
|
+
let unsubscribePendingFrames = () => {};
|
|
2070
|
+
this.#pendingRouterFrames.set(id, bufferedFrames);
|
|
1830
2071
|
try {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
2072
|
+
await this.#ensureRouterReady();
|
|
2073
|
+
const attachment = lifecycleResult
|
|
2074
|
+
? await this.#router.adoptLifecycleResult(lifecycleResult, { sessionId: id, cwd })
|
|
2075
|
+
: this.#router.attachment(id);
|
|
2076
|
+
if (!attachment)
|
|
2077
|
+
throw new AcpSdkAdapterError("unavailable", `ACP session ${id} has no current Router attachment.`);
|
|
2078
|
+
let currentAttachment = this.#router.attachment(id);
|
|
2079
|
+
for (let attempt = 0; !currentAttachment && attempt < 40; attempt++) {
|
|
2080
|
+
await Bun.sleep(50);
|
|
2081
|
+
await this.#router.reconcile();
|
|
2082
|
+
currentAttachment = this.#router.attachment(id);
|
|
2083
|
+
}
|
|
2084
|
+
if (!currentAttachment)
|
|
2085
|
+
throw new AcpSdkAdapterError("unavailable", `ACP session ${id} lost exact Router authority.`);
|
|
2086
|
+
adapter = new AcpSdkAdapter({
|
|
2087
|
+
router: this.#router,
|
|
2088
|
+
attachment: currentAttachment,
|
|
2089
|
+
sessionId: id,
|
|
1834
2090
|
connection: this.#reverseConnection(id),
|
|
1835
2091
|
providers: this.#providers(),
|
|
1836
2092
|
});
|
|
2093
|
+
unsubscribePendingFrames = adapter.onFrame(frame => pendingAdapterFrames.push(frame));
|
|
2094
|
+
this.#pendingRouterAdapters.set(id, adapter);
|
|
2095
|
+
await adapter.start();
|
|
1837
2096
|
let capabilities: JsonObject | undefined;
|
|
1838
2097
|
try {
|
|
1839
2098
|
const response = object(await adapter.query("runtime.capabilities"));
|
|
@@ -1848,9 +2107,13 @@ export class AcpAgent implements Agent {
|
|
|
1848
2107
|
"This ACP client requires a newer GJC SDK session; restart the session.",
|
|
1849
2108
|
);
|
|
1850
2109
|
this.#assertSessionEpoch(id, epoch);
|
|
2110
|
+
const exactAttachment = this.#router.attachment(id) ?? currentAttachment;
|
|
2111
|
+
if (!exactAttachment.isCurrent())
|
|
2112
|
+
throw new AcpSdkAdapterError("unavailable", `ACP session ${id} lost exact Router authority.`);
|
|
1851
2113
|
const record: SessionRecord = {
|
|
1852
2114
|
cwd,
|
|
1853
2115
|
adapter,
|
|
2116
|
+
attachment: exactAttachment,
|
|
1854
2117
|
closeIdempotencyKey: randomUUID(),
|
|
1855
2118
|
unsubscribe: () => {},
|
|
1856
2119
|
reconnectUnsubscribe: () => {},
|
|
@@ -1866,21 +2129,41 @@ export class AcpAgent implements Agent {
|
|
|
1866
2129
|
this.#recoverSessionAfterTransportFailure(id, adapter!, error),
|
|
1867
2130
|
);
|
|
1868
2131
|
this.#sessions.set(id, record);
|
|
2132
|
+
unsubscribePendingFrames();
|
|
2133
|
+
for (const frame of bufferedFrames) adapter.acceptFrame(frame);
|
|
2134
|
+
for (const frame of pendingAdapterFrames) this.#enqueueSdkFrame(id, adapter, frame);
|
|
2135
|
+
this.#pendingRouterAdapters.delete(id);
|
|
2136
|
+
this.#pendingRouterFrames.delete(id);
|
|
1869
2137
|
this.#knownSessionCwds.set(id, cwd);
|
|
1870
2138
|
await applyAcpPermissionMode(adapter, this.#clientCapabilities);
|
|
1871
2139
|
this.#assertSessionEpoch(id, epoch);
|
|
2140
|
+
// A successful attachment establishes a new live-owner epoch. Any locator
|
|
2141
|
+
// retained from an earlier cleanup_pending delete belongs to the terminal
|
|
2142
|
+
// owner and must not suppress remote close when this live session is deleted.
|
|
2143
|
+
this.#pendingDeleteLocators.delete(id);
|
|
1872
2144
|
this.#pendingCloseIdempotencyKeys.delete(id);
|
|
1873
2145
|
} catch (error) {
|
|
2146
|
+
unsubscribePendingFrames();
|
|
2147
|
+
this.#pendingRouterAdapters.delete(id);
|
|
2148
|
+
this.#pendingRouterFrames.delete(id);
|
|
1874
2149
|
if (adapter && this.#sessions.get(id)?.adapter === adapter) {
|
|
1875
2150
|
try {
|
|
1876
2151
|
await this.#teardownSession(id, "attachment failed", false);
|
|
1877
2152
|
} finally {
|
|
1878
2153
|
this.#knownSessionCwds.delete(id);
|
|
2154
|
+
this.#knownSessionMcpServers.delete(id);
|
|
1879
2155
|
}
|
|
1880
2156
|
} else if (adapter) {
|
|
1881
2157
|
try {
|
|
1882
2158
|
await adapter.close();
|
|
1883
2159
|
} catch {}
|
|
2160
|
+
try {
|
|
2161
|
+
await this.#router.attachment(id)?.retire?.();
|
|
2162
|
+
} catch {}
|
|
2163
|
+
} else {
|
|
2164
|
+
try {
|
|
2165
|
+
await this.#router.attachment(id)?.retire?.();
|
|
2166
|
+
} catch {}
|
|
1884
2167
|
}
|
|
1885
2168
|
throw error;
|
|
1886
2169
|
}
|
|
@@ -1889,6 +2172,16 @@ export class AcpAgent implements Agent {
|
|
|
1889
2172
|
#recoverSessionAfterTransportFailure(id: string, adapter: AcpSdkAdapter, error: Error): void {
|
|
1890
2173
|
const record = this.#sessions.get(id);
|
|
1891
2174
|
if (!record || record.adapter !== adapter) return;
|
|
2175
|
+
if (error instanceof SdkClientError && error.code !== "reconnect_exhausted") {
|
|
2176
|
+
logger.warn(
|
|
2177
|
+
`ACP session ${id} transport failure is not reconnect_exhausted (${error.code}); ignoring terminal recovery.`,
|
|
2178
|
+
);
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
if (!(error instanceof SdkClientError)) {
|
|
2182
|
+
logger.warn(`ACP session ${id} non-transport error reached reconnect handler; ignoring terminal recovery.`);
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
1892
2185
|
const detail = error.message || "SDK transport reconnect failed.";
|
|
1893
2186
|
const terminal = new AcpSdkAdapterError("connection_closed", `ACP session transport was lost: ${detail}`);
|
|
1894
2187
|
void this.#recoverSessionAfterTransportFailureAsync(id, adapter, record.cwd, terminal);
|
|
@@ -1902,16 +2195,28 @@ export class AcpAgent implements Agent {
|
|
|
1902
2195
|
): Promise<void> {
|
|
1903
2196
|
await this.#failSession(id, adapter, error);
|
|
1904
2197
|
if (this.#disposed || this.#knownSessionCwds.get(id) !== cwd) return;
|
|
2198
|
+
const mcpServers = this.#knownSessionMcpServers.get(id) ?? [];
|
|
1905
2199
|
try {
|
|
1906
|
-
await this.#attachExisting(id, cwd);
|
|
1907
|
-
} catch {
|
|
1908
|
-
|
|
2200
|
+
await this.#attachExisting(id, cwd, mcpServers);
|
|
2201
|
+
} catch (attachError) {
|
|
2202
|
+
const detail = attachError instanceof Error ? attachError.message : String(attachError);
|
|
2203
|
+
logger.warn(`ACP session ${id} auto-reattach after transport loss failed: ${detail}`);
|
|
2204
|
+
try {
|
|
2205
|
+
await this.#connection.sessionUpdate({
|
|
2206
|
+
sessionId: id,
|
|
2207
|
+
update: {
|
|
2208
|
+
sessionUpdate: "session_info_update",
|
|
2209
|
+
_meta: { gjcRecoverFailed: true, gjcRecoverError: detail },
|
|
2210
|
+
},
|
|
2211
|
+
});
|
|
2212
|
+
} catch {}
|
|
1909
2213
|
}
|
|
1910
2214
|
}
|
|
1911
2215
|
|
|
1912
2216
|
async #discardNewSession(id: string): Promise<void> {
|
|
1913
2217
|
await this.#teardownSession(id, "discarded", true);
|
|
1914
2218
|
this.#knownSessionCwds.delete(id);
|
|
2219
|
+
this.#knownSessionMcpServers.delete(id);
|
|
1915
2220
|
this.#knownSessionMetadata.delete(id);
|
|
1916
2221
|
}
|
|
1917
2222
|
|
|
@@ -1924,6 +2229,7 @@ export class AcpAgent implements Agent {
|
|
|
1924
2229
|
if (attaching) await Promise.allSettled([attaching.task]);
|
|
1925
2230
|
await this.#teardownSession(id, "closed", true);
|
|
1926
2231
|
this.#knownSessionCwds.delete(id);
|
|
2232
|
+
this.#knownSessionMcpServers.delete(id);
|
|
1927
2233
|
this.#knownSessionMetadata.delete(id);
|
|
1928
2234
|
return {};
|
|
1929
2235
|
} finally {
|
|
@@ -1964,11 +2270,6 @@ export class AcpAgent implements Agent {
|
|
|
1964
2270
|
}
|
|
1965
2271
|
|
|
1966
2272
|
const failures: unknown[] = [];
|
|
1967
|
-
try {
|
|
1968
|
-
await record?.adapter.close();
|
|
1969
|
-
} catch (error) {
|
|
1970
|
-
failures.push(error);
|
|
1971
|
-
}
|
|
1972
2273
|
if (closeRemote) {
|
|
1973
2274
|
const closeIdempotencyKey =
|
|
1974
2275
|
record?.closeIdempotencyKey ?? this.#pendingCloseIdempotencyKeys.get(id) ?? randomUUID();
|
|
@@ -1980,6 +2281,16 @@ export class AcpAgent implements Agent {
|
|
|
1980
2281
|
if (!(ownershipBound && this.#isAlreadyGone(error))) failures.push(error);
|
|
1981
2282
|
}
|
|
1982
2283
|
}
|
|
2284
|
+
try {
|
|
2285
|
+
await record?.adapter.close();
|
|
2286
|
+
} catch (error) {
|
|
2287
|
+
failures.push(error);
|
|
2288
|
+
}
|
|
2289
|
+
try {
|
|
2290
|
+
await record?.attachment.retire?.();
|
|
2291
|
+
} catch (error) {
|
|
2292
|
+
failures.push(error);
|
|
2293
|
+
}
|
|
1983
2294
|
if (failures.length > 0) {
|
|
1984
2295
|
const detail = failures
|
|
1985
2296
|
.map(failure => (failure instanceof Error ? failure.message : String(failure)))
|
|
@@ -2006,18 +2317,15 @@ export class AcpAgent implements Agent {
|
|
|
2006
2317
|
try {
|
|
2007
2318
|
await adapter.close();
|
|
2008
2319
|
} catch {}
|
|
2320
|
+
try {
|
|
2321
|
+
await record.attachment.retire?.();
|
|
2322
|
+
} catch {}
|
|
2009
2323
|
}
|
|
2010
2324
|
|
|
2011
2325
|
async #brokerAdapter(): Promise<AcpSdkAdapter> {
|
|
2012
2326
|
return (await this.#brokerConnection()).adapter;
|
|
2013
2327
|
}
|
|
2014
2328
|
|
|
2015
|
-
/** Machine-local endpoint lookup; never routed through ACP extension methods. */
|
|
2016
|
-
async #brokerEndpoint(sessionId: string, endpointGeneration: number | undefined): Promise<unknown> {
|
|
2017
|
-
const input = { sessionId, ...(endpointGeneration === undefined ? {} : { endpointGeneration }) };
|
|
2018
|
-
return await (await this.#brokerConnection()).client.global("session.get_endpoint", input);
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
2329
|
async #brokerConnection(): Promise<BrokerConnection> {
|
|
2022
2330
|
if (!this.#broker) {
|
|
2023
2331
|
let pending!: Promise<BrokerConnection>;
|
|
@@ -2026,7 +2334,7 @@ export class AcpAgent implements Agent {
|
|
|
2026
2334
|
const discovery = await readSdkBrokerDiscovery(this.#agentDir);
|
|
2027
2335
|
if (!discovery) throw new AcpSdkAdapterError("unavailable", "SDK broker discovery is unavailable.");
|
|
2028
2336
|
const client = await SdkClient.connect(discovery.url, discovery.token, { ...ACP_SESSION_RECONNECT });
|
|
2029
|
-
const adapter = new AcpSdkAdapter({
|
|
2337
|
+
const adapter = new AcpSdkAdapter({ client });
|
|
2030
2338
|
adapter.onReconnectFailed(() => {
|
|
2031
2339
|
if (this.#broker === pending) this.#broker = undefined;
|
|
2032
2340
|
void adapter.close().catch(() => undefined);
|
|
@@ -2182,6 +2490,7 @@ export class AcpAgent implements Agent {
|
|
|
2182
2490
|
#enqueueSdkFrame(id: string, adapter: AcpSdkAdapter, frame: JsonObject): void {
|
|
2183
2491
|
const record = this.#sessions.get(id);
|
|
2184
2492
|
if (!record || record.adapter !== adapter) return;
|
|
2493
|
+
if (typeof frame.connectionId === "string") record.connectionId = frame.connectionId;
|
|
2185
2494
|
// Ingress ordering is recorded before queued work begins.
|
|
2186
2495
|
this.#observeSessionActivity(record, frame);
|
|
2187
2496
|
// Correlation is checked at ingress before a prompt-owned frame may refresh the
|
|
@@ -2217,6 +2526,9 @@ export class AcpAgent implements Agent {
|
|
|
2217
2526
|
"The prompt owner connection was lost before completion.",
|
|
2218
2527
|
),
|
|
2219
2528
|
);
|
|
2529
|
+
// The turn is settled by the rejection; release the running phase
|
|
2530
|
+
// best-effort so a cancelled prompt is not left reporting working.
|
|
2531
|
+
void this.#publishPromptPhaseIdle(id, record.adapter);
|
|
2220
2532
|
}
|
|
2221
2533
|
}
|
|
2222
2534
|
return;
|
|
@@ -2400,17 +2712,22 @@ export class AcpAgent implements Agent {
|
|
|
2400
2712
|
record.settledPromptCorrelations.shift();
|
|
2401
2713
|
}
|
|
2402
2714
|
// The turn is over even though it ended badly, so the client's running phase has
|
|
2403
|
-
// to be released.
|
|
2404
|
-
//
|
|
2405
|
-
|
|
2715
|
+
// to be released. Rejection is the settlement; the advisory idle publication must
|
|
2716
|
+
// never gate it (a backpressured transport would otherwise hold the rejection
|
|
2717
|
+
// forever). Skipping the idle publish entirely is what left a client composer
|
|
2718
|
+
// spinning on a turn that already produced its terminal.
|
|
2406
2719
|
waiter.reject(error);
|
|
2720
|
+
void this.#publishPromptPhaseIdle(id, record.adapter);
|
|
2407
2721
|
}
|
|
2408
2722
|
|
|
2409
2723
|
/**
|
|
2410
2724
|
* Publishes only the phase transition — no `context.get`/`session.metadata` queries —
|
|
2411
2725
|
* because an abnormal settlement has no trustworthy usage or title to report. Publish
|
|
2412
2726
|
* failures are swallowed: the turn is already settled, and escalating to session
|
|
2413
|
-
* failure here would tear down a session the client can still use.
|
|
2727
|
+
* failure here would tear down a session the client can still use. Callers must
|
|
2728
|
+
* settle the prompt BEFORE invoking this (or detach it with `void`): `sessionUpdate`
|
|
2729
|
+
* awaits the transport write, so awaiting it ahead of resolution lets a backpressured
|
|
2730
|
+
* client hold the settlement hostage.
|
|
2414
2731
|
*/
|
|
2415
2732
|
async #publishPromptPhaseIdle(id: string, adapter: AcpSdkAdapter): Promise<void> {
|
|
2416
2733
|
const record = this.#sessions.get(id);
|
|
@@ -3161,7 +3478,7 @@ export class AcpAgent implements Agent {
|
|
|
3161
3478
|
mcpServers: SessionLifecycleMcpServer[],
|
|
3162
3479
|
): Promise<unknown> {
|
|
3163
3480
|
try {
|
|
3164
|
-
return await (await this.#brokerAdapter()).
|
|
3481
|
+
return await (await this.#brokerAdapter()).lifecycle(operation, input, idempotencyKey);
|
|
3165
3482
|
} catch (error) {
|
|
3166
3483
|
throw acpMcpLaunchFailure(error, mcpServers);
|
|
3167
3484
|
}
|
|
@@ -3190,6 +3507,7 @@ export class AcpAgent implements Agent {
|
|
|
3190
3507
|
this.#attaching.clear();
|
|
3191
3508
|
this.#resolvingExisting.clear();
|
|
3192
3509
|
this.#knownSessionCwds.clear();
|
|
3510
|
+
this.#knownSessionMcpServers.clear();
|
|
3193
3511
|
this.#knownSessionMetadata.clear();
|
|
3194
3512
|
this.#pendingDeleteLocators.clear();
|
|
3195
3513
|
this.#pendingCloseIdempotencyKeys.clear();
|
|
@@ -3204,6 +3522,13 @@ export class AcpAgent implements Agent {
|
|
|
3204
3522
|
failures.push(error);
|
|
3205
3523
|
}
|
|
3206
3524
|
}
|
|
3525
|
+
this.#pendingRouterAdapters.clear();
|
|
3526
|
+
this.#pendingRouterFrames.clear();
|
|
3527
|
+
try {
|
|
3528
|
+
await this.#router.stop();
|
|
3529
|
+
} catch (error) {
|
|
3530
|
+
failures.push(error);
|
|
3531
|
+
}
|
|
3207
3532
|
if (failures.length > 0) {
|
|
3208
3533
|
const detail = failures
|
|
3209
3534
|
.map(failure => (failure instanceof Error ? failure.message : String(failure)))
|