@vellumai/assistant 0.12.1-staging.1 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) 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/openapi.yaml +1 -291
  5. package/package.json +1 -1
  6. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  7. package/src/__tests__/app-compiler.test.ts +0 -6
  8. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  9. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  10. package/src/__tests__/app-source-watcher.test.ts +218 -0
  11. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  12. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  13. package/src/__tests__/computer-use-tools.test.ts +5 -17
  14. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  15. package/src/__tests__/config-schema.test.ts +29 -28
  16. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  17. package/src/__tests__/config-watcher.test.ts +8 -23
  18. package/src/__tests__/connection-model-compat.test.ts +2 -2
  19. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  20. package/src/__tests__/conversation-pairing.test.ts +0 -26
  21. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  22. package/src/__tests__/conversation-queue.test.ts +207 -48
  23. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  24. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  25. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  26. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  27. package/src/__tests__/conversation-title-service.test.ts +10 -14
  28. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  29. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  30. package/src/__tests__/credential-record-retire.test.ts +1 -171
  31. package/src/__tests__/credential-routes.test.ts +3 -212
  32. package/src/__tests__/credential-security-invariants.test.ts +1 -2
  33. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  34. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  35. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  36. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  37. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  38. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  39. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  40. package/src/__tests__/host-file-proxy.test.ts +23 -11
  41. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  42. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  43. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  44. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  45. package/src/__tests__/mcp-health-check.test.ts +11 -8
  46. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  47. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  48. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  49. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  51. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  52. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  53. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  54. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  55. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  56. package/src/__tests__/model-intents.test.ts +0 -3
  57. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  58. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  59. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  60. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  61. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  62. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  63. package/src/__tests__/speaker-identification.test.ts +56 -0
  64. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  65. package/src/__tests__/subagent-tools.test.ts +2 -3
  66. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  67. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  68. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  69. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  70. package/src/__tests__/workspace-git-service.test.ts +0 -26
  71. package/src/acp/__tests__/agent-process.test.ts +4 -258
  72. package/src/acp/__tests__/auth-required.test.ts +0 -96
  73. package/src/acp/__tests__/client-handler.test.ts +2 -90
  74. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  75. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  76. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  77. package/src/acp/__tests__/session-manager.test.ts +4 -895
  78. package/src/acp/agent-process.test.ts +1 -4
  79. package/src/acp/agent-process.ts +9 -94
  80. package/src/acp/auth-required.ts +1 -50
  81. package/src/acp/auto-install.test.ts +7 -9
  82. package/src/acp/client-handler.ts +3 -16
  83. package/src/acp/resolve-agent.test.ts +6 -220
  84. package/src/acp/resolve-agent.ts +23 -88
  85. package/src/acp/session-manager.test.ts +3 -196
  86. package/src/acp/session-manager.ts +18 -441
  87. package/src/acp/types.ts +5 -50
  88. package/src/api/events/app-files-changed.ts +3 -4
  89. package/src/api/index.ts +0 -6
  90. package/src/apps/app-store.ts +1 -1
  91. package/src/bundler/app-compiler.ts +0 -15
  92. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  93. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  94. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  95. package/src/calls/call-constants.ts +4 -24
  96. package/src/calls/call-controller.ts +199 -499
  97. package/src/calls/call-transport.ts +0 -16
  98. package/src/calls/media-stream-output.ts +0 -32
  99. package/src/calls/media-stream-server.ts +30 -82
  100. package/src/calls/media-stream-stt-session.ts +0 -15
  101. package/src/calls/resolve-call-tts-provider.ts +2 -33
  102. package/src/calls/speaker-identification.ts +233 -0
  103. package/src/calls/tts-call-strategy.ts +5 -9
  104. package/src/calls/types.ts +0 -2
  105. package/src/calls/voice-control-protocol.ts +21 -53
  106. package/src/calls/voice-session-bridge.ts +46 -59
  107. package/src/calls/voice-triage-escalate.ts +56 -175
  108. package/src/cli/commands/apps.help.ts +13 -67
  109. package/src/cli/commands/apps.ts +0 -183
  110. package/src/cli/commands/channels/index.help.ts +2 -70
  111. package/src/cli/commands/channels/index.ts +3 -16
  112. package/src/cli/commands/credentials.help.ts +2 -6
  113. package/src/cli/commands/credentials.ts +2 -2
  114. package/src/cli/commands/mcp.help.ts +8 -8
  115. package/src/cli/commands/mcp.ts +1 -1
  116. package/src/cli/commands/roadmap.help.ts +1 -5
  117. package/src/cli/commands/roadmap.ts +1 -1
  118. package/src/cli/lib/bundled-marketplace.json +90 -103
  119. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  120. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  121. package/src/config/__tests__/platform-rehydration.test.ts +0 -93
  122. package/src/config/acp-defaults.test.ts +3 -13
  123. package/src/config/acp-defaults.ts +6 -23
  124. package/src/config/acp-schema.ts +2 -30
  125. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  126. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  127. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  128. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  129. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  130. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  131. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  132. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  133. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  134. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  135. package/src/config/default-profile-catalog.ts +1 -1
  136. package/src/config/env.ts +2 -2
  137. package/src/config/feature-flag-registry.json +12 -8
  138. package/src/config/memory-v3-gate.test.ts +0 -31
  139. package/src/config/memory-v3-gate.ts +16 -20
  140. package/src/config/platform-rehydration.ts +21 -75
  141. package/src/config/schema.ts +2 -2
  142. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  143. package/src/config/schemas/calls.ts +28 -1
  144. package/src/config/schemas/live-voice.ts +173 -1
  145. package/src/config/schemas/mcp.ts +1 -1
  146. package/src/config/schemas/memory-retrospective.ts +0 -10
  147. package/src/config/schemas/skills.ts +14 -0
  148. package/src/credential-execution/process-manager.test.ts +19 -18
  149. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  150. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  151. package/src/daemon/app-source-watcher.ts +218 -0
  152. package/src/daemon/config-watcher.ts +194 -13
  153. package/src/daemon/conversation-process.ts +64 -35
  154. package/src/daemon/conversation-skill-tools.ts +31 -13
  155. package/src/daemon/conversation-store.ts +1 -1
  156. package/src/daemon/conversation-surfaces.ts +8 -10
  157. package/src/daemon/conversation-tool-setup.ts +43 -37
  158. package/src/daemon/conversation.ts +3 -0
  159. package/src/daemon/host-bash-proxy.ts +12 -8
  160. package/src/daemon/host-browser-proxy.ts +1 -15
  161. package/src/daemon/host-file-proxy.ts +10 -8
  162. package/src/daemon/host-transfer-proxy.ts +18 -14
  163. package/src/daemon/lifecycle.ts +5 -0
  164. package/src/daemon/mcp-reload-service.ts +5 -5
  165. package/src/daemon/message-types/sync.ts +0 -1
  166. package/src/daemon/providers-setup.ts +1 -1
  167. package/src/daemon/shutdown-handlers.ts +9 -1
  168. package/src/daemon/skill-memory-refresh.ts +0 -64
  169. package/src/daemon/tool-side-effects.ts +18 -7
  170. package/src/hooks/types.ts +0 -33
  171. package/src/inbound/platform-callback-registration.ts +5 -4
  172. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  173. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  174. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  175. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  176. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  177. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  178. package/src/live-voice/live-voice-session.ts +693 -213
  179. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  180. package/src/live-voice/protocol.ts +2 -0
  181. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  182. package/src/mcp/effective-config.ts +3 -3
  183. package/src/mcp/mcp-header-store.ts +45 -29
  184. package/src/mcp/startup.ts +4 -8
  185. package/src/messaging/provider.ts +3 -10
  186. package/src/messaging/providers/callback-routing.ts +0 -18
  187. package/src/messaging/providers/channel-transport.ts +0 -56
  188. package/src/messaging/providers/discord/transport.ts +0 -17
  189. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  190. package/src/messaging/providers/slack/adapter.ts +44 -7
  191. package/src/messaging/providers/slack/transport.ts +0 -16
  192. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  193. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  194. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  195. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  196. package/src/messaging/providers/whatsapp/client.ts +29 -0
  197. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  198. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  199. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  200. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  201. package/src/notifications/assistant-reply-producer.ts +3 -8
  202. package/src/notifications/conversation-pairing.ts +16 -36
  203. package/src/notifications/delivered-post-record.ts +1 -11
  204. package/src/notifications/home-feed-side-effect.ts +7 -55
  205. package/src/outbound-proxy/certs.ts +5 -3
  206. package/src/permissions/checker.test.ts +6 -11
  207. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  208. package/src/persistence/conversation-crud.ts +63 -71
  209. package/src/persistence/conversation-lineage.ts +4 -12
  210. package/src/persistence/conversation-title-service.ts +26 -9
  211. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  212. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  213. package/src/persistence/message-reads.ts +0 -30
  214. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  215. package/src/persistence/schema/memory-core.ts +0 -5
  216. package/src/platform/client.test.ts +0 -2
  217. package/src/platform/client.ts +10 -2
  218. package/src/plugin-api/constants.ts +0 -2
  219. package/src/plugin-api/index.ts +0 -1
  220. package/src/plugin-api/types.ts +0 -2
  221. package/src/plugins/defaults/index.ts +0 -2
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  224. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  225. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  226. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  227. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  228. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  229. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  230. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  231. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  232. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  233. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  234. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  235. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  236. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  237. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  238. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  239. package/src/plugins/defaults/memory/startup.ts +1 -6
  240. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  241. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  242. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  243. package/src/plugins/defaults/memory/worker.ts +0 -6
  244. package/src/plugins/mcp-servers.ts +75 -15
  245. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  246. package/src/providers/gemini/client.ts +23 -9
  247. package/src/providers/model-catalog.ts +0 -34
  248. package/src/providers/model-intents.ts +5 -10
  249. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  250. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  251. package/src/providers/openai/chat-completions-provider.ts +78 -192
  252. package/src/providers/openai/codex-models.ts +4 -0
  253. package/src/runtime/AGENTS.md +0 -2
  254. package/src/runtime/auth/same-actor.ts +3 -38
  255. package/src/runtime/http-server.ts +151 -160
  256. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  257. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  258. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  259. package/src/runtime/routes/acp-routes.test.ts +0 -15
  260. package/src/runtime/routes/acp-routes.ts +9 -72
  261. package/src/runtime/routes/app-management-routes.ts +0 -73
  262. package/src/runtime/routes/app-routes.ts +1 -1
  263. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  264. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  265. package/src/runtime/routes/credential-routes.ts +10 -19
  266. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  267. package/src/runtime/routes/index.ts +0 -4
  268. package/src/runtime/routes/log-export-routes.ts +0 -2
  269. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  270. package/src/runtime/routes/migration-routes.ts +21 -8
  271. package/src/runtime/routes/platform-routes.ts +15 -18
  272. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  273. package/src/runtime/services/conversation-serializer.ts +3 -16
  274. package/src/runtime/sync/resource-sync-events.ts +1 -2
  275. package/src/schedule/worker.ts +1 -1
  276. package/src/security/ces-rpc-record-backend.ts +0 -7
  277. package/src/subagent/consult-context.ts +25 -12
  278. package/src/tools/acp/list-agents.test.ts +1 -3
  279. package/src/tools/acp/spawn.test.ts +11 -146
  280. package/src/tools/acp/spawn.ts +3 -21
  281. package/src/tools/acp/status.ts +3 -40
  282. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  283. package/src/tools/capability-offer.test.ts +2 -22
  284. package/src/tools/capability-offer.ts +1 -16
  285. package/src/tools/computer-use/definitions.ts +2 -4
  286. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  287. package/src/tools/credentials/metadata-store.ts +0 -100
  288. package/src/tools/host-filesystem/edit.test.ts +1 -2
  289. package/src/tools/host-filesystem/edit.ts +2 -2
  290. package/src/tools/host-filesystem/read.test.ts +1 -2
  291. package/src/tools/host-filesystem/read.ts +2 -2
  292. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  293. package/src/tools/host-filesystem/transfer.ts +2 -2
  294. package/src/tools/host-filesystem/write.test.ts +1 -2
  295. package/src/tools/host-filesystem/write.ts +2 -2
  296. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  297. package/src/tools/host-terminal/host-shell.ts +20 -4
  298. package/src/tools/policy-context.ts +7 -6
  299. package/src/tools/subagent/spawn.ts +8 -5
  300. package/src/tools/watch/watch-retro-report.ts +8 -0
  301. package/src/util/__tests__/language-subtag.test.ts +1 -23
  302. package/src/util/fs-watcher-error.ts +36 -0
  303. package/src/util/language-subtag.ts +0 -16
  304. package/src/util/platform.ts +0 -8
  305. package/src/workspace/git-service.ts +54 -5
  306. package/src/workspace/migrations/registry.ts +0 -6
  307. package/scripts/smoke-desktop-window-theme.ts +0 -186
  308. package/src/__tests__/git-porcelain.test.ts +0 -47
  309. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  310. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  311. package/src/__tests__/resolve-app.test.ts +0 -135
  312. package/src/__tests__/source-fingerprint.test.ts +0 -184
  313. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  314. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  315. package/src/acp/model-config.test.ts +0 -166
  316. package/src/acp/model-config.ts +0 -138
  317. package/src/api/events/acp-session-model-update.test.ts +0 -88
  318. package/src/api/events/acp-session-model-update.ts +0 -46
  319. package/src/apps/resolve-app.ts +0 -81
  320. package/src/apps/source-fingerprint.ts +0 -266
  321. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  322. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  323. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  324. package/src/calls/barge-in-guard.ts +0 -118
  325. package/src/calls/voice-leg-coordinator.ts +0 -189
  326. package/src/calls/voice-progress-cadence.ts +0 -423
  327. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  328. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  329. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  330. package/src/cli/commands/channels/send.ts +0 -128
  331. package/src/config/__tests__/platform-identity.test.ts +0 -117
  332. package/src/config/acp-agent-write.ts +0 -43
  333. package/src/config/acp-schema.test.ts +0 -81
  334. package/src/config/platform-identity.ts +0 -144
  335. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  336. package/src/config/schemas/voice.ts +0 -150
  337. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  338. package/src/daemon/app-change-notify.ts +0 -45
  339. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  340. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  341. package/src/desktop/desktop-chrome-policy.ts +0 -42
  342. package/src/desktop/desktop-dependencies.test.ts +0 -102
  343. package/src/desktop/desktop-dependencies.ts +0 -306
  344. package/src/desktop/desktop-feature.test.ts +0 -18
  345. package/src/desktop/desktop-feature.ts +0 -19
  346. package/src/desktop/desktop-panel-config.test.ts +0 -135
  347. package/src/desktop/desktop-panel-config.ts +0 -149
  348. package/src/desktop/desktop-session-manager.test.ts +0 -627
  349. package/src/desktop/desktop-session-manager.ts +0 -814
  350. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  351. package/src/desktop/desktop-stream-bridge.ts +0 -214
  352. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  353. package/src/desktop/desktop-wallpaper.ts +0 -113
  354. package/src/desktop/desktop-window-theme.ts +0 -170
  355. package/src/i18n/AGENTS.md +0 -22
  356. package/src/i18n/__tests__/i18n.test.ts +0 -108
  357. package/src/i18n/index.ts +0 -16
  358. package/src/i18n/locales.ts +0 -93
  359. package/src/i18n/messages.ts +0 -61
  360. package/src/i18n/title.ts +0 -25
  361. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  362. package/src/mcp/spec-schema.ts +0 -111
  363. package/src/mcp/workspace-mcp-config.ts +0 -188
  364. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  365. package/src/persistence/conversation-title-placeholders.ts +0 -23
  366. package/src/persistence/message-cursor.ts +0 -78
  367. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  368. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  369. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  370. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  371. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  372. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  373. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  374. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  375. package/src/providers/gemini-thought-signature.test.ts +0 -63
  376. package/src/providers/gemini-thought-signature.ts +0 -44
  377. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  378. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  379. package/src/providers/openai/google-thought-signature.ts +0 -320
  380. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  381. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  382. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  383. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  384. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  385. package/src/runtime/channel-send.ts +0 -316
  386. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  387. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  388. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  389. package/src/runtime/routes/channel-send-routes.ts +0 -85
  390. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  391. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  392. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  393. package/src/security/ces-http-record-backend.ts +0 -120
  394. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  395. package/src/skills/workspace-skill-md-signature.ts +0 -50
  396. package/src/tools/acp/status.test.ts +0 -94
  397. package/src/util/ca-bundle.ts +0 -13
  398. package/src/workspace/git-porcelain.ts +0 -66
  399. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  400. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  401. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  402. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  403. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  404. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -104,7 +104,6 @@ mock.module("../runtime/sync/resource-sync-events.js", () => ({
104
104
  publishConversationTitleChanged: mockPublishConversationTitleChanged,
105
105
  }));
