@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
@@ -15,6 +15,7 @@ import {
15
15
  getPlatformUserId,
16
16
  } from "../config/env.js";
17
17
  import { getConfig } from "../config/loader.js";
18
+ import { queryUnreportedAuthFallbackEvents } from "../memory/auth-fallback-events-store.js";
18
19
  import {
19
20
  getMemoryCheckpoint,
20
21
  setMemoryCheckpoint,
@@ -47,6 +48,10 @@ const CHECKPOINT_KEY_ONBOARDING_WATERMARK =
47
48
  "telemetry:onboarding:last_reported_at";
48
49
  const CHECKPOINT_KEY_ONBOARDING_WATERMARK_ID =
49
50
  "telemetry:onboarding:last_reported_id";
51
+ const CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK =
52
+ "telemetry:auth_fallback:last_reported_at";
53
+ const CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK_ID =
54
+ "telemetry:auth_fallback:last_reported_id";
50
55
  const REPORT_INTERVAL_MS = 5 * 60 * 1000;
51
56
  const INITIAL_FLUSH_DELAY_MS = 30_000; // Delay first flush to let CES handshake complete
52
57
  const BATCH_SIZE = 500;
@@ -139,6 +144,7 @@ export class UsageTelemetryReporter {
139
144
  setMemoryCheckpoint(CHECKPOINT_KEY_TURN_WATERMARK, now);
140
145
  setMemoryCheckpoint(CHECKPOINT_KEY_LIFECYCLE_WATERMARK, now);
141
146
  setMemoryCheckpoint(CHECKPOINT_KEY_ONBOARDING_WATERMARK, now);
147
+ setMemoryCheckpoint(CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK, now);
142
148
  return;
143
149
  }
144
150
 
@@ -171,6 +177,14 @@ export class UsageTelemetryReporter {
171
177
  getMemoryCheckpoint(CHECKPOINT_KEY_ONBOARDING_WATERMARK_ID) ??
172
178
  undefined;
173
179
 
180
+ // Read auth-fallback watermark (compound cursor: createdAt + id)
181
+ const authFallbackWatermark = Number(
182
+ getMemoryCheckpoint(CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK) ?? "0",
183
+ );
184
+ const authFallbackWatermarkId =
185
+ getMemoryCheckpoint(CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK_ID) ??
186
+ undefined;
187
+
174
188
  // Query unreported events
175
189
  const events = queryUnreportedUsageEvents(
176
190
  watermark,
@@ -192,12 +206,18 @@ export class UsageTelemetryReporter {
192
206
  onboardingWatermarkId,
193
207
  BATCH_SIZE,
194
208
  );
209
+ const authFallbackEvents = queryUnreportedAuthFallbackEvents(
210
+ authFallbackWatermark,
211
+ authFallbackWatermarkId,
212
+ BATCH_SIZE,
213
+ );
195
214
 
196
215
  if (
197
216
  events.length === 0 &&
198
217
  turnEvents.length === 0 &&
199
218
  lifecycleEvents.length === 0 &&
200
- onboardingEvents.length === 0
219
+ onboardingEvents.length === 0 &&
220
+ authFallbackEvents.length === 0
201
221
  )
202
222
  return;
203
223
 
@@ -211,6 +231,7 @@ export class UsageTelemetryReporter {
211
231
  turnCount: turnEvents.length,
212
232
  lifecycleCount: lifecycleEvents.length,
213
233
  onboardingCount: onboardingEvents.length,
234
+ authFallbackCount: authFallbackEvents.length,
214
235
  },
215
236
  "Telemetry flush: resolved auth context",
216
237
  );
@@ -337,6 +358,25 @@ export class UsageTelemetryReporter {
337
358
  assistant_version: APP_VERSION,
338
359
  }),
339
360
  ),
361
+ ...authFallbackEvents.map(
362
+ (e): TelemetryEvent => ({
363
+ type: "auth_fallback",
364
+ daemon_event_id: e.id,
365
+ recorded_at: e.createdAt,
366
+ guard: e.guard,
367
+ path: e.path,
368
+ failure_kind: e.failureKind,
369
+ count: e.count,
370
+ window_start: e.windowStart,
371
+ window_end: e.windowEnd,
372
+ // Aggregated counts forwarded by the gateway carry no record-time
373
+ // binary version; stamp the running binary's `APP_VERSION` so the
374
+ // wire value is concrete rather than an explicit null that would
375
+ // override the envelope under the platform's per-event-wins
376
+ // contract.
377
+ assistant_version: APP_VERSION,
378
+ }),
379
+ ),
340
380
  ];
