@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.
Files changed (475) 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 +263 -121
  12. package/package.json +2 -2
  13. package/src/__tests__/activation-early-marking.test.ts +120 -0
  14. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  15. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  16. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  17. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  18. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  19. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  20. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  21. package/src/__tests__/agent-loop.test.ts +441 -96
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  23. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  24. package/src/__tests__/anthropic-provider.test.ts +23 -8
  25. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/app-executors.test.ts +132 -0
  29. package/src/__tests__/approval-cascade.test.ts +6 -5
  30. package/src/__tests__/approval-routes-http.test.ts +4 -1
  31. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  32. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  33. package/src/__tests__/channel-approvals.test.ts +1 -1
  34. package/src/__tests__/compaction-circuit.test.ts +258 -0
  35. package/src/__tests__/compaction-direct.test.ts +146 -0
  36. package/src/__tests__/compaction-events.test.ts +7 -7
  37. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  38. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  39. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  40. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  41. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  42. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  43. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  44. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  45. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  46. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  47. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  48. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  49. package/src/__tests__/conversation-error.test.ts +15 -1
  50. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  52. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  53. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  55. package/src/__tests__/conversation-queue.test.ts +10 -10
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  57. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  58. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  59. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  60. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  61. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  62. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  65. package/src/__tests__/conversation-undo.test.ts +2 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  67. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/corrected-target.test.ts +93 -0
  71. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  72. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  73. package/src/__tests__/db-acp-history.test.ts +101 -0
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +1 -1
  76. package/src/__tests__/file-write-tool.test.ts +63 -0
  77. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  78. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  79. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  80. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  82. package/src/__tests__/heartbeat-service.test.ts +1 -0
  83. package/src/__tests__/history-repair-hook.test.ts +5 -1
  84. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  85. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  86. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  87. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  88. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  89. package/src/__tests__/injector-background-turn.test.ts +13 -23
  90. package/src/__tests__/injector-chain.test.ts +268 -44
  91. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  92. package/src/__tests__/injector-document-comments.test.ts +97 -114
  93. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  94. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  95. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  96. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  97. package/src/__tests__/llm-resolver.test.ts +73 -0
  98. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  99. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  100. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  101. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  102. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  103. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  104. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  105. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  106. package/src/__tests__/plugin-registry.test.ts +3 -49
  107. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  108. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  109. package/src/__tests__/plugin-types.test.ts +0 -70
  110. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  111. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  112. package/src/__tests__/published-app-updater.test.ts +138 -0
  113. package/src/__tests__/reaction-persistence.test.ts +1 -1
  114. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  115. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  116. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  117. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  118. package/src/__tests__/subagent-detail.test.ts +25 -7
  119. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  120. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  121. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  122. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  123. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  124. package/src/__tests__/title-generate-hook.test.ts +5 -1
  125. package/src/__tests__/tool-error-hook.test.ts +1 -1
  126. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  127. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  128. package/src/__tests__/web-fetch.test.ts +45 -0
  129. package/src/acp/__tests__/agent-process.test.ts +161 -0
  130. package/src/acp/__tests__/client-handler.test.ts +40 -0
  131. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  132. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  133. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  134. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  135. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  136. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  137. package/src/acp/agent-process.ts +61 -1
  138. package/src/acp/auto-install.test.ts +280 -0
  139. package/src/acp/auto-install.ts +232 -0
  140. package/src/acp/client-handler.ts +31 -0
  141. package/src/acp/prepare-agent-env.ts +80 -27
  142. package/src/acp/resolve-agent.test.ts +188 -28
  143. package/src/acp/resolve-agent.ts +119 -42
  144. package/src/acp/resume-hint.ts +23 -0
  145. package/src/acp/session-manager.ts +507 -73
  146. package/src/agent/compaction-circuit.ts +60 -102
  147. package/src/agent/loop.ts +403 -251
  148. package/src/api/responses/conversation-message.ts +22 -2
  149. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  150. package/src/approvals/guardian-request-resolvers.ts +27 -1
  151. package/src/background-wake/next-wake.ts +1 -0
  152. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  153. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  154. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  155. package/src/cli/commands/memory-v3.ts +57 -199
  156. package/src/cli/commands/plugins.ts +43 -37
  157. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  158. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  159. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  160. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  161. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  162. package/src/cli/lib/install-from-github.ts +797 -60
  163. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  164. package/src/cli/lib/plugin-details.ts +416 -0
  165. package/src/cli/lib/plugin-marketplace.ts +215 -0
  166. package/src/cli/lib/search-plugins.ts +201 -29
  167. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  168. package/src/config/acp-defaults.test.ts +10 -0
  169. package/src/config/acp-defaults.ts +9 -3
  170. package/src/config/acp-schema.ts +1 -7
  171. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  172. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  173. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  174. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  175. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  176. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  177. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  178. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  180. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  181. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  182. package/src/config/bundled-tool-registry.ts +4 -0
  183. package/src/config/call-site-defaults.ts +0 -3
  184. package/src/config/feature-flag-registry.json +10 -16
  185. package/src/config/llm-resolver.ts +39 -7
  186. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  187. package/src/config/schemas/call-site-catalog.ts +0 -21
  188. package/src/config/schemas/heartbeat.ts +9 -0
  189. package/src/config/schemas/llm.ts +17 -3
  190. package/src/config/schemas/memory-v3.ts +58 -8
  191. package/src/config/seed-inference-profiles.ts +18 -0
  192. package/src/context/compactor.ts +22 -4
  193. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  194. package/src/context/strip-injections.ts +8 -2
  195. package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
  196. package/src/daemon/conversation-agent-loop.ts +308 -1297
  197. package/src/daemon/conversation-error.ts +31 -4
  198. package/src/daemon/conversation-history.ts +1 -1
  199. package/src/daemon/conversation-lifecycle.ts +11 -255
  200. package/src/daemon/conversation-media-retry.ts +19 -6
  201. package/src/daemon/conversation-messaging.ts +17 -0
  202. package/src/daemon/conversation-process.ts +22 -141
  203. package/src/daemon/conversation-queue-manager.ts +8 -0
  204. package/src/daemon/conversation-registry.ts +159 -0
  205. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  206. package/src/daemon/conversation-store.ts +9 -90
  207. package/src/daemon/conversation-surfaces.ts +165 -11
  208. package/src/daemon/conversation-workspace.ts +17 -0
  209. package/src/daemon/conversation.ts +444 -62
  210. package/src/daemon/disk-pressure-policy.ts +0 -1
  211. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  212. package/src/daemon/handlers/config-channels.ts +11 -2
  213. package/src/daemon/handlers/conversations.ts +3 -1
  214. package/src/daemon/handlers/skills.ts +4 -1
  215. package/src/daemon/host-app-control-proxy.ts +72 -57
  216. package/src/daemon/host-proxy-preactivation.ts +1 -3
  217. package/src/daemon/lifecycle.ts +21 -7
  218. package/src/daemon/persist-unsendable-image.ts +62 -25
  219. package/src/daemon/process-message.ts +1 -1
  220. package/src/daemon/server.ts +2 -0
  221. package/src/daemon/tool-side-effects.ts +15 -0
  222. package/src/daemon/wake-conversation-ops.ts +269 -0
  223. package/src/embedded/plugin-api.ts +2 -2
  224. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  225. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  226. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  227. package/src/heartbeat/heartbeat-service.ts +26 -0
  228. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  229. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  230. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  231. package/src/ipc/skill-routes/memory.ts +4 -2
  232. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  233. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  234. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  235. package/src/memory/activation-session-store.ts +43 -0
  236. package/src/memory/conversation-crud.ts +29 -19
  237. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  238. package/src/memory/db-init.ts +6 -0
  239. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  240. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  241. package/src/memory/jobs-worker.ts +25 -1
  242. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  243. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  244. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  245. package/src/memory/migrations/index.ts +3 -0
  246. package/src/memory/onboarding-events-store.ts +66 -18
  247. package/src/memory/schema/acp.ts +4 -0
  248. package/src/memory/schema/infrastructure.ts +13 -0
  249. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  250. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  251. package/src/memory/v2/consolidation-job.ts +2 -63
  252. package/src/memory/v2/types.ts +5 -0
  253. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  254. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  255. package/src/notifications/access-request-copy.ts +298 -0
  256. package/src/notifications/adapters/slack.ts +3 -3
  257. package/src/notifications/adapters/telegram.ts +9 -2
  258. package/src/notifications/copy-composer.ts +49 -267
  259. package/src/notifications/decision-engine.ts +16 -35
  260. package/src/notifications/home-feed-side-effect.ts +1 -6
  261. package/src/plugin-api/constants.ts +4 -0
  262. package/src/plugin-api/index.ts +40 -11
  263. package/src/plugin-api/types.ts +108 -0
  264. package/src/plugins/defaults/compaction/compact.ts +106 -0
  265. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  266. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  267. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  268. package/src/plugins/defaults/compaction/package.json +1 -2
  269. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  270. package/src/plugins/defaults/empty-response/package.json +0 -1
  271. package/src/plugins/defaults/history-repair/package.json +0 -1
  272. package/src/plugins/defaults/index.ts +156 -26
  273. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  274. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  275. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  276. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  277. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  278. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  282. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  283. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  284. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  285. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  286. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  287. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  288. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  289. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  290. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  291. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  293. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  294. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  295. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  296. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  297. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  298. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  299. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  300. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  301. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  302. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  303. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  304. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  306. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  307. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  308. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  309. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  310. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  311. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  312. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  313. package/src/plugins/defaults/title-generate/package.json +0 -1
  314. package/src/plugins/defaults/tool-error/package.json +0 -1
  315. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  316. package/src/plugins/external-api.ts +12 -2
  317. package/src/plugins/pipeline.ts +6 -293
  318. package/src/plugins/registry.ts +9 -37
  319. package/src/plugins/types.ts +76 -381
  320. package/src/plugins/user-loader.ts +30 -127
  321. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  322. package/src/prompts/system-prompt.ts +61 -10
  323. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  324. package/src/providers/anthropic/client.ts +9 -10
  325. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  326. package/src/providers/inference/logit-bias.ts +55 -0
  327. package/src/providers/model-catalog.ts +34 -0
  328. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  329. package/src/providers/openai/chat-completions-provider.ts +44 -1
  330. package/src/providers/retry.ts +22 -0
  331. package/src/providers/types.ts +6 -0
  332. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  333. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  334. package/src/runtime/agent-wake.ts +108 -209
  335. package/src/runtime/assistant-event-hub.ts +1 -1
  336. package/src/runtime/channel-approvals.ts +1 -1
  337. package/src/runtime/interactive-ui.ts +1 -1
  338. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  339. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  340. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  341. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  342. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  343. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  344. package/src/runtime/routes/acp-routes.test.ts +185 -32
  345. package/src/runtime/routes/acp-routes.ts +328 -30
  346. package/src/runtime/routes/app-management-routes.ts +3 -0
  347. package/src/runtime/routes/approval-routes.ts +1 -1
  348. package/src/runtime/routes/browser-routes.ts +1 -1
  349. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  350. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  351. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  352. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  354. package/src/runtime/routes/conversation-routes.ts +30 -2
  355. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  356. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  357. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  358. package/src/runtime/routes/host-cu-routes.ts +1 -1
  359. package/src/runtime/routes/identity-routes.ts +18 -3
  360. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  361. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  362. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  363. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  364. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  365. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  366. package/src/runtime/routes/playground/helpers.ts +1 -1
  367. package/src/runtime/routes/plugins-routes.ts +337 -35
  368. package/src/runtime/routes/stt-routes.ts +45 -12
  369. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  370. package/src/runtime/routes/work-items-routes.ts +2 -4
  371. package/src/runtime/routes/workspace-routes.ts +50 -15
  372. package/src/runtime/services/conversation-serializer.ts +1 -1
  373. package/src/runtime/verification-outbound-actions.ts +147 -2
  374. package/src/runtime/verification-templates.ts +29 -3
  375. package/src/services/published-app-updater.ts +30 -8
  376. package/src/signals/cancel.ts +2 -4
  377. package/src/subagent/manager.ts +21 -5
  378. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  379. package/src/telemetry/activation-funnel.ts +167 -0
  380. package/src/telemetry/types.ts +13 -0
  381. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  382. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  383. package/src/tools/acp/context.ts +20 -0
  384. package/src/tools/acp/list-agents.test.ts +9 -19
  385. package/src/tools/acp/list-agents.ts +3 -15
  386. package/src/tools/acp/spawn.test.ts +173 -202
  387. package/src/tools/acp/spawn.ts +37 -172
  388. package/src/tools/acp/steer.test.ts +105 -8
  389. package/src/tools/acp/steer.ts +48 -17
  390. package/src/tools/apps/executors.ts +166 -50
  391. package/src/tools/browser/browser-execution.ts +12 -2
  392. package/src/tools/filesystem/write.ts +34 -0
  393. package/src/tools/network/web-fetch.ts +65 -24
  394. package/src/tools/skills/load.ts +1 -1
  395. package/src/tools/subagent/spawn.ts +2 -4
  396. package/src/tools/ui-surface/definitions.ts +32 -5
  397. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  398. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  399. package/docs/plugins.md +0 -832
  400. package/examples/plugins/echo/register.ts +0 -143
  401. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  402. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  403. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  404. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  405. package/src/__tests__/pipeline-runner.test.ts +0 -554
  406. package/src/daemon/wake-target-adapter.ts +0 -253
  407. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  408. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  409. package/src/memory/v3/__tests__/core.test.ts +0 -39
  410. package/src/memory/v3/__tests__/health.test.ts +0 -219
  411. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  412. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  413. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  414. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  415. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  416. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  417. package/src/memory/v3/__tests__/router.test.ts +0 -332
  418. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  419. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  420. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  421. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  422. package/src/memory/v3/assign.ts +0 -268
  423. package/src/memory/v3/capabilities.ts +0 -124
  424. package/src/memory/v3/core.ts +0 -26
  425. package/src/memory/v3/health.ts +0 -0
  426. package/src/memory/v3/maintain-job.ts +0 -314
  427. package/src/memory/v3/needle.ts +0 -115
  428. package/src/memory/v3/orchestrate.ts +0 -126
  429. package/src/memory/v3/page-content.ts +0 -34
  430. package/src/memory/v3/provider-blocks.ts +0 -26
  431. package/src/memory/v3/reconcile.ts +0 -523
  432. package/src/memory/v3/router.ts +0 -190
  433. package/src/memory/v3/selection-log-store.ts +0 -84
  434. package/src/memory/v3/selector.ts +0 -226
  435. package/src/memory/v3/shadow-plugin.ts +0 -411
  436. package/src/memory/v3/snapshot.ts +0 -209
  437. package/src/memory/v3/tree.ts +0 -174
  438. package/src/memory/v3/types.ts +0 -59
  439. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  440. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  441. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  442. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  443. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  444. package/src/plugins/defaults/compaction/register.ts +0 -35
  445. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  446. package/src/plugins/defaults/empty-response/register.ts +0 -23
  447. package/src/plugins/defaults/history-repair/register.ts +0 -24
  448. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  449. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  450. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  451. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  452. package/src/plugins/defaults/title-generate/register.ts +0 -35
  453. package/src/plugins/defaults/tool-error/register.ts +0 -23
  454. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  455. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  456. package/src/proactive-artifact/decision.test.ts +0 -226
  457. package/src/proactive-artifact/decision.ts +0 -165
  458. package/src/proactive-artifact/index.ts +0 -7
  459. package/src/proactive-artifact/job.test.ts +0 -962
  460. package/src/proactive-artifact/job.ts +0 -372
  461. package/src/proactive-artifact/message-copy.ts +0 -58
  462. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  463. package/src/proactive-artifact/trigger-state.ts +0 -123
  464. package/src/util/map-limit.ts +0 -27
  465. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  466. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  467. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  468. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  469. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  470. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  471. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  472. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  473. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  474. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  475. /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";