@vellumai/assistant 0.8.0 → 0.8.2

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 (991) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +2 -7
  3. package/Dockerfile +80 -5
  4. package/README.md +2 -2
  5. package/bun.lock +11 -1
  6. package/docker-entrypoint.sh +21 -0
  7. package/docker-init-apt-root.sh +94 -0
  8. package/docker-kata-apt-env.sh +39 -0
  9. package/docs/plugins.md +88 -47
  10. package/docs/skills.md +9 -7
  11. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  12. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  13. package/eslint.config.mjs +12 -0
  14. package/examples/plugins/echo/README.md +27 -27
  15. package/examples/plugins/echo/package.json +3 -0
  16. package/examples/plugins/echo/register.ts +31 -31
  17. package/knip.json +2 -1
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  20. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  21. package/openapi.yaml +4462 -991
  22. package/package.json +5 -1
  23. package/scripts/generate-openapi.ts +135 -14
  24. package/scripts/sync-llm-catalog.ts +165 -0
  25. package/scripts/sync-web-search-catalog.ts +129 -0
  26. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  27. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  28. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  29. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  30. package/src/__tests__/anthropic-provider.test.ts +137 -2
  31. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  32. package/src/__tests__/app-control-flow.test.ts +7 -0
  33. package/src/__tests__/app-executors.test.ts +220 -4
  34. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  35. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  36. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  37. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  38. package/src/__tests__/btw-routes.test.ts +1 -0
  39. package/src/__tests__/bundled-asset.test.ts +6 -6
  40. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  41. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  42. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  43. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  44. package/src/__tests__/channel-policy.test.ts +12 -0
  45. package/src/__tests__/checker.test.ts +89 -0
  46. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  47. package/src/__tests__/clawhub.test.ts +75 -16
  48. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  49. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  50. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  51. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  52. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  53. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  54. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  55. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  56. package/src/__tests__/config-schema.test.ts +35 -3
  57. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  58. package/src/__tests__/config-set-route.test.ts +278 -0
  59. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  60. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  61. package/src/__tests__/config-watcher.test.ts +6 -0
  62. package/src/__tests__/contacts-tools.test.ts +51 -199
  63. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  64. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  65. package/src/__tests__/context-search-conversations-source.test.ts +159 -18
  66. package/src/__tests__/context-search-fanout.test.ts +20 -157
  67. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -4
  68. package/src/__tests__/context-search-types.test.ts +7 -2
  69. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  70. package/src/__tests__/context-token-estimator.test.ts +1 -0
  71. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  72. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  73. package/src/__tests__/conversation-agent-loop-overflow.test.ts +93 -92
  74. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  75. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  76. package/src/__tests__/conversation-error.test.ts +80 -3
  77. package/src/__tests__/conversation-fork-crud.test.ts +323 -1
  78. package/src/__tests__/conversation-inference-profile-route.test.ts +54 -18
  79. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  80. package/src/__tests__/conversation-lifecycle.test.ts +297 -0
  81. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  82. package/src/__tests__/conversation-pairing.test.ts +54 -0
  83. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  84. package/src/__tests__/conversation-process-callsite.test.ts +25 -2
  85. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  86. package/src/__tests__/conversation-queue.test.ts +4 -1
  87. package/src/__tests__/conversation-runtime-assembly.test.ts +80 -13
  88. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  89. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  90. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  91. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  92. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  93. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  94. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  95. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  96. package/src/__tests__/credential-security-invariants.test.ts +8 -8
  97. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  98. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  99. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  100. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  101. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  102. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  103. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  104. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  105. package/src/__tests__/dm-backfill.test.ts +121 -10
  106. package/src/__tests__/document-tool-security.test.ts +258 -0
  107. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  108. package/src/__tests__/edit-propagation.test.ts +33 -0
  109. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  110. package/src/__tests__/external-plugin-loader.test.ts +482 -0
  111. package/src/__tests__/filing-service.test.ts +163 -3
  112. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  113. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  114. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  115. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  116. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +42 -69
  117. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  118. package/src/__tests__/heartbeat-service.test.ts +50 -233
  119. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  120. package/src/__tests__/helpers/wait-for.ts +21 -0
  121. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  122. package/src/__tests__/history-repair.test.ts +162 -0
  123. package/src/__tests__/host-app-control-proxy.test.ts +365 -1
  124. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  125. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  126. package/src/__tests__/host-browser-routes.test.ts +325 -33
  127. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  128. package/src/__tests__/image-credentials.test.ts +1 -1
  129. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  130. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  131. package/src/__tests__/inference-profile-reaper.test.ts +156 -0
  132. package/src/__tests__/inference-profile-session-handler.test.ts +410 -0
  133. package/src/__tests__/inference-profile-session-ipc.test.ts +248 -0
  134. package/src/__tests__/injector-chain.test.ts +10 -8
  135. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  136. package/src/__tests__/install-skill-routing.test.ts +157 -39
  137. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +107 -3
  138. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  139. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  140. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  141. package/src/__tests__/llm-catalog-parity.test.ts +190 -2
  142. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +222 -0
  143. package/src/__tests__/llm-request-log-source-factory.test.ts +100 -0
  144. package/src/__tests__/llm-resolver.test.ts +46 -0
  145. package/src/__tests__/llm-usage-store.test.ts +114 -0
  146. package/src/__tests__/managed-profile-guard.test.ts +145 -14
  147. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  148. package/src/__tests__/managed-store.test.ts +84 -192
  149. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  150. package/src/__tests__/mcp-cli.test.ts +182 -220
  151. package/src/__tests__/mcp-health-check.test.ts +56 -27
  152. package/src/__tests__/media-generate-image.test.ts +1 -1
  153. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  154. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  155. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  156. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  157. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  158. package/src/__tests__/oauth-cli.test.ts +38 -2009
  159. package/src/__tests__/oauth-commands-routes.test.ts +863 -0
  160. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  161. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  162. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  163. package/src/__tests__/openai-provider.test.ts +24 -0
  164. package/src/__tests__/openai-responses-cutover-guard.test.ts +48 -19
  165. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  166. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  167. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  168. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  169. package/src/__tests__/platform.test.ts +2 -0
  170. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  171. package/src/__tests__/plugin-bootstrap.test.ts +41 -38
  172. package/src/__tests__/plugin-external-api.test.ts +68 -0
  173. package/src/__tests__/plugin-registry.test.ts +0 -77
  174. package/src/__tests__/plugin-route-contribution.test.ts +31 -4
  175. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  176. package/src/__tests__/plugin-tool-contribution.test.ts +47 -18
  177. package/src/__tests__/plugin-types.test.ts +15 -23
  178. package/src/__tests__/process-message-background-slack.test.ts +53 -0
  179. package/src/__tests__/process-message-display-content.test.ts +421 -0
  180. package/src/__tests__/profile-entry-status.test.ts +43 -0
  181. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  182. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  183. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +20 -12
  184. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  185. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  186. package/src/__tests__/relay-server.test.ts +118 -0
  187. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  188. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  189. package/src/__tests__/schedule-retry.test.ts +56 -4
  190. package/src/__tests__/schedule-routes.test.ts +151 -0
  191. package/src/__tests__/schedule-store.test.ts +94 -0
  192. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  193. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  194. package/src/__tests__/scheduler-reuse-conversation.test.ts +208 -5
  195. package/src/__tests__/scheduler-wake.test.ts +0 -63
  196. package/src/__tests__/schema-transforms.test.ts +20 -0
  197. package/src/__tests__/search-skills-unified.test.ts +0 -5
  198. package/src/__tests__/secret-allowlist.test.ts +1 -0
  199. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +12 -4
  200. package/src/__tests__/server-history-render.test.ts +43 -0
  201. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  202. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  203. package/src/__tests__/skill-load-feature-flag.test.ts +1 -12
  204. package/src/__tests__/skill-load-tool.test.ts +29 -93
  205. package/src/__tests__/skill-memory.test.ts +23 -3
  206. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  207. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  208. package/src/__tests__/skills-install-extract.test.ts +49 -38
  209. package/src/__tests__/skills-install-staging.test.ts +159 -0
  210. package/src/__tests__/skills-uninstall.test.ts +9 -41
  211. package/src/__tests__/skills.test.ts +51 -58
  212. package/src/__tests__/slack-channel-config.test.ts +9 -0
  213. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  214. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  215. package/src/__tests__/suggestion-routes.test.ts +3 -3
  216. package/src/__tests__/sync-message-contract.test.ts +63 -0
  217. package/src/__tests__/system-prompt.test.ts +737 -63
  218. package/src/__tests__/task-scheduler.test.ts +88 -23
  219. package/src/__tests__/terminal-tools.test.ts +28 -1
  220. package/src/__tests__/thread-backfill.test.ts +557 -27
  221. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  222. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  223. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  224. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  225. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  226. package/src/__tests__/tool-executor.test.ts +16 -4
  227. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  228. package/src/__tests__/turn-events-store.test.ts +256 -0
  229. package/src/__tests__/twilio-routes.test.ts +4 -0
  230. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  231. package/src/__tests__/usage-cli.test.ts +11 -73
  232. package/src/__tests__/user-plugin-loader.test.ts +143 -5
  233. package/src/__tests__/vercel-config.test.ts +168 -0
  234. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  235. package/src/__tests__/web-search-catalog-parity.test.ts +108 -0
  236. package/src/__tests__/web-search.test.ts +303 -2
  237. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  238. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +170 -0
  239. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  240. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +241 -0
  241. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  242. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  243. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  244. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  245. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  246. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  247. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  248. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  249. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  250. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  251. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  252. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  253. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  254. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  255. package/src/acp/resolve-agent.test.ts +25 -0
  256. package/src/acp/resolve-agent.ts +13 -2
  257. package/src/acp/session-manager.ts +14 -0
  258. package/src/agent/image-optimize.ts +13 -5
  259. package/src/approvals/guardian-request-resolvers.ts +32 -87
  260. package/src/calls/relay-server.ts +35 -0
  261. package/src/calls/relay-setup-router.ts +36 -0
  262. package/src/calls/types.ts +1 -0
  263. package/src/calls/voice-session-bridge.ts +74 -36
  264. package/src/channels/config.ts +14 -1
  265. package/src/channels/types.ts +109 -0
  266. package/src/cli/AGENTS.md +164 -4
  267. package/src/cli/__tests__/notifications.test.ts +54 -0
  268. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  269. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  270. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  271. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  272. package/src/cli/commands/__tests__/changelog.test.ts +578 -0
  273. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  274. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  275. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  276. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  277. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  278. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  279. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  280. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  281. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  282. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  283. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  284. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  285. package/src/cli/commands/__tests__/status.test.ts +249 -0
  286. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  287. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  288. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  289. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  290. package/src/cli/commands/attachment.ts +8 -3
  291. package/src/cli/commands/audit.ts +95 -64
  292. package/src/cli/commands/auth.ts +61 -58
  293. package/src/cli/commands/avatar.ts +276 -390
  294. package/src/cli/commands/backup.ts +409 -505
  295. package/src/cli/commands/bash.ts +9 -5
  296. package/src/cli/commands/browser.ts +28 -9
  297. package/src/cli/commands/cache.ts +9 -4
  298. package/src/cli/commands/changelog.ts +478 -0
  299. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  300. package/src/cli/commands/clients.ts +8 -3
  301. package/src/cli/commands/completions.ts +9 -9
  302. package/src/cli/commands/config.ts +102 -72
  303. package/src/cli/commands/contacts.ts +575 -696
  304. package/src/cli/commands/conversations-defer.ts +17 -69
  305. package/src/cli/commands/conversations-import.ts +90 -253
  306. package/src/cli/commands/conversations.ts +429 -434
  307. package/src/cli/commands/credential-execution.ts +9 -6
  308. package/src/cli/commands/credentials.ts +456 -736
  309. package/src/cli/commands/default-action.ts +10 -53
  310. package/src/cli/commands/domain.ts +128 -206
  311. package/src/cli/commands/email.ts +606 -794
  312. package/src/cli/commands/gateway.ts +8 -1
  313. package/src/cli/commands/image-generation.ts +157 -205
  314. package/src/cli/commands/inference-providers.ts +352 -0
  315. package/src/cli/commands/inference-session.ts +415 -0
  316. package/src/cli/commands/inference.ts +87 -65
  317. package/src/cli/commands/keys.ts +8 -3
  318. package/src/cli/commands/mcp.ts +103 -287
  319. package/src/cli/commands/memory-v2.ts +162 -516
  320. package/src/cli/commands/notifications.ts +342 -304
  321. package/src/cli/commands/oauth/apps.ts +292 -261
  322. package/src/cli/commands/oauth/connect.ts +176 -297
  323. package/src/cli/commands/oauth/disconnect.ts +16 -215
  324. package/src/cli/commands/oauth/index.ts +49 -45
  325. package/src/cli/commands/oauth/mode.ts +43 -199
  326. package/src/cli/commands/oauth/ping.ts +17 -125
  327. package/src/cli/commands/oauth/providers.ts +732 -921
  328. package/src/cli/commands/oauth/request.ts +60 -350
  329. package/src/cli/commands/oauth/shared.ts +11 -121
  330. package/src/cli/commands/oauth/status.ts +31 -121
  331. package/src/cli/commands/oauth/token.ts +13 -55
  332. package/src/cli/commands/pending.ts +19 -10
  333. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  334. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  335. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  336. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  337. package/src/cli/commands/platform/connect.ts +16 -80
  338. package/src/cli/commands/platform/disconnect.ts +14 -112
  339. package/src/cli/commands/platform/index.ts +177 -246
  340. package/src/cli/commands/plugins.ts +185 -0
  341. package/src/cli/commands/routes.ts +153 -336
  342. package/src/cli/commands/schedules.ts +391 -0
  343. package/src/cli/commands/sequence.ts +316 -360
  344. package/src/cli/commands/skills.ts +449 -671
  345. package/src/cli/commands/status.ts +58 -37
  346. package/src/cli/commands/stt.ts +94 -262
  347. package/src/cli/commands/task.ts +14 -40
  348. package/src/cli/commands/telemetry.ts +40 -0
  349. package/src/cli/commands/trust.ts +8 -3
  350. package/src/cli/commands/tts.ts +162 -167
  351. package/src/cli/commands/ui.ts +35 -42
  352. package/src/cli/commands/usage.ts +188 -126
  353. package/src/cli/commands/watchers.ts +8 -3
  354. package/src/cli/commands/webhooks.ts +99 -193
  355. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  356. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  357. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  358. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  359. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  360. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  361. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  362. package/src/cli/lib/cli-colors.ts +12 -0
  363. package/src/cli/lib/confirm-prompt.ts +79 -0
  364. package/src/cli/lib/daemon-credential-client.ts +4 -5
  365. package/src/cli/lib/install-from-github.ts +304 -0
  366. package/src/cli/lib/list-installed-plugins.ts +137 -0
  367. package/src/cli/lib/nested-value.ts +44 -0
  368. package/src/cli/lib/open-browser.ts +36 -0
  369. package/src/cli/lib/register-command.ts +19 -0
  370. package/src/cli/lib/time-ago.ts +34 -0
  371. package/src/cli/lib/uninstall-plugin.ts +82 -0
  372. package/src/cli/lib/unknown-command.ts +111 -0
  373. package/src/cli/program.ts +40 -6
  374. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  375. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  376. package/src/cli/utils/conversation-id.ts +30 -0
  377. package/src/cli/utils/parse-duration.ts +41 -0
  378. package/src/config/acp-defaults.test.ts +5 -1
  379. package/src/config/acp-defaults.ts +11 -4
  380. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  381. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  382. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  383. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  384. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  385. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  386. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  387. package/src/config/bundled-skills/document/SKILL.md +23 -3
  388. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  389. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  390. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  391. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  392. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  393. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  394. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  395. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  396. package/src/config/bundled-tool-registry.ts +6 -2
  397. package/src/config/feature-flag-registry.json +57 -1
  398. package/src/config/llm-resolver.ts +16 -1
  399. package/src/config/loader.ts +140 -52
  400. package/src/config/raw-config-utils.ts +2 -30
  401. package/src/config/schema.ts +8 -7
  402. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  403. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  404. package/src/config/schemas/call-site-catalog.ts +29 -7
  405. package/src/config/schemas/channels.ts +8 -0
  406. package/src/config/schemas/compaction.ts +28 -0
  407. package/src/config/schemas/heartbeat.ts +9 -0
  408. package/src/config/schemas/llm-request-logs.ts +81 -0
  409. package/src/config/schemas/llm.ts +55 -2
  410. package/src/config/schemas/memory-retrieval.ts +18 -0
  411. package/src/config/schemas/memory-retrospective.ts +48 -0
  412. package/src/config/schemas/memory-v2.ts +32 -1
  413. package/src/config/schemas/memory.ts +4 -0
  414. package/src/config/schemas/services.ts +15 -12
  415. package/src/config/schemas/tools.ts +14 -0
  416. package/src/config/seed-inference-profiles.ts +195 -134
  417. package/src/config/skills.ts +3 -96
  418. package/src/contacts/contact-store.ts +0 -61
  419. package/src/context/compactor.ts +1047 -0
  420. package/src/context/token-estimator.ts +2 -2
  421. package/src/context/window-manager.ts +197 -1334
  422. package/src/credential-execution/managed-catalog.ts +37 -0
  423. package/src/credential-health/credential-health-service.ts +280 -19
  424. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +113 -0
  425. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  426. package/src/daemon/__tests__/conversation-tool-setup.test.ts +183 -4
  427. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  428. package/src/daemon/approval-generators.ts +26 -30
  429. package/src/daemon/config-watcher.ts +94 -29
  430. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  431. package/src/daemon/conversation-agent-loop.ts +293 -103
  432. package/src/daemon/conversation-error.ts +188 -33
  433. package/src/daemon/conversation-lifecycle.ts +80 -26
  434. package/src/daemon/conversation-messaging.ts +25 -6
  435. package/src/daemon/conversation-process.ts +85 -31
  436. package/src/daemon/conversation-runtime-assembly.ts +30 -6
  437. package/src/daemon/conversation-slash.ts +184 -25
  438. package/src/daemon/conversation-store.ts +24 -10
  439. package/src/daemon/conversation-surfaces.ts +76 -12
  440. package/src/daemon/conversation-tool-setup.ts +63 -21
  441. package/src/daemon/conversation.ts +81 -10
  442. package/src/daemon/external-plugins-bootstrap.ts +231 -185
  443. package/src/daemon/first-greeting.ts +22 -2
  444. package/src/daemon/guardian-action-generators.ts +7 -22
  445. package/src/daemon/handlers/config-model.ts +13 -130
  446. package/src/daemon/handlers/config-slack-channel.ts +25 -10
  447. package/src/daemon/handlers/config-vercel.ts +3 -1
  448. package/src/daemon/handlers/shared.ts +14 -5
  449. package/src/daemon/handlers/skills.ts +166 -84
  450. package/src/daemon/history-repair.ts +61 -7
  451. package/src/daemon/host-app-control-proxy.ts +129 -29
  452. package/src/daemon/host-bash-proxy.ts +85 -158
  453. package/src/daemon/host-browser-proxy.ts +96 -35
  454. package/src/daemon/host-proxy-base.ts +13 -1
  455. package/src/daemon/host-proxy-preactivation.ts +25 -1
  456. package/src/daemon/identity-helpers.ts +19 -0
  457. package/src/daemon/lifecycle.ts +79 -70
  458. package/src/daemon/meet-host-supervisor.ts +20 -19
  459. package/src/daemon/memory-v2-startup.ts +58 -2
  460. package/src/daemon/message-protocol.ts +7 -0
  461. package/src/daemon/message-types/bookmarks.ts +18 -0
  462. package/src/daemon/message-types/conversations.ts +37 -9
  463. package/src/daemon/message-types/messages.ts +70 -1
  464. package/src/daemon/message-types/subagents.ts +1 -0
  465. package/src/daemon/message-types/sync.ts +61 -0
  466. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  467. package/src/daemon/pkb-reminder-builder.ts +21 -7
  468. package/src/daemon/plugin-source-watcher.ts +146 -0
  469. package/src/daemon/process-message.ts +77 -26
  470. package/src/daemon/server.ts +34 -20
  471. package/src/daemon/shutdown-handlers.ts +0 -2
  472. package/src/daemon/skill-memory-refresh.ts +29 -0
  473. package/src/daemon/tool-setup-types.ts +9 -0
  474. package/src/daemon/tool-side-effects.ts +6 -4
  475. package/src/daemon/wake-target-adapter.ts +11 -0
  476. package/src/documents/document-store.ts +221 -3
  477. package/src/embedded/plugin-api.ts +40 -0
  478. package/src/export/transcript-formatter.ts +61 -2
  479. package/src/filing/filing-service.ts +79 -53
  480. package/src/heartbeat/__tests__/heartbeat-service.test.ts +444 -0
  481. package/src/heartbeat/heartbeat-run-store.ts +3 -1
  482. package/src/heartbeat/heartbeat-service.ts +189 -127
  483. package/src/home/__tests__/feed-types.test.ts +99 -127
  484. package/src/home/__tests__/feed-writer.test.ts +77 -278
  485. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  486. package/src/home/feed-types.ts +41 -73
  487. package/src/home/feed-writer.ts +25 -156
  488. package/src/home/post-connect-feed.ts +2 -3
  489. package/src/index.ts +18 -1
  490. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  491. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  492. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  493. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  494. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  495. package/src/ipc/assistant-server.ts +55 -6
  496. package/src/ipc/cli-client.ts +370 -50
  497. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  498. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  499. package/src/ipc/skill-routes/events.ts +30 -3
  500. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  501. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  502. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  503. package/src/live-voice/live-voice-session-manager.ts +11 -4
  504. package/src/live-voice/live-voice-session.ts +14 -6
  505. package/src/mcp/client.ts +20 -4
  506. package/src/media/image-credentials.ts +3 -3
  507. package/src/memory/__tests__/bookmark-crud.test.ts +264 -0
  508. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  509. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  510. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  511. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  512. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  513. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  514. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  515. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +318 -0
  516. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  517. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  518. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  519. package/src/memory/__tests__/message-content.test.ts +35 -0
  520. package/src/memory/bookmark-crud.ts +211 -0
  521. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  522. package/src/memory/context-search/agent-protocol.ts +5 -1
  523. package/src/memory/context-search/agent-runner.ts +60 -85
  524. package/src/memory/context-search/limits.ts +1 -4
  525. package/src/memory/context-search/search.ts +23 -113
  526. package/src/memory/context-search/sources/conversations.ts +80 -8
  527. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  528. package/src/memory/context-search/sources/memory.ts +7 -0
  529. package/src/memory/context-search/sources/workspace.ts +17 -10
  530. package/src/memory/context-search/types.ts +1 -1
  531. package/src/memory/conversation-bootstrap.ts +11 -0
  532. package/src/memory/conversation-crud.ts +368 -22
  533. package/src/memory/conversation-queries.ts +116 -12
  534. package/src/memory/conversation-title-service.ts +1 -0
  535. package/src/memory/conversation-types.ts +16 -0
  536. package/src/memory/db-init.ts +20 -0
  537. package/src/memory/delivery-crud.ts +152 -5
  538. package/src/memory/embedding-backend.ts +6 -5
  539. package/src/memory/embedding-runtime-manager.ts +1 -2
  540. package/src/memory/external-conversation-store.ts +66 -5
  541. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  542. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  543. package/src/memory/graph/conversation-graph-memory.ts +92 -5
  544. package/src/memory/graph/extraction.ts +4 -0
  545. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  546. package/src/memory/graph/tool-handlers.ts +17 -7
  547. package/src/memory/graph/tools.ts +45 -6
  548. package/src/memory/indexer.ts +51 -29
  549. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +86 -15
  550. package/src/memory/jobs/embed-concept-page.ts +65 -20
  551. package/src/memory/jobs-store.ts +51 -1
  552. package/src/memory/jobs-worker.ts +57 -3
  553. package/src/memory/llm-request-log-source-clickhouse.ts +324 -0
  554. package/src/memory/llm-request-log-source-local.ts +26 -0
  555. package/src/memory/llm-request-log-source.ts +64 -0
  556. package/src/memory/llm-request-log-store.ts +1 -1
  557. package/src/memory/llm-usage-store.ts +125 -5
  558. package/src/memory/memory-retrospective-constants.ts +13 -0
  559. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  560. package/src/memory/memory-retrospective-job.ts +351 -0
  561. package/src/memory/memory-retrospective-startup-cleanup.ts +175 -0
  562. package/src/memory/memory-retrospective-state.ts +162 -0
  563. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  564. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  565. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  566. package/src/memory/message-content.ts +38 -1
  567. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  568. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  569. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  570. package/src/memory/migrations/229-delete-private-conversations.test.ts +107 -1
  571. package/src/memory/migrations/229-delete-private-conversations.ts +19 -0
  572. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  573. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  574. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  575. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  576. package/src/memory/migrations/243-provider-connections.ts +68 -0
  577. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  578. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  579. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  580. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  581. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  582. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  583. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  584. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  585. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  586. package/src/memory/migrations/index.ts +13 -0
  587. package/src/memory/migrations/registry.ts +8 -0
  588. package/src/memory/onboarding-events-store.ts +106 -0
  589. package/src/memory/published-pages-store.ts +16 -0
  590. package/src/memory/schema/bookmarks.ts +36 -0
  591. package/src/memory/schema/calls.ts +1 -0
  592. package/src/memory/schema/conversations.ts +2 -0
  593. package/src/memory/schema/index.ts +2 -0
  594. package/src/memory/schema/inference.ts +27 -0
  595. package/src/memory/schema/infrastructure.ts +12 -0
  596. package/src/memory/schema/memory-core.ts +9 -0
  597. package/src/memory/search/semantic.ts +1 -4
  598. package/src/memory/turn-events-store.ts +127 -2
  599. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  600. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  601. package/src/memory/v2/__tests__/activation.test.ts +11 -12
  602. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  603. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  604. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  605. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  606. package/src/memory/v2/__tests__/injection.test.ts +726 -18
  607. package/src/memory/v2/__tests__/migration.test.ts +94 -3
  608. package/src/memory/v2/__tests__/page-index.test.ts +360 -0
  609. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  610. package/src/memory/v2/__tests__/prompts-router.test.ts +309 -0
  611. package/src/memory/v2/__tests__/qdrant.test.ts +138 -3
  612. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  613. package/src/memory/v2/__tests__/router.test.ts +531 -0
  614. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  615. package/src/memory/v2/__tests__/skill-store.test.ts +445 -11
  616. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  617. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  618. package/src/memory/v2/activation-store.ts +34 -5
  619. package/src/memory/v2/activation.ts +40 -27
  620. package/src/memory/v2/backfill-jobs.ts +17 -84
  621. package/src/memory/v2/consolidation-job.ts +85 -78
  622. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  623. package/src/memory/v2/injection.ts +466 -109
  624. package/src/memory/v2/migration.ts +147 -20
  625. package/src/memory/v2/page-index.ts +221 -0
  626. package/src/memory/v2/page-store.ts +3 -0
  627. package/src/memory/v2/prompts/consolidation.ts +9 -7
  628. package/src/memory/v2/prompts/router.ts +195 -0
  629. package/src/memory/v2/prompts/sweep.ts +2 -2
  630. package/src/memory/v2/qdrant.ts +234 -93
  631. package/src/memory/v2/reranker.ts +14 -7
  632. package/src/memory/v2/router.ts +323 -0
  633. package/src/memory/v2/sim.ts +25 -12
  634. package/src/memory/v2/skill-store.ts +204 -30
  635. package/src/memory/v2/static-context.ts +16 -9
  636. package/src/memory/v2/sweep-job.ts +122 -96
  637. package/src/memory/v2/types.ts +10 -6
  638. package/src/memory/validation.ts +13 -0
  639. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  640. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  641. package/src/messaging/providers/slack/adapter.ts +43 -5
  642. package/src/messaging/providers/slack/client.ts +27 -0
  643. package/src/messaging/providers/slack/deep-link.ts +65 -0
  644. package/src/messaging/providers/slack/download.ts +104 -0
  645. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  646. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  647. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  648. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  649. package/src/messaging/providers/slack/types.ts +20 -1
  650. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  651. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  652. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  653. package/src/notifications/adapters/platform.ts +171 -0
  654. package/src/notifications/conversation-pairing.ts +4 -3
  655. package/src/notifications/copy-composer.ts +15 -0
  656. package/src/notifications/decision-engine.ts +2 -1
  657. package/src/notifications/destination-resolver.ts +21 -0
  658. package/src/notifications/emit-signal.ts +48 -2
  659. package/src/notifications/home-feed-side-effect.ts +165 -0
  660. package/src/notifications/signal.ts +8 -1
  661. package/src/oauth/connection-resolver.ts +8 -4
  662. package/src/oauth/platform-connection.ts +6 -2
  663. package/src/oauth/seed-providers.ts +10 -1
  664. package/src/permissions/checker.ts +14 -0
  665. package/src/permissions/ipc-risk-types.ts +3 -0
  666. package/src/permissions/question-prompter.test.ts +416 -0
  667. package/src/permissions/question-prompter.ts +294 -0
  668. package/src/platform/client.test.ts +1 -1
  669. package/src/platform/client.ts +1 -1
  670. package/src/plugin-api/constants.ts +26 -0
  671. package/src/plugin-api/index.ts +46 -0
  672. package/src/plugin-api/package.json +12 -0
  673. package/src/plugin-api/types.ts +144 -0
  674. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  675. package/src/plugins/defaults/compaction.ts +0 -4
  676. package/src/plugins/defaults/empty-response.ts +0 -2
  677. package/src/plugins/defaults/history-repair.ts +0 -2
  678. package/src/plugins/defaults/injectors.ts +55 -6
  679. package/src/plugins/defaults/llm-call.ts +0 -2
  680. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  681. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  682. package/src/plugins/defaults/persistence.ts +0 -2
  683. package/src/plugins/defaults/title-generate.ts +0 -5
  684. package/src/plugins/defaults/token-estimate.ts +0 -2
  685. package/src/plugins/defaults/tool-error.ts +0 -7
  686. package/src/plugins/defaults/tool-execute.ts +0 -2
  687. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  688. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  689. package/src/plugins/external-api.ts +104 -0
  690. package/src/plugins/external-plugin-loader.ts +367 -0
  691. package/src/plugins/feature-gate.ts +22 -0
  692. package/src/plugins/pipeline.ts +37 -0
  693. package/src/plugins/registry.ts +48 -80
  694. package/src/plugins/types.ts +74 -53
  695. package/src/plugins/user-loader.ts +85 -43
  696. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  697. package/src/proactive-artifact/job.test.ts +49 -9
  698. package/src/proactive-artifact/job.ts +4 -0
  699. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  700. package/src/proactive-artifact/trigger-state.ts +4 -0
  701. package/src/prompts/__tests__/system-prompt.test.ts +117 -0
  702. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  703. package/src/prompts/normalize-onboarding.ts +27 -0
  704. package/src/prompts/sections.ts +302 -0
  705. package/src/prompts/system-prompt.ts +72 -154
  706. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  707. package/src/prompts/templates/system-sections.ts +173 -0
  708. package/src/prompts/update-bulletin-job.ts +61 -73
  709. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  710. package/src/providers/__tests__/inference.test.ts +303 -0
  711. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  712. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  713. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  714. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  715. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  716. package/src/providers/anthropic/client.ts +123 -54
  717. package/src/providers/call-site-routing.ts +94 -16
  718. package/src/providers/connection-resolution.ts +170 -0
  719. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  720. package/src/providers/inference/adapter-factory.ts +210 -0
  721. package/src/providers/inference/auth.ts +112 -0
  722. package/src/providers/inference/backfill.ts +196 -0
  723. package/src/providers/inference/connections.ts +401 -0
  724. package/src/providers/inference/resolve-auth.ts +73 -0
  725. package/src/providers/model-catalog.ts +386 -6
  726. package/src/providers/openai/chat-completions-provider.ts +10 -2
  727. package/src/providers/openai/responses-provider.ts +4 -2
  728. package/src/providers/openrouter/client.ts +7 -0
  729. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  730. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  731. package/src/providers/provider-availability.ts +17 -2
  732. package/src/providers/provider-catalog-visibility.ts +36 -0
  733. package/src/providers/provider-env-vars.ts +17 -7
  734. package/src/providers/provider-secret-catalog.ts +49 -30
  735. package/src/providers/provider-send-message.ts +41 -20
  736. package/src/providers/registry.ts +151 -159
  737. package/src/providers/retry.ts +65 -11
  738. package/src/providers/search-provider-catalog.ts +121 -0
  739. package/src/runtime/AGENTS.md +18 -5
  740. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  741. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  742. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  743. package/src/runtime/actor-trust-resolver.ts +32 -10
  744. package/src/runtime/agent-wake.ts +64 -7
  745. package/src/runtime/assistant-event-hub.ts +3 -85
  746. package/src/runtime/auth/route-policy.ts +311 -9
  747. package/src/runtime/auth/same-actor.ts +2 -0
  748. package/src/runtime/background-job-runner.ts +339 -0
  749. package/src/runtime/btw-sidechain.ts +3 -0
  750. package/src/runtime/http-router.ts +36 -1
  751. package/src/runtime/http-server.ts +31 -5
  752. package/src/runtime/http-types.ts +21 -0
  753. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  754. package/src/runtime/middleware/request-logger.ts +62 -1
  755. package/src/runtime/migrations/origin-mode.ts +1 -1
  756. package/src/runtime/pending-interactions.ts +1 -0
  757. package/src/runtime/pre-first-message-gate.ts +83 -0
  758. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  759. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +268 -0
  760. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  761. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +319 -0
  762. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +280 -4
  763. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  764. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  765. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  766. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  767. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  768. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  769. package/src/runtime/routes/__tests__/tts-routes.test.ts +70 -3
  770. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  771. package/src/runtime/routes/acp-routes.ts +12 -8
  772. package/src/runtime/routes/app-management-routes.ts +228 -3
  773. package/src/runtime/routes/approval-routes.ts +0 -18
  774. package/src/runtime/routes/audit-routes.ts +43 -0
  775. package/src/runtime/routes/auth-routes.ts +72 -0
  776. package/src/runtime/routes/avatar-routes.ts +273 -20
  777. package/src/runtime/routes/backup-routes.ts +406 -2
  778. package/src/runtime/routes/bookmark-routes.ts +156 -0
  779. package/src/runtime/routes/btw-routes.ts +5 -1
  780. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  781. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  782. package/src/runtime/routes/contact-routes.ts +0 -160
  783. package/src/runtime/routes/conversation-cli-routes.ts +233 -0
  784. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  785. package/src/runtime/routes/conversation-management-routes.ts +47 -85
  786. package/src/runtime/routes/conversation-query-routes.ts +350 -97
  787. package/src/runtime/routes/conversation-routes.ts +121 -21
  788. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  789. package/src/runtime/routes/credential-routes.ts +540 -0
  790. package/src/runtime/routes/debug-routes.ts +2 -2
  791. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  792. package/src/runtime/routes/documents-routes.ts +25 -86
  793. package/src/runtime/routes/domain-routes.ts +167 -0
  794. package/src/runtime/routes/email-routes.ts +603 -0
  795. package/src/runtime/routes/errors.ts +2 -2
  796. package/src/runtime/routes/events-routes.ts +192 -0
  797. package/src/runtime/routes/group-routes.ts +5 -0
  798. package/src/runtime/routes/home-feed-routes.ts +6 -78
  799. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  800. package/src/runtime/routes/host-browser-routes.ts +103 -22
  801. package/src/runtime/routes/http-adapter.ts +2 -0
  802. package/src/runtime/routes/identity-routes.ts +5 -0
  803. package/src/runtime/routes/image-generation-routes.ts +99 -0
  804. package/src/runtime/routes/inbound-conversation.ts +28 -8
  805. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  806. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +248 -1
  807. package/src/runtime/routes/inbound-stages/background-dispatch.ts +118 -7
  808. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  809. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  810. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  811. package/src/runtime/routes/index.ts +42 -0
  812. package/src/runtime/routes/inference-profile-session-handler.ts +285 -0
  813. package/src/runtime/routes/inference-profile-session-reaper.ts +84 -0
  814. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  815. package/src/runtime/routes/inference-provider-connection-routes.ts +361 -0
  816. package/src/runtime/routes/inference-send-routes.ts +115 -0
  817. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  818. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  819. package/src/runtime/routes/integrations/twilio.ts +7 -13
  820. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  821. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  822. package/src/runtime/routes/notification-routes.ts +3 -1
  823. package/src/runtime/routes/oauth-apps.ts +112 -7
  824. package/src/runtime/routes/oauth-commands-routes.ts +1097 -0
  825. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  826. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  827. package/src/runtime/routes/oauth-providers.ts +298 -8
  828. package/src/runtime/routes/platform-routes.ts +336 -0
  829. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  830. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  831. package/src/runtime/routes/playground/state.ts +2 -1
  832. package/src/runtime/routes/publish-routes.ts +221 -0
  833. package/src/runtime/routes/question-routes.ts +259 -0
  834. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  835. package/src/runtime/routes/schedule-routes.ts +79 -0
  836. package/src/runtime/routes/sequence-routes.ts +291 -0
  837. package/src/runtime/routes/settings-routes.ts +2 -10
  838. package/src/runtime/routes/skills-routes.ts +31 -1
  839. package/src/runtime/routes/stt-routes.ts +240 -3
  840. package/src/runtime/routes/subagents-routes.ts +57 -18
  841. package/src/runtime/routes/surface-action-routes.ts +43 -7
  842. package/src/runtime/routes/telemetry-routes.ts +27 -0
  843. package/src/runtime/routes/tts-routes.ts +93 -1
  844. package/src/runtime/routes/types.ts +32 -0
  845. package/src/runtime/routes/user-routes-cli.ts +243 -0
  846. package/src/runtime/routes/webhook-routes.ts +165 -0
  847. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  848. package/src/runtime/routes/workspace-routes.ts +28 -0
  849. package/src/runtime/services/conversation-serializer.ts +39 -7
  850. package/src/runtime/sync/resource-sync-events.ts +117 -0
  851. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  852. package/src/runtime/sync/sync-publisher.ts +21 -0
  853. package/src/schedule/schedule-store.ts +27 -2
  854. package/src/schedule/scheduler.ts +208 -123
  855. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  856. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  857. package/src/security/secret-patterns.ts +3 -0
  858. package/src/security/untrusted-content.ts +93 -8
  859. package/src/sequence/engine.ts +38 -40
  860. package/src/skills/catalog-files.ts +1 -1
  861. package/src/skills/catalog-install.ts +233 -116
  862. package/src/skills/clawhub.ts +70 -13
  863. package/src/skills/managed-store.ts +4 -119
  864. package/src/skills/skillssh-registry.ts +27 -48
  865. package/src/subagent/manager.ts +28 -15
  866. package/src/telemetry/types.ts +113 -1
  867. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  868. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  869. package/src/tools/apps/executors.ts +58 -7
  870. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  871. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  872. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  873. package/src/tools/browser/browser-execution.ts +29 -14
  874. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  875. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  876. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  877. package/src/tools/browser/cdp-client/factory.ts +66 -5
  878. package/src/tools/browser/runtime-check.ts +77 -0
  879. package/src/tools/computer-use/definitions.ts +3 -3
  880. package/src/tools/credentials/vault.ts +1 -1
  881. package/src/tools/document/document-tool.ts +124 -1
  882. package/src/tools/filesystem/edit.ts +1 -1
  883. package/src/tools/filesystem/list.ts +1 -1
  884. package/src/tools/filesystem/read.ts +1 -1
  885. package/src/tools/filesystem/write.ts +5 -2
  886. package/src/tools/host-filesystem/transfer.ts +1 -1
  887. package/src/tools/host-terminal/host-shell.ts +1 -1
  888. package/src/tools/memory/register.test.ts +3 -3
  889. package/src/tools/memory/register.ts +9 -1
  890. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  891. package/src/tools/network/web-search.ts +280 -37
  892. package/src/tools/permission-checker.ts +14 -6
  893. package/src/tools/registry.ts +17 -7
  894. package/src/tools/schedule/create.ts +2 -2
  895. package/src/tools/schema-transforms.ts +7 -2
  896. package/src/tools/side-effects.ts +1 -0
  897. package/src/tools/skills/delete-managed.ts +4 -4
  898. package/src/tools/skills/execute.ts +1 -1
  899. package/src/tools/skills/scaffold-managed.ts +3 -2
  900. package/src/tools/subagent/notify-parent.ts +1 -1
  901. package/src/tools/subagent/spawn.ts +3 -3
  902. package/src/tools/system/request-permission.ts +2 -2
  903. package/src/tools/terminal/safe-env.ts +60 -1
  904. package/src/tools/terminal/shell.ts +44 -0
  905. package/src/tools/tool-manifest.ts +2 -0
  906. package/src/tools/types.ts +72 -21
  907. package/src/tools/ui-surface/definitions.ts +6 -5
  908. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  909. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  910. package/src/types/onboarding-context.ts +2 -0
  911. package/src/usage/attribution.ts +3 -2
  912. package/src/util/errors.ts +17 -0
  913. package/src/util/platform.ts +10 -0
  914. package/src/util/pricing.ts +86 -160
  915. package/src/watcher/__tests__/engine.test.ts +323 -0
  916. package/src/watcher/constants.ts +7 -0
  917. package/src/watcher/engine.ts +94 -90
  918. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  919. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  920. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +94 -5
  921. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  922. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +117 -0
  923. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +95 -0
  924. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  925. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  926. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  927. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  928. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  929. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  930. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  931. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  932. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  933. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  934. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  935. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  936. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  937. package/src/workspace/migrations/registry.ts +30 -0
  938. package/src/workspace/migrations/runner.ts +46 -5
  939. package/src/workspace/migrations/types.ts +17 -3
  940. package/src/workspace/provider-commit-message-generator.ts +3 -2
  941. package/examples/plugins/echo/bun.lock +0 -25
  942. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  943. package/src/__tests__/context-window-manager.test.ts +0 -2093
  944. package/src/__tests__/credentials-cli.test.ts +0 -1225
  945. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  946. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  947. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  948. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  949. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  950. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  951. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  952. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  953. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  954. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  955. package/src/cli/commands/autonomy.ts +0 -365
  956. package/src/cli/commands/memory.ts +0 -424
  957. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  958. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  959. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  960. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  961. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  962. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  963. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  964. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  965. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  966. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  967. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  968. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  969. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  970. package/src/context/prompts/compact.md +0 -26
  971. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  972. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  973. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  974. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  975. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  976. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  977. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  978. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  979. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  980. package/src/home/assistant-feed-authoring.ts +0 -135
  981. package/src/home/emit-feed-event.ts +0 -169
  982. package/src/home/feed-scheduler.ts +0 -281
  983. package/src/home/platform-gmail-digest.ts +0 -163
  984. package/src/home/rewrite-command-preview.ts +0 -66
  985. package/src/home/rewrite-feed-title.ts +0 -58
  986. package/src/home/rollup-producer.ts +0 -426
  987. package/src/memory/admin.ts +0 -326
  988. package/src/memory/context-search/sources/pkb.ts +0 -476
  989. package/src/memory/graph/compaction.ts +0 -299
  990. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  991. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -5,31 +5,62 @@ import {
5
5
  writeFileSync,
6
6
  } from "node:fs";
