@vellumai/assistant 0.12.0 → 0.12.1-dev.202609141913.5e16607

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 (441) hide show
  1. package/AGENTS.md +5 -1
  2. package/ARCHITECTURE.md +11 -12
  3. package/docs/architecture/turn-actor.md +15 -8
  4. package/docs/guardian-request-flow.md +16 -0
  5. package/openapi.yaml +292 -50
  6. package/package.json +1 -1
  7. package/scripts/smoke-desktop-window-theme.ts +186 -0
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  9. package/src/__tests__/app-compiler.test.ts +6 -0
  10. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  11. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  12. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  13. package/src/__tests__/computer-use-tools.test.ts +17 -5
  14. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  15. package/src/__tests__/config-schema.test.ts +28 -29
  16. package/src/__tests__/config-watcher.test.ts +23 -8
  17. package/src/__tests__/connection-model-compat.test.ts +2 -2
  18. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  19. package/src/__tests__/conversation-pairing.test.ts +26 -0
  20. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  21. package/src/__tests__/conversation-queue.test.ts +48 -207
  22. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  23. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  24. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  25. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  26. package/src/__tests__/conversation-title-service.test.ts +14 -10
  27. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  28. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  29. package/src/__tests__/credential-record-retire.test.ts +171 -1
  30. package/src/__tests__/credential-routes.test.ts +212 -3
  31. package/src/__tests__/credential-security-invariants.test.ts +4 -2
  32. package/src/__tests__/cu-unified-flow.test.ts +8 -2
  33. package/src/__tests__/document-sync-tags.test.ts +25 -0
  34. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  35. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  36. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  37. package/src/__tests__/git-porcelain.test.ts +47 -0
  38. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  39. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  40. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  42. package/src/__tests__/host-file-proxy.test.ts +11 -23
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  44. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  45. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  47. package/src/__tests__/mcp-health-check.test.ts +8 -11
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  49. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  50. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  51. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  52. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  53. package/src/__tests__/model-intents.test.ts +3 -0
  54. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  55. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  56. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  57. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  58. package/src/__tests__/plugin-import-boundary-guard.test.ts +13 -0
  59. package/src/__tests__/resolve-app.test.ts +135 -0
  60. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  61. package/src/__tests__/service-account-token.test.ts +247 -0
  62. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  63. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  64. package/src/__tests__/source-fingerprint.test.ts +184 -0
  65. package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
  66. package/src/__tests__/subagent-tools.test.ts +3 -2
  67. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  68. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  69. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  70. package/src/__tests__/workspace-git-service.test.ts +26 -0
  71. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  72. package/src/acp/__tests__/agent-process.test.ts +258 -4
  73. package/src/acp/__tests__/auth-required.test.ts +96 -0
  74. package/src/acp/__tests__/client-handler.test.ts +90 -2
  75. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  76. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  77. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  78. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  79. package/src/acp/__tests__/session-manager.test.ts +895 -4
  80. package/src/acp/agent-process.test.ts +4 -1
  81. package/src/acp/agent-process.ts +94 -9
  82. package/src/acp/auth-required.ts +50 -1
  83. package/src/acp/auto-install.test.ts +9 -7
  84. package/src/acp/client-handler.ts +16 -3
  85. package/src/acp/model-config.test.ts +166 -0
  86. package/src/acp/model-config.ts +138 -0
  87. package/src/acp/resolve-agent.test.ts +220 -6
  88. package/src/acp/resolve-agent.ts +88 -23
  89. package/src/acp/session-manager.test.ts +196 -3
  90. package/src/acp/session-manager.ts +441 -18
  91. package/src/acp/types.ts +50 -5
  92. package/src/api/events/acp-session-model-update.test.ts +88 -0
  93. package/src/api/events/acp-session-model-update.ts +46 -0
  94. package/src/api/events/app-files-changed.ts +4 -3
  95. package/src/api/events/notification-intent.ts +2 -0
  96. package/src/api/index.ts +6 -0
  97. package/src/apps/app-store.ts +1 -1
  98. package/src/apps/resolve-app.ts +81 -0
  99. package/src/apps/source-fingerprint.ts +266 -0
  100. package/src/bundler/app-compiler.ts +15 -0
  101. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  102. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  103. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  104. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  105. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  106. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  107. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  108. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  109. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  110. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  111. package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
  112. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  113. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  114. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  115. package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
  116. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  117. package/src/calls/barge-in-guard.ts +118 -0
  118. package/src/calls/call-constants.ts +24 -4
  119. package/src/calls/call-controller.ts +499 -199
  120. package/src/calls/call-transport.ts +16 -0
  121. package/src/calls/media-stream-output.ts +32 -0
  122. package/src/calls/media-stream-server.ts +82 -30
  123. package/src/calls/media-stream-stt-session.ts +15 -0
  124. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  125. package/src/calls/resolve-call-tts-provider.ts +33 -2
  126. package/src/calls/tts-call-strategy.ts +9 -5
  127. package/src/calls/types.ts +2 -0
  128. package/src/calls/voice-control-protocol.ts +53 -21
  129. package/src/calls/voice-leg-coordinator.ts +189 -0
  130. package/src/calls/voice-progress-cadence.ts +423 -0
  131. package/src/calls/voice-session-bridge.ts +59 -46
  132. package/src/calls/voice-triage-escalate.ts +175 -56
  133. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  134. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  135. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  136. package/src/cli/commands/apps.help.ts +67 -13
  137. package/src/cli/commands/apps.ts +183 -0
  138. package/src/cli/commands/bash.help.ts +8 -13
  139. package/src/cli/commands/bash.ts +7 -25
  140. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  141. package/src/cli/commands/channels/index.help.ts +70 -2
  142. package/src/cli/commands/channels/index.ts +16 -3
  143. package/src/cli/commands/channels/send.ts +128 -0
  144. package/src/cli/commands/credentials.help.ts +6 -2
  145. package/src/cli/commands/credentials.ts +2 -2
  146. package/src/cli/commands/mcp.help.ts +8 -8
  147. package/src/cli/commands/mcp.ts +1 -1
  148. package/src/cli/commands/roadmap.help.ts +5 -1
  149. package/src/cli/commands/roadmap.ts +1 -1
  150. package/src/cli/lib/bundled-marketplace.json +103 -90
  151. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  152. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  153. package/src/config/__tests__/platform-identity.test.ts +255 -0
  154. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  155. package/src/config/acp-agent-write.ts +43 -0
  156. package/src/config/acp-defaults.test.ts +13 -3
  157. package/src/config/acp-defaults.ts +23 -6
  158. package/src/config/acp-schema.test.ts +81 -0
  159. package/src/config/acp-schema.ts +30 -2
  160. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  161. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  162. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  163. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  164. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  165. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  166. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  167. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  168. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  169. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  170. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  171. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  172. package/src/config/default-profile-catalog.ts +1 -1
  173. package/src/config/env.ts +2 -2
  174. package/src/config/feature-flag-registry.json +24 -12
  175. package/src/config/loader.ts +1 -0
  176. package/src/config/memory-v3-gate.test.ts +31 -0
  177. package/src/config/memory-v3-gate.ts +20 -16
  178. package/src/config/platform-identity.ts +201 -0
  179. package/src/config/platform-rehydration.ts +11 -27
  180. package/src/config/schema.ts +2 -2
  181. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  182. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  183. package/src/config/schemas/calls.ts +1 -28
  184. package/src/config/schemas/live-voice.ts +1 -173
  185. package/src/config/schemas/mcp.ts +1 -1
  186. package/src/config/schemas/memory-retrospective.ts +10 -0
  187. package/src/config/schemas/services.ts +8 -0
  188. package/src/config/schemas/skills.ts +0 -14
  189. package/src/config/schemas/voice.ts +150 -0
  190. package/src/credential-execution/process-manager.test.ts +18 -19
  191. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  192. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  193. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  194. package/src/daemon/app-change-notify.ts +45 -0
  195. package/src/daemon/config-watcher.ts +13 -194
  196. package/src/daemon/conversation-process.ts +35 -64
  197. package/src/daemon/conversation-skill-tools.ts +13 -31
  198. package/src/daemon/conversation-store.ts +1 -1
  199. package/src/daemon/conversation-surfaces.ts +10 -8
  200. package/src/daemon/conversation-tool-setup.ts +37 -43
  201. package/src/daemon/conversation.ts +0 -3
  202. package/src/daemon/host-bash-proxy.ts +8 -12
  203. package/src/daemon/host-browser-proxy.ts +15 -1
  204. package/src/daemon/host-file-proxy.ts +8 -10
  205. package/src/daemon/host-transfer-proxy.ts +14 -18
  206. package/src/daemon/lifecycle.ts +0 -5
  207. package/src/daemon/mcp-reload-service.ts +5 -5
  208. package/src/daemon/message-types/sync.ts +1 -0
  209. package/src/daemon/orphan-reaper.ts +1 -1
  210. package/src/daemon/providers-setup.ts +1 -1
  211. package/src/daemon/shutdown-handlers.ts +1 -9
  212. package/src/daemon/skill-memory-refresh.ts +64 -0
  213. package/src/daemon/tool-side-effects.ts +7 -18
  214. package/src/desktop/__tests__/fake-desktop.ts +119 -0
  215. package/src/desktop/desktop-chrome-policy.test.ts +71 -0
  216. package/src/desktop/desktop-chrome-policy.ts +42 -0
  217. package/src/desktop/desktop-dependencies.test.ts +102 -0
  218. package/src/desktop/desktop-dependencies.ts +306 -0
  219. package/src/desktop/desktop-feature.test.ts +18 -0
  220. package/src/desktop/desktop-feature.ts +19 -0
  221. package/src/desktop/desktop-panel-config.test.ts +135 -0
  222. package/src/desktop/desktop-panel-config.ts +149 -0
  223. package/src/desktop/desktop-session-manager.test.ts +627 -0
  224. package/src/desktop/desktop-session-manager.ts +814 -0
  225. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  226. package/src/desktop/desktop-stream-bridge.ts +214 -0
  227. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  228. package/src/desktop/desktop-wallpaper.ts +113 -0
  229. package/src/desktop/desktop-window-theme.ts +170 -0
  230. package/src/home/__tests__/feed-writer.test.ts +22 -0
  231. package/src/home/feed-writer.ts +11 -2
  232. package/src/hooks/types.ts +33 -0
  233. package/src/i18n/AGENTS.md +22 -0
  234. package/src/i18n/__tests__/i18n.test.ts +108 -0
  235. package/src/i18n/index.ts +16 -0
  236. package/src/i18n/locales.ts +93 -0
  237. package/src/i18n/messages.ts +61 -0
  238. package/src/i18n/title.ts +25 -0
  239. package/src/inbound/platform-callback-registration.ts +4 -5
  240. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  241. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  242. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  243. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  244. package/src/live-voice/live-voice-session.ts +213 -693
  245. package/src/live-voice/protocol.ts +0 -2
  246. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  247. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  248. package/src/mcp/effective-config.ts +3 -3
  249. package/src/mcp/mcp-header-store.ts +29 -45
  250. package/src/mcp/spec-schema.ts +111 -0
  251. package/src/mcp/startup.ts +8 -4
  252. package/src/mcp/workspace-mcp-config.ts +188 -0
  253. package/src/messaging/provider.ts +10 -3
  254. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  255. package/src/messaging/providers/callback-routing.ts +18 -0
  256. package/src/messaging/providers/channel-transport.ts +56 -0
  257. package/src/messaging/providers/discord/transport.ts +17 -0
  258. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  259. package/src/messaging/providers/slack/adapter.ts +7 -44
  260. package/src/messaging/providers/slack/transport.ts +16 -0
  261. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  262. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  263. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  264. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  265. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  266. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  267. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  268. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  269. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  270. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  271. package/src/notifications/adapters/macos.ts +3 -0
  272. package/src/notifications/assistant-reply-producer.ts +8 -3
  273. package/src/notifications/conversation-pairing.ts +36 -16
  274. package/src/notifications/delivered-post-record.ts +11 -1
  275. package/src/notifications/guardian-feed-projection.ts +125 -5
  276. package/src/notifications/home-feed-side-effect.ts +55 -7
  277. package/src/notifications/schedule-result-producer.ts +66 -22
  278. package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
  279. package/src/oauth/byo-connection.test.ts +45 -0
  280. package/src/oauth/byo-connection.ts +36 -6
  281. package/src/oauth/connection-resolver.test.ts +73 -0
  282. package/src/oauth/connection-resolver.ts +95 -2
  283. package/src/oauth/seed-providers.ts +165 -0
  284. package/src/outbound-proxy/certs.ts +3 -5
  285. package/src/permissions/checker.test.ts +11 -6
  286. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  287. package/src/persistence/conversation-crud.ts +71 -63
  288. package/src/persistence/conversation-lineage.ts +12 -4
  289. package/src/persistence/conversation-title-placeholders.ts +23 -0
  290. package/src/persistence/conversation-title-service.ts +9 -26
  291. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  292. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  293. package/src/persistence/message-cursor.ts +78 -0
  294. package/src/persistence/message-reads.ts +30 -0
  295. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  296. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  297. package/src/persistence/schema/memory-core.ts +5 -0
  298. package/src/platform/client.test.ts +4 -2
  299. package/src/platform/client.ts +2 -10
  300. package/src/plugin-api/constants.ts +2 -0
  301. package/src/plugin-api/index.ts +1 -0
  302. package/src/plugin-api/types.ts +2 -0
  303. package/src/plugins/defaults/index.ts +2 -0
  304. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  305. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  306. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  307. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  308. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  309. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  310. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  311. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  312. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  313. package/src/plugins/defaults/memory/graph/capability-seed.ts +4 -6
  314. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  315. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  316. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  317. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  318. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  319. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  320. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  321. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  322. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  323. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  324. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  325. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  326. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  327. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  328. package/src/plugins/defaults/memory/startup.ts +6 -1
  329. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  330. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  331. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +89 -0
  332. package/src/plugins/defaults/memory/worker.ts +6 -0
  333. package/src/plugins/mcp-servers.ts +15 -75
  334. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  335. package/src/providers/gemini/client.ts +9 -23
  336. package/src/providers/gemini-thought-signature.test.ts +63 -0
  337. package/src/providers/gemini-thought-signature.ts +44 -0
  338. package/src/providers/inference/auth.ts +2 -3
  339. package/src/providers/inference/connection-availability.ts +27 -8
  340. package/src/providers/inference/resolve-auth.ts +22 -4
  341. package/src/providers/inference/service-account-token.ts +248 -0
  342. package/src/providers/model-catalog.ts +34 -0
  343. package/src/providers/model-intents.ts +10 -5
  344. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  345. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  346. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  347. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  348. package/src/providers/openai/chat-completions-provider.ts +192 -78
  349. package/src/providers/openai/codex-models.ts +0 -4
  350. package/src/providers/openai/google-thought-signature.ts +320 -0
  351. package/src/runtime/AGENTS.md +2 -0
  352. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  353. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  354. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  355. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  356. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  357. package/src/runtime/auth/same-actor.ts +38 -3
  358. package/src/runtime/channel-send.ts +316 -0
  359. package/src/runtime/http-server.ts +160 -151
  360. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  361. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
  362. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  363. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  364. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  365. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  366. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  367. package/src/runtime/routes/acp-routes.test.ts +15 -0
  368. package/src/runtime/routes/acp-routes.ts +72 -9
  369. package/src/runtime/routes/app-management-routes.ts +73 -0
  370. package/src/runtime/routes/app-routes.ts +1 -1
  371. package/src/runtime/routes/channel-send-routes.ts +85 -0
  372. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  373. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  374. package/src/runtime/routes/credential-routes.ts +19 -10
  375. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  376. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  377. package/src/runtime/routes/diagnostics-routes.ts +55 -3
  378. package/src/runtime/routes/index.ts +4 -2
  379. package/src/runtime/routes/log-export-routes.ts +2 -0
  380. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  381. package/src/runtime/routes/migration-routes.ts +6 -20
  382. package/src/runtime/routes/platform-routes.ts +18 -15
  383. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  384. package/src/runtime/services/conversation-serializer.ts +16 -3
  385. package/src/runtime/sync/resource-sync-events.ts +2 -1
  386. package/src/schedule/worker.ts +1 -1
  387. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  388. package/src/security/ces-http-record-backend.ts +120 -0
  389. package/src/security/ces-rpc-record-backend.ts +7 -0
  390. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  391. package/src/skills/workspace-skill-md-signature.ts +50 -0
  392. package/src/subagent/consult-context.ts +12 -25
  393. package/src/tools/acp/list-agents.test.ts +3 -1
  394. package/src/tools/acp/spawn.test.ts +146 -11
  395. package/src/tools/acp/spawn.ts +21 -3
  396. package/src/tools/acp/status.test.ts +94 -0
  397. package/src/tools/acp/status.ts +40 -3
  398. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  399. package/src/tools/capability-offer.test.ts +22 -2
  400. package/src/tools/capability-offer.ts +16 -1
  401. package/src/tools/computer-use/definitions.ts +4 -2
  402. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  403. package/src/tools/credentials/metadata-store.ts +100 -0
  404. package/src/tools/host-filesystem/edit.test.ts +2 -1
  405. package/src/tools/host-filesystem/edit.ts +2 -2
  406. package/src/tools/host-filesystem/read.test.ts +2 -1
  407. package/src/tools/host-filesystem/read.ts +2 -2
  408. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  409. package/src/tools/host-filesystem/transfer.ts +2 -2
  410. package/src/tools/host-filesystem/write.test.ts +2 -1
  411. package/src/tools/host-filesystem/write.ts +2 -2
  412. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  413. package/src/tools/host-terminal/host-shell.ts +4 -20
  414. package/src/tools/policy-context.ts +6 -7
  415. package/src/tools/subagent/spawn.ts +5 -8
  416. package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
  417. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  418. package/src/tools/terminal/safe-env.ts +23 -3
  419. package/src/tools/terminal/sanitized-bash.ts +105 -0
  420. package/src/tools/watch/watch-retro-report.ts +0 -8
  421. package/src/util/__tests__/language-subtag.test.ts +23 -1
  422. package/src/util/ca-bundle.ts +13 -0
  423. package/src/util/language-subtag.ts +16 -0
  424. package/src/util/platform.ts +8 -0
  425. package/src/workspace/git-porcelain.ts +66 -0
  426. package/src/workspace/git-service.ts +5 -54
  427. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  428. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  429. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  430. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  431. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  432. package/src/workspace/migrations/registry.ts +6 -0
  433. package/src/__tests__/app-source-watcher.test.ts +0 -218
  434. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  435. package/src/__tests__/speaker-identification.test.ts +0 -56
  436. package/src/calls/speaker-identification.ts +0 -233
  437. package/src/daemon/app-source-watcher.ts +0 -218
  438. package/src/messaging/providers/whatsapp/client.ts +0 -29
  439. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  440. package/src/util/fs-watcher-error.ts +0 -36
  441. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -22,6 +22,7 @@ import {
22
22
  getConfiguredProvider,
23
23
  userMessage,
24
24
  } from "../../providers/provider-send-message.js";
