@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-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 (439) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/docs/guardian-request-flow.md +0 -16
  5. package/openapi.yaml +49 -291
  6. package/package.json +1 -1
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  8. package/src/__tests__/app-compiler.test.ts +0 -6
  9. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  10. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  11. package/src/__tests__/app-source-watcher.test.ts +218 -0
  12. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  13. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  14. package/src/__tests__/computer-use-tools.test.ts +5 -17
  15. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  16. package/src/__tests__/config-schema.test.ts +29 -28
  17. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  18. package/src/__tests__/config-watcher.test.ts +8 -23
  19. package/src/__tests__/connection-model-compat.test.ts +2 -2
  20. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  21. package/src/__tests__/conversation-pairing.test.ts +0 -26
  22. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  23. package/src/__tests__/conversation-queue.test.ts +207 -48
  24. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  25. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  26. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  27. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  28. package/src/__tests__/conversation-title-service.test.ts +10 -14
  29. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  30. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  31. package/src/__tests__/credential-record-retire.test.ts +1 -171
  32. package/src/__tests__/credential-routes.test.ts +3 -212
  33. package/src/__tests__/credential-security-invariants.test.ts +2 -4
  34. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  35. package/src/__tests__/document-sync-tags.test.ts +0 -25
  36. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  37. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  38. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  39. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  40. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  42. package/src/__tests__/host-file-proxy.test.ts +23 -11
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  45. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  47. package/src/__tests__/mcp-health-check.test.ts +11 -8
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  49. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  51. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  52. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  53. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  54. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  55. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  56. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  57. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  58. package/src/__tests__/model-intents.test.ts +0 -3
  59. package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
  60. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  61. package/src/__tests__/platform-callback-registration.test.ts +16 -8
  62. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  63. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  64. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  66. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  67. package/src/__tests__/speaker-identification.test.ts +56 -0
  68. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  69. package/src/__tests__/subagent-tools.test.ts +2 -3
  70. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  71. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  72. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  73. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  74. package/src/__tests__/workspace-git-service.test.ts +0 -26
  75. package/src/acp/__tests__/agent-process.test.ts +4 -258
  76. package/src/acp/__tests__/auth-required.test.ts +0 -96
  77. package/src/acp/__tests__/client-handler.test.ts +2 -90
  78. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  79. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  80. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  81. package/src/acp/__tests__/session-manager.test.ts +4 -895
  82. package/src/acp/agent-process.test.ts +1 -4
  83. package/src/acp/agent-process.ts +9 -94
  84. package/src/acp/auth-required.ts +1 -50
  85. package/src/acp/auto-install.test.ts +7 -9
  86. package/src/acp/client-handler.ts +3 -16
  87. package/src/acp/resolve-agent.test.ts +6 -220
  88. package/src/acp/resolve-agent.ts +23 -88
  89. package/src/acp/session-manager.test.ts +3 -196
  90. package/src/acp/session-manager.ts +18 -441
  91. package/src/acp/types.ts +5 -50
  92. package/src/api/events/app-files-changed.ts +3 -4
  93. package/src/api/events/notification-intent.ts +0 -2
  94. package/src/api/index.ts +0 -6
  95. package/src/apps/app-store.ts +1 -1
  96. package/src/bundler/app-compiler.ts +0 -15
  97. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  98. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  99. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  100. package/src/calls/call-constants.ts +4 -24
  101. package/src/calls/call-controller.ts +199 -499
  102. package/src/calls/call-transport.ts +0 -16
  103. package/src/calls/media-stream-output.ts +0 -32
  104. package/src/calls/media-stream-server.ts +30 -82
  105. package/src/calls/media-stream-stt-session.ts +0 -15
  106. package/src/calls/resolve-call-tts-provider.ts +2 -33
  107. package/src/calls/speaker-identification.ts +233 -0
  108. package/src/calls/tts-call-strategy.ts +5 -9
  109. package/src/calls/types.ts +0 -2
  110. package/src/calls/voice-control-protocol.ts +21 -53
  111. package/src/calls/voice-session-bridge.ts +46 -59
  112. package/src/calls/voice-triage-escalate.ts +56 -175
  113. package/src/cli/commands/apps.help.ts +13 -67
  114. package/src/cli/commands/apps.ts +0 -183
  115. package/src/cli/commands/bash.help.ts +13 -8
  116. package/src/cli/commands/bash.ts +25 -7
  117. package/src/cli/commands/channels/index.help.ts +2 -70
  118. package/src/cli/commands/channels/index.ts +3 -16
  119. package/src/cli/commands/credentials.help.ts +2 -6
  120. package/src/cli/commands/credentials.ts +2 -2
  121. package/src/cli/commands/mcp.help.ts +8 -8
  122. package/src/cli/commands/mcp.ts +1 -1
  123. package/src/cli/commands/roadmap.help.ts +1 -5
  124. package/src/cli/commands/roadmap.ts +1 -1
  125. package/src/cli/lib/bundled-marketplace.json +90 -103
  126. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  127. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  128. package/src/config/__tests__/platform-rehydration.test.ts +9 -99
  129. package/src/config/acp-defaults.test.ts +3 -13
  130. package/src/config/acp-defaults.ts +6 -23
  131. package/src/config/acp-schema.ts +2 -30
  132. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  133. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  134. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  135. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  136. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  137. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  138. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  139. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  140. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  141. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  142. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  143. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  144. package/src/config/default-profile-catalog.ts +1 -1
  145. package/src/config/env.ts +2 -2
  146. package/src/config/feature-flag-registry.json +12 -24
  147. package/src/config/loader.ts +0 -1
  148. package/src/config/memory-v3-gate.test.ts +0 -31
  149. package/src/config/memory-v3-gate.ts +16 -20
  150. package/src/config/platform-rehydration.ts +27 -11
  151. package/src/config/schema.ts +2 -2
  152. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  153. package/src/config/schemas/calls.ts +28 -1
  154. package/src/config/schemas/live-voice.ts +173 -1
  155. package/src/config/schemas/mcp.ts +1 -1
  156. package/src/config/schemas/memory-retrospective.ts +0 -10
  157. package/src/config/schemas/services.ts +0 -8
  158. package/src/config/schemas/skills.ts +14 -0
  159. package/src/credential-execution/process-manager.test.ts +19 -18
  160. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  161. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  162. package/src/daemon/app-source-watcher.ts +218 -0
  163. package/src/daemon/config-watcher.ts +194 -13
  164. package/src/daemon/conversation-process.ts +64 -35
  165. package/src/daemon/conversation-skill-tools.ts +31 -13
  166. package/src/daemon/conversation-store.ts +1 -1
  167. package/src/daemon/conversation-surfaces.ts +8 -10
  168. package/src/daemon/conversation-tool-setup.ts +43 -37
  169. package/src/daemon/conversation.ts +3 -0
  170. package/src/daemon/host-bash-proxy.ts +12 -8
  171. package/src/daemon/host-browser-proxy.ts +1 -15
  172. package/src/daemon/host-file-proxy.ts +10 -8
  173. package/src/daemon/host-transfer-proxy.ts +18 -14
  174. package/src/daemon/lifecycle.ts +5 -0
  175. package/src/daemon/mcp-reload-service.ts +5 -5
  176. package/src/daemon/message-types/sync.ts +0 -1
  177. package/src/daemon/orphan-reaper.ts +1 -1
  178. package/src/daemon/providers-setup.ts +1 -1
  179. package/src/daemon/shutdown-handlers.ts +9 -1
  180. package/src/daemon/skill-memory-refresh.ts +0 -64
  181. package/src/daemon/tool-side-effects.ts +18 -7
  182. package/src/home/__tests__/feed-writer.test.ts +0 -22
  183. package/src/home/feed-writer.ts +2 -11
  184. package/src/hooks/types.ts +0 -33
  185. package/src/inbound/platform-callback-registration.ts +5 -4
  186. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  187. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  188. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  189. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  190. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  191. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  192. package/src/live-voice/live-voice-session.ts +693 -213
  193. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  194. package/src/live-voice/protocol.ts +2 -0
  195. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  196. package/src/mcp/effective-config.ts +3 -3
  197. package/src/mcp/mcp-header-store.ts +45 -29
  198. package/src/mcp/startup.ts +4 -8
  199. package/src/messaging/provider.ts +3 -10
  200. package/src/messaging/providers/callback-routing.ts +0 -18
  201. package/src/messaging/providers/channel-transport.ts +0 -56
  202. package/src/messaging/providers/discord/transport.ts +0 -17
  203. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  204. package/src/messaging/providers/slack/adapter.ts +44 -7
  205. package/src/messaging/providers/slack/transport.ts +0 -16
  206. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  207. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  208. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  209. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  210. package/src/messaging/providers/whatsapp/client.ts +29 -0
  211. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  212. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  213. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  214. package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
  215. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  216. package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
  217. package/src/notifications/adapters/macos.ts +0 -3
  218. package/src/notifications/assistant-reply-producer.ts +3 -8
  219. package/src/notifications/conversation-pairing.ts +16 -36
  220. package/src/notifications/delivered-post-record.ts +1 -11
  221. package/src/notifications/guardian-feed-projection.ts +5 -125
  222. package/src/notifications/home-feed-side-effect.ts +7 -55
  223. package/src/notifications/schedule-result-producer.ts +22 -66
  224. package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
  225. package/src/oauth/byo-connection.test.ts +0 -45
  226. package/src/oauth/byo-connection.ts +6 -36
  227. package/src/oauth/connection-resolver.test.ts +0 -73
  228. package/src/oauth/connection-resolver.ts +2 -95
  229. package/src/oauth/seed-providers.ts +0 -165
  230. package/src/outbound-proxy/certs.ts +5 -3
  231. package/src/permissions/checker.test.ts +6 -11
  232. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  233. package/src/persistence/conversation-crud.ts +63 -71
  234. package/src/persistence/conversation-lineage.ts +4 -12
  235. package/src/persistence/conversation-title-service.ts +26 -9
  236. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  237. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  238. package/src/persistence/message-reads.ts +0 -30
  239. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  240. package/src/persistence/schema/memory-core.ts +0 -5
  241. package/src/platform/client.test.ts +2 -4
  242. package/src/platform/client.ts +10 -2
  243. package/src/plugin-api/constants.ts +0 -2
  244. package/src/plugin-api/index.ts +0 -1
  245. package/src/plugin-api/types.ts +0 -2
  246. package/src/plugins/defaults/index.ts +0 -2
  247. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  248. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  249. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  250. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  251. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  252. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  253. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  254. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  255. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  256. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  257. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  258. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  259. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  260. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  261. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  262. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  263. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  264. package/src/plugins/defaults/memory/startup.ts +1 -6
  265. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  266. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  267. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  268. package/src/plugins/defaults/memory/worker.ts +0 -6
  269. package/src/plugins/mcp-servers.ts +75 -15
  270. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  271. package/src/providers/gemini/client.ts +23 -9
  272. package/src/providers/inference/auth.ts +3 -2
  273. package/src/providers/inference/connection-availability.ts +8 -27
  274. package/src/providers/inference/resolve-auth.ts +4 -22
  275. package/src/providers/model-catalog.ts +0 -34
  276. package/src/providers/model-intents.ts +5 -10
  277. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  278. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  279. package/src/providers/openai/chat-completions-provider.ts +78 -192
  280. package/src/providers/openai/codex-models.ts +4 -0
  281. package/src/runtime/AGENTS.md +0 -2
  282. package/src/runtime/auth/same-actor.ts +3 -38
  283. package/src/runtime/http-server.ts +151 -160
  284. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  286. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  287. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  288. package/src/runtime/routes/acp-routes.test.ts +0 -15
  289. package/src/runtime/routes/acp-routes.ts +9 -72
  290. package/src/runtime/routes/app-management-routes.ts +0 -73
  291. package/src/runtime/routes/app-routes.ts +1 -1
  292. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  293. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  294. package/src/runtime/routes/credential-routes.ts +10 -19
  295. package/src/runtime/routes/debug-bash-routes.ts +173 -0
  296. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  297. package/src/runtime/routes/index.ts +2 -4
  298. package/src/runtime/routes/log-export-routes.ts +0 -2
  299. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  300. package/src/runtime/routes/migration-routes.ts +20 -6
  301. package/src/runtime/routes/platform-routes.ts +15 -18
  302. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  303. package/src/runtime/services/conversation-serializer.ts +3 -16
  304. package/src/runtime/sync/resource-sync-events.ts +1 -2
  305. package/src/schedule/worker.ts +1 -1
  306. package/src/security/ces-rpc-record-backend.ts +0 -7
  307. package/src/subagent/consult-context.ts +25 -12
  308. package/src/tools/acp/list-agents.test.ts +1 -3
  309. package/src/tools/acp/spawn.test.ts +11 -146
  310. package/src/tools/acp/spawn.ts +3 -21
  311. package/src/tools/acp/status.ts +3 -40
  312. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  313. package/src/tools/capability-offer.test.ts +2 -22
  314. package/src/tools/capability-offer.ts +1 -16
  315. package/src/tools/computer-use/definitions.ts +2 -4
  316. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  317. package/src/tools/credentials/metadata-store.ts +0 -100
  318. package/src/tools/host-filesystem/edit.test.ts +1 -2
  319. package/src/tools/host-filesystem/edit.ts +2 -2
  320. package/src/tools/host-filesystem/read.test.ts +1 -2
  321. package/src/tools/host-filesystem/read.ts +2 -2
  322. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  323. package/src/tools/host-filesystem/transfer.ts +2 -2
  324. package/src/tools/host-filesystem/write.test.ts +1 -2
  325. package/src/tools/host-filesystem/write.ts +2 -2
  326. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  327. package/src/tools/host-terminal/host-shell.ts +20 -4
  328. package/src/tools/policy-context.ts +7 -6
  329. package/src/tools/subagent/spawn.ts +8 -5
  330. package/src/tools/watch/watch-retro-report.ts +8 -0
  331. package/src/util/__tests__/language-subtag.test.ts +1 -23
  332. package/src/util/fs-watcher-error.ts +36 -0
  333. package/src/util/language-subtag.ts +0 -16
  334. package/src/util/platform.ts +0 -8
  335. package/src/workspace/git-service.ts +54 -5
  336. package/src/workspace/migrations/registry.ts +0 -6
  337. package/scripts/smoke-desktop-window-theme.ts +0 -186
  338. package/src/__tests__/git-porcelain.test.ts +0 -47
  339. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  340. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  341. package/src/__tests__/resolve-app.test.ts +0 -135
  342. package/src/__tests__/service-account-token.test.ts +0 -247
  343. package/src/__tests__/source-fingerprint.test.ts +0 -184
  344. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  345. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  346. package/src/acp/model-config.test.ts +0 -166
  347. package/src/acp/model-config.ts +0 -138
  348. package/src/api/events/acp-session-model-update.test.ts +0 -88
  349. package/src/api/events/acp-session-model-update.ts +0 -46
  350. package/src/apps/resolve-app.ts +0 -81
  351. package/src/apps/source-fingerprint.ts +0 -266
  352. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  353. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  354. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  355. package/src/calls/barge-in-guard.ts +0 -118
  356. package/src/calls/voice-leg-coordinator.ts +0 -189
  357. package/src/calls/voice-progress-cadence.ts +0 -423
  358. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  359. package/src/cli/commands/__tests__/bash.test.ts +0 -53
  360. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  361. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  362. package/src/cli/commands/channels/send.ts +0 -128
  363. package/src/config/__tests__/platform-identity.test.ts +0 -255
  364. package/src/config/acp-agent-write.ts +0 -43
  365. package/src/config/acp-schema.test.ts +0 -81
  366. package/src/config/platform-identity.ts +0 -201
  367. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  368. package/src/config/schemas/voice.ts +0 -150
  369. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  370. package/src/daemon/app-change-notify.ts +0 -45
  371. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  372. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  373. package/src/desktop/desktop-chrome-policy.ts +0 -42
  374. package/src/desktop/desktop-dependencies.test.ts +0 -102
  375. package/src/desktop/desktop-dependencies.ts +0 -306
  376. package/src/desktop/desktop-feature.test.ts +0 -18
  377. package/src/desktop/desktop-feature.ts +0 -19
  378. package/src/desktop/desktop-panel-config.test.ts +0 -135
  379. package/src/desktop/desktop-panel-config.ts +0 -149
  380. package/src/desktop/desktop-session-manager.test.ts +0 -627
  381. package/src/desktop/desktop-session-manager.ts +0 -814
  382. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  383. package/src/desktop/desktop-stream-bridge.ts +0 -214
  384. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  385. package/src/desktop/desktop-wallpaper.ts +0 -113
  386. package/src/desktop/desktop-window-theme.ts +0 -170
  387. package/src/i18n/AGENTS.md +0 -22
  388. package/src/i18n/__tests__/i18n.test.ts +0 -108
  389. package/src/i18n/index.ts +0 -16
  390. package/src/i18n/locales.ts +0 -93
  391. package/src/i18n/messages.ts +0 -61
  392. package/src/i18n/title.ts +0 -25
  393. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  394. package/src/mcp/spec-schema.ts +0 -111
  395. package/src/mcp/workspace-mcp-config.ts +0 -188
  396. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  397. package/src/persistence/conversation-title-placeholders.ts +0 -23
  398. package/src/persistence/message-cursor.ts +0 -78
  399. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  400. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  401. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  402. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  403. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  404. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  405. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  406. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  407. package/src/providers/gemini-thought-signature.test.ts +0 -63
  408. package/src/providers/gemini-thought-signature.ts +0 -44
  409. package/src/providers/inference/service-account-token.ts +0 -248
  410. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  411. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  412. package/src/providers/openai/google-thought-signature.ts +0 -320
  413. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  414. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  415. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  416. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  417. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  418. package/src/runtime/channel-send.ts +0 -316
  419. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  420. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  421. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  422. package/src/runtime/routes/channel-send-routes.ts +0 -85
  423. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  424. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  425. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  426. package/src/security/ces-http-record-backend.ts +0 -120
  427. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  428. package/src/skills/workspace-skill-md-signature.ts +0 -50
  429. package/src/tools/acp/status.test.ts +0 -94
  430. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
  431. package/src/tools/terminal/sanitized-bash.ts +0 -105
  432. package/src/util/ca-bundle.ts +0 -13
  433. package/src/workspace/git-porcelain.ts +0 -66
  434. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  435. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  436. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  437. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  438. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  439. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -1,6 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { basename } from "node:path";