106
106
 
107
- import { MESSAGE_KEYS } from "../i18n/index.js";
108
107
  import {
109
108
  AUTO_TITLE_DETERMINISTIC,
110
109
  AUTO_TITLE_LLM,
@@ -310,10 +309,10 @@ describe("conversation-title-service", () => {
310
309
  userMessage: "so about that t-shirt...",
311
310
  });
312
311
 
313
- expect(result.title).toBe(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED);
312
+ expect(result.title).toBe("Untitled Conversation");
314
313
  expect(mockUpdateConversationTitle).toHaveBeenCalledWith(
315
314
  "conv-1",
316
- MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED,
315
+ "Untitled Conversation",
317
316
  AUTO_TITLE_DETERMINISTIC,
318
317
  );
319
318
  });
@@ -334,7 +333,7 @@ describe("conversation-title-service", () => {
334
333
  userMessage: "something",
335
334
  });
336
335
 
337
- expect(result.title).toBe(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED);
336
+ expect(result.title).toBe("Untitled Conversation");
338
337
  });
339
338
 
340
339
  // The core bug this PR fixes: weak title models emit their reasoning or
@@ -362,10 +361,10 @@ describe("conversation-title-service", () => {
362
361
  userMessage: "hey baby",
363
362
  });
364
363
 
