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

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