3
3
 
4
+ import { isChannelId } from "../../../../channels/types.js";
4
5
  import type { OutboundAttachment } from "../../../../messaging/provider-types.js";
5
6
  import {
6
7
  createDraft,
@@ -15,10 +16,11 @@ import {
15
16
  toOutlookFileAttachments,
16
17
  } from "../../../../messaging/providers/outlook/client.js";
17
18
  import type { OutlookDraftMessage } from "../../../../messaging/providers/outlook/types.js";
18
- import {
19
- isProactivelyAddressable,
20
- sendChannelText,
21
- } from "../../../../runtime/channel-send.js";
19
+ import { resolveProactiveHomeConversation } from "../../../../notifications/conversation-pairing.js";
20
+ import { recordDeliveredChannelPost } from "../../../../notifications/delivered-post-record.js";
21
+ import { getConversation } from "../../../../persistence/conversation-crud.js";
22
+ import { syncMessageToDisk } from "../../../../persistence/conversation-disk-view.js";
23
+ import { normalizeExternalThreadId } from "../../../../persistence/external-conversation-store.js";
22
24
  import {
23
25
  isAbortLikeError,
24
26
  throwIfCancelled,
@@ -27,6 +29,7 @@ import type {
27
29
  ToolContext,
28
30
  ToolExecutionResult,
29
31
  } from "../../../../tools/types.js";
32
+ import { getLogger } from "../../../../util/logger.js";
30
33
  import { guessMimeType } from "../../../../util/mime-type.js";
31
34
  import {
32
35
  err,
@@ -39,6 +42,8 @@ import {
39
42
  resolveProvider,
40
43
  } from "./shared.js";
41
44
 
45
+ const log = getLogger("messaging-send");
46
+
42
47
  /** Read attachment files from disk into in-memory parts for outbound sending. */
43
48
  async function readAttachments(paths: string[]): Promise<OutboundAttachment[]> {
44
49
  return Promise.all(
@@ -53,6 +58,123 @@ async function readAttachments(paths: string[]): Promise<OutboundAttachment[]> {
53
58
  /** Email providers that accept file attachments on outbound sends. */
54
59
  const ATTACHMENT_CAPABLE_PLATFORMS = new Set(["gmail", "outlook"]);
55
60
 
61
+ /**
62
+ * Record a message the provider just accepted where the chat's proactive
63
+ * posts live, so the chat's own conversation and `recall` can see what was
64
+ * sent from elsewhere (a scheduled run, another conversation).
65
+ *
66
+ * Only channel providers have such a home; an email send is tool-mediated
67
+ * and has no chat conversation. The row is written after the provider
68
+ * returned the message id, never before, and carries that id on its
69
+ * envelope and in `channel_outbound_posts` like every other post the daemon
70
+ * makes.
71
+ *
72
+ * A send into the sending turn's own chat writes no row: its tool call and
73
+ * result already sit in that conversation's history, and a second assistant
74
+ * row beside the tool pair would break history repair. Whether the send is
75
+ * "its own" is decided by the delivered target against the turn, not by the
76
+ * home: the turn arrived on a channel, in a chat, in a thread (the turn-local
77
+ * snapshot on the tool context), and a post the provider delivered to exactly
78
+ * that place is a same-conversation send even when the chat's home resolves
79
+ * elsewhere. The delivered thread is the one the provider reports, not the
80
+ * one requested: a provider that ignores the request lands the post in the
81
+ * thread-less chat, and the record must say so; a post into a thread is
82
+ * recorded in the thread's own conversation, where its replies arrive. The home
83
+ * comparison stays as the second test, for a sender that arrived through no
84
+ * channel but is the home. The post is then in the outbound index only
85
+ * through no path, which is the same class as a raw API send and is deferred
86
+ * with it.
87
+ *
88
+ * Failures here never fail the send: the message is already out.
89
+ */
90
+ async function recordSentChannelPost(params: {
91
+ providerId: string;
92
+ externalChatId: string;
93
+ /** The thread the provider delivered into, as it reported it. */
94
+ deliveredThreadId: string | undefined;
95
+ text: string;
96
+ providerMessageId: string;
97
+ sender: ToolContext;
98
+ }): Promise<void> {
99
+ const { providerId, externalChatId, sender } = params;
100
+ if (!isChannelId(providerId) || !params.providerMessageId) {
101
+ return;
102
+ }
103
+ try {
104
+ if (
105
+ isSendIntoOwnChat(
106
+ sender,
107
+ providerId,
108
+ externalChatId,
109
+ params.deliveredThreadId,
110
+ )
111
+ ) {
112
+ return;
113
+ }
114
+ const home = await resolveProactiveHomeConversation({
115
+ sourceChannel: providerId,
116
+ externalChatId,
117
+ threadId: params.deliveredThreadId,
118
+ source: "notification",
119
+ conversationType: "background",
120
+ title: `Messages to ${externalChatId}`,
121
+ });
122
+ if (home.conversationId === sender.conversationId) {
123
+ return;
124
+ }
125
+ const recorded = await recordDeliveredChannelPost({
126
+ conversationId: home.conversationId,
127
+ channel: providerId,
128
+ externalChatId,
129
+ ...(params.deliveredThreadId
130
+ ? { threadId: params.deliveredThreadId }
131
+ : {}),
132
+ text: params.text,
133
+ providerMessageId: params.providerMessageId,
134
+ crossPostedFrom: sender.conversationId,
135
+ });
136
+ const homeConversation = getConversation(home.conversationId);
137
+ if (homeConversation) {
138
+ syncMessageToDisk(
139
+ home.conversationId,
140
+ recorded.messageId,
141
+ homeConversation.createdAt,
142
+ );
143
+ }
144
+ } catch (e) {
145
+ log.warn(
146
+ { err: e, provider: providerId, externalChatId },
147
+ "Failed to record the sent message in the chat's conversation",
148
+ );
149
+ }
150
+ }
151
+
152
+ /**
153
+ * True when the delivered chat and thread are the ones the sending turn
154
+ * arrived in. Reads the turn-local snapshot the tool context carries
155
+ * (`executionChannel`, `requesterChatId`, `sourceThreadId`), never the live
156
+ * binding, which a concurrent inbound can rewrite while this call runs.
157
+ * Thread ids compare in the binding store's normalized form, so an absent
158
+ * thread on both sides matches and a thread-less delivery never matches a
159
+ * turn that arrived in a thread.
160
+ */
161
+ function isSendIntoOwnChat(
162
+ sender: ToolContext,
163
+ channel: string,
164
+ externalChatId: string,
165
+ deliveredThreadId: string | undefined,
166
+ ): boolean {
167
+ if (!sender.executionChannel || !sender.requesterChatId) {
168
+ return false;
169
+ }
170
+ return (
171
+ sender.executionChannel === channel &&
172
+ sender.requesterChatId === externalChatId &&
173
+ normalizeExternalThreadId(sender.sourceThreadId) ===
174
+ normalizeExternalThreadId(deliveredThreadId)
175
+ );
176
+ }
177
+
56
178
  export async function run(
57
179
  input: Record<string, unknown>,
58
180
  context: ToolContext,
@@ -75,62 +197,9 @@ export async function run(
75
197
  throwIfCancelled(context);
76
198
 
77
199
  try {
78
- // A channel whose transport can be addressed from a named chat is sent
79
- // through the transport, which is the one send implementation for that
80
- // channel, records what it sent, and reaches channels with no messaging
81
- // provider at all. A platform named outright is checked first; one
82
- // auto-detected from the connected providers is checked the same way.
83
- const namedChannel =
84
- platform && isProactivelyAddressable(platform) ? platform : undefined;
85
- const provider = namedChannel ? undefined : await resolveProvider(platform);
86
- const channel =
87
- namedChannel ??
88
- (provider && isProactivelyAddressable(provider.id)
89
- ? provider.id
90
- : undefined);
91
-
92
- if (channel) {
93
- if (attachmentPaths?.length) {
94
- return err("Attachments are only supported on Gmail and Outlook.");
95
- }
96
- // A channel's transport sends as the channel's one bot identity, so an
97
- // account cannot select where the send goes out from. Refusing is
98
- // safer than silently sending from the default account.
99
- if (input.account) {
100
- return err(
101
- `account does not apply to ${channel}: a channel send goes out as the channel's own bot.`,
102
- );
103
- }
104
- // Recheck: provider resolution above is an await, and this posts to
105
- // the channel.
106
- throwIfCancelled(context);
107
- const sent = await sendChannelText({
108
- channel,
109
- target: {
110
- kind: "chat",
111
- chatId: conversationId,
112
- ...(threadId ? { threadId } : {}),
113
- },
114
- text,
115
- renderRichly: true,
116
- assistantId: context.assistantId,
117
- sender: {
118
- conversationId: context.conversationId,
119
- executionChannel: context.executionChannel,
120
- requesterChatId: context.requesterChatId,
121
- sourceThreadId: context.sourceThreadId,
122
- },
123
- });
124
- const threadSuffix = sent.threadId
125
- ? `, "thread_id": "${sent.threadId}"`
126
- : "";
127
- return ok(`Message sent (ID: ${sent.lastMessageId}${threadSuffix}).`);
128
- }
129
- if (!provider) {
130
- throw new Error(`Messaging provider "${platform}" not found.`);
131
- }
200
+ const provider = await resolveProvider(platform);
132
201
 
133
- // Reject attachments on platforms that can't carry them.
202
+ // Reject attachments on platforms that can't carry them (e.g. Telegram, WhatsApp).
134
203
  if (
135
204
  attachmentPaths?.length &&
136
205
  !ATTACHMENT_CAPABLE_PLATFORMS.has(provider.id)
@@ -357,14 +426,7 @@ export async function run(
357
426
  );
358
427
  }
359
428
 
360
- // A provider with a send of its own and no direct transport (a plugin's,
361
- // for instance). Its send is tool-mediated and has no chat conversation
362
- // to record in.
363
- if (!provider.sendMessage) {
364
- return err(
365
- `${provider.displayName} cannot send from here: it has no send of its own and no channel transport to address.`,
366
- );
367
- }
429
+ // Non-email platforms
368
430
  const attachments = attachmentPaths?.length
369
431
  ? await readAttachments(attachmentPaths)
370
432
  : undefined;
@@ -380,6 +442,16 @@ export async function run(
380
442
  const threadSuffix = result.threadId
381
443
  ? `, "thread_id": "${result.threadId}"`
382
444
  : "";
445
+
446
+ await recordSentChannelPost({
447
+ providerId: provider.id,
448
+ externalChatId: conversationId,
449
+ deliveredThreadId: result.threadId,
450
+ text,
451
+ providerMessageId: result.id,
452
+ sender: context,
453
+ });
454
+
383
455
  return ok(`Message sent (ID: ${result.id}${threadSuffix}).`);
384
456
  } catch (e) {
385
457
  // A cancelled turn is not a send failure: let it reach the executor's
@@ -23,7 +23,7 @@ sqlite3 "$VELLUM_WORKSPACE_DIR/data/db/assistant.db" \
23
23
 
24
24
  This returns all messages in chronological order with:
25
25
 
26
- - `role: "user"` - caller speech and system events
26
+ - `role: "user"` - caller speech (prefixed with `[SPEAKER]` tags) and system events
27
27
  - `role: "assistant"` - assistant responses, including `text` content and any `tool_use`/`tool_result` blocks
28
28
 
29
29
  ## Quick one-liner for the most recent call
@@ -209,6 +209,7 @@ When you mention a schedule or a conversation it owns in Vellum chat, write a ma
209
209
 
210
210
  Use these paths only in Vellum chat. Do not use them in Slack, Telegram, Discord, email, or notifications. Those surfaces cannot navigate `/assistant/...` routes.
211
211
 
212
+
212
213
  ### Anchored & Ambiguous Relative Time
213
214
 
214
215
  Phrases like "at the 45 minute mark", "at the top of the hour", "at noon", or "20 minutes in" are **clock-position or anchored relative time** expressions. Do NOT treat them as offsets from now.
@@ -255,13 +256,13 @@ Write the delivery step into the `message` when you create the schedule — not
255
256
 
256
257
  A schedule whose message has no delivery step is not finished. Before calling `schedule_create` in `execute` mode, read your own message back and check that it says where the output goes.
257
258
 
258
- There is a safety net, and it is not a substitute for the above. When an execute-mode run finishes with user-facing output and nothing delivered it, the assistant sends a notification carrying the run's final reply, so a schedule can no longer run and leave no trace. `messaging_send` counts as a delivery only when the call reported success, and `assistant notifications send` once the notification pipeline has taken it: a send the channel refused, or one that never reported at all, leaves the run eligible for the net, so the failure reaches you instead of vanishing into a conversation nobody has open. A Slack `chat.postMessage` never counts, because Slack refuses a call with a successful HTTP status and `ok: false` in the body, so the command succeeding proves nothing; a run that posts Block Kit that way also gets the net's notification. It fires on the raw reply, whatever the run happened to end on, at whatever length. An authored delivery step gets a title and body you chose, sent at the moment you chose. Rely on the net and you get the machine's guess instead.
259
+ There is a safety net, and it is not a substitute for the above. When an execute-mode run finishes with user-facing output and delivered nothing — no `assistant notifications send`, no `messaging_send`, no Slack `chat.postMessage` — the assistant sends a notification carrying the run's final reply, so a schedule can no longer run and leave no trace. It fires on the raw reply — whatever the run happened to end on, at whatever length. An authored delivery step gets a title and body you chose, sent at the moment you chose. Rely on the net and you get the machine's guess instead.
259
260
 
260
261
  Choose the right delivery tool based on the content:
261
262
 
262
- - **Rich content** (digests, summaries, reports): Use `messaging_send` with the target platform and conversation ID. It reaches Gmail or Outlook as a draft, and posts to a Slack, Telegram, or Discord chat through that channel's own transport, where the post is recorded and can be found again afterwards. Reach for `chat.postMessage` through `assistant oauth request` only for a Block Kit shape `messaging_send` cannot carry, with the provider the **slack** skill says to pass for posting on this workspace (`slack_channel` when the bot is set up; where only the `slack` integration exists, the skill says to tell the user before posting as them), never through `curl` or a revealed token. Either way the full content posts directly.
263
+ - **Rich content** (digests, summaries, reports): For Gmail, use `messaging_send` with the target platform and conversation ID. For Slack, call `chat.postMessage` through `assistant oauth request`, with the provider the **slack** skill says to pass for posting on this workspace (`slack_channel` when the bot is set up; where only the `slack` integration exists, the skill says to tell the user before posting as them), never through `curl` or a revealed token. This preserves the full content and posts directly.
263
264
  - **Short alerts** (status updates, completion notices): Use `assistant notifications send` via `bash` to let the notification router pick the best channel. Note: the router's decision engine rewrites content into short alerts, so it is not suitable for rich content.
264
265
 
265
266
  Example schedule message for a Slack digest:
266
267
 
267
- > "Scan my Slack channels for the last 24 hours using `assistant oauth request` for every Slack Web API read, with the provider the slack skill names for this workspace, then post the summary to the channel the user named with `messaging_send`, passing `platform: \"slack\"` and that channel's ID."
268
+ > "Scan my Slack channels for the last 24 hours using `assistant oauth request` for every Slack Web API call, with the provider the slack skill names for this workspace, then post the summary with `chat.postMessage` through the same command to the channel the user named."
@@ -124,14 +124,7 @@ their ear. Silence is the cue that it is their turn.
124
124
 
125
125
  **Advance on evidence, not on time.** Move to the next step when a fresh
126
126
  picture shows this one done, or when they tell you it is. Do not move on
127
- because a plausible amount of time has passed.
128
-
129
- The most direct telling is automatic. When the user clicks the control an
130
- arrow is pointing at, a message arrives as their turn saying they clicked it,
131
- by name, and that mark comes down on its own. Treat it as the step done: say
132
- what comes next and point at it. Nothing arrives for a click anywhere else,
133
- and nothing arrives for a ring, so for those the picture and their words are
134
- still the evidence. If the next picture shows the
127
+ because a plausible amount of time has passed. If the next picture shows the
135
128
  step not done, or done to the wrong thing, point at the same place again with
136
129
  a shorter caption and say what you saw. Pointing at the next step while the
137
130
  previous one is still open is how someone ends up two steps behind a mark.
@@ -53,10 +53,7 @@ describe("screen_point_at", () => {
53
53
  );
54
54
 
55
55
  expect(result.isError).toBe(true);
56
- expect(result.content).toContain(
57
- "The Vellum desktop app is needed to view or control your screen",
58
- );
59
- expect(result.content).toContain("https://www.vellum.ai/downloads");
56
+ expect(result.content).toContain("connected desktop client");
60
57
  });
61
58
 
62
59
  /** Drawing on the user's screen is a side effect a stopped turn must not have. */
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "scaffold_managed_skill",
6
- "description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load when a valid top-level SKILL.md is written under the skill directory. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory.",
6
+ "description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load when a valid top-level SKILL.md is written under the skill directory. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory. After a skill is written, the next turn may run in a recreated conversation due to file-watcher eviction - continue normally.",
7
7
  "category": "skills",
8
8
  "risk": "high",
9
9
  "input_schema": {
@@ -92,7 +92,7 @@
92
92
  },
93
93
  {
94
94
  "name": "delete_managed_skill",
95
- "description": "Delete a managed skill directory from {workspaceDir}/skills. Never delete a skill without explicit user confirmation.",
95
+ "description": "Delete a managed skill directory from {workspaceDir}/skills. Never delete a skill without explicit user confirmation. After deletion, the next turn may run in a recreated conversation due to file-watcher eviction - continue normally.",
96
96
  "category": "skills",
97
97
  "risk": "high",
98
98
  "input_schema": {
@@ -137,7 +137,7 @@ const VELLUM_PROFILE_IMPLS: ProfileImpls = {
137
137
  // The managed latency class, also what the live-voice front model runs on.
138
138
  // Its leading tokens are the turn-taking verdict, so what this profile
139
139
  // optimizes is the tail of time-to-first-token rather than the median: a
140
- // verdict slower than `voice.frontModel.endpointDecisionTimeoutMs`
140
+ // verdict slower than `liveVoice.frontModel.endpointDecisionTimeoutMs`
141
141
  // trips the speculative fail-open commit in live-voice-session.ts, which
142
142
  // is audible dead air.
143
143
  //
package/src/config/env.ts CHANGED
@@ -229,8 +229,8 @@ export function setPlatformAssistantId(value: string | undefined): void {
229
229
  * Platform assistant ID — UUID of this assistant on the platform.
230
230
  *
231
231
  * Resolved from the in-memory override (populated by providers-setup
232
- * rehydration via platform validate, or by secret-routes when the
233
- * platform pushes the value).
232
+ * rehydration from the credential store at daemon startup, or by
233
+ * secret-routes when the platform pushes the value).
234
234
  */
235
235
  export function getPlatformAssistantId(): string {
236
236
  return _platformAssistantIdOverride ?? "";
@@ -29,6 +29,18 @@
29
29
  "description": "Enable user-hosted onboarding flow",
30
30
  "defaultEnabled": false
31
31
  },
32
+ {
33
+ "id": "proactive-tips",
34
+ "scope": "client",
35
+ "key": "proactive-tips",
36
+ "label": "Proactive Tips",
37
+ "description": "Gates the dismissible proactive tip card in the web sidebar. String-valued so future A/B arms can be added as new values.",
38
+ "defaultEnabled": "off",
39
+ "values": [
40
+ "off",
41
+ "on"
42
+ ]
43
+ },
32
44
  {
33
45
  "id": "vision-mode",
34
46
  "scope": "client",
@@ -442,14 +454,6 @@
442
454
  "description": "Native notifications show the assistant's avatar as the icon with the assistant's name on line one. Same key the platform reads for APNs/FCM sender blocks.",
443
455
  "defaultEnabled": false
444
456
  },
445
- {
446
- "id": "local-notification-avatar",
447
- "scope": "client",
448
- "key": "local-notification-avatar",
449
- "label": "Local Notification Avatar",
450
- "description": "Shows the assistant name and avatar in notifications posted locally by native clients and supported browsers. Independent of remote push avatar presentation.",
451
- "defaultEnabled": false
452
- },
453
457
  {
454
458
  "id": "channel-conversation-sidecar",
455
459
  "scope": "client",
@@ -474,14 +478,6 @@
474
478
  "description": "Gates the seeded Figma OAuth provider. When off, the provider is hidden from GET /v1/oauth/providers, its get-by-id route, and the connect/update routes, so it cannot be listed or connected from the CLI, gateway, or web integrations list. The row is still seeded into oauth_providers on startup, and existing connections keep injecting credentials; only its visibility changes.",
475
479
  "defaultEnabled": false
476
480
  },
477
- {
478
- "id": "shopify-oauth",
479
- "scope": "assistant",
480
- "key": "shopify-oauth",
481
- "label": "Shopify Integration",
482
- "description": "Gates the seeded Shopify OAuth provider. When off, the provider is hidden from GET /v1/oauth/providers, its get-by-id route, and the connect/update routes, so it cannot be listed or connected from the CLI, gateway, or web integrations list. The row is still seeded into oauth_providers on startup, and existing connections keep injecting credentials; only its visibility changes. Off by default because Shopify is the first per-tenant provider: connecting needs the merchant's myshopify.com domain, and the shop-domain input in the clients ships separately from the seed.",
483
- "defaultEnabled": false
484
- },
485
481
  {
486
482
  "id": "chatgpt-device-code-login",
487
483
  "scope": "client",
@@ -527,14 +523,6 @@
527
523
  "label": "Send User Message Tool",
528
524
  "description": "Route all user-facing assistant text through the send_user_message tool instead of streamed assistant text",
529
525
  "defaultEnabled": false
530
- },
531
- {
532
- "id": "assistant-desktop",
533
- "scope": "assistant",
534
- "key": "assistant-desktop",
535
- "label": "Assistant Desktop",
536
- "description": "Streams an interactive desktop from a containerized assistant pod to the web client over /v1/desktop/stream. Off: no desktop affordance is rendered. On: the chat header offers a desktop panel that views and controls the pod's display.",
537
- "defaultEnabled": false
538
526
  }
539
527
  ]
540
528
  }
@@ -180,7 +180,6 @@ export function getDeploymentContextDefaults(): Record<string, unknown> {
180
180
  "discord-oauth": managed,
181
181
  "hubspot-oauth": managed,
182
182
  "monday-oauth": managed,
183
- "shopify-oauth": managed,
184
183
  "stripe-link-oauth": managed,
185
184
  "figma-oauth": managed,
186
185
  "eventbrite-oauth": managed,
@@ -6,7 +6,6 @@ import {
6
6
  isMemoryV1Active,
7
7
  isMemoryV2ExplicitlyDisabled,
8
8
  isMemoryV3Live,
9
- isSkillImprovementActive,
10
9
  isV2InjectionEngineActive,
11
10
  isV3TierActive,
12
11
  usesConceptPageMemory,
@@ -56,16 +55,12 @@ function makeConfig(
56
55
  enabled: TriState,
57
56
  v2Enabled: TriState,
58
57
  v3Live: TriState,
59
- skillImprovement?: boolean,
60
58
  ): AssistantConfig {
61
59
  return {
62
60
  memory: {
63
61
  ...(enabled === undefined ? {} : { enabled }),
64
62
  ...(v2Enabled === undefined ? {} : { v2: { enabled: v2Enabled } }),
65
63
  ...(v3Live === undefined ? {} : { v3: { live: v3Live } }),
66
- ...(skillImprovement === undefined
67
- ? {}
68
- : { retrospective: { skillImprovement } }),
69
64
  },
70
65
  } as AssistantConfig;
71
66
  }
@@ -301,32 +296,6 @@ describe("v1-staleness guard is one condition", () => {
301
296
  * proc-to-skills alias of `isMemoryV3Live` (which did not). One predicate now
302
297
  * answers for both features.
303
298
  */
304
- describe("skill improvement gate", () => {
305
- test("defaults on for a live v3 tier", () => {
306
- expect(isSkillImprovementActive(makeConfig(true, undefined, true))).toBe(
307
- true,
308
- );
309
- expect(isSkillImprovementActive(makeConfig(undefined, undefined, true))).toBe(
310
- true,
311
- );
312
- });
313
-
314
- test("explicit opt-out disables skill improvement without disabling v3", () => {
315
- const config = makeConfig(true, undefined, true, false);
316
-
317
- expect(isV3TierActive(config)).toBe(true);
318
- expect(isSkillImprovementActive(config)).toBe(false);
319
- });
320
-
321
- test("requires the live v3 tier even when explicitly enabled", () => {
322
- const v2Config = makeConfig(true, true, false, true);
323
- const memoryOffConfig = makeConfig(false, undefined, true, true);
324
-
325
- expect(isSkillImprovementActive(v2Config)).toBe(false);
326
- expect(isSkillImprovementActive(memoryOffConfig)).toBe(false);
327
- });
328
- });
329
-
330
299
  describe("v3 tier is one condition", () => {
331
300
  test("memory off suppresses the v3 tier even with memory.v3.live set", () => {
332
301
  const config = makeConfig(false, undefined, true);
@@ -81,32 +81,28 @@ export function isMemoryV3Live(config: AssistantConfig): boolean {
81
81
  * FEATURE asks this predicate; only the injection-suppression checks that must
82
82
  * mirror the raw `memory.v3.live` key ask {@link isMemoryV3Live} directly.
83
83
  *
84
- * The memory concept graph is gated on it: both `GET /memory-graph`
85
- * (`supported`) and the cheap `graph_supported` bit on `GET /memory/stats`
86
- * must agree with whether the graph can build from the v3 concept-page
87
- * substrate.
84
+ * The two features gated on it, and why they must agree:
88
85
  *
89
- * Retrospective skill improvement is a separate optional v3-tier feature.
90
- * Ask {@link isSkillImprovementActive} for it so an operator can stop managed
91
- * skill authoring without turning off ordinary retrospective memory capture.
86
+ * - The memory concept graph — the single source of truth for both
87
+ * `GET /memory-graph` (`supported`) and the cheap `graph_supported` bit on
88
+ * `GET /memory/stats`, so the advertised capability and the actual build can
89
+ * never drift. The graph builds off the v3 concept-page substrate.
90
+ * - Procedural-memory-as-skills — the retrospective's skill-authoring step and
91
+ * its permission grant. Scoped to the v3 tier because skill retrieval rides
92
+ * the v3 lanes and the usage-prune stage lives in the v3 maintain job. That
93
+ * prune ships observe-first: with `memory.maintenance.skillPruneDays` at its
94
+ * default (`null`) it reports stale assistant-authored skills
95
+ * (`prunableSkills`) but deletes none — so a v3-tier assistant authors skills
96
+ * without an automatic retirement bound until a positive `skillPruneDays` is
97
+ * configured.
98
+ *
99
+ * Both write into memory on the user's behalf, so both honor the Memory
100
+ * opt-out identically: one predicate, one answer.
92
101
  */
93
102
  export function isV3TierActive(config: AssistantConfig): boolean {
94
103
  return isMemoryEnabled(config) && isMemoryV3Live(config);
95
104
  }
96
105
 
97
- /**
98
- * Whether a retrospective may turn observed procedures into managed skills.
99
- * This requires the v3 tier because skills are retrieved through its memory
100
- * substrate, and it honors the retrospective-specific opt-out without
101
- * disabling ordinary retrospective fact retention.
102
- */
103
- export function isSkillImprovementActive(config: AssistantConfig): boolean {
104
- return (
105
- isV3TierActive(config) &&
106
- config.memory?.retrospective?.skillImprovement !== false
107
- );
108
- }
109
-
110
106
  /**
111
107
  * Minimal structural view of the memory config the concept-page gate reads.
112
108
  * Accepting the shape (rather than the full `MemoryConfig`) lets call sites
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Rehydrate the in-memory platform identity/URL overrides at process startup.
2
+ * Rehydrate the in-memory platform identity/URL overrides from the credential
3
+ * store at process startup.
3
4
  *
4
5
  * These overrides (`setPlatformBaseUrl` and the platform ID setters in
5
6
  * `config/env.ts`) are normally only populated at runtime by the secret-routes
@@ -9,17 +10,18 @@
9
10
  * environment default (e.g. dev-platform) while the credential store holds the
10
11
  * real production values, and requests are sent to the wrong environment.
11
12
  *
12
- * Bound platform ids come from `POST /v1/internal/assistants/validate/`,
13
- * which trades the assistant API key for assistant/organization/user ids.
14
- *
15
13
  * Each field is best-effort: a credential-store read failure is logged and
16
14
  * skipped so a single missing value never blocks startup.
17
15
  */
18
16
  import { credentialKey } from "../security/credential-key.js";
19
17
  import { getSecureKeyAsync } from "../security/secure-keys.js";
20
18
  import { getLogger } from "../util/logger.js";
21
- import { setPlatformBaseUrl } from "./env.js";
22
- import { ensurePlatformIdentityIds } from "./platform-identity.js";
19
+ import {
20
+ setPlatformAssistantId,
21
+ setPlatformBaseUrl,
22
+ setPlatformOrganizationId,
23
+ setPlatformUserId,
24
+ } from "./env.js";
23
25
 
24
26
  const log = getLogger("platform-rehydration");
25
27
 
@@ -45,16 +47,30 @@ async function rehydrateField(
45
47
 
46
48
  /**
47
49
  * Rehydrate the platform base URL and the related platform IDs (assistant,
48
- * organization, user). Safe to call more than once.
50
+ * organization, user) from the credential store into their in-memory
51
+ * overrides. Safe to call more than once.
49
52
  */
50
53
  export async function rehydratePlatformCredentials(): Promise<void> {
51
- // Base URL first so managed proxy activation and identity validate resolve
52
- // the correct environment for every request that follows.
54
+ // Base URL first so managed proxy activation resolves the correct
55
+ // environment for the ID lookups and every request that follows.
53
56
  await rehydrateField(
54
57
  "platform_base_url",
55
58
  setPlatformBaseUrl,
56
59
  "platform base URL",
57
60
  );
58
-
59
- await ensurePlatformIdentityIds();
61
+ await rehydrateField(
62
+ "platform_assistant_id",
63
+ setPlatformAssistantId,
64
+ "platform assistant ID",
65
+ );
66
+ await rehydrateField(
67
+ "platform_organization_id",
68
+ setPlatformOrganizationId,
69
+ "platform organization ID",
70
+ );
71
+ await rehydrateField(
72
+ "platform_user_id",
73
+ setPlatformUserId,
74
+ "platform user ID",
75
+ );
60
76
  }
@@ -41,6 +41,7 @@ import {
41
41
  AuditLogConfigSchema,
42
42
  LogFileConfigSchema,
43
43
  } from "./schemas/logging.js";
44
+ import { McpConfigSchema } from "./schemas/mcp.js";
44
45
  import { MemoryConfigSchema } from "./schemas/memory.js";
45
46
  import { MigrationsConfigSchema } from "./schemas/migrations.js";
46
47
  import { MonitoringConfigSchema } from "./schemas/monitoring.js";
@@ -61,7 +62,6 @@ import {
61
62
  } from "./schemas/timeouts.js";
62
63
  import { ToolsConfigSchema } from "./schemas/tools.js";
63
64
  import { UserRoutesConfigSchema } from "./schemas/user-routes.js";
64
- import { VoiceConfigSchema } from "./schemas/voice.js";
65
65
  import { WorkflowsConfigSchema } from "./schemas/workflows.js";
66
66
  import { WorkspaceGitConfigSchema } from "./schemas/workspace-git.js";
67
67
 
@@ -108,6 +108,7 @@ export const AssistantConfigSchema = z.object({
108
108
  ),
109
109
  journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
110
110
  backup: BackupConfigSchema.default(BackupConfigSchema.parse({})),
111
+ mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
111
112
  acp: AcpConfigSchema.default(AcpConfigSchema.parse({})),
112
113
  skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
113
114
  workspaceGit: WorkspaceGitConfigSchema.default(
@@ -118,7 +119,6 @@ export const AssistantConfigSchema = z.object({
118
119
  twilio: TwilioConfigSchema.default(TwilioConfigSchema.parse({})),
119
120
  calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
120
121
  liveVoice: LiveVoiceConfigSchema.default(LiveVoiceConfigSchema.parse({})),
121
- voice: VoiceConfigSchema.default(VoiceConfigSchema.parse({})),
122
122
  sight: SightConfigSchema.default(SightConfigSchema.parse({})),
123
123
  whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
124
124
  telegram: TelegramConfigSchema.default(TelegramConfigSchema.parse({})),