@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
@@ -14,7 +14,7 @@
14
14
  import { answerCall } from "../calls/call-domain.js";
15
15
  import { findContactChannel } from "../contacts/contact-store.js";
16
16
  import { upsertContactChannel } from "../contacts/contacts-write.js";
17
- import { findConversation } from "../daemon/conversation-store.js";
17
+ import { findConversation } from "../daemon/conversation-registry.js";
18
18
  import {
19
19
  type CanonicalGuardianRequest,
20
20
  getCanonicalGuardianRequest,
@@ -41,6 +41,7 @@ const IMAGE_FILENAME = "avatar-image.png";
41
41
  const TRAITS_FILENAME = "character-traits.json";
42
42
  const ASCII_FILENAME = "character-ascii.txt";
43
43
  const MANIFEST_FILENAME = "avatar.json";
44
+ const NATIVE_RENDER_TEST_TIMEOUT_MS = 15_000;
44
45
 
45
46
  interface ManifestShape {
46
47
  kind: string;
@@ -84,35 +85,39 @@ describe("avatar-store", () => {
84
85
  };
85
86
 
86
87
  describe("setCharacter", () => {
87
- test("writes traits + PNG and a character manifest when render succeeds", () => {
88
- const result = setCharacter(VALID_TRAITS);
89
-
90
- // The native @resvg/resvg-js binding may be absent in this environment.
91
- // When it is, the store returns `native_unavailable` and writes nothing —
92
- // we assert that contract instead of the success path so the suite is
93
- // deterministic either way.
94
- if (!result.ok) {
95
- expect(result.reason).toBe("native_unavailable");
96
- expect(existsSync(path(TRAITS_FILENAME))).toBe(false);
97
- expect(existsSync(path(IMAGE_FILENAME))).toBe(false);
98
- expect(existsSync(path(MANIFEST_FILENAME))).toBe(false);
99
- return;
100
- }
101
-
102
- expect(existsSync(path(TRAITS_FILENAME))).toBe(true);
103
- expect(existsSync(path(IMAGE_FILENAME))).toBe(true);
104
- expect(JSON.parse(readFileSync(path(TRAITS_FILENAME), "utf-8"))).toEqual(
105
- VALID_TRAITS,
106
- );
107
-
108
- const manifest = readManifestFile();
109
- expect(manifest).not.toBeNull();
110
- expect(manifest!.kind).toBe("character");
111
- expect(manifest!.traits).toEqual(VALID_TRAITS);
112
- expect(manifest!.source).toBe("builder");
113
- expect(manifest!.image).not.toBeNull();
114
- expect(manifest!.image!.etag).toMatch(/^[0-9a-f]{16}$/);
115
- });
88
+ test(
89
+ "writes traits + PNG and a character manifest when render succeeds",
90
+ () => {
91
+ const result = setCharacter(VALID_TRAITS);
92
+
93
+ // The native @resvg/resvg-js binding may be absent in this environment.
94
+ // When it is, the store returns `native_unavailable` and writes nothing —
95
+ // we assert that contract instead of the success path so the suite is
96
+ // deterministic either way.
97
+ if (!result.ok) {
98
+ expect(result.reason).toBe("native_unavailable");
99
+ expect(existsSync(path(TRAITS_FILENAME))).toBe(false);
100
+ expect(existsSync(path(IMAGE_FILENAME))).toBe(false);
101
+ expect(existsSync(path(MANIFEST_FILENAME))).toBe(false);
102
+ return;
103
+ }
104
+
105
+ expect(existsSync(path(TRAITS_FILENAME))).toBe(true);
106
+ expect(existsSync(path(IMAGE_FILENAME))).toBe(true);
107
+ expect(
108
+ JSON.parse(readFileSync(path(TRAITS_FILENAME), "utf-8")),
109
+ ).toEqual(VALID_TRAITS);
110
+
111
+ const manifest = readManifestFile();
112
+ expect(manifest).not.toBeNull();
113
+ expect(manifest!.kind).toBe("character");
114
+ expect(manifest!.traits).toEqual(VALID_TRAITS);
115
+ expect(manifest!.source).toBe("builder");
116
+ expect(manifest!.image).not.toBeNull();
117
+ expect(manifest!.image!.etag).toMatch(/^[0-9a-f]{16}$/);
118
+ },
119
+ NATIVE_RENDER_TEST_TIMEOUT_MS,
120
+ );
116
121
 
117
122
  test("propagates invalid_traits without writing a manifest", () => {
118
123
  const result = setCharacter({ bodyShape: "", eyeStyle: "", color: "" });
@@ -97,6 +97,7 @@ function getHeartbeatWakeSource(now: number): HeartbeatWakeSource | null {
97
97
 
98
98
  const service = HeartbeatService.getInstance();
99
99
  if (service?.isConsecutiveRunCapReached) return null;
100
+ if (service?.isDailyCapReached) return null;
100
101
 
101
102
  const serviceNextRunAt = service?.nextRunAt ?? null;
102
103
  let nextRunAt = serviceNextRunAt;
@@ -66,6 +66,12 @@ interface CommandResult {
66
66
  exitCode: number;
67
67
  }
68
68
 
69
+ interface RawCommandResult {
70
+ stdout: string;
71
+ stderr: string;
72
+ exitCode: number;
73
+ }
74
+
69
75
  /**
70
76
  * Run a notifications subcommand and capture the JSON output.
71
77
  * Always passes --json to get compact, single-line JSON output and suppress log messages.
@@ -74,6 +80,17 @@ interface CommandResult {
74
80
  * reset to 0, capture, then reset back to 0 so bun test exits cleanly.
75
81
  */
76
82
  async function runCommand(args: string[]): Promise<CommandResult> {
83
+ const raw = await runRawCommand(["notifications", "--json", ...args]);
84
+
85
+ const firstLine = raw.stdout.trim().split("\n")[0];
86
+ const parsed = firstLine
87
+ ? (JSON.parse(firstLine) as Record<string, unknown>)
88
+ : {};
89
+
90
+ return { parsed, stderr: raw.stderr, exitCode: raw.exitCode };
91
+ }
92
+
93
+ async function runRawCommand(args: string[]): Promise<RawCommandResult> {
77
94
  const chunks: string[] = [];
78
95
  const stderrChunks: string[] = [];
79
96
  const originalWrite = process.stdout.write;
@@ -95,13 +112,7 @@ async function runCommand(args: string[]): Promise<CommandResult> {
95
112
  const program = new Command();
96
113
  program.exitOverride();
97
114
  registerNotificationsCommand(program);
98
- await program.parseAsync([
99
- "node",
100
- "test",
101
- "notifications",
102
- "--json",
103
- ...args,
104
- ]);
115
+ await program.parseAsync(["node", "test", ...args]);
105
116
  } catch {
106
117
  // Commander throws on .exitOverride() for --help/errors; ignore
107
118
  } finally {
@@ -112,13 +123,11 @@ async function runCommand(args: string[]): Promise<CommandResult> {
112
123
  const exitCode = process.exitCode ?? 0;
113
124
  process.exitCode = 0;
114
125
 
115
- const output = chunks.join("");
116
- const firstLine = output.trim().split("\n")[0];
117
- const parsed = firstLine
118
- ? (JSON.parse(firstLine) as Record<string, unknown>)
119
- : {};
120
-
121
- return { parsed, stderr: stderrChunks.join(""), exitCode };
126
+ return {
127
+ stdout: chunks.join(""),
128
+ stderr: stderrChunks.join(""),
129
+ exitCode,
130
+ };
122
131
  }
123
132
 
124
133
  function lastSendBody(): Record<string, unknown> {
@@ -244,6 +253,41 @@ describe("notifications send", () => {
244
253
  expect(ipcCalls).toHaveLength(0);
245
254
  });
246
255
 
256
+ test("send rejects invalid source-channel", async () => {
257
+ const { parsed, exitCode } = await runCommand([
258
+ "send",
259
+ "--source-channel",
260
+ "water_reminder",
261
+ "--source-event-name",
262
+ "user.send_notification",
263
+ "--message",
264
+ "Hello",
265
+ ]);
266
+
267
+ expect(exitCode).toBe(1);
268
+ expect(parsed).toEqual({
269
+ ok: false,
270
+ error:
271
+ 'Invalid source-channel "water_reminder". Must be one of: assistant_tool, vellum, phone, telegram, slack, scheduler, watcher',
272
+ });
273
+ expect(ipcCalls).toHaveLength(0);
274
+ });
275
+
276
+ test("send help lists source-channel values", async () => {
277
+ const { stdout, exitCode } = await runRawCommand([
278
+ "notifications",
279
+ "send",
280
+ "--help",
281
+ ]);
282
+
283
+ expect(exitCode).toBe(0);
284
+ const normalizedHelp = stdout.replace(/\s+/g, " ");
285
+ expect(normalizedHelp).toContain(
286
+ "One of: assistant_tool, vellum, phone, telegram, slack, scheduler, watcher",
287
+ );
288
+ expect(normalizedHelp).toContain("(default: assistant_tool)");
289
+ });
290
+
247
291
  test("send --conversation-id pins the vellum affinity hint", async () => {
248
292
  const { parsed, exitCode } = await runCommand([
249
293
  "send",
@@ -23,6 +23,39 @@ interface ListHomeFeedPayload {
23
23
  updatedAt: string;
24
24
  }
25
25
 
26
+ const NOTIFICATION_SOURCE_CHANNEL_VALUES = [
27
+ "assistant_tool",
28
+ "vellum",
29
+ "phone",
30
+ "telegram",
31
+ "slack",
32
+ "scheduler",
33
+ "watcher",
34
+ ] as const;
35
+
36
+ const URGENCY_VALUES = ["low", "medium", "high", "critical"] as const;
37
+
38
+ const NOTIFICATION_STATUS_VALUES = [
39
+ "new",
40
+ "seen",
41
+ "acted_on",
42
+ "dismissed",
43
+ ] as const;
44
+
45
+ const NOTIFICATION_CATEGORY_VALUES = [
46
+ "security",
47
+ "scheduling",
48
+ "background",
49
+ "email",
50
+ "system",
51
+ ] as const;
52
+
53
+ const DEFAULT_SOURCE_CHANNEL = "assistant_tool";
54
+ const SOURCE_CHANNEL_HELP = NOTIFICATION_SOURCE_CHANNEL_VALUES.join(", ");
55
+ const URGENCY_HELP = URGENCY_VALUES.join(", ");
56
+ const STATUS_HELP = NOTIFICATION_STATUS_VALUES.join(", ");
57
+ const CATEGORY_HELP = NOTIFICATION_CATEGORY_VALUES.join(", ");
58
+
26
59
  function parseBoundedInt(
27
60
  raw: string | undefined,
28
61
  label: string,
@@ -71,6 +104,30 @@ function renderFeedItemsHuman(payload: ListHomeFeedPayload): void {
71
104
  }
72
105
  }
73
106
 
107
+ function validateEnumValue(
108
+ value: string | undefined,
109
+ label: string,
110
+ allowed: readonly string[],
111
+ ): { error?: string } {
112
+ if (value === undefined || allowed.includes(value)) return {};
113
+ return {
114
+ error: `Invalid ${label} "${value}". Must be one of: ${allowed.join(", ")}`,
115
+ };
116
+ }
117
+
118
+ function validateEnumFlag(
119
+ values: string[] | undefined,
120
+ label: string,
121
+ allowed: readonly string[],
122
+ ): { error?: string } {
123
+ if (!values) return {};
124
+ for (const v of values) {
125
+ const result = validateEnumValue(v, label, allowed);
126
+ if (result.error) return result;
127
+ }
128
+ return {};
129
+ }
130
+
74
131
  // ---------------------------------------------------------------------------
75
132
  // Command registration
76
133
  // ---------------------------------------------------------------------------
@@ -122,7 +179,7 @@ Examples:
122
179
  )
123
180
  .option(
124
181
  "--source-channel <channel>",
125
- "Source channel producing this notification (default: assistant_tool)",
182
+ `Source channel producing this notification. One of: ${SOURCE_CHANNEL_HELP} (default: ${DEFAULT_SOURCE_CHANNEL})`,
126
183
  )
127
184
  .option(
128
185
  "--source-event-name <name>",
@@ -134,7 +191,7 @@ Examples:
134
191
  )
135
192
  .option(
136
193
  "--urgency <urgency>",
137
- "Urgency hint: low, medium, high, critical (default: low; use --urgent for critical)",
194
+ `Urgency hint. One of: ${URGENCY_HELP} (default: low; use --urgent for critical)`,
138
195
  )
139
196
  .option(
140
197
  "--requires-action",
@@ -233,9 +290,24 @@ Examples:
233
290
  try {
234
291
  // Apply defaults for optional source fields (minimal-surface
235
292
  // ergonomics; explicit values from the CLI still win).
236
- const sourceChannel = opts.sourceChannel ?? "assistant_tool";
293
+ const sourceChannel =
294
+ opts.sourceChannel ?? DEFAULT_SOURCE_CHANNEL;
237
295
  const sourceEventName = opts.sourceEventName ?? "assistant.share";
238
296
 
297
+ const sourceChannelError = validateEnumValue(
298
+ sourceChannel,
299
+ "source-channel",
300
+ NOTIFICATION_SOURCE_CHANNEL_VALUES,
301
+ );
302
+ if (sourceChannelError.error) {
303
+ writeOutput(cmd, {
304
+ ok: false,
305
+ error: sourceChannelError.error,
306
+ });
307
+ process.exitCode = 1;
308
+ return;
309
+ }
310
+
239
311
  // Validate --message (keep basic validation for immediate CLI feedback)
240
312
  const message = opts.message.trim();
241
313
  if (message.length === 0) {
@@ -256,15 +328,15 @@ Examples:
256
328
 
257
329
  // Validate --urgency
258
330
  const urgency = opts.urgency ?? urgentDefaults.urgency;
259
- if (
260
- urgency !== "low" &&
261
- urgency !== "medium" &&
262
- urgency !== "high" &&
263
- urgency !== "critical"
264
- ) {
331
+ const urgencyError = validateEnumValue(
332
+ urgency,
333
+ "urgency",
334
+ URGENCY_VALUES,
335
+ );
336
+ if (urgencyError.error) {
265
337
  writeOutput(cmd, {
266
338
  ok: false,
267
- error: `Invalid urgency "${opts.urgency}". Must be one of: low, medium, high, critical`,
339
+ error: urgencyError.error,
268
340
  });
269
341
  process.exitCode = 1;
270
342
  return;
@@ -425,22 +497,6 @@ Examples:
425
497
  prev: string[] | undefined,
426
498
  ): string[] => [...(prev ?? []), value];
427
499
 
428
- function validateEnumFlag(
429
- values: string[] | undefined,
430
- label: string,
431
- allowed: readonly string[],
432
- ): { error?: string } {
433
- if (!values) return {};
434
- for (const v of values) {
435
- if (!allowed.includes(v)) {
436
- return {
437
- error: `Invalid ${label} "${v}". Must be one of: ${allowed.join(", ")}`,
438
- };
439
- }
440
- }
441
- return {};
442
- }
443
-
444
500
  notifications
445
501
  .command("list")
446
502
  .description(
@@ -453,7 +509,7 @@ Examples:
453
509
  )
454
510
  .option(
455
511
  "--status <status>",
456
- "Filter by status (new|seen|acted_on|dismissed); repeatable. Overrides --all default behavior.",
512
+ `Filter by status. One of: ${STATUS_HELP}; repeatable. Overrides --all default behavior.`,
457
513
  collectFlag,
458
514
  )
459
515
  .option(
@@ -466,12 +522,12 @@ Examples:
466
522
  )
467
523
  .option(
468
524
  "--urgency <urgency>",
469
- "Filter by urgency (low|medium|high|critical); repeatable",
525
+ `Filter by urgency. One of: ${URGENCY_HELP}; repeatable`,
470
526
  collectFlag,
471
527
  )
472
528
  .option(
473
529
  "--category <category>",
474
- "Filter by category (security|scheduling|background|email|system); repeatable",
530
+ `Filter by category. One of: ${CATEGORY_HELP}; repeatable`,
475
531
  collectFlag,
476
532
  )
477
533
  .option(
@@ -525,25 +581,17 @@ Examples:
525
581
  ) => {
526
582
  try {
527
583
  const enumChecks: Array<{ error?: string }> = [
528
- validateEnumFlag(opts.status, "status", [
529
- "new",
530
- "seen",
531
- "acted_on",
532
- "dismissed",
533
- ]),
534
- validateEnumFlag(opts.urgency, "urgency", [
535
- "low",
536
- "medium",
537
- "high",
538
- "critical",
539
- ]),
540
- validateEnumFlag(opts.category, "category", [
541
- "security",
542
- "scheduling",
543
- "background",
544
- "email",
545
- "system",
546
- ]),
584
+ validateEnumFlag(
585
+ opts.status,
586
+ "status",
587
+ NOTIFICATION_STATUS_VALUES,
588
+ ),
589
+ validateEnumFlag(opts.urgency, "urgency", URGENCY_VALUES),
590
+ validateEnumFlag(
591
+ opts.category,
592
+ "category",
593
+ NOTIFICATION_CATEGORY_VALUES,
594
+ ),
547
595
  ];
548
596
  const enumError = enumChecks.find((c) => c.error);
549
597
  if (enumError) {
@@ -641,11 +689,11 @@ Examples:
641
689
  .option("--title <title>", "New short headline (≤ 8 words).")
642
690
  .option(
643
691
  "--urgency <urgency>",
644
- "Set urgency (low|medium|high|critical). Feed-only — does not re-push channel messages.",
692
+ `Set urgency. One of: ${URGENCY_HELP}. Feed-only — does not re-push channel messages.`,
645
693
  )
646
694
  .option(
647
695
  "--status <status>",
648
- "Set lifecycle status (new|seen|acted_on|dismissed). Feed-only.",
696
+ `Set lifecycle status. One of: ${STATUS_HELP}. Feed-only.`,
649
697
  )
650
698
  .addHelpText(
651
699
  "after",
@@ -701,24 +749,28 @@ Examples:
701
749
  return;
702
750
  }
703
751
 
704
- if (
705
- opts.urgency != null &&
706
- !["low", "medium", "high", "critical"].includes(opts.urgency)
707
- ) {
752
+ const urgencyError = validateEnumValue(
753
+ opts.urgency,
754
+ "urgency",
755
+ URGENCY_VALUES,
756
+ );
757
+ if (urgencyError.error) {
708
758
  writeOutput(cmd, {
709
759
  ok: false,
710
- error: `Invalid urgency "${opts.urgency}". Must be one of: low, medium, high, critical`,
760
+ error: urgencyError.error,
711
761
  });
712
762
  process.exitCode = 1;
713
763
  return;
714
764
  }
715
- if (
716
- opts.status != null &&
717
- !["new", "seen", "acted_on", "dismissed"].includes(opts.status)
718
- ) {
765
+ const statusError = validateEnumValue(
766
+ opts.status,
767
+ "status",
768
+ NOTIFICATION_STATUS_VALUES,
769
+ );
770
+ if (statusError.error) {
719
771
  writeOutput(cmd, {
720
772
  ok: false,
721
- error: `Invalid status "${opts.status}". Must be one of: new, seen, acted_on, dismissed`,
773
+ error: statusError.error,
722
774
  });
723
775
  process.exitCode = 1;
724
776
  return;
@@ -85,8 +85,8 @@ describe("unified feature flag registry guard", () => {
85
85
  ) {
86
86
  violations.push(`${prefix}: missing or non-string 'description'`);
87
87
  }
88
- if (typeof flag.defaultEnabled !== "boolean") {
89
- violations.push(`${prefix}: missing or non-boolean 'defaultEnabled'`);
88
+ if (typeof flag.defaultEnabled !== "boolean" && typeof flag.defaultEnabled !== "string") {
89
+ violations.push(`${prefix}: missing or invalid 'defaultEnabled' (expected boolean or string)`);
90
90
  }
91
91
  }
92
92
 
@@ -10,6 +10,7 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
10
10
  expect(Object.keys(DEFAULT_ACP_AGENT_PROFILES).sort()).toEqual([
11
11
  "claude",
12
12
  "codex",
13
+ "gemini",
13
14
  ]);
14
15
  });
15
16
 
@@ -29,6 +30,14 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
29
30
  });
30
31
  });
31
32
 
33
+ test("gemini profile speaks native ACP via gemini --acp (no adapter binary)", () => {
34
+ expect(DEFAULT_ACP_AGENT_PROFILES.gemini).toEqual({
35
+ command: "gemini",
36
+ args: ["--acp"],
37
+ description: "Google Gemini CLI (native ACP via gemini --acp)",
38
+ });
39
+ });
40
+
32
41
  test("is deeply frozen so mutation throws in strict mode", () => {
33
42
  expect(Object.isFrozen(DEFAULT_ACP_AGENT_PROFILES)).toBe(true);
34
43
  for (const profile of Object.values(DEFAULT_ACP_AGENT_PROFILES)) {
@@ -46,6 +55,7 @@ describe("DEFAULT_AGENT_NPM_PACKAGES", () => {
46
55
  expect(DEFAULT_AGENT_NPM_PACKAGES).toEqual({
47
56
  "claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
48
57
  "codex-acp": "@zed-industries/codex-acp",
58
+ gemini: "@google/gemini-cli",
49
59
  });
50
60
  });
51
61
 
@@ -30,6 +30,11 @@ export const DEFAULT_ACP_AGENT_PROFILES: Readonly<
30
30
  args: FROZEN_EMPTY_ARGS,
31
31
  description: "OpenAI Codex CLI (via @zed-industries/codex-acp)",
32
32
  }),
33
+ gemini: Object.freeze({
34
+ command: "gemini",
35
+ args: Object.freeze(["--acp"]) as unknown as string[],
36
+ description: "Google Gemini CLI (native ACP via gemini --acp)",
37
+ }),
33
38
  });
34
39
 
35
40
  /**
@@ -44,4 +49,5 @@ export const DEFAULT_AGENT_NPM_PACKAGES: Readonly<Record<string, string>> =
44
49
  Object.freeze({
45
50
  "claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
46
51
  "codex-acp": "@zed-industries/codex-acp",
52
+ gemini: "@google/gemini-cli",
47
53
  });
@@ -33,7 +33,7 @@ const log = getLogger("assistant-feature-flags");
33
33
  // ---------------------------------------------------------------------------
34
34
 
35
35
  export interface FeatureFlagDefault {
36
- defaultEnabled: boolean;
36
+ defaultEnabled: boolean | string;
37
37
  description: string;
38
38
  label: string;
39
39
  }
@@ -103,7 +103,7 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
103
103
  const entry = flag as Record<string, unknown>;
104
104
  if (entry.scope !== "assistant" && entry.scope !== "both") continue;
105
105
  if (typeof entry.key !== "string") continue;
106
- if (typeof entry.defaultEnabled !== "boolean") continue;
106
+ if (typeof entry.defaultEnabled !== "boolean" && typeof entry.defaultEnabled !== "string") continue;
107
107
 
108
108
  result[entry.key as string] = {
109
109
  defaultEnabled: entry.defaultEnabled,
@@ -133,7 +133,7 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
133
133
  */
134
134
  async function fetchOverridesFromGateway(
135
135
  timeoutMs?: number,
136
- ): Promise<Record<string, boolean>> {
136
+ ): Promise<Record<string, boolean | string>> {
137
137
  try {
138
138
  return await ipcGetFeatureFlags(timeoutMs);
139
139
  } catch {
@@ -238,7 +238,7 @@ export async function initFeatureFlagOverrides(options?: {
238
238
  * Returns the gateway-populated cache if `initFeatureFlagOverrides()` was
239
239
  * called at startup, or an empty record otherwise.
240
240
  */
241
- function loadOverrides(): Record<string, boolean> {
241
+ function loadOverrides(): Record<string, boolean | string> {
242
242
  return getCachedOverrides() ?? {};
243
243
  }
244
244
 
@@ -273,7 +273,7 @@ export async function refreshOverridesFromGateway(): Promise<void> {
273
273
  // ---------------------------------------------------------------------------
274
274
 
275
275
  /**
276
- * Resolve whether an assistant feature flag is enabled.
276
+ * Resolve the raw value for an assistant feature flag.
277
277
  *
278
278
  * Resolution order:
279
279
  * 1. Override from the gateway IPC fetch (includes platform-pushed remote
@@ -282,21 +282,32 @@ export async function refreshOverridesFromGateway(): Promise<void> {
282
282
  * 2. Registry `defaultEnabled` (for declared assistant-scope keys)
283
283
  * 3. `false` (for undeclared keys with no override)
284
284
  */
285
- export function isAssistantFeatureFlagEnabled(
285
+ export function getAssistantFeatureFlagValue(
286
286
  key: string,
287
287
  _config: AssistantConfig,
288
- ): boolean {
288
+ ): boolean | string {
289
289
  const defaults = loadDefaultsRegistry();
290
290
  const declared = defaults[key];
291
291
  const overrides = loadOverrides();
292
292
 
293
- // 1. Check overrides from the gateway IPC cache.
294
293
  const explicit = overrides[key];
295
- if (typeof explicit === "boolean") return explicit;
294
+ if (explicit !== undefined) return explicit;
296
295
 
297
- // 2. For declared keys, use the registry default.
298
296
  if (declared) return declared.defaultEnabled;
299
297
 
300
- // 3. Undeclared keys with no override fail closed.
301
298
  return false;
302
299
  }
300
+
301
+ /**
302
+ * Resolve whether an assistant feature flag is enabled (boolean coercion).
303
+ *
304
+ * For boolean flags, returns the resolved value directly.
305
+ * For string flags, returns true if the value is non-empty.
306
+ * Undeclared keys return `false` (fail closed).
307
+ */
308
+ export function isAssistantFeatureFlagEnabled(
309
+ key: string,
310
+ config: AssistantConfig,
311
+ ): boolean {
312
+ return !!getAssistantFeatureFlagValue(key, config);
313
+ }