@vellumai/assistant 0.12.1 → 0.12.2-staging.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 (577) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +20 -21
  3. package/docs/architecture/turn-actor.md +15 -8
  4. package/docs/guardian-request-flow.md +16 -0
  5. package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
  6. package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
  7. package/openapi.yaml +360 -51
  8. package/package.json +3 -1
  9. package/scripts/smoke-desktop-window-theme.ts +186 -0
  10. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  11. package/src/__tests__/anthropic-provider.test.ts +0 -63
  12. package/src/__tests__/app-compiler.test.ts +6 -0
  13. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  14. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  15. package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
  16. package/src/__tests__/assistant-event-hub.test.ts +44 -0
  17. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  18. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  19. package/src/__tests__/computer-use-tools.test.ts +19 -7
  20. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  21. package/src/__tests__/config-schema.test.ts +28 -29
  22. package/src/__tests__/config-watcher.test.ts +23 -8
  23. package/src/__tests__/connection-model-compat.test.ts +2 -2
  24. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  25. package/src/__tests__/conversation-pairing.test.ts +26 -0
  26. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  27. package/src/__tests__/conversation-queue.test.ts +48 -207
  28. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  29. package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
  30. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  31. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  32. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
  33. package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
  34. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  35. package/src/__tests__/conversation-title-service.test.ts +14 -10
  36. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  37. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  38. package/src/__tests__/credential-record-retire.test.ts +171 -1
  39. package/src/__tests__/credential-routes.test.ts +250 -3
  40. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  41. package/src/__tests__/cu-unified-flow.test.ts +14 -4
  42. package/src/__tests__/document-sync-tags.test.ts +25 -0
  43. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  44. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  45. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  46. package/src/__tests__/git-porcelain.test.ts +47 -0
  47. package/src/__tests__/heartbeat-service.test.ts +56 -40
  48. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  49. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  50. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  51. package/src/__tests__/host-cu-proxy.test.ts +596 -0
  52. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  53. package/src/__tests__/host-file-proxy.test.ts +11 -23
  54. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  55. package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
  56. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  57. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  58. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  59. package/src/__tests__/mcp-health-check.test.ts +8 -11
  60. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  61. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  62. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  63. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  64. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  65. package/src/__tests__/model-intents.test.ts +3 -0
  66. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  67. package/src/__tests__/oauth-commands-routes.test.ts +168 -0
  68. package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
  69. package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
  70. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  71. package/src/__tests__/openai-provider.test.ts +1 -0
  72. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  73. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  74. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  75. package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
  76. package/src/__tests__/qdrant-manager.test.ts +132 -0
  77. package/src/__tests__/resolve-app.test.ts +135 -0
  78. package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
  79. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  80. package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
  81. package/src/__tests__/service-account-token.test.ts +247 -0
  82. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  83. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +9 -4
  85. package/src/__tests__/source-fingerprint.test.ts +184 -0
  86. package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
  87. package/src/__tests__/subagent-tools.test.ts +3 -2
  88. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  89. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  90. package/src/__tests__/ui-channel-variants.test.ts +1 -56
  91. package/src/__tests__/unicode.test.ts +13 -0
  92. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  93. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  94. package/src/__tests__/workspace-git-service.test.ts +26 -0
  95. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  96. package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
  97. package/src/acp/__tests__/acp-credentials.test.ts +13 -0
  98. package/src/acp/__tests__/agent-process.test.ts +258 -4
  99. package/src/acp/__tests__/auth-required.test.ts +96 -0
  100. package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
  101. package/src/acp/__tests__/client-handler.test.ts +90 -2
  102. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  103. package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
  104. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  105. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  106. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  107. package/src/acp/__tests__/session-manager.test.ts +895 -4
  108. package/src/acp/acp-claude-oauth.ts +328 -14
  109. package/src/acp/acp-credentials.ts +19 -0
  110. package/src/acp/agent-process.test.ts +4 -1
  111. package/src/acp/agent-process.ts +96 -10
  112. package/src/acp/auth-required.ts +50 -1
  113. package/src/acp/auto-install.test.ts +9 -7
  114. package/src/acp/claude-token-refresh.ts +150 -0
  115. package/src/acp/client-handler.ts +16 -3
  116. package/src/acp/model-config.test.ts +166 -0
  117. package/src/acp/model-config.ts +138 -0
  118. package/src/acp/prepare-agent-env.ts +21 -6
  119. package/src/acp/resolve-agent.test.ts +220 -6
  120. package/src/acp/resolve-agent.ts +88 -23
  121. package/src/acp/session-manager.test.ts +196 -3
  122. package/src/acp/session-manager.ts +441 -18
  123. package/src/acp/types.ts +50 -5
  124. package/src/agent/loop.ts +1 -0
  125. package/src/api/events/acp-session-model-update.test.ts +88 -0
  126. package/src/api/events/acp-session-model-update.ts +46 -0
  127. package/src/api/events/app-files-changed.ts +4 -3
  128. package/src/api/events/notification-intent.ts +2 -0
  129. package/src/api/events/user-message-echo.ts +7 -1
  130. package/src/api/index.ts +6 -0
  131. package/src/api/responses/conversation-message.ts +5 -0
  132. package/src/apps/app-store.ts +1 -1
  133. package/src/apps/resolve-app.ts +81 -0
  134. package/src/apps/source-fingerprint.ts +266 -0
  135. package/src/avatar/identity-avatar.ts +7 -2
  136. package/src/bundler/app-compiler.ts +15 -0
  137. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  138. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  139. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  140. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  141. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  142. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  143. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  144. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  145. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  146. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  147. package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
  148. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  149. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  150. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  151. package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
  152. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  153. package/src/calls/barge-in-guard.ts +118 -0
  154. package/src/calls/call-constants.ts +24 -4
  155. package/src/calls/call-controller.ts +499 -199
  156. package/src/calls/call-transport.ts +16 -0
  157. package/src/calls/media-stream-output.ts +32 -0
  158. package/src/calls/media-stream-server.ts +82 -30
  159. package/src/calls/media-stream-stt-session.ts +15 -0
  160. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  161. package/src/calls/resolve-call-tts-provider.ts +33 -2
  162. package/src/calls/tts-call-strategy.ts +9 -5
  163. package/src/calls/types.ts +2 -0
  164. package/src/calls/voice-control-protocol.ts +155 -21
  165. package/src/calls/voice-leg-coordinator.ts +189 -0
  166. package/src/calls/voice-progress-cadence.ts +423 -0
  167. package/src/calls/voice-session-bridge.ts +92 -72
  168. package/src/calls/voice-triage-escalate.ts +178 -59
  169. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  170. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  171. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  172. package/src/cli/commands/apps.help.ts +67 -13
  173. package/src/cli/commands/apps.ts +183 -0
  174. package/src/cli/commands/bash.help.ts +8 -13
  175. package/src/cli/commands/bash.ts +7 -25
  176. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  177. package/src/cli/commands/channels/index.help.ts +70 -2
  178. package/src/cli/commands/channels/index.ts +16 -3
  179. package/src/cli/commands/channels/send.ts +128 -0
  180. package/src/cli/commands/credentials.help.ts +6 -2
  181. package/src/cli/commands/credentials.ts +2 -2
  182. package/src/cli/commands/mcp.help.ts +8 -8
  183. package/src/cli/commands/mcp.ts +1 -1
  184. package/src/cli/commands/oauth/status.ts +5 -0
  185. package/src/cli/commands/roadmap.help.ts +5 -1
  186. package/src/cli/commands/roadmap.ts +1 -1
  187. package/src/cli/lib/bundled-marketplace.json +103 -90
  188. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  189. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  190. package/src/config/__tests__/platform-identity.test.ts +255 -0
  191. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  192. package/src/config/acp-agent-write.ts +43 -0
  193. package/src/config/acp-defaults.test.ts +13 -3
  194. package/src/config/acp-defaults.ts +23 -6
  195. package/src/config/acp-schema.test.ts +81 -0
  196. package/src/config/acp-schema.ts +30 -2
  197. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  198. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  199. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  200. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  201. package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
  202. package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
  203. package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
  204. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
  205. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
  206. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
  207. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  208. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  209. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  210. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  211. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
  212. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
  213. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  214. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  215. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  216. package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
  217. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
  218. package/src/config/default-profile-catalog.ts +1 -1
  219. package/src/config/env.ts +2 -2
  220. package/src/config/feature-flag-registry.json +33 -13
  221. package/src/config/loader.ts +2 -0
  222. package/src/config/memory-v3-gate.test.ts +31 -0
  223. package/src/config/memory-v3-gate.ts +20 -16
  224. package/src/config/platform-identity.ts +201 -0
  225. package/src/config/platform-rehydration.ts +11 -27
  226. package/src/config/schema.ts +2 -2
  227. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  228. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  229. package/src/config/schemas/calls.ts +1 -28
  230. package/src/config/schemas/live-voice.ts +1 -173
  231. package/src/config/schemas/mcp.ts +1 -1
  232. package/src/config/schemas/memory-retrospective.ts +10 -0
  233. package/src/config/schemas/services.ts +18 -0
  234. package/src/config/schemas/skills.ts +0 -14
  235. package/src/config/schemas/voice.ts +150 -0
  236. package/src/credential-execution/process-manager.test.ts +18 -19
  237. package/src/credential-health/credential-health-service.ts +11 -17
  238. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  239. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  240. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  241. package/src/daemon/app-change-notify.ts +45 -0
  242. package/src/daemon/config-watcher.ts +13 -194
  243. package/src/daemon/conversation-error.ts +3 -2
  244. package/src/daemon/conversation-media-retry.ts +2 -1
  245. package/src/daemon/conversation-process.ts +35 -64
  246. package/src/daemon/conversation-runtime-assembly.ts +20 -7
  247. package/src/daemon/conversation-skill-tools.ts +13 -31
  248. package/src/daemon/conversation-store.ts +1 -1
  249. package/src/daemon/conversation-surfaces.ts +21 -10
  250. package/src/daemon/conversation-tool-setup.ts +45 -59
  251. package/src/daemon/conversation.ts +0 -3
  252. package/src/daemon/dictation-profile-store.ts +2 -1
  253. package/src/daemon/handlers/shared.ts +2 -1
  254. package/src/daemon/handlers/skills.ts +6 -3
  255. package/src/daemon/host-bash-proxy.ts +8 -12
  256. package/src/daemon/host-browser-proxy.ts +15 -1
  257. package/src/daemon/host-cu-proxy.ts +224 -19
  258. package/src/daemon/host-file-proxy.ts +8 -10
  259. package/src/daemon/host-proxy-preactivation.ts +2 -1
  260. package/src/daemon/host-transfer-proxy.ts +14 -18
  261. package/src/daemon/lifecycle.ts +0 -5
  262. package/src/daemon/mcp-reload-service.ts +5 -5
  263. package/src/daemon/message-types/sync.ts +1 -0
  264. package/src/daemon/orphan-reaper.ts +1 -1
  265. package/src/daemon/providers-setup.ts +1 -1
  266. package/src/daemon/reaction-history-render.ts +2 -1
  267. package/src/daemon/shutdown-handlers.ts +1 -9
  268. package/src/daemon/skill-memory-refresh.ts +64 -0
  269. package/src/daemon/tool-side-effects.ts +7 -18
  270. package/src/desktop/__tests__/fake-desktop.ts +122 -0
  271. package/src/desktop/desktop-chrome-policy.test.ts +79 -0
  272. package/src/desktop/desktop-chrome-policy.ts +47 -0
  273. package/src/desktop/desktop-chrome-session.ts +37 -0
  274. package/src/desktop/desktop-dependencies.test.ts +102 -0
  275. package/src/desktop/desktop-dependencies.ts +308 -0
  276. package/src/desktop/desktop-feature.test.ts +18 -0
  277. package/src/desktop/desktop-feature.ts +19 -0
  278. package/src/desktop/desktop-panel-config.test.ts +135 -0
  279. package/src/desktop/desktop-panel-config.ts +149 -0
  280. package/src/desktop/desktop-session-manager.test.ts +730 -0
  281. package/src/desktop/desktop-session-manager.ts +850 -0
  282. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  283. package/src/desktop/desktop-stream-bridge.ts +214 -0
  284. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  285. package/src/desktop/desktop-wallpaper.ts +113 -0
  286. package/src/desktop/desktop-window-drag.ts +177 -0
  287. package/src/desktop/desktop-window-manager-config.test.ts +230 -0
  288. package/src/desktop/desktop-window-manager-config.ts +238 -0
  289. package/src/desktop/desktop-window-theme.ts +170 -0
  290. package/src/heartbeat/heartbeat-service.ts +21 -3
  291. package/src/home/__tests__/feed-writer.test.ts +22 -0
  292. package/src/home/feed-writer.ts +11 -2
  293. package/src/hooks/types.ts +33 -0
  294. package/src/i18n/AGENTS.md +22 -0
  295. package/src/i18n/__tests__/i18n.test.ts +108 -0
  296. package/src/i18n/index.ts +16 -0
  297. package/src/i18n/locales.ts +93 -0
  298. package/src/i18n/messages.ts +61 -0
  299. package/src/i18n/title.ts +25 -0
  300. package/src/inbound/platform-callback-registration.ts +4 -5
  301. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
  302. package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
  303. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  304. package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
  305. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
  306. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  307. package/src/live-voice/__tests__/protocol.test.ts +39 -0
  308. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  309. package/src/live-voice/__tests__/session-controls.test.ts +79 -0
  310. package/src/live-voice/live-voice-photo.ts +25 -6
  311. package/src/live-voice/live-voice-session.ts +297 -699
  312. package/src/live-voice/protocol.ts +60 -2
  313. package/src/live-voice/session-controls.ts +111 -0
  314. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  315. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  316. package/src/mcp/effective-config.ts +3 -3
  317. package/src/mcp/mcp-header-store.ts +29 -45
  318. package/src/mcp/spec-schema.ts +111 -0
  319. package/src/mcp/startup.ts +8 -4
  320. package/src/mcp/workspace-mcp-config.ts +188 -0
  321. package/src/messaging/provider.ts +10 -3
  322. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  323. package/src/messaging/providers/callback-routing.ts +18 -0
  324. package/src/messaging/providers/channel-transport.ts +56 -0
  325. package/src/messaging/providers/discord/transport.ts +17 -0
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  327. package/src/messaging/providers/slack/adapter.ts +7 -44
  328. package/src/messaging/providers/slack/transport.ts +16 -0
  329. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  330. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  331. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  332. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  333. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  334. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  335. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  336. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  337. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  338. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  339. package/src/notifications/adapters/macos.ts +3 -0
  340. package/src/notifications/assistant-reply-producer.ts +8 -3
  341. package/src/notifications/conversation-pairing.ts +36 -16
  342. package/src/notifications/delivered-post-record.ts +11 -1
  343. package/src/notifications/guardian-feed-projection.ts +125 -5
  344. package/src/notifications/home-feed-side-effect.ts +55 -7
  345. package/src/notifications/schedule-result-producer.ts +66 -22
  346. package/src/oauth/AGENTS.md +2 -0
  347. package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
  348. package/src/oauth/byo-connection.test.ts +105 -0
  349. package/src/oauth/byo-connection.ts +36 -6
  350. package/src/oauth/connection-resolver.test.ts +100 -0
  351. package/src/oauth/connection-resolver.ts +120 -3
  352. package/src/oauth/provider-serializer.ts +9 -0
  353. package/src/oauth/scope-utils.ts +24 -0
  354. package/src/oauth/seed-providers.ts +314 -27
  355. package/src/outbound-proxy/certs.ts +3 -5
  356. package/src/permissions/checker.test.ts +11 -6
  357. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  358. package/src/persistence/conversation-crud.ts +71 -63
  359. package/src/persistence/conversation-lineage.ts +12 -4
  360. package/src/persistence/conversation-queries.ts +2 -2
  361. package/src/persistence/conversation-title-placeholders.ts +23 -0
  362. package/src/persistence/conversation-title-service.ts +9 -26
  363. package/src/persistence/embeddings/qdrant-manager.ts +163 -18
  364. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  365. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  366. package/src/persistence/message-cursor.ts +78 -0
  367. package/src/persistence/message-reads.ts +30 -0
  368. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  369. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  370. package/src/persistence/schema/memory-core.ts +5 -0
  371. package/src/platform/client.test.ts +4 -2
  372. package/src/platform/client.ts +2 -10
  373. package/src/plugin-api/constants.ts +2 -0
  374. package/src/plugin-api/index.ts +1 -0
  375. package/src/plugin-api/types.ts +2 -0
  376. package/src/plugins/defaults/index.ts +2 -0
  377. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  378. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  379. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  380. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  381. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  382. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
  383. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  384. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  385. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  386. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  387. package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
  388. package/src/plugins/defaults/memory/context-search/format.ts +3 -2
  389. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
  390. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
  391. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
  392. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  393. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  394. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  395. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  396. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  397. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  398. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  399. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  400. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  401. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  402. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  403. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  404. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  405. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  406. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  407. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  408. package/src/plugins/defaults/memory/startup.ts +28 -2
  409. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  410. package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
  411. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  412. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
  413. package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
  414. package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
  415. package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
  416. package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
  417. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
  418. package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
  419. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
  420. package/src/plugins/defaults/memory/v3/card.ts +3 -2
  421. package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
  422. package/src/plugins/defaults/memory/v3/sections.ts +9 -3
  423. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
  424. package/src/plugins/defaults/memory/worker.ts +6 -0
  425. package/src/plugins/mcp-servers.ts +15 -75
  426. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  427. package/src/providers/__tests__/retry-callsite.test.ts +41 -0
  428. package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
  429. package/src/providers/anthropic/client.ts +0 -47
  430. package/src/providers/gemini/client.ts +9 -23
  431. package/src/providers/gemini-thought-signature.test.ts +63 -0
  432. package/src/providers/gemini-thought-signature.ts +44 -0
  433. package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
  434. package/src/providers/inference/auth.ts +2 -3
  435. package/src/providers/inference/connection-availability.ts +27 -8
  436. package/src/providers/inference/endpoint-probe.ts +10 -1
  437. package/src/providers/inference/resolve-auth.ts +22 -4
  438. package/src/providers/inference/service-account-token.ts +248 -0
  439. package/src/providers/model-catalog.ts +34 -0
  440. package/src/providers/model-intents.ts +10 -5
  441. package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
  442. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  443. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  444. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  445. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  446. package/src/providers/openai/chat-completions-provider.ts +212 -79
  447. package/src/providers/openai/codex-models.ts +0 -4
  448. package/src/providers/openai/google-thought-signature.ts +320 -0
  449. package/src/providers/opencode/client.test.ts +73 -4
  450. package/src/providers/opencode/client.ts +60 -3
  451. package/src/providers/openrouter/client.ts +20 -0
  452. package/src/providers/outbound-request-sanitize.ts +96 -0
  453. package/src/providers/retry.ts +30 -19
  454. package/src/providers/unparseable-tool-args.ts +5 -1
  455. package/src/runtime/AGENTS.md +3 -1
  456. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  457. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  458. package/src/runtime/__tests__/host-observe.test.ts +40 -1
  459. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  460. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  461. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  462. package/src/runtime/agent-wake.ts +2 -1
  463. package/src/runtime/assistant-event-hub.ts +21 -3
  464. package/src/runtime/auth/same-actor.ts +38 -3
  465. package/src/runtime/channel-send.ts +316 -0
  466. package/src/runtime/host-observe.ts +11 -1
  467. package/src/runtime/http-server.ts +160 -151
  468. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
  469. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  470. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
  471. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  472. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  473. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  474. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  475. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  476. package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
  477. package/src/runtime/routes/acp-routes.test.ts +15 -0
  478. package/src/runtime/routes/acp-routes.ts +72 -9
  479. package/src/runtime/routes/app-management-routes.ts +90 -2
  480. package/src/runtime/routes/app-routes.ts +1 -1
  481. package/src/runtime/routes/channel-send-routes.ts +85 -0
  482. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  483. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  484. package/src/runtime/routes/conversation-routes.ts +16 -3
  485. package/src/runtime/routes/credential-routes.ts +20 -14
  486. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  487. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  488. package/src/runtime/routes/diagnostics-routes.ts +61 -4
  489. package/src/runtime/routes/events-routes.ts +4 -0
  490. package/src/runtime/routes/host-cu-routes.ts +6 -0
  491. package/src/runtime/routes/index.ts +4 -2
  492. package/src/runtime/routes/log-export-routes.ts +2 -0
  493. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  494. package/src/runtime/routes/migration-routes.ts +6 -20
  495. package/src/runtime/routes/oauth-commands-routes.ts +50 -0
  496. package/src/runtime/routes/oauth-providers.ts +7 -0
  497. package/src/runtime/routes/platform-routes.ts +18 -15
  498. package/src/runtime/routes/workspace-routes.test.ts +55 -26
  499. package/src/runtime/routes/workspace-routes.ts +46 -63
  500. package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
  501. package/src/runtime/routes/workspace-tree-walk.ts +240 -0
  502. package/src/runtime/routes/workspace-utils.ts +17 -0
  503. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  504. package/src/runtime/services/conversation-serializer.ts +16 -3
  505. package/src/runtime/sync/resource-sync-events.ts +2 -1
  506. package/src/schedule/run-script.ts +2 -1
  507. package/src/schedule/schedule-store.ts +9 -2
  508. package/src/schedule/worker.ts +1 -1
  509. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  510. package/src/security/ces-http-record-backend.ts +120 -0
  511. package/src/security/ces-rpc-record-backend.ts +7 -0
  512. package/src/security/untrusted-content.ts +3 -1
  513. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  514. package/src/skills/workspace-skill-md-signature.ts +50 -0
  515. package/src/subagent/consult-context.ts +12 -25
  516. package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
  517. package/src/tools/acp/list-agents.test.ts +3 -1
  518. package/src/tools/acp/spawn.test.ts +146 -11
  519. package/src/tools/acp/spawn.ts +21 -3
  520. package/src/tools/acp/status.test.ts +94 -0
  521. package/src/tools/acp/status.ts +40 -3
  522. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  523. package/src/tools/browser/browser-execution.ts +1 -1
  524. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
  525. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
  526. package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
  527. package/src/tools/browser/network-recorder.ts +2 -1
  528. package/src/tools/capability-offer.test.ts +22 -2
  529. package/src/tools/capability-offer.ts +16 -1
  530. package/src/tools/computer-use/definitions.ts +121 -2
  531. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  532. package/src/tools/credentials/metadata-store.ts +100 -0
  533. package/src/tools/filesystem/search.ts +2 -1
  534. package/src/tools/host-filesystem/edit.test.ts +2 -1
  535. package/src/tools/host-filesystem/edit.ts +2 -2
  536. package/src/tools/host-filesystem/read.test.ts +2 -1
  537. package/src/tools/host-filesystem/read.ts +2 -2
  538. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  539. package/src/tools/host-filesystem/transfer.ts +2 -2
  540. package/src/tools/host-filesystem/write.test.ts +2 -1
  541. package/src/tools/host-filesystem/write.ts +2 -2
  542. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  543. package/src/tools/host-terminal/host-shell.ts +4 -20
  544. package/src/tools/network/web-search.ts +6 -1
  545. package/src/tools/policy-context.ts +6 -7
  546. package/src/tools/skills/scaffold-managed.ts +61 -3
  547. package/src/tools/subagent/spawn.ts +5 -8
  548. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  549. package/src/tools/terminal/sanitized-bash.ts +105 -0
  550. package/src/tools/ui-surface/channel-variants.ts +10 -59
  551. package/src/tools/watch/watch-retro-report.ts +0 -8
  552. package/src/types/host-capabilities.ts +5 -3
  553. package/src/util/__tests__/language-subtag.test.ts +23 -1
  554. package/src/util/ca-bundle.ts +13 -0
  555. package/src/util/language-subtag.ts +16 -0
  556. package/src/util/platform.ts +8 -0
  557. package/src/util/unicode.ts +15 -1
  558. package/src/watch/watch-retro.ts +6 -8
  559. package/src/watch/watch-timeline.ts +2 -2
  560. package/src/workflows/run-manager.ts +2 -1
  561. package/src/workspace/git-porcelain.ts +66 -0
  562. package/src/workspace/git-service.ts +6 -55
  563. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  564. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  565. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  566. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  567. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  568. package/src/workspace/migrations/registry.ts +6 -0
  569. package/src/__tests__/app-source-watcher.test.ts +0 -218
  570. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  571. package/src/__tests__/speaker-identification.test.ts +0 -56
  572. package/src/calls/speaker-identification.ts +0 -233
  573. package/src/daemon/app-source-watcher.ts +0 -218
  574. package/src/messaging/providers/whatsapp/client.ts +0 -29
  575. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  576. package/src/util/fs-watcher-error.ts +0 -36
  577. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -1,28 +1,19 @@
