@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae
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/docs/activation-funnel-telemetry.md +310 -0
- 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 +263 -121
- package/package.json +2 -2
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- 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 +23 -8
- 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 +6 -5
- 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 +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- 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 +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- 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 +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -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 +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- 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__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -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 +31 -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 +5 -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/__tests__/web-fetch.test.ts +45 -0
- 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-config-stub.ts +0 -2
- 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/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- 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 +403 -251
- package/src/api/responses/conversation-message.ts +22 -2
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -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 +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- 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 -3
- package/src/config/feature-flag-registry.json +10 -16
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -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__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -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/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +9 -2
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- 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 +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- 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/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- 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 +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -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/external-api.ts +12 -2
- 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/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +22 -0
- package/src/providers/types.ts +6 -0
- 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 +466 -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__/stt-routes.test.ts +112 -0
- 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 +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- 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/channel-verification-routes.ts +14 -5
- 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 +30 -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/inbound-stages/acl-enforcement.ts +33 -34
- 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 +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- 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/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -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/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- 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/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- 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/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/util/map-limit.ts +0 -27
- /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__/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}/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}/llm-retry.ts +0 -0
|
@@ -4,23 +4,31 @@
|
|
|
4
4
|
* Exposes spawn, steer, cancel, close, sessions, and permission operations
|
|
5
5
|
* over HTTP and IPC.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
|
|
9
|
+
import { and, desc, eq, inArray, notInArray } from "drizzle-orm";
|
|
8
10
|
import { z } from "zod";
|
|
9
11
|
|
|
12
|
+
import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
|
|
10
13
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
11
14
|
import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
|
|
12
|
-
import {
|
|
15
|
+
import { formatResolveFailure } from "../../acp/resolve-agent.js";
|
|
16
|
+
import { AcpResumeError } from "../../acp/session-manager.js";
|
|
13
17
|
import type { AcpSessionState } from "../../acp/types.js";
|
|
18
|
+
import { getConfig } from "../../config/loader.js";
|
|
14
19
|
import { getDb } from "../../memory/db-connection.js";
|
|
15
20
|
import { rawChanges } from "../../memory/raw-query.js";
|
|
16
21
|
import { acpSessionHistory } from "../../memory/schema.js";
|
|
22
|
+
import type { UserDecision } from "../../permissions/types.js";
|
|
17
23
|
import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
|
|
18
24
|
import { getLogger } from "../../util/logger.js";
|
|
19
25
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
26
|
+
import * as pendingInteractions from "../pending-interactions.js";
|
|
20
27
|
import {
|
|
21
28
|
BadRequestError,
|
|
22
29
|
ConflictError,
|
|
23
30
|
FailedDependencyError,
|
|
31
|
+
ForbiddenError,
|
|
24
32
|
NotFoundError,
|
|
25
33
|
} from "./errors.js";
|
|
26
34
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -51,7 +59,109 @@ type SessionEntry = z.infer<typeof sessionEntrySchema>;
|
|
|
51
59
|
// Handlers
|
|
52
60
|
// ---------------------------------------------------------------------------
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Surface a high-risk confirmation for an ACP route that starts a host agent
|
|
64
|
+
* subprocess (`POST /v1/acp/spawn`, and the resume branch of
|
|
65
|
+
* `POST /v1/acp/:id/steer`) and resolve to the guardian's decision.
|
|
66
|
+
*
|
|
67
|
+
* Unlike the `acp_spawn` / `acp_steer` skill tools (which are dispatched
|
|
68
|
+
* through `ToolExecutor`/`PermissionChecker` and so inherit the descriptors'
|
|
69
|
+
* `risk: "high"` approval prompt), these HTTP/IPC routes reach the ACP
|
|
70
|
+
* session manager directly. A spawned ACP agent is a host subprocess with
|
|
71
|
+
* advertised filesystem + terminal access whose in-session permission
|
|
72
|
+
* requests are auto-allowed, so starting one is exactly the high-risk action
|
|
73
|
+
* the descriptors gate. Without this prompt a caller holding only
|
|
74
|
+
* `chat.write` could start one with no human in the loop — directly via
|
|
75
|
+
* spawn (arbitrary `cwd`), or by resuming a terminal session via steer (its
|
|
76
|
+
* persisted `cwd`, enumerable through `GET /v1/acp/sessions`) (ATL-822).
|
|
77
|
+
*
|
|
78
|
+
* The confirmation is registered with `directResolve` so `POST /v1/confirm`
|
|
79
|
+
* resolves it without a live `Conversation` object. That route requires the
|
|
80
|
+
* bound guardian (`approval.write` + `requireGuardian`) — a strictly higher
|
|
81
|
+
* bar than the `chat.write` these routes demand — so the `chat.write` caller
|
|
82
|
+
* cannot self-approve. Timeout and client disconnect both deny, matching the
|
|
83
|
+
* tool path's "no interactive client → do not run" posture.
|
|
84
|
+
*/
|
|
85
|
+
function awaitRouteApproval(args: {
|
|
86
|
+
toolName: string;
|
|
87
|
+
input: Record<string, unknown>;
|
|
88
|
+
conversationId: string;
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
}): Promise<UserDecision> {
|
|
91
|
+
const { toolName, input, conversationId, signal } = args;
|
|
92
|
+
const requestId = randomUUID();
|
|
93
|
+
// Mirror the interactive prompt timeout; fall back to the schema default
|
|
94
|
+
// (300s) if the timeouts block is somehow absent so the gate never wedges.
|
|
95
|
+
const timeoutMs = (getConfig().timeouts?.permissionTimeoutSec ?? 300) * 1000;
|
|
96
|
+
|
|
97
|
+
return new Promise<UserDecision>((resolve) => {
|
|
98
|
+
let settled = false;
|
|
99
|
+
const settle = (
|
|
100
|
+
decision: UserDecision,
|
|
101
|
+
state: "approved" | "rejected" | "cancelled",
|
|
102
|
+
) => {
|
|
103
|
+
if (settled) return;
|
|
104
|
+
settled = true;
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
signal?.removeEventListener("abort", onAbort);
|
|
107
|
+
// Idempotent: the `/v1/confirm` directResolve path already removed the
|
|
108
|
+
// entry before invoking us, so this is a no-op there and only fires
|
|
109
|
+
// `interaction_resolved` for the timeout/abort paths.
|
|
110
|
+
pendingInteractions.resolve(requestId, state);
|
|
111
|
+
resolve(decision);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const timer = setTimeout(() => {
|
|
115
|
+
log.warn(
|
|
116
|
+
{ requestId, toolName, conversationId },
|
|
117
|
+
"ACP route approval timed out — denying",
|
|
118
|
+
);
|
|
119
|
+
settle("deny", "cancelled");
|
|
120
|
+
}, timeoutMs);
|
|
121
|
+
|
|
122
|
+
const onAbort = () => settle("deny", "cancelled");
|
|
123
|
+
if (signal?.aborted) {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
resolve("deny");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
129
|
+
|
|
130
|
+
pendingInteractions.register(requestId, {
|
|
131
|
+
conversationId,
|
|
132
|
+
kind: "confirmation",
|
|
133
|
+
confirmationDetails: {
|
|
134
|
+
toolName,
|
|
135
|
+
input,
|
|
136
|
+
riskLevel: "high",
|
|
137
|
+
executionTarget: "host",
|
|
138
|
+
allowlistOptions: [],
|
|
139
|
+
scopeOptions: [],
|
|
140
|
+
persistentDecisionsAllowed: false,
|
|
141
|
+
},
|
|
142
|
+
directResolve: (decision) =>
|
|
143
|
+
settle(decision, decision === "allow" ? "approved" : "rejected"),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
broadcastMessage(
|
|
147
|
+
{
|
|
148
|
+
type: "confirmation_request",
|
|
149
|
+
requestId,
|
|
150
|
+
toolName,
|
|
151
|
+
input,
|
|
152
|
+
riskLevel: "high",
|
|
153
|
+
executionTarget: "host",
|
|
154
|
+
allowlistOptions: [],
|
|
155
|
+
scopeOptions: [],
|
|
156
|
+
conversationId,
|
|
157
|
+
persistentDecisionsAllowed: false,
|
|
158
|
+
},
|
|
159
|
+
conversationId,
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function spawnSession({ body, abortSignal }: RouteHandlerArgs) {
|
|
55
165
|
const agent = body?.agent as string | undefined;
|
|
56
166
|
const task = body?.task as string | undefined;
|
|
57
167
|
const conversationId = body?.conversationId as string | undefined;
|
|
@@ -61,26 +171,35 @@ async function spawnSession({ body }: RouteHandlerArgs) {
|
|
|
61
171
|
throw new BadRequestError("agent, task, and conversationId are required");
|
|
62
172
|
}
|
|
63
173
|
|
|
64
|
-
|
|
174
|
+
// High-risk approval gate. Block BEFORE any side effects — resolution can
|
|
175
|
+
// trigger a `bun` global install (auto-install.ts) and `manager.spawn`
|
|
176
|
+
// launches the host subprocess — so an unapproved request mutates nothing.
|
|
177
|
+
const decision = await awaitRouteApproval({
|
|
178
|
+
toolName: "acp_spawn",
|
|
179
|
+
input: { agent, task, cwd },
|
|
180
|
+
conversationId,
|
|
181
|
+
signal: abortSignal,
|
|
182
|
+
});
|
|
183
|
+
if (decision !== "allow") {
|
|
184
|
+
throw new ForbiddenError(
|
|
185
|
+
"Spawning an ACP coding agent requires guardian approval, which was " +
|
|
186
|
+
"not granted.",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Resolve the agent, silently auto-installing a missing allowlisted
|
|
191
|
+
// adapter binary (see acp/auto-install.ts). Shared with the skill-tool
|
|
192
|
+
// path in tools/acp/spawn.ts; only the transport mapping differs.
|
|
193
|
+
const { resolved, failureMessage } = await resolveAgentWithAutoInstall(agent);
|
|
194
|
+
if (failureMessage) {
|
|
195
|
+
throw new FailedDependencyError(failureMessage);
|
|
196
|
+
}
|
|
65
197
|
if (!resolved.ok) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case "unknown_agent":
|
|
70
|
-
throw new BadRequestError(
|
|
71
|
-
`Unknown agent "${agent}". Available: ${resolved.available.join(", ")}.`,
|
|
72
|
-
);
|
|
73
|
-
case "binary_not_found":
|
|
74
|
-
throw new FailedDependencyError(
|
|
75
|
-
`${resolved.command} is not on PATH. ${resolved.hint}`,
|
|
76
|
-
);
|
|
77
|
-
default: {
|
|
78
|
-
const _exhaustive: never = resolved;
|
|
79
|
-
throw new Error(
|
|
80
|
-
`Unexpected acp resolver reason: ${(_exhaustive as { reason: string }).reason}`,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
198
|
+
const message = formatResolveFailure(agent, resolved);
|
|
199
|
+
if (resolved.reason === "binary_not_found") {
|
|
200
|
+
throw new FailedDependencyError(message);
|
|
83
201
|
}
|
|
202
|
+
throw new BadRequestError(message);
|
|
84
203
|
}
|
|
85
204
|
|
|
86
205
|
// Inject required env vars and preflight via the shared helper. See
|
|
@@ -116,13 +235,143 @@ async function steerSession({ pathParams, body }: RouteHandlerArgs) {
|
|
|
116
235
|
throw new BadRequestError("instruction is required");
|
|
117
236
|
}
|
|
118
237
|
|
|
238
|
+
// Sessions no longer in memory (completed, or lost to a daemon restart)
|
|
239
|
+
// are transparently resumed from persisted history with the instruction
|
|
240
|
+
// fired in the same call, mirroring the acp_steer skill tool.
|
|
241
|
+
// broadcastMessage plays the sender role spawnSession gives it, so
|
|
242
|
+
// connected clients render the session.
|
|
119
243
|
const manager = getAcpSessionManager();
|
|
244
|
+
|
|
245
|
+
// Resume re-spawns the host agent subprocess, so it crosses the same
|
|
246
|
+
// high-risk boundary as spawn and needs guardian approval (ATL-822). A
|
|
247
|
+
// steer of a session still active in memory only redirects an
|
|
248
|
+
// already-running, already-approved process, so it is left unprompted —
|
|
249
|
+
// matching "gate before spawning". A resume only happens when the id is
|
|
250
|
+
// absent from memory AND a resumable history row (non-null cwd) exists.
|
|
251
|
+
if (!manager.getActiveAndPendingIds().includes(id)) {
|
|
252
|
+
const resumable = getDb()
|
|
253
|
+
.select({
|
|
254
|
+
cwd: acpSessionHistory.cwd,
|
|
255
|
+
agentId: acpSessionHistory.agentId,
|
|
256
|
+
parentConversationId: acpSessionHistory.parentConversationId,
|
|
257
|
+
})
|
|
258
|
+
.from(acpSessionHistory)
|
|
259
|
+
.where(eq(acpSessionHistory.id, id))
|
|
260
|
+
.get();
|
|
261
|
+
if (resumable?.cwd != null) {
|
|
262
|
+
// Guardian approval is an out-of-band human action that can take far
|
|
263
|
+
// longer than a client's ack timeout (the macOS client uses 10s; the
|
|
264
|
+
// approval window is permissionTimeoutSec, default 300s). Blocking the
|
|
265
|
+
// HTTP response here would surface as a client transport failure even
|
|
266
|
+
// on an eventual approval. Acknowledge immediately and run approval +
|
|
267
|
+
// resume in the background, streaming via SSE like any other ACP
|
|
268
|
+
// activity. The request abortSignal is deliberately NOT threaded into
|
|
269
|
+
// the wait — the request is already complete.
|
|
270
|
+
void approveThenResume({
|
|
271
|
+
id,
|
|
272
|
+
instruction,
|
|
273
|
+
agentId: resumable.agentId,
|
|
274
|
+
cwd: resumable.cwd,
|
|
275
|
+
conversationId: resumable.parentConversationId,
|
|
276
|
+
});
|
|
277
|
+
return { acpSessionId: id, steered: false, approvalPending: true };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
120
281
|
try {
|
|
121
|
-
await manager.
|
|
122
|
-
|
|
282
|
+
const { resumed } = await manager.steerOrResume(
|
|
283
|
+
id,
|
|
284
|
+
instruction,
|
|
285
|
+
broadcastMessage,
|
|
286
|
+
);
|
|
287
|
+
return resumed
|
|
288
|
+
? { acpSessionId: id, steered: true, resumed: true }
|
|
289
|
+
: { acpSessionId: id, steered: true };
|
|
290
|
+
} catch (err) {
|
|
291
|
+
// Resume errors carry the actionable hint (legacy row without cwd,
|
|
292
|
+
// agent capability missing, resolver failures, ...).
|
|
293
|
+
if (err instanceof AcpResumeError) {
|
|
294
|
+
throw new FailedDependencyError(err.message);
|
|
295
|
+
}
|
|
296
|
+
// Unknown ids (no in-memory session, no history row) and plain steer
|
|
297
|
+
// failures both map to 404, as before.
|
|
123
298
|
throw new NotFoundError("ACP session not found");
|
|
124
299
|
}
|
|
125
|
-
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Background worker for an approval-gated ACP resume initiated over the steer
|
|
304
|
+
* route. Awaits the guardian decision (see `awaitRouteApproval`), then resumes
|
|
305
|
+
* via the session manager so the agent's output streams back over SSE. A
|
|
306
|
+
* denial, timeout, or resume failure is surfaced as an `acp_session_error` on
|
|
307
|
+
* the parent conversation so the client isn't left with an optimistic steer
|
|
308
|
+
* row and no follow-up. Never throws — it owns the request lifecycle after the
|
|
309
|
+
* route has already acked.
|
|
310
|
+
*/
|
|
311
|
+
async function approveThenResume(args: {
|
|
312
|
+
id: string;
|
|
313
|
+
instruction: string;
|
|
314
|
+
agentId: string;
|
|
315
|
+
cwd: string;
|
|
316
|
+
conversationId: string;
|
|
317
|
+
}): Promise<void> {
|
|
318
|
+
const decision = await awaitRouteApproval({
|
|
319
|
+
toolName: "acp_steer",
|
|
320
|
+
input: {
|
|
321
|
+
acp_session_id: args.id,
|
|
322
|
+
instruction: args.instruction,
|
|
323
|
+
agent: args.agentId,
|
|
324
|
+
cwd: args.cwd,
|
|
325
|
+
},
|
|
326
|
+
conversationId: args.conversationId,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (decision !== "allow") {
|
|
330
|
+
log.info(
|
|
331
|
+
{ acpSessionId: args.id, conversationId: args.conversationId },
|
|
332
|
+
"ACP resume approval not granted — skipping resume",
|
|
333
|
+
);
|
|
334
|
+
broadcastMessage(
|
|
335
|
+
{
|
|
336
|
+
type: "acp_session_error",
|
|
337
|
+
// Key the error by the daemon session id (the value the steer route
|
|
338
|
+
// accepts and ACP SSE consumers index by — see registerSession, where
|
|
339
|
+
// state.id is the broadcast acpSessionId), NOT the persisted protocol
|
|
340
|
+
// id. A denied resume never emits acp_session_spawned to map the
|
|
341
|
+
// protocol id, so a protocol-keyed error would be dropped.
|
|
342
|
+
acpSessionId: args.id,
|
|
343
|
+
error: "Resume was not approved.",
|
|
344
|
+
},
|
|
345
|
+
args.conversationId,
|
|
346
|
+
);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await getAcpSessionManager().steerOrResume(
|
|
352
|
+
args.id,
|
|
353
|
+
args.instruction,
|
|
354
|
+
broadcastMessage,
|
|
355
|
+
);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
const message =
|
|
358
|
+
err instanceof AcpResumeError || err instanceof Error
|
|
359
|
+
? err.message
|
|
360
|
+
: String(err);
|
|
361
|
+
log.warn(
|
|
362
|
+
{ acpSessionId: args.id, conversationId: args.conversationId, err },
|
|
363
|
+
"Approved ACP resume failed",
|
|
364
|
+
);
|
|
365
|
+
broadcastMessage(
|
|
366
|
+
{
|
|
367
|
+
type: "acp_session_error",
|
|
368
|
+
// Daemon session id (route + SSE key), not the protocol id — see above.
|
|
369
|
+
acpSessionId: args.id,
|
|
370
|
+
error: message,
|
|
371
|
+
},
|
|
372
|
+
args.conversationId,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
126
375
|
}
|
|
127
376
|
|
|
128
377
|
async function cancelSession({ pathParams }: RouteHandlerArgs) {
|
|
@@ -161,9 +410,25 @@ function bulkDeleteSessions({ queryParams }: RouteHandlerArgs) {
|
|
|
161
410
|
"status query param is required and must be 'completed'",
|
|
162
411
|
);
|
|
163
412
|
}
|
|
413
|
+
// Exclude sessions currently active in memory AND ids with a resume in
|
|
414
|
+
// flight (reserved but not yet registered): a resumed session reuses its
|
|
415
|
+
// original id, and its history row keeps the old terminal status until
|
|
416
|
+
// the next terminal upsert - a status-only delete would remove the row
|
|
417
|
+
// out from under the live (or resuming) session, and the later terminal
|
|
418
|
+
// upsert would resurrect it. Mirrors the 409 guard on the single-id
|
|
419
|
+
// delete route.
|
|
420
|
+
const activeIds = getAcpSessionManager().getActiveAndPendingIds();
|
|
421
|
+
const terminalFilter = inArray(
|
|
422
|
+
acpSessionHistory.status,
|
|
423
|
+
TERMINAL_SESSION_STATUSES,
|
|
424
|
+
);
|
|
164
425
|
getDb()
|
|
165
426
|
.delete(acpSessionHistory)
|
|
166
|
-
.where(
|
|
427
|
+
.where(
|
|
428
|
+
activeIds.length > 0
|
|
429
|
+
? and(terminalFilter, notInArray(acpSessionHistory.id, activeIds))
|
|
430
|
+
: terminalFilter,
|
|
431
|
+
)
|
|
167
432
|
.run();
|
|
168
433
|
const deleted = rawChanges();
|
|
169
434
|
log.info({ deleted }, "Bulk-cleared terminal ACP session history");
|
|
@@ -172,9 +437,10 @@ function bulkDeleteSessions({ queryParams }: RouteHandlerArgs) {
|
|
|
172
437
|
|
|
173
438
|
function deleteSession({ pathParams }: RouteHandlerArgs) {
|
|
174
439
|
const id = pathParams?.id as string;
|
|
440
|
+
const manager = getAcpSessionManager();
|
|
175
441
|
|
|
176
442
|
try {
|
|
177
|
-
const state =
|
|
443
|
+
const state = manager.getStatus(id);
|
|
178
444
|
if (
|
|
179
445
|
!Array.isArray(state) &&
|
|
180
446
|
(state.status === "running" || state.status === "initializing")
|
|
@@ -185,7 +451,16 @@ function deleteSession({ pathParams }: RouteHandlerArgs) {
|
|
|
185
451
|
}
|
|
186
452
|
} catch (err) {
|
|
187
453
|
if (err instanceof ConflictError) throw err;
|
|
188
|
-
// Not in memory
|
|
454
|
+
// Not registered in memory, but a resume may be in flight (id reserved
|
|
455
|
+
// while awaiting env preparation). Its history row must survive until
|
|
456
|
+
// the resume lands - the later terminal upsert would resurrect a
|
|
457
|
+
// deleted row.
|
|
458
|
+
if (manager.getActiveAndPendingIds().includes(id)) {
|
|
459
|
+
throw new ConflictError(
|
|
460
|
+
`ACP session "${id}" has a resume in flight. Wait for it to finish before deleting.`,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
// Otherwise fall through to the (idempotent) DB delete.
|
|
189
464
|
}
|
|
190
465
|
|
|
191
466
|
getDb().delete(acpSessionHistory).where(eq(acpSessionHistory.id, id)).run();
|
|
@@ -233,7 +508,14 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
233
508
|
},
|
|
234
509
|
handler: steerSession,
|
|
235
510
|
summary: "Steer ACP session",
|
|
236
|
-
description:
|
|
511
|
+
description:
|
|
512
|
+
"Send a steering instruction to an ACP session. Sessions no longer " +
|
|
513
|
+
"in memory (completed, or lost to a daemon restart) are " +
|
|
514
|
+
"transparently resumed from persisted history first, when the agent " +
|
|
515
|
+
"supports ACP session loading. Resuming a terminal session re-spawns " +
|
|
516
|
+
"the host agent, so it requires guardian approval: the route acks " +
|
|
517
|
+
"immediately with approvalPending=true and performs the resume in the " +
|
|
518
|
+
"background once approved, streaming results over SSE.",
|
|
237
519
|
tags: ["acp"],
|
|
238
520
|
requestBody: z.object({
|
|
239
521
|
instruction: z.string(),
|
|
@@ -241,6 +523,19 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
241
523
|
responseBody: z.object({
|
|
242
524
|
acpSessionId: z.string(),
|
|
243
525
|
steered: z.boolean(),
|
|
526
|
+
resumed: z
|
|
527
|
+
.boolean()
|
|
528
|
+
.optional()
|
|
529
|
+
.describe(
|
|
530
|
+
"True when the session was resumed from persisted history before steering.",
|
|
531
|
+
),
|
|
532
|
+
approvalPending: z
|
|
533
|
+
.boolean()
|
|
534
|
+
.optional()
|
|
535
|
+
.describe(
|
|
536
|
+
"True when the steer triggered a guardian-approval-gated resume " +
|
|
537
|
+
"that will run asynchronously; watch SSE for the outcome.",
|
|
538
|
+
),
|
|
244
539
|
}),
|
|
245
540
|
},
|
|
246
541
|
{
|
|
@@ -324,7 +619,9 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
324
619
|
summary: "Bulk-clear terminal ACP sessions",
|
|
325
620
|
description:
|
|
326
621
|
"Remove every terminal-state row (completed/failed/cancelled) from " +
|
|
327
|
-
"the persisted acp_session_history table."
|
|
622
|
+
"the persisted acp_session_history table. Rows whose session is " +
|
|
623
|
+
"currently active in memory (e.g. resumed) or has a resume in " +
|
|
624
|
+
"flight are excluded.",
|
|
328
625
|
tags: ["acp"],
|
|
329
626
|
queryParams: [
|
|
330
627
|
{
|
|
@@ -350,7 +647,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
350
647
|
summary: "Delete ACP session from history",
|
|
351
648
|
description:
|
|
352
649
|
"Remove a persisted ACP session row. Rejects with 409 when the " +
|
|
353
|
-
"session is still active in memory
|
|
650
|
+
"session is still active in memory or has a resume in flight; " +
|
|
651
|
+
"idempotent for unknown ids.",
|
|
354
652
|
tags: ["acp"],
|
|
355
653
|
responseBody: z.object({
|
|
356
654
|
deleted: z.boolean(),
|
|
@@ -89,6 +89,7 @@ function listAppsFiltered(apps?: AppDefinition[]): Array<{
|
|
|
89
89
|
description?: string;
|
|
90
90
|
icon?: string;
|
|
91
91
|
createdAt: number;
|
|
92
|
+
updatedAt: number;
|
|
92
93
|
version: string;
|
|
93
94
|
contentId: string;
|
|
94
95
|
}> {
|
|
@@ -101,6 +102,7 @@ function listAppsFiltered(apps?: AppDefinition[]): Array<{
|
|
|
101
102
|
description: a.description,
|
|
102
103
|
icon: a.icon,
|
|
103
104
|
createdAt: a.createdAt,
|
|
105
|
+
updatedAt: a.updatedAt,
|
|
104
106
|
version,
|
|
105
107
|
contentId,
|
|
106
108
|
};
|
|
@@ -751,6 +753,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
751
753
|
description: z.string().optional(),
|
|
752
754
|
icon: z.string().optional(),
|
|
753
755
|
createdAt: z.number(),
|
|
756
|
+
updatedAt: z.number(),
|
|
754
757
|
version: z.string(),
|
|
755
758
|
contentId: z.string(),
|
|
756
759
|
}),
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
|
-
import { findConversation } from "../../daemon/conversation-
|
|
12
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
13
13
|
import { getConversationByKey } from "../../memory/conversation-key-store.js";
|
|
14
14
|
import type { UserDecision } from "../../permissions/types.js";
|
|
15
15
|
import { getLogger } from "../../util/logger.js";
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
BROWSER_OPERATIONS,
|
|
17
17
|
type BrowserOperation,
|
|
18
18
|
} from "../../browser/types.js";
|
|
19
|
-
import { findConversation } from "../../daemon/conversation-
|
|
19
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
20
20
|
import type { ContentBlock } from "../../providers/types.js";
|
|
21
21
|
import { LOCAL_PRINCIPALS } from "../auth/route-policy.js";
|
|
22
22
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
-
import { findConversation } from "../../daemon/conversation-
|
|
10
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
11
11
|
import { getCdpClient } from "../../tools/browser/cdp-client/factory.js";
|
|
12
12
|
import {
|
|
13
13
|
clearPinnedTab,
|
|
@@ -73,11 +73,7 @@ async function handleBrowserTabs({ body = {} }: RouteHandlerArgs) {
|
|
|
73
73
|
? result.clientId
|
|
74
74
|
: undefined;
|
|
75
75
|
if (result?.tabId !== undefined) {
|
|
76
|
-
setPinnedTab(
|
|
77
|
-
resolvedConversationId,
|
|
78
|
-
String(result.tabId),
|
|
79
|
-
clientId,
|
|
80
|
-
);
|
|
76
|
+
setPinnedTab(resolvedConversationId, String(result.tabId), clientId);
|
|
81
77
|
}
|
|
82
78
|
return { ok: true, tab: result };
|
|
83
79
|
} finally {
|
|
@@ -88,10 +84,10 @@ async function handleBrowserTabs({ body = {} }: RouteHandlerArgs) {
|
|
|
88
84
|
if (command === "new") {
|
|
89
85
|
const cdp = getCdpClient(context, cdpOptions);
|
|
90
86
|
try {
|
|
91
|
-
const result = await cdp.send<{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
);
|
|
87
|
+
const result = await cdp.send<{
|
|
88
|
+
tabId?: number | string;
|
|
89
|
+
clientId?: string;
|
|
90
|
+
}>("Vellum.createTab", {});
|
|
95
91
|
// Normalise to string for internal use (setCdpSessionId / setPinnedTab)
|
|
96
92
|
// and keep the numeric form for the API response.
|
|
97
93
|
const newTabIdStr: string | undefined =
|
|
@@ -23,6 +23,7 @@ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
|
23
23
|
import { revokePendingSessions } from "../channel-verification-service.js";
|
|
24
24
|
import {
|
|
25
25
|
cancelOutbound,
|
|
26
|
+
deliverVerificationEmail,
|
|
26
27
|
deliverVerificationSlack,
|
|
27
28
|
normalizeTelegramDestination,
|
|
28
29
|
resendOutbound,
|
|
@@ -139,11 +140,15 @@ export async function handleCreateVerificationSession({
|
|
|
139
140
|
verificationRateLimiter.recordFailure(rateLimitKey);
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
// Dispatch
|
|
143
|
+
// Dispatch delivery from the daemon process (not sandboxed).
|
|
143
144
|
if (result._pendingSlackDm) {
|
|
144
145
|
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
145
146
|
deliverVerificationSlack(userId, text, aid);
|
|
146
147
|
}
|
|
148
|
+
if (result._pendingEmail) {
|
|
149
|
+
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
150
|
+
deliverVerificationEmail(to, text, subject, aid);
|
|
151
|
+
}
|
|
147
152
|
|
|
148
153
|
if (!result.success) {
|
|
149
154
|
if (result.error === "rate_limited") {
|
|
@@ -157,8 +162,8 @@ export async function handleCreateVerificationSession({
|
|
|
157
162
|
);
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
// Strip internal
|
|
161
|
-
const { _pendingSlackDm: _, ...publicResult } = result;
|
|
165
|
+
// Strip internal fields from the response
|
|
166
|
+
const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
|
|
162
167
|
return publicResult;
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -204,11 +209,15 @@ export async function handleResendVerificationSession({
|
|
|
204
209
|
|
|
205
210
|
const result = resendOutbound({ channel, originConversationId });
|
|
206
211
|
|
|
207
|
-
// Dispatch
|
|
212
|
+
// Dispatch delivery from the daemon process (not sandboxed).
|
|
208
213
|
if (result._pendingSlackDm) {
|
|
209
214
|
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
210
215
|
deliverVerificationSlack(userId, text, aid);
|
|
211
216
|
}
|
|
217
|
+
if (result._pendingEmail) {
|
|
218
|
+
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
219
|
+
deliverVerificationEmail(to, text, subject, aid);
|
|
220
|
+
}
|
|
212
221
|
|
|
213
222
|
if (!result.success) {
|
|
214
223
|
if (result.error === "rate_limited") {
|
|
@@ -221,7 +230,7 @@ export async function handleResendVerificationSession({
|
|
|
221
230
|
);
|
|
222
231
|
}
|
|
223
232
|
|
|
224
|
-
const { _pendingSlackDm: _, ...publicResult } = result;
|
|
233
|
+
const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
|
|
225
234
|
return publicResult;
|
|
226
235
|
}
|
|
227
236
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
|
-
import { findConversation } from "../../daemon/conversation-
|
|
12
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
13
13
|
import { clearAllConversations as clearAllActive } from "../../daemon/handlers/conversations.js";
|
|
14
14
|
import { formatJson, formatMarkdown } from "../../export/formatter.js";
|
|
15
15
|
import { ipcCall as ipcCallGateway } from "../../ipc/gateway-client.js";
|
|
@@ -72,7 +72,7 @@ import { type LogRow } from "../../memory/llm-request-log-store.js";
|
|
|
72
72
|
import { getMemoryRecallLogByMessageIds } from "../../memory/memory-recall-log-store.js";
|
|
73
73
|
import { getMemoryV2ActivationLogByMessageIds } from "../../memory/memory-v2-activation-log-store.js";
|
|
74
74
|
import { MEMORY_V2_CONSOLIDATION_SOURCE } from "../../memory/v2/constants.js";
|
|
75
|
-
import { getMemoryV3SelectionForInspector } from "../../memory
|
|
75
|
+
import { getMemoryV3SelectionForInspector } from "../../plugins/defaults/memory-v3-shadow/selection-log-store.js";
|
|
76
76
|
import {
|
|
77
77
|
createConnection,
|
|
78
78
|
listConnections,
|