@sayknow-cli/coding-agent 0.4.7 → 0.5.1
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 +122 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-contract.d.ts +40 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +156 -10
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +33 -2
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +5 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +13 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
- package/dist/types/runtime-mcp/manager.d.ts +9 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +52 -4
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/query/handlers.d.ts +14 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +9 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +83 -24
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
- package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +89 -18
- package/dist/types/session/session-storage.d.ts +31 -0
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/skc-runtime/state-writer.d.ts +5 -1
- package/dist/types/skc-runtime/team-launch.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
- package/dist/types/skc-runtime/team-store.d.ts +12 -0
- package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +133 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +22 -9
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +7 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -12
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/compile-args.ts +0 -1
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-docs-index.ts +4 -1
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +58 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +16 -100
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +6 -38
- package/src/cli/read-cli.ts +2 -1
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +133 -11
- package/src/commands/daemon.ts +22 -5
- package/src/commands/deep-interview.ts +26 -1
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +134 -17
- package/src/commands/team.ts +1 -1
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-contract.ts +174 -0
- package/src/config/model-profiles.ts +51 -10
- package/src/config/model-registry.ts +24 -4
- package/src/config/model-resolver.ts +2 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +157 -10
- package/src/config/settings.ts +254 -108
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
- package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
- package/src/defaults/skc/skills/team/SKILL.md +1 -1
- package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/eval/py/executor.ts +37 -13
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/extensibility/shared-events.ts +5 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/internal-urls/docs-index.generated.ts +17 -16
- package/src/internal-urls/local-protocol.ts +87 -25
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +120 -42
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +52 -0
- package/src/modes/acp/acp-agent.ts +647 -83
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/sayknow-pet-widget.ts +20 -8
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +26 -8
- package/src/modes/components/welcome.ts +50 -23
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +101 -54
- package/src/modes/controllers/event-controller.ts +119 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +100 -8
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +4 -1
- package/src/modes/interactive-mode.ts +201 -60
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +6 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +99 -4
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +17 -3
- package/src/prompts/agents/critic.md +17 -2
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -3
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
- package/src/runtime-mcp/json-rpc.ts +28 -11
- package/src/runtime-mcp/manager.ts +77 -59
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +293 -43
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +22 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +10 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +793 -121
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +87 -25
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/prompt-reconciliation.ts +240 -0
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
- package/src/sdk/bus/telegram-daemon-control.ts +161 -40
- package/src/sdk/bus/telegram-daemon.ts +3649 -593
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +8 -2
- package/src/sdk/host/control/operations.ts +3 -2
- package/src/sdk/host/host.ts +27 -9
- package/src/sdk/host/query/handlers.ts +125 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +145 -0
- package/src/sdk/protocol/operation-registry.ts +24 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +69 -8
- package/src/sdk/startup-capability.ts +15 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +1112 -314
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +1426 -259
- package/src/session/internal/managed-session-storage.ts +414 -238
- package/src/session/internal/native-publish-outcome.ts +271 -0
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2079 -469
- package/src/session/session-storage.ts +151 -11
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +49 -5
- package/src/setup/provider-presets.json +2 -1
- package/src/skc-runtime/deep-interview-runtime.ts +303 -36
- package/src/skc-runtime/deep-interview-stage.ts +1016 -0
- package/src/skc-runtime/deep-interview-state.ts +8 -5
- package/src/skc-runtime/gc-render.ts +1 -0
- package/src/skc-runtime/gc-runtime.ts +11 -1
- package/src/skc-runtime/linux-proc.ts +70 -36
- package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/skc-runtime/ralplan-runtime.ts +1057 -75
- package/src/skc-runtime/repository-binding.ts +267 -0
- package/src/skc-runtime/session-state-sidecar.ts +25 -9
- package/src/skc-runtime/state-runtime.ts +50 -12
- package/src/skc-runtime/state-writer.ts +68 -56
- package/src/skc-runtime/team-launch.ts +34 -5
- package/src/skc-runtime/team-runtime.ts +41 -1
- package/src/skc-runtime/team-store.ts +32 -0
- package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/skc-runtime/team-workers.ts +1 -0
- package/src/skc-runtime/tmux-common.ts +13 -2
- package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
- package/src/skc-runtime/tmux-sessions.ts +45 -6
- package/src/skc-runtime/ultragoal-guard.ts +93 -15
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
- package/src/skc-runtime/ultragoal-runtime.ts +469 -38
- package/src/skc-runtime/workflow-manifest.generated.json +195 -5
- package/src/skc-runtime/workflow-manifest.ts +67 -3
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +104 -12
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/executor.ts +165 -47
- package/src/task/index.ts +123 -15
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/receipt.ts +3 -0
- package/src/task/render.ts +45 -26
- package/src/task/skc-command.ts +2 -2
- package/src/task/types.ts +55 -10
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +1 -1
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +127 -154
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +7 -1
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1544 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type AuthenticateRequest,
|
|
7
7
|
type AuthenticateResponse,
|
|
8
8
|
type AuthMethod,
|
|
9
|
+
type AvailableCommand,
|
|
9
10
|
type ClientCapabilities,
|
|
10
11
|
type CloseSessionRequest,
|
|
11
12
|
type CloseSessionResponse,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
PROTOCOL_VERSION,
|
|
25
26
|
type PromptRequest,
|
|
26
27
|
type PromptResponse,
|
|
28
|
+
RequestError,
|
|
27
29
|
type ResumeSessionRequest,
|
|
28
30
|
type ResumeSessionResponse,
|
|
29
31
|
type SessionInfo,
|
|
@@ -33,7 +35,7 @@ import {
|
|
|
33
35
|
type SetSessionModeRequest,
|
|
34
36
|
type SetSessionModeResponse,
|
|
35
37
|
} from "@agentclientprotocol/sdk";
|
|
36
|
-
import { getAgentDir } from "@sayknow-cli/utils";
|
|
38
|
+
import { getAgentDir, logger } from "@sayknow-cli/utils";
|
|
37
39
|
import packageJson from "../../../package.json" with { type: "json" };
|
|
38
40
|
import {
|
|
39
41
|
type AcpProviderRegistration,
|
|
@@ -41,9 +43,15 @@ import {
|
|
|
41
43
|
AcpSdkAdapter,
|
|
42
44
|
AcpSdkAdapterError,
|
|
43
45
|
} from "../../sdk/acp";
|
|
46
|
+
import { resolveAcpFinalText } from "../../sdk/acp/final-text";
|
|
47
|
+
import { ACP_MCP_LIFECYCLE_TIMEOUT_MS, type SessionLifecycleMcpServer } from "../../sdk/acp/mcp";
|
|
44
48
|
import { ensureBroker } from "../../sdk/broker/ensure";
|
|
45
|
-
import { readSdkBrokerDiscovery, SdkClient } from "../../sdk/client";
|
|
46
|
-
import {
|
|
49
|
+
import { readSdkBrokerDiscovery, SdkClient, SdkClientError } from "../../sdk/client";
|
|
50
|
+
import {
|
|
51
|
+
buildToolCallStartUpdate,
|
|
52
|
+
mapAgentSessionEventToAcpSessionUpdates,
|
|
53
|
+
mapAgentWireEventPayloadToAcpSessionUpdates,
|
|
54
|
+
} from "./acp-event-mapper";
|
|
47
55
|
import { resolveAcpPermissionMode } from "./permission-mode";
|
|
48
56
|
import type { AcpStartupOptions } from "./startup-options";
|
|
49
57
|
import { ACP_TERMINAL_AUTH_FLAG } from "./terminal-auth";
|
|
@@ -52,6 +60,8 @@ const ACP_DEFAULT_MODE_ID = "default";
|
|
|
52
60
|
const ACP_PLAN_MODE_ID = "plan";
|
|
53
61
|
const MODE_CONFIG_ID = "mode";
|
|
54
62
|
const MODEL_CONFIG_ID = "model";
|
|
63
|
+
const MODEL_PRESET_CONFIG_KEY = "modelPreset";
|
|
64
|
+
const ACP_CUSTOM_MODEL_PRESET = "__custom__";
|
|
55
65
|
const THINKING_CONFIG_ID = "thinking";
|
|
56
66
|
const SESSION_PAGE_SIZE = 50;
|
|
57
67
|
export const ACP_BOOTSTRAP_RACE_GUARD_MS = 50;
|
|
@@ -59,11 +69,10 @@ const MAX_ACP_REPLAY_PAGES = 10_000;
|
|
|
59
69
|
|
|
60
70
|
type JsonObject = Record<string, unknown>;
|
|
61
71
|
/**
|
|
62
|
-
* ACP prompt completion is tied to
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* can never settle the waiter that acknowledgement creates.
|
|
72
|
+
* ACP prompt completion is tied to lifecycle activity observed after dispatch.
|
|
73
|
+
* A fast host may stream and finish a turn before acknowledging `turn.prompt`,
|
|
74
|
+
* so pre-ack activity is retained but cannot resolve the waiter until the
|
|
75
|
+
* acknowledgement supplies its command/turn correlation.
|
|
67
76
|
*/
|
|
68
77
|
interface PromptWaiter {
|
|
69
78
|
cancelRequested: boolean;
|
|
@@ -75,6 +84,7 @@ interface PromptWaiter {
|
|
|
75
84
|
boundary: number;
|
|
76
85
|
correlation: PromptCorrelation;
|
|
77
86
|
messageProgress?: { textEmitted: boolean; thoughtEmitted: boolean };
|
|
87
|
+
emittedAssistantText: string;
|
|
78
88
|
pendingTerminal?: PromptCorrelation;
|
|
79
89
|
resolve: (response: PromptResponse) => void;
|
|
80
90
|
reject: (error: Error) => void;
|
|
@@ -88,6 +98,7 @@ type PendingAttachment = { epoch: number; task: Promise<void> };
|
|
|
88
98
|
type SessionRecord = {
|
|
89
99
|
cwd: string;
|
|
90
100
|
adapter: AcpSdkAdapter;
|
|
101
|
+
closeIdempotencyKey: string;
|
|
91
102
|
unsubscribe: () => void;
|
|
92
103
|
reconnectUnsubscribe: () => void;
|
|
93
104
|
/** Per-session frame work queue; callbacks never race prompt ownership. */
|
|
@@ -96,6 +107,10 @@ type SessionRecord = {
|
|
|
96
107
|
inboundSequence: number;
|
|
97
108
|
/** Updated at ingress so a prompt acknowledgement can distinguish a steer from a fresh turn. */
|
|
98
109
|
busy: boolean;
|
|
110
|
+
/** Start/update args retained because tool_execution_end does not carry them. */
|
|
111
|
+
toolArgs: Map<string, unknown>;
|
|
112
|
+
/** Actionable model-profile authentication failure detected before prompt dispatch. */
|
|
113
|
+
authFailure?: string;
|
|
99
114
|
activePrompt?: PromptWaiter;
|
|
100
115
|
};
|
|
101
116
|
type Endpoint = { url: string; token: string };
|
|
@@ -105,6 +120,9 @@ type BrokerSession = {
|
|
|
105
120
|
locator?: { repo?: string };
|
|
106
121
|
live?: boolean;
|
|
107
122
|
endpointGeneration?: number;
|
|
123
|
+
endpointMtimeMs?: number;
|
|
124
|
+
title?: string;
|
|
125
|
+
updatedAt?: string;
|
|
108
126
|
};
|
|
109
127
|
|
|
110
128
|
function parseAcpStartupOptions(value: unknown): AcpStartupOptions | undefined {
|
|
@@ -135,7 +153,12 @@ function aggregateAcpFailure(code: string, message: string, failures: unknown[])
|
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
/** Applies ACP's offset cursor after narrowing the broker listing to the requested cwd. */
|
|
138
|
-
export function paginateAcpSessions(
|
|
156
|
+
export function paginateAcpSessions(
|
|
157
|
+
listed: unknown[],
|
|
158
|
+
cwd: string | undefined,
|
|
159
|
+
offset: number,
|
|
160
|
+
sessionMetadata: ReadonlyMap<string, { title?: string; updatedAt?: string }> = new Map(),
|
|
161
|
+
): ListSessionsResponse {
|
|
139
162
|
const filtered = listed
|
|
140
163
|
.map(value => object(value) as BrokerSession | undefined)
|
|
141
164
|
.filter(
|
|
@@ -143,12 +166,28 @@ export function paginateAcpSessions(listed: unknown[], cwd: string | undefined,
|
|
|
143
166
|
typeof value?.sessionId === "string" && typeof value.locator?.repo === "string",
|
|
144
167
|
)
|
|
145
168
|
.filter(value => !cwd || value.locator.repo === cwd);
|
|
146
|
-
const sessions = filtered
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
const sessions = filtered.slice(offset, offset + SESSION_PAGE_SIZE).map(value => {
|
|
170
|
+
const metadata = sessionMetadata.get(value.sessionId);
|
|
171
|
+
const updatedAt =
|
|
172
|
+
typeof metadata?.updatedAt === "string"
|
|
173
|
+
? metadata.updatedAt
|
|
174
|
+
: typeof value.updatedAt === "string"
|
|
175
|
+
? value.updatedAt
|
|
176
|
+
: typeof value.endpointMtimeMs === "number" && Number.isFinite(value.endpointMtimeMs)
|
|
177
|
+
? new Date(value.endpointMtimeMs).toISOString()
|
|
178
|
+
: undefined;
|
|
179
|
+
return {
|
|
180
|
+
sessionId: value.sessionId,
|
|
181
|
+
cwd: value.locator.repo,
|
|
182
|
+
title:
|
|
183
|
+
typeof metadata?.title === "string" && metadata.title
|
|
184
|
+
? metadata.title
|
|
185
|
+
: typeof value.title === "string" && value.title
|
|
186
|
+
? value.title
|
|
187
|
+
: value.sessionId,
|
|
188
|
+
...(updatedAt ? { updatedAt } : {}),
|
|
189
|
+
} satisfies SessionInfo;
|
|
190
|
+
});
|
|
152
191
|
return {
|
|
153
192
|
sessions,
|
|
154
193
|
nextCursor: offset + sessions.length < filtered.length ? String(offset + sessions.length) : undefined,
|
|
@@ -179,6 +218,26 @@ function pageItems(value: unknown): unknown[] {
|
|
|
179
218
|
return Array.isArray(page?.items) ? page.items : [];
|
|
180
219
|
}
|
|
181
220
|
|
|
221
|
+
/** Build the ACP command palette from the shared builtins and live SDK skill state. */
|
|
222
|
+
export function acpAvailableCommandsFromSkills(query: unknown): AvailableCommand[] {
|
|
223
|
+
const commands = new Map<string, AvailableCommand>();
|
|
224
|
+
for (const item of pageItems(query)) {
|
|
225
|
+
const skill = object(item);
|
|
226
|
+
if (typeof skill?.name !== "string" || !skill.name) continue;
|
|
227
|
+
const name = `skill:${skill.name}`;
|
|
228
|
+
if (commands.has(name)) continue;
|
|
229
|
+
commands.set(name, {
|
|
230
|
+
name,
|
|
231
|
+
description:
|
|
232
|
+
typeof skill.description === "string" && skill.description
|
|
233
|
+
? skill.description
|
|
234
|
+
: `Run the ${skill.name} skill`,
|
|
235
|
+
input: { hint: "[request]" },
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return [...commands.values()];
|
|
239
|
+
}
|
|
240
|
+
|
|
182
241
|
function correlationFrom(...values: unknown[]): PromptCorrelation {
|
|
183
242
|
const correlation: PromptCorrelation = {};
|
|
184
243
|
for (const value of values) {
|
|
@@ -267,6 +326,7 @@ function receivedSdkEvent(frame: JsonObject): ReceivedSdkEvent | undefined {
|
|
|
267
326
|
const type = frame.state === "busy" ? "agent_start" : frame.state === "idle" ? "agent_end" : undefined;
|
|
268
327
|
return type ? { event: { type, ...correlationFrom(frame) } } : undefined;
|
|
269
328
|
}
|
|
329
|
+
if (frame.type === "agent_start" || frame.type === "agent_end") return { event: frame };
|
|
270
330
|
if (frame.type !== "event") return undefined;
|
|
271
331
|
const payload = object(frame.payload);
|
|
272
332
|
if (!payload) return undefined;
|
|
@@ -330,6 +390,19 @@ function configValues(query: unknown): Map<string, string> {
|
|
|
330
390
|
return values;
|
|
331
391
|
}
|
|
332
392
|
|
|
393
|
+
function modelPresetConfigOptions(query: unknown, current: string): { value: string; name: string }[] {
|
|
394
|
+
const options = new Map<string, string>();
|
|
395
|
+
for (const item of pageItems(query)) {
|
|
396
|
+
const profile = object(item);
|
|
397
|
+
if (!profile || typeof profile.id !== "string") continue;
|
|
398
|
+
if (profile.available === false && profile.id !== current) continue;
|
|
399
|
+
options.set(profile.id, typeof profile.displayName === "string" ? profile.displayName : profile.id);
|
|
400
|
+
}
|
|
401
|
+
if (current === ACP_CUSTOM_MODEL_PRESET) options.set(ACP_CUSTOM_MODEL_PRESET, "Custom (current model)");
|
|
402
|
+
else if (!options.has(current)) options.set(current, current);
|
|
403
|
+
return [...options].map(([value, name]) => ({ value, name }));
|
|
404
|
+
}
|
|
405
|
+
|
|
333
406
|
function modelConfigOptions(query: unknown, current: string | undefined): { value: string; name: string }[] {
|
|
334
407
|
const options = new Map<string, string>();
|
|
335
408
|
for (const item of pageItems(query)) {
|
|
@@ -347,9 +420,10 @@ const THINKING_CONFIG_OPTIONS = ["off", "minimal", "low", "medium", "high", "xhi
|
|
|
347
420
|
name: value,
|
|
348
421
|
}));
|
|
349
422
|
|
|
350
|
-
/** Maps live canonical SDK config and model
|
|
351
|
-
export function acpSessionStateFromConfig(query: unknown,
|
|
423
|
+
/** Maps live canonical SDK config and the selected model catalog into the ACP 1.2.1 session state surface. */
|
|
424
|
+
export function acpSessionStateFromConfig(query: unknown, modelCatalogQuery?: unknown, modelPreset?: string) {
|
|
352
425
|
const values = configValues(query);
|
|
426
|
+
const useModelPresets = modelPreset !== undefined;
|
|
353
427
|
const currentModeId = values.get(MODE_CONFIG_ID) === ACP_PLAN_MODE_ID ? ACP_PLAN_MODE_ID : ACP_DEFAULT_MODE_ID;
|
|
354
428
|
return {
|
|
355
429
|
configOptions: [
|
|
@@ -364,15 +438,28 @@ export function acpSessionStateFromConfig(query: unknown, modelsQuery?: unknown)
|
|
|
364
438
|
],
|
|
365
439
|
},
|
|
366
440
|
...ACP_CONFIG_OPTIONS.flatMap(option => {
|
|
367
|
-
const value =
|
|
441
|
+
const value =
|
|
442
|
+
option.id === MODEL_CONFIG_ID && useModelPresets
|
|
443
|
+
? (values.get(MODEL_PRESET_CONFIG_KEY) ?? ACP_CUSTOM_MODEL_PRESET)
|
|
444
|
+
: values.get(option.id);
|
|
368
445
|
if (value === undefined) return [];
|
|
369
446
|
const options =
|
|
370
447
|
option.id === MODEL_CONFIG_ID
|
|
371
|
-
?
|
|
448
|
+
? useModelPresets
|
|
449
|
+
? modelPresetConfigOptions(modelCatalogQuery, value)
|
|
450
|
+
: modelConfigOptions(modelCatalogQuery, value)
|
|
372
451
|
: option.id === THINKING_CONFIG_ID
|
|
373
452
|
? THINKING_CONFIG_OPTIONS
|
|
374
453
|
: [...option.options];
|
|
375
|
-
return [
|
|
454
|
+
return [
|
|
455
|
+
{
|
|
456
|
+
...option,
|
|
457
|
+
...(option.id === MODEL_CONFIG_ID && useModelPresets ? { name: "Preset" } : {}),
|
|
458
|
+
type: "select" as const,
|
|
459
|
+
currentValue: value,
|
|
460
|
+
options,
|
|
461
|
+
},
|
|
462
|
+
];
|
|
376
463
|
}),
|
|
377
464
|
],
|
|
378
465
|
modes: {
|
|
@@ -446,6 +533,34 @@ export function acpPromptPayload(blocks: PromptRequest["prompt"]): {
|
|
|
446
533
|
return { text: text.join("\n"), images };
|
|
447
534
|
}
|
|
448
535
|
|
|
536
|
+
/**
|
|
537
|
+
* `AcpSdkAdapterError.code` is an internal string, but the SDK only derives a
|
|
538
|
+
* JSON-RPC code from a `RequestError`. Everything else collapses to an opaque
|
|
539
|
+
* `-32603 Internal error`, which hides the reason and defeats client-side
|
|
540
|
+
* recovery (an ACP client cannot see that it must authenticate). Map the codes
|
|
541
|
+
* that have a defined ACP/JSON-RPC counterpart onto a real `RequestError`.
|
|
542
|
+
*/
|
|
543
|
+
export function acpRequestFailure(error: unknown): unknown {
|
|
544
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
|
|
545
|
+
if (typeof code !== "string") return error;
|
|
546
|
+
const message = error instanceof Error ? error.message : code;
|
|
547
|
+
switch (code) {
|
|
548
|
+
case "authentication_failed":
|
|
549
|
+
return RequestError.authRequired({ code, details: message }, message);
|
|
550
|
+
case "not_found":
|
|
551
|
+
return RequestError.resourceNotFound(message);
|
|
552
|
+
case "invalid_input":
|
|
553
|
+
case "unsupported":
|
|
554
|
+
case "unsupported_content":
|
|
555
|
+
return RequestError.invalidParams({ code, details: message }, message);
|
|
556
|
+
default:
|
|
557
|
+
// The remaining internal codes (conflict, unavailable, busy, …) have no ACP
|
|
558
|
+
// counterpart and stay -32603. Keep the discriminator in `data` so a client can
|
|
559
|
+
// branch on retry/reconnect instead of parsing an English message.
|
|
560
|
+
return RequestError.internalError({ code, details: message }, message);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
449
564
|
/** Registers a permission provider only when the ACP client requires prompts. */
|
|
450
565
|
export function acpProviderRegistrations(
|
|
451
566
|
capabilities: ClientCapabilities | undefined,
|
|
@@ -571,8 +686,11 @@ export class AcpAgent implements Agent {
|
|
|
571
686
|
readonly #attaching = new Map<string, PendingAttachment>();
|
|
572
687
|
readonly #resolvingExisting = new Map<string, PendingAttachment>();
|
|
573
688
|
readonly #knownSessionCwds = new Map<string, string>();
|
|
689
|
+
readonly #knownSessionMetadata = new Map<string, { title?: string; updatedAt?: string }>();
|
|
690
|
+
readonly #pendingCloseIdempotencyKeys = new Map<string, string>();
|
|
574
691
|
readonly #sessionEpochs = new Map<string, number>();
|
|
575
692
|
readonly #tearingDown = new Map<string, number>();
|
|
693
|
+
readonly #closing = new Map<string, Promise<CloseSessionResponse>>();
|
|
576
694
|
#clientCapabilities: ClientCapabilities | undefined;
|
|
577
695
|
#broker: Promise<BrokerConnection> | undefined;
|
|
578
696
|
readonly #startupOptions: AcpStartupOptions | undefined;
|
|
@@ -621,7 +739,14 @@ export class AcpAgent implements Agent {
|
|
|
621
739
|
agentCapabilities: {
|
|
622
740
|
loadSession: true,
|
|
623
741
|
promptCapabilities: { embeddedContext: true, image: true },
|
|
624
|
-
|
|
742
|
+
mcpCapabilities: { http: true, sse: true },
|
|
743
|
+
sessionCapabilities: {
|
|
744
|
+
list: {},
|
|
745
|
+
fork: {},
|
|
746
|
+
resume: {},
|
|
747
|
+
close: {},
|
|
748
|
+
delete: {},
|
|
749
|
+
},
|
|
625
750
|
},
|
|
626
751
|
};
|
|
627
752
|
}
|
|
@@ -633,16 +758,19 @@ export class AcpAgent implements Agent {
|
|
|
633
758
|
}
|
|
634
759
|
|
|
635
760
|
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
636
|
-
this.#
|
|
761
|
+
const mcpServers = this.#mcpServers(params);
|
|
637
762
|
this.#assertAbsoluteCwd(params.cwd);
|
|
638
|
-
|
|
763
|
+
this.#assertNoAdditionalDirectories(params.additionalDirectories);
|
|
764
|
+
const result = await this.#launchSessionWithMcp(
|
|
639
765
|
"session.create",
|
|
640
766
|
{
|
|
641
767
|
cwd: params.cwd,
|
|
642
768
|
target: { path: params.cwd },
|
|
643
769
|
...(this.#startupOptions?.modelPreset ? { modelPreset: this.#startupOptions.modelPreset } : {}),
|
|
770
|
+
...(mcpServers.length > 0 ? { mcpServers, readinessTimeoutMs: ACP_MCP_LIFECYCLE_TIMEOUT_MS } : {}),
|
|
644
771
|
},
|
|
645
772
|
randomUUID(),
|
|
773
|
+
mcpServers,
|
|
646
774
|
);
|
|
647
775
|
const id = sessionId(result);
|
|
648
776
|
this.#knownSessionCwds.set(id, params.cwd);
|
|
@@ -650,7 +778,7 @@ export class AcpAgent implements Agent {
|
|
|
650
778
|
await this.#attach(id, params.cwd, endpoint(result));
|
|
651
779
|
await applyAcpStartupOptions(this.#adapter(id), this.#startupOptions);
|
|
652
780
|
this.#scheduleBootstrap(id);
|
|
653
|
-
return { sessionId: id, ...(await this.#sessionState(id)) };
|
|
781
|
+
return { sessionId: id, ...(await this.#sessionState(id, true)) };
|
|
654
782
|
} catch (error) {
|
|
655
783
|
await this.#discardNewSession(id);
|
|
656
784
|
throw error;
|
|
@@ -658,35 +786,40 @@ export class AcpAgent implements Agent {
|
|
|
658
786
|
}
|
|
659
787
|
|
|
660
788
|
async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
|
|
661
|
-
this.#
|
|
789
|
+
const mcpServers = this.#mcpServers(params);
|
|
662
790
|
this.#assertAbsoluteCwd(params.cwd);
|
|
663
|
-
|
|
791
|
+
this.#assertNoAdditionalDirectories(params.additionalDirectories);
|
|
792
|
+
await this.#attachExisting(params.sessionId, params.cwd, mcpServers);
|
|
664
793
|
await this.#replaySession(params.sessionId);
|
|
665
794
|
this.#scheduleBootstrap(params.sessionId);
|
|
666
795
|
return await this.#sessionState(params.sessionId);
|
|
667
796
|
}
|
|
668
797
|
|
|
669
798
|
async resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse> {
|
|
670
|
-
this.#
|
|
799
|
+
const mcpServers = this.#mcpServers(params);
|
|
671
800
|
this.#assertAbsoluteCwd(params.cwd);
|
|
672
|
-
|
|
801
|
+
this.#assertNoAdditionalDirectories(params.additionalDirectories);
|
|
802
|
+
await this.#attachExisting(params.sessionId, params.cwd, mcpServers);
|
|
673
803
|
this.#scheduleBootstrap(params.sessionId);
|
|
674
804
|
return await this.#sessionState(params.sessionId);
|
|
675
805
|
}
|
|
676
806
|
|
|
677
807
|
async unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse> {
|
|
678
|
-
this.#
|
|
808
|
+
const mcpServers = this.#mcpServers(params);
|
|
679
809
|
this.#assertAbsoluteCwd(params.cwd);
|
|
810
|
+
this.#assertNoAdditionalDirectories(params.additionalDirectories);
|
|
680
811
|
const source = await this.#resolveSavedSession(params.sessionId, params.cwd);
|
|
681
|
-
const result = await
|
|
812
|
+
const result = await this.#launchSessionWithMcp(
|
|
682
813
|
"session.fork",
|
|
683
814
|
{
|
|
684
815
|
cwd: params.cwd,
|
|
685
816
|
sourceSessionId: params.sessionId,
|
|
686
817
|
sourceSessionPath: source,
|
|
687
818
|
target: { path: params.cwd },
|
|
819
|
+
...(mcpServers.length > 0 ? { mcpServers, readinessTimeoutMs: ACP_MCP_LIFECYCLE_TIMEOUT_MS } : {}),
|
|
688
820
|
},
|
|
689
821
|
randomUUID(),
|
|
822
|
+
mcpServers,
|
|
690
823
|
);
|
|
691
824
|
const id = sessionId(result);
|
|
692
825
|
this.#knownSessionCwds.set(id, params.cwd);
|
|
@@ -727,22 +860,29 @@ export class AcpAgent implements Agent {
|
|
|
727
860
|
this.#knownSessionCwds.set(candidate.sessionId, params.cwd);
|
|
728
861
|
}
|
|
729
862
|
}
|
|
730
|
-
return paginateAcpSessions(
|
|
863
|
+
return paginateAcpSessions(
|
|
864
|
+
listed,
|
|
865
|
+
params.cwd ?? undefined,
|
|
866
|
+
this.#cursor(params.cursor),
|
|
867
|
+
this.#knownSessionMetadata,
|
|
868
|
+
);
|
|
731
869
|
}
|
|
732
870
|
|
|
733
|
-
|
|
871
|
+
closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse> {
|
|
734
872
|
const record = this.#sessions.get(params.sessionId);
|
|
735
873
|
const cwd = record?.cwd ?? this.#knownSessionCwds.get(params.sessionId);
|
|
736
874
|
// ACP close has no cwd. Only connection-owned sessions may reach broker lifecycle control.
|
|
737
|
-
if (!cwd) return {};
|
|
738
|
-
this.#
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
this.#
|
|
745
|
-
}
|
|
875
|
+
if (!cwd) return Promise.resolve({});
|
|
876
|
+
const existing = this.#closing.get(params.sessionId);
|
|
877
|
+
if (existing) return existing;
|
|
878
|
+
const deferred = Promise.withResolvers<CloseSessionResponse>();
|
|
879
|
+
this.#closing.set(params.sessionId, deferred.promise);
|
|
880
|
+
void this.#closeOwnedSession(params.sessionId).then(deferred.resolve, deferred.reject);
|
|
881
|
+
const cleanup = deferred.promise.finally(() => {
|
|
882
|
+
if (this.#closing.get(params.sessionId) === deferred.promise) this.#closing.delete(params.sessionId);
|
|
883
|
+
});
|
|
884
|
+
void cleanup.catch(() => undefined);
|
|
885
|
+
return deferred.promise;
|
|
746
886
|
}
|
|
747
887
|
|
|
748
888
|
async deleteSession(params: DeleteSessionRequest): Promise<DeleteSessionResponse> {
|
|
@@ -760,6 +900,7 @@ export class AcpAgent implements Agent {
|
|
|
760
900
|
} catch (error) {
|
|
761
901
|
if (error instanceof AcpSdkAdapterError && error.code === "not_found") {
|
|
762
902
|
this.#knownSessionCwds.delete(params.sessionId);
|
|
903
|
+
this.#knownSessionMetadata.delete(params.sessionId);
|
|
763
904
|
return {};
|
|
764
905
|
}
|
|
765
906
|
throw error;
|
|
@@ -770,6 +911,7 @@ export class AcpAgent implements Agent {
|
|
|
770
911
|
this.#lifecycleIdempotencyKey(params.sessionId, "session.delete"),
|
|
771
912
|
);
|
|
772
913
|
this.#knownSessionCwds.delete(params.sessionId);
|
|
914
|
+
this.#knownSessionMetadata.delete(params.sessionId);
|
|
773
915
|
return {};
|
|
774
916
|
} finally {
|
|
775
917
|
this.#finishTeardown(params.sessionId);
|
|
@@ -795,7 +937,11 @@ export class AcpAgent implements Agent {
|
|
|
795
937
|
await this.setSessionMode({ sessionId: params.sessionId, modeId: params.value });
|
|
796
938
|
break;
|
|
797
939
|
case MODEL_CONFIG_ID:
|
|
798
|
-
|
|
940
|
+
if (this.#startupOptions?.modelPreset === undefined) {
|
|
941
|
+
await this.#adapter(params.sessionId).setModel(params.value);
|
|
942
|
+
} else if (params.value !== ACP_CUSTOM_MODEL_PRESET) {
|
|
943
|
+
await this.#adapter(params.sessionId).control("model.profile.set", { id: params.value });
|
|
944
|
+
}
|
|
799
945
|
break;
|
|
800
946
|
case THINKING_CONFIG_ID:
|
|
801
947
|
await this.#adapter(params.sessionId).control("thinking.set", { level: params.value });
|
|
@@ -818,6 +964,7 @@ export class AcpAgent implements Agent {
|
|
|
818
964
|
const record = this.#sessions.get(params.sessionId);
|
|
819
965
|
if (!record) throw new AcpSdkAdapterError("not_found", `Unsupported ACP session: ${params.sessionId}`);
|
|
820
966
|
if (record.activePrompt) throw new AcpSdkAdapterError("conflict", "ACP session already has an active prompt.");
|
|
967
|
+
if (record.authFailure) throw new AcpSdkAdapterError("authentication_failed", record.authFailure);
|
|
821
968
|
const payload = acpPromptPayload(params.prompt);
|
|
822
969
|
let waiter!: PromptWaiter;
|
|
823
970
|
const response = new Promise<PromptResponse>((resolve, reject) => {
|
|
@@ -828,6 +975,7 @@ export class AcpAgent implements Agent {
|
|
|
828
975
|
steeringAtAcknowledgement: record.busy,
|
|
829
976
|
boundary: record.inboundSequence,
|
|
830
977
|
correlation: {},
|
|
978
|
+
emittedAssistantText: "",
|
|
831
979
|
resolve,
|
|
832
980
|
reject,
|
|
833
981
|
};
|
|
@@ -869,18 +1017,33 @@ export class AcpAgent implements Agent {
|
|
|
869
1017
|
}
|
|
870
1018
|
|
|
871
1019
|
async extMethod(method: string, params: JsonObject): Promise<JsonObject> {
|
|
1020
|
+
// An unrecognized extension method is a protocol failure, not an application
|
|
1021
|
+
// result: it must reach the client as JSON-RPC -32601 rather than a resolved
|
|
1022
|
+
// payload. Recognized `_skc/*` methods keep their `{ok:false}` result contract.
|
|
1023
|
+
if (
|
|
1024
|
+
method !== "session/set_model" &&
|
|
1025
|
+
method !== "_skc/sdk/global" &&
|
|
1026
|
+
method !== "_skc/sdk/control" &&
|
|
1027
|
+
method !== "_skc/sdk/query"
|
|
1028
|
+
)
|
|
1029
|
+
throw RequestError.methodNotFound(method);
|
|
872
1030
|
try {
|
|
1031
|
+
if (method === "session/set_model") {
|
|
1032
|
+
const sessionId = typeof params.sessionId === "string" ? params.sessionId : undefined;
|
|
1033
|
+
const modelId = typeof params.modelId === "string" ? params.modelId : undefined;
|
|
1034
|
+
if (!sessionId) throw new AcpSdkAdapterError("invalid_input", "sessionId is required.");
|
|
1035
|
+
if (!modelId) throw new AcpSdkAdapterError("invalid_input", "modelId is required.");
|
|
1036
|
+
await this.setSessionConfigOption({ sessionId, configId: MODEL_CONFIG_ID, value: modelId });
|
|
1037
|
+
return {};
|
|
1038
|
+
}
|
|
873
1039
|
if (method === "_skc/sdk/global") {
|
|
874
1040
|
const result = await (await this.#brokerAdapter()).handle(method, params);
|
|
875
1041
|
return object(result) ?? {};
|
|
876
1042
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
return object(result) ?? {};
|
|
882
|
-
}
|
|
883
|
-
throw new AcpSdkAdapterError("method_not_found", `Unknown ACP ext method: ${method}`);
|
|
1043
|
+
const id = typeof params.sessionId === "string" ? params.sessionId : undefined;
|
|
1044
|
+
if (!id) throw new AcpSdkAdapterError("invalid_input", "sessionId is required.");
|
|
1045
|
+
const result = await this.#adapter(id).handle(method, params);
|
|
1046
|
+
return object(result) ?? {};
|
|
884
1047
|
} catch (error) {
|
|
885
1048
|
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "internal";
|
|
886
1049
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -932,12 +1095,23 @@ export class AcpAgent implements Agent {
|
|
|
932
1095
|
);
|
|
933
1096
|
}
|
|
934
1097
|
|
|
935
|
-
|
|
1098
|
+
#isDefinitiveBrokerResponse(error: unknown): boolean {
|
|
1099
|
+
// Response-derived client errors retain the broker error as details. Responses
|
|
1100
|
+
// that represent ongoing or ambiguous lifecycle work must keep their key.
|
|
1101
|
+
if (!(error instanceof SdkClientError)) return false;
|
|
1102
|
+
const details = object(error.details);
|
|
1103
|
+
if (details?.code !== error.code || details.message !== error.message) return false;
|
|
1104
|
+
return !["terminal_uncertain", "cleanup_pending", "broker_restarting", "unavailable"].includes(error.code);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
async #attachExisting(id: string, cwd: string, mcpServers: SessionLifecycleMcpServer[] = []): Promise<void> {
|
|
936
1108
|
const epoch = this.#sessionEpoch(id);
|
|
937
1109
|
const attached = this.#sessions.get(id);
|
|
938
1110
|
if (attached) {
|
|
939
1111
|
if (path.resolve(attached.cwd) !== path.resolve(cwd))
|
|
940
1112
|
throw new AcpSdkAdapterError("conflict", `ACP session ${id} has conflicting cwd authority.`);
|
|
1113
|
+
if (mcpServers.length > 0)
|
|
1114
|
+
throw new AcpSdkAdapterError("conflict", `ACP session ${id} already has immutable MCP configuration.`);
|
|
941
1115
|
return;
|
|
942
1116
|
}
|
|
943
1117
|
const knownCwd = this.#knownSessionCwds.get(id);
|
|
@@ -954,7 +1128,7 @@ export class AcpAgent implements Agent {
|
|
|
954
1128
|
return;
|
|
955
1129
|
}
|
|
956
1130
|
|
|
957
|
-
const task = this.#resolveExistingAttachment(id, cwd, epoch);
|
|
1131
|
+
const task = this.#resolveExistingAttachment(id, cwd, epoch, mcpServers);
|
|
958
1132
|
const pending = { epoch, task };
|
|
959
1133
|
this.#resolvingExisting.set(id, pending);
|
|
960
1134
|
try {
|
|
@@ -965,11 +1139,18 @@ export class AcpAgent implements Agent {
|
|
|
965
1139
|
}
|
|
966
1140
|
}
|
|
967
1141
|
|
|
968
|
-
async #resolveExistingAttachment(
|
|
1142
|
+
async #resolveExistingAttachment(
|
|
1143
|
+
id: string,
|
|
1144
|
+
cwd: string,
|
|
1145
|
+
epoch: number,
|
|
1146
|
+
mcpServers: SessionLifecycleMcpServer[],
|
|
1147
|
+
): Promise<void> {
|
|
969
1148
|
this.#assertSessionEpoch(id, epoch);
|
|
970
1149
|
const indexed = await this.#scopedBrokerSession(id, cwd);
|
|
971
1150
|
this.#assertSessionEpoch(id, epoch);
|
|
972
1151
|
if (indexed?.live) {
|
|
1152
|
+
if (mcpServers.length > 0)
|
|
1153
|
+
throw new AcpSdkAdapterError("conflict", `ACP session ${id} already has immutable MCP configuration.`);
|
|
973
1154
|
const result = await this.#brokerEndpoint(id, indexed.endpointGeneration);
|
|
974
1155
|
this.#assertSessionEpoch(id, epoch);
|
|
975
1156
|
await this.#attach(id, cwd, endpoint(result), epoch);
|
|
@@ -978,10 +1159,17 @@ export class AcpAgent implements Agent {
|
|
|
978
1159
|
|
|
979
1160
|
const saved = await this.#resolveSavedSession(id, cwd);
|
|
980
1161
|
this.#assertSessionEpoch(id, epoch);
|
|
981
|
-
const result = await
|
|
1162
|
+
const result = await this.#launchSessionWithMcp(
|
|
982
1163
|
"session.resume",
|
|
983
|
-
{
|
|
1164
|
+
{
|
|
1165
|
+
cwd,
|
|
1166
|
+
sessionId: id,
|
|
1167
|
+
sessionPath: saved,
|
|
1168
|
+
target: { path: cwd },
|
|
1169
|
+
...(mcpServers.length > 0 ? { mcpServers, readinessTimeoutMs: ACP_MCP_LIFECYCLE_TIMEOUT_MS } : {}),
|
|
1170
|
+
},
|
|
984
1171
|
randomUUID(),
|
|
1172
|
+
mcpServers,
|
|
985
1173
|
);
|
|
986
1174
|
this.#assertSessionEpoch(id, epoch);
|
|
987
1175
|
await this.#attach(id, cwd, endpoint(result), epoch);
|
|
@@ -1045,11 +1233,13 @@ export class AcpAgent implements Agent {
|
|
|
1045
1233
|
const record: SessionRecord = {
|
|
1046
1234
|
cwd,
|
|
1047
1235
|
adapter,
|
|
1236
|
+
closeIdempotencyKey: randomUUID(),
|
|
1048
1237
|
unsubscribe: () => {},
|
|
1049
1238
|
reconnectUnsubscribe: () => {},
|
|
1050
1239
|
frameTail: Promise.resolve(),
|
|
1051
1240
|
inboundSequence: 0,
|
|
1052
1241
|
busy: false,
|
|
1242
|
+
toolArgs: new Map(),
|
|
1053
1243
|
};
|
|
1054
1244
|
record.unsubscribe = adapter.onFrame(frame => this.#enqueueSdkFrame(id, adapter!, frame));
|
|
1055
1245
|
record.reconnectUnsubscribe = adapter.onReconnectFailed(error =>
|
|
@@ -1059,6 +1249,7 @@ export class AcpAgent implements Agent {
|
|
|
1059
1249
|
this.#knownSessionCwds.set(id, cwd);
|
|
1060
1250
|
await applyAcpPermissionMode(adapter, this.#clientCapabilities);
|
|
1061
1251
|
this.#assertSessionEpoch(id, epoch);
|
|
1252
|
+
this.#pendingCloseIdempotencyKeys.delete(id);
|
|
1062
1253
|
} catch (error) {
|
|
1063
1254
|
if (adapter && this.#sessions.get(id)?.adapter === adapter) {
|
|
1064
1255
|
try {
|
|
@@ -1101,6 +1292,23 @@ export class AcpAgent implements Agent {
|
|
|
1101
1292
|
async #discardNewSession(id: string): Promise<void> {
|
|
1102
1293
|
await this.#teardownSession(id, "discarded", true);
|
|
1103
1294
|
this.#knownSessionCwds.delete(id);
|
|
1295
|
+
this.#knownSessionMetadata.delete(id);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
async #closeOwnedSession(id: string): Promise<CloseSessionResponse> {
|
|
1299
|
+
this.#beginTeardown(id);
|
|
1300
|
+
try {
|
|
1301
|
+
const attaching = this.#attaching.get(id);
|
|
1302
|
+
// The record is published before permission initialization. Let a canceled
|
|
1303
|
+
// provisional attachment retire it before selecting the generation key.
|
|
1304
|
+
if (attaching) await Promise.allSettled([attaching.task]);
|
|
1305
|
+
await this.#teardownSession(id, "closed", true);
|
|
1306
|
+
this.#knownSessionCwds.delete(id);
|
|
1307
|
+
this.#knownSessionMetadata.delete(id);
|
|
1308
|
+
return {};
|
|
1309
|
+
} finally {
|
|
1310
|
+
this.#finishTeardown(id);
|
|
1311
|
+
}
|
|
1104
1312
|
}
|
|
1105
1313
|
|
|
1106
1314
|
/**
|
|
@@ -1130,13 +1338,13 @@ export class AcpAgent implements Agent {
|
|
|
1130
1338
|
failures.push(error);
|
|
1131
1339
|
}
|
|
1132
1340
|
if (closeRemote) {
|
|
1341
|
+
const closeIdempotencyKey =
|
|
1342
|
+
record?.closeIdempotencyKey ?? this.#pendingCloseIdempotencyKeys.get(id) ?? randomUUID();
|
|
1343
|
+
this.#pendingCloseIdempotencyKeys.set(id, closeIdempotencyKey);
|
|
1133
1344
|
try {
|
|
1134
|
-
await (await this.#brokerAdapter()).global(
|
|
1135
|
-
"session.close",
|
|
1136
|
-
{ sessionId: id },
|
|
1137
|
-
this.#lifecycleIdempotencyKey(id, "session.close"),
|
|
1138
|
-
);
|
|
1345
|
+
await (await this.#brokerAdapter()).global("session.close", { sessionId: id }, closeIdempotencyKey);
|
|
1139
1346
|
} catch (error) {
|
|
1347
|
+
if (this.#isDefinitiveBrokerResponse(error)) this.#pendingCloseIdempotencyKeys.delete(id);
|
|
1140
1348
|
if (!(ownershipBound && this.#isAlreadyGone(error))) failures.push(error);
|
|
1141
1349
|
}
|
|
1142
1350
|
}
|
|
@@ -1146,6 +1354,7 @@ export class AcpAgent implements Agent {
|
|
|
1146
1354
|
.join("; ");
|
|
1147
1355
|
throw aggregateAcpFailure("terminal_uncertain", `ACP session cleanup is uncertain: ${detail}`, failures);
|
|
1148
1356
|
}
|
|
1357
|
+
if (closeRemote) this.#pendingCloseIdempotencyKeys.delete(id);
|
|
1149
1358
|
} finally {
|
|
1150
1359
|
this.#finishTeardown(id);
|
|
1151
1360
|
}
|
|
@@ -1275,13 +1484,23 @@ export class AcpAgent implements Agent {
|
|
|
1275
1484
|
const received = receivedSdkEvent(frame);
|
|
1276
1485
|
if (!received) return;
|
|
1277
1486
|
const { event, wirePayload } = received;
|
|
1487
|
+
const toolCallId = typeof event.toolCallId === "string" ? event.toolCallId : undefined;
|
|
1488
|
+
if (
|
|
1489
|
+
toolCallId &&
|
|
1490
|
+
(event.type === "tool_execution_start" || event.type === "tool_execution_update") &&
|
|
1491
|
+
"args" in event
|
|
1492
|
+
) {
|
|
1493
|
+
record.toolArgs.set(toolCallId, event.args);
|
|
1494
|
+
}
|
|
1278
1495
|
const correlation = correlationFrom(frame, event);
|
|
1279
1496
|
const activePrompt = record.activePrompt;
|
|
1280
1497
|
// Prompt ownership is updated before publishing client notifications, but a
|
|
1281
|
-
// terminal waiter is resolved only after
|
|
1282
|
-
//
|
|
1498
|
+
// terminal waiter is resolved only after acknowledgement and publication
|
|
1499
|
+
// succeed. Fast hosts may emit the whole turn before acknowledging the
|
|
1500
|
+
// control request; retaining that activity prevents an idle ACP prompt from
|
|
1501
|
+
// remaining open forever.
|
|
1283
1502
|
if (
|
|
1284
|
-
activePrompt
|
|
1503
|
+
activePrompt &&
|
|
1285
1504
|
sequence > activePrompt.boundary &&
|
|
1286
1505
|
!correlationsConflict(activePrompt.correlation, correlation)
|
|
1287
1506
|
) {
|
|
@@ -1290,6 +1509,8 @@ export class AcpAgent implements Agent {
|
|
|
1290
1509
|
} else if (event.type === "agent_end") {
|
|
1291
1510
|
if (
|
|
1292
1511
|
correlationsMatch(activePrompt.correlation, correlation) ||
|
|
1512
|
+
correlation.commandId !== undefined ||
|
|
1513
|
+
correlation.turnId !== undefined ||
|
|
1293
1514
|
activePrompt.activityObserved ||
|
|
1294
1515
|
activePrompt.steeringAtAcknowledgement
|
|
1295
1516
|
)
|
|
@@ -1299,17 +1520,69 @@ export class AcpAgent implements Agent {
|
|
|
1299
1520
|
if (wirePayload) {
|
|
1300
1521
|
for (const notification of mapAgentWireEventPayloadToAcpSessionUpdates(wirePayload as never, id, {
|
|
1301
1522
|
cwd: record.cwd,
|
|
1523
|
+
getToolArgs: id => record.toolArgs.get(id),
|
|
1302
1524
|
getMessageProgress: message => {
|
|
1303
1525
|
if (!activePrompt || !object(message)) return undefined;
|
|
1304
1526
|
activePrompt.messageProgress ??= { textEmitted: false, thoughtEmitted: false };
|
|
1305
1527
|
return activePrompt.messageProgress;
|
|
1306
1528
|
},
|
|
1307
|
-
}))
|
|
1529
|
+
})) {
|
|
1530
|
+
if (
|
|
1531
|
+
activePrompt &&
|
|
1532
|
+
notification.update.sessionUpdate === "agent_message_chunk" &&
|
|
1533
|
+
notification.update.content.type === "text"
|
|
1534
|
+
)
|
|
1535
|
+
activePrompt.emittedAssistantText += notification.update.content.text;
|
|
1308
1536
|
await this.#publishSessionUpdate(id, notification, adapter);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (toolCallId && event.type === "tool_execution_end") record.toolArgs.delete(toolCallId);
|
|
1540
|
+
if (event.type === "agent_start") {
|
|
1541
|
+
await this.#publishSessionUpdate(
|
|
1542
|
+
id,
|
|
1543
|
+
{
|
|
1544
|
+
sessionId: id,
|
|
1545
|
+
update: {
|
|
1546
|
+
sessionUpdate: "session_info_update",
|
|
1547
|
+
updatedAt: new Date().toISOString(),
|
|
1548
|
+
_meta: { skcPhase: "working", running: true, skcRunning: true },
|
|
1549
|
+
},
|
|
1550
|
+
},
|
|
1551
|
+
adapter,
|
|
1552
|
+
);
|
|
1309
1553
|
}
|
|
1310
1554
|
if (event.type === "message_end" && object(event.message)?.role === "assistant" && activePrompt)
|
|
1311
1555
|
activePrompt.messageProgress = undefined;
|
|
1312
|
-
if (event.type === "agent_end")
|
|
1556
|
+
if (event.type === "agent_end") {
|
|
1557
|
+
const finalText = typeof event.finalText === "string" ? event.finalText : "";
|
|
1558
|
+
if (activePrompt && finalText) {
|
|
1559
|
+
const resolution = resolveAcpFinalText(activePrompt.emittedAssistantText, finalText);
|
|
1560
|
+
if (resolution.kind === "emit") {
|
|
1561
|
+
activePrompt.emittedAssistantText += resolution.text;
|
|
1562
|
+
await this.#publishSessionUpdate(
|
|
1563
|
+
id,
|
|
1564
|
+
{
|
|
1565
|
+
sessionId: id,
|
|
1566
|
+
update: {
|
|
1567
|
+
sessionUpdate: "agent_message_chunk",
|
|
1568
|
+
content: { type: "text", text: resolution.text },
|
|
1569
|
+
...(resolution.final.truncated ? { _meta: { skcFinalTextTruncated: true } } : {}),
|
|
1570
|
+
},
|
|
1571
|
+
},
|
|
1572
|
+
adapter,
|
|
1573
|
+
);
|
|
1574
|
+
} else if (resolution.kind === "divergent") {
|
|
1575
|
+
logger.warn("acp_final_text_diverged", {
|
|
1576
|
+
sessionId: id,
|
|
1577
|
+
...(activePrompt.correlation.commandId ? { commandId: activePrompt.correlation.commandId } : {}),
|
|
1578
|
+
...(activePrompt.correlation.turnId ? { turnId: activePrompt.correlation.turnId } : {}),
|
|
1579
|
+
streamedLength: activePrompt.emittedAssistantText.length,
|
|
1580
|
+
finalLength: resolution.final.text.length,
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
await this.#emitEndOfTurnUpdates(id, adapter);
|
|
1585
|
+
}
|
|
1313
1586
|
if (activePrompt) this.#settlePrompt(record, activePrompt);
|
|
1314
1587
|
}
|
|
1315
1588
|
|
|
@@ -1328,6 +1601,7 @@ export class AcpAgent implements Agent {
|
|
|
1328
1601
|
|
|
1329
1602
|
async #emitEndOfTurnUpdates(id: string, adapter: AcpSdkAdapter): Promise<void> {
|
|
1330
1603
|
let usage: JsonObject | undefined;
|
|
1604
|
+
let title: string | undefined;
|
|
1331
1605
|
try {
|
|
1332
1606
|
const response = object(await adapter.query("context.get"));
|
|
1333
1607
|
const result = object(response?.result) ?? response;
|
|
@@ -1335,6 +1609,15 @@ export class AcpAgent implements Agent {
|
|
|
1335
1609
|
} catch {
|
|
1336
1610
|
// Context usage is advisory ACP metadata; prompt completion remains authoritative.
|
|
1337
1611
|
}
|
|
1612
|
+
try {
|
|
1613
|
+
const response = object(await adapter.query("session.metadata"));
|
|
1614
|
+
const result = object(response?.result) ?? response;
|
|
1615
|
+
const metadata = pageItems(result)[0];
|
|
1616
|
+
const item = object(metadata);
|
|
1617
|
+
if (typeof item?.name === "string" && item.name) title = item.name;
|
|
1618
|
+
} catch {
|
|
1619
|
+
// Session naming is advisory; prompt completion remains authoritative.
|
|
1620
|
+
}
|
|
1338
1621
|
if (typeof usage?.tokens === "number" && typeof usage.contextWindow === "number") {
|
|
1339
1622
|
await this.#publishSessionUpdate(
|
|
1340
1623
|
id,
|
|
@@ -1349,13 +1632,16 @@ export class AcpAgent implements Agent {
|
|
|
1349
1632
|
adapter,
|
|
1350
1633
|
);
|
|
1351
1634
|
}
|
|
1635
|
+
const updatedAt = new Date().toISOString();
|
|
1636
|
+
this.#knownSessionMetadata.set(id, { ...(title ? { title } : {}), updatedAt });
|
|
1352
1637
|
await this.#publishSessionUpdate(
|
|
1353
1638
|
id,
|
|
1354
1639
|
{
|
|
1355
1640
|
sessionId: id,
|
|
1356
1641
|
update: {
|
|
1357
1642
|
sessionUpdate: "session_info_update",
|
|
1358
|
-
|
|
1643
|
+
...(title ? { title } : {}),
|
|
1644
|
+
updatedAt,
|
|
1359
1645
|
_meta: { skcPhase: "idle", running: false, skcRunning: false },
|
|
1360
1646
|
},
|
|
1361
1647
|
},
|
|
@@ -1379,27 +1665,68 @@ export class AcpAgent implements Agent {
|
|
|
1379
1665
|
}
|
|
1380
1666
|
}
|
|
1381
1667
|
|
|
1382
|
-
async #sessionState(
|
|
1668
|
+
async #sessionState(
|
|
1669
|
+
id: string,
|
|
1670
|
+
rejectUnavailableStartupPreset = false,
|
|
1671
|
+
): Promise<Pick<NewSessionResponse, "configOptions" | "modes">> {
|
|
1383
1672
|
const record = this.#sessions.get(id);
|
|
1384
1673
|
if (!record) throw new AcpSdkAdapterError("not_found", `Unsupported ACP session: ${id}`);
|
|
1385
|
-
const
|
|
1674
|
+
const modelPreset = this.#startupOptions?.modelPreset;
|
|
1675
|
+
const [config, modelCatalog] = await Promise.all([
|
|
1386
1676
|
record.adapter.query("config.list/get"),
|
|
1387
|
-
record.adapter.query("models.list/current"),
|
|
1677
|
+
record.adapter.query(modelPreset === undefined ? "models.list/current" : "models.profiles.list"),
|
|
1388
1678
|
]);
|
|
1389
|
-
|
|
1679
|
+
record.authFailure = undefined;
|
|
1680
|
+
if (modelPreset !== undefined) {
|
|
1681
|
+
const activePreset = configValues(config).get(MODEL_PRESET_CONFIG_KEY);
|
|
1682
|
+
const activeProfile = pageItems(modelCatalog)
|
|
1683
|
+
.map(item => object(item))
|
|
1684
|
+
.find(item => item?.id === activePreset);
|
|
1685
|
+
if (activePreset && activeProfile?.available === false) {
|
|
1686
|
+
record.authFailure =
|
|
1687
|
+
`Model preset "${activePreset}" has no usable provider credentials. ` +
|
|
1688
|
+
"Authenticate the required provider in Sayknow-CLI or select an available preset before prompting.";
|
|
1689
|
+
if (rejectUnavailableStartupPreset && activePreset === modelPreset)
|
|
1690
|
+
throw new AcpSdkAdapterError("authentication_failed", record.authFailure);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return acpSessionStateFromConfig(config, modelCatalog, modelPreset);
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
async #publishAvailableCommands(id: string, adapter: AcpSdkAdapter): Promise<void> {
|
|
1697
|
+
let skills: unknown;
|
|
1698
|
+
try {
|
|
1699
|
+
skills = await adapter.query("skill.list/state");
|
|
1700
|
+
} catch {
|
|
1701
|
+
// Builtins remain useful when an older SDK host cannot expose skill state.
|
|
1702
|
+
}
|
|
1703
|
+
await this.#publishSessionUpdate(
|
|
1704
|
+
id,
|
|
1705
|
+
{
|
|
1706
|
+
sessionId: id,
|
|
1707
|
+
update: {
|
|
1708
|
+
sessionUpdate: "available_commands_update",
|
|
1709
|
+
availableCommands: acpAvailableCommandsFromSkills(skills),
|
|
1710
|
+
},
|
|
1711
|
+
},
|
|
1712
|
+
adapter,
|
|
1713
|
+
);
|
|
1390
1714
|
}
|
|
1391
1715
|
|
|
1392
1716
|
async #replaySession(id: string): Promise<void> {
|
|
1393
1717
|
const adapter = this.#adapter(id);
|
|
1718
|
+
const record = this.#sessions.get(id);
|
|
1719
|
+
if (!record) return;
|
|
1394
1720
|
let cursor: string | undefined;
|
|
1395
1721
|
let imageLimitationReported = false;
|
|
1722
|
+
const replayTools = new Map<string, { name: string; args: unknown }>();
|
|
1396
1723
|
for (let pageCount = 0; pageCount < MAX_ACP_REPLAY_PAGES; pageCount++) {
|
|
1397
1724
|
const response = object(await adapter.query("transcript.list", {}, cursor));
|
|
1398
1725
|
const result = object(response?.result) ?? response;
|
|
1399
1726
|
const page = object(result?.page);
|
|
1400
1727
|
for (const item of Array.isArray(page?.items) ? page.items : []) {
|
|
1401
1728
|
const message = object(item);
|
|
1402
|
-
if (!message
|
|
1729
|
+
if (!message) continue;
|
|
1403
1730
|
const content = transcriptReplayContent(message);
|
|
1404
1731
|
if (!imageLimitationReported) {
|
|
1405
1732
|
imageLimitationReported = true;
|
|
@@ -1416,6 +1743,95 @@ export class AcpAgent implements Agent {
|
|
|
1416
1743
|
);
|
|
1417
1744
|
}
|
|
1418
1745
|
const messageId = typeof message.id === "string" ? message.id : undefined;
|
|
1746
|
+
const richContent = Array.isArray(message.content) ? message.content : undefined;
|
|
1747
|
+
if ((message.role === "user" || message.role === "assistant") && richContent) {
|
|
1748
|
+
for (const rawBlock of richContent) {
|
|
1749
|
+
const block = object(rawBlock);
|
|
1750
|
+
if (!block || typeof block.type !== "string") continue;
|
|
1751
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.length > 0) {
|
|
1752
|
+
await this.#publishSessionUpdate(
|
|
1753
|
+
id,
|
|
1754
|
+
{
|
|
1755
|
+
sessionId: id,
|
|
1756
|
+
update: {
|
|
1757
|
+
sessionUpdate: message.role === "user" ? "user_message_chunk" : "agent_message_chunk",
|
|
1758
|
+
content: { type: "text", text: block.text },
|
|
1759
|
+
...(messageId ? { messageId } : {}),
|
|
1760
|
+
},
|
|
1761
|
+
},
|
|
1762
|
+
adapter,
|
|
1763
|
+
);
|
|
1764
|
+
} else if (
|
|
1765
|
+
message.role === "assistant" &&
|
|
1766
|
+
block.type === "thinking" &&
|
|
1767
|
+
typeof block.thinking === "string" &&
|
|
1768
|
+
block.thinking.length > 0
|
|
1769
|
+
) {
|
|
1770
|
+
await this.#publishSessionUpdate(
|
|
1771
|
+
id,
|
|
1772
|
+
{
|
|
1773
|
+
sessionId: id,
|
|
1774
|
+
update: {
|
|
1775
|
+
sessionUpdate: "agent_thought_chunk",
|
|
1776
|
+
content: { type: "text", text: block.thinking },
|
|
1777
|
+
...(messageId ? { messageId } : {}),
|
|
1778
|
+
},
|
|
1779
|
+
},
|
|
1780
|
+
adapter,
|
|
1781
|
+
);
|
|
1782
|
+
} else if (
|
|
1783
|
+
message.role === "assistant" &&
|
|
1784
|
+
block.type === "toolCall" &&
|
|
1785
|
+
typeof block.id === "string" &&
|
|
1786
|
+
typeof block.name === "string"
|
|
1787
|
+
) {
|
|
1788
|
+
const args = block.arguments ?? {};
|
|
1789
|
+
replayTools.set(block.id, { name: block.name, args });
|
|
1790
|
+
await this.#publishSessionUpdate(
|
|
1791
|
+
id,
|
|
1792
|
+
{
|
|
1793
|
+
sessionId: id,
|
|
1794
|
+
update: buildToolCallStartUpdate({
|
|
1795
|
+
toolCallId: block.id,
|
|
1796
|
+
toolName: block.name,
|
|
1797
|
+
args,
|
|
1798
|
+
cwd: record.cwd,
|
|
1799
|
+
}),
|
|
1800
|
+
},
|
|
1801
|
+
adapter,
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
if (message.role === "toolResult" && typeof message.toolCallId === "string") {
|
|
1808
|
+
const replayTool = replayTools.get(message.toolCallId);
|
|
1809
|
+
const toolName = typeof message.toolName === "string" ? message.toolName : replayTool?.name;
|
|
1810
|
+
if (!toolName) continue;
|
|
1811
|
+
const resultContent = richContent
|
|
1812
|
+
?.map(object)
|
|
1813
|
+
.filter(
|
|
1814
|
+
(block): block is JsonObject =>
|
|
1815
|
+
block !== undefined && block.type === "text" && typeof block.text === "string",
|
|
1816
|
+
)
|
|
1817
|
+
.map(block => ({ type: "text" as const, text: String(block.text) }));
|
|
1818
|
+
for (const notification of mapAgentSessionEventToAcpSessionUpdates(
|
|
1819
|
+
{
|
|
1820
|
+
type: "tool_execution_end",
|
|
1821
|
+
toolCallId: message.toolCallId,
|
|
1822
|
+
toolName,
|
|
1823
|
+
result: { content: resultContent ?? content.blocks },
|
|
1824
|
+
isError: message.isError === true,
|
|
1825
|
+
} as never,
|
|
1826
|
+
id,
|
|
1827
|
+
{ cwd: record.cwd, getToolArgs: toolCallId => replayTools.get(toolCallId)?.args },
|
|
1828
|
+
)) {
|
|
1829
|
+
await this.#publishSessionUpdate(id, notification, adapter);
|
|
1830
|
+
}
|
|
1831
|
+
replayTools.delete(message.toolCallId);
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
if (message.role !== "user" && message.role !== "assistant") continue;
|
|
1419
1835
|
for (const block of content.blocks) {
|
|
1420
1836
|
await this.#publishSessionUpdate(
|
|
1421
1837
|
id,
|
|
@@ -1441,17 +1857,33 @@ export class AcpAgent implements Agent {
|
|
|
1441
1857
|
setTimeout(() => {
|
|
1442
1858
|
const record = this.#sessions.get(id);
|
|
1443
1859
|
if (!record || this.#connection.signal.aborted) return;
|
|
1444
|
-
void
|
|
1445
|
-
id,
|
|
1446
|
-
{
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1860
|
+
void (async () => {
|
|
1861
|
+
await this.#publishAvailableCommands(id, record.adapter);
|
|
1862
|
+
if (record.authFailure) {
|
|
1863
|
+
await this.#publishSessionUpdate(
|
|
1864
|
+
id,
|
|
1865
|
+
{
|
|
1866
|
+
sessionId: id,
|
|
1867
|
+
update: {
|
|
1868
|
+
sessionUpdate: "agent_thought_chunk",
|
|
1869
|
+
content: { type: "text", text: `[error:auth] ${record.authFailure}\n` },
|
|
1870
|
+
},
|
|
1871
|
+
},
|
|
1872
|
+
record.adapter,
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
await this.#publishSessionUpdate(
|
|
1876
|
+
id,
|
|
1877
|
+
{
|
|
1878
|
+
sessionId: id,
|
|
1879
|
+
update: {
|
|
1880
|
+
sessionUpdate: "session_info_update",
|
|
1881
|
+
_meta: { skcPhase: "idle", running: false, skcRunning: false },
|
|
1882
|
+
},
|
|
1451
1883
|
},
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
).catch(() => undefined);
|
|
1884
|
+
record.adapter,
|
|
1885
|
+
);
|
|
1886
|
+
})().catch(() => undefined);
|
|
1455
1887
|
}, ACP_BOOTSTRAP_RACE_GUARD_MS);
|
|
1456
1888
|
}
|
|
1457
1889
|
|
|
@@ -1466,9 +1898,138 @@ export class AcpAgent implements Agent {
|
|
|
1466
1898
|
if (!path.isAbsolute(cwd)) throw new Error(`ACP cwd must be an absolute path: ${cwd}`);
|
|
1467
1899
|
}
|
|
1468
1900
|
|
|
1469
|
-
#
|
|
1470
|
-
if (
|
|
1471
|
-
throw new AcpSdkAdapterError("unsupported", "
|
|
1901
|
+
#assertNoAdditionalDirectories(directories: string[] | null | undefined): void {
|
|
1902
|
+
if (directories && directories.length > 0)
|
|
1903
|
+
throw new AcpSdkAdapterError("unsupported", "ACP additional directories are not supported.");
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
#mcpServers(params: { mcpServers?: unknown[] }): SessionLifecycleMcpServer[] {
|
|
1907
|
+
const servers = params.mcpServers ?? [];
|
|
1908
|
+
if (servers.length > 64)
|
|
1909
|
+
throw new AcpSdkAdapterError("unsupported", "ACP supports at most 64 MCP servers per session.");
|
|
1910
|
+
const result: SessionLifecycleMcpServer[] = [];
|
|
1911
|
+
const names = new Set<string>();
|
|
1912
|
+
for (const value of servers) {
|
|
1913
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
1914
|
+
throw new AcpSdkAdapterError("invalid_input", "ACP MCP server definitions must be objects.");
|
|
1915
|
+
const server = value as Record<string, unknown>;
|
|
1916
|
+
if (typeof server.name !== "string" || !/^[A-Za-z0-9_.-]{1,100}$/.test(server.name) || names.has(server.name))
|
|
1917
|
+
throw new AcpSdkAdapterError("invalid_input", "ACP MCP servers must have unique safe names.");
|
|
1918
|
+
names.add(server.name);
|
|
1919
|
+
if (server.type === "http" || server.type === "sse") {
|
|
1920
|
+
if (
|
|
1921
|
+
typeof server.url !== "string" ||
|
|
1922
|
+
server.url.length > 8_192 ||
|
|
1923
|
+
!Array.isArray(server.headers) ||
|
|
1924
|
+
server.headers.length > 100
|
|
1925
|
+
)
|
|
1926
|
+
throw new AcpSdkAdapterError("invalid_input", "ACP remote MCP servers require a valid URL and headers.");
|
|
1927
|
+
let parsedUrl: URL;
|
|
1928
|
+
try {
|
|
1929
|
+
parsedUrl = new URL(server.url);
|
|
1930
|
+
} catch {
|
|
1931
|
+
throw new AcpSdkAdapterError("invalid_input", "ACP MCP URL is invalid.");
|
|
1932
|
+
}
|
|
1933
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:")
|
|
1934
|
+
throw new AcpSdkAdapterError("invalid_input", "ACP MCP URLs must use HTTP or HTTPS.");
|
|
1935
|
+
const headers: Record<string, string> = {};
|
|
1936
|
+
for (const value of server.headers) {
|
|
1937
|
+
const header = object(value);
|
|
1938
|
+
if (
|
|
1939
|
+
typeof header?.name !== "string" ||
|
|
1940
|
+
header.name.length === 0 ||
|
|
1941
|
+
header.name.length > 256 ||
|
|
1942
|
+
header.name.includes("\r") ||
|
|
1943
|
+
header.name.includes("\n") ||
|
|
1944
|
+
typeof header.value !== "string" ||
|
|
1945
|
+
header.value.length > 8_192 ||
|
|
1946
|
+
header.value.includes("\r") ||
|
|
1947
|
+
header.value.includes("\n") ||
|
|
1948
|
+
Object.hasOwn(headers, header.name)
|
|
1949
|
+
)
|
|
1950
|
+
throw new AcpSdkAdapterError(
|
|
1951
|
+
"invalid_input",
|
|
1952
|
+
"ACP MCP headers must have unique valid names and values.",
|
|
1953
|
+
);
|
|
1954
|
+
headers[header.name] = header.value;
|
|
1955
|
+
}
|
|
1956
|
+
result.push({
|
|
1957
|
+
type: server.type,
|
|
1958
|
+
name: server.name,
|
|
1959
|
+
url: parsedUrl.toString(),
|
|
1960
|
+
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
1961
|
+
});
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
if (
|
|
1965
|
+
(server.type !== undefined && server.type !== "stdio") ||
|
|
1966
|
+
typeof server.command !== "string" ||
|
|
1967
|
+
server.command.length > 4_096 ||
|
|
1968
|
+
!path.isAbsolute(server.command) ||
|
|
1969
|
+
!Array.isArray(server.args) ||
|
|
1970
|
+
server.args.length > 100 ||
|
|
1971
|
+
!server.args.every(argument => typeof argument === "string" && argument.length <= 8_192) ||
|
|
1972
|
+
!Array.isArray(server.env) ||
|
|
1973
|
+
server.env.length > 100
|
|
1974
|
+
)
|
|
1975
|
+
throw new AcpSdkAdapterError(
|
|
1976
|
+
"invalid_input",
|
|
1977
|
+
"ACP stdio MCP servers require an absolute command and bounded arguments and environment variables.",
|
|
1978
|
+
);
|
|
1979
|
+
const env: Record<string, string> = {};
|
|
1980
|
+
for (const value of server.env) {
|
|
1981
|
+
const variable = object(value);
|
|
1982
|
+
if (
|
|
1983
|
+
typeof variable?.name !== "string" ||
|
|
1984
|
+
!/^[A-Za-z_][A-Za-z0-9_]*$/.test(variable.name) ||
|
|
1985
|
+
typeof variable.value !== "string" ||
|
|
1986
|
+
variable.value.length > 32_768 ||
|
|
1987
|
+
Object.hasOwn(env, variable.name)
|
|
1988
|
+
)
|
|
1989
|
+
throw new AcpSdkAdapterError(
|
|
1990
|
+
"invalid_input",
|
|
1991
|
+
"ACP MCP environment variables must have unique valid names and string values.",
|
|
1992
|
+
);
|
|
1993
|
+
env[variable.name] = variable.value;
|
|
1994
|
+
}
|
|
1995
|
+
result.push({
|
|
1996
|
+
name: server.name,
|
|
1997
|
+
command: server.command,
|
|
1998
|
+
args: server.args as string[],
|
|
1999
|
+
...(Object.keys(env).length > 0 ? { env } : {}),
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
return result;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
async #launchSessionWithMcp(
|
|
2006
|
+
operation: "session.create" | "session.fork" | "session.resume",
|
|
2007
|
+
input: JsonObject,
|
|
2008
|
+
idempotencyKey: string,
|
|
2009
|
+
mcpServers: SessionLifecycleMcpServer[],
|
|
2010
|
+
): Promise<unknown> {
|
|
2011
|
+
try {
|
|
2012
|
+
return await (await this.#brokerAdapter()).global(operation, input, idempotencyKey);
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
const code =
|
|
2015
|
+
typeof error === "object" && error !== null && "code" in error && typeof error.code === "string"
|
|
2016
|
+
? error.code
|
|
2017
|
+
: undefined;
|
|
2018
|
+
if (
|
|
2019
|
+
mcpServers.length === 0 ||
|
|
2020
|
+
code === "invalid_input" ||
|
|
2021
|
+
code === "authentication_failed" ||
|
|
2022
|
+
code === "unknown_model_profile" ||
|
|
2023
|
+
code === "model_profile_registry_error"
|
|
2024
|
+
)
|
|
2025
|
+
throw error;
|
|
2026
|
+
const names = mcpServers
|
|
2027
|
+
.slice(0, 8)
|
|
2028
|
+
.map(server => server.name)
|
|
2029
|
+
.join(", ");
|
|
2030
|
+
const suffix = mcpServers.length > 8 ? `, and ${mcpServers.length - 8} more` : "";
|
|
2031
|
+
throw new AcpSdkAdapterError("unavailable", `MCP server request failed to start (${names}${suffix}).`);
|
|
2032
|
+
}
|
|
1472
2033
|
}
|
|
1473
2034
|
|
|
1474
2035
|
#beginDispose(): void {
|
|
@@ -1494,6 +2055,9 @@ export class AcpAgent implements Agent {
|
|
|
1494
2055
|
this.#attaching.clear();
|
|
1495
2056
|
this.#resolvingExisting.clear();
|
|
1496
2057
|
this.#knownSessionCwds.clear();
|
|
2058
|
+
this.#knownSessionMetadata.clear();
|
|
2059
|
+
this.#pendingCloseIdempotencyKeys.clear();
|
|
2060
|
+
if (this.#closing.size === 0) this.#closing.clear();
|
|
1497
2061
|
this.#tearingDown.clear();
|
|
1498
2062
|
if (this.#broker) {
|
|
1499
2063
|
const broker = this.#broker;
|