365
- expect(result.title).toBe(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED);
364
+ expect(result.title).toBe("Untitled Conversation");
366
365
  expect(mockUpdateConversationTitle).toHaveBeenCalledWith(
367
366
  "conv-1",
368
- MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED,
367
+ "Untitled Conversation",
369
368
  AUTO_TITLE_DETERMINISTIC,
370
369
  );
371
370
  });
@@ -424,7 +423,7 @@ describe("conversation-title-service", () => {
424
423
  userMessage: "x",
425
424
  });
426
425
 
427
- expect(result.title).toBe(MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED);
426
+ expect(result.title).toBe("Untitled Conversation");
428
427
  });
429
428
 
430
429
  test("regeneration skips LLM call when recent messages have no extractable text", async () => {
@@ -516,17 +515,17 @@ describe("conversation-title-service", () => {
516
515
  });
517
516
 
518
517
  expect(provider.sendMessage).not.toHaveBeenCalled();
519
- expect(result).toEqual({ title: MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED, updated: true });
518
+ expect(result).toEqual({ title: "Untitled Conversation", updated: true });
520
519
  expect(mockUpdateConversationTitle).toHaveBeenCalledWith(
521
520
  "conv-1",
522
- MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED,
521
+ "Untitled Conversation",
523
522
  AUTO_TITLE_DETERMINISTIC,
524
523
  );
525
524
  });
