@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
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Search
|
|
2
|
+
* Search the installable plugin catalog in the canonical GitHub source.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* git ref
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The catalog is the union of two sources, both fetched from the repo at the
|
|
5
|
+
* configured git ref:
|
|
6
|
+
* 1. First-party plugins — directories under
|
|
7
|
+
* `vellum-ai/vellum-assistant/experimental/plugins/`.
|
|
8
|
+
* 2. Whitelisted external ecosystem plugins — entries in the curated
|
|
9
|
+
* `experimental/plugins/marketplace.json` manifest (see
|
|
10
|
+
* {@link ./plugin-marketplace}).
|
|
11
|
+
*
|
|
12
|
+
* Entries are filtered by case-insensitive ECMAScript regex against the
|
|
13
|
+
* plugin name. A plain query like `"memory"` matches anywhere in the name;
|
|
14
|
+
* anchors like `"^simple"` work without escaping.
|
|
8
15
|
*
|
|
9
16
|
* Designed for direct programmatic use. The CLI command
|
|
10
17
|
* `assistant plugins search <query>` is a thin wrapper that supplies
|
|
@@ -15,6 +22,10 @@
|
|
|
15
22
|
|
|
16
23
|
import type { FetchLike } from "./install-from-github.js";
|
|
17
24
|
import { DEFAULT_PLUGIN_REF } from "./install-from-github.js";
|
|
25
|
+
import {
|
|
26
|
+
fetchMarketplaceEntries,
|
|
27
|
+
type MarketplaceEntry,
|
|
28
|
+
} from "./plugin-marketplace.js";
|
|
18
29
|
|
|
19
30
|
// Re-export the dep-injection type so callers can grab everything they need
|
|
20
31
|
// from one module rather than reaching into `install-from-github.js`.
|
|
@@ -50,12 +61,33 @@ export interface SearchPluginsDeps {
|
|
|
50
61
|
readonly fetch: FetchLike;
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
/**
|
|
64
|
+
/** Where a catalog match comes from. */
|
|
65
|
+
export type PluginMatchSource =
|
|
66
|
+
| { readonly kind: "first-party" }
|
|
67
|
+
| {
|
|
68
|
+
readonly kind: "github";
|
|
69
|
+
/** `owner/repo` of the external plugin repository. */
|
|
70
|
+
readonly repo: string;
|
|
71
|
+
/** Directory within the repo, when the plugin is not at the root. */
|
|
72
|
+
readonly path?: string;
|
|
73
|
+
/** Pinned git ref the plugin is fetched from. */
|
|
74
|
+
readonly ref: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** One matching catalog entry. */
|
|
54
78
|
export interface PluginSearchMatch {
|
|
55
|
-
/**
|
|
79
|
+
/** Install name — `assistant plugins install <name>` resolves to it. */
|
|
56
80
|
readonly name: string;
|
|
57
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Human-readable origin of the entry: the repo-relative path for
|
|
83
|
+
* first-party plugins (e.g. `experimental/plugins/<name>`) or a
|
|
84
|
+
* `github:owner/repo@ref` locator for external ones.
|
|
85
|
+
*/
|
|
58
86
|
readonly path: string;
|
|
87
|
+
/** Short description, when known (external entries only today). */
|
|
88
|
+
readonly description?: string;
|
|
89
|
+
/** Discriminated origin, so callers can render/install accordingly. */
|
|
90
|
+
readonly source: PluginMatchSource;
|
|
59
91
|
}
|
|
60
92
|
|
|
61
93
|
/** Search result envelope. */
|
|
@@ -75,12 +107,28 @@ export class InvalidSearchPatternError extends Error {
|
|
|
75
107
|
}
|
|
76
108
|
|
|
77
109
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
110
|
+
* The catalog source (GitHub) was reachable but refused or could not serve
|
|
111
|
+
* the request right now — rate limiting (HTTP 403 with the rate-limit budget
|
|
112
|
+
* exhausted, or 429) or an upstream 5xx. Distinct from a hard 404 on the
|
|
113
|
+
* plugins prefix (a real "source gone" misconfiguration): a transient
|
|
114
|
+
* upstream failure should surface as a retryable "temporarily unavailable"
|
|
115
|
+
* rather than a generic internal error, and is a candidate for serving a
|
|
116
|
+
* stale cached catalog.
|
|
117
|
+
*/
|
|
118
|
+
export class PluginCatalogUnavailableError extends Error {
|
|
119
|
+
/** Upstream HTTP status that triggered the failure. */
|
|
120
|
+
readonly status: number;
|
|
121
|
+
constructor(message: string, status: number) {
|
|
122
|
+
super(message);
|
|
123
|
+
this.name = "PluginCatalogUnavailableError";
|
|
124
|
+
this.status = status;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Build the catalog at {@link opts.ref} and return the entries whose name
|
|
130
|
+
* matches {@link opts.query} (case-insensitive ECMAScript regex; an empty
|
|
131
|
+
* query matches everything).
|
|
84
132
|
*/
|
|
85
133
|
export async function searchPlugins(
|
|
86
134
|
opts: SearchPluginsOptions,
|
|
@@ -92,17 +140,119 @@ export async function searchPlugins(
|
|
|
92
140
|
// the network — keeps "user typo" cheap to recover from.
|
|
93
141
|
const matcher = buildMatcher(opts.query);
|
|
94
142
|
|
|
95
|
-
const
|
|
143
|
+
const { matches: catalog } = await loadPluginCatalog({ ref }, deps);
|
|
144
|
+
const matches = catalog.filter((m) => matcher(m.name));
|
|
145
|
+
|
|
146
|
+
return { query: opts.query, ref, matches };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Validate that {@link query} compiles as a case-insensitive ECMAScript regex,
|
|
151
|
+
* throwing {@link InvalidSearchPatternError} if not. Lets a caching caller
|
|
152
|
+
* (the daemon) reject a malformed query before loading the catalog, so a typo
|
|
153
|
+
* is a cheap deterministic 400 rather than a wasted GitHub request.
|
|
154
|
+
*/
|
|
155
|
+
export function assertValidSearchPattern(query: string): void {
|
|
156
|
+
buildMatcher(query);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Filter a pre-loaded {@link PluginCatalog} by {@link query}, compiling it as
|
|
161
|
+
* a case-insensitive ECMAScript regex (an empty query matches everything).
|
|
162
|
+
* Lets a caching caller (the daemon) reuse one catalog load across many
|
|
163
|
+
* searches. Throws {@link InvalidSearchPatternError} on a malformed pattern.
|
|
164
|
+
*/
|
|
165
|
+
export function filterPluginCatalog(
|
|
166
|
+
catalog: PluginCatalog,
|
|
167
|
+
query: string,
|
|
168
|
+
): PluginSearchMatch[] {
|
|
169
|
+
const matcher = buildMatcher(query);
|
|
170
|
+
return catalog.matches.filter((m) => matcher(m.name));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The full, unfiltered catalog at a given ref. */
|
|
174
|
+
export interface PluginCatalog {
|
|
175
|
+
readonly ref: string;
|
|
176
|
+
/** Every catalog entry, deduped and sorted alphabetically by name. */
|
|
177
|
+
readonly matches: readonly PluginSearchMatch[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Build the full catalog at {@link opts.ref}: every first-party plugin
|
|
182
|
+
* directory under `experimental/plugins/` merged with every whitelisted
|
|
183
|
+
* external entry in the marketplace manifest.
|
|
184
|
+
*
|
|
185
|
+
* The result is **query-independent** — `searchPlugins` applies the regex
|
|
186
|
+
* filter in memory afterwards. That separation is what lets a long-lived
|
|
187
|
+
* caller (the daemon) cache one catalog load and serve any number of
|
|
188
|
+
* searches from it without re-hitting GitHub (see {@link ./plugin-catalog-cache}).
|
|
189
|
+
*/
|
|
190
|
+
export async function loadPluginCatalog(
|
|
191
|
+
opts: { readonly ref?: string },
|
|
192
|
+
deps: SearchPluginsDeps,
|
|
193
|
+
): Promise<PluginCatalog> {
|
|
194
|
+
const ref = opts.ref ?? DEFAULT_PLUGIN_REF;
|
|
195
|
+
|
|
196
|
+
const [entries, marketplace] = await Promise.all([
|
|
197
|
+
listDir(PLUGIN_SOURCE_PATH_PREFIX, ref, deps.fetch),
|
|
198
|
+
fetchMarketplaceSafe(deps.fetch, ref),
|
|
199
|
+
]);
|
|
96
200
|
|
|
97
201
|
const matches: PluginSearchMatch[] = [];
|
|
202
|
+
const seen = new Set<string>();
|
|
98
203
|
for (const entry of entries) {
|
|
99
204
|
if (entry.type !== "dir") continue;
|
|
100
|
-
|
|
101
|
-
|
|
205
|
+
matches.push({
|
|
206
|
+
name: entry.name,
|
|
207
|
+
path: entry.path,
|
|
208
|
+
source: { kind: "first-party" },
|
|
209
|
+
});
|
|
210
|
+
seen.add(entry.name);
|
|
102
211
|
}
|
|
212
|
+
|
|
213
|
+
for (const entry of marketplace) {
|
|
214
|
+
// First-party plugins win a name collision — the curated manifest is
|
|
215
|
+
// additive, never an override of what ships in-repo.
|
|
216
|
+
if (seen.has(entry.name)) continue;
|
|
217
|
+
matches.push(marketplaceMatch(entry));
|
|
218
|
+
seen.add(entry.name);
|
|
219
|
+
}
|
|
220
|
+
|
|
103
221
|
matches.sort((a, b) => a.name.localeCompare(b.name));
|
|
104
222
|
|
|
105
|
-
return {
|
|
223
|
+
return { ref, matches };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Project a marketplace entry onto the catalog match shape, building a
|
|
228
|
+
* `github:owner/repo[/path]@ref` locator for display.
|
|
229
|
+
*/
|
|
230
|
+
function marketplaceMatch(entry: MarketplaceEntry): PluginSearchMatch {
|
|
231
|
+
const { repo, path, ref } = entry.source;
|
|
232
|
+
const locator = `github:${repo}${path ? `/${path}` : ""}@${ref}`;
|
|
233
|
+
return {
|
|
234
|
+
name: entry.name,
|
|
235
|
+
path: locator,
|
|
236
|
+
description: entry.description,
|
|
237
|
+
source: { kind: "github", repo, path, ref },
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Fetch the marketplace manifest, degrading to an empty whitelist on any
|
|
243
|
+
* failure. The manifest is supplementary to the first-party listing, so a
|
|
244
|
+
* missing or malformed manifest must never break the core catalog — mirroring
|
|
245
|
+
* the daemon's "never block over a subsystem failure" philosophy.
|
|
246
|
+
*/
|
|
247
|
+
async function fetchMarketplaceSafe(
|
|
248
|
+
fetchFn: FetchLike,
|
|
249
|
+
ref: string,
|
|
250
|
+
): Promise<readonly MarketplaceEntry[]> {
|
|
251
|
+
try {
|
|
252
|
+
return await fetchMarketplaceEntries({ fetch: fetchFn }, { ref });
|
|
253
|
+
} catch {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
106
256
|
}
|
|
107
257
|
|
|
108
258
|
function buildMatcher(query: string): (name: string) => boolean {
|
|
@@ -127,13 +277,17 @@ async function listDir(
|
|
|
127
277
|
|
|
128
278
|
const res = await githubFetch(url, fetchFn);
|
|
129
279
|
if (!res.ok) {
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
)
|
|
280
|
+
const detail = `GitHub contents listing failed for ${apiPath} @ ${ref}: HTTP ${res.status}`;
|
|
281
|
+
// Rate limiting (403 with the budget exhausted, or 429) and upstream
|
|
282
|
+
// 5xx are transient — surface them as a retryable "temporarily
|
|
283
|
+
// unavailable" so the caller can serve a stale cache and the route can
|
|
284
|
+
// map to 503 instead of a misleading 500. A 404 on the plugins prefix
|
|
285
|
+
// itself means the canonical source path is gone (a real
|
|
286
|
+
// misconfiguration), so it stays a hard error.
|
|
287
|
+
if (isTransientUpstreamStatus(res)) {
|
|
288
|
+
throw new PluginCatalogUnavailableError(detail, res.status);
|
|
289
|
+
}
|
|
290
|
+
throw new Error(detail);
|
|
137
291
|
}
|
|
138
292
|
|
|
139
293
|
const body = (await res.json()) as unknown;
|
|
@@ -150,10 +304,22 @@ async function listDir(
|
|
|
150
304
|
* request. Unauthenticated — the canonical source is a public repo, mirroring
|
|
151
305
|
* `installPlugin` which uses the same envelope.
|
|
152
306
|
*/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)
|
|
307
|
+
/**
|
|
308
|
+
* Whether a non-OK GitHub response should be treated as a transient
|
|
309
|
+
* "temporarily unavailable" failure rather than a hard error. Covers
|
|
310
|
+
* rate limiting (429, or 403 once the rate-limit budget is exhausted) and
|
|
311
|
+
* upstream server errors (5xx). A bare 403 without the rate-limit signal
|
|
312
|
+
* (e.g. a genuine permissions problem) is not treated as transient.
|
|
313
|
+
*/
|
|
314
|
+
function isTransientUpstreamStatus(res: Response): boolean {
|
|
315
|
+
if (res.status === 429 || res.status >= 500) return true;
|
|
316
|
+
if (res.status === 403) {
|
|
317
|
+
return res.headers.get("x-ratelimit-remaining") === "0";
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function githubFetch(url: string, fetchFn: FetchLike): Promise<Response> {
|
|
157
323
|
return fetchFn(url, {
|
|
158
324
|
headers: {
|
|
159
325
|
Accept: "application/vnd.github+json",
|
|
@@ -85,8 +85,8 @@ describe("unified feature flag registry guard", () => {
|
|
|
85
85
|
) {
|
|
86
86
|
violations.push(`${prefix}: missing or non-string 'description'`);
|
|
87
87
|
}
|
|
88
|
-
if (typeof flag.defaultEnabled !== "boolean") {
|
|
89
|
-
violations.push(`${prefix}: missing or
|
|
88
|
+
if (typeof flag.defaultEnabled !== "boolean" && typeof flag.defaultEnabled !== "string") {
|
|
89
|
+
violations.push(`${prefix}: missing or invalid 'defaultEnabled' (expected boolean or string)`);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -10,6 +10,7 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
|
|
|
10
10
|
expect(Object.keys(DEFAULT_ACP_AGENT_PROFILES).sort()).toEqual([
|
|
11
11
|
"claude",
|
|
12
12
|
"codex",
|
|
13
|
+
"gemini",
|
|
13
14
|
]);
|
|
14
15
|
});
|
|
15
16
|
|
|
@@ -29,6 +30,14 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
|
|
|
29
30
|
});
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
test("gemini profile speaks native ACP via gemini --acp (no adapter binary)", () => {
|
|
34
|
+
expect(DEFAULT_ACP_AGENT_PROFILES.gemini).toEqual({
|
|
35
|
+
command: "gemini",
|
|
36
|
+
args: ["--acp"],
|
|
37
|
+
description: "Google Gemini CLI (native ACP via gemini --acp)",
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
32
41
|
test("is deeply frozen so mutation throws in strict mode", () => {
|
|
33
42
|
expect(Object.isFrozen(DEFAULT_ACP_AGENT_PROFILES)).toBe(true);
|
|
34
43
|
for (const profile of Object.values(DEFAULT_ACP_AGENT_PROFILES)) {
|
|
@@ -46,6 +55,7 @@ describe("DEFAULT_AGENT_NPM_PACKAGES", () => {
|
|
|
46
55
|
expect(DEFAULT_AGENT_NPM_PACKAGES).toEqual({
|
|
47
56
|
"claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
|
|
48
57
|
"codex-acp": "@zed-industries/codex-acp",
|
|
58
|
+
gemini: "@google/gemini-cli",
|
|
49
59
|
});
|
|
50
60
|
});
|
|
51
61
|
|
|
@@ -30,6 +30,11 @@ export const DEFAULT_ACP_AGENT_PROFILES: Readonly<
|
|
|
30
30
|
args: FROZEN_EMPTY_ARGS,
|
|
31
31
|
description: "OpenAI Codex CLI (via @zed-industries/codex-acp)",
|
|
32
32
|
}),
|
|
33
|
+
gemini: Object.freeze({
|
|
34
|
+
command: "gemini",
|
|
35
|
+
args: Object.freeze(["--acp"]) as unknown as string[],
|
|
36
|
+
description: "Google Gemini CLI (native ACP via gemini --acp)",
|
|
37
|
+
}),
|
|
33
38
|
});
|
|
34
39
|
|
|
35
40
|
/**
|
|
@@ -44,4 +49,5 @@ export const DEFAULT_AGENT_NPM_PACKAGES: Readonly<Record<string, string>> =
|
|
|
44
49
|
Object.freeze({
|
|
45
50
|
"claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
|
|
46
51
|
"codex-acp": "@zed-industries/codex-acp",
|
|
52
|
+
gemini: "@google/gemini-cli",
|
|
47
53
|
});
|
|
@@ -7,29 +7,30 @@ metadata:
|
|
|
7
7
|
vellum:
|
|
8
8
|
display-name: "ACP"
|
|
9
9
|
activation-hints:
|
|
10
|
-
- "User
|
|
10
|
+
- "User asks to use Claude Code, Codex, or Gemini to do something"
|
|
11
|
+
- "User wants to delegate a coding task to Claude Code, Codex, Gemini, or another ACP agent"
|
|
12
|
+
- "User wants to hand a coding task to another agent and check on it later"
|
|
11
13
|
- "User wants to spawn an external coding agent that runs autonomously and streams results back"
|
|
12
|
-
- "User mentions ACP, claude-agent-acp, codex-acp, or running multiple coding agents in parallel"
|
|
14
|
+
- "User mentions ACP, claude-agent-acp, codex-acp, gemini --acp, or running multiple coding agents in parallel"
|
|
13
15
|
avoid-when:
|
|
14
|
-
- "Task is small enough to do inline with the assistant's own tools
|
|
16
|
+
- "Task is small enough to do inline with the assistant's own tools - no need for an external agent"
|
|
15
17
|
---
|
|
16
18
|
|
|
17
|
-
ACP agent orchestration - spawn external coding agents (Claude Code, Codex,
|
|
19
|
+
ACP agent orchestration - spawn external coding agents (Claude Code, Codex, Gemini) to work on tasks via the Agent Client Protocol. Each agent runs as its own subprocess speaking ACP over stdio and streams results back into the conversation.
|
|
18
20
|
|
|
19
21
|
## Usage
|
|
20
22
|
|
|
21
23
|
Use `acp_spawn` to delegate a coding task to an external agent. The agent runs as a subprocess speaking the ACP protocol over stdio and streams results back.
|
|
22
24
|
|
|
25
|
+
Users can refer to agents by natural names: "claude code", "codex cli", "openai codex", "gemini cli", and "google gemini" all resolve to the canonical `claude`, `codex`, and `gemini` ids (unless the user's config defines an agent literally keyed by that name, which always wins).
|
|
26
|
+
|
|
23
27
|
## First-time setup
|
|
24
28
|
|
|
25
|
-
When the user first tries to use ACP and it's not
|
|
29
|
+
When the user first tries to use ACP and it's not enabled, set it up automatically:
|
|
26
30
|
|
|
27
|
-
1. **
|
|
28
|
-
```bash
|
|
29
|
-
npm i -g @agentclientprotocol/claude-agent-acp
|
|
30
|
-
```
|
|
31
|
+
1. **Enable the `acp` feature flag** (the primary enablement path). Either PATCH it via the gateway feature-flags endpoint or direct the user to toggle "ACP Coding Agents" in the client's feature flags UI. Flag changes are hot-refreshed in the assistant - no restart needed.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
As a supported alternative, edit the workspace config file to add the `acp` section. Default profiles for `claude`, `codex`, and `gemini` ship out-of-box, so the minimal config is just:
|
|
33
34
|
```json
|
|
34
35
|
{
|
|
35
36
|
"acp": {
|
|
@@ -38,57 +39,108 @@ When the user first tries to use ACP and it's not configured, set it up automati
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
```
|
|
42
|
+
If you go the config route, **wait a few seconds** for the config watcher to pick up the change (it hot-reloads automatically - no restart needed).
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
2. Then retry the `acp_spawn` call. Do NOT run `vellum sleep && vellum wake` - that kills the conversation.
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
No manual binary installation is needed first: missing adapter binaries are installed automatically (see below).
|
|
45
47
|
|
|
46
|
-
##
|
|
48
|
+
## Automatic adapter availability
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
When `acp_spawn` finds the agent's binary missing from PATH, the assistant installs it once via a sandboxed bun global install and then runs the real installed binary. The install runs in a fresh empty temporary directory (never the task's project directory), with the Claude/Gemini secrets stripped from the installer environment and the registry pinned to the public npm registry, so a malicious project directory cannot hijack package resolution or capture a token. After this one-time install, the adapter is a normal trusted binary on PATH and every later spawn (and resume) uses it directly.
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
Only the allowlisted out-of-box packages are ever installed this way (`@agentclientprotocol/claude-agent-acp`, `@zed-industries/codex-acp`, `@google/gemini-cli`); user-configured agents with custom commands are never installed automatically.
|
|
53
|
+
|
|
54
|
+
Manual installation is fallback guidance for unusual setups: bun unavailable, restricted global installs, or an auto-install failure (the failure reason is surfaced in the tool result).
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bun add -g @agentclientprotocol/claude-agent-acp # claude
|
|
58
|
+
bun add -g @zed-industries/codex-acp # codex
|
|
59
|
+
bun add -g @google/gemini-cli # gemini
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Claude setup
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
The `claude-agent-acp` adapter requires a Claude OAuth token. Store it once in the credential store and every spawn injects it as `CLAUDE_CODE_OAUTH_TOKEN` automatically:
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
```bash
|
|
67
|
+
assistant credentials set --service acp --field claude_oauth_token <token>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
When the token is missing, do NOT ask the user to paste it into chat. Collect it via the secret-request flow instead: `credential_store` with action `prompt`, service `acp`, field `claude_oauth_token`. That prompts the user through a secure UI so the token never enters the conversation or the workspace config. Users generate the token by running `claude setup-token` on a machine where they are logged in to Claude.
|
|
71
|
+
|
|
72
|
+
## Codex setup
|
|
73
|
+
|
|
74
|
+
The `codex-acp` adapter is installed automatically when missing, but it shells out to the underlying `codex` CLI, which must also be on PATH:
|
|
75
|
+
|
|
76
|
+
1. **Install the Codex CLI** (version 0.111 or higher) via OpenAI's distribution channel of choice. The adapter will fail if `codex` isn't on PATH.
|
|
77
|
+
|
|
78
|
+
2. **Authenticate.** The `codex-acp` adapter inherits whatever auth the underlying `codex` CLI uses. Typical flows:
|
|
59
79
|
- `codex login` (OAuth)
|
|
60
80
|
- `CODEX_API_KEY` environment variable
|
|
61
81
|
- `OPENAI_API_KEY` environment variable
|
|
62
82
|
|
|
63
|
-
|
|
83
|
+
## Gemini setup
|
|
84
|
+
|
|
85
|
+
Gemini CLI speaks ACP natively (`gemini --acp`) - there is no separate adapter binary. The CLI itself is installed from `@google/gemini-cli` when missing.
|
|
86
|
+
|
|
87
|
+
**Authenticate** with an API key through the credential store (the primary path):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
assistant credentials set --service acp --field gemini_api_key <key>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Or collect the key via the secret-request flow: `credential_store` with action `prompt`, service `acp`, field `gemini_api_key`. Either way the key never appears in chat or workspace config, and every spawn injects it as `GEMINI_API_KEY` automatically. The key is optional - a spawn proceeds without it when the vault has no entry.
|
|
94
|
+
|
|
95
|
+
The alternative is browser OAuth: run `gemini` once interactively and complete the sign-in flow. This is impractical on hosted assistants (no browser), so prefer the credential store there.
|
|
96
|
+
|
|
97
|
+
Do NOT put API keys (or any secret) in the workspace config file - secrets never belong in the workspace directory. Use the credential store instead.
|
|
98
|
+
|
|
99
|
+
A workspace `acp.agents.gemini` override is only for non-secret customization (custom binary path, extra args, non-secret env vars). It must spell out the full `command` and `args` - see "Critical: correct agent command" below for the replace-not-merge rule:
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"acp": {
|
|
103
|
+
"agents": {
|
|
104
|
+
"gemini": {
|
|
105
|
+
"command": "gemini",
|
|
106
|
+
"args": ["--acp"],
|
|
107
|
+
"env": { "NO_COLOR": "1" }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
64
113
|
|
|
65
114
|
## Critical: correct agent command
|
|
66
115
|
|
|
67
|
-
- `claude-agent-acp`
|
|
68
|
-
- NEVER use `claude`, `claude -p`, `claude --acp`, the bare `codex` CLI
|
|
69
|
-
- Default profiles for
|
|
70
|
-
- NEVER change an existing ACP config to use a different command. If the config already has `claude-agent-acp
|
|
116
|
+
- Three agents are supported out-of-box: `claude` (via the `claude-agent-acp` adapter), `codex` (via the `codex-acp` adapter), and `gemini` (via `gemini --acp` - Gemini speaks ACP natively, no adapter binary).
|
|
117
|
+
- NEVER use `claude`, `claude -p`, `claude --acp`, or the bare `codex` CLI as the ACP `command`. Claude and Codex only speak the protocol through their dedicated `*-acp` adapters. Gemini is the exception: the `gemini` CLI itself speaks ACP when launched with `--acp`.
|
|
118
|
+
- Default profiles for all three ship out-of-box. Users only need an `agents.<id>` entry in config if they want to override the defaults (e.g. point to a custom binary path or pass extra args/env). An `acp.agents.<id>` entry replaces the bundled default entirely (no field merge), so any override must spell out the full `command` and `args`, not just the field being changed.
|
|
119
|
+
- NEVER change an existing ACP config to use a different command. If the config already has `claude-agent-acp`, `codex-acp`, or `gemini`, leave it alone.
|
|
71
120
|
|
|
72
|
-
## Updating
|
|
121
|
+
## Updating an adapter
|
|
73
122
|
|
|
74
|
-
|
|
123
|
+
Adapters are installed once via a bun global install. To update one to the latest version, ask the user first, then re-install it globally:
|
|
75
124
|
|
|
76
125
|
```bash
|
|
77
|
-
|
|
126
|
+
bun add -g @agentclientprotocol/claude-agent-acp@latest
|
|
127
|
+
# or
|
|
128
|
+
bun add -g @zed-industries/codex-acp@latest
|
|
78
129
|
# or
|
|
79
|
-
|
|
130
|
+
bun add -g @google/gemini-cli@latest
|
|
80
131
|
```
|
|
81
132
|
|
|
82
133
|
Then retry the `acp_spawn` call.
|
|
83
134
|
|
|
84
135
|
## When to use acp_steer vs acp_spawn
|
|
85
136
|
|
|
86
|
-
-
|
|
87
|
-
- **
|
|
137
|
+
- **On a running session, `acp_steer` interrupts the in-flight prompt.** Use it to course-correct ("stop, do X instead"). It cancels whatever the agent is currently working on and replaces it with the new instruction. Queued follow-ups behind a running prompt are not supported - wait for the `acp_session_completed` notification instead.
|
|
138
|
+
- **On a completed (or assistant-restarted) session, `acp_steer` transparently resumes it.** The session is restored from persisted history via ACP session loading when the agent supports it, and the new instruction runs with the agent's full prior context. This is the primary way to do follow-up work on an existing session id - prefer it over spawning a fresh session that would lose context.
|
|
139
|
+
- If resume isn't possible (the session was recorded before resume support and has no working directory, or the agent lacks the capability), the error explains why; fall back to `acp_spawn`. For claude sessions, the completion message also includes a `claude --resume <id>` CLI hint for resuming outside the assistant.
|
|
88
140
|
|
|
89
141
|
## Discoverability
|
|
90
142
|
|
|
91
|
-
Use `acp_list_agents` to see what's set up and what's missing. It returns each available agent profile, whether ACP is enabled, whether the agent's binary is on PATH, and an install hint if not. This is the right tool to call when deciding between `claude` and `
|
|
143
|
+
Use `acp_list_agents` to see what's set up and what's missing. It returns each available agent profile, whether ACP is enabled, whether the agent's binary is on PATH (missing binaries are installed automatically on first spawn), and an install hint if not. This is the right tool to call when deciding between `claude`, `codex`, and `gemini`, or when the user asks "what coding agents do I have?"
|
|
92
144
|
|
|
93
145
|
## Working directory
|
|
94
146
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"tools": [
|
|
4
4
|
{
|
|
5
5
|
"name": "acp_spawn",
|
|
6
|
-
"description": "Spawn an external coding agent (e.g. Claude Code, Codex) via ACP to work on a task. Default profiles ship for `claude` (`claude-agent-acp`)
|
|
6
|
+
"description": "Spawn an external coding agent (e.g. Claude Code, Codex, Gemini) via ACP to work on a task. Default profiles ship for `claude` (`claude-agent-acp`), `codex` (`codex-acp`), and `gemini` (`gemini --acp`); the assistant resolves the agent id to the right binary. The agent runs as a subprocess and streams results back. Use this when you want to delegate a coding task to an external agent that has its own tools, file editing, and terminal access. If a default agent's binary is missing, the assistant installs it once via a sandboxed bun global install and proceeds in the same call; if that fails (e.g. bun unavailable), an actionable install hint is returned - do NOT alter `agents.<id>.command` to swap binaries. If ACP is disabled, follow the setup instructions in SKILL.md.",
|
|
7
7
|
"category": "orchestration",
|
|
8
8
|
"risk": "high",
|
|
9
9
|
"input_schema": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"properties": {
|
|
12
12
|
"agent": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"description": "Which agent to spawn (e.g. 'claude', 'codex'). Defaults to 'claude'."
|
|
14
|
+
"description": "Which agent to spawn (e.g. 'claude', 'codex', 'gemini'). Natural names like 'claude code' or 'gemini cli' also resolve. Defaults to 'claude'."
|
|
15
15
|
},
|
|
16
16
|
"task": {
|
|
17
17
|
"type": "string",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"name": "acp_steer",
|
|
68
|
-
"description": "**
|
|
68
|
+
"description": "Sends `instruction` to an ACP session. On a running session this **interrupts** the in-flight prompt and replaces it - use it to redirect the agent (e.g., 'stop, do X instead'). A completed or assistant-restarted session is transparently resumed from persisted history via ACP session loading when the agent supports it, so follow-up work on an existing session id CAN go through this tool instead of spawning a new session. If resume is not possible (legacy session without a recorded working directory, or the agent lacks the capability), the error explains why; fall back to `acp_spawn`.",
|
|
69
69
|
"category": "orchestration",
|
|
70
70
|
"risk": "high",
|
|
71
71
|
"input_schema": {
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
89
|
"name": "acp_list_agents",
|
|
90
|
-
"description": "Lists ACP coding agents available to spawn. Each entry includes whether ACP is enabled, whether the agent's binary is on PATH, and an install command if not. Use this to decide between 'claude' and '
|
|
90
|
+
"description": "Lists ACP coding agents available to spawn. Each entry includes whether ACP is enabled, whether the agent's binary is on PATH (missing binaries are installed automatically on first spawn), and an install command if not. Use this to decide between 'claude', 'codex', and 'gemini' or to surface setup steps to the user.",
|
|
91
91
|
"category": "orchestration",
|
|
92
92
|
"risk": "low",
|
|
93
93
|
"input_schema": {
|