@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,7 +1,7 @@
1
- import * as realChildProcess from "node:child_process";
2
1
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
3
2
 
4
3
  import { installAcpConfigStub } from "../../acp/__tests__/helpers/acp-config-stub.js";
4
+ import { installExecFileStub } from "../../acp/__tests__/helpers/exec-file-stub.js";
5
5
  import { installWhichStub } from "../../acp/__tests__/helpers/which-stub.js";
6
6
  import type { ToolContext } from "../types.js";
7
7
 
@@ -9,58 +9,18 @@ import type { ToolContext } from "../types.js";
9
9
  // Mock infrastructure
10
10
  // ---------------------------------------------------------------------------
11
11
 
12
- type ExecCallback = (
13
- err: Error | null,
14
- stdout: string | Buffer,
15
- stderr: string | Buffer,
16
- ) => void;
17
-
18
- interface ExecScript {
19
- /** When set, the call rejects with this error. */
20
- error?: Error;
21
- /** When set, the call resolves with this stdout. */
22
- stdout?: string;
23
- }
12
+ const {
13
+ execScripts,
14
+ execFileMock,
15
+ reset: resetExecFileStub,
16
+ } = installExecFileStub();
24
17
 
25
- /**
26
- * Per-call scripted responses for `execFile`. Keyed by `${command} ${args[0]}`
27
- * so tests can target `npm ls` and `npm view` independently.
28
- */
29
- const execScripts: Map<string, ExecScript> = new Map();
30
-
31
- const execFileMock = mock(
32
- (
33
- command: string,
34
- args: string[],
35
- _options: unknown,
36
- callback?: ExecCallback,
37
- ) => {
38
- const key = `${command} ${args[0]}`;
39
- const script = execScripts.get(key);
40
- queueMicrotask(() => {
41
- if (!callback) return;
42
- if (!script) {
43
- callback(new Error(`No script for ${key}`), "", "");
44
- return;
45
- }
46
- if (script.error) {
47
- callback(script.error, "", "");
48
- return;
49
- }
50
- callback(null, script.stdout ?? "", "");
51
- });
52
- // Return value is not used by execFileWithTimeout.
53
- return {} as ReturnType<typeof realChildProcess.execFile>;
54
- },
55
- );
56
-
57
- mock.module("node:child_process", () => ({
58
- ...realChildProcess,
59
- execFile: execFileMock,
60
- }));
18
+ /** Fixed resolved `bun` path so install script keys are predictable. */
19
+ const BUN_BIN = "/usr/local/bin/bun";
20
+ const BUN_ADD_KEY = `${BUN_BIN} add`;
61
21
 
62
22
  // Default ACP config used by these tests: the `unknown-agent` entry is here
63
- // to give the "no version check" test a configured agent whose binary isn't
23
+ // to give the "unmapped binary" tests a configured agent whose command isn't
64
24
  // in DEFAULT_AGENT_NPM_PACKAGES.
