@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62

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.
Files changed (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -1,13 +1,24 @@
1
1
  /**
2
- * Tests for `GET /v1/acp/sessions`.
2
+ * Tests for ACP route handlers.
3
3
  *
4
- * The handler merges in-memory `AcpSessionManager.getStatus()` output with
5
- * persisted `acp_session_history` rows, deduping by id (in-memory wins),
6
- * filtering by `?conversationId`, sorting newest-first, and truncating to
7
- * `?limit` (default 50, max 500).
4
+ * `GET /v1/acp/sessions`: the handler merges in-memory
5
+ * `AcpSessionManager.getStatus()` output with persisted
6
+ * `acp_session_history` rows, deduping by id (in-memory wins), filtering by
7
+ * `?conversationId`, sorting newest-first, and truncating to `?limit`
8
+ * (default 50, max 500).
9
+ *
10
+ * `POST /v1/acp/spawn`: when the adapter binary is missing, the handler
11
+ * silently auto-installs allowlisted adapter packages via a sandboxed `bun`
12
+ * global install before failing with the install hint. `execFile` is stubbed
13
+ * via the shared `installExecFileStub` helper so tests can script
14
+ * `bun add --global` outcomes.
8
15
  */
9
16
 
10
- import { beforeEach, describe, expect, mock, test } from "bun:test";
17
+ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
18
+
19
+ import { installAcpConfigStub } from "../../../acp/__tests__/helpers/acp-config-stub.js";
20
+ import { installExecFileStub } from "../../../acp/__tests__/helpers/exec-file-stub.js";
21
+ import { installWhichStub } from "../../../acp/__tests__/helpers/which-stub.js";
11
22
 
12
23
  mock.module("../../../util/logger.js", () => ({
13
24
  getLogger: () =>
@@ -16,6 +27,12 @@ mock.module("../../../util/logger.js", () => ({
16
27
  }),
17
28
  }));
18
29
 
30
+ const {
31
+ execScripts,
32
+ execFileMock,
33
+ reset: resetExecFileStub,
34
+ } = installExecFileStub();
35
+
19
36
  // ---------------------------------------------------------------------------
20
37
  // Stub the ACP session manager so tests control the in-memory side without
21
38
  // spawning real child processes. The route handler imports
@@ -37,64 +54,90 @@ interface FakeSessionState {
37
54
 
38
55
  let fakeInMemorySessions: FakeSessionState[] = [];
39
56
 
57
+ const spawnMock = mock(async () => ({
58
+ acpSessionId: "acp-route-session",
59
+ protocolSessionId: "proto-route-session",
60
+ }));
61
+
62
+ const defaultSteerOrResumeImpl = async (
63
+ _id: string,
64
+ _instruction: string,
65
+ ): Promise<{ resumed: boolean }> => ({ resumed: false });
66
+ let steerOrResumeImpl: (
67
+ id: string,
68
+ instruction: string,
69
+ ) => Promise<{ resumed: boolean }> = defaultSteerOrResumeImpl;
70
+ const steerOrResumeMock = mock(
71
+ (id: string, instruction: string, _send: unknown) =>
72
+ steerOrResumeImpl(id, instruction),
73
+ );
74
+
40
75
  mock.module("../../../acp/index.js", () => ({
41
76
  getAcpSessionManager: () => ({
42
77
  getStatus: () => fakeInMemorySessions,
78
+ getActiveAndPendingIds: () => fakeInMemorySessions.map((s) => s.id),
79
+ spawn: spawnMock,
80
+ steerOrResume: steerOrResumeMock,
43
81
  }),
44
82
  }));
45
83
 
46
- import { getSqlite } from "../../../memory/db-connection.js";
47
- import { initializeDb } from "../../../memory/db-init.js";
48
- import { ROUTES } from "../acp-routes.js";
84
+ // Identity env-prep: the credential-broker plumbing it wraps is exercised in
85
+ // its own suite; spawn tests here only care about the resolve/install flow.
86
+ mock.module("../../../acp/prepare-agent-env.js", () => ({
87
+ prepareAgentEnv: async (agentConfig: unknown) => agentConfig,
88
+ }));
49
89
 
50
- initializeDb();
90
+ // The spawn route and steer's resume branch gate on a high-risk approval
91
+ // (ATL-822) before starting the host agent. These tests pin the
92
+ // resolve/install/resume flow, so the hub mock auto-resolves the freshly
93
+ // registered confirmation the same way `POST /v1/confirm` would (resolve +
94
+ // directResolve). `approvalBehavior` flips allow/deny; `confirmationRequests`
95
+ // captures the prompts. Other event types are ignored.
96
+ import * as pendingInteractions from "../../../runtime/pending-interactions.js";
51
97
 
52
- function clearHistory(): void {
53
- getSqlite().run("DELETE FROM acp_session_history");
54
- }
98
+ let approvalBehavior: "allow" | "deny" = "allow";
99
+ const confirmationRequests: Array<Record<string, unknown>> = [];
100
+ const broadcasts: Array<Record<string, unknown>> = [];
55
101
 
56
- function insertHistoryRow(row: {
57
- id: string;
58
- agentId: string;
59
- acpSessionId: string;
60
- parentConversationId: string;
61
- startedAt: number;
62
- completedAt?: number | null;
63
- status: string;
64
- stopReason?: string | null;
65
- error?: string | null;
66
- eventLogJson?: string;
67
- }): void {
68
- getSqlite()
69
- .query(
70
- /*sql*/ `
71
- INSERT INTO acp_session_history (
72
- id,
73
- agent_id,
74
- acp_session_id,
75
- parent_conversation_id,
76
- started_at,
77
- completed_at,
78
- status,
79
- stop_reason,
80
- error,
81
- event_log_json
82
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
83
- `,
84
- )
85
- .run(
86
- row.id,
87
- row.agentId,
88
- row.acpSessionId,
89
- row.parentConversationId,
90
- row.startedAt,
91
- row.completedAt ?? null,
92
- row.status,
93
- row.stopReason ?? null,
94
- row.error ?? null,
95
- row.eventLogJson ?? "[]",
102
+ mock.module("../../../runtime/assistant-event-hub.js", () => ({
103
+ broadcastMessage: (msg: { type?: string; requestId?: string }) => {
104
+ broadcasts.push(msg as Record<string, unknown>);
105
+ if (msg?.type !== "confirmation_request") return;
106
+ confirmationRequests.push(msg as Record<string, unknown>);
107
+ const interaction = pendingInteractions.resolve(
108
+ msg.requestId as string,
109
+ approvalBehavior === "allow" ? "approved" : "rejected",
96
110
  );
97
- }
111
+ interaction?.directResolve?.(approvalBehavior);
112
+ },
113
+ }));
114
+
115
+ /** Drain pending micro/macrotasks so background resume work settles. */
116
+ const flushAsync = () => new Promise((resolve) => setTimeout(resolve, 0));
117
+
118
+ const config = await installAcpConfigStub();
119
+ const which = installWhichStub();
120
+
121
+ import {
122
+ clearHistory,
123
+ insertHistoryRow,
124
+ } from "../../../acp/__tests__/helpers/acp-history-db.js";
125
+ import {
126
+ AcpResumeError,
127
+ AcpSessionNotFoundError,
128
+ } from "../../../acp/session-manager.js";
129
+ import { initializeDb } from "../../../memory/db-init.js";
130
+ import { FailedDependencyError, NotFoundError } from "../errors.js";
131
+
132
+ const { ROUTES } = await import("../acp-routes.js");
133
+ const { _resetAdapterInstallCacheForTests } =
134
+ await import("../../../acp/auto-install.js");
135
+
136
+ initializeDb();
137
+
138
+ afterAll(() => {
139
+ which.restore();
140
+ });
98
141
 
99
142
  function getSessionsHandler() {
100
143
  const route = ROUTES.find(
@@ -122,6 +165,16 @@ interface ResponseShape {
122
165
  beforeEach(() => {
123
166
  fakeInMemorySessions = [];
124
167
  clearHistory();
168
+ resetExecFileStub();
169
+ spawnMock.mockClear();
170
+ steerOrResumeMock.mockClear();
171
+ steerOrResumeImpl = defaultSteerOrResumeImpl;
172
+ _resetAdapterInstallCacheForTests();
173
+ config.setConfig({});
174
+ which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
175
+ approvalBehavior = "allow";
176
+ confirmationRequests.length = 0;
177
+ broadcasts.length = 0;
125
178
  });
126
179
 
127
180
  describe("GET /v1/acp/sessions — merged in-memory + history", () => {
@@ -306,7 +359,9 @@ describe("GET /v1/acp/sessions — merged in-memory + history", () => {
306
359
  });
307
360
 
308
361
  const handler = getSessionsHandler();
309
- const body = (await handler({ queryParams: { limit: "2" } })) as ResponseShape;
362
+ const body = (await handler({
363
+ queryParams: { limit: "2" },
364
+ })) as ResponseShape;
310
365
  expect(body.sessions).toHaveLength(2);
311
366
  expect(body.sessions.map((s) => s.id)).toEqual(["live-newest", "hist-mid"]);
312
367
  });
@@ -392,3 +447,359 @@ describe("GET /v1/acp/sessions — merged in-memory + history", () => {
392
447
  expect(body.sessions).toHaveLength(3);
393
448
  });
394
449
  });
450
+
451
+ // ---------------------------------------------------------------------------
452
+ // POST /v1/acp/spawn: auto-install on missing adapter binary
453
+ // ---------------------------------------------------------------------------
454
+
455
+ function getSpawnHandler() {
456
+ const route = ROUTES.find(
457
+ (r) => r.endpoint === "acp/spawn" && r.method === "POST",
458
+ );
459
+ if (!route) throw new Error("acp/spawn POST route not found");
460
+ return route.handler;
461
+ }
462
+
463
+ const SPAWN_BODY = {
464
+ agent: "claude",
465
+ task: "do something",
466
+ conversationId: "conv-1",
467
+ };
468
+
469
+ const BUN_BIN = "/usr/local/bin/bun";
470
+ const BUN_ADD_KEY = `${BUN_BIN} add`;
471
+
472
+ describe("POST /v1/acp/spawn: sandboxed bun auto-install on missing binary", () => {
473
+ test("known command + bun present: installs via bun, then spawns the real binary", async () => {
474
+ // Binary appears on PATH only after `bun add --global` runs, simulating a
475
+ // successful global install that links the adapter bin onto PATH.
476
+ let binaryOnPath = false;
477
+ which.setWhich((cmd) => {
478
+ if (cmd === "bun") return BUN_BIN;
479
+ if (binaryOnPath) return `/usr/local/bin/${cmd}`;
480
+ return null;
481
+ });
482
+ execScripts.set(BUN_ADD_KEY, {
483
+ stdout: "",
484
+ onCall: () => {
485
+ binaryOnPath = true;
486
+ },
487
+ });
488
+
489
+ const handler = getSpawnHandler();
490
+ const body = (await handler({ body: SPAWN_BODY })) as Record<
491
+ string,
492
+ unknown
493
+ >;
494
+
495
+ expect(body).toEqual({
496
+ acpSessionId: "acp-route-session",
497
+ protocolSessionId: "proto-route-session",
498
+ agent: "claude",
499
+ });
500
+ expect(spawnMock).toHaveBeenCalledTimes(1);
501
+ // The real adapter binary is spawned, not a `bun x` wrapper.
502
+ const agentConfigArg = (spawnMock.mock.calls[0] as unknown[])[1] as {
503
+ command: string;
504
+ };
505
+ expect(agentConfigArg.command).toBe("claude-agent-acp");
506
+ // Exactly one install, and it was `bun add --global` (never npm).
507
+ expect(execFileMock).toHaveBeenCalledTimes(1);
508
+ const [command, args] = execFileMock.mock.calls[0];
509
+ expect(command).toBe(BUN_BIN);
510
+ expect(args).toEqual([
511
+ "add",
512
+ "--global",
513
+ "@agentclientprotocol/claude-agent-acp",
514
+ ]);
515
+ });
516
+
517
+ test("install runs in a temp dir (not the task cwd) with secrets stripped", async () => {
518
+ let binaryOnPath = false;
519
+ which.setWhich((cmd) => {
520
+ if (cmd === "bun") return BUN_BIN;
521
+ if (binaryOnPath) return `/usr/local/bin/${cmd}`;
522
+ return null;
523
+ });
524
+ execScripts.set(BUN_ADD_KEY, {
525
+ stdout: "",
526
+ onCall: () => {
527
+ binaryOnPath = true;
528
+ },
529
+ });
530
+
531
+ const handler = getSpawnHandler();
532
+ await handler({ body: { ...SPAWN_BODY, cwd: "/untrusted/project" } });
533
+
534
+ const options = execFileMock.mock.calls[0][2] as {
535
+ cwd?: string;
536
+ env?: NodeJS.ProcessEnv;
537
+ };
538
+ expect(options.cwd).not.toBe("/untrusted/project");
539
+ expect(options.cwd).toContain("vellum-acp-install-");
540
+ expect(options.env?.CLAUDE_CODE_OAUTH_TOKEN).toBeUndefined();
541
+ expect(options.env?.GEMINI_API_KEY).toBeUndefined();
542
+ expect(options.env?.BUN_CONFIG_REGISTRY).toBe(
543
+ "https://registry.npmjs.org/",
544
+ );
545
+ });
546
+
547
+ test("bun absent: no install attempted, FailedDependencyError with the hint", async () => {
548
+ which.setWhich({}); // neither bun nor the adapter on PATH
549
+
550
+ const handler = getSpawnHandler();
551
+ const promise = handler({ body: SPAWN_BODY });
552
+ await expect(promise).rejects.toBeInstanceOf(FailedDependencyError);
553
+ await expect(promise).rejects.toThrow(
554
+ /claude-agent-acp is not on PATH.*bun add -g @agentclientprotocol\/claude-agent-acp/,
555
+ );
556
+ expect(execFileMock).not.toHaveBeenCalled();
557
+ expect(spawnMock).not.toHaveBeenCalled();
558
+ });
559
+
560
+ test("install failure: FailedDependencyError carries hint and failure reason, never npm", async () => {
561
+ which.setWhich({ bun: BUN_BIN });
562
+ execScripts.set(BUN_ADD_KEY, {
563
+ error: new Error("EACCES: permission denied"),
564
+ });
565
+
566
+ const handler = getSpawnHandler();
567
+ const promise = handler({ body: SPAWN_BODY });
568
+ await expect(promise).rejects.toBeInstanceOf(FailedDependencyError);
569
+ await expect(promise).rejects.toThrow(
570
+ /claude-agent-acp is not on PATH.*bun add -g @agentclientprotocol\/claude-agent-acp.*auto-install failed.*EACCES/,
571
+ );
572
+ for (const call of execFileMock.mock.calls) {
573
+ expect(call[0]).not.toBe("npm");
574
+ }
575
+ expect(spawnMock).not.toHaveBeenCalled();
576
+ });
577
+
578
+ test("unknown command: plain hint maps to FailedDependencyError", async () => {
579
+ // The allowlist itself (no npm invocation for unmapped commands) is
580
+ // pinned in auto-install.test.ts and spawn.test.ts; this asserts only
581
+ // the route's transport mapping of the plain-hint failure.
582
+ config.setConfig({
583
+ agents: { custom: { command: "custom-bin", args: [] } },
584
+ });
585
+ which.setWhich({});
586
+
587
+ const handler = getSpawnHandler();
588
+ const promise = handler({ body: { ...SPAWN_BODY, agent: "custom" } });
589
+ await expect(promise).rejects.toBeInstanceOf(FailedDependencyError);
590
+ await expect(promise).rejects.toThrow(
591
+ "custom-bin is not on PATH. Install 'custom-bin' and ensure it is on PATH.",
592
+ );
593
+ });
594
+ });
595
+
596
+ // ---------------------------------------------------------------------------
597
+ // POST /v1/acp/:id/steer: transparent resume of sessions not in memory
598
+ // ---------------------------------------------------------------------------
599
+
600
+ function getSteerHandler() {
601
+ const route = ROUTES.find(
602
+ (r) => r.endpoint === "acp/:id/steer" && r.method === "POST",
603
+ );
604
+ if (!route) throw new Error("acp/:id/steer POST route not found");
605
+ return route.handler;
606
+ }
607
+
608
+ describe("POST /v1/acp/:id/steer: resume fallback", () => {
609
+ test("in-memory session steers without a resume", async () => {
610
+ const handler = getSteerHandler();
611
+ const body = await handler({
612
+ pathParams: { id: "live-1" },
613
+ body: { instruction: "redirect" },
614
+ });
615
+
616
+ expect(body).toEqual({ acpSessionId: "live-1", steered: true });
617
+ expect(steerOrResumeMock).toHaveBeenCalledTimes(1);
618
+ expect(steerOrResumeMock.mock.calls[0][0]).toBe("live-1");
619
+ expect(steerOrResumeMock.mock.calls[0][1]).toBe("redirect");
620
+ expect(typeof steerOrResumeMock.mock.calls[0][2]).toBe("function");
621
+ });
622
+
623
+ test("resumed session reports the resumed flag", async () => {
624
+ steerOrResumeImpl = async () => ({ resumed: true });
625
+
626
+ const handler = getSteerHandler();
627
+ const body = await handler({
628
+ pathParams: { id: "gone-1" },
629
+ body: { instruction: "keep going" },
630
+ });
631
+
632
+ expect(body).toEqual({
633
+ acpSessionId: "gone-1",
634
+ steered: true,
635
+ resumed: true,
636
+ });
637
+ });
638
+
639
+ test("typed not-found (no session, no history row) maps to NotFoundError", async () => {
640
+ steerOrResumeImpl = async (id) => {
641
+ throw new AcpSessionNotFoundError(id);
642
+ };
643
+
644
+ const handler = getSteerHandler();
645
+ const promise = handler({
646
+ pathParams: { id: "missing-1" },
647
+ body: { instruction: "go" },
648
+ });
649
+ await expect(promise).rejects.toBeInstanceOf(NotFoundError);
650
+ });
651
+
652
+ test("resume failure surfaces as FailedDependencyError with the actionable hint", async () => {
653
+ steerOrResumeImpl = async (id) => {
654
+ throw new AcpResumeError(
655
+ new Error(
656
+ `ACP session "${id}" was recorded before resume support ` +
657
+ `(no working directory persisted) and cannot be resumed. ` +
658
+ `Spawn a new session instead.`,
659
+ ),
660
+ );
661
+ };
662
+
663
+ const handler = getSteerHandler();
664
+ const promise = handler({
665
+ pathParams: { id: "legacy-1" },
666
+ body: { instruction: "go" },
667
+ });
668
+ await expect(promise).rejects.toBeInstanceOf(FailedDependencyError);
669
+ await expect(promise).rejects.toThrow(/recorded before resume support/);
670
+ });
671
+
672
+ test("plain steer errors map to NotFoundError", async () => {
673
+ steerOrResumeImpl = async (id) => {
674
+ throw new Error(
675
+ `ACP session "${id}" is not running (status: initializing)`,
676
+ );
677
+ };
678
+
679
+ const handler = getSteerHandler();
680
+ const promise = handler({
681
+ pathParams: { id: "init-1" },
682
+ body: { instruction: "go" },
683
+ });
684
+ await expect(promise).rejects.toBeInstanceOf(NotFoundError);
685
+ });
686
+ });
687
+
688
+ // ---------------------------------------------------------------------------
689
+ // POST /v1/acp/:id/steer: resume crosses the host-spawn boundary, so it is
690
+ // gated by the same high-risk guardian approval as spawn (ATL-822). Steering
691
+ // a session still active in memory only redirects an already-approved live
692
+ // process and is NOT prompted.
693
+ // ---------------------------------------------------------------------------
694
+
695
+ describe("POST /v1/acp/:id/steer: resume approval gate", () => {
696
+ test("acks immediately with approvalPending, then resumes once granted", async () => {
697
+ insertHistoryRow({
698
+ id: "gone-1",
699
+ agentId: "claude",
700
+ parentConversationId: "conv-z",
701
+ status: "completed",
702
+ cwd: "/work/repo",
703
+ });
704
+ steerOrResumeImpl = async () => ({ resumed: true });
705
+
706
+ const handler = getSteerHandler();
707
+ // The ack returns before the (async) resume completes, so a slow guardian
708
+ // approval can't trip the client's short ack timeout (ATL-822 / Codex P2).
709
+ const body = await handler({
710
+ pathParams: { id: "gone-1" },
711
+ body: { instruction: "keep going" },
712
+ });
713
+ expect(body).toEqual({
714
+ acpSessionId: "gone-1",
715
+ steered: false,
716
+ approvalPending: true,
717
+ });
718
+
719
+ // The high-risk prompt is surfaced for the resume.
720
+ expect(confirmationRequests).toHaveLength(1);
721
+ const prompt = confirmationRequests[0];
722
+ expect(prompt.toolName).toBe("acp_steer");
723
+ expect(prompt.riskLevel).toBe("high");
724
+ expect(prompt.executionTarget).toBe("host");
725
+ expect(prompt.conversationId).toBe("conv-z");
726
+ expect((prompt.input as { cwd?: string }).cwd).toBe("/work/repo");
727
+
728
+ // After approval settles, the background worker performs the resume.
729
+ await flushAsync();
730
+ expect(steerOrResumeMock).toHaveBeenCalledTimes(1);
731
+ expect(steerOrResumeMock.mock.calls[0][0]).toBe("gone-1");
732
+ });
733
+
734
+ test("denied resume never reaches the session manager and reports an error event", async () => {
735
+ approvalBehavior = "deny";
736
+ insertHistoryRow({
737
+ id: "gone-2",
738
+ acpSessionId: "proto-gone-2",
739
+ parentConversationId: "conv-z",
740
+ status: "completed",
741
+ cwd: "/work/repo",
742
+ });
743
+
744
+ const handler = getSteerHandler();
745
+ const body = await handler({
746
+ pathParams: { id: "gone-2" },
747
+ body: { instruction: "do evil" },
748
+ });
749
+ expect(body).toEqual({
750
+ acpSessionId: "gone-2",
751
+ steered: false,
752
+ approvalPending: true,
753
+ });
754
+
755
+ await flushAsync();
756
+ // Denied before any host re-spawn, and the denial surfaces over SSE.
757
+ expect(steerOrResumeMock).not.toHaveBeenCalled();
758
+ expect(pendingInteractions.getAll()).toHaveLength(0);
759
+ // Keyed by the daemon/route id (what SSE consumers index by), not the
760
+ // persisted protocol id.
761
+ const errEvent = broadcasts.find((m) => m.type === "acp_session_error");
762
+ expect(errEvent?.acpSessionId).toBe("gone-2");
763
+ });
764
+
765
+ test("a legacy row without a persisted cwd is not resumable, so no prompt", async () => {
766
+ insertHistoryRow({ id: "legacy-2", status: "completed", cwd: null });
767
+ steerOrResumeImpl = async (id) => {
768
+ throw new AcpResumeError(
769
+ new Error(`ACP session "${id}" cannot be resumed.`),
770
+ );
771
+ };
772
+
773
+ const handler = getSteerHandler();
774
+ await expect(
775
+ handler({ pathParams: { id: "legacy-2" }, body: { instruction: "go" } }),
776
+ ).rejects.toBeInstanceOf(FailedDependencyError);
777
+ // No spawn would occur, so the gate stays out of the way.
778
+ expect(confirmationRequests).toHaveLength(0);
779
+ });
780
+
781
+ test("steering a session active in memory is not prompted", async () => {
782
+ fakeInMemorySessions = [
783
+ {
784
+ id: "live-2",
785
+ agentId: "claude",
786
+ acpSessionId: "proto-live-2",
787
+ parentConversationId: "conv-1",
788
+ status: "running",
789
+ startedAt: 1000,
790
+ },
791
+ ];
792
+ // A resumable row also exists, but the in-memory session wins → steer.
793
+ insertHistoryRow({ id: "live-2", status: "completed", cwd: "/work/repo" });
794
+
795
+ const handler = getSteerHandler();
796
+ const body = await handler({
797
+ pathParams: { id: "live-2" },
798
+ body: { instruction: "redirect" },
799
+ });
800
+
801
+ expect(confirmationRequests).toHaveLength(0);
802
+ expect(body).toEqual({ acpSessionId: "live-2", steered: true });
803
+ expect(steerOrResumeMock).toHaveBeenCalledTimes(1);
804
+ });
805
+ });
@@ -23,7 +23,7 @@ mock.module("../../assistant-event-hub.js", () => ({
23
23
  broadcastMessage: () => {},
24
24
  }));
25
25
 
26
- import { findConversation } from "../../../daemon/conversation-store.js";
26
+ import { findConversation } from "../../../daemon/conversation-registry.js";
27
27
  import { createConversation } from "../../../memory/conversation-crud.js";
28
28
  import { getDb } from "../../../memory/db-connection.js";
29
29
  import { initializeDb } from "../../../memory/db-init.js";