@vellumai/assistant 0.8.8 → 0.8.9-staging.2
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/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +235 -6
- package/package.json +2 -2
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +1 -1
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +5 -4
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -5
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +142 -218
- package/src/__tests__/conversation-agent-loop.test.ts +297 -586
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +14 -14
- package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
- package/src/__tests__/conversation-queue.test.ts +9 -9
- package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +6 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
- package/src/__tests__/conversation-speed-override.test.ts +10 -9
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
- package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -1
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +131 -26
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +12 -23
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +1 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +225 -9
- package/src/acp/resolve-agent.ts +122 -17
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +414 -248
- package/src/api/responses/conversation-message.ts +14 -1
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
- package/src/cli/lib/install-from-github.ts +464 -55
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +409 -0
- package/src/cli/lib/plugin-marketplace.ts +197 -0
- package/src/cli/lib/search-plugins.ts +195 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/bundled-skills/acp/SKILL.md +85 -33
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +15 -6
- package/src/config/schemas/call-site-catalog.ts +0 -14
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +0 -2
- package/src/context/compactor.ts +22 -4
- package/src/context/strip-injections.ts +8 -2
- package/src/context/window-manager.ts +27 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
- package/src/daemon/conversation-agent-loop.ts +175 -1055
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-process.ts +8 -136
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +293 -392
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +24 -8
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +404 -57
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +14 -19
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/server.ts +2 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +2 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
- package/src/memory/v2/consolidation-job.ts +13 -4
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +6 -5
- package/src/plugin-api/types.ts +75 -0
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +135 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +9 -6
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +8 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +10 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +7 -7
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +315 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +28 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +16 -6
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +8 -2
- package/src/tools/acp/spawn.test.ts +176 -195
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +25 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/plugins/external-api.ts +0 -104
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/provider-blocks.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
package/src/tools/acp/spawn.ts
CHANGED
|
@@ -1,128 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { basename } from "node:path";
|
|
2
2
|
|
|
3
|
+
import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
|
|
3
4
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
4
5
|
import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { getLogger } from "../../util/logger.js";
|
|
6
|
+
import { formatResolveFailure } from "../../acp/resolve-agent.js";
|
|
7
|
+
import { claudeResumeHint } from "../../acp/resume-hint.js";
|
|
8
8
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
9
|
-
|
|
10
|
-
const log = getLogger("acp:spawn");
|
|
11
|
-
|
|
12
|
-
/** Per-call timeout for `npm` probes. Best-effort: timeouts are non-fatal. */
|
|
13
|
-
const NPM_PROBE_TIMEOUT_MS = 5_000;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Cache of resolved version-check outcomes — including `null` for "skipped" —
|
|
17
|
-
* keyed by command. Lives for the process lifetime so retries don't reprobe.
|
|
18
|
-
*/
|
|
19
|
-
const adapterVersionCache = new Map<string, AdapterVersionInfo | null>();
|
|
20
|
-
|
|
21
|
-
interface AdapterVersionInfo {
|
|
22
|
-
outdated: true;
|
|
23
|
-
installed: string;
|
|
24
|
-
latest: string;
|
|
25
|
-
packageName: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Run `execFile` with an AbortController-driven timeout. Returns the stdout
|
|
30
|
-
* on success; throws on error or timeout. Caller treats any throw as a
|
|
31
|
-
* best-effort skip.
|
|
32
|
-
*/
|
|
33
|
-
function execFileWithTimeout(
|
|
34
|
-
command: string,
|
|
35
|
-
args: string[],
|
|
36
|
-
timeoutMs: number,
|
|
37
|
-
): Promise<string> {
|
|
38
|
-
return new Promise((resolve, reject) => {
|
|
39
|
-
const controller = new AbortController();
|
|
40
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
|
-
execFile(
|
|
42
|
-
command,
|
|
43
|
-
args,
|
|
44
|
-
{ signal: controller.signal, encoding: "utf8" },
|
|
45
|
-
(err, stdout) => {
|
|
46
|
-
clearTimeout(timer);
|
|
47
|
-
if (err) {
|
|
48
|
-
reject(err);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
resolve(stdout);
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Checks if the globally-installed ACP adapter for `command` is outdated.
|
|
59
|
-
* Best-effort: any error or timeout returns `null` (skipped). Unknown
|
|
60
|
-
* commands also return `null`. Results are cached per-command for the
|
|
61
|
-
* process lifetime.
|
|
62
|
-
*
|
|
63
|
-
* Note: `npm ls -g` doesn't see Homebrew/tarball installs, so a "not found"
|
|
64
|
-
* here doesn't mean the binary is missing — it just means we can't compare
|
|
65
|
-
* versions. The caller must NEVER block the spawn on this result.
|
|
66
|
-
*/
|
|
67
|
-
async function checkAdapterVersion(
|
|
68
|
-
command: string,
|
|
69
|
-
): Promise<AdapterVersionInfo | null> {
|
|
70
|
-
if (adapterVersionCache.has(command)) {
|
|
71
|
-
return adapterVersionCache.get(command) ?? null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const packageName = DEFAULT_AGENT_NPM_PACKAGES[command];
|
|
75
|
-
if (!packageName) {
|
|
76
|
-
adapterVersionCache.set(command, null);
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const [installedRaw, latestRaw] = await Promise.all([
|
|
82
|
-
execFileWithTimeout(
|
|
83
|
-
"npm",
|
|
84
|
-
["ls", "-g", "--json", packageName],
|
|
85
|
-
NPM_PROBE_TIMEOUT_MS,
|
|
86
|
-
),
|
|
87
|
-
execFileWithTimeout(
|
|
88
|
-
"npm",
|
|
89
|
-
["view", packageName, "version"],
|
|
90
|
-
NPM_PROBE_TIMEOUT_MS,
|
|
91
|
-
),
|
|
92
|
-
]);
|
|
93
|
-
|
|
94
|
-
const installed =
|
|
95
|
-
JSON.parse(installedRaw)?.dependencies?.[packageName]?.version;
|
|
96
|
-
const latest = latestRaw.trim();
|
|
97
|
-
|
|
98
|
-
if (!installed || !latest || installed === latest) {
|
|
99
|
-
adapterVersionCache.set(command, null);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
log.info({ installed, latest, packageName }, "ACP adapter is outdated");
|
|
104
|
-
const info: AdapterVersionInfo = {
|
|
105
|
-
outdated: true,
|
|
106
|
-
installed,
|
|
107
|
-
latest,
|
|
108
|
-
packageName,
|
|
109
|
-
};
|
|
110
|
-
adapterVersionCache.set(command, info);
|
|
111
|
-
return info;
|
|
112
|
-
} catch (err) {
|
|
113
|
-
log.warn(
|
|
114
|
-
{ err, packageName },
|
|
115
|
-
"Failed to check ACP adapter version (best-effort, skipping)",
|
|
116
|
-
);
|
|
117
|
-
adapterVersionCache.set(command, null);
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** @internal — exposed for tests only. */
|
|
123
|
-
export function _resetAdapterVersionCacheForTests(): void {
|
|
124
|
-
adapterVersionCache.clear();
|
|
125
|
-
}
|
|
9
|
+
import { getSendToClient } from "./context.js";
|
|
126
10
|
|
|
127
11
|
export async function executeAcpSpawn(
|
|
128
12
|
input: Record<string, unknown>,
|
|
@@ -135,34 +19,11 @@ export async function executeAcpSpawn(
|
|
|
135
19
|
return { content: '"task" is required.', isError: true };
|
|
136
20
|
}
|
|
137
21
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
case "unknown_agent":
|
|
144
|
-
return {
|
|
145
|
-
content: `Unknown agent "${agent}". Available: ${resolved.available.join(
|
|
146
|
-
", ",
|
|
147
|
-
)}.`,
|
|
148
|
-
isError: true,
|
|
149
|
-
};
|
|
150
|
-
case "binary_not_found":
|
|
151
|
-
return {
|
|
152
|
-
content: `${resolved.command} is not on PATH. ${resolved.hint}`,
|
|
153
|
-
isError: true,
|
|
154
|
-
};
|
|
155
|
-
default: {
|
|
156
|
-
const _exhaustive: never = resolved;
|
|
157
|
-
throw new Error(
|
|
158
|
-
`Unexpected acp resolver reason: ${(_exhaustive as { reason: string }).reason}`,
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const sendToClient = context.sendToClient as
|
|
164
|
-
| ((msg: { type: string; [key: string]: unknown }) => void)
|
|
165
|
-
| undefined;
|
|
22
|
+
// Pure precondition: check for a connected client BEFORE any side effects
|
|
23
|
+
// (auto-install mutates the host via a `bun` global install and can block
|
|
24
|
+
// for up to the install timeout). Without a client the spawn cannot
|
|
25
|
+
// succeed anyway.
|
|
26
|
+
const sendToClient = getSendToClient(context);
|
|
166
27
|
if (!sendToClient) {
|
|
167
28
|
return {
|
|
168
29
|
content: "No client connected - cannot spawn ACP agent.",
|
|
@@ -170,6 +31,17 @@ export async function executeAcpSpawn(
|
|
|
170
31
|
};
|
|
171
32
|
}
|
|
172
33
|
|
|
34
|
+
// Resolve the agent, silently auto-installing a missing allowlisted
|
|
35
|
+
// adapter binary (see acp/auto-install.ts). Shared with the HTTP route.
|
|
36
|
+
const { resolved, autoInstalledPackage, failureMessage } =
|
|
37
|
+
await resolveAgentWithAutoInstall(agent);
|
|
38
|
+
if (failureMessage) {
|
|
39
|
+
return { content: failureMessage, isError: true };
|
|
40
|
+
}
|
|
41
|
+
if (!resolved.ok) {
|
|
42
|
+
return { content: formatResolveFailure(agent, resolved), isError: true };
|
|
43
|
+
}
|
|
44
|
+
|
|
173
45
|
// Inject required env vars and preflight via the shared helper. Mirrors
|
|
174
46
|
// the HTTP route at `runtime/routes/acp-routes.ts:spawnSession` — both
|
|
175
47
|
// call sites MUST go through `prepareAgentEnv` before `manager.spawn`,
|
|
@@ -184,10 +56,6 @@ export async function executeAcpSpawn(
|
|
|
184
56
|
return { content: msg, isError: true };
|
|
185
57
|
}
|
|
186
58
|
|
|
187
|
-
// Best-effort version check — never blocks the spawn. If outdated, we
|
|
188
|
-
// append a non-blocking warning to the success payload.
|
|
189
|
-
const versionInfo = await checkAdapterVersion(agentConfig.command);
|
|
190
|
-
|
|
191
59
|
try {
|
|
192
60
|
const manager = getAcpSessionManager();
|
|
193
61
|
const cwd = (input.cwd as string) || context.workingDir;
|
|
@@ -197,17 +65,21 @@ export async function executeAcpSpawn(
|
|
|
197
65
|
task,
|
|
198
66
|
cwd,
|
|
199
67
|
context.conversationId,
|
|
200
|
-
sendToClient
|
|
68
|
+
sendToClient,
|
|
201
69
|
);
|
|
202
70
|
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
71
|
+
// Claude Code-only resume hint; empty for other adapters. Keyed off the
|
|
72
|
+
// resolved command basename (always the real adapter binary). See
|
|
73
|
+
// acp/resume-hint.ts for the gating rationale.
|
|
74
|
+
const hint = claudeResumeHint(
|
|
75
|
+
basename(agentConfig.command),
|
|
76
|
+
cwd,
|
|
77
|
+
protocolSessionId,
|
|
78
|
+
);
|
|
79
|
+
const resumeHint = hint ? ` ${hint}` : "";
|
|
80
|
+
const installNote = autoInstalledPackage
|
|
81
|
+
? ` Installed ${autoInstalledPackage} automatically.`
|
|
82
|
+
: "";
|
|
211
83
|
const payload = JSON.stringify({
|
|
212
84
|
acpSessionId,
|
|
213
85
|
protocolSessionId,
|
|
@@ -216,18 +88,11 @@ export async function executeAcpSpawn(
|
|
|
216
88
|
status: "running",
|
|
217
89
|
message:
|
|
218
90
|
`ACP agent "${agent}" spawned (session: ${protocolSessionId}). ` +
|
|
219
|
-
`Results stream back via SSE. You will be notified when it completes
|
|
91
|
+
`Results stream back via SSE. You will be notified when it completes.` +
|
|
92
|
+
`${installNote}${resumeHint}`,
|
|
220
93
|
});
|
|
221
94
|
|
|
222
|
-
|
|
223
|
-
if (versionInfo) {
|
|
224
|
-
content +=
|
|
225
|
-
`\n\nNote: ${versionInfo.packageName} is outdated ` +
|
|
226
|
-
`(installed: ${versionInfo.installed}, latest: ${versionInfo.latest}). ` +
|
|
227
|
-
`To update, run: npm install -g ${versionInfo.packageName}@${versionInfo.latest}`;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return { content, isError: false };
|
|
95
|
+
return { content: payload, isError: false };
|
|
231
96
|
} catch (err) {
|
|
232
97
|
const msg =
|
|
233
98
|
err instanceof Error
|
|
@@ -7,6 +7,7 @@ interface SteerCall {
|
|
|
7
7
|
instruction: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// Direct steer (no connected client -> no resume fallback).
|
|
10
11
|
const steerCalls: SteerCall[] = [];
|
|
11
12
|
const defaultSteer = (acpSessionId: string, instruction: string) => {
|
|
12
13
|
steerCalls.push({ acpSessionId, instruction });
|
|
@@ -15,6 +16,13 @@ const defaultSteer = (acpSessionId: string, instruction: string) => {
|
|
|
15
16
|
let steerImpl: (acpSessionId: string, instruction: string) => Promise<void> =
|
|
16
17
|
defaultSteer;
|
|
17
18
|
|
|
19
|
+
// steerOrResume (connected client path).
|
|
20
|
+
const steerOrResumeCalls: Array<SteerCall & { send: unknown }> = [];
|
|
21
|
+
let steerOrResumeImpl: (
|
|
22
|
+
acpSessionId: string,
|
|
23
|
+
instruction: string,
|
|
24
|
+
) => Promise<{ resumed: boolean }> = async () => ({ resumed: false });
|
|
25
|
+
|
|
18
26
|
// Spread the real module's exports so transitive importers that pull other
|
|
19
27
|
// names from `../../acp/index.js` still resolve at parse time. Bun's `mock.module` is
|
|
20
28
|
// process-global and returns *exactly* the keys the factory returns.
|
|
@@ -24,35 +32,50 @@ mock.module("../../acp/index.js", () => ({
|
|
|
24
32
|
getAcpSessionManager: () => ({
|
|
25
33
|
steer: (acpSessionId: string, instruction: string) =>
|
|
26
34
|
steerImpl(acpSessionId, instruction),
|
|
35
|
+
steerOrResume: (
|
|
36
|
+
acpSessionId: string,
|
|
37
|
+
instruction: string,
|
|
38
|
+
send: unknown,
|
|
39
|
+
) => {
|
|
40
|
+
steerOrResumeCalls.push({ acpSessionId, instruction, send });
|
|
41
|
+
return steerOrResumeImpl(acpSessionId, instruction);
|
|
42
|
+
},
|
|
27
43
|
}),
|
|
28
44
|
}));
|
|
29
45
|
|
|
30
46
|
const { executeAcpSteer } = await import("./steer.js");
|
|
31
47
|
|
|
32
|
-
function makeContext(): ToolContext {
|
|
48
|
+
function makeContext(opts?: { withClient?: boolean }): ToolContext {
|
|
33
49
|
return {
|
|
34
50
|
workingDir: "/tmp",
|
|
35
51
|
conversationId: "conv-test",
|
|
36
52
|
trustClass: "guardian",
|
|
37
|
-
|
|
53
|
+
...(opts?.withClient ? { sendToClient: () => {} } : {}),
|
|
54
|
+
} as ToolContext;
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
beforeEach(() => {
|
|
41
58
|
steerCalls.length = 0;
|
|
59
|
+
steerOrResumeCalls.length = 0;
|
|
42
60
|
steerImpl = defaultSteer;
|
|
61
|
+
steerOrResumeImpl = async () => ({ resumed: false });
|
|
43
62
|
});
|
|
44
63
|
|
|
45
64
|
describe("executeAcpSteer", () => {
|
|
46
|
-
test("happy path: returns steered status and forwards args to
|
|
65
|
+
test("happy path: returns steered status and forwards args to steerOrResume", async () => {
|
|
47
66
|
const result = await executeAcpSteer(
|
|
48
67
|
{ acp_session_id: "acp-123", instruction: "stop, do X instead" },
|
|
49
|
-
makeContext(),
|
|
68
|
+
makeContext({ withClient: true }),
|
|
50
69
|
);
|
|
51
70
|
|
|
52
71
|
expect(result.isError).toBe(false);
|
|
53
|
-
expect(
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
expect(steerOrResumeCalls).toHaveLength(1);
|
|
73
|
+
expect(steerOrResumeCalls[0]).toMatchObject({
|
|
74
|
+
acpSessionId: "acp-123",
|
|
75
|
+
instruction: "stop, do X instead",
|
|
76
|
+
});
|
|
77
|
+
expect(typeof steerOrResumeCalls[0]!.send).toBe("function");
|
|
78
|
+
expect(steerCalls).toEqual([]);
|
|
56
79
|
|
|
57
80
|
const parsed = JSON.parse(result.content as string);
|
|
58
81
|
expect(parsed).toEqual({
|
|
@@ -71,6 +94,7 @@ describe("executeAcpSteer", () => {
|
|
|
71
94
|
expect(result.isError).toBe(true);
|
|
72
95
|
expect(result.content).toContain('"instruction" is required');
|
|
73
96
|
expect(steerCalls).toEqual([]);
|
|
97
|
+
expect(steerOrResumeCalls).toEqual([]);
|
|
74
98
|
});
|
|
75
99
|
|
|
76
100
|
test("missing acp_session_id returns isError", async () => {
|
|
@@ -82,9 +106,23 @@ describe("executeAcpSteer", () => {
|
|
|
82
106
|
expect(result.isError).toBe(true);
|
|
83
107
|
expect(result.content).toContain('"acp_session_id" is required');
|
|
84
108
|
expect(steerCalls).toEqual([]);
|
|
109
|
+
expect(steerOrResumeCalls).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("no connected client: steers directly and never resumes", async () => {
|
|
113
|
+
const result = await executeAcpSteer(
|
|
114
|
+
{ acp_session_id: "acp-direct", instruction: "redirect" },
|
|
115
|
+
makeContext(),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(result.isError).toBe(false);
|
|
119
|
+
expect(steerCalls).toEqual([
|
|
120
|
+
{ acpSessionId: "acp-direct", instruction: "redirect" },
|
|
121
|
+
]);
|
|
122
|
+
expect(steerOrResumeCalls).toEqual([]);
|
|
85
123
|
});
|
|
86
124
|
|
|
87
|
-
test("
|
|
125
|
+
test("'session not found' without a connected client surfaces the error", async () => {
|
|
88
126
|
steerImpl = () =>
|
|
89
127
|
Promise.reject(new Error('ACP session "acp-x" not found'));
|
|
90
128
|
|
|
@@ -96,5 +134,64 @@ describe("executeAcpSteer", () => {
|
|
|
96
134
|
expect(result.isError).toBe(true);
|
|
97
135
|
expect(result.content).toContain('Could not steer ACP session "acp-x"');
|
|
98
136
|
expect(result.content).toContain("not found");
|
|
137
|
+
expect(steerOrResumeCalls).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("resumed session reports the resumed flag and message", async () => {
|
|
141
|
+
steerOrResumeImpl = async () => ({ resumed: true });
|
|
142
|
+
|
|
143
|
+
const result = await executeAcpSteer(
|
|
144
|
+
{ acp_session_id: "acp-gone", instruction: "keep going" },
|
|
145
|
+
makeContext({ withClient: true }),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result.isError).toBe(false);
|
|
149
|
+
expect(steerOrResumeCalls).toHaveLength(1);
|
|
150
|
+
|
|
151
|
+
const parsed = JSON.parse(result.content as string);
|
|
152
|
+
expect(parsed).toEqual({
|
|
153
|
+
acpSessionId: "acp-gone",
|
|
154
|
+
status: "steered",
|
|
155
|
+
resumed: true,
|
|
156
|
+
message:
|
|
157
|
+
"Session was resumed from history; new instruction is now running.",
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("resume failure returns its actionable error message", async () => {
|
|
162
|
+
steerOrResumeImpl = () =>
|
|
163
|
+
Promise.reject(
|
|
164
|
+
new Error(
|
|
165
|
+
'ACP session "acp-legacy" was recorded before resume support (no working directory persisted) and cannot be resumed. Spawn a new session instead.',
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const result = await executeAcpSteer(
|
|
170
|
+
{ acp_session_id: "acp-legacy", instruction: "more work" },
|
|
171
|
+
makeContext({ withClient: true }),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
expect(result.isError).toBe(true);
|
|
175
|
+
expect(result.content).toContain(
|
|
176
|
+
'Could not steer ACP session "acp-legacy"',
|
|
177
|
+
);
|
|
178
|
+
expect(result.content).toContain("recorded before resume support");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("plain steer errors surface unchanged", async () => {
|
|
182
|
+
steerOrResumeImpl = () =>
|
|
183
|
+
Promise.reject(
|
|
184
|
+
new Error(
|
|
185
|
+
'ACP session "acp-init" is not running (status: initializing)',
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const result = await executeAcpSteer(
|
|
190
|
+
{ acp_session_id: "acp-init", instruction: "redirect" },
|
|
191
|
+
makeContext({ withClient: true }),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(result.isError).toBe(true);
|
|
195
|
+
expect(result.content).toContain("is not running");
|
|
99
196
|
});
|
|
100
197
|
});
|
package/src/tools/acp/steer.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
2
2
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
3
|
+
import { getSendToClient } from "./context.js";
|
|
3
4
|
|
|
4
5
|
export async function executeAcpSteer(
|
|
5
6
|
input: Record<string, unknown>,
|
|
6
|
-
|
|
7
|
+
context: ToolContext,
|
|
7
8
|
): Promise<ToolExecutionResult> {
|
|
8
9
|
const acpSessionId = input.acp_session_id as string;
|
|
9
10
|
if (!acpSessionId) {
|
|
@@ -15,24 +16,54 @@ export async function executeAcpSteer(
|
|
|
15
16
|
return { content: '"instruction" is required.', isError: true };
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await manager.steer(acpSessionId, instruction);
|
|
19
|
+
const manager = getAcpSessionManager();
|
|
20
|
+
const sendToClient = getSendToClient(context);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
try {
|
|
23
|
+
if (!sendToClient) {
|
|
24
|
+
// Without a connected client there is no one to receive a resumed
|
|
25
|
+
// session's events, so skip the transparent resume fallback and
|
|
26
|
+
// steer the in-memory session only.
|
|
27
|
+
await manager.steer(acpSessionId, instruction);
|
|
28
|
+
return steeredResult(acpSessionId, { resumed: false });
|
|
29
|
+
}
|
|
30
|
+
// Sessions no longer in memory (completed, or lost to a daemon
|
|
31
|
+
// restart) are transparently resumed from persisted history and the
|
|
32
|
+
// instruction fired in the same call. Failure messages carry the
|
|
33
|
+
// actionable hint (e.g. "recorded before resume support", agent
|
|
34
|
+
// capability missing).
|
|
35
|
+
const { resumed } = await manager.steerOrResume(
|
|
36
|
+
acpSessionId,
|
|
37
|
+
instruction,
|
|
38
|
+
sendToClient,
|
|
39
|
+
);
|
|
40
|
+
return steeredResult(acpSessionId, { resumed });
|
|
31
41
|
} catch (err) {
|
|
32
42
|
const msg = err instanceof Error ? err.message : String(err);
|
|
33
|
-
return
|
|
34
|
-
content: `Could not steer ACP session "${acpSessionId}": ${msg}`,
|
|
35
|
-
isError: true,
|
|
36
|
-
};
|
|
43
|
+
return steerError(acpSessionId, msg);
|
|
37
44
|
}
|
|
38
45
|
}
|
|
46
|
+
|
|
47
|
+
function steeredResult(
|
|
48
|
+
acpSessionId: string,
|
|
49
|
+
opts: { resumed: boolean },
|
|
50
|
+
): ToolExecutionResult {
|
|
51
|
+
return {
|
|
52
|
+
content: JSON.stringify({
|
|
53
|
+
acpSessionId,
|
|
54
|
+
status: "steered",
|
|
55
|
+
...(opts.resumed ? { resumed: true } : {}),
|
|
56
|
+
message: opts.resumed
|
|
57
|
+
? "Session was resumed from history; new instruction is now running."
|
|
58
|
+
: "Interrupted in-flight prompt; new instruction is now running.",
|
|
59
|
+
}),
|
|
60
|
+
isError: false,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function steerError(acpSessionId: string, msg: string): ToolExecutionResult {
|
|
65
|
+
return {
|
|
66
|
+
content: `Could not steer ACP session "${acpSessionId}": ${msg}`,
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|