1
1
  /**
2
- * `assistant bash` run a shell command in the assistant's process
3
- * environment (debug tool, requires VELLUM_DEBUG=1 on the daemon).
2
+ * `assistant bash` runs a shell command with the same sanitized environment
3
+ * the bash tool forwards to tool-call subprocesses.
4
4
  */
5
5
 
6
6
  import type { Command } from "commander";
7
7
 
8
- import { cliIpcCall } from "../../ipc/cli-client.js";
9
8
  import { applyCommandHelp, commandSpec } from "../lib/cli-command-help.js";
10
9
  import { registerCommand } from "../lib/register-command.js";
11
10
  import { log } from "../logger.js";
12
11
  import { bashHelp } from "./bash.help.js";
13
12
 
14
- interface DebugBashResult {
15
- stdout: string;
16
- stderr: string;
17
- exitCode: number | null;
18
- timedOut: boolean;
19
- error?: string;
20
- }
21
-
22
13
  export function registerBashCommand(program: Command): void {
23
14
  registerCommand(program, {
24
15
  name: commandSpec(bashHelp),
25
- transport: "ipc",
16
+ transport: "local",
26
17
  description: bashHelp.description,
27
18
  build: (cmd) => {
28
19
  applyCommandHelp(cmd, bashHelp);
@@ -34,19 +25,10 @@ export function registerBashCommand(program: Command): void {
34
25
  return;
35
26
  }
36
27
 
37
- const result = await cliIpcCall<DebugBashResult>(
38
- "debug_bash",
39
- { body: { command, timeoutMs } },
40
- { timeoutMs: timeoutMs + 10_000 },
28
+ const { runSanitizedBash } = await import(
29
+ "../../tools/terminal/sanitized-bash.js"
41
30
  );
42
-
43
- if (!result.ok) {
44
- log.error(result.error ?? "Failed to reach the assistant.");
45
- process.exitCode = 1;
46
- return;
47
- }
48
-
49
- const data = result.result!;
31
+ const data = await runSanitizedBash(command, timeoutMs);
50
32
 
51
33
  if (data.error) {
52
34
  log.error(data.error);
@@ -69,7 +51,7 @@ export function registerBashCommand(program: Command): void {
69
51
  }
70
52
 
71
53
  if (data.timedOut) {
72
- log.info(`Command timed out in assistant.`);
54
+ log.info("Command timed out.");
73
55
  }
74
56
 
75
57
  if (data.exitCode != null && data.exitCode !== 0) {
@@ -0,0 +1,218 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ // The command is a door onto the daemon's one send implementation, so what
4
+ // matters is the request it hands the route and what it does with the
5
+ // answer. IPC is stubbed to capture that request; the exit-code helper
6
+ // stays real, so the tests assert the mapping that ships.
7
+
8
+ const ipcCalls: Array<{
9
+ method: string;
10
+ params: Record<string, unknown>;
11
+ opts?: Record<string, unknown>;
12
+ }> = [];
13
+ let ipcResult: Record<string, unknown> = {
14
+ ok: true,
15
+ result: {
16
+ channel: "slack",
17
+ chatId: "C0123456789",
18
+ messageIds: ["1700000000.000100"],
19
+ lastMessageId: "1700000000.000100",
20
+ recordedIn: "conv-home",
21
+ },
22
+ };
23
+
24
+ const actualCliClient = await import("../../../../ipc/cli-client.js");
25
+ mock.module("../../../../ipc/cli-client.js", () => ({
26
+ ...actualCliClient,
27
+ cliIpcCall: async (
28
+ method: string,
29
+ params: Record<string, unknown>,
30
+ opts?: Record<string, unknown>,
31
+ ) => {
32
+ ipcCalls.push({ method, params, ...(opts ? { opts } : {}) });
33
+ return ipcResult;
34
+ },
35
+ }));
36
+
37
+ // Imported after the mock is installed, so the command module under test
38
+ // binds the stubbed IPC call rather than the real one.
39
+ const { runCliCommand } = await import("../../__tests__/cli-test-harness.js");
40
+ const { registerChannelsCommand } = await import("../index.js");
41
+
42
+ beforeEach(() => {
43
+ ipcCalls.length = 0;
44
+ ipcResult = {
45
+ ok: true,
46
+ result: {
47
+ channel: "slack",
48
+ chatId: "C0123456789",
49
+ messageIds: ["1700000000.000100"],
50
+ lastMessageId: "1700000000.000100",
51
+ recordedIn: "conv-home",
52
+ },
53
+ };
54
+ });
55
+
56
+ // The shipped registration as-is: `send` declares its own `--json`, which is
57
+ // why the program's global leaf-option pass skips it.
58
+ function runSend(args: string[]) {
59
+ return runCliCommand(registerChannelsCommand, ["channels", "send", ...args]);
60
+ }
61
+
62
+ /** The body the command handed the route on its one call. */
63
+ function sentBody(): Record<string, unknown> {
64
+ expect(ipcCalls).toHaveLength(1);
65
+ expect(ipcCalls[0]!.method).toBe("channels_send_post");
66
+ return ipcCalls[0]!.params.body as Record<string, unknown>;
67
+ }
68
+
69
+ describe("the request the command makes", () => {
70
+ test("names the chat and asks for the channel's rich rendering", async () => {
71
+ const r = await runSend([
72
+ "slack",
73
+ "C0123456789",
74
+ "--text",
75
+ "deploy is green",
76
+ ]);
77
+ expect(r.exitCode).toBe(0);
78
+ expect(sentBody()).toEqual({
79
+ channel: "slack",
80
+ target: { kind: "chat", chatId: "C0123456789" },
81
+ text: "deploy is green",
82
+ renderRichly: true,
83
+ });
84
+ });
85
+
86
+ test("carries a named thread as the target's thread", async () => {
87
+ await runSend([
88
+ "slack",
89
+ "C0123456789",
90
+ "--thread",
91
+ "1700000000.000001",
92
+ "--text",
93
+ "and the smoke tests passed",
94
+ ]);
95
+ expect(sentBody().target).toEqual({
96
+ kind: "chat",
97
+ chatId: "C0123456789",
98
+ threadId: "1700000000.000001",
99
+ });
100
+ });
101
+
102
+ test("--plain asks for the text verbatim", async () => {
103
+ await runSend(["telegram", "123456789", "--text", "morning", "--plain"]);
104
+ const body = sentBody();
105
+ expect(body.renderRichly).toBeUndefined();
106
+ expect(body.channel).toBe("telegram");
107
+ });
108
+
109
+ test("sends the text as the caller wrote it, whitespace included", async () => {
110
+ await runSend([
111
+ "slack",
112
+ "C0123456789",
113
+ "--text",
114
+ " indented ",
115
+ "--plain",
116
+ ]);
117
+ expect(sentBody().text).toBe(" indented ");
118
+ });
119
+
120
+ test("waits longer than the IPC default, since the send itself retries", async () => {
121
+ await runSend(["slack", "C0123456789", "--text", "hi"]);
122
+ expect(ipcCalls[0]!.opts).toMatchObject({ timeoutMs: 120_000 });
123
+ });
124
+
125
+ test("refuses an empty text before it sends anything", async () => {
126
+ for (const args of [
127
+ ["slack", "C0123456789"],
128
+ ["slack", "C0123456789", "--text", " "],
129
+ ]) {
130
+ ipcCalls.length = 0;
131
+ const r = await runSend(args);
132
+ expect(r.exitCode).toBe(1);
133
+ expect(r.stderr).toContain("--text is required");
134
+ expect(ipcCalls).toEqual([]);
135
+ }
136
+ });
137
+ });
138
+
139
+ describe("what the command reports", () => {
140
+ test("names where the post landed and the conversation it was recorded in", async () => {
141
+ const r = await runSend(["slack", "C0123456789", "--text", "hello"]);
142
+ expect(r.stdout).toContain("C0123456789");
143
+ expect(r.stdout).toContain("1700000000.000100");
144
+ expect(r.stdout).toContain("conv-home");
145
+ });
146
+
147
+ test("says so when the channel split the text", async () => {
148
+ ipcResult = {
149
+ ok: true,
150
+ result: {
151
+ channel: "telegram",
152
+ chatId: "123456789",
153
+ messageIds: ["10", "11", "12"],
154
+ lastMessageId: "12",
155
+ },
156
+ };
157
+ const r = await runSend(["telegram", "123456789", "--text", "a long one"]);
158
+ expect(r.stdout).toContain("3 posts");
159
+ });
160
+
161
+ test("--json answers with the route's own result", async () => {
162
+ const r = await runSend([
163
+ "slack",
164
+ "C0123456789",
165
+ "--text",
166
+ "hello",
167
+ "--json",
168
+ ]);
169
+ expect(JSON.parse(r.stdout)).toEqual({
170
+ channel: "slack",
171
+ chatId: "C0123456789",
172
+ messageIds: ["1700000000.000100"],
173
+ lastMessageId: "1700000000.000100",
174
+ recordedIn: "conv-home",
175
+ });
176
+ });
177
+
178
+ test("a refused send reports the route's reason and its exit code", async () => {
179
+ ipcResult = {
180
+ ok: false,
181
+ error: 'Channel "email" is not addressable',
182
+ statusCode: 400,
183
+ };
184
+ const r = await runSend(["email", "someone", "--text", "hello"]);
185
+ expect(r.exitCode).toBe(
186
+ actualCliClient.exitCodeFromIpcResult({ statusCode: 400 }),
187
+ );
188
+ expect(r.stderr).toContain("not addressable");
189
+ });
190
+
191
+ test("a timeout is reported as an unknown outcome, not a failure to retry", async () => {
192
+ ipcResult = { ok: false, error: "Request timed out", timedOut: true };
193
+ const r = await runSend(["slack", "C0123456789", "--text", "hello"]);
194
+ expect(r.stderr).toContain("may still be in flight");
195
+ expect(r.stderr).toContain("before sending again");
196
+ expect(r.stderr).not.toContain("The send failed");
197
+ });
198
+
199
+ test("a refusal in --json mode is a JSON error envelope", async () => {
200
+ ipcResult = {
201
+ ok: false,
202
+ error: 'Channel "slack" did not acknowledge the send',
203
+ statusCode: 502,
204
+ };
205
+ const r = await runSend([
206
+ "slack",
207
+ "C0123456789",
208
+ "--text",
209
+ "hello",
210
+ "--json",
211
+ ]);
212
+ expect(JSON.parse(r.stdout)).toEqual({
213
+ ok: false,
214
+ error: 'Channel "slack" did not acknowledge the send',
215
+ });
216
+ expect(r.exitCode).toBe(3);
217
+ });
218
+ });
@@ -36,9 +36,14 @@ assistant's own bot on that channel (${BOT_CHANNELS}), resolving the bot
36
36
  credential from the channel id so no token is handled. Acting as a person
37
37
  through their OAuth integration is 'assistant oauth request'.
38
38
 
39
+ To say something in a chat, use 'send' rather than 'request': it goes
40
+ through the channel's transport, so the post is threaded and rendered like
41
+ every other message the assistant sends there, and it is recorded.
42
+
39
43
  Examples:
40
44
  $ assistant channels list
41
45
  $ assistant channels get slack
46
+ $ assistant channels send slack C0123456789 --text 'deploy is green'
42
47
  $ assistant channels request slack /auth.test --json
43
48
  $ assistant channels request slack -X POST \\
44
49
  -d '{"channel":"D0123456789","limit":20}' /conversations.history --json`,
@@ -65,8 +70,9 @@ This command can do anything the bot's API allows, including sending,
65
70
  editing, deleting, uploading, and reacting, so it is classified high risk
66
71
  and asks for approval like any action with irreversible effects. Reads are
67
72
  not distinguished from writes: the effect is the endpoint's. To send a
68
- message, use the messaging tool, which records what it sent; do not post
69
- through this command.
73
+ message, use 'assistant channels send', which posts through the channel's
74
+ transport and records what it sent; a message posted through this command
75
+ reaches the platform directly and leaves no record of what was said.
70
76
 
71
77
  Arguments:
72
78
  <channel> One of: ${BOT_CHANNELS}. A channel with no bot credential of
@@ -86,6 +92,68 @@ Examples:
86
92
  $ assistant channels request slack -X POST \\
87
93
  -d '{"channel":"D0123456789","limit":100}' /conversations.history --json
88
94
  $ assistant channels request discord /users/@me --json`,
95
+ },
96
+ {
97
+ name: "send",
98
+ description:
99
+ "Post text to a chat on the channel, as the assistant's bot, recorded",
100
+ arguments: [
101
+ {
102
+ name: "<channel>",
103
+ description: `Channel to post on. One with a transport that can be addressed from a named chat; anything else is refused before the send.`,
104
+ },
105
+ {
106
+ name: "<chat-id>",
107
+ description:
108
+ "The chat in the channel's own id space (a Slack channel or DM id, a Telegram chat id, a Discord channel id, a WhatsApp number).",
109
+ },
110
+ ],
111
+ options: [
112
+ {
113
+ flags: "--text <text>",
114
+ description: "The message text. Required.",
115
+ },
116
+ {
117
+ flags: "--thread <id>",
118
+ description:
119
+ "Post inside this thread or topic, in the channel's own id space. Omit to post to the chat itself.",
120
+ },
121
+ {
122
+ flags: "--plain",
123
+ description:
124
+ "Send the text verbatim instead of the channel's rich rendering.",
125
+ },
126
+ {
127
+ flags: "--json",
128
+ description: "Machine-readable compact JSON output",
129
+ },
130
+ ],
131
+ helpText: `
132
+ Posts one message through the channel's own transport, the same path a
133
+ reply takes. Once the channel acknowledges it, the daemon records it in
134
+ the chat's conversation, which is what lets the assistant see later what
135
+ it said here and lets a reaction or an edit naming that post resolve back
136
+ to it. Recording is best effort and never fails a send that already went
137
+ out, so the result names the conversation only when the record was
138
+ written.
139
+
140
+ This sends a message people will read, so it is classified high risk and
141
+ asks for approval like any other action with effects nobody can take back.
142
+
143
+ The send fails, and records nothing, when the channel does not acknowledge
144
+ it or names no message id for it. A channel the assistant cannot address
145
+ from a named chat is refused before anything is sent. If the daemon does
146
+ not answer in time, the message may still go out: that is reported as an
147
+ unknown outcome rather than a failure, so check the chat before sending
148
+ again.
149
+
150
+ Attachments are not sent from here.
151
+
152
+ Examples:
153
+ $ assistant channels send slack C0123456789 --text 'deploy is green'
154
+ $ assistant channels send slack C0123456789 --thread 1700000000.000100 \
155
+ --text 'and the smoke tests passed'
156
+ $ assistant channels send telegram 123456789 --text 'morning' --json`,
89
157
  },
90
158
  {
91
159
  name: "list",
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * `assistant channels`: inspect messaging channels, and act as their bots.
3
3
  *
4
- * list : overview of every channel + ready state
5
- * get <channel> : detailed live snapshot of a single channel
6
- * request <channel> <url> : call the channel's platform API as its bot
4
+ * list : overview of every channel + ready state
5
+ * get <channel> : detailed live snapshot of a single channel
6
+ * request <channel> <url> : call the channel's platform API as its bot
7
+ * send <channel> <chat-id> : post text to a chat, recorded
7
8
  *
8
9
  * `get` always re-runs remote probes (it invalidates the readiness cache
9
10
  * before reading), so the CLI answer matches the live source-of-truth.
@@ -12,6 +13,11 @@
12
13
  * channel's bot credential and makes the same authenticated request
13
14
  * `oauth request` makes, without the caller naming a provider.
14
15
  *
16
+ * `send` is the send door (see ./send.ts): it runs the daemon's one send
17
+ * implementation for the channel, so the post is threaded, rendered, and
18
+ * recorded like every other message the assistant sends there. Posting
19
+ * through `request` instead reaches the platform API and leaves no record.
20
+ *
15
21
  * A mutating `refresh` verb (for reconnecting channels, e.g. supplying
16
22
  * fresh Slack tokens) is intentionally not part of this group.
17
23
  */
@@ -26,6 +32,7 @@ import { log } from "../../logger.js";
26
32
  import { shouldOutputJson, writeOutput } from "../../output.js";
27
33
  import { CHANNELS_PLUGIN_SEARCH_HINT, channelsHelp } from "./index.help.js";
28
34
  import { registerChannelsRequestCommand } from "./request.js";
35
+ import { registerChannelsSendCommand } from "./send.js";
29
36
 
30
37
  // ---------------------------------------------------------------------------
31
38
  // Snapshot shape
@@ -211,6 +218,12 @@ export function registerChannelsCommand(program: Command): void {
211
218
  // -----------------------------------------------------------------------
212
219
 
213
220
  registerChannelsRequestCommand(channels);
221
+
222
+ // -----------------------------------------------------------------------
223
+ // send: post text to a chat, through the channel's transport
224
+ // -----------------------------------------------------------------------
225
+
226
+ registerChannelsSendCommand(channels);
214
227
  },
215
228
  });
216
229
  }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * `assistant channels send`: post text to a chat on a channel, as the
3
+ * assistant's own bot.
4
+ *
5
+ * The send door. It runs the daemon function the messaging tool runs
6
+ * (`sendChannelText`, over `POST /v1/channels/send`), so a send from the
7
+ * command line reaches the chat through the channel's transport and leaves
8
+ * the same record: threaded where a thread is named, rendered the way the
9
+ * channel renders a reply, and written to the chat's conversation only once
10
+ * the channel has acknowledged it.
11
+ *
12
+ * Posting the same text through `channels request` reaches the platform API
13
+ * directly and leaves no record of what was said, which is why this command
14
+ * exists beside it. Both act as the bot and both carry the same rating: the
15
+ * effect is a message either way.
16
+ *
17
+ * A channel whose transport cannot be addressed from a named chat is
18
+ * refused by the route before anything is sent.
19
+ */
20
+
21
+ import type { Command } from "commander";
22
+
23
+ import { cliIpcCall, exitCodeFromIpcResult } from "../../../ipc/cli-client.js";
24
+ import { subcommand } from "../../lib/cli-command-help.js";
25
+ import { log } from "../../logger.js";
26
+ import { shouldOutputJson, writeError, writeOutput } from "../../output.js";
27
+
28
+ /** What the channel acknowledged, and where the post was recorded. */
29
+ interface ChannelSendResponse {
30
+ channel: string;
31
+ chatId: string;
32
+ threadId?: string;
33
+ messageIds: string[];
34
+ lastMessageId: string;
35
+ recordedIn?: string;
36
+ }
37
+
38
+ interface ChannelSendOptions {
39
+ text?: string;
40
+ thread?: string;
41
+ plain?: boolean;
42
+ }
43
+
44
+ /**
45
+ * How long to wait for the daemon before giving up on an answer.
46
+ *
47
+ * Longer than the IPC default because the work behind this call is a
48
+ * platform request that retries: a rate-limited Slack send sleeps for the
49
+ * `Retry-After` Slack names and tries again up to three times
50
+ * (`MAX_RATE_LIMIT_RETRIES`, providers/slack/web-api-transport.ts), and the
51
+ * other channels back off similarly. Waiting past that is better than
52
+ * abandoning a send that is still being made, because giving up tells the
53
+ * caller nothing about whether the message went out.
54
+ */
55
+ const SEND_CALL_TIMEOUT_MS = 120_000;
56
+
57
+ export function registerChannelsSendCommand(channels: Command): void {
58
+ subcommand(channels, "send").action(
59
+ async (
60
+ channel: string,
61
+ chatId: string,
62
+ opts: ChannelSendOptions,
63
+ cmd: Command,
64
+ ) => {
65
+ // Trimmed only to reject a blank message: the text goes out as the
66
+ // caller wrote it, which `--plain` in particular promises.
67
+ const text = opts.text;
68
+ if (!text?.trim()) {
69
+ writeError(cmd, "--text is required and cannot be empty.");
70
+ process.exitCode = 1;
71
+ return;
72
+ }
73
+
74
+ const threadId = opts.thread?.trim();
75
+ const r = await cliIpcCall<ChannelSendResponse>(
76
+ "channels_send_post",
77
+ {
78
+ body: {
79
+ channel,
80
+ target: {
81
+ kind: "chat",
82
+ chatId,
83
+ ...(threadId ? { threadId } : {}),
84
+ },
85
+ text,
86
+ // Rich unless asked otherwise, so a send from here reads like
87
+ // the assistant's other posts in that chat rather than like a
88
+ // different sender.
89
+ ...(opts.plain ? {} : { renderRichly: true }),
90
+ },
91
+ },
92
+ { timeoutMs: SEND_CALL_TIMEOUT_MS },
93
+ );
94
+ if (!r.ok) {
95
+ // A timeout is not a failure: closing the client socket does not
96
+ // stop the daemon, so the message may still go out. Saying so is
97
+ // what keeps a retry from posting it twice.
98
+ const message = r.timedOut
99
+ ? `The daemon did not answer within ${Math.round(SEND_CALL_TIMEOUT_MS / 1000)}s. The send may still be in flight, so it may or may not have gone out. Check the chat before sending again.`
100
+ : (r.error ?? "The send failed.");
101
+ // Reported rather than exited, so `--json` gets an error envelope
102
+ // like every other answer this command gives.
103
+ writeError(cmd, message);
104
+ process.exitCode = exitCodeFromIpcResult({
105
+ statusCode: r.statusCode,
106
+ });
107
+ return;
108
+ }
109
+
110
+ const sent = r.result!;
111
+ if (shouldOutputJson(cmd)) {
112
+ writeOutput(cmd, sent);
113
+ return;
114
+ }
115
+
116
+ const where = sent.threadId
117
+ ? `${sent.chatId}, thread ${sent.threadId}`
118
+ : sent.chatId;
119
+ log.info(`Sent to ${channel} ${where} (id: ${sent.lastMessageId})`);
120
+ if (sent.messageIds.length > 1) {
121
+ log.info(`The channel split it into ${sent.messageIds.length} posts`);
122
+ }
123
+ if (sent.recordedIn) {
124
+ log.info(`Recorded in conversation ${sent.recordedIn}`);
125
+ }
126
+ },
127
+ );
128
+ }
@@ -45,8 +45,12 @@ Examples:
45
45
  },
46
46
  ],
47
47
  helpText: `
48
- Lists all credentials in the vault. Each entry includes the same fields as
49
- "inspect" scrubbed value, timestamps, policy, and metadata.
48
+ Lists credentials from the live credential vault. Each entry includes the same
49
+ fields as inspect: scrubbed value, timestamps, policy, and metadata.
50
+
51
+ Fails immediately with an error if the credential vault is unreachable. An
52
+ empty list means the vault answered and has no matching credentials, not that
53
+ the store is down.
50
54
 
51
55
  The --search flag filters results by case-insensitive substring match against
52
56
  the credential's service name, field name, label, or description. For example, --search
@@ -343,8 +343,8 @@ export function registerCredentialsCommand(program: Command): void {
343
343
  } else {
344
344
  printCredentialHuman(output);
345
345
  if (output.brokerUnreachable) {
346
- log.info(
347
- " Credential store is unreachable ensure the assistant is running",
346
+ log.warn(
347
+ " Credential store is unreachable. Retry in a moment, or check that the credential vault is running.",
348
348
  );
349
349
  }
350
350
  }
@@ -7,8 +7,8 @@ export const mcpHelp: CliCommandHelp = {
7
7
  description: "Manage MCP (Model Context Protocol) servers",
8
8
  helpText: `
9
9
  MCP servers extend the assistant's capabilities with external tools. Servers
10
- are configured in the assistant's config.json under the mcp.servers key, or
11
- declared by an installed plugin in its root mcp.json. Each
10
+ are configured in the assistant's mcp.json, or declared by an installed
11
+ plugin in its root mcp.json. Each
12
12
  server uses one of three transport types:
13
13
 
14
14
  stdio Local process communicating over stdin/stdout
@@ -30,8 +30,8 @@ Examples:
30
30
  options: [{ flags: "--json", description: "Output as JSON" }],
31
31
  helpText: `
32
32
  Shows each MCP server with its current status and configuration. Servers come
33
- from two places: the mcp.servers key in config.json, and the root mcp.json of
34
- any installed plugin that declares one.
33
+ from two places: the workspace mcp.json, and the root mcp.json of any
34
+ installed plugin that declares one.
35
35
 
36
36
  Name The server identifier
37
37
  Status Health check result for workspace servers:
@@ -43,7 +43,7 @@ any installed plugin that declares one.
43
43
  health-checked, so no stored credential can reach a URL a
44
44
  plugin chose.
45
45
  Source Shown only for plugin-declared servers, naming the plugin.
46
- Servers from config.json print no Source line.
46
+ Servers from workspace mcp.json print no Source line.
47
47
  Transport stdio, sse, or streamable-http
48
48
  URL/Command The server URL (sse/streamable-http) or command (stdio)
49
49
 
@@ -64,7 +64,7 @@ on their next turn automatically. The assistant must be running.
64
64
 
65
65
  Examples:
66
66
  $ vellum mcp reload
67
- $ vellum mcp reload # after editing config.json to add a new server
67
+ $ vellum mcp reload # after editing mcp.json to add a new server
68
68
  $ vellum mcp reload # after running "vellum mcp auth <server>"`,
69
69
  },
70
70
  {
@@ -95,7 +95,7 @@ Examples:
95
95
  ],
96
96
  helpText: `
97
97
  Arguments:
98
- name Unique identifier for the server (used as the key in config.json)
98
+ name Unique identifier for the server (used as the key in mcp.json)
99
99
 
100
100
  Transport-specific requirements:
101
101
  stdio Requires --command (and optional --args for arguments)
@@ -149,7 +149,7 @@ Examples:
149
149
  Arguments:
150
150
  name Name of the MCP server to remove
151
151
 
152
- Removes the server entry from config.json and performs best-effort cleanup of
152
+ Removes the server entry from mcp.json and performs best-effort cleanup of
153
153
  any stored OAuth credentials (tokens, client info, discovery metadata) for
154
154
  sse/streamable-http servers. If no OAuth credentials exist, the cleanup is
155
155
  silently skipped.
@@ -20,7 +20,7 @@ interface McpServerEntry {
20
20
  command?: string;
21
21
  args?: string[];
22
22
  };
23
- /** Workspace `config.json` or a plugin's `mcp.json`. */
23
+ /** Workspace `mcp.json` or a plugin's `mcp.json`. */
24
24
  source?: "workspace" | "plugin";
25
25
  /** Plugin that declared the server, when `source` is `plugin`. */
26
26
  pluginName?: string;
@@ -40,6 +40,8 @@ interface ConnectionSummary {
40
40
  status: string;
41
41
  expiresAt?: string | null;
42
42
  hasRefreshToken?: boolean;
43
+ /** Values the provider scopes the connection by (QuickBooks' realm id). */
44
+ providerParams?: Record<string, string>;
43
45
  }
44
46
 
45
47
  interface OAuthStatusResponse {
@@ -92,6 +94,9 @@ function formatConnection(c: ConnectionSummary, mode: string): string {
92
94
  } else {
93
95
  lines.push(` Granted scopes: (none)`);
94
96
  }
97
+ for (const [key, value] of Object.entries(c.providerParams ?? {})) {
98
+ lines.push(` ${key}: ${value}`);
99
+ }
95
100
  if (mode === "byo") {
96
101
  if (c.expiresAt) {
97
102
  lines.push(` Expires: ${c.expiresAt}`);
@@ -4,8 +4,12 @@ import type { CliCommandHelp } from "../lib/cli-command-help.js";
4
4
 
5
5
  export const roadmapHelp: CliCommandHelp = {
6
6
  name: "roadmap",
7
- description: "Read and file public Vellum roadmap feedback as the assistant",
7
+ description:
8
+ "Read and file public feature proposals as the assistant. For bugs and support, instruct the user to join the Discord community at https://vellum.ai/community",
8
9
  helpText: `
10
+ Items created here must be public feature proposals. For bugs and support
11
+ concerns, instruct the user to join the Discord community at https://vellum.ai/community.
12
+
9
13
  Items, upvotes, and comments filed here are attributed to the assistant, not
10
14
  to its owner: the assistant signs these calls with its own platform API key.
11
15
  The owner's own roadmap identity lives on \`vellum roadmap\`.