@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea

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 (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  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__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -20,56 +20,91 @@
20
20
  * owns the plaintext read boundary.
21
21
  */
22
22
 
23
- import { basename } from "node:path";
24
-
25
23
  import { FailedDependencyError } from "../runtime/routes/errors.js";
26
24
  import { credentialBroker } from "../tools/credentials/broker.js";
27
25
  import {
28
26
  getCredentialMetadata,
29
27
  upsertCredentialMetadata,
30
28
  } from "../tools/credentials/metadata-store.js";
29
+ import { getLogger } from "../util/logger.js";
30
+ import { adapterCommandOf } from "./resolve-agent.js";
31
31
  import type { AcpAgentConfig } from "./types.js";
32
32
 
33
+ const log = getLogger("acp:prepare-agent-env");
34
+
33
35
  const ACP_SPAWN_TOOL = "acp_spawn";
36
+ const ACP_SERVICE = "acp";
34
37
 
35
38
  /**
36
- * Ensure the `acp/claude_oauth_token` credential has metadata that allows
37
- * the `acp_spawn` tool to read it, but only for legacy/unmanaged cases:
39
+ * Ensure an `acp/<field>` credential has metadata that allows the
40
+ * `acp_spawn` tool to read it, but only for legacy/unmanaged cases:
38
41
  *
39
- * - No metadata at all create with `allowedTools: ["acp_spawn"]`.
40
- * - Metadata exists with an empty `allowedTools` default provisioning
42
+ * - No metadata at all: create with `allowedTools: ["acp_spawn"]`.
43
+ * - Metadata exists with an empty `allowedTools`: default provisioning
41
44
  * path (user ran `credentials set` without `--allowed-tools`), add it.
42
- * - Metadata exists with a non-empty `allowedTools` explicit policy set
43
- * by the user/admin. Respect it even if `acp_spawn` is absent the
44
- * broker will deny the read and the preflight will throw.
45
+ * - Metadata exists with a non-empty `allowedTools`: explicit policy set
46
+ * by the user/admin. Respect it even if `acp_spawn` is absent; the
47
+ * broker will deny the read and the caller decides whether that's fatal.
45
48
  */
46
- function ensureAcpTokenPolicy(): void {
47
- const meta = getCredentialMetadata("acp", "claude_oauth_token");
49
+ function ensureAcpCredentialPolicy(
50
+ field: string,
51
+ usageDescription: string,
52
+ ): void {
53
+ const meta = getCredentialMetadata(ACP_SERVICE, field);
48
54
  if (!meta) {
49
- upsertCredentialMetadata("acp", "claude_oauth_token", {
55
+ upsertCredentialMetadata(ACP_SERVICE, field, {
50
56
  allowedTools: [ACP_SPAWN_TOOL],
51
- usageDescription:
52
- "Claude OAuth token for ACP agent authentication",
57
+ usageDescription,
53
58
  });
54
59
  return;
55
60
  }
56
61
  const tools = meta.allowedTools ?? [];
57
62
  if (tools.length === 0) {
58
- upsertCredentialMetadata("acp", "claude_oauth_token", {
63
+ upsertCredentialMetadata(ACP_SERVICE, field, {
59
64
  allowedTools: [ACP_SPAWN_TOOL],
60
65
  });
61
66
  }
62
67
  }
63
68
 
69
+ /**
70
+ * Read an `acp/<field>` credential through the broker and inject it into
71
+ * `env` under `envVar`. Returns the broker's failure reason when the value
72
+ * was not injected (missing credential, denied policy, no stored value),
73
+ * or undefined on success. Never throws: `serverUse` signals every failure
74
+ * mode, including a simply-absent credential, as `{ success: false,
75
+ * reason }`, so callers choose whether a miss is fatal.
76
+ */
77
+ async function injectCredential(
78
+ env: Record<string, string>,
79
+ field: string,
80
+ envVar: string,
81
+ usageDescription: string,
82
+ ): Promise<string | undefined> {
83
+ ensureAcpCredentialPolicy(field, usageDescription);
84
+ const result = await credentialBroker.serverUse<void>({
85
+ service: ACP_SERVICE,
86
+ field,
87
+ toolName: ACP_SPAWN_TOOL,
88
+ execute: async (value) => {
89
+ env[envVar] = value;
90
+ },
91
+ });
92
+ return result.success ? undefined : result.reason;
93
+ }
94
+
64
95
  /**
65
96
  * Returns a NEW config with any required credentials merged into `env`.
66
97
  * Does NOT mutate the input. Throws `FailedDependencyError` if a required
67
98
  * credential is missing from both the user-supplied env override and the
68
99
  * secure store.
69
100
  *
70
- * Gating is keyed off the resolved agent COMMAND (basename), not the
71
- * user-facing agent id, so a custom `acp.agents.my-claude = { command:
72
- * "claude-agent-acp", ... }` alias still gets the env it needs.
101
+ * Gating is keyed off the canonical adapter identity (`adapterCommand` set
102
+ * by the resolver, falling back to the command basename for plain configs),
103
+ * not the user-facing agent id. A custom `acp.agents.my-claude = { command:
104
+ * "claude-agent-acp", ... }` alias still gets the env it needs, and so does
105
+ * the bunx-rewritten claude adapter (whose `command` is "bun"). Without
106
+ * the adapterCommand gate, bunx-resolved spawns would start with no auth
107
+ * and die as zombies on the first prompt.
73
108
  *
74
109
  * For `claude-agent-acp` the only required env var is
75
110
  * `CLAUDE_CODE_OAUTH_TOKEN`. Two provisioning routes converge on it, with
@@ -84,6 +119,11 @@ function ensureAcpTokenPolicy(): void {
84
119
  * before spawning. The "fail-fast" throw is symmetric with the existing
85
120
  * `binary_not_found` preflight in `resolveAcpAgent` and strictly better
86
121
  * than a `warn` + zombie subprocess 10 seconds later.
122
+ *
123
+ * For `gemini` the env var is `GEMINI_API_KEY`, provisioned the same two
124
+ * ways (config.json override wins, vault field `gemini_api_key` second),
125
+ * but it is OPTIONAL: the Gemini CLI supports its own OAuth login, so a
126
+ * vault miss proceeds without the key instead of failing the spawn.
87
127
  */
88
128
  export async function prepareAgentEnv(
89
129
  agentConfig: AcpAgentConfig,
@@ -92,19 +132,16 @@ export async function prepareAgentEnv(
92
132
  // agent reference. The local `env` binding sidesteps TS narrowing
93
133
  // limitations on the optional `AcpAgentConfig.env` field.
94
134
  const env: Record<string, string> = { ...(agentConfig.env ?? {}) };
95
- const commandBasename = basename(agentConfig.command);
135
+ const adapterCommand = adapterCommandOf(agentConfig);
96
136
 
97
- if (commandBasename === "claude-agent-acp") {
137
+ if (adapterCommand === "claude-agent-acp") {
98
138
  if (!env.CLAUDE_CODE_OAUTH_TOKEN) {
99
- ensureAcpTokenPolicy();
100
- await credentialBroker.serverUse<void>({
101
- service: "acp",
102
- field: "claude_oauth_token",
103
- toolName: ACP_SPAWN_TOOL,
104
- execute: async (token) => {
105
- env.CLAUDE_CODE_OAUTH_TOKEN = token;
106
- },
107
- });
139
+ await injectCredential(
140
+ env,
141
+ "claude_oauth_token",
142
+ "CLAUDE_CODE_OAUTH_TOKEN",
143
+ "Claude OAuth token for ACP agent authentication",
144
+ );
108
145
  }
109
146
  if (!env.CLAUDE_CODE_OAUTH_TOKEN) {
110
147
  throw new FailedDependencyError(
@@ -113,6 +150,23 @@ export async function prepareAgentEnv(
113
150
  "(or set it under acp.agents.<id>.env in config.json).",
114
151
  );
115
152
  }
153
+ } else if (adapterCommand === "gemini") {
154
+ if (!env.GEMINI_API_KEY) {
155
+ const missReason = await injectCredential(
156
+ env,
157
+ "gemini_api_key",
158
+ "GEMINI_API_KEY",
159
+ "Gemini API key for ACP agent authentication",
160
+ );
161
+ if (missReason !== undefined) {
162
+ // Optional credential: Gemini CLI can authenticate via its own
163
+ // OAuth login, so a vault miss must not fail the spawn.
164
+ log.debug(
165
+ { reason: missReason },
166
+ "Gemini API key unavailable from the vault; spawning without GEMINI_API_KEY",
167
+ );
168
+ }
169
+ }
116
170
  }
117
171
 
118
172
  return { ...agentConfig, env };
@@ -1,19 +1,26 @@
1
1
  import { afterAll, beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
+ import { setOverridesForTesting } from "../__tests__/feature-flag-test-helpers.js";
3
4
  import { installAcpConfigStub } from "./__tests__/helpers/acp-config-stub.js";
4
5
  import { installWhichStub } from "./__tests__/helpers/which-stub.js";
6
+ import { ACP_FLAG_KEY } from "./feature-gate.js";
5
7
 
6
8
  const config = await installAcpConfigStub();
7
9
  const which = installWhichStub();
8
10
 
9
11
  afterAll(() => {
10
12
  which.restore();
13
+ setOverridesForTesting({});
11
14
  });
12
15
 
13
- const { resolveAcpAgent, listAcpAgents } = await import("./resolve-agent.js");
16
+ const { resolveAcpAgent, listAcpAgents, adapterCommandOf, runsViaBunx } =
17
+ await import("./resolve-agent.js");
14
18
 
15
19
  beforeEach(() => {
16
20
  config.setConfig({});
21
+ // Default: no flag overrides, so the `acp` flag falls back to its registry
22
+ // default (false) and enablement comes from the config stub alone.
23
+ setOverridesForTesting({});
17
24
  // Default: every command on PATH so binary preflight passes unless a test
18
25
  // explicitly says otherwise.
19
26
  which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
@@ -24,7 +31,7 @@ beforeEach(() => {
24
31
  // ---------------------------------------------------------------------------
25
32
 
26
33
  describe("resolveAcpAgent", () => {
27
- test("returns acp_disabled when config.acp.enabled is false", () => {
34
+ test("returns acp_disabled when both the feature flag and config.acp.enabled are off", () => {
28
35
  config.setConfig({ enabled: false });
29
36
 
30
37
  const result = resolveAcpAgent("claude");
@@ -33,10 +40,23 @@ describe("resolveAcpAgent", () => {
33
40
  if (result.ok) return;
34
41
  expect(result.reason).toBe("acp_disabled");
35
42
  if (result.reason !== "acp_disabled") return;
43
+ expect(result.hint).toContain("ACP Coding Agents");
44
+ expect(result.hint).toContain("feature flag");
36
45
  expect(result.hint).toContain("acp.enabled");
37
46
  expect(result.hint).toContain("config.json");
38
47
  });
39
48
 
49
+ test("resolution proceeds when the acp feature flag is on and config.acp.enabled is false", () => {
50
+ config.setConfig({ enabled: false });
51
+ setOverridesForTesting({ [ACP_FLAG_KEY]: true });
52
+
53
+ const result = resolveAcpAgent("claude");
54
+
55
+ expect(result.ok).toBe(true);
56
+ if (!result.ok) return;
57
+ expect(result.agent.command).toBe("claude-agent-acp");
58
+ });
59
+
40
60
  test("user config wins over default profile", () => {
41
61
  config.setConfig({
42
62
  agents: {
@@ -78,6 +98,79 @@ describe("resolveAcpAgent", () => {
78
98
  expect(result.agent.command).toBe("claude-agent-acp");
79
99
  });
80
100
 
101
+ test("falls back to the gemini default profile when no user entry", () => {
102
+ config.setConfig({ agents: {} });
103
+
104
+ const result = resolveAcpAgent("gemini");
105
+
106
+ expect(result.ok).toBe(true);
107
+ if (!result.ok) return;
108
+ expect(result.agent.command).toBe("gemini");
109
+ expect(result.agent.args).toEqual(["--acp"]);
110
+ });
111
+
112
+ test.each([
113
+ ["claude code", "claude-agent-acp"],
114
+ ["Claude Code", "claude-agent-acp"],
115
+ ["claude-code", "claude-agent-acp"],
116
+ ["claude_code", "claude-agent-acp"],
117
+ ["codex cli", "codex-acp"],
118
+ ["OpenAI Codex", "codex-acp"],
119
+ ["gemini cli", "gemini"],
120
+ ["Gemini CLI", "gemini"],
121
+ ["google gemini", "gemini"],
122
+ ])("alias %p resolves to the %p profile", (alias, command) => {
123
+ config.setConfig({ agents: {} });
124
+
125
+ const result = resolveAcpAgent(alias);
126
+
127
+ expect(result.ok).toBe(true);
128
+ if (!result.ok) return;
129
+ expect(result.agent.command).toBe(command);
130
+ });
131
+
132
+ test("user config entry literally keyed 'claude code' beats the alias", () => {
133
+ config.setConfig({
134
+ agents: {
135
+ "claude code": {
136
+ command: "my-claude-fork",
137
+ args: [],
138
+ description: "user-defined agent that happens to share an alias",
139
+ },
140
+ },
141
+ });
142
+
143
+ const result = resolveAcpAgent("claude code");
144
+
145
+ expect(result.ok).toBe(true);
146
+ if (!result.ok) return;
147
+ expect(result.agent.command).toBe("my-claude-fork");
148
+ });
149
+
150
+ test("alias re-runs the normal lookup, so a user override of the canonical id wins", () => {
151
+ config.setConfig({
152
+ agents: {
153
+ claude: { command: "my-custom-claude", args: [] },
154
+ },
155
+ });
156
+
157
+ const result = resolveAcpAgent("claude code");
158
+
159
+ expect(result.ok).toBe(true);
160
+ if (!result.ok) return;
161
+ expect(result.agent.command).toBe("my-custom-claude");
162
+ });
163
+
164
+ test("non-alias unknown id still returns unknown_agent", () => {
165
+ config.setConfig({ agents: {} });
166
+
167
+ const result = resolveAcpAgent("cursor cli");
168
+
169
+ expect(result.ok).toBe(false);
170
+ if (result.ok) return;
171
+ expect(result.reason).toBe("unknown_agent");
172
+ });
173
+
81
174
  test("returns unknown_agent with merged available list when id not found", () => {
82
175
  config.setConfig({
83
176
  agents: {
@@ -92,7 +185,12 @@ describe("resolveAcpAgent", () => {
92
185
  expect(result.reason).toBe("unknown_agent");
93
186
  if (result.reason !== "unknown_agent") return;
94
187
  // Defaults plus user-only ids, deduped, in stable order (defaults first).
95
- expect(result.available).toEqual(["claude", "codex", "user-only"]);
188
+ expect(result.available).toEqual([
189
+ "claude",
190
+ "codex",
191
+ "gemini",
192
+ "user-only",
193
+ ]);
96
194
  });
97
195
 
98
196
  test("unknown_agent available list contains both defaults when user config is empty", () => {
@@ -106,6 +204,7 @@ describe("resolveAcpAgent", () => {
106
204
  if (result.reason !== "unknown_agent") return;
107
205
  expect(result.available).toContain("claude");
108
206
  expect(result.available).toContain("codex");
207
+ expect(result.available).toContain("gemini");
109
208
  });
110
209
 
111
210
  test("returns binary_not_found with the registered install hint", () => {
@@ -200,6 +299,158 @@ describe("resolveAcpAgent", () => {
200
299
  if (!result.ok) return;
201
300
  expect(result.agent.args).toEqual(["--verbose"]);
202
301
  });
302
+
303
+ test("direct resolution sets adapterCommand to the command basename", () => {
304
+ config.setConfig({
305
+ agents: {
306
+ custom: { command: "/opt/bin/claude-agent-acp", args: [] },
307
+ },
308
+ });
309
+
310
+ const direct = resolveAcpAgent("claude");
311
+ expect(direct.ok).toBe(true);
312
+ if (!direct.ok) return;
313
+ expect(direct.agent.adapterCommand).toBe("claude-agent-acp");
314
+ expect(runsViaBunx(direct.agent)).toBe(false);
315
+
316
+ const fullPath = resolveAcpAgent("custom");
317
+ expect(fullPath.ok).toBe(true);
318
+ if (!fullPath.ok) return;
319
+ expect(fullPath.agent.adapterCommand).toBe("claude-agent-acp");
320
+ });
321
+ });
322
+
323
+ // ---------------------------------------------------------------------------
324
+ // resolveAcpAgent - bunx fallback for missing binaries
325
+ // ---------------------------------------------------------------------------
326
+
327
+ describe("resolveAcpAgent - bunx fallback", () => {
328
+ test("binary missing + bun present: rewrites to `bun x --bun <pkg>` with adapterCommand preserved", () => {
329
+ config.setConfig({ agents: {} });
330
+ which.setWhich({ bun: "/usr/local/bin/bun" });
331
+
332
+ const result = resolveAcpAgent("claude");
333
+
334
+ expect(result.ok).toBe(true);
335
+ if (!result.ok) return;
336
+ expect(result.agent.command).toBe("bun");
337
+ expect(result.agent.args).toEqual([
338
+ "x",
339
+ "--bun",
340
+ "@agentclientprotocol/claude-agent-acp",
341
+ ]);
342
+ expect(result.agent.adapterCommand).toBe("claude-agent-acp");
343
+ expect(runsViaBunx(result.agent)).toBe(true);
344
+ expect(adapterCommandOf(result.agent)).toBe("claude-agent-acp");
345
+ });
346
+
347
+ test("bunx rewrite appends the original args after the package (gemini --acp)", () => {
348
+ config.setConfig({ agents: {} });
349
+ which.setWhich({ bun: "/usr/local/bin/bun" });
350
+
351
+ const result = resolveAcpAgent("gemini");
352
+
353
+ expect(result.ok).toBe(true);
354
+ if (!result.ok) return;
355
+ expect(result.agent.command).toBe("bun");
356
+ expect(result.agent.args).toEqual([
357
+ "x",
358
+ "--bun",
359
+ "@google/gemini-cli",
360
+ "--acp",
361
+ ]);
362
+ expect(result.agent.adapterCommand).toBe("gemini");
363
+ });
364
+
365
+ test("bunx rewrite leaves env unchanged", () => {
366
+ config.setConfig({
367
+ agents: {
368
+ claude: {
369
+ command: "claude-agent-acp",
370
+ args: [],
371
+ env: { KEEP_ME: "yes" },
372
+ },
373
+ },
374
+ });
375
+ which.setWhich({ bun: "/usr/local/bin/bun" });
376
+
377
+ const result = resolveAcpAgent("claude");
378
+
379
+ expect(result.ok).toBe(true);
380
+ if (!result.ok) return;
381
+ expect(result.agent.command).toBe("bun");
382
+ expect(result.agent.env).toEqual({ KEEP_ME: "yes" });
383
+ });
384
+
385
+ test("bun lookup honors agent.env.PATH override (matches spawn env)", () => {
386
+ config.setConfig({
387
+ agents: {
388
+ claude: {
389
+ command: "claude-agent-acp",
390
+ args: [],
391
+ env: { PATH: "/opt/custom/bin" },
392
+ },
393
+ },
394
+ });
395
+ which.setWhich((cmd, options) =>
396
+ cmd === "bun" && options?.PATH === "/opt/custom/bin"
397
+ ? "/opt/custom/bin/bun"
398
+ : null,
399
+ );
400
+
401
+ const result = resolveAcpAgent("claude");
402
+
403
+ expect(result.ok).toBe(true);
404
+ if (!result.ok) return;
405
+ expect(result.agent.command).toBe("bun");
406
+ });
407
+
408
+ test("user-config command without a package mapping is NOT rewritten even when bun is present", () => {
409
+ config.setConfig({
410
+ agents: {
411
+ custom: { command: "unknown-binary", args: [] },
412
+ },
413
+ });
414
+ which.setWhich({ bun: "/usr/local/bin/bun" });
415
+
416
+ const result = resolveAcpAgent("custom");
417
+
418
+ expect(result.ok).toBe(false);
419
+ if (result.ok) return;
420
+ expect(result.reason).toBe("binary_not_found");
421
+ });
422
+
423
+ test("binary missing + bun missing: binary_not_found with the npm hint", () => {
424
+ config.setConfig({ agents: {} });
425
+ which.setWhich({});
426
+
427
+ const result = resolveAcpAgent("claude");
428
+
429
+ expect(result.ok).toBe(false);
430
+ if (result.ok) return;
431
+ expect(result.reason).toBe("binary_not_found");
432
+ if (result.reason !== "binary_not_found") return;
433
+ expect(result.hint).toBe("npm i -g @agentclientprotocol/claude-agent-acp");
434
+ });
435
+ });
436
+
437
+ // ---------------------------------------------------------------------------
438
+ // adapterCommandOf - fallback for plain configs
439
+ // ---------------------------------------------------------------------------
440
+
441
+ describe("adapterCommandOf", () => {
442
+ test("falls back to the command basename for configs without adapterCommand", () => {
443
+ expect(adapterCommandOf({ command: "/opt/bin/claude-agent-acp" })).toBe(
444
+ "claude-agent-acp",
445
+ );
446
+ expect(adapterCommandOf({ command: "codex-acp" })).toBe("codex-acp");
447
+ });
448
+
449
+ test("prefers an explicit adapterCommand over the command", () => {
450
+ expect(
451
+ adapterCommandOf({ command: "bun", adapterCommand: "claude-agent-acp" }),
452
+ ).toBe("claude-agent-acp");
453
+ });
203
454
  });
204
455
 
205
456
  // ---------------------------------------------------------------------------
@@ -207,7 +458,7 @@ describe("resolveAcpAgent", () => {
207
458
  // ---------------------------------------------------------------------------
208
459
 
209
460
  describe("listAcpAgents", () => {
210
- test("returns enabled: false with empty agents when ACP is disabled", () => {
461
+ test("returns enabled: false with empty agents when both the flag and config are off", () => {
211
462
  config.setConfig({ enabled: false });
212
463
 
213
464
  const result = listAcpAgents();
@@ -216,14 +467,28 @@ describe("listAcpAgents", () => {
216
467
  expect(result.agents).toEqual([]);
217
468
  });
218
469
 
219
- test("includes both bundled defaults when user config is empty", () => {
470
+ test("returns the catalog when the acp feature flag is on and config.acp.enabled is false", () => {
471
+ config.setConfig({ enabled: false });
472
+ setOverridesForTesting({ [ACP_FLAG_KEY]: true });
473
+
474
+ const result = listAcpAgents();
475
+
476
+ expect(result.enabled).toBe(true);
477
+ expect(result.agents.map((a) => a.id)).toEqual([
478
+ "claude",
479
+ "codex",
480
+ "gemini",
481
+ ]);
482
+ });
483
+
484
+ test("includes all bundled defaults when user config is empty", () => {
220
485
  config.setConfig({ agents: {} });
221
486
 
222
487
  const result = listAcpAgents();
223
488
 
224
489
  expect(result.enabled).toBe(true);
225
490
  const ids = result.agents.map((a) => a.id);
226
- expect(ids).toEqual(["claude", "codex"]);
491
+ expect(ids).toEqual(["claude", "codex", "gemini"]);
227
492
  for (const entry of result.agents) {
228
493
  expect(entry.source).toBe("default");
229
494
  expect(entry.available).toBe(true);
@@ -245,6 +510,7 @@ describe("listAcpAgents", () => {
245
510
  which.setWhich({
246
511
  "my-claude": "/usr/bin/my-claude",
247
512
  "codex-acp": "/usr/bin/codex-acp",
513
+ gemini: "/usr/bin/gemini",
248
514
  });
249
515
 
250
516
  const result = listAcpAgents();
@@ -257,6 +523,25 @@ describe("listAcpAgents", () => {
257
523
  expect(codex?.source).toBe("default");
258
524
  });
259
525
 
526
+ test("missing binaries with bun present are listed available (bunx fallback)", () => {
527
+ config.setConfig({ agents: {} });
528
+ which.setWhich({ bun: "/usr/local/bin/bun" });
529
+
530
+ const result = listAcpAgents();
531
+
532
+ for (const entry of result.agents) {
533
+ expect(entry.available).toBe(true);
534
+ expect(entry.unavailableReason).toBeUndefined();
535
+ expect(entry.setupHint).toBeUndefined();
536
+ }
537
+ // The catalog keeps the canonical adapter commands, not the rewrite.
538
+ expect(result.agents.map((a) => a.command)).toEqual([
539
+ "claude-agent-acp",
540
+ "codex-acp",
541
+ "gemini",
542
+ ]);
543
+ });
544
+
260
545
  test("unavailable agent surfaces install hint derived from DEFAULT_AGENT_NPM_PACKAGES", () => {
261
546
  config.setConfig({ agents: {} });
262
547
  which.setWhich({ "claude-agent-acp": "/usr/bin/claude-agent-acp" });
@@ -269,6 +554,32 @@ describe("listAcpAgents", () => {
269
554
  expect(codex?.setupHint).toBe("npm i -g @zed-industries/codex-acp");
270
555
  });
271
556
 
557
+ test("unavailable gemini surfaces the @google/gemini-cli install hint", () => {
558
+ config.setConfig({ agents: {} });
559
+ which.setWhich({
560
+ "claude-agent-acp": "/usr/bin/claude-agent-acp",
561
+ "codex-acp": "/usr/bin/codex-acp",
562
+ });
563
+
564
+ const result = listAcpAgents();
565
+
566
+ const gemini = result.agents.find((a) => a.id === "gemini");
567
+ expect(gemini?.available).toBe(false);
568
+ expect(gemini?.unavailableReason).toBe("'gemini' is not on PATH");
569
+ expect(gemini?.setupHint).toBe("npm i -g @google/gemini-cli");
570
+ });
571
+
572
+ test("aliases are resolution sugar, not catalog entries", () => {
573
+ config.setConfig({ agents: {} });
574
+
575
+ // "gemini cli" resolves via the alias...
576
+ expect(resolveAcpAgent("gemini cli").ok).toBe(true);
577
+
578
+ // ...but the catalog lists only canonical ids.
579
+ const ids = listAcpAgents().agents.map((a) => a.id);
580
+ expect(ids).toEqual(["claude", "codex", "gemini"]);
581
+ });
582
+
272
583
  test("user-only agent appended after defaults in stable order", () => {
273
584
  config.setConfig({
274
585
  agents: {
@@ -282,6 +593,7 @@ describe("listAcpAgents", () => {
282
593
  which.setWhich({
283
594
  "claude-agent-acp": "/x",
284
595
  "codex-acp": "/x",
596
+ gemini: "/x",
285
597
  "my-binary": "/x",
286
598
  });
287
599
 
@@ -290,9 +602,10 @@ describe("listAcpAgents", () => {
290
602
  expect(result.agents.map((a) => a.id)).toEqual([
291
603
  "claude",
292
604
  "codex",
605
+ "gemini",
293
606
  "my-agent",
294
607
  ]);
295
- const userOnly = result.agents[2];
608
+ const userOnly = result.agents[3];
296
609
  expect(userOnly.source).toBe("config");
297
610
  expect(userOnly.description).toBe("user-only");
298
611
  });