341
381
 
342
382
  const organizationId = getPlatformOrganizationId() || undefined;
@@ -422,12 +462,27 @@ export class UsageTelemetryReporter {
422
462
  );
423
463
  }
424
464
 
465
+ // Advance auth-fallback watermark (compound cursor)
466
+ if (authFallbackEvents.length > 0) {
467
+ const lastAuthFallback =
468
+ authFallbackEvents[authFallbackEvents.length - 1];
469
+ setMemoryCheckpoint(
470
+ CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK,
471
+ String(lastAuthFallback.createdAt),
472
+ );
473
+ setMemoryCheckpoint(
474
+ CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK_ID,
475
+ lastAuthFallback.id,
476
+ );
477
+ }
478
+
425
479
  // If we got a full batch of any type, there may be more — recurse
426
480
  if (
427
481
  events.length === BATCH_SIZE ||
428
482
  turnEvents.length === BATCH_SIZE ||
429
483
  lifecycleEvents.length === BATCH_SIZE ||
430
- onboardingEvents.length === BATCH_SIZE
484
+ onboardingEvents.length === BATCH_SIZE ||
485
+ authFallbackEvents.length === BATCH_SIZE
431
486
  ) {
432
487
  await this._doFlush(batchCount + 1);
433
488
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared ToolContext accessors for the ACP tools.
3
+ */
4
+
5
+ import type { ServerMessage } from "../../daemon/message-protocol.js";
6
+ import type { ToolContext } from "../types.js";
7
+
8
+ /**
9
+ * Narrows `ToolContext.sendToClient` to the `ServerMessage` sender the ACP
10
+ * session manager expects. ToolContext types the callback against an
11
+ * index-signature message shape (`{ type: string; [key: string]: unknown }`)
12
+ * that ServerMessage's union members don't structurally satisfy, so the
13
+ * cast lives here once instead of being duplicated at every ACP tool call
14
+ * site.
15
+ */
16
+ export function getSendToClient(
17
+ context: ToolContext,
18
+ ): ((msg: ServerMessage) => void) | undefined {
19
+ return context.sendToClient as ((msg: ServerMessage) => void) | undefined;
20
+ }
@@ -47,7 +47,7 @@ describe("executeAcpListAgents", () => {
47
47
  expect(parsed.hint).toContain("config.json");
48
48
  });
49
49
 
50
- test("enabled, no user config: both defaults present with source 'default' and available based on Bun.which", async () => {
50
+ test("enabled, no user config: all defaults present with source 'default' and available based on Bun.which", async () => {
51
51
  config.setConfig({ agents: {} });
52
52
 
53
53
  const result = await executeAcpListAgents({}, makeContext());
@@ -58,6 +58,7 @@ describe("executeAcpListAgents", () => {
58
58
  expect(parsed.agents.map((a: { id: string }) => a.id)).toEqual([
59
59
  "claude",
60
60
  "codex",
61
+ "gemini",
61
62
  ]);
62
63
  for (const entry of parsed.agents) {
63
64
  expect(entry.source).toBe("default");
@@ -108,6 +109,11 @@ describe("executeAcpListAgents", () => {
108
109
  expect(codex.unavailableReason).toBe("'codex-acp' is not on PATH");
109
110
  expect(codex.setupHint).toBe("npm i -g @zed-industries/codex-acp");
110
111
 
112
+ const gemini = parsed.agents.find((a: { id: string }) => a.id === "gemini");
113
+ expect(gemini.available).toBe(false);
114
+ expect(gemini.unavailableReason).toBe("'gemini' is not on PATH");
115
+ expect(gemini.setupHint).toBe("npm i -g @google/gemini-cli");
116
+
111
117
  const claude = parsed.agents.find((a: { id: string }) => a.id === "claude");
112
118
  expect(claude.available).toBe(true);
113
119
  expect(claude.setupHint).toBeUndefined();
@@ -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,55 +9,11 @@ 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
- }
24
-
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
- }));
12
+ const {
13
+ execScripts,
14
+ execFileMock,
15
+ reset: resetExecFileStub,
16
+ } = installExecFileStub();
61
17
 
62
18
  // Default ACP config used by these tests: the `unknown-agent` entry is here
63
19
  // to give the "no version check" test a configured agent whose binary isn't
@@ -189,6 +145,9 @@ mock.module("../../acp/index.js", () => ({
189
145
 
190
146
  const { executeAcpSpawn, _resetAdapterVersionCacheForTests } =
191
147
  await import("./spawn.js");
148
+ const { _resetAdapterInstallCacheForTests } = await import(
149
+ "../../acp/auto-install.js"
150
+ );
192
151
 
193
152
  // ---------------------------------------------------------------------------
194
153
  // Helpers
@@ -204,10 +163,10 @@ function makeContext(): ToolContext {
204
163
  }
205
164
 
206
165
  beforeEach(() => {
207
- execScripts.clear();
208
- execFileMock.mockClear();
166
+ resetExecFileStub();
209
167
  spawnMock.mockClear();
210
168
  _resetAdapterVersionCacheForTests();
169
+ _resetAdapterInstallCacheForTests();
211
170
  config.setConfig({ agents: DEFAULT_TEST_AGENTS });
212
171
  which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
213
172
  // Default: vault has a claude token so the preflight in `prepareAgentEnv`
@@ -391,8 +350,9 @@ describe("executeAcpSpawn — input validation", () => {
391
350
  expect(result.content).toContain("acp.enabled");
392
351
  });
393
352
 
394
- test("missing binary returns install hint", async () => {
353
+ test("missing binary returns install hint when auto-install fails", async () => {
395
354
  which.setWhich({});
355
+ execScripts.set("npm i", { error: new Error("npm not installed") });
396
356
  const result = await executeAcpSpawn(
397
357
  { agent: "claude", task: "do something" },
398
358
  makeContext(),
@@ -426,6 +386,149 @@ describe("executeAcpSpawn — input validation", () => {
426
386
  });
427
387
  });
428
388
 
389
+ describe("executeAcpSpawn: auto-install on missing binary", () => {
390
+ test("known command: installs the mapped package and spawn proceeds with a note", async () => {
391
+ // Binary appears on PATH only after `npm i -g` runs, simulating a
392
+ // successful global install.
393
+ let binaryOnPath = false;
394
+ which.setWhich((cmd) => (binaryOnPath ? `/usr/local/bin/${cmd}` : null));
395
+ execScripts.set("npm i", {
396
+ stdout: "",
397
+ onCall: () => {
398
+ binaryOnPath = true;
399
+ },
400
+ });
401
+ // Version probes after the install: best-effort, scripted to skip.
402
+ execScripts.set("npm ls", { error: new Error("skip") });
403
+ execScripts.set("npm view", { error: new Error("skip") });
404
+
405
+ const result = await executeAcpSpawn(
406
+ { agent: "claude", task: "do something" },
407
+ makeContext(),
408
+ );
409
+
410
+ expect(result.isError).toBe(false);
411
+ expect(spawnMock).toHaveBeenCalledTimes(1);
412
+ const [payloadJson] = result.content.split("\n\n");
413
+ const payload = JSON.parse(payloadJson);
414
+ expect(payload.message).toContain(
415
+ "Installed @agentclientprotocol/claude-agent-acp automatically.",
416
+ );
417
+ const installCalls = execFileMock.mock.calls.filter(
418
+ (call) => (call[1] as string[])[0] === "i",
419
+ );
420
+ expect(installCalls).toHaveLength(1);
421
+ expect(installCalls[0][1]).toEqual([
422
+ "i",
423
+ "-g",
424
+ "@agentclientprotocol/claude-agent-acp",
425
+ ]);
426
+ });
427
+
428
+ test("unknown command: no install attempted, plain hint returned", async () => {
429
+ which.setWhich({});
430
+
431
+ const result = await executeAcpSpawn(
432
+ { agent: "unknown-agent", task: "do something" },
433
+ makeContext(),
434
+ );
435
+
436
+ expect(result.isError).toBe(true);
437
+ expect(result.content).toContain("some-other-binary is not on PATH");
438
+ expect(result.content).toContain("Install 'some-other-binary'");
439
+ expect(result.content).not.toContain("auto-install failed");
440
+ expect(execFileMock).not.toHaveBeenCalled();
441
+ expect(spawnMock).not.toHaveBeenCalled();
442
+ });
443
+
444
+ test("no client connected: no install attempted even when the binary is missing", async () => {
445
+ // The no-client guard is a pure precondition and must run BEFORE the
446
+ // auto-install side effect: without a client the spawn fails anyway, so
447
+ // the host must not be mutated by `npm i -g` (which can also block for
448
+ // up to the install timeout).
449
+ which.setWhich({});
450
+
451
+ const result = await executeAcpSpawn(
452
+ { agent: "claude", task: "do something" },
453
+ { ...makeContext(), sendToClient: undefined },
454
+ );
455
+
456
+ expect(result.isError).toBe(true);
457
+ expect(result.content).toContain("No client connected");
458
+ expect(execFileMock).not.toHaveBeenCalled();
459
+ expect(spawnMock).not.toHaveBeenCalled();
460
+ });
461
+
462
+ test("npm failure: hint and install failure both surface", async () => {
463
+ which.setWhich({});
464
+ execScripts.set("npm i", {
465
+ error: new Error("EACCES: permission denied"),
466
+ });
467
+
468
+ const result = await executeAcpSpawn(
469
+ { agent: "claude", task: "do something" },
470
+ makeContext(),
471
+ );
472
+
473
+ expect(result.isError).toBe(true);
474
+ expect(result.content).toContain("claude-agent-acp is not on PATH");
475
+ expect(result.content).toContain(
476
+ "npm i -g @agentclientprotocol/claude-agent-acp",
477
+ );
478
+ expect(result.content).toContain("auto-install failed");
479
+ expect(result.content).toContain("EACCES");
480
+ expect(spawnMock).not.toHaveBeenCalled();
481
+ });
482
+ });
483
+
484
+ describe("executeAcpSpawn - bunx fallback when binary missing", () => {
485
+ test("binary missing + bun present: spawns via `bun x` with env injection and resume hint, no npm calls", async () => {
486
+ // Only bun is on PATH - the platform-hosted image (bun, no node/npm).
487
+ which.setWhich({ bun: "/usr/local/bin/bun" });
488
+
489
+ const result = await executeAcpSpawn(
490
+ { agent: "claude", task: "do something" },
491
+ makeContext(),
492
+ );
493
+
494
+ expect(result.isError).toBe(false);
495
+ // No npm install AND no npm version probe: bunx fetches the package on
496
+ // first use and there is no global install to compare.
497
+ expect(execFileMock).not.toHaveBeenCalled();
498
+ expect(result.content).not.toContain("outdated");
499
+
500
+ expect(spawnMock).toHaveBeenCalledTimes(1);
501
+ const agentConfigArg = spawnMock.mock.calls[0][1] as {
502
+ command: string;
503
+ args: string[];
504
+ adapterCommand?: string;
505
+ env?: Record<string, string>;
506
+ };
507
+ expect(agentConfigArg.command).toBe("bun");
508
+ expect(agentConfigArg.args).toEqual([
509
+ "x",
510
+ "--bun",
511
+ "@agentclientprotocol/claude-agent-acp",
512
+ ]);
513
+ expect(agentConfigArg.adapterCommand).toBe("claude-agent-acp");
514
+ // CLAUDE_CODE_OAUTH_TOKEN injection still applies to the bunx-resolved
515
+ // claude adapter (gated on adapterCommand, not the spawn command).
516
+ expect(agentConfigArg.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
517
+ "default-test-token",
518
+ );
519
+
520
+ const [payloadJson] = result.content.split("\n\n");
521
+ const payload = JSON.parse(payloadJson);
522
+ // No auto-install happened; the claude resume hint still fires.
523
+ expect(payload.message).not.toContain("Installed");
524
+ expect(payload.message).toContain("claude --resume");
525
+ });
526
+
527
+ // The bun-absent npm fallback is covered by the existing auto-install
528
+ // suite below ("known command: installs the mapped package..."), whose
529
+ // which-stub leaves bun off PATH until the install completes.
530
+ });
531
+
429
532
  describe("executeAcpSpawn — per-agent resume hint", () => {
430
533
  test("claude payload includes the `claude --resume` hint", async () => {
431
534
  execScripts.set("npm ls", { error: new Error("npm not installed") });
@@ -440,7 +543,7 @@ describe("executeAcpSpawn — per-agent resume hint", () => {
440
543
  const [payloadJson] = result.content.split("\n\n");
441
544
  const payload = JSON.parse(payloadJson);
442
545
  expect(payload.message).toContain("claude --resume");
443
- expect(payload.message).toContain("To resume this session later");
546
+ expect(payload.message).toContain("To resume:");
444
547
  });
445
548
 
446
549
  test("non-claude payload omits the `claude --resume` hint", async () => {
@@ -458,7 +561,7 @@ describe("executeAcpSpawn — per-agent resume hint", () => {
458
561
  const [payloadJson] = result.content.split("\n\n");
459
562
  const payload = JSON.parse(payloadJson);
460
563
  expect(payload.message).not.toContain("claude --resume");
461
- expect(payload.message).not.toContain("To resume this session later");
564
+ expect(payload.message).not.toContain("To resume:");
462
565
  });
463
566
  });
464
567
 
@@ -1,11 +1,19 @@
1
- import { execFile } from "node:child_process";
2
-
1
+ import {
2
+ execFileWithTimeout,
3
+ resolveAgentWithAutoInstall,
4
+ } from "../../acp/auto-install.js";
3
5
  import { getAcpSessionManager } from "../../acp/index.js";
4
6
  import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
5
- import { resolveAcpAgent } from "../../acp/resolve-agent.js";
7
+ import {
8
+ adapterCommandOf,
9
+ formatResolveFailure,
10
+ runsViaBunx,
11
+ } from "../../acp/resolve-agent.js";
12
+ import { claudeResumeHint } from "../../acp/resume-hint.js";
6
13
  import { DEFAULT_AGENT_NPM_PACKAGES } from "../../config/acp-defaults.js";
7
14
  import { getLogger } from "../../util/logger.js";
8
15
  import type { ToolContext, ToolExecutionResult } from "../types.js";
16
+ import { getSendToClient } from "./context.js";
9
17
 
10
18
  const log = getLogger("acp:spawn");
11
19
 
@@ -25,35 +33,6 @@ interface AdapterVersionInfo {
25
33
  packageName: string;
26
34
  }
27
35
 
28
- /**
29
- * Run `execFile` with an AbortController-driven timeout. Returns the stdout
30
- * on success; throws on error or timeout. Caller treats any throw as a
31
- * best-effort skip.
32
- */
33
- function execFileWithTimeout(
34
- command: string,
35
- args: string[],
36
- timeoutMs: number,
37
- ): Promise<string> {
38
- return new Promise((resolve, reject) => {
39
- const controller = new AbortController();
40
- const timer = setTimeout(() => controller.abort(), timeoutMs);
41
- execFile(
42
- command,
43
- args,
44
- { signal: controller.signal, encoding: "utf8" },
45
- (err, stdout) => {
46
- clearTimeout(timer);
47
- if (err) {
48
- reject(err);
49
- return;
50
- }
51
- resolve(stdout);
52
- },
53
- );
54
- });
55
- }
56
-
57
36
  /**
58
37
  * Checks if the globally-installed ACP adapter for `command` is outdated.
59
38
  * Best-effort: any error or timeout returns `null` (skipped). Unknown
@@ -135,34 +114,10 @@ export async function executeAcpSpawn(
135
114
  return { content: '"task" is required.', isError: true };
136
115
  }
137
116
 
138
- const resolved = resolveAcpAgent(agent);
139
- if (!resolved.ok) {
140
- switch (resolved.reason) {
141
- case "acp_disabled":
142
- return { content: resolved.hint, isError: true };
143
- case "unknown_agent":
144
- return {
145
- content: `Unknown agent "${agent}". Available: ${resolved.available.join(
146
- ", ",
147
- )}.`,
148
- isError: true,
149
- };
150
- case "binary_not_found":
151
- return {
152
- content: `${resolved.command} is not on PATH. ${resolved.hint}`,
153
- isError: true,
154
- };
155
- default: {
156
- const _exhaustive: never = resolved;
157
- throw new Error(
158
- `Unexpected acp resolver reason: ${(_exhaustive as { reason: string }).reason}`,
159
- );
160
- }
161
- }
162
- }
163
- const sendToClient = context.sendToClient as
164
- | ((msg: { type: string; [key: string]: unknown }) => void)
165
- | undefined;
117
+ // Pure precondition: check for a connected client BEFORE any side effects
118
+ // (auto-install mutates the host via `npm i -g` and can block for up to
119
+ // the install timeout). Without a client the spawn cannot succeed anyway.
120
+ const sendToClient = getSendToClient(context);
166
121
  if (!sendToClient) {
167
122
  return {
168
123
  content: "No client connected - cannot spawn ACP agent.",
@@ -170,6 +125,17 @@ export async function executeAcpSpawn(
170
125
  };
171
126
  }
172
127
 
128
+ // Resolve the agent, silently auto-installing a missing allowlisted
129
+ // adapter binary (see acp/auto-install.ts). Shared with the HTTP route.
130
+ const { resolved, autoInstalledPackage, failureMessage } =
131
+ await resolveAgentWithAutoInstall(agent);
132
+ if (failureMessage) {
133
+ return { content: failureMessage, isError: true };
134
+ }
135
+ if (!resolved.ok) {
136
+ return { content: formatResolveFailure(agent, resolved), isError: true };
137
+ }
138
+
173
139
  // Inject required env vars and preflight via the shared helper. Mirrors
174
140
  // the HTTP route at `runtime/routes/acp-routes.ts:spawnSession` — both
175
141
  // call sites MUST go through `prepareAgentEnv` before `manager.spawn`,
@@ -185,8 +151,12 @@ export async function executeAcpSpawn(
185
151
  }
186
152
 
187
153
  // Best-effort version check — never blocks the spawn. If outdated, we
188
- // append a non-blocking warning to the success payload.
189
- const versionInfo = await checkAdapterVersion(agentConfig.command);
154
+ // append a non-blocking warning to the success payload. Skipped for
155
+ // bunx-resolved agents: there is no global npm install to compare (bun
156
+ // fetches the package on first use), and npm may not exist on the host.
157
+ const versionInfo = runsViaBunx(agentConfig)
158
+ ? null
159
+ : await checkAdapterVersion(adapterCommandOf(agentConfig));
190
160
 
191
161
  try {
192
162
  const manager = getAcpSessionManager();
@@ -197,17 +167,21 @@ export async function executeAcpSpawn(
197
167
  task,
198
168
  cwd,
199
169
  context.conversationId,
200
- sendToClient as (msg: unknown) => void,
170
+ sendToClient,
201
171
  );
202
172
 
203
- // `claude --resume <id>` is Claude Code-specific (the claude-agent-acp
204
- // adapter binary). Other adapters resume differently or not at all,
205
- // so the hint is gated by the resolved binary, not the agent id —
206
- // this stays correct when a user aliases an id to a different binary.
207
- const resumeHint =
208
- agentConfig.command === "claude-agent-acp"
209
- ? ` To resume this session later, run: cd ${cwd} && claude --resume ${protocolSessionId}`
210
- : "";
173
+ // Claude Code-only resume hint; empty for other adapters. Keyed off the
174
+ // canonical adapter command so it survives the bunx rewrite. See
175
+ // acp/resume-hint.ts for the gating rationale.
176
+ const hint = claudeResumeHint(
177
+ adapterCommandOf(agentConfig),
178
+ cwd,
179
+ protocolSessionId,
180
+ );
181
+ const resumeHint = hint ? ` ${hint}` : "";
182
+ const installNote = autoInstalledPackage
183
+ ? ` Installed ${autoInstalledPackage} automatically.`
184
+ : "";
211
185
  const payload = JSON.stringify({
212
186
  acpSessionId,
213
187
  protocolSessionId,
@@ -216,7 +190,8 @@ export async function executeAcpSpawn(
216
190
  status: "running",
217
191
  message:
218
192
  `ACP agent "${agent}" spawned (session: ${protocolSessionId}). ` +
219
- `Results stream back via SSE. You will be notified when it completes.${resumeHint}`,
193
+ `Results stream back via SSE. You will be notified when it completes.` +
194
+ `${installNote}${resumeHint}`,
220
195
  });
221
196
 
222
197
  let content = payload;