526
525
 
527
526
  test("keeps a background job's deterministic title when the model declines", async () => {
528
527
  // The model answered with prose the normalizer rejects, so there is no
529
- // generated title. Settling for the untitled fallback would name the
528
+ // generated title. Settling for "Untitled Conversation" would name the
530
529
  // job's work less well than the bootstrap title already does.
531
530
  mockGetConversation.mockImplementation(() => ({
532
531
  title: "Memory consolidation",
@@ -585,9 +584,6 @@ describe("conversation-title-service", () => {
585
584
  expect(content).not.toContain("Generate a very short title");
586
585
  expect(content).not.toContain("do NOT respond");
587
586
  expect(options.systemPrompt).toContain("Do NOT respond");
588
- expect(options.systemPrompt).toContain(
589
- "same language as the conversation content",
590
- );
591
587
  });
592
588
 
593
589
  test("queueGenerateConversationTitle serializes concurrent calls", async () => {
@@ -665,7 +661,7 @@ describe("conversation-title-service", () => {
665
661
  ).find((c) => c[0] === "conv-1");
666
662
  expect(firstUpdate).toEqual([
667
663
  "conv-1",
668
- MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED,
664
+ "Untitled Conversation",
669
665
  AUTO_TITLE_DETERMINISTIC,
670
666
  ]);
671
667
  const secondUpdate = (
@@ -7,8 +7,8 @@
7
7
  * app_id from the executor's typed `resolvedAppId` side channel — and that
8
8
  * non-hooked tools (app_file_edit, app_file_write) do not trigger side effects.
9
9
  *
10
- * File-change detection for file_write/file_edit is not hooked: compile with
11
- * `app_refresh` or `assistant apps refresh` after a batch of edits.
10
+ * File-change detection for file_write/file_edit is handled by
11
+ * AppSourceWatcher (see app-source-watcher.test.ts).
12
12
  */
13
13
 
14
14
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -50,7 +50,9 @@ mock.module("../tools/browser/browser-screencast.js", () => ({
50
50
  registerConversationSender: mock(() => {}),
51
51
  }));
52
52
 
53
- // Stub app-store functions used by other modules (e.g. conversation-surfaces)
53
+ // Stub app-store functions used by other modules (e.g. app-source-watcher,
54
+ // conversation-surfaces) so tool-side-effects' hooks can run without touching
55
+ // the real app store during tests.
54
56
  mock.module("../apps/app-store.js", () => ({
55
57
  getApp: mock(() => null),
56
58
  getAppDirPath: mock(() => "/tmp/test-apps/dummy"),
@@ -354,57 +354,6 @@ describe("createToolExecutor isInteractive threading", () => {
354
354
  });
355
355
  });
356
356
 
357
- describe("createToolExecutor unattended host-tool gate", () => {
358
- test("rejects a direct host tool in a background turn before dispatch", async () => {
359
- const { executor, calls } = makeCapturingExecutor();
360
- const toolFn = makeToolFn(
361
- executor,
362
- makeCtx({ currentTurnIsNonInteractive: true, transportInterface: "web" }),
363
- );
364
-
365
- const result = await toolFn("host_bash", { command: "pwd" });
366
-
367
- expect(result).toMatchObject({ isError: true });
368
- expect(result.content).toContain("requires an interactive user turn");
369
- expect(calls).toHaveLength(0);
370
- });
371
-
372
- test("rejects a skill-dispatched host tool in a background turn before dispatch", async () => {
373
- const { executor, calls } = makeCapturingExecutor();
374
- const toolFn = makeToolFn(
375
- executor,
376
- makeCtx({ currentTurnIsNonInteractive: true, transportInterface: "web" }),
377
- );
378
-
379
- const result = await toolFn("skill_execute", {
380
- tool: "host_file_read",
381
- input: { path: "/tmp/a" },
382
- activity: "testing",
383
- });
384
-
385
- expect(result).toMatchObject({ isError: true });
386
- expect(result.content).toContain("requires an interactive user turn");
387
- expect(calls).toHaveLength(0);
388
- });
389
-
390
- test("dispatches a host tool during an interactive turn", async () => {
391
- const { executor, calls } = makeCapturingExecutor();
392
- const toolFn = makeToolFn(
393
- executor,
394
- makeCtx({
395
- currentTurnIsNonInteractive: false,
396
- transportInterface: "web",
397
- }),
398
- );
399
-
400
- const result = await toolFn("host_bash", { command: "pwd" });
401
-
402
- expect(result).toMatchObject({ content: "ok", isError: false });
403
- expect(calls).toHaveLength(1);
404
- expect(calls[0].name).toBe("host_bash");
405
- });
406
- });
407
-
408
357
  describe("createToolExecutor channel-permission coordinate threading", () => {
409
358
  beforeEach(() => {
410
359
  mockBindingExternalChatId = null;
@@ -8,10 +8,8 @@ import type { CredentialRecord } from "@vellumai/service-contracts/credential-rp
8
8
  import type { CredentialRecordBackend } from "../security/ces-rpc-record-backend.js";
9
9
  import {
10
10
  _ensureCesRecordsLoaded,
11
- _setHttpRecordBackendForTests,
12
11
  _setMetadataPath,
13
12
  getCredentialMetadata,
14
- listCredentialRecordsLive,
15
13
  setCredentialRecordBackend,
16
14
  upsertCredentialMetadata,
17
15
  } from "../tools/credentials/metadata-store.js";
@@ -37,9 +35,7 @@ function leftoverContents(): string {
37
35
  return readFileSync(leftoverPath(), "utf-8");
38
36
  }
39
37
 
40
- function makeBackend(
41
- name?: string,
42
- ): CredentialRecordBackend & {
38
+ function makeBackend(): CredentialRecordBackend & {
43
39
  store: Map<string, CredentialRecord>;
44
40
  bulkSetCalls: number;
45
41
  listCalls: number;
@@ -50,7 +46,6 @@ function makeBackend(
50
46
  bulkSetCalls: number;
51
47
  listCalls: number;
52
48
  } = {
53
- name,
54
49
  store,
55
50
  bulkSetCalls: 0,
56
51
  listCalls: 0,
@@ -88,7 +83,6 @@ function makeBackend(
88
83
  describe("CES credential record cache", () => {
89
84
  beforeEach(() => {
90
85
  setCredentialRecordBackend(undefined);
91
- _setHttpRecordBackendForTests(null);
92
86
  _setMetadataPath(null);
93
87
  const leftover = leftoverPath();
94
88
  if (existsSync(leftover)) {
@@ -98,7 +92,6 @@ describe("CES credential record cache", () => {
98
92
 
99
93
  afterEach(() => {
100
94
  setCredentialRecordBackend(undefined);
101
- _setHttpRecordBackendForTests(null);
102
95
  _setMetadataPath(null);
103
96
  const leftover = leftoverPath();
104
97
  if (existsSync(leftover)) {
@@ -225,169 +218,6 @@ describe("CES credential record cache", () => {
225
218
  await _ensureCesRecordsLoaded();
226
219
  });
227
220
 
228
- test("live catalog list returns CES records without the in-process cache", async () => {
229
- const backend = makeBackend();
230
- const record: CredentialRecord = {
231
- credentialId: "cred-live",
232
- service: "github",
233
- field: "token",
234
- allowedTools: ["bash"],
235
- allowedDomains: [],
236
- createdAt: 1,
237
- updatedAt: 2,
238
- };
239
- backend.store.set(credentialKey("github", "token"), record);
240
- setCredentialRecordBackend(backend);
241
-
242
- const live = await listCredentialRecordsLive();
243
- expect(live.unreachable).toBe(false);
244
- expect(live.records).toHaveLength(1);
245
- expect(live.records[0]?.credentialId).toBe("cred-live");
246
- expect(getCredentialMetadata("github", "token")).toBeUndefined();
247
- });
248
-
249
- test("live catalog list reports unreachable when CES list fails", async () => {
250
- const backend = makeBackend();
251
- backend.list = async () => null;
252
- setCredentialRecordBackend(backend);
253
-
254
- const live = await listCredentialRecordsLive();
255
- expect(live.unreachable).toBe(true);
256
- expect(live.records).toEqual([]);
257
- });
258
-
259
- test("live catalog list reports unreachable when no record backend is attached", async () => {
260
- const live = await listCredentialRecordsLive();
261
- expect(live.unreachable).toBe(true);
262
- expect(live.records).toEqual([]);
263
- });
264
-
265
- test("live catalog list fails over to CES HTTP when CES RPC list fails", async () => {
266
- const rpc = makeBackend("ces-rpc");
267
- rpc.list = async () => null;
268
- setCredentialRecordBackend(rpc);
269
-
270
- const http = makeBackend("ces-http");
271
- const record: CredentialRecord = {
272
- credentialId: "cred-http",
273
- service: "github",
274
- field: "token",
275
- allowedTools: ["bash"],
276
- allowedDomains: [],
277
- createdAt: 1,
278
- updatedAt: 2,
279
- };
280
- http.store.set(credentialKey("github", "token"), record);
281
- _setHttpRecordBackendForTests(http);
282
-
283
- const live = await listCredentialRecordsLive();
284
- expect(live.unreachable).toBe(false);
285
- expect(live.records).toHaveLength(1);
286
- expect(live.records[0]?.credentialId).toBe("cred-http");
287
- expect(http.listCalls).toBe(1);
288
- });
289
-
290
- test("live catalog list fails over to CES HTTP when CES RPC is unavailable", async () => {
291
- const rpc = makeBackend("ces-rpc");
292
- rpc.isAvailable = () => false;
293
- setCredentialRecordBackend(rpc);
294
-
295
- const http = makeBackend("ces-http");
296
- const record: CredentialRecord = {
297
- credentialId: "cred-http-down-rpc",
298
- service: "github",
299
- field: "token",
300
- allowedTools: ["bash"],
301
- allowedDomains: [],
302
- createdAt: 1,
303
- updatedAt: 2,
304
- };
305
- http.store.set(credentialKey("github", "token"), record);
306
- _setHttpRecordBackendForTests(http);
307
-
308
- const live = await listCredentialRecordsLive();
309
- expect(live.unreachable).toBe(false);
310
- expect(live.records[0]?.credentialId).toBe("cred-http-down-rpc");
311
- });
312
-
313
- test("live catalog list uses CES HTTP when no record backend is attached", async () => {
314
- const http = makeBackend("ces-http");
315
- const record: CredentialRecord = {
316
- credentialId: "cred-http-only",
317
- service: "github",
318
- field: "token",
319
- allowedTools: ["bash"],
320
- allowedDomains: [],
321
- createdAt: 1,
322
- updatedAt: 2,
323
- };
324
- http.store.set(credentialKey("github", "token"), record);
325
- _setHttpRecordBackendForTests(http);
326
-
327
- const live = await listCredentialRecordsLive();
328
- expect(live.unreachable).toBe(false);
329
- expect(live.records[0]?.credentialId).toBe("cred-http-only");
330
- });
331
-
332
- test("live catalog list does not fail over when CES RPC returns an empty catalog", async () => {
333
- const rpc = makeBackend("ces-rpc");
334
- setCredentialRecordBackend(rpc);
335
-
336
- const http = makeBackend("ces-http");
337
- http.store.set(credentialKey("github", "token"), {
338
- credentialId: "cred-http-ignored",
339
- service: "github",
340
- field: "token",
341
- allowedTools: ["bash"],
342
- allowedDomains: [],
343
- createdAt: 1,
344
- updatedAt: 2,
345
- });
346
- _setHttpRecordBackendForTests(http);
347
-
348
- const live = await listCredentialRecordsLive();
349
- expect(live.unreachable).toBe(false);
350
- expect(live.records).toEqual([]);
351
- expect(http.listCalls).toBe(0);
352
- });
353
-
354
- test("live catalog list does not fail over for a non-RPC record backend", async () => {
355
- const backend = makeBackend();
356
- backend.list = async () => null;
357
- setCredentialRecordBackend(backend);
358
-
359
- const http = makeBackend("ces-http");
360
- http.store.set(credentialKey("github", "token"), {
361
- credentialId: "cred-http-ignored",
362
- service: "github",
363
- field: "token",
364
- allowedTools: ["bash"],
365
- allowedDomains: [],
366
- createdAt: 1,
367
- updatedAt: 2,
368
- });
369
- _setHttpRecordBackendForTests(http);
370
-
371
- const live = await listCredentialRecordsLive();
372
- expect(live.unreachable).toBe(true);
373
- expect(live.records).toEqual([]);
374
- expect(http.listCalls).toBe(0);
375
- });
376
-
377
- test("live catalog list reports unreachable when CES RPC and HTTP both fail", async () => {
378
- const rpc = makeBackend("ces-rpc");
379
- rpc.list = async () => null;
380
- setCredentialRecordBackend(rpc);
381
-
382
- const http = makeBackend("ces-http");
383
- http.list = async () => null;
384
- _setHttpRecordBackendForTests(http);
385
-
386
- const live = await listCredentialRecordsLive();
387
- expect(live.unreachable).toBe(true);
388
- expect(live.records).toEqual([]);
389
- });
390
-
391
221
  test("upsert write-throughs to CES without updating leftover metadata.json", async () => {
392
222
  const leftoverRecord: CredentialRecord = {
393
223
  credentialId: "cred-leftover",
@@ -1,10 +1,6 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
- import {
4
- BadRequestError,
5
- ForbiddenError,
6
- InternalError,
7
- } from "../runtime/routes/errors.js";
3
+ import { BadRequestError, ForbiddenError } from "../runtime/routes/errors.js";
8
4
  import type { CredentialMetadata } from "../tools/credentials/metadata-store.js";
9
5
 
10
6
  // ---------------------------------------------------------------------------
@@ -19,8 +15,6 @@ let disconnectedProviders: string[];
19
15
  let credentialIdCounter: number;
20
16
  let scrubbedValues: string[];
21
17
  let scrubRejects: boolean;
22
- let liveRecordsUnreachable: boolean;
23
- let secretGetUnreachable: boolean;
24
18
 
25
19
  function metaKey(service: string, field: string): string {
26
20
  return `${service}:${field}`;
@@ -55,7 +49,7 @@ mock.module("../security/secure-keys.js", () => ({
55
49
  getSecureKeyAsync: mock(async (key: string) => secureStore.get(key)),
56
50
  getSecureKeyResultAsync: mock(async (key: string) => ({
57
51
  value: secureStore.get(key),
58
- unreachable: secretGetUnreachable,
52
+ unreachable: false,
59
53
  })),
60
54
  deleteSecureKeyAsync: mock(async (key: string) =>
61
55
  secureStore.delete(key) ? "deleted" : "not-found",
@@ -66,10 +60,7 @@ mock.module("../security/secure-keys.js", () => ({
66
60
 
67
61
  mock.module("../tools/credentials/metadata-store.js", () => ({
68
62
  assertMetadataWritable: () => {},
69
- listCredentialRecordsLive: mock(async () => ({
70
- records: Array.from(metadataStore.values()),
71
- unreachable: liveRecordsUnreachable,
72
- })),
63
+ listCredentialMetadata: mock(() => Array.from(metadataStore.values())),
73
64
  getCredentialMetadata: mock((service: string, field: string) =>
74
65
  metadataStore.get(metaKey(service, field)),
75
66
  ),
@@ -210,8 +201,6 @@ describe("credentials routes", () => {
210
201
  credentialIdCounter = 0;
211
202
  scrubbedValues = [];
212
203
  scrubRejects = false;
213
- liveRecordsUnreachable = false;
214
- secretGetUnreachable = false;
215
204
  _resetRevealSuccessRegistryForTest();
216
205
  resetForChatMintRegistryForTest();
217
206
  chatCredentialRevealFlag = false;
@@ -810,204 +799,6 @@ describe("credentials routes", () => {
810
799
  expect(result.credentials).toHaveLength(1);
811
800
  expect(result.credentials[0].service).toBe("github");
812
801
  });
813
-
814
- test("fails immediately when the credential vault is unreachable", async () => {
815
- liveRecordsUnreachable = true;
816
- await setRoute!.handler({
817
- body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
818
- });
819
-
820
- await expect(listRoute!.handler({ body: {} })).rejects.toThrow(
821
- InternalError,
822
- );
823
- await expect(listRoute!.handler({ body: {} })).rejects.toThrow(
824
- /Credential store is unreachable/,
825
- );
826
- });
827
-
828
- test("fails immediately when a secret fetch reports the vault unreachable", async () => {
829
- await setRoute!.handler({
830
- body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
831
- });
832
- secretGetUnreachable = true;
833
-
834
- await expect(listRoute!.handler({ body: {} })).rejects.toThrow(
835
- InternalError,
836
- );
837
- });
838
- });
839
-
840
- describe("platform-managed credentials", () => {
841
- const PLATFORM_KEY = "vk-platform-provisioned-key";
842
- const PLATFORM_BASE_URL = "https://platform.example";
843
-
844
- /**
845
- * The platform provisions the pod's own credentials by POSTing them to
846
- * `/v1/secrets` as `credential` entries, which land in the same secure
847
- * store and metadata table as a user's own credentials.
848
- */
849
- function seedPlatformCredential(field: string, value: string): string {
850
- const key = `vellum:${field}`;
851
- secureStore.set(key, value);
852
- const now = Date.now();
853
- metadataStore.set(key, {
854
- credentialId: `cred-${++credentialIdCounter}`,
855
- service: "vellum",
856
- field,
857
- allowedTools: [],
858
- allowedDomains: [],
859
- createdAt: now,
860
- updatedAt: now,
861
- });
862
- return metadataStore.get(key)!.credentialId;
863
- }
864
-
865
- test("the listing omits platform credentials and keeps the user's own", async () => {
866
- // GIVEN the platform's provisioned credentials alongside a user's
867
- seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
868
- seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
869
- await setRoute!.handler({
870
- body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
871
- });
872
-
873
- // WHEN the credentials are listed
874
- const result = (await listRoute!.handler({ body: {} })) as ListResponse;
875
-
876
- // THEN only the user's credential is a row, and no platform value leaks
877
- expect(result.credentials.map((c) => `${c.service}:${c.field}`)).toEqual([
878
- "vercel:api_token",
879
- ]);
880
- expect(JSON.stringify(result)).not.toContain(PLATFORM_KEY);
881
- });
882
-
883
- test("reveal refuses the platform API key for every principal", async () => {
884
- /**
885
- * Both farming paths land on this handler: the assistant's own tool
886
- * shell arrives as `local`, a Settings click as `user`.
887
- */
888
- seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
889
-
890
- for (const principal of ["local", "user", "svc_gateway"]) {
891
- await expect(
892
- revealRoute!.handler({
893
- body: { service: "vellum", field: "assistant_api_key" },
894
- headers: { "x-vellum-principal-type": principal },
895
- }),
896
- ).rejects.toThrow(ForbiddenError);
897
- }
898
- });
899
-
900
- test("reveal refuses a platform credential looked up by UUID", async () => {
901
- const credentialId = seedPlatformCredential(
902
- "assistant_api_key",
903
- PLATFORM_KEY,
904
- );
905
-
906
- await expect(
907
- revealRoute!.handler({
908
- body: { id: credentialId },
909
- headers: { "x-vellum-principal-type": "local" },
910
- }),
911
- ).rejects.toThrow(/owned by the Vellum platform/);
912
- });
913
-
914
- test("inspect refuses platform credentials", async () => {
915
- seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
916
- seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
917
-
918
- for (const field of ["assistant_api_key", "platform_base_url"]) {
919
- await expect(
920
- inspectRoute!.handler({
921
- body: { service: "vellum", field },
922
- }),
923
- ).rejects.toThrow(ForbiddenError);
924
- }
925
- });
926
-
927
- test("set refuses platform credentials", async () => {
928
- /**
929
- * A write is a read by another name: repointing `platform_base_url`
930
- * sends the next platform call, bearing the API key, to a host the
931
- * writer chose. The platform's own provisioning does not come through
932
- * this route; it writes over `POST /v1/secrets`.
933
- */
934
- for (const field of ["assistant_api_key", "platform_base_url"]) {
935
- await expect(
936
- setRoute!.handler({
937
- body: { service: "vellum", field, value: "https://attacker.test" },
938
- }),
939
- ).rejects.toThrow(ForbiddenError);
940
- }
941
-
942
- expect(secureStore.has("vellum:assistant_api_key")).toBe(false);
943
- expect(secureStore.has("vellum:platform_base_url")).toBe(false);
944
- });
945
-
946
- test("set does not overwrite an already-provisioned platform credential", async () => {
947
- seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
948
-
949
- await expect(
950
- setRoute!.handler({
951
- body: {
952
- service: "vellum",
953
- field: "platform_base_url",
954
- value: "https://attacker.test",
955
- },
956
- }),
957
- ).rejects.toThrow(/owned by the Vellum platform/);
958
-
959
- expect(secureStore.get("vellum:platform_base_url")).toBe(
960
- PLATFORM_BASE_URL,
961
- );
962
- });
963
-
964
- test("delete refuses platform credentials", async () => {
965
- seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
966
- seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
967
-
968
- for (const field of ["assistant_api_key", "platform_base_url"]) {
969
- await expect(
970
- deleteRoute!.handler({ body: { service: "vellum", field } }),
971
- ).rejects.toThrow(ForbiddenError);
972
- }
973
-
974
- expect(secureStore.get("vellum:assistant_api_key")).toBe(PLATFORM_KEY);
975
- expect(secureStore.get("vellum:platform_base_url")).toBe(
976
- PLATFORM_BASE_URL,
977
- );
978
- });
979
-
980
- test("a user credential is still writable and deletable", async () => {
981
- const stored = (await setRoute!.handler({
982
- body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
983
- })) as SetResponse;
984
- expect(stored.service).toBe("vercel");
985
- expect(secureStore.get("vercel:api_token")).toBe(SECRET_VALUE);
986
-
987
- const deleted = (await deleteRoute!.handler({
988
- body: { service: "vercel", field: "api_token" },
989
- })) as DeleteResponse;
990
- expect(deleted.field).toBe("api_token");
991
- expect(secureStore.has("vercel:api_token")).toBe(false);
992
- });
993
-
994
- test("a user credential still inspects and reveals", async () => {
995
- seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
996
- await setRoute!.handler({
997
- body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
998
- });
999
-
1000
- const inspected = (await inspectRoute!.handler({
1001
- body: { service: "vercel", field: "api_token" },
1002
- })) as { scrubbedValue: string };
1003
- expect(inspected.scrubbedValue).toBe("supe****");
1004
-
1005
- const revealed = (await revealRoute!.handler({
1006
- body: { service: "vercel", field: "api_token" },
1007
- headers: { "x-vellum-principal-type": "user" },
1008
- })) as { value: string };
1009
- expect(revealed.value).toBe(SECRET_VALUE);
1010
- });
1011
802
  });
1012
803
 
1013
804
  describe("platform-managed credentials", () => {
@@ -211,8 +211,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
211
211
  "persistence/llm-request-log-source-clickhouse.ts", // ClickHouse read source — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped mirror reads
212
212
  "persistence/llm-request-log-sink-clickhouse.ts", // ClickHouse write sink — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped log writes
213
213
  "persistence/compaction-log-store-clickhouse.ts", // ClickHouse compaction log writer — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped event writes
214
- "config/platform-identity.ts", // in-memory platform ids, then vault copies of vellum:platform_*
215
- "config/platform-rehydration.ts", // startup rehydration of platform base URL from the credential store and ids from platform validate
214
+ "config/platform-rehydration.ts", // startup rehydration of platform base URL + IDs from credential store (daemon and schedule worker)
216
215
  "workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
217
216
  "workspace/migrations/018-rekey-compound-credential-keys.ts", // re-key compound credential storage keys
218
217
  "daemon/conversation-process.ts", // masked provider key display
@@ -224,10 +224,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
224
224
  );
225
225
 
226
226
  expect(result.isError).toBe(true);
227
- expect(result.content).toContain(
228
- "The Vellum desktop app is needed to view or control your screen",
229
- );
230
- expect(result.content).toContain("https://www.vellum.ai/downloads");
227
+ expect(result.content).toContain("no desktop client connected");
231
228
  });
232
229
 
233
230
  test("returns error when proxy exists but client not connected", async () => {
@@ -240,10 +237,7 @@ describe("surfaceProxyResolver — CU tool routing", () => {
240
237
  });
241
238
 
242
239
  expect(result.isError).toBe(true);
243
- expect(result.content).toContain(
244
- "The Vellum desktop app is needed to view or control your screen",
245
- );
246
- expect(result.content).toContain("https://www.vellum.ai/downloads");
240
+ expect(result.content).toContain("no desktop client connected");
247
241
  proxyObj.dispose();
248
242
  });
249
243
 
@@ -79,6 +79,8 @@ function makeFakeConversation(
79
79
  getTurnInterfaceContext: () => null,
80
80
  setTurnInterfaceContext: () => {},
81
81
  setTransportHints: () => {},
82
+ setTrustContext: () => {},
83
+ ensureActorScopedHistory: async () => {},
82
84
  emitActivityState: () => {
83
85
  if (activityFailures > 0) {
84
86
  activityFailures -= 1;