65
25
  const DEFAULT_TEST_AGENTS = {
66
26
  claude: { command: "claude-agent-acp", args: [] },
@@ -187,8 +147,10 @@ mock.module("../../acp/index.js", () => ({
187
147
  getAcpSessionManager: () => ({ spawn: spawnMock }),
188
148
  }));
189
149
 
190
- const { executeAcpSpawn, _resetAdapterVersionCacheForTests } =
191
- await import("./spawn.js");
150
+ const { executeAcpSpawn } = await import("./spawn.js");
151
+ const { _resetAdapterInstallCacheForTests } = await import(
152
+ "../../acp/auto-install.js"
153
+ );
192
154
 
193
155
  // ---------------------------------------------------------------------------
194
156
  // Helpers
@@ -204,11 +166,12 @@ function makeContext(): ToolContext {
204
166
  }
205
167
 
206
168
  beforeEach(() => {
207
- execScripts.clear();
208
- execFileMock.mockClear();
169
+ resetExecFileStub();
209
170
  spawnMock.mockClear();
210
- _resetAdapterVersionCacheForTests();
171
+ _resetAdapterInstallCacheForTests();
211
172
  config.setConfig({ agents: DEFAULT_TEST_AGENTS });
173
+ // Default: every command (including bun and the adapters) on PATH, so
174
+ // spawns resolve directly with no install.
212
175
  which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
213
176
  // Default: vault has a claude token so the preflight in `prepareAgentEnv`
214
177
  // succeeds for tests that don't care about env injection. Env-injection
@@ -222,135 +185,41 @@ beforeEach(() => {
222
185
  // Tests
223
186
  // ---------------------------------------------------------------------------
224
187
 
225
- describe("executeAcpSpawn — version check", () => {
226
- test("execFile failure: spawn proceeds without warning", async () => {
227
- execScripts.set("npm ls", { error: new Error("npm not installed") });
228
- execScripts.set("npm view", { error: new Error("npm not installed") });
229
-
188
+ describe("executeAcpSpawn - happy path", () => {
189
+ test("binary on PATH: spawns directly with no install and no npm probe", async () => {
230
190
  const result = await executeAcpSpawn(
231
191
  { agent: "claude", task: "do something" },
232
192
  makeContext(),
233
193
  );
234
194
 
235
195
  expect(result.isError).toBe(false);
196
+ // No package manager is ever invoked when the binary is already present.
197
+ expect(execFileMock).not.toHaveBeenCalled();
236
198
  expect(result.content).not.toContain("outdated");
237
- expect(result.content).not.toContain("Note:");
238
- // Spawn was actually invoked.
239
199
  expect(spawnMock).toHaveBeenCalledTimes(1);
240
- // Sanity: payload shape preserved.
241
- const lines = result.content.split("\n\n");
242
- const payload = JSON.parse(lines[0]);
200
+ const payload = JSON.parse(result.content);
243
201
  expect(payload.acpSessionId).toBe("acp-session-test");
244
202
  expect(payload.status).toBe("running");
203
+ // The real adapter binary is spawned (no `bun x` wrapper).
204
+ const agentConfigArg = spawnMock.mock.calls[0][1] as { command: string };
205
+ expect(agentConfigArg.command).toBe("claude-agent-acp");
245
206
  });
246
207
 
247
- test("outdated version: spawn proceeds AND warning appears in content", async () => {
248
- execScripts.set("npm ls", {
249
- stdout: JSON.stringify({
250
- dependencies: {
251
- "@agentclientprotocol/claude-agent-acp": { version: "0.1.0" },
252
- },
253
- }),
254
- });
255
- execScripts.set("npm view", { stdout: "0.2.0\n" });
256
-
257
- const result = await executeAcpSpawn(
258
- { agent: "claude", task: "do something" },
259
- makeContext(),
260
- );
261
-
262
- expect(result.isError).toBe(false);
263
- expect(result.content).toContain("outdated");
264
- expect(result.content).toContain("@agentclientprotocol/claude-agent-acp");
265
- expect(result.content).toContain("0.1.0");
266
- expect(result.content).toContain("0.2.0");
267
- expect(result.content).toContain(
268
- "npm install -g @agentclientprotocol/claude-agent-acp@0.2.0",
269
- );
270
- expect(spawnMock).toHaveBeenCalledTimes(1);
271
- // Payload still parses as JSON before the warning suffix.
272
- const [payloadJson] = result.content.split("\n\n");
273
- const payload = JSON.parse(payloadJson);
274
- expect(payload.acpSessionId).toBe("acp-session-test");
275
- });
276
-
277
- test("up-to-date version: spawn proceeds, no warning", async () => {
278
- execScripts.set("npm ls", {
279
- stdout: JSON.stringify({
280
- dependencies: {
281
- "@agentclientprotocol/claude-agent-acp": { version: "0.2.0" },
282
- },
283
- }),
284
- });
285
- execScripts.set("npm view", { stdout: "0.2.0\n" });
286
-
287
- const result = await executeAcpSpawn(
288
- { agent: "claude", task: "do something" },
289
- makeContext(),
290
- );
291
-
292
- expect(result.isError).toBe(false);
293
- expect(result.content).not.toContain("outdated");
294
- expect(spawnMock).toHaveBeenCalledTimes(1);
295
- });
296
-
297
- test("unknown command: no version check is performed", async () => {
298
- // No execScripts set — if the implementation tried to run npm, the
299
- // mock would callback with "No script for ..." and we could detect
300
- // the failure. But since the registry doesn't include this command,
301
- // the implementation should skip the check entirely without calling
302
- // execFile.
303
- execFileMock.mockClear();
208
+ test("default-profile fallback when user config is empty", async () => {
209
+ // No user `agents.codex` entry, but `agent: "codex"` works via the bundled
210
+ // default profile (command: "codex-acp"). The resolver merges defaults
211
+ // automatically.
212
+ config.setConfig({ agents: {} });
304
213
 
305
214
  const result = await executeAcpSpawn(
306
- { agent: "unknown-agent", task: "do something" },
215
+ { agent: "codex", task: "do something" },
307
216
  makeContext(),
308
217
  );
309
218
 
310
219
  expect(result.isError).toBe(false);
311
- expect(execFileMock).not.toHaveBeenCalled();
312
220
  expect(spawnMock).toHaveBeenCalledTimes(1);
313
- // No outdated note suffix.
314
- expect(result.content).not.toContain("outdated");
315
- });
316
-
317
- test("cached null result: second call does not reprobe", async () => {
318
- execScripts.set("npm ls", { error: new Error("npm not installed") });
319
- execScripts.set("npm view", { error: new Error("npm not installed") });
320
-
321
- await executeAcpSpawn({ agent: "claude", task: "task 1" }, makeContext());
322
- const firstCallCount = execFileMock.mock.calls.length;
323
- expect(firstCallCount).toBeGreaterThan(0);
324
-
325
- await executeAcpSpawn({ agent: "claude", task: "task 2" }, makeContext());
326
- // No additional execFile calls — result was cached.
327
- expect(execFileMock.mock.calls.length).toBe(firstCallCount);
328
- });
329
-
330
- test("cached outdated result: second call does not reprobe but still warns", async () => {
331
- execScripts.set("npm ls", {
332
- stdout: JSON.stringify({
333
- dependencies: {
334
- "@agentclientprotocol/claude-agent-acp": { version: "0.1.0" },
335
- },
336
- }),
337
- });
338
- execScripts.set("npm view", { stdout: "0.2.0\n" });
339
-
340
- const first = await executeAcpSpawn(
341
- { agent: "claude", task: "task 1" },
342
- makeContext(),
343
- );
344
- expect(first.content).toContain("outdated");
345
- const firstCallCount = execFileMock.mock.calls.length;
346
-
347
- const second = await executeAcpSpawn(
348
- { agent: "claude", task: "task 2" },
349
- makeContext(),
350
- );
351
- expect(second.content).toContain("outdated");
352
- // No additional execFile calls — outdated info was cached.
353
- expect(execFileMock.mock.calls.length).toBe(firstCallCount);
221
+ const agentConfigArg = spawnMock.mock.calls[0][1] as { command: string };
222
+ expect(agentConfigArg.command).toBe("codex-acp");
354
223
  });
355
224
  });
356
225
 
@@ -381,84 +250,190 @@ describe("executeAcpSpawn — input validation", () => {
381
250
  expect(result.content).toContain("Available:");
382
251
  });
383
252
 
384
- test("acp disabled returns error with config hint", async () => {
385
- config.setConfig({ enabled: false, agents: DEFAULT_TEST_AGENTS });
253
+ test("missing binary + bun absent returns install hint, no install attempted", async () => {
254
+ which.setWhich({}); // neither bun nor the adapter on PATH
386
255
  const result = await executeAcpSpawn(
387
256
  { agent: "claude", task: "do something" },
388
257
  makeContext(),
389
258
  );
390
259
  expect(result.isError).toBe(true);
391
- expect(result.content).toContain("acp.enabled");
260
+ expect(result.content).toContain("claude-agent-acp is not on PATH");
261
+ expect(result.content).toContain(
262
+ "bun add -g @agentclientprotocol/claude-agent-acp",
263
+ );
264
+ expect(execFileMock).not.toHaveBeenCalled();
265
+ expect(spawnMock).not.toHaveBeenCalled();
392
266
  });
267
+ });
268
+
269
+ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () => {
270
+ test("known command + bun present: installs via bun and spawn proceeds with a note", async () => {
271
+ // Only bun is on PATH until `bun add --global` runs, simulating a
272
+ // successful global install that links the adapter bin onto PATH.
273
+ let binaryOnPath = false;
274
+ which.setWhich((cmd) => {
275
+ if (cmd === "bun") return BUN_BIN;
276
+ if (binaryOnPath) return `/usr/local/bin/${cmd}`;
277
+ return null;
278
+ });
279
+ execScripts.set(BUN_ADD_KEY, {
280
+ stdout: "",
281
+ onCall: () => {
282
+ binaryOnPath = true;
283
+ },
284
+ });
393
285
 
394
- test("missing binary returns install hint", async () => {
395
- which.setWhich({});
396
286
  const result = await executeAcpSpawn(
397
287
  { agent: "claude", task: "do something" },
398
288
  makeContext(),
399
289
  );
290
+
291
+ expect(result.isError).toBe(false);
292
+ expect(spawnMock).toHaveBeenCalledTimes(1);
293
+ const payload = JSON.parse(result.content);
294
+ expect(payload.message).toContain(
295
+ "Installed @agentclientprotocol/claude-agent-acp automatically.",
296
+ );
297
+ // The real binary was spawned with cwd = the project dir and token
298
+ // injected (trusted-binary config, no resolution at spawn).
299
+ const agentConfigArg = spawnMock.mock.calls[0][1] as {
300
+ command: string;
301
+ env?: Record<string, string>;
302
+ };
303
+ expect(agentConfigArg.command).toBe("claude-agent-acp");
304
+ expect(agentConfigArg.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
305
+ "default-test-token",
306
+ );
307
+ expect(spawnMock.mock.calls[0][3]).toBe("/tmp");
308
+
309
+ // Exactly one install, and it was `bun add --global` (never npm).
310
+ expect(execFileMock).toHaveBeenCalledTimes(1);
311
+ const [command, args] = execFileMock.mock.calls[0];
312
+ expect(command).toBe(BUN_BIN);
313
+ expect(args).toEqual([
314
+ "add",
315
+ "--global",
316
+ "@agentclientprotocol/claude-agent-acp",
317
+ ]);
318
+ });
319
+
320
+ test("the installer cwd is a temp dir (not the project cwd) with secrets stripped", async () => {
321
+ let binaryOnPath = false;
322
+ which.setWhich((cmd) => {
323
+ if (cmd === "bun") return BUN_BIN;
324
+ if (binaryOnPath) return `/usr/local/bin/${cmd}`;
325
+ return null;
326
+ });
327
+ execScripts.set(BUN_ADD_KEY, {
328
+ stdout: "",
329
+ onCall: () => {
330
+ binaryOnPath = true;
331
+ },
332
+ });
333
+
334
+ await executeAcpSpawn(
335
+ { agent: "claude", task: "do something", cwd: "/untrusted/project" },
336
+ makeContext(),
337
+ );
338
+
339
+ const options = execFileMock.mock.calls[0][2] as {
340
+ cwd?: string;
341
+ env?: NodeJS.ProcessEnv;
342
+ };
343
+ expect(options.cwd).toBeDefined();
344
+ expect(options.cwd).not.toBe("/untrusted/project");
345
+ expect(options.cwd).toContain("vellum-acp-install-");
346
+ expect(options.env?.CLAUDE_CODE_OAUTH_TOKEN).toBeUndefined();
347
+ expect(options.env?.GEMINI_API_KEY).toBeUndefined();
348
+ expect(options.env?.BUN_CONFIG_REGISTRY).toBe(
349
+ "https://registry.npmjs.org/",
350
+ );
351
+ });
352
+
353
+ test("unmapped command: no install attempted, plain hint returned", async () => {
354
+ which.setWhich({});
355
+
356
+ const result = await executeAcpSpawn(
357
+ { agent: "unknown-agent", task: "do something" },
358
+ makeContext(),
359
+ );
360
+
400
361
  expect(result.isError).toBe(true);
401
- expect(result.content).toContain("claude-agent-acp is not on PATH");
402
- expect(result.content).toContain(
403
- "npm i -g @agentclientprotocol/claude-agent-acp",
362
+ expect(result.content).toContain("some-other-binary is not on PATH");
363
+ expect(result.content).toContain("Install 'some-other-binary'");
364
+ expect(result.content).not.toContain("auto-install failed");
365
+ expect(execFileMock).not.toHaveBeenCalled();
366
+ expect(spawnMock).not.toHaveBeenCalled();
367
+ });
368
+
369
+ test("no client connected: no install attempted even when the binary is missing", async () => {
370
+ // The no-client guard is a pure precondition and must run BEFORE the
371
+ // auto-install side effect: without a client the spawn fails anyway, so
372
+ // the host must not be mutated by a global install (which can also block
373
+ // for up to the install timeout).
374
+ which.setWhich({ bun: BUN_BIN });
375
+
376
+ const result = await executeAcpSpawn(
377
+ { agent: "claude", task: "do something" },
378
+ { ...makeContext(), sendToClient: undefined },
404
379
  );
380
+
381
+ expect(result.isError).toBe(true);
382
+ expect(result.content).toContain("No client connected");
383
+ expect(execFileMock).not.toHaveBeenCalled();
405
384
  expect(spawnMock).not.toHaveBeenCalled();
406
385
  });
407
386
 
408
- test("default-profile fallback when user config is empty", async () => {
409
- // No user `agents.codex` entry, but `agent: "codex"` works via the bundled
410
- // default profile (command: "codex-acp"). The resolver merges defaults
411
- // automatically.
412
- config.setConfig({ agents: {} });
413
- execScripts.set("npm ls", { error: new Error("npm not installed") });
414
- execScripts.set("npm view", { error: new Error("npm not installed") });
387
+ test("install failure: hint and install failure both surface, never npm", async () => {
388
+ which.setWhich({ bun: BUN_BIN }); // bun present, adapter never appears
389
+ execScripts.set(BUN_ADD_KEY, {
390
+ error: new Error("EACCES: permission denied"),
391
+ });
415
392
 
416
393
  const result = await executeAcpSpawn(
417
- { agent: "codex", task: "do something" },
394
+ { agent: "claude", task: "do something" },
418
395
  makeContext(),
419
396
  );
420
397
 
421
- expect(result.isError).toBe(false);
422
- expect(spawnMock).toHaveBeenCalledTimes(1);
423
- // The agentConfig handed to spawn() should be the bundled default.
424
- const agentConfigArg = spawnMock.mock.calls[0][1] as { command: string };
425
- expect(agentConfigArg.command).toBe("codex-acp");
398
+ expect(result.isError).toBe(true);
399
+ expect(result.content).toContain("claude-agent-acp is not on PATH");
400
+ expect(result.content).toContain(
401
+ "bun add -g @agentclientprotocol/claude-agent-acp",
402
+ );
403
+ expect(result.content).toContain("auto-install failed");
404
+ expect(result.content).toContain("EACCES");
405
+ for (const call of execFileMock.mock.calls) {
406
+ expect(call[0]).not.toBe("npm");
407
+ }
408
+ expect(spawnMock).not.toHaveBeenCalled();
426
409
  });
427
410
  });
428
411
 
429
412
  describe("executeAcpSpawn — per-agent resume hint", () => {
430
413
  test("claude payload includes the `claude --resume` hint", async () => {
431
- execScripts.set("npm ls", { error: new Error("npm not installed") });
432
- execScripts.set("npm view", { error: new Error("npm not installed") });
433
-
434
414
  const result = await executeAcpSpawn(
435
415
  { agent: "claude", task: "do something" },
436
416
  makeContext(),
437
417
  );
438
418
 
439
419
  expect(result.isError).toBe(false);
440
- const [payloadJson] = result.content.split("\n\n");
441
- const payload = JSON.parse(payloadJson);
420
+ const payload = JSON.parse(result.content);
442
421
  expect(payload.message).toContain("claude --resume");
443
- expect(payload.message).toContain("To resume this session later");
422
+ expect(payload.message).toContain("To resume:");
444
423
  });
445
424
 
446
425
  test("non-claude payload omits the `claude --resume` hint", async () => {
447
426
  // `claude --resume <id>` is Claude Code-specific. Codex (and any other
448
427
  // adapter) should not have that command suggested back to the user.
449
- execScripts.set("npm ls", { error: new Error("npm not installed") });
450
- execScripts.set("npm view", { error: new Error("npm not installed") });
451
-
452
428
  const result = await executeAcpSpawn(
453
429
  { agent: "codex", task: "do something" },
454
430
  makeContext(),
455
431
  );
456
432
 
457
433
  expect(result.isError).toBe(false);
458
- const [payloadJson] = result.content.split("\n\n");
459
- const payload = JSON.parse(payloadJson);
434
+ const payload = JSON.parse(result.content);
460
435
  expect(payload.message).not.toContain("claude --resume");
461
- expect(payload.message).not.toContain("To resume this session later");
436
+ expect(payload.message).not.toContain("To resume:");
462
437
  });
463
438
  });
464
439
 
@@ -482,8 +457,6 @@ describe("executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
482
457
  vaultStore.clear();
483
458
  metadataStore.clear();
484
459
  vaultStore.set("acp/claude_oauth_token", "tool-vault-token-abc");
485
- execScripts.set("npm ls", { error: new Error("npm not installed") });
486
- execScripts.set("npm view", { error: new Error("npm not installed") });
487
460
 
488
461
  const result = await executeAcpSpawn(
489
462
  { agent: "claude", task: "do something" },
@@ -514,8 +487,6 @@ describe("executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
514
487
  "unknown-agent": { command: "some-other-binary", args: [] },
515
488
  },
516
489
  });
517
- execScripts.set("npm ls", { error: new Error("npm not installed") });
518
- execScripts.set("npm view", { error: new Error("npm not installed") });
519
490
 
520
491
  const result = await executeAcpSpawn(
521
492
  { agent: "claude", task: "do something" },