@vellumai/assistant 0.8.0 → 0.8.1

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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, rmSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import {
@@ -42,11 +42,15 @@ mock.module("../ipc/cli-client.js", () => ({
42
42
  params?: Record<string, unknown>,
43
43
  opts?: { timeoutMs?: number },
44
44
  ) => mockCliIpcCallFn(method, params, opts),
45
+ exitFromIpcResult: (r: { ok: false; error?: string; statusCode?: number }) => {
46
+ process.stderr.write((r.error ?? "Unknown error") + "\n");
47
+ process.exitCode = 10;
48
+ },
45
49
  }));
46
50
 
47
- let mockOpenInHostBrowserFn = mock(async (_url: string) => {});
51
+ let mockOpenInHostBrowserFn = mock((_url: string) => {});
48
52
 
49
- mock.module("../util/browser.js", () => ({
53
+ mock.module("../cli/lib/open-browser.js", () => ({
50
54
  openInHostBrowser: (url: string) => mockOpenInHostBrowserFn(url),
51
55
  }));
52
56
 
@@ -74,36 +78,11 @@ mock.module("../util/logger.js", () => ({
74
78
  }),
75
79
  }));
76
80
 
77
- mock.module("../mcp/mcp-oauth-provider.js", () => ({
78
- deleteMcpOAuthCredentials: async () => {},
79
- }));
80
-
81
- mock.module("../mcp/client.js", () => ({
82
- McpClient: class {
83
- async connect() {
84
- throw new Error("Connection refused");
85
- }
86
- async disconnect() {}
87
- get isConnected() {
88
- return false;
89
- }
90
- },
91
- }));
92
-
93
81
  const { registerMcpCommand } = await import("../cli/commands/mcp.js");
94
82
 
95
83
  // ── Helpers ───────────────────────────────────────────────────────────
96
84
 
97
85
  let testDataDir: string;
98
- let configPath: string;
99
-
100
- function writeConfig(config: Record<string, unknown>): void {
101
- writeFileSync(configPath, JSON.stringify(config), "utf-8");
102
- }
103
-
104
- function readConfig(): Record<string, unknown> {
105
- return JSON.parse(readFileSync(configPath, "utf-8"));
106
- }
107
86
 
108
87
  async function runMcp(
109
88
  subcommand: string,
@@ -128,7 +107,7 @@ async function runMcp(
128
107
  if (code !== undefined) process.exitCode = code;
129
108
  }) as typeof process.exit;
130
109
 
131
- // Point config loader at the test data dir
110
+ // Point workspace dir at the test data dir
132
111
  process.env.VELLUM_WORKSPACE_DIR = testDataDir;
133
112
 
134
113
  try {
@@ -174,8 +153,6 @@ describe("assistant mcp list", () => {
174
153
  `vellum-mcp-cli-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
175
154
  );
176
155
  mkdirSync(testDataDir, { recursive: true });
177
- configPath = join(testDataDir, "config.json");
178
- writeConfig({});
179
156
  });
180
157
 
181
158
  afterAll(() => {
@@ -183,7 +160,12 @@ describe("assistant mcp list", () => {
183
160
  });
184
161
 
185
162
  beforeEach(() => {
186
- writeConfig({});
163
+ mockCliIpcCallFn = mock(() =>
164
+ Promise.resolve({
165
+ ok: true,
166
+ result: { servers: [] },
167
+ }),
168
+ );
187
169
  });
188
170
 
189
171
  test("shows message when no MCP servers configured", async () => {
@@ -193,20 +175,25 @@ describe("assistant mcp list", () => {
193
175
  });
194
176
 
195
177
  test("lists configured servers", async () => {
196
- writeConfig({
197
- mcp: {
198
- servers: {
199
- "test-server": {
200
- transport: {
201
- type: "streamable-http",
202
- url: "https://example.com/mcp",
178
+ mockCliIpcCallFn = mock(() =>
179
+ Promise.resolve({
180
+ ok: true,
181
+ result: {
182
+ servers: [
183
+ {
184
+ id: "test-server",
185
+ status: "✓ Connected",
186
+ transport: {
187
+ type: "streamable-http",
188
+ url: "https://example.com/mcp",
189
+ },
190
+ enabled: true,
191
+ defaultRiskLevel: "medium",
203
192
  },
204
- enabled: true,
205
- defaultRiskLevel: "medium",
206
- },
193
+ ],
207
194
  },
208
- },
209
- });
195
+ }),
196
+ );
210
197
 
211
198
  const { stdout, exitCode } = await runMcpList();
212
199
  expect(exitCode).toBe(0);
@@ -218,17 +205,22 @@ describe("assistant mcp list", () => {
218
205
  });
219
206
 
220
207
  test("shows disabled status", async () => {
221
- writeConfig({
222
- mcp: {
223
- servers: {
224
- "disabled-server": {
225
- transport: { type: "sse", url: "https://example.com/sse" },
226
- enabled: false,
227
- defaultRiskLevel: "high",
228
- },
208
+ mockCliIpcCallFn = mock(() =>
209
+ Promise.resolve({
210
+ ok: true,
211
+ result: {
212
+ servers: [
213
+ {
214
+ id: "disabled-server",
215
+ status: "✗ disabled",
216
+ transport: { type: "sse", url: "https://example.com/sse" },
217
+ enabled: false,
218
+ defaultRiskLevel: "high",
219
+ },
220
+ ],
229
221
  },
230
- },
231
- });
222
+ }),
223
+ );
232
224
 
233
225
  const { stdout, exitCode } = await runMcpList();
234
226
  expect(exitCode).toBe(0);
@@ -236,21 +228,26 @@ describe("assistant mcp list", () => {
236
228
  });
237
229
 
238
230
  test("shows stdio command info", async () => {
239
- writeConfig({
240
- mcp: {
241
- servers: {
242
- "stdio-server": {
243
- transport: {
244
- type: "stdio",
245
- command: "npx",
246
- args: ["-y", "some-mcp-server"],
231
+ mockCliIpcCallFn = mock(() =>
232
+ Promise.resolve({
233
+ ok: true,
234
+ result: {
235
+ servers: [
236
+ {
237
+ id: "stdio-server",
238
+ status: " Connected",
239
+ transport: {
240
+ type: "stdio",
241
+ command: "npx",
242
+ args: ["-y", "some-mcp-server"],
243
+ },
244
+ enabled: true,
245
+ defaultRiskLevel: "low",
247
246
  },
248
- enabled: true,
249
- defaultRiskLevel: "low",
250
- },
247
+ ],
251
248
  },
252
- },
253
- });
249
+ }),
250
+ );
254
251
 
255
252
  const { stdout, exitCode } = await runMcpList();
256
253
  expect(exitCode).toBe(0);
@@ -261,20 +258,25 @@ describe("assistant mcp list", () => {
261
258
  });
262
259
 
263
260
  test("--json outputs valid JSON", async () => {
264
- writeConfig({
265
- mcp: {
266
- servers: {
267
- "json-server": {
268
- transport: {
269
- type: "streamable-http",
270
- url: "https://example.com/mcp",
261
+ mockCliIpcCallFn = mock(() =>
262
+ Promise.resolve({
263
+ ok: true,
264
+ result: {
265
+ servers: [
266
+ {
267
+ id: "json-server",
268
+ status: "✓ Connected",
269
+ transport: {
270
+ type: "streamable-http",
271
+ url: "https://example.com/mcp",
272
+ },
273
+ enabled: true,
274
+ defaultRiskLevel: "high",
271
275
  },
272
- enabled: true,
273
- defaultRiskLevel: "high",
274
- },
276
+ ],
275
277
  },
276
- },
277
- });
278
+ }),
279
+ );
278
280
 
279
281
  const { stdout, exitCode } = await runMcpList(["--json"]);
280
282
  expect(exitCode).toBe(0);
@@ -300,8 +302,6 @@ describe("assistant mcp add", () => {
300
302
  `vellum-mcp-add-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
301
303
  );
302
304
  mkdirSync(testDataDir, { recursive: true });
303
- configPath = join(testDataDir, "config.json");
304
- writeConfig({});
305
305
  });
306
306
 
307
307
  afterAll(() => {
@@ -309,7 +309,9 @@ describe("assistant mcp add", () => {
309
309
  });
310
310
 
311
311
  beforeEach(() => {
312
- writeConfig({});
312
+ mockCliIpcCallFn = mock(() =>
313
+ Promise.resolve({ ok: true, result: { added: true } }),
314
+ );
313
315
  });
314
316
 
315
317
  test("adds a streamable-http server", async () => {
@@ -324,19 +326,19 @@ describe("assistant mcp add", () => {
324
326
  expect(exitCode).toBe(0);
325
327
  expect(stdout).toContain('Added MCP server "test-http"');
326
328
 
327
- const updated = readConfig();
328
- const servers = (updated.mcp as Record<string, unknown> | undefined)
329
- ?.servers as Record<string, unknown> | undefined;
330
- const server = servers?.["test-http"] as Record<string, unknown>;
331
- expect(server).toBeDefined();
332
- expect((server.transport as Record<string, unknown>).type).toBe(
333
- "streamable-http",
329
+ // Verify IPC was called with correct params
330
+ const addCall = mockCliIpcCallFn.mock.calls.find(
331
+ (c) => c[0] === "internal_mcp_add",
334
332
  );
335
- expect((server.transport as Record<string, unknown>).url).toBe(
336
- "https://example.com/mcp",
337
- );
338
- expect(server.defaultRiskLevel).toBe("medium");
339
- expect(server.enabled).toBe(true);
333
+ expect(addCall).toBeDefined();
334
+ const body = (addCall![1] as Record<string, unknown>).body as Record<
335
+ string,
336
+ unknown
337
+ >;
338
+ expect(body.name).toBe("test-http");
339
+ expect(body.transportType).toBe("streamable-http");
340
+ expect(body.url).toBe("https://example.com/mcp");
341
+ expect(body.risk).toBe("medium");
340
342
  });
341
343
 
342
344
  test("adds a stdio server with args", async () => {
@@ -354,14 +356,17 @@ describe("assistant mcp add", () => {
354
356
  expect(exitCode).toBe(0);
355
357
  expect(stdout).toContain('Added MCP server "test-stdio"');
356
358
 
357
- const updated = readConfig();
358
- const servers = (updated.mcp as Record<string, unknown> | undefined)
359
- ?.servers as Record<string, unknown> | undefined;
360
- const server = servers?.["test-stdio"] as Record<string, unknown>;
361
- const transport = server.transport as Record<string, unknown>;
362
- expect(transport.type).toBe("stdio");
363
- expect(transport.command).toBe("npx");
364
- expect(transport.args).toEqual(["-y", "some-server"]);
359
+ const addCall = mockCliIpcCallFn.mock.calls.find(
360
+ (c) => c[0] === "internal_mcp_add",
361
+ );
362
+ expect(addCall).toBeDefined();
363
+ const body = (addCall![1] as Record<string, unknown>).body as Record<
364
+ string,
365
+ unknown
366
+ >;
367
+ expect(body.transportType).toBe("stdio");
368
+ expect(body.command).toBe("npx");
369
+ expect(body.args).toEqual(["-y", "some-server"]);
365
370
  });
366
371
 
367
372
  test("adds server as disabled with --disabled flag", async () => {
@@ -374,25 +379,24 @@ describe("assistant mcp add", () => {
374
379
  ]);
375
380
  expect(exitCode).toBe(0);
376
381
 
377
- const updated = readConfig();
378
- const servers = (updated.mcp as Record<string, unknown> | undefined)
379
- ?.servers as Record<string, unknown> | undefined;
380
- const server = servers?.["test-disabled"] as Record<string, unknown>;
381
- expect(server.enabled).toBe(false);
382
- });
383
-
384
- test("rejects duplicate server name", async () => {
385
- writeConfig({
386
- mcp: {
387
- servers: {
388
- existing: {
389
- transport: { type: "sse", url: "https://example.com" },
390
- enabled: true,
391
- defaultRiskLevel: "high",
392
- },
393
- },
394
- },
395
- });
382
+ const addCall = mockCliIpcCallFn.mock.calls.find(
383
+ (c) => c[0] === "internal_mcp_add",
384
+ );
385
+ expect(addCall).toBeDefined();
386
+ const body = (addCall![1] as Record<string, unknown>).body as Record<
387
+ string,
388
+ unknown
389
+ >;
390
+ expect(body.disabled).toBe(true);
391
+ });
392
+
393
+ test("rejects duplicate server name via IPC error", async () => {
394
+ mockCliIpcCallFn = mock(() =>
395
+ Promise.resolve({
396
+ ok: false,
397
+ error: 'MCP server "existing" already exists. Remove it first with: assistant mcp remove existing',
398
+ }),
399
+ );
396
400
 
397
401
  const { stderr } = await runMcpAdd("existing", [
398
402
  "-t",
@@ -403,12 +407,26 @@ describe("assistant mcp add", () => {
403
407
  expect(stderr).toContain("already exists");
404
408
  });
405
409
 
406
- test("rejects stdio without --command", async () => {
410
+ test("rejects stdio without --command via IPC error", async () => {
411
+ mockCliIpcCallFn = mock(() =>
412
+ Promise.resolve({
413
+ ok: false,
414
+ error: "--command is required for stdio transport",
415
+ }),
416
+ );
417
+
407
418
  const { stderr } = await runMcpAdd("bad-stdio", ["-t", "stdio"]);
408
419
  expect(stderr).toContain("--command is required");
409
420
  });
410
421
 
411
- test("rejects streamable-http without --url", async () => {
422
+ test("rejects streamable-http without --url via IPC error", async () => {
423
+ mockCliIpcCallFn = mock(() =>
424
+ Promise.resolve({
425
+ ok: false,
426
+ error: "--url is required for streamable-http transport",
427
+ }),
428
+ );
429
+
412
430
  const { stderr } = await runMcpAdd("bad-http", ["-t", "streamable-http"]);
413
431
  expect(stderr).toContain("--url is required");
414
432
  });
@@ -422,15 +440,21 @@ describe("assistant mcp add", () => {
422
440
  ]);
423
441
  expect(exitCode).toBe(0);
424
442
 
425
- const updated = readConfig();
426
- const servers = (updated.mcp as Record<string, unknown> | undefined)
427
- ?.servers as Record<string, unknown> | undefined;
428
- const server = servers?.["default-risk"] as Record<string, unknown>;
429
- expect(server.defaultRiskLevel).toBe("high");
443
+ const addCall = mockCliIpcCallFn.mock.calls.find(
444
+ (c) => c[0] === "internal_mcp_add",
445
+ );
446
+ expect(addCall).toBeDefined();
447
+ const body = (addCall![1] as Record<string, unknown>).body as Record<
448
+ string,
449
+ unknown
450
+ >;
451
+ expect(body.risk).toBe("high");
430
452
  });
431
453
 
432
- test("calls cliIpcCall with internal_mcp_reload after saving server", async () => {
433
- mockCliIpcCallFn = mock(() => Promise.resolve({ ok: true }));
454
+ test("calls cliIpcCall with internal_mcp_add", async () => {
455
+ mockCliIpcCallFn = mock(() =>
456
+ Promise.resolve({ ok: true, result: { added: true } }),
457
+ );
434
458
 
435
459
  await runMcpAdd("reload-test-server", [
436
460
  "-t",
@@ -439,11 +463,10 @@ describe("assistant mcp add", () => {
439
463
  "https://example.com/sse",
440
464
  ]);
441
465
 
442
- const reloadCall = mockCliIpcCallFn.mock.calls.find(
443
- (c) => c[0] === "internal_mcp_reload",
466
+ const addCall = mockCliIpcCallFn.mock.calls.find(
467
+ (c) => c[0] === "internal_mcp_add",
444
468
  );
445
- expect(reloadCall).toBeDefined();
446
- expect(reloadCall![1]).toEqual({ body: {} });
469
+ expect(addCall).toBeDefined();
447
470
  });
448
471
  });
449
472
 
@@ -456,8 +479,6 @@ describe("assistant mcp remove", () => {
456
479
  .slice(2)}`,
457
480
  );
458
481
  mkdirSync(testDataDir, { recursive: true });
459
- configPath = join(testDataDir, "config.json");
460
- writeConfig({});
461
482
  });
462
483
 
463
484
  afterAll(() => {
@@ -465,68 +486,39 @@ describe("assistant mcp remove", () => {
465
486
  });
466
487
 
467
488
  beforeEach(() => {
468
- writeConfig({});
489
+ mockCliIpcCallFn = mock(() =>
490
+ Promise.resolve({ ok: true, result: { removed: true } }),
491
+ );
469
492
  });
470
493
 
471
494
  test("removes an existing server", async () => {
472
- writeConfig({
473
- mcp: {
474
- servers: {
475
- "my-server": {
476
- transport: { type: "sse", url: "https://example.com/sse" },
477
- enabled: true,
478
- defaultRiskLevel: "high",
479
- },
480
- },
481
- },
482
- });
483
-
484
495
  const { stdout, exitCode } = await runMcpRemove("my-server");
485
496
  expect(exitCode).toBe(0);
486
497
  expect(stdout).toContain('Removed MCP server "my-server"');
487
498
 
488
- const updated = readConfig();
489
- const servers = (updated.mcp as Record<string, unknown> | undefined)
490
- ?.servers as Record<string, unknown> | undefined;
491
- expect(servers?.["my-server"]).toBeUndefined();
499
+ const removeCall = mockCliIpcCallFn.mock.calls.find(
500
+ (c) => c[0] === "internal_mcp_remove",
501
+ );
502
+ expect(removeCall).toBeDefined();
503
+ const body = (removeCall![1] as Record<string, unknown>).body as Record<
504
+ string,
505
+ unknown
506
+ >;
507
+ expect(body.name).toBe("my-server");
492
508
  });
493
509
 
494
510
  test("errors when server does not exist", async () => {
511
+ mockCliIpcCallFn = mock(() =>
512
+ Promise.resolve({
513
+ ok: false,
514
+ error: 'MCP server "nonexistent" not found.',
515
+ }),
516
+ );
517
+
495
518
  const { stderr, exitCode } = await runMcpRemove("nonexistent");
496
519
  expect(exitCode).toBe(1);
497
520
  expect(stderr).toContain("not found");
498
521
  });
499
-
500
- test("preserves other servers when removing one", async () => {
501
- writeConfig({
502
- mcp: {
503
- servers: {
504
- "keep-me": {
505
- transport: {
506
- type: "streamable-http",
507
- url: "https://example.com/keep",
508
- },
509
- enabled: true,
510
- defaultRiskLevel: "low",
511
- },
512
- "remove-me": {
513
- transport: { type: "sse", url: "https://example.com/remove" },
514
- enabled: true,
515
- defaultRiskLevel: "high",
516
- },
517
- },
518
- },
519
- });
520
-
521
- const { exitCode } = await runMcpRemove("remove-me");
522
- expect(exitCode).toBe(0);
523
-
524
- const updated = readConfig();
525
- const servers = (updated.mcp as Record<string, unknown> | undefined)
526
- ?.servers as Record<string, unknown> | undefined;
527
- expect(servers?.["remove-me"]).toBeUndefined();
528
- expect(servers?.["keep-me"]).toBeDefined();
529
- });
530
522
  });
531
523
 
532
524
  describe("assistant mcp reload", () => {
@@ -536,8 +528,6 @@ describe("assistant mcp reload", () => {
536
528
  `vellum-mcp-reload-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
537
529
  );
538
530
  mkdirSync(testDataDir, { recursive: true });
539
- configPath = join(testDataDir, "config.json");
540
- writeConfig({});
541
531
  });
542
532
 
543
533
  afterAll(() => {
@@ -575,49 +565,26 @@ describe("assistant mcp reload", () => {
575
565
  });
576
566
 
577
567
  describe("assistant mcp auth — IPC path", () => {
578
- let ipcTestDataDir: string;
579
- let ipcConfigPath: string;
580
-
581
568
  beforeAll(() => {
582
- ipcTestDataDir = join(
569
+ testDataDir = join(
583
570
  tmpdir(),
584
571
  `vellum-mcp-auth-ipc-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
585
572
  );
586
- mkdirSync(ipcTestDataDir, { recursive: true });
587
- ipcConfigPath = join(ipcTestDataDir, "config.json");
588
- writeFileSync(
589
- ipcConfigPath,
590
- JSON.stringify({
591
- mcp: {
592
- servers: {
593
- srv: {
594
- transport: { type: "sse", url: "https://mcp.example.com/sse" },
595
- enabled: true,
596
- defaultRiskLevel: "high",
597
- },
598
- },
599
- },
600
- }),
601
- "utf-8",
602
- );
573
+ mkdirSync(testDataDir, { recursive: true });
603
574
  });
604
575
 
605
576
  afterAll(() => {
606
- rmSync(ipcTestDataDir, { recursive: true, force: true });
577
+ rmSync(testDataDir, { recursive: true, force: true });
607
578
  });
608
579
 
609
580
  beforeEach(() => {
610
- // runMcp() sets VELLUM_WORKSPACE_DIR = testDataDir; align the module-level
611
- // variable with the IPC-specific directory so the config loader sees the
612
- // SSE server config created in beforeAll.
613
- testDataDir = ipcTestDataDir;
614
581
  mockCliIpcCallFn = mock(() =>
615
582
  Promise.resolve({
616
583
  ok: false,
617
584
  error: "Could not connect to assistant daemon. Is it running?",
618
585
  }),
619
586
  );
620
- mockOpenInHostBrowserFn = mock(async (_url: string) => {});
587
+ mockOpenInHostBrowserFn = mock((_url: string) => {});
621
588
  stdoutLines = [];
622
589
  stderrLines = [];
623
590
  process.exitCode = 0;
@@ -680,7 +647,7 @@ describe("assistant mcp auth — IPC path", () => {
680
647
  expect(stderr).toContain("Is it running?");
681
648
  });
682
649
 
683
- test("IPC start returns ok=false with a real daemon error → exits 1 without falling back to loopback", async () => {
650
+ test("IPC start returns ok=false with a real daemon error → exits 1 without opening browser", async () => {
684
651
  mockCliIpcCallFn = mock(() =>
685
652
  Promise.resolve({
686
653
  ok: false,
@@ -690,9 +657,6 @@ describe("assistant mcp auth — IPC path", () => {
690
657
 
691
658
  await runMcp("auth", ["srv"]);
692
659
 
693
- // Should have surfaced the daemon error and set exit code 1, not fallen
694
- // back to loopback (which would have constructed the OAuth provider and
695
- // possibly opened a browser via the loopback path).
696
660
  expect(process.exitCode).toBe(1);
697
661
  expect(mockOpenInHostBrowserFn).not.toHaveBeenCalled();
698
662
  // Exactly one IPC call (the start) — no polling, no retry.
@@ -700,7 +664,7 @@ describe("assistant mcp auth — IPC path", () => {
700
664
  expect(mockCliIpcCallFn.mock.calls[0][0]).toBe("internal_mcp_auth_start");
701
665
  });
702
666
 
703
- test("polling complete → exits 0 (daemon-side reload triggers itself; CLI no longer writes file signal)", async () => {
667
+ test("polling complete → exits 0", async () => {
704
668
  let ipcCallIndex = 0;
705
669
  mockCliIpcCallFn = mock(() => {
706
670
  ipcCallIndex++;
@@ -720,8 +684,6 @@ describe("assistant mcp auth — IPC path", () => {
720
684
 
721
685
  expect(exitCode).toBe(0);
722
686
  expect(stdout).toContain("Authentication successful");
723
- // The IPC success path relies on the daemon-side orchestrator to call
724
- // reloadMcpServers() itself on completion — no CLI-side signal needed.
725
687
  });
726
688
 
727
689
  test("polling error → exits 1 with error message", async () => {