25
+ import { isMaxTokensStopReason } from "../../providers/stop-reasons.js";
25
26
  import { getLogger } from "../../util/logger.js";
26
27
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
27
28
  import { BadRequestError } from "./errors.js";
@@ -176,9 +177,44 @@ function buildCommandPrompt(body: DictationBody, stylePrompt?: string): string {
176
177
  return sections.join("\n");
177
178
  }
178
179
 
179
- function computeMaxTokens(inputLength: number): number {
180
+ export function computeMaxTokens(inputLength: number): number {
180
181
  const estimatedInputTokens = Math.ceil(inputLength / 3);
181
- return Math.max(256, estimatedInputTokens + 128);
182
+ // The cleanup tool call carries the whole transcript back in its `text`
183
+ // argument, plus a `reasoning` string and the JSON scaffolding around both,
184
+ // so the budget has to cover well more than the input alone. `max_tokens` is
185
+ // a ceiling rather than a target: a call that stops short of it costs no
186
+ // extra latency.
187
+ return Math.max(512, estimatedInputTokens * 2 + 256);
188
+ }
189
+
190
+ export type CleanupRejection = "truncated";
191
+
192
+ /**
193
+ * Decide whether the cleanup model's rewrite is safe to use as the payload.
194
+ *
195
+ * The rewrite replaces what the user actually said. A token-cap stop means the
196
+ * tool JSON was cut mid-argument, so however complete the `text` argument
197
+ * happens to look, it is a fragment; the raw transcript is the safer payload
198
+ * then. Unpolished beats wrong.
199
+ *
200
+ * Whether a shorter rewrite still preserved the meaning is the model's call
201
+ * (the prompt asks it to drop fillers and tighten phrasing), so there is no
202
+ * length check here: a deterministic ratio cannot tell a concise cleanup from
203
+ * a summary.
204
+ */
205
+ export function resolveCleanedDictation(
206
+ raw: string,
207
+ cleaned: string,
208
+ stopReason: string | null | undefined,
209
+ ): { text: string; rejected: CleanupRejection | null } {
210
+ const trimmed = cleaned.trim();
211
+ if (!trimmed) {
212
+ return { text: raw, rejected: null };
213
+ }
214
+ if (isMaxTokensStopReason(stopReason)) {
215
+ return { text: raw, rejected: "truncated" };
216
+ }
217
+ return { text: trimmed, rejected: null };
182
218
  }
183
219
 
184
220
  interface DictationResult {
@@ -329,7 +365,23 @@ async function handleDictation(body: DictationBody): Promise<DictationResult> {
329
365
  ...profileMeta,
330
366
  };
331
367
  }
332
- const cleanedText = input.text?.trim() || transcription;
368
+ const { text: cleanedText, rejected } = resolveCleanedDictation(
369
+ transcription,
370
+ input.text ?? "",
371
+ response.stopReason,
372
+ );
373
+ if (rejected) {
374
+ // Lengths only -- transcript content must never be logged.
375
+ log.warn(
376
+ {
377
+ rejected,
378
+ stopReason: response.stopReason,
379
+ rawChars: transcription.length,
380
+ cleanedChars: input.text?.trim().length ?? 0,
381
+ },
382
+ "Dictation cleanup rejected, using raw transcription",
383
+ );
384
+ }
333
385
  const normalizedText = applyDictionary(cleanedText, profile.dictionary);
334
386
  return {
335
387
  text: normalizedText,
@@ -39,6 +39,7 @@ import { ROUTES as CALL_ROUTES } from "./call-routes.js";
39
39
  import { ROUTES as CHANNEL_AVAILABILITY_ROUTES } from "./channel-availability-routes.js";
40
40
  import { ROUTES as CHANNEL_READINESS_ROUTES } from "./channel-readiness-routes.js";
41
41
  import { CHANNEL_ROUTES } from "./channel-route-definitions.js";
42
+ import { ROUTES as CHANNEL_SEND_ROUTES } from "./channel-send-routes.js";
42
43
  import { ROUTES as CHANNEL_VERIFICATION_ROUTES } from "./channel-verification-routes.js";
43
44
  import { ROUTES as CHATGPT_SUBSCRIPTION_AUTH_ROUTES } from "./chatgpt-subscription-auth-routes.js";
44
45
  import { ROUTES as CLIENT_ROUTES } from "./client-routes.js";
@@ -58,10 +59,10 @@ import { ROUTES as CONVERSATIONS_IMPORT_ROUTES } from "./conversations-import-ro
58
59
  import { ROUTES as CREDENTIAL_PROMPT_ROUTES } from "./credential-prompt-routes.js";
59
60
  import { ROUTES as CREDENTIAL_REQUEST_ROUTES } from "./credential-request-routes.js";
60
61
  import { ROUTES as CREDENTIAL_ROUTES } from "./credential-routes.js";
61
- import { ROUTES as DEBUG_BASH_ROUTES } from "./debug-bash-routes.js";
62
62
  import { ROUTES as DEBUG_ROUTES } from "./debug-routes.js";
63
63
  import { ROUTES as DEFAULT_PROVIDER_ROUTES } from "./default-provider-routes.js";
64
64
  import { ROUTES as DEFER_ROUTES } from "./defer-routes.js";
65
+ import { ROUTES as DESKTOP_SETUP_ROUTES } from "./desktop-setup-routes.js";
65
66
  import { ROUTES as DIAGNOSTICS_ROUTES } from "./diagnostics-routes.js";
66
67
  import { ROUTES as DISK_PRESSURE_ROUTES } from "./disk-pressure-routes.js";
67
68
  import { ROUTES as DOCUMENT_COMMENT_ROUTES } from "./document-comments-routes.js";
@@ -191,6 +192,7 @@ export const ROUTES: RouteDefinition[] = [
191
192
  ...CHANNEL_VERIFICATION_ROUTES,
192
193
  ...CHANNEL_AVAILABILITY_ROUTES,
193
194
  ...CHANNEL_READINESS_ROUTES,
195
+ ...CHANNEL_SEND_ROUTES,
194
196
  ...BROWSER_ROUTES,
195
197
  ...BROWSER_TABS_ROUTES,
196
198
  ...BTW_ROUTES,
@@ -215,9 +217,9 @@ export const ROUTES: RouteDefinition[] = [
215
217
  ...CONVERSATION_QUERY_ROUTES,
216
218
  ...CONVERSATION_STARTER_ROUTES,
217
219
  ...ACTIVATION_ROUTES,
218
- ...DEBUG_BASH_ROUTES,
219
220
  ...DEBUG_ROUTES,
220
221
  ...DEFAULT_PROVIDER_ROUTES,
222
+ ...DESKTOP_SETUP_ROUTES,
221
223
  ...DIAGNOSTICS_ROUTES,
222
224
  ...DISK_PRESSURE_ROUTES,
223
225
  ...RESOURCE_PRESSURE_ROUTES,
@@ -22,6 +22,7 @@ import { join } from "node:path";
22
22
  import { and, desc, eq, gte, lte } from "drizzle-orm";
23
23
  import { z } from "zod";
24
24
 
25
+ import { overlayWorkspaceMcpForConfigRead } from "../../mcp/workspace-mcp-config.js";
25
26
  import { getDb, getLogsDb } from "../../persistence/db-connection.js";
26
27
  import {
27
28
  llmRequestLogs,
@@ -568,6 +569,7 @@ function readSanitizedConfig(): Record<string, unknown> | undefined {
568
569
  }
569
570
  }
570
571
 
572
+ overlayWorkspaceMcpForConfigRead(config);
571
573
  const mcpServers = asRecord(asRecord(config.mcp)?.servers);
572
574
  for (const server of Object.values(mcpServers ?? {})) {
573
575
  const transport = asRecord(asRecord(server)?.transport);
@@ -14,8 +14,7 @@
14
14
 
15
15
  import { z } from "zod";
16
16
 
17
- import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
18
- import type { McpConfig, McpServerConfig } from "../../config/schemas/mcp.js";
17
+ import type { McpServerConfig } from "../../config/schemas/mcp.js";
19
18
  import { estimateToolDefinitionTokens } from "../../context/token-estimator.js";
20
19
  import { reloadMcpServers } from "../../daemon/mcp-reload-service.js";
21
20
  import { McpClient } from "../../mcp/client.js";
@@ -31,6 +30,10 @@ import {
31
30
  deleteMcpOAuthCredentials,
32
31
  hasMcpOAuthTokens,
33
32
  } from "../../mcp/mcp-oauth-provider.js";
33
+ import {
34
+ loadWorkspaceMcpConfig,
35
+ saveWorkspaceMcpConfig,
36
+ } from "../../mcp/workspace-mcp-config.js";
34
37
  import { readPluginMcpServers } from "../../plugins/mcp-servers.js";
35
38
  import { getMcpToolsByServer } from "../../tools/registry.js";
36
39
  import { getLogger } from "../../util/logger.js";
@@ -65,6 +68,18 @@ const McpAddParams = z.object({
65
68
 
66
69
  const McpRemoveParams = z.object({ name: z.string() });
67
70
 
71
+ function persistWorkspaceMcpConfig(config: {
72
+ servers: Record<string, McpServerConfig>;
73
+ }): void {
74
+ try {
75
+ saveWorkspaceMcpConfig(config);
76
+ } catch (err) {
77
+ throw new InternalError(
78
+ err instanceof Error ? err.message : String(err),
79
+ );
80
+ }
81
+ }
82
+
68
83
  async function handleMcpAuthStart({
69
84
  body,
70
85
  }: {
@@ -76,8 +91,7 @@ async function handleMcpAuthStart({
76
91
  }> {
77
92
  const { serverId } = parseBody(McpServerIdParams, body);
78
93
 
79
- const raw = loadRawConfig();
80
- const servers = (raw.mcp as Partial<McpConfig> | undefined)?.servers ?? {};
94
+ const servers = loadWorkspaceMcpConfig().servers;
81
95
  const serverConfig = servers[serverId];
82
96
 
83
97
  if (!serverConfig) {
@@ -253,9 +267,7 @@ function detectAuthType(headers: Record<string, string>): "bearer" | "api-key" {
253
267
  async function handleMcpList(_args: {
254
268
  body?: Record<string, unknown>;
255
269
  }): Promise<{ servers: McpServerEntry[] }> {
256
- const raw = loadRawConfig();
257
- const mcpConfig = raw.mcp as Partial<McpConfig> | undefined;
258
- const servers = mcpConfig?.servers ?? {};
270
+ const servers = loadWorkspaceMcpConfig().servers;
259
271
  const configEntries = (
260
272
  Object.entries(servers) as [string, McpServerConfig][]
261
273
  ).filter(([, config]) => config && typeof config === "object");
@@ -434,18 +446,13 @@ async function handleMcpUpdate({
434
446
  }): Promise<{ updated: true }> {
435
447
  const { name, headers } = parseBody(McpUpdateParams, body);
436
448
 
437
- const raw = loadRawConfig();
438
- const mcpConfig = raw.mcp as Record<string, unknown> | undefined;
439
- const serverMap = mcpConfig?.servers as
440
- | Record<string, Record<string, unknown>>
441
- | undefined;
449
+ const servers = loadWorkspaceMcpConfig().servers;
450
+ const server = servers[name];
442
451
 
443
- if (!serverMap || !serverMap[name]) {
452
+ if (!server) {
444
453
  throw new NotFoundError(`MCP server "${name}" not found.`);
445
454
  }
446
455
 
447
- const server = serverMap[name];
448
-
449
456
  if (headers !== undefined) {
450
457
  const transport = server.transport as Record<string, unknown> | undefined;
451
458
  if (
@@ -474,7 +481,7 @@ async function handleMcpUpdate({
474
481
  }
475
482
  }
476
483
 
477
- saveRawConfig(raw);
484
+ persistWorkspaceMcpConfig({ servers });
478
485
  triggerReload("internal_mcp_update");
479
486
 
480
487
  return { updated: true };
@@ -494,7 +501,7 @@ async function handleMcpAdd({
494
501
  body,
495
502
  );
496
503
 
497
- let transport: Record<string, unknown>;
504
+ let transport: McpServerConfig["transport"];
498
505
  switch (transportType) {
499
506
  case "stdio":
500
507
  if (!command) {
@@ -517,27 +524,16 @@ async function handleMcpAdd({
517
524
  );
518
525
  }
519
526
 
520
- const raw = loadRawConfig();
521
- if (!raw.mcp) {
522
- raw.mcp = { servers: {} };
523
- }
524
- const mcpConfig = raw.mcp as Record<string, unknown>;
525
- if (!mcpConfig.servers) {
526
- mcpConfig.servers = {};
527
- }
528
- const serverMap = mcpConfig.servers as Record<string, unknown>;
527
+ const servers = { ...loadWorkspaceMcpConfig().servers };
529
528
 
530
- if (serverMap[name]) {
529
+ if (servers[name]) {
531
530
  throw new BadRequestError(
532
531
  `MCP server "${name}" already exists. Remove it first with: assistant mcp remove ${name}`,
533
532
  );
534
533
  }
535
534
 
536
- serverMap[name] = {
537
- transport,
538
- };
535
+ servers[name] = { transport };
539
536
 
540
- // Store auth headers in credential store, not config
541
537
  if (headers && Object.keys(headers).length > 0) {
542
538
  const ok = await setMcpHeaders(name, headers);
543
539
  if (!ok) {
@@ -547,7 +543,7 @@ async function handleMcpAdd({
547
543
  }
548
544
  }
549
545
 
550
- saveRawConfig(raw);
546
+ persistWorkspaceMcpConfig({ servers });
551
547
  triggerReload("internal_mcp_add");
552
548
 
553
549
  return { added: true };
@@ -564,8 +560,7 @@ async function handleMcpAuthRevoke({
564
560
  }): Promise<{ revoked: true }> {
565
561
  const { serverId } = parseBody(McpServerIdParams, body);
566
562
 
567
- const raw = loadRawConfig();
568
- const servers = (raw.mcp as Partial<McpConfig> | undefined)?.servers ?? {};
563
+ const servers = loadWorkspaceMcpConfig().servers;
569
564
  const serverConfig = servers[serverId];
570
565
 
571
566
  if (!serverConfig) {
@@ -602,32 +597,29 @@ async function handleMcpRemove({
602
597
  }): Promise<{ removed: true }> {
603
598
  const { name } = parseBody(McpRemoveParams, body);
604
599
 
605
- const raw = loadRawConfig();
606
- const mcpConfig = raw.mcp as Record<string, unknown> | undefined;
607
- const serverMap = mcpConfig?.servers as Record<string, unknown> | undefined;
600
+ const servers = { ...loadWorkspaceMcpConfig().servers };
601
+ const serverConfig = servers[name];
608
602
 
609
- if (!serverMap || !serverMap[name]) {
603
+ if (!serverConfig) {
610
604
  throw new NotFoundError(`MCP server "${name}" not found.`);
611
605
  }
612
606
 
613
- // Best-effort cleanup of credentials stored for this server
614
- const serverConfig = serverMap[name] as Record<string, unknown>;
615
- const transport = serverConfig?.transport as
616
- | Record<string, unknown>
617
- | undefined;
618
- if (transport?.type === "sse" || transport?.type === "streamable-http") {
607
+ if (
608
+ serverConfig.transport.type === "sse" ||
609
+ serverConfig.transport.type === "streamable-http"
610
+ ) {
619
611
  try {
620
612
  await Promise.all([
621
613
  deleteMcpOAuthCredentials(name),
622
614
  deleteMcpHeaders(name),
623
615
  ]);
624
616
  } catch {
625
- // Ignore — credentials may not exist
617
+ // Credentials may not exist.
626
618
  }
627
619
  }
628
620
 
629
- delete serverMap[name];
630
- saveRawConfig(raw);
621
+ delete servers[name];
622
+ persistWorkspaceMcpConfig({ servers });
631
623
  triggerReload("internal_mcp_remove");
632
624
 
633
625
  return { removed: true };
@@ -22,8 +22,8 @@ import { PassThrough, Readable } from "node:stream";
22
22
 
23
23
  import { z } from "zod";
24
24
 
25
- import { getPlatformAssistantId } from "../../config/env.js";
26
25
  import { invalidateConfigCache } from "../../config/loader.js";
26
+ import { resolvePlatformAssistantId } from "../../config/platform-identity.js";
27
27
  import { getAssistantName } from "../../daemon/identity-helpers.js";
28
28
  import { runAsyncSqlite } from "../../persistence/db-async-query.js";
29
29
  import {
@@ -236,28 +236,14 @@ interface ExportManifestInputs {
236
236
  /**
237
237
  * Resolve the `assistant.id` for an export.
238
238
  *
239
- * Mirrors `platform/client.ts`'s precedence: in-memory override (set at
240
- * daemon startup or by secret-routes) → credential store → daemon-internal
241
- * fallback. The schema requires `id` to be non-empty, so we fall back to
239
+ * In-memory identity (validate rehydration or secret-routes). The schema
240
+ * requires `id` to be non-empty, so we fall back to
242
241
  * `DAEMON_INTERNAL_ASSISTANT_ID` rather than the empty string.
243
242
  */
244
243
  async function resolveAssistantId(): Promise<string> {
245
- const inMemory = getPlatformAssistantId();
246
- if (inMemory) {
247
- return inMemory;
248
- }
249
- try {
250
- const stored = await getSecureKeyAsync(
251
- credentialKey("vellum", "platform_assistant_id"),
252
- );
253
- if (stored) {
254
- return stored;
255
- }
256
- } catch (err) {
257
- log.warn(
258
- { err },
259
- "Failed to read platform_assistant_id from credential store; falling back to daemon-internal id",
260
- );
244
+ const resolved = await resolvePlatformAssistantId();
245
+ if (resolved) {
246
+ return resolved;
261
247
  }
262
248
  return DAEMON_INTERNAL_ASSISTANT_ID;
263
249
  }
@@ -39,7 +39,17 @@ import {
39
39
  } from "@vellumai/service-contracts/platform-credential";
40
40
  import { z } from "zod";
41
41
 
42
+ import {
43
+ setPlatformAssistantId,
44
+ setPlatformBaseUrl,
45
+ setPlatformOrganizationId,
46
+ setPlatformUserId,
47
+ } from "../../config/env.js";
42
48
  import { isPlatformRemote } from "../../config/env-registry.js";
49
+ import {
50
+ resolvePlatformOrganizationId,
51
+ resolvePlatformUserId,
52
+ } from "../../config/platform-identity.js";
43
53
  import {
44
54
  registerCallbackRoute,
45
55
  resolvePlatformCallbackRegistrationContext,
@@ -289,24 +299,12 @@ async function handlePlatformStatus(
289
299
  ): Promise<PlatformStatusResponse> {
290
300
  const context = await resolvePlatformCallbackRegistrationContext();
291
301
 
292
- const [orgIdRaw, userIdRaw, webhookSecretRaw] = await Promise.all([
293
- getSecureKeyAsync(
294
- credentialKey(
295
- CREDENTIAL_KEYS.organizationId.service,
296
- CREDENTIAL_KEYS.organizationId.field,
297
- ),
298
- ),
299
- getSecureKeyAsync(
300
- credentialKey(
301
- CREDENTIAL_KEYS.userId.service,
302
- CREDENTIAL_KEYS.userId.field,
303
- ),
304
- ),
302
+ const [organizationId, userId, webhookSecretRaw] = await Promise.all([
303
+ resolvePlatformOrganizationId(),
304
+ resolvePlatformUserId(),
305
305
  getSecureKeyAsync(credentialKey("vellum", "webhook_secret")),
306
306
  ]);
307
307
 
308
- const organizationId = orgIdRaw?.trim() ?? "";
309
- const userId = userIdRaw?.trim() ?? "";
310
308
  const hasWebhookSecret = !!webhookSecretRaw;
311
309
 
312
310
  return {
@@ -467,6 +465,11 @@ async function handlePlatformDisconnect(
467
465
  );
468
466
  }
469
467
 
468
+ setPlatformBaseUrl(undefined);
469
+ setPlatformAssistantId(undefined);
470
+ setPlatformOrganizationId(undefined);
471
+ setPlatformUserId(undefined);
472
+
470
473
  // Notify connected clients
471
474
  broadcastMessage({ type: "platform_disconnected" });
472
475
 
@@ -159,3 +159,31 @@ describe("serializeConversationSummary · enabledPlugins", () => {
159
159
  expect("enabledPlugins" in summary).toBe(false);
160
160
  });
161
161
  });
162
+
163
+ describe("serializeConversationSummary · title keys", () => {
164
+ test("resolves a stored title key for the locale", () => {
165
+ const summary = serializeConversationSummary({
166
+ conversation: makeConversationRow({
167
+ title: "conversation.title.generating",
168
+ }),
169
+ displayMeta: { displayOrder: null, isPinned: false, groupId: null },
170
+ parentCache: new Map(),
171
+ isProcessing: false,
172
+ locale: "es",
173
+ });
174
+ expect(summary.title).toBe("Generando título...");
175
+ expect("titleState" in summary).toBe(false);
176
+ });
177
+
178
+ test("passes a stored display string through", () => {
179
+ const summary = serialize(
180
+ makeConversationRow({ title: "Untitled Conversation" }),
181
+ );
182
+ expect(summary.title).toBe("Untitled Conversation");
183
+ });
184
+
185
+ test("passes a custom title through", () => {
186
+ const summary = serialize(makeConversationRow({ title: "Weekly planning" }));
187
+ expect(summary.title).toBe("Weekly planning");
188
+ });
189
+ });
@@ -8,6 +8,10 @@
8
8
 
9
9
  import { parseChannelId } from "../../channels/types.js";
10
10
  import { normalizeConversationType } from "../../daemon/message-types/shared.js";
11
+ import {
12
+ resolveConversationTitle,
13
+ type SupportedLocale,
14
+ } from "../../i18n/index.js";
11
15
  import { buildChannelBindingMetadata } from "../../messaging/channel-binding-metadata.js";
12
16
  import {
13
17
  type AttentionState,
@@ -78,6 +82,7 @@ interface ForkLineage {
78
82
  function buildForkLineage(
79
83
  conversation: ConversationRow,
80
84
  parentCache: Map<string, ConversationRow | null>,
85
+ locale?: SupportedLocale,
81
86
  ): ForkLineage {
82
87
  const parentConversationId = conversation.forkParentConversationId;
83
88
  const parentMessageId = conversation.forkParentMessageId;
@@ -101,7 +106,7 @@ function buildForkLineage(
101
106
  forkParent: {
102
107
  conversationId: parentConversationId,
103
108
  messageId: parentMessageId,
104
- title: parentConversation.title ?? "Untitled",
109
+ title: resolveConversationTitle(parentConversation.title, locale),
105
110
  },
106
111
  };
107
112
  }
@@ -178,6 +183,11 @@ export function serializeConversationSummary(params: {
178
183
  * with no daemon-store coupling.
179
184
  */
180
185
  isProcessing: boolean;
186
+ /**
187
+ * Locale used to resolve stored title keys. Omitted locale is English
188
+ * inside {@link resolveConversationTitle}.
189
+ */
190
+ locale?: SupportedLocale;
181
191
  }) {
182
192
  const {
183
193
  conversation,
@@ -186,14 +196,15 @@ export function serializeConversationSummary(params: {
186
196
  displayMeta,
187
197
  parentCache,
188
198
  isProcessing,
199
+ locale,
189
200
  } = params;
190
201
  const originChannel = parseChannelId(conversation.originChannel);
191
202
  const assistantAttention = buildAssistantAttention(attentionState);
192
- const forkLineage = buildForkLineage(conversation, parentCache);
203
+ const forkLineage = buildForkLineage(conversation, parentCache, locale);
193
204
 
194
205
  return {
195
206
  id: conversation.id,
196
- title: conversation.title ?? "Untitled",
207
+ title: resolveConversationTitle(conversation.title, locale),
197
208
  createdAt: conversation.createdAt,
198
209
  updatedAt: conversation.updatedAt,
199
210
  lastMessageAt: conversation.lastMessageAt,
@@ -253,6 +264,7 @@ export function serializeConversationSummary(params: {
253
264
  */
254
265
  export function buildConversationDetailResponse(
255
266
  conversationId: string,
267
+ locale?: SupportedLocale,
256
268
  ): { conversation: ReturnType<typeof serializeConversationSummary> } | null {
257
269
  const conversation = getConversation(conversationId);
258
270
  if (!conversation || conversation.conversationType === "private") {
@@ -274,6 +286,7 @@ export function buildConversationDetailResponse(
274
286
  // Checks in-memory flag first (hot path), falls back to the
275
287
  // persisted `processing_started_at` column for cold conversations.
276
288
  isProcessing: isConversationProcessing(conversation.id),
289
+ locale,
277
290
  }),
278
291
  };
279
292
  }
@@ -4,6 +4,7 @@ import {
4
4
  conversationMetadataSyncTag,
5
5
  SYNC_TAGS,
6
6
  } from "../../daemon/message-types/sync.js";
7
+ import { resolveConversationTitle } from "../../i18n/index.js";
7
8
  import { syncAvatarToPlatform } from "../../platform/sync-avatar.js";
8
9
  import { getAvatarImagePath } from "../../util/platform.js";
9
10
  import { broadcastMessage } from "../assistant-event-hub.js";
@@ -257,7 +258,7 @@ export function publishConversationTitleChanged(
257
258
  {
258
259
  type: "conversation_title_updated",
259
260
  conversationId,
260
- title,
261
+ title: resolveConversationTitle(title),
261
262
  },
262
263
  conversationId,
263
264
  );
@@ -195,7 +195,7 @@ async function main(): Promise<void> {
195
195
  //
196
196
  // Bounded by MCP_STARTUP_GRACE_MS: unreachable servers must not hold the
197
197
  // schedules that do not use them. The connect runs on past the deadline.
198
- const mcpStartup = startConfiguredMcpServers(getConfig().mcp);
198
+ const mcpStartup = startConfiguredMcpServers();
199
199
  await Promise.race([
200
200
  mcpStartup,
201
201
  // Unref'd so a connect that beats the deadline leaves nothing pending.