7
7
  import { basename, join } from "node:path";
8
- import type { Readable } from "node:stream";
9
- import { pipeline } from "node:stream/promises";
10
8
 
11
9
  import type { Command } from "commander";
12
10
 
13
11
  import { getAssistantDomain } from "../../config/env.js";
14
- import { markdownToEmailHtml } from "../../email/html-renderer.js";
15
- import { VellumPlatformClient } from "../../platform/client.js";
12
+ import {
13
+ cliIpcCall,
14
+ cliIpcCallStream,
15
+ exitFromIpcResult,
16
+ } from "../../ipc/cli-client.js";
17
+ import { registerCommand } from "../lib/register-command.js";
16
18
  import { getCliLogger } from "../logger.js";
17
19
  import { shouldOutputJson, writeOutput } from "../output.js";
18
20
 
19
21
  const log = getCliLogger("email");
20
22
 
23
+ /**
24
+ * Handle an IPC error in the email command. In --json mode, writes a
25
+ * `{"error": "..."}` envelope to stdout so callers can parse it. In all
26
+ * modes, sets a non-zero exit code without calling process.exit() so tests
27
+ * using runAssistantCommandFull can inspect the exit code after the call.
28
+ */
29
+ function handleEmailIpcError(
30
+ r: { ok: false; error?: string; statusCode?: number },
31
+ cmd: Command,
32
+ ): void {
33
+ const exitCode =
34
+ r.statusCode == null
35
+ ? 10
36
+ : r.statusCode >= 500
37
+ ? 3
38
+ : r.statusCode >= 400
39
+ ? 2
40
+ : 1;
41
+ if (shouldOutputJson(cmd)) {
42
+ process.stdout.write(
43
+ JSON.stringify({ error: r.error ?? "Unknown error" }) + "\n",
44
+ );
45
+ process.exitCode = exitCode;
46
+ return;
47
+ }
48
+ exitFromIpcResult(r, cmd);
49
+ }
50
+
21
51
  export function registerEmailCommand(program: Command): void {
22
52
  const domain = getAssistantDomain();
23
- const email = program
24
- .command("email")
25
- .description(
26
- `Get your own email address (@${domain}) — register, send, receive, and manage email natively`,
27
- )
28
- .option("--json", "Machine-readable compact JSON output");
29
-
30
- email.addHelpText(
31
- "after",
32
- `
53
+ registerCommand(program, {
54
+ name: "email",
55
+ transport: "ipc",
56
+ description: `Get your own email address (@${domain}) — register, send, receive, and manage email natively`,
57
+ build: (email) => {
58
+ // Keep the --json option at the email namespace level
59
+ email.option("--json", "Machine-readable compact JSON output");
60
+
61
+ email.addHelpText(
62
+ "after",
63
+ `
33
64
  Set up and manage this assistant's native email address on the Vellum
34
65
  platform. No third-party email provider or browser sign-up needed.
35
66
 
@@ -42,14 +73,14 @@ Examples:
42
73
  $ assistant email attachment msg_abc1 --list
43
74
  $ assistant email attachment msg_abc1 att_xyz1
44
75
  $ assistant email register mybot --json`,
45
- );
46
-
47
- email
48
- .command("register <username>")
49
- .description(`Register an @${domain} email address for this assistant`)
50
- .addHelpText(
51
- "after",
52
- `
76
+ );
77
+
78
+ email
79
+ .command("register <username>")
80
+ .description(`Register an @${domain} email address for this assistant`)
81
+ .addHelpText(
82
+ "after",
83
+ `
53
84
  Arguments:
54
85
  username The local part of the email address (e.g. "mybot" → mybot@${domain})
55
86
 
@@ -63,74 +94,32 @@ Examples:
63
94
 
64
95
  $ assistant email register support --json
65
96
  {"address":"support@${domain}","id":"...","created_at":"..."}`,
66
- )
67
- .action(async (username: string, _opts: unknown, cmd: Command) => {
68
- try {
69
- const client = await VellumPlatformClient.create();
70
- if (!client) {
71
- throw new Error(
72
- "Platform credentials not configured. Run: assistant platform connect",
73
- );
74
- }
75
- if (!client.platformAssistantId) {
76
- throw new Error(
77
- "Assistant ID not configured. Run: assistant platform connect",
78
- );
79
- }
80
-
81
- const response = await client.fetch(
82
- `/v1/assistants/${client.platformAssistantId}/email-addresses/`,
83
- {
84
- method: "POST",
85
- headers: { "Content-Type": "application/json" },
86
- body: JSON.stringify({ username }),
87
- },
88
- );
97
+ )
98
+ .action(async (username: string, _opts: unknown, cmd: Command) => {
99
+ const r = await cliIpcCall<{
100
+ id: string;
101
+ address: string;
102
+ created_at: string;
103
+ }>("email_register", { body: { username } });
104
+ if (!r.ok)
105
+ return handleEmailIpcError(
106
+ { ok: false, error: r.error, statusCode: r.statusCode },
107
+ cmd,
108
+ );
109
+ if (shouldOutputJson(cmd)) {
110
+ writeOutput(cmd, r.result);
111
+ } else {
112
+ log.info(`✓ Registered ${r.result!.address}`);
113
+ }
114
+ });
89
115
 
90
- if (!response.ok) {
91
- const body = (await response.json().catch(() => ({}))) as Record<
92
- string,
93
- unknown
94
- >;
95
- const detail =
96
- body.detail ??
97
- (Array.isArray(body.username) ? body.username[0] : undefined) ??
98
- (Array.isArray(body.assistant_id)
99
- ? body.assistant_id[0]
100
- : undefined) ??
101
- `HTTP ${response.status}`;
102
- throw new Error(String(detail));
103
- }
104
-
105
- const data = (await response.json()) as {
106
- id: string;
107
- address: string;
108
- created_at: string;
109
- };
110
-
111
- if (shouldOutputJson(cmd)) {
112
- writeOutput(cmd, data);
113
- } else {
114
- log.info(`✓ Registered ${data.address}`);
115
- }
116
- } catch (err) {
117
- const message = err instanceof Error ? err.message : String(err);
118
- if (shouldOutputJson(cmd)) {
119
- writeOutput(cmd, { error: message });
120
- } else {
121
- log.error(`Error: ${message}`);
122
- }
123
- process.exitCode = 1;
124
- }
125
- });
126
-
127
- email
128
- .command("unregister")
129
- .description("Remove the email address registered for this assistant")
130
- .option("--confirm", "Skip confirmation prompt")
131
- .addHelpText(
132
- "after",
133
- `
116
+ email
117
+ .command("unregister")
118
+ .description("Remove the email address registered for this assistant")
119
+ .option("--confirm", "Skip confirmation prompt")
120
+ .addHelpText(
121
+ "after",
122
+ `
134
123
  Removes the email address currently registered for this assistant.
135
124
  The address is deactivated immediately — inbound email will no longer
136
125
  be delivered. The username enters a cooldown period and is not
@@ -146,93 +135,50 @@ Examples:
146
135
 
147
136
  $ assistant email unregister --json
148
137
  {"unregistered":"mybot@${domain}"}`,
149
- )
150
- .action(async (_opts: { confirm?: boolean }, cmd: Command) => {
151
- try {
152
- const client = await VellumPlatformClient.create();
153
- if (!client) {
154
- throw new Error(
155
- "Platform credentials not configured. Run: assistant platform connect",
156
- );
157
- }
158
- if (!client.platformAssistantId) {
159
- throw new Error(
160
- "Assistant ID not configured. Run: assistant platform connect",
161
- );
162
- }
163
-
164
- const listResponse = await client.fetch(
165
- `/v1/assistants/${client.platformAssistantId}/email-addresses/`,
166
- );
167
-
168
- if (!listResponse.ok) {
169
- throw new Error(
170
- `Failed to list email addresses: HTTP ${listResponse.status}`,
138
+ )
139
+ .action(async (_opts: { confirm?: boolean }, cmd: Command) => {
140
+ if (!_opts.confirm && !shouldOutputJson(cmd)) {
141
+ const rl = await import("node:readline");
142
+ // We need to get the address to show in the prompt, but we can't
143
+ // know it without making an IPC call. Use a generic prompt here.
144
+ const iface = rl.createInterface({
145
+ input: process.stdin,
146
+ output: process.stderr,
147
+ });
148
+ const answer = await new Promise<string>((resolve) => {
149
+ iface.question(
150
+ `Remove registered email address? (y/N) `,
151
+ resolve,
152
+ );
153
+ });
154
+ iface.close();
155
+ if (answer.trim().toLowerCase() !== "y") {
156
+ log.info("Cancelled.");
157
+ return;
158
+ }
159
+ }
160
+ const r = await cliIpcCall<{ unregistered: string }>(
161
+ "email_unregister",
162
+ {},
171
163
  );
172
- }
173
-
174
- const listData = (await listResponse.json()) as {
175
- results: { id: string; address: string }[];
176
- };
177
-
178
- const addresses = listData.results ?? [];
179
- if (addresses.length === 0) {
180
- throw new Error("No email address registered for this assistant.");
181
- }
182
-
183
- const target = addresses[0];
184
-
185
- if (!_opts.confirm && !shouldOutputJson(cmd)) {
186
- const rl = await import("node:readline");
187
- const iface = rl.createInterface({
188
- input: process.stdin,
189
- output: process.stderr,
190
- });
191
- const answer = await new Promise<string>((resolve) => {
192
- iface.question(`Remove ${target.address}? (y/N) `, resolve);
193
- });
194
- iface.close();
195
- if (answer.trim().toLowerCase() !== "y") {
196
- log.info("Cancelled.");
197
- return;
164
+ if (!r.ok)
165
+ return handleEmailIpcError(
166
+ { ok: false, error: r.error, statusCode: r.statusCode },
167
+ cmd,
168
+ );
169
+ if (shouldOutputJson(cmd)) {
170
+ writeOutput(cmd, r.result);
171
+ } else {
172
+ log.info(`✓ Unregistered ${r.result!.unregistered}`);
198
173
  }
199
- }
200
-
201
- const deleteResponse = await client.fetch(
202
- `/v1/assistants/${client.platformAssistantId}/email-addresses/${target.id}/`,
203
- { method: "DELETE" },
204
- );
174
+ });
205
175
 
206
- if (!deleteResponse.ok) {
207
- const body = (await deleteResponse
208
- .json()
209
- .catch(() => ({}))) as Record<string, unknown>;
210
- const detail = body.detail ?? `HTTP ${deleteResponse.status}`;
211
- throw new Error(String(detail));
212
- }
213
-
214
- if (shouldOutputJson(cmd)) {
215
- writeOutput(cmd, { unregistered: target.address });
216
- } else {
217
- log.info(`✓ Unregistered ${target.address}`);
218
- }
219
- } catch (err) {
220
- const message = err instanceof Error ? err.message : String(err);
221
- if (shouldOutputJson(cmd)) {
222
- writeOutput(cmd, { error: message });
223
- } else {
224
- log.error(`Error: ${message}`);
225
- }
226
- process.exitCode = 1;
227
- }
228
- });
229
-
230
- email
231
- .command("status")
232
- .description("Show email address info and usage for this assistant")
233
- .addHelpText(
234
- "after",
235
- `
176
+ email
177
+ .command("status")
178
+ .description("Show email address info and usage for this assistant")
179
+ .addHelpText(
180
+ "after",
181
+ `
236
182
  Shows the email address registered for this assistant along with
237
183
  current usage and quota information from the platform.
238
184
 
@@ -247,111 +193,60 @@ Examples:
247
193
 
248
194
  $ assistant email status --json
249
195
  {"address":"hi@mybot.${domain}","status":"active","created_at":"2026-04-15T...","usage":{...}}`,
250
- )
251
- .action(async (_opts: unknown, cmd: Command) => {
252
- try {
253
- const client = await VellumPlatformClient.create();
254
- if (!client) {
255
- throw new Error(
256
- "Platform credentials not configured. Run: assistant platform connect",
257
- );
258
- }
259
- if (!client.platformAssistantId) {
260
- throw new Error(
261
- "Assistant ID not configured. Run: assistant platform connect",
262
- );
263
- }
264
-
265
- // 1. List addresses to find the registered one
266
- const listResponse = await client.fetch(
267
- `/v1/assistants/${client.platformAssistantId}/email-addresses/`,
268
- );
269
-
270
- if (!listResponse.ok) {
271
- throw new Error(
272
- `Failed to list email addresses: HTTP ${listResponse.status}`,
273
- );
274
- }
275
-
276
- const listData = (await listResponse.json()) as {
277
- results: { id: string; address: string }[];
278
- };
279
-
280
- const addresses = listData.results ?? [];
281
- if (addresses.length === 0) {
282
- throw new Error(
283
- "No email address registered for this assistant. Run: assistant email register <username>",
284
- );
285
- }
286
-
287
- const target = addresses[0];
288
-
289
- // 2. Fetch status/usage for this address
290
- const statusResponse = await client.fetch(
291
- `/v1/assistants/${client.platformAssistantId}/email-addresses/${target.id}/status/`,
292
- );
293
-
294
- if (!statusResponse.ok) {
295
- const body = (await statusResponse
296
- .json()
297
- .catch(() => ({}))) as Record<string, unknown>;
298
- const detail = body.detail ?? `HTTP ${statusResponse.status}`;
299
- throw new Error(String(detail));
300
- }
301
-
302
- const statusData = (await statusResponse.json()) as {
303
- address: string;
304
- status: string;
305
- created_at: string;
306
- usage: {
307
- sent_today: number;
308
- daily_limit: number;
309
- received_today: number;
310
- sent_this_month: number;
311
- received_this_month: number;
312
- };
313
- };
314
-
315
- if (shouldOutputJson(cmd)) {
316
- writeOutput(cmd, statusData);
317
- } else {
318
- log.info(`Address: ${statusData.address}`);
319
- log.info(`Status: ${statusData.status}`);
320
- log.info(`Since: ${statusData.created_at.split("T")[0]}`);
321
- if (statusData.usage) {
322
- log.info(
323
- `Sent: ${statusData.usage.sent_today} / ${statusData.usage.daily_limit} (daily)`,
324
- );
325
- log.info(`Received: ${statusData.usage.received_today} (today)`);
326
- log.info(
327
- `Monthly: ${statusData.usage.sent_this_month} sent, ${statusData.usage.received_this_month} received`,
196
+ )
197
+ .action(async (_opts: unknown, cmd: Command) => {
198
+ const r = await cliIpcCall<{
199
+ address: string;
200
+ status: string;
201
+ created_at: string;
202
+ usage: {
203
+ sent_today: number;
204
+ daily_limit: number;
205
+ received_today: number;
206
+ sent_this_month: number;
207
+ received_this_month: number;
208
+ };
209
+ }>("email_status", {});
210
+ if (!r.ok)
211
+ return handleEmailIpcError(
212
+ { ok: false, error: r.error, statusCode: r.statusCode },
213
+ cmd,
328
214
  );
215
+ const statusData = r.result!;
216
+ if (shouldOutputJson(cmd)) {
217
+ writeOutput(cmd, statusData);
218
+ } else {
219
+ log.info(`Address: ${statusData.address}`);
220
+ log.info(`Status: ${statusData.status}`);
221
+ log.info(`Since: ${statusData.created_at.split("T")[0]}`);
222
+ if (statusData.usage) {
223
+ log.info(
224
+ `Sent: ${statusData.usage.sent_today} / ${statusData.usage.daily_limit} (daily)`,
225
+ );
226
+ log.info(`Received: ${statusData.usage.received_today} (today)`);
227
+ log.info(
228
+ `Monthly: ${statusData.usage.sent_this_month} sent, ${statusData.usage.received_this_month} received`,
229
+ );
230
+ }
329
231
  }
330
- }
331
- } catch (err) {
332
- const message = err instanceof Error ? err.message : String(err);
333
- if (shouldOutputJson(cmd)) {
334
- writeOutput(cmd, { error: message });
335
- } else {
336
- log.error(`Error: ${message}`);
337
- }
338
- process.exitCode = 1;
339
- }
340
- });
341
-
342
- email
343
- .command("list")
344
- .description("List received and sent emails for this assistant")
345
- .option(
346
- "-d, --direction <direction>",
347
- "Filter by direction: inbound, outbound, or all",
348
- "all",
349
- )
350
- .option("-l, --limit <count>", "Maximum number of results", "20")
351
- .option("--since <date>", "Only show messages since this date (ISO 8601)")
352
- .addHelpText(
353
- "after",
354
- `
232
+ });
233
+
234
+ email
235
+ .command("list")
236
+ .description("List received and sent emails for this assistant")
237
+ .option(
238
+ "-d, --direction <direction>",
239
+ "Filter by direction: inbound, outbound, or all",
240
+ "all",
241
+ )
242
+ .option("-l, --limit <count>", "Maximum number of results", "20")
243
+ .option(
244
+ "--since <date>",
245
+ "Only show messages since this date (ISO 8601)",
246
+ )
247
+ .addHelpText(
248
+ "after",
249
+ `
355
250
  Lists email messages for this assistant. Shows subject, from, to,
356
251
  direction, and timestamp for each message.
357
252
 
@@ -359,109 +254,79 @@ Examples:
359
254
  $ assistant email list
360
255
  $ assistant email list --direction inbound --limit 5
361
256
  $ assistant email list --since 2026-04-01 --json`,
362
- )
363
- .action(
364
- async (
365
- opts: {
366
- direction?: string;
367
- limit?: string;
368
- since?: string;
369
- },
370
- cmd: Command,
371
- ) => {
372
- try {
373
- const client = await VellumPlatformClient.create();
374
- if (!client) {
375
- throw new Error(
376
- "Platform credentials not configured. Run: assistant platform connect",
377
- );
378
- }
379
- if (!client.platformAssistantId) {
380
- throw new Error(
381
- "Assistant ID not configured. Run: assistant platform connect",
382
- );
383
- }
384
-
385
- const params = new URLSearchParams();
386
- if (opts.direction && opts.direction !== "all") {
387
- params.set("direction", opts.direction);
388
- }
389
- if (opts.limit) {
390
- params.set("limit", opts.limit);
391
- }
392
- if (opts.since) {
393
- params.set("since", opts.since);
394
- }
395
-
396
- const qs = params.toString();
397
- const path = `/v1/assistants/${client.platformAssistantId}/emails/${qs ? `?${qs}` : ""}`;
398
- const response = await client.fetch(path);
399
-
400
- if (!response.ok) {
401
- const body = (await response.json().catch(() => ({}))) as Record<
402
- string,
403
- unknown
404
- >;
405
- const detail = body.detail ?? `HTTP ${response.status}`;
406
- throw new Error(String(detail));
407
- }
408
-
409
- const data = (await response.json()) as {
410
- results: {
411
- id: string;
412
- direction: string;
413
- from_address: string;
414
- to_addresses: string[];
415
- subject: string;
416
- created_at: string;
417
- }[];
418
- count: number;
419
- };
257
+ )
258
+ .action(
259
+ async (
260
+ opts: {
261
+ direction?: string;
262
+ limit?: string;
263
+ since?: string;
264
+ },
265
+ cmd: Command,
266
+ ) => {
267
+ const params: Record<string, string> = {};
268
+ if (opts.direction && opts.direction !== "all") {
269
+ params.direction = opts.direction;
270
+ }
271
+ if (opts.limit) {
272
+ params.limit = opts.limit;
273
+ }
274
+ if (opts.since) {
275
+ params.since = opts.since;
276
+ }
420
277
 
421
- if (shouldOutputJson(cmd)) {
422
- writeOutput(cmd, data);
423
- } else {
424
- const messages = data.results ?? [];
425
- if (messages.length === 0) {
426
- log.info("No email messages found.");
278
+ const r = await cliIpcCall<{
279
+ results: {
280
+ id: string;
281
+ direction: string;
282
+ from_address: string;
283
+ to_addresses: string[];
284
+ subject: string;
285
+ created_at: string;
286
+ }[];
287
+ count: number;
288
+ }>("email_list", { queryParams: params });
289
+ if (!r.ok)
290
+ return handleEmailIpcError(
291
+ { ok: false, error: r.error, statusCode: r.statusCode },
292
+ cmd,
293
+ );
294
+ const data = r.result!;
295
+ if (shouldOutputJson(cmd)) {
296
+ writeOutput(cmd, data);
427
297
  } else {
428
- for (const msg of messages) {
429
- const dir = msg.direction === "inbound" ? "←" : "→";
430
- const to = Array.isArray(msg.to_addresses)
431
- ? msg.to_addresses.join(", ")
432
- : "";
433
- const date = new Date(msg.created_at).toLocaleString();
434
- log.info(
435
- `${dir} ${date} ${msg.from_address} → ${to} "${msg.subject || "(no subject)"}"`,
436
- );
298
+ const messages = data.results ?? [];
299
+ if (messages.length === 0) {
300
+ log.info("No email messages found.");
301
+ } else {
302
+ for (const msg of messages) {
303
+ const dir = msg.direction === "inbound" ? "←" : "→";
304
+ const to = Array.isArray(msg.to_addresses)
305
+ ? msg.to_addresses.join(", ")
306
+ : "";
307
+ const date = new Date(msg.created_at).toLocaleString();
308
+ log.info(
309
+ `${dir} ${date} ${msg.from_address} → ${to} "${msg.subject || "(no subject)"}"`,
310
+ );
311
+ }
312
+ log.info(`\n${data.count} total message(s)`);
437
313
  }
438
- log.info(`\n${data.count} total message(s)`);
439
314
  }
440
- }
441
- } catch (err) {
442
- const message = err instanceof Error ? err.message : String(err);
443
- if (shouldOutputJson(cmd)) {
444
- writeOutput(cmd, { error: message });
445
- } else {
446
- log.error(`Error: ${message}`);
447
- }
448
- process.exitCode = 1;
449
- }
450
- },
451
- );
315
+ },
316
+ );
452
317
 
453
- email
454
- .command("download <message-id>")
455
- .description("Download a specific email message")
456
- .option(
457
- "--format <type>",
458
- "Output format: text, html, json (default: text)",
459
- "text",
460
- )
461
- .option("-o, --output <path>", "Write to file instead of stdout")
462
- .addHelpText(
463
- "after",
464
- `
318
+ email
319
+ .command("download <message-id>")
320
+ .description("Download a specific email message")
321
+ .option(
322
+ "--format <type>",
323
+ "Output format: text, html, json (default: text)",
324
+ "text",
325
+ )
326
+ .option("-o, --output <path>", "Write to file instead of stdout")
327
+ .addHelpText(
328
+ "after",
329
+ `
465
330
  Arguments:
466
331
  message-id Email message ID (from \`assistant email list --json\`)
467
332
 
@@ -483,132 +348,113 @@ Examples:
483
348
 
484
349
  $ assistant email download msg_abc123 -o email.txt
485
350
  ✓ Saved to email.txt`,
486
- )
487
- .action(
488
- async (
489
- messageId: string,
490
- opts: {
491
- format?: string;
492
- output?: string;
493
- },
494
- cmd: Command,
495
- ) => {
496
- try {
497
- const client = await VellumPlatformClient.create();
498
- if (!client) {
499
- throw new Error(
500
- "Platform credentials not configured. Run: assistant platform connect",
501
- );
502
- }
503
- if (!client.platformAssistantId) {
504
- throw new Error(
505
- "Assistant ID not configured. Run: assistant platform connect",
506
- );
507
- }
508
-
509
- const response = await client.fetch(
510
- `/v1/assistants/${client.platformAssistantId}/emails/${messageId}/`,
511
- );
512
-
513
- if (!response.ok) {
514
- const body = (await response.json().catch(() => ({}))) as Record<
515
- string,
516
- unknown
517
- >;
518
- const detail = body.detail ?? `HTTP ${response.status}`;
519
- throw new Error(String(detail));
520
- }
521
-
522
- const msg = (await response.json()) as {
523
- id: string;
524
- direction: string;
525
- from_address: string;
526
- to_addresses: string[];
527
- subject: string;
528
- body_text: string;
529
- body_html: string;
530
- in_reply_to: string;
531
- references: string[];
532
- created_at: string;
533
- };
534
-
535
- const fmt = opts.format ?? "text";
536
-
537
- let content: string;
538
- if (fmt === "json" || shouldOutputJson(cmd)) {
539
- content = JSON.stringify(msg, null, 2) + "\n";
540
- } else if (fmt === "html") {
541
- if (!msg.body_html) {
542
- throw new Error("No HTML body available for this message.");
543
- }
544
- content = msg.body_html;
545
- } else {
546
- // text format: headers + body
547
- const to = Array.isArray(msg.to_addresses)
548
- ? msg.to_addresses.join(", ")
549
- : "";
550
- const date = new Date(msg.created_at).toLocaleString();
551
- const lines = [
552
- `From: ${msg.from_address}`,
553
- `To: ${to}`,
554
- `Subject: ${msg.subject || "(no subject)"}`,
555
- `Date: ${date}`,
556
- ];
557
- if (msg.in_reply_to) {
558
- lines.push(`In-Reply-To: ${msg.in_reply_to}`);
351
+ )
352
+ .action(
353
+ async (
354
+ messageId: string,
355
+ opts: {
356
+ format?: string;
357
+ output?: string;
358
+ },
359
+ cmd: Command,
360
+ ) => {
361
+ const r = await cliIpcCall<{
362
+ id: string;
363
+ direction: string;
364
+ from_address: string;
365
+ to_addresses: string[];
366
+ subject: string;
367
+ body_text: string;
368
+ body_html: string;
369
+ in_reply_to: string;
370
+ references: string[];
371
+ created_at: string;
372
+ }>("email_download", { queryParams: { messageId } });
373
+ if (!r.ok)
374
+ return handleEmailIpcError(
375
+ { ok: false, error: r.error, statusCode: r.statusCode },
376
+ cmd,
377
+ );
378
+ const msg = r.result!;
379
+
380
+ const fmt = opts.format ?? "text";
381
+
382
+ let content: string;
383
+ if (fmt === "json" || shouldOutputJson(cmd)) {
384
+ content = JSON.stringify(msg, null, 2) + "\n";
385
+ } else if (fmt === "html") {
386
+ if (!msg.body_html) {
387
+ log.error("No HTML body available for this message.");
388
+ process.exitCode = 1;
389
+ return;
390
+ }
391
+ content = msg.body_html;
392
+ } else {
393
+ // text format: headers + body
394
+ const to = Array.isArray(msg.to_addresses)
395
+ ? msg.to_addresses.join(", ")
396
+ : "";
397
+ const date = new Date(msg.created_at).toLocaleString();
398
+ const lines = [
399
+ `From: ${msg.from_address}`,
400
+ `To: ${to}`,
401
+ `Subject: ${msg.subject || "(no subject)"}`,
402
+ `Date: ${date}`,
403
+ ];
404
+ if (msg.in_reply_to) {
405
+ lines.push(`In-Reply-To: ${msg.in_reply_to}`);
406
+ }
407
+ lines.push("", msg.body_text || "(no plain-text body)");
408
+ content = lines.join("\n") + "\n";
559
409
  }
560
- lines.push("", msg.body_text || "(no plain-text body)");
561
- content = lines.join("\n") + "\n";
562
- }
563
410
 
564
- if (opts.output) {
565
- writeFileSync(opts.output, content, "utf-8");
566
- if (!shouldOutputJson(cmd)) {
567
- log.info(`✓ Saved to ${opts.output}`);
411
+ if (opts.output) {
412
+ try {
413
+ writeFileSync(opts.output, content, "utf-8");
414
+ } catch (err) {
415
+ log.error(
416
+ `Failed to write --output ${opts.output}: ${err instanceof Error ? err.message : String(err)}`,
417
+ );
418
+ process.exitCode = 1;
419
+ return;
420
+ }
421
+ if (!shouldOutputJson(cmd)) {
422
+ log.info(`✓ Saved to ${opts.output}`);
423
+ } else {
424
+ writeOutput(cmd, { saved: opts.output, bytes: content.length });
425
+ }
568
426
  } else {
569
- writeOutput(cmd, { saved: opts.output, bytes: content.length });
427
+ process.stdout.write(content);
570
428
  }
571
- } else {
572
- process.stdout.write(content);
573
- }
574
- } catch (err) {
575
- const message = err instanceof Error ? err.message : String(err);
576
- if (shouldOutputJson(cmd)) {
577
- writeOutput(cmd, { error: message });
578
- } else {
579
- log.error(`Error: ${message}`);
580
- }
581
- process.exitCode = 1;
582
- }
583
- },
584
- );
429
+ },
430
+ );
585
431
 
586
- email
587
- .command("send <to...>")
588
- .description("Send an email from this assistant")
589
- .option("-s, --subject <text>", "Subject line")
590
- .option("-b, --body <text>", "Email body (plain text)")
591
- .option("-f, --file <path>", "Read body from file")
592
- .option("--html <path>", "HTML body file (optional)")
593
- .option(
594
- "--cc <address>",
595
- "CC recipient (repeatable)",
596
- (val: string, prev: string[]) => [...prev, val],
597
- [] as string[],
598
- )
599
- .option(
600
- "--bcc <address>",
601
- "BCC recipient (repeatable)",
602
- (val: string, prev: string[]) => [...prev, val],
603
- [] as string[],
604
- )
605
- .option(
606
- "--reply-to <email_id>",
607
- "Reply to an email by its ID (auto-resolves threading headers and subject)",
608
- )
609
- .addHelpText(
610
- "after",
611
- `
432
+ email
433
+ .command("send <to...>")
434
+ .description("Send an email from this assistant")
435
+ .option("-s, --subject <text>", "Subject line")
436
+ .option("-b, --body <text>", "Email body (plain text)")
437
+ .option("-f, --file <path>", "Read body from file")
438
+ .option("--html <path>", "HTML body file (optional)")
439
+ .option(
440
+ "--cc <address>",
441
+ "CC recipient (repeatable)",
442
+ (val: string, prev: string[]) => [...prev, val],
443
+ [] as string[],
444
+ )
445
+ .option(
446
+ "--bcc <address>",
447
+ "BCC recipient (repeatable)",
448
+ (val: string, prev: string[]) => [...prev, val],
449
+ [] as string[],
450
+ )
451
+ .option(
452
+ "--reply-to <email_id>",
453
+ "Reply to an email by its ID (auto-resolves threading headers and subject)",
454
+ )
455
+ .addHelpText(
456
+ "after",
457
+ `
612
458
  Arguments:
613
459
  to Recipient email address(es) — one or more
614
460
 
@@ -637,151 +483,107 @@ Examples:
637
483
 
638
484
  $ assistant email send user@example.com -s "Hello" -b "Hi" --json
639
485
  {"delivery_id":"abc123","status":"accepted"}`,
640
- )
641
- .action(
642
- async (
643
- to: string[],
644
- opts: {
645
- subject?: string;
646
- body?: string;
647
- file?: string;
648
- html?: string;
649
- cc?: string[];
650
- bcc?: string[];
651
- replyTo?: string;
652
- },
653
- cmd: Command,
654
- ) => {
655
- try {
656
- const client = await VellumPlatformClient.create();
657
- if (!client) {
658
- throw new Error(
659
- "Platform credentials not configured. Run: assistant platform connect",
660
- );
661
- }
662
- if (!client.platformAssistantId) {
663
- throw new Error(
664
- "Assistant ID not configured. Run: assistant platform connect",
665
- );
666
- }
667
-
668
- // 1. Resolve the assistant's registered email address (the "from").
669
- const listResponse = await client.fetch(
670
- `/v1/assistants/${client.platformAssistantId}/email-addresses/`,
671
- );
672
-
673
- if (!listResponse.ok) {
674
- throw new Error(
675
- `Failed to list email addresses: HTTP ${listResponse.status}`,
676
- );
677
- }
678
-
679
- const listData = (await listResponse.json()) as {
680
- results: { id: string; address: string }[];
681
- };
486
+ )
487
+ .action(
488
+ async (
489
+ to: string[],
490
+ opts: {
491
+ subject?: string;
492
+ body?: string;
493
+ file?: string;
494
+ html?: string;
495
+ cc?: string[];
496
+ bcc?: string[];
497
+ replyTo?: string;
498
+ },
499
+ cmd: Command,
500
+ ) => {
501
+ // Resolve body text: --body > --file > stdin
502
+ let text = opts.body;
503
+ if (!text && opts.file) {
504
+ try {
505
+ text = readFileSync(opts.file, "utf-8");
506
+ } catch (err) {
507
+ log.error(
508
+ `Failed to read --file ${opts.file}: ${err instanceof Error ? err.message : String(err)}`,
509
+ );
510
+ process.exitCode = 1;
511
+ return;
512
+ }
513
+ }
514
+ if (!text && !process.stdin.isTTY) {
515
+ try {
516
+ text = readFileSync("/dev/stdin", "utf-8");
517
+ } catch (err) {
518
+ log.error(
519
+ `Failed to read body from stdin: ${err instanceof Error ? err.message : String(err)}`,
520
+ );
521
+ process.exitCode = 1;
522
+ return;
523
+ }
524
+ }
525
+ if (!text) {
526
+ log.error(
527
+ "Email body is required. Use --body, --file, or pipe via stdin.",
528
+ );
529
+ process.exitCode = 1;
530
+ return;
531
+ }
682
532
 
683
- const addresses = listData.results ?? [];
684
- if (addresses.length === 0) {
685
- throw new Error(
686
- "No email address registered for this assistant. Run: assistant email register <username>",
687
- );
688
- }
533
+ // Read HTML file if --html given; pass raw content to route
534
+ let html: string | undefined;
535
+ if (opts.html) {
536
+ try {
537
+ html = readFileSync(opts.html, "utf-8");
538
+ } catch (err) {
539
+ log.error(
540
+ `Failed to read --html ${opts.html}: ${err instanceof Error ? err.message : String(err)}`,
541
+ );
542
+ process.exitCode = 1;
543
+ return;
544
+ }
545
+ }
689
546
 
690
- const fromAddress = addresses[0].address;
547
+ const params: Record<string, unknown> = { to, text };
548
+ if (opts.subject) params.subject = opts.subject;
549
+ if (html) params.html = html;
550
+ if (opts.cc && opts.cc.length > 0) params.cc = opts.cc;
551
+ if (opts.bcc && opts.bcc.length > 0) params.bcc = opts.bcc;
552
+ if (opts.replyTo) params.reply_to = opts.replyTo;
691
553
 
692
- // 2. Resolve body text: --body > --file > stdin
693
- let text = opts.body;
694
- if (!text && opts.file) {
695
- text = readFileSync(opts.file, "utf-8");
696
- }
697
- if (!text && !process.stdin.isTTY) {
698
- text = readFileSync("/dev/stdin", "utf-8");
699
- }
700
- if (!text) {
701
- throw new Error(
702
- "Email body is required. Use --body, --file, or pipe via stdin.",
554
+ const r = await cliIpcCall<{ delivery_id: string; status: string }>(
555
+ "email_send",
556
+ { body: params },
703
557
  );
704
- }
705
-
706
- // 3. Resolve HTML body: explicit file > auto-generate from text
707
- let html: string | undefined;
708
- if (opts.html) {
709
- html = readFileSync(opts.html, "utf-8");
710
- } else {
711
- // Auto-generate HTML from the text body (markdown → email HTML).
712
- html = markdownToEmailHtml(text);
713
- }
714
-
715
- // 4. Build payload
716
- const payload: Record<string, unknown> = {
717
- to,
718
- from_address: fromAddress,
719
- text,
720
- };
721
- if (opts.subject) payload.subject = opts.subject;
722
- if (html) payload.html = html;
723
- if (opts.cc && opts.cc.length > 0) payload.cc = opts.cc;
724
- if (opts.bcc && opts.bcc.length > 0) payload.bcc = opts.bcc;
725
- if (opts.replyTo) payload.reply_to = opts.replyTo;
726
-
727
- // 5. Send via runtime proxy
728
- const response = await client.fetch("/v1/runtime-proxy/email/send/", {
729
- method: "POST",
730
- headers: { "Content-Type": "application/json" },
731
- body: JSON.stringify(payload),
732
- });
733
-
734
- if (!response.ok) {
735
- const body = (await response.json().catch(() => ({}))) as Record<
736
- string,
737
- unknown
738
- >;
739
- if (response.status === 402) {
740
- throw new Error(
741
- "Insufficient balance to send email. Add credits at https://platform.vellum.ai/billing",
558
+ if (!r.ok)
559
+ return handleEmailIpcError(
560
+ { ok: false, error: r.error, statusCode: r.statusCode },
561
+ cmd,
562
+ );
563
+ const data = r.result!;
564
+ if (shouldOutputJson(cmd)) {
565
+ writeOutput(cmd, data);
566
+ } else {
567
+ log.info(
568
+ `✓ Sent to ${to.join(", ")} (delivery_id: ${data.delivery_id})`,
742
569
  );
743
570
  }
744
- const detail = body.detail ?? `HTTP ${response.status}`;
745
- throw new Error(String(detail));
746
- }
747
-
748
- const data = (await response.json()) as {
749
- delivery_id: string;
750
- status: string;
751
- };
752
-
753
- if (shouldOutputJson(cmd)) {
754
- writeOutput(cmd, data);
755
- } else {
756
- log.info(
757
- `✓ Sent to ${to.join(", ")} (delivery_id: ${data.delivery_id})`,
758
- );
759
- }
760
- } catch (err) {
761
- const message = err instanceof Error ? err.message : String(err);
762
- if (shouldOutputJson(cmd)) {
763
- writeOutput(cmd, { error: message });
764
- } else {
765
- log.error(`Error: ${message}`);
766
- }
767
- process.exitCode = 1;
768
- }
769
- },
770
- );
571
+ },
572
+ );
771
573
 
772
- email
773
- .command("attachment <message-id> [attachment-id]")
774
- .description("Download email attachments")
775
- .option("--all", "Download all attachments for the message")
776
- .option(
777
- "-o, --output <dir>",
778
- "Output directory (default: current directory)",
779
- ".",
780
- )
781
- .option("--list", "List attachments without downloading")
782
- .addHelpText(
783
- "after",
784
- `
574
+ email
575
+ .command("attachment <message-id> [attachment-id]")
576
+ .description("Download email attachments")
577
+ .option("--all", "Download all attachments for the message")
578
+ .option(
579
+ "-o, --output <dir>",
580
+ "Output directory (default: current directory)",
581
+ ".",
582
+ )
583
+ .option("--list", "List attachments without downloading")
584
+ .addHelpText(
585
+ "after",
586
+ `
785
587
  Arguments:
786
588
  message-id Email message ID (from \`assistant email list --json\`)
787
589
  attachment-id Attachment ID (optional — required unless --all or --list)
@@ -796,162 +598,167 @@ $ assistant email attachment msg_abc1 att_xyz1 -o ./downloads/
796
598
  $ assistant email attachment msg_abc1 --all
797
599
  $ assistant email attachment msg_abc1 --all -o ./attachments/
798
600
  $ assistant email attachment msg_abc1 --list --json`,
799
- )
800
- .action(
801
- async (
802
- messageId: string,
803
- attachmentId: string | undefined,
804
- opts: {
805
- all?: boolean;
806
- output?: string;
807
- list?: boolean;
808
- },
809
- cmd: Command,
810
- ) => {
811
- try {
812
- const client = await VellumPlatformClient.create();
813
- if (!client) {
814
- throw new Error(
815
- "Platform credentials not configured. Run: assistant platform connect",
816
- );
817
- }
818
- if (!client.platformAssistantId) {
819
- throw new Error(
820
- "Assistant ID not configured. Run: assistant platform connect",
821
- );
822
- }
823
-
824
- const assistantId = client.platformAssistantId;
825
- const basePath = `/v1/assistants/${assistantId}/emails/${messageId}/attachments`;
826
-
827
- if (opts.list) {
828
- // List mode — show attachment metadata without downloading
829
- const response = await client.fetch(`${basePath}/`);
830
- if (!response.ok) {
831
- const body = (await response.json().catch(() => ({}))) as Record<
832
- string,
833
- unknown
834
- >;
835
- const detail = body.detail ?? `HTTP ${response.status}`;
836
- throw new Error(String(detail));
837
- }
838
-
839
- const data = (await response.json()) as {
840
- results: AttachmentMeta[];
841
- };
842
-
843
- if (shouldOutputJson(cmd)) {
844
- writeOutput(cmd, data);
845
- } else {
846
- const attachments = data.results ?? [];
847
- if (attachments.length === 0) {
848
- log.info("No attachments for this message.");
601
+ )
602
+ .action(
603
+ async (
604
+ messageId: string,
605
+ attachmentId: string | undefined,
606
+ opts: {
607
+ all?: boolean;
608
+ output?: string;
609
+ list?: boolean;
610
+ },
611
+ cmd: Command,
612
+ ) => {
613
+ if (opts.list) {
614
+ // List mode show attachment metadata without downloading
615
+ const r = await cliIpcCall<{ results: AttachmentMeta[] }>(
616
+ "email_attachment_list",
617
+ { queryParams: { messageId } },
618
+ );
619
+ if (!r.ok)
620
+ return handleEmailIpcError(
621
+ { ok: false, error: r.error, statusCode: r.statusCode },
622
+ cmd,
623
+ );
624
+ const data = r.result!;
625
+ if (shouldOutputJson(cmd)) {
626
+ writeOutput(cmd, data);
849
627
  } else {
850
- for (const att of attachments) {
851
- log.info(
852
- ` ${att.id} ${att.filename} (${att.content_type}, ${formatBytes(att.size_bytes)})`,
853
- );
628
+ const attachments = data.results ?? [];
629
+ if (attachments.length === 0) {
630
+ log.info("No attachments for this message.");
631
+ } else {
632
+ for (const att of attachments) {
633
+ log.info(
634
+ ` ${att.id} ${att.filename} (${att.content_type}, ${formatBytes(att.size_bytes)})`,
635
+ );
636
+ }
637
+ log.info(`\n${attachments.length} attachment(s)`);
854
638
  }
855
- log.info(`\n${attachments.length} attachment(s)`);
856
639
  }
857
- }
858
- return;
859
- }
860
-
861
- if (!opts.all && !attachmentId) {
862
- throw new Error(
863
- "Specify an attachment ID, or use --all to download all attachments. Use --list to see available attachments.",
864
- );
865
- }
866
-
867
- // Ensure output directory exists
868
- const outDir = opts.output ?? ".";
869
- mkdirSync(outDir, { recursive: true });
870
-
871
- if (opts.all) {
872
- // Download all attachments
873
- const listResponse = await client.fetch(`${basePath}/`);
874
- if (!listResponse.ok) {
875
- const body = (await listResponse
876
- .json()
877
- .catch(() => ({}))) as Record<string, unknown>;
878
- const detail = body.detail ?? `HTTP ${listResponse.status}`;
879
- throw new Error(String(detail));
640
+ return;
880
641
  }
881
642
 
882
- const listData = (await listResponse.json()) as {
883
- results: AttachmentMeta[];
884
- };
885
-
886
- const attachments = listData.results ?? [];
887
- if (attachments.length === 0) {
888
- throw new Error("No attachments for this message.");
889
- }
890
-
891
- const downloaded: { filename: string; size_bytes: number }[] = [];
892
- for (const att of attachments) {
893
- const dest = join(outDir, safeFilename(att.filename));
894
- await downloadAttachment(client, basePath, att.id, dest);
895
- downloaded.push({
896
- filename: att.filename,
897
- size_bytes: att.size_bytes,
898
- });
643
+ if (!opts.all && !attachmentId) {
644
+ log.error(
645
+ "Specify an attachment ID, or use --all to download all. Use --list to see available.",
646
+ );
647
+ process.exitCode = 1;
648
+ return;
899
649
  }
900
650
 
901
- if (shouldOutputJson(cmd)) {
902
- writeOutput(cmd, {
903
- downloaded: downloaded.length,
904
- directory: outDir,
905
- files: downloaded,
906
- });
907
- } else {
908
- log.info(
909
- `✓ Downloaded ${downloaded.length} attachment(s) to ${outDir}`,
651
+ // Ensure output directory exists and download attachment(s)
652
+ const outDir = opts.output ?? ".";
653
+ try {
654
+ mkdirSync(outDir, { recursive: true });
655
+ } catch (err) {
656
+ log.error(
657
+ `Failed to create output directory ${outDir}: ${err instanceof Error ? err.message : String(err)}`,
910
658
  );
911
- for (const f of downloaded) {
912
- log.info(` - ${f.filename} (${formatBytes(f.size_bytes)})`);
913
- }
914
- }
915
- } else {
916
- // Download single attachment — first get metadata for the filename
917
- const metaResponse = await client.fetch(
918
- `${basePath}/${attachmentId}/`,
919
- );
920
- if (!metaResponse.ok) {
921
- const body = (await metaResponse
922
- .json()
923
- .catch(() => ({}))) as Record<string, unknown>;
924
- const detail = body.detail ?? `HTTP ${metaResponse.status}`;
925
- throw new Error(String(detail));
659
+ process.exitCode = 1;
660
+ return;
926
661
  }
927
662
 
928
- const meta = (await metaResponse.json()) as AttachmentMeta;
929
- const dest = join(outDir, safeFilename(meta.filename));
930
- await downloadAttachment(client, basePath, meta.id, dest);
663
+ try {
664
+ if (opts.all) {
665
+ // Download all attachments — list first to get filenames
666
+ const listR = await cliIpcCall<{ results: AttachmentMeta[] }>(
667
+ "email_attachment_list",
668
+ { queryParams: { messageId } },
669
+ );
670
+ if (!listR.ok)
671
+ return handleEmailIpcError(
672
+ {
673
+ ok: false,
674
+ error: listR.error,
675
+ statusCode: listR.statusCode,
676
+ },
677
+ cmd,
678
+ );
679
+ const attachments = listR.result!.results ?? [];
680
+ if (attachments.length === 0) {
681
+ log.error("No attachments for this message.");
682
+ process.exitCode = 1;
683
+ return;
684
+ }
931
685
 
932
- if (shouldOutputJson(cmd)) {
933
- writeOutput(cmd, {
934
- filename: meta.filename,
935
- size_bytes: meta.size_bytes,
936
- saved: dest,
937
- });
938
- } else {
939
- log.info(
940
- `✓ Downloaded ${meta.filename} (${formatBytes(meta.size_bytes)})`,
686
+ const downloaded: { filename: string; size_bytes: number }[] =
687
+ [];
688
+ for (const att of attachments) {
689
+ const dest = join(outDir, safeFilename(att.filename));
690
+ await streamDownloadAttachment(att.id, messageId, dest);
691
+ downloaded.push({
692
+ filename: att.filename,
693
+ size_bytes: att.size_bytes,
694
+ });
695
+ }
696
+
697
+ if (shouldOutputJson(cmd)) {
698
+ writeOutput(cmd, {
699
+ downloaded: downloaded.length,
700
+ directory: outDir,
701
+ files: downloaded,
702
+ });
703
+ } else {
704
+ log.info(
705
+ `✓ Downloaded ${downloaded.length} attachment(s) to ${outDir}`,
706
+ );
707
+ for (const f of downloaded) {
708
+ log.info(
709
+ ` - ${f.filename} (${formatBytes(f.size_bytes)})`,
710
+ );
711
+ }
712
+ }
713
+ } else {
714
+ // Download single attachment — look up metadata from the list first
715
+ const listR = await cliIpcCall<{ results: AttachmentMeta[] }>(
716
+ "email_attachment_list",
717
+ { queryParams: { messageId } },
718
+ );
719
+ if (!listR.ok)
720
+ return handleEmailIpcError(
721
+ {
722
+ ok: false,
723
+ error: listR.error,
724
+ statusCode: listR.statusCode,
725
+ },
726
+ cmd,
727
+ );
728
+ const meta = (listR.result!.results ?? []).find(
729
+ (a) => a.id === attachmentId,
730
+ );
731
+ if (!meta) {
732
+ log.error(`Attachment not found: ${attachmentId}`);
733
+ process.exitCode = 2;
734
+ return;
735
+ }
736
+ const dest = join(outDir, safeFilename(meta.filename));
737
+ await streamDownloadAttachment(attachmentId!, messageId, dest);
738
+
739
+ if (shouldOutputJson(cmd)) {
740
+ writeOutput(cmd, {
741
+ filename: meta.filename,
742
+ size_bytes: meta.size_bytes,
743
+ saved: dest,
744
+ });
745
+ } else {
746
+ log.info(
747
+ `✓ Downloaded ${meta.filename} (${formatBytes(meta.size_bytes)})`,
748
+ );
749
+ }
750
+ }
751
+ } catch (err) {
752
+ log.error(
753
+ `Failed to download attachment: ${err instanceof Error ? err.message : String(err)}`,
941
754
  );
755
+ process.exitCode = 1;
756
+ return;
942
757
  }
943
- }
944
- } catch (err) {
945
- const message = err instanceof Error ? err.message : String(err);
946
- if (shouldOutputJson(cmd)) {
947
- writeOutput(cmd, { error: message });
948
- } else {
949
- log.error(`Error: ${message}`);
950
- }
951
- process.exitCode = 1;
952
- }
953
- },
954
- );
758
+ },
759
+ );
760
+ },
761
+ });
955
762
  }
956
763
 
957
764
  interface AttachmentMeta {
@@ -974,27 +781,32 @@ function safeFilename(name: string): string {
974
781
  return basename(name).replace(/[\x00/\\]/g, "_") || "attachment";
975
782
  }
976
783
 
977
- async function downloadAttachment(
978
- client: VellumPlatformClient,
979
- basePath: string,
784
+ async function streamDownloadAttachment(
980
785
  attachmentId: string,
786
+ messageId: string,
981
787
  dest: string,
982
788
  ): Promise<void> {
983
- const response = await client.fetch(`${basePath}/${attachmentId}/download/`);
984
-
985
- if (!response.ok) {
986
- const body = (await response.json().catch(() => ({}))) as Record<
987
- string,
988
- unknown
989
- >;
990
- const detail = body.detail ?? `HTTP ${response.status}`;
991
- throw new Error(`Failed to download attachment: ${detail}`);
992
- }
993
-
994
- if (!response.body) {
995
- throw new Error("Empty response body from download endpoint.");
996
- }
789
+ const r = await cliIpcCallStream("email_attachment_get", {
790
+ queryParams: { messageId, attachmentId },
791
+ });
792
+ if (!r.ok) throw new Error(r.error ?? "Stream failed");
997
793
 
998
794
  const fileStream = createWriteStream(dest);
999
- await pipeline(response.body as unknown as Readable, fileStream);
795
+ const reader = r.body.getReader();
796
+ try {
797
+ while (true) {
798
+ const { done, value } = await reader.read();
799
+ if (done) break;
800
+ await new Promise<void>((resolve, reject) =>
801
+ fileStream.write(value, (err) => (err ? reject(err) : resolve())),
802
+ );
803
+ }
804
+ await new Promise<void>((resolve, reject) =>
805
+ fileStream.close((err) => (err ? reject(err) : resolve())),
806
+ );
807
+ } catch (err) {
808
+ r.abort();
809
+ fileStream.destroy();
810
+ throw err;
811
+ }
1000
812
  }