@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (439) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/docs/guardian-request-flow.md +0 -16
  5. package/openapi.yaml +49 -291
  6. package/package.json +1 -1
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  8. package/src/__tests__/app-compiler.test.ts +0 -6
  9. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  10. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  11. package/src/__tests__/app-source-watcher.test.ts +218 -0
  12. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  13. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  14. package/src/__tests__/computer-use-tools.test.ts +5 -17
  15. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  16. package/src/__tests__/config-schema.test.ts +29 -28
  17. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  18. package/src/__tests__/config-watcher.test.ts +8 -23
  19. package/src/__tests__/connection-model-compat.test.ts +2 -2
  20. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  21. package/src/__tests__/conversation-pairing.test.ts +0 -26
  22. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  23. package/src/__tests__/conversation-queue.test.ts +207 -48
  24. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  25. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  26. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  27. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  28. package/src/__tests__/conversation-title-service.test.ts +10 -14
  29. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  30. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  31. package/src/__tests__/credential-record-retire.test.ts +1 -171
  32. package/src/__tests__/credential-routes.test.ts +3 -212
  33. package/src/__tests__/credential-security-invariants.test.ts +2 -4
  34. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  35. package/src/__tests__/document-sync-tags.test.ts +0 -25
  36. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  37. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  38. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  39. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  40. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  42. package/src/__tests__/host-file-proxy.test.ts +23 -11
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  45. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  47. package/src/__tests__/mcp-health-check.test.ts +11 -8
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  49. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  51. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  52. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  53. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  54. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  55. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  56. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  57. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  58. package/src/__tests__/model-intents.test.ts +0 -3
  59. package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
  60. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  61. package/src/__tests__/platform-callback-registration.test.ts +16 -8
  62. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  63. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  64. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  66. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  67. package/src/__tests__/speaker-identification.test.ts +56 -0
  68. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  69. package/src/__tests__/subagent-tools.test.ts +2 -3
  70. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  71. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  72. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  73. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  74. package/src/__tests__/workspace-git-service.test.ts +0 -26
  75. package/src/acp/__tests__/agent-process.test.ts +4 -258
  76. package/src/acp/__tests__/auth-required.test.ts +0 -96
  77. package/src/acp/__tests__/client-handler.test.ts +2 -90
  78. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  79. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  80. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  81. package/src/acp/__tests__/session-manager.test.ts +4 -895
  82. package/src/acp/agent-process.test.ts +1 -4
  83. package/src/acp/agent-process.ts +9 -94
  84. package/src/acp/auth-required.ts +1 -50
  85. package/src/acp/auto-install.test.ts +7 -9
  86. package/src/acp/client-handler.ts +3 -16
  87. package/src/acp/resolve-agent.test.ts +6 -220
  88. package/src/acp/resolve-agent.ts +23 -88
  89. package/src/acp/session-manager.test.ts +3 -196
  90. package/src/acp/session-manager.ts +18 -441
  91. package/src/acp/types.ts +5 -50
  92. package/src/api/events/app-files-changed.ts +3 -4
  93. package/src/api/events/notification-intent.ts +0 -2
  94. package/src/api/index.ts +0 -6
  95. package/src/apps/app-store.ts +1 -1
  96. package/src/bundler/app-compiler.ts +0 -15
  97. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  98. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  99. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  100. package/src/calls/call-constants.ts +4 -24
  101. package/src/calls/call-controller.ts +199 -499
  102. package/src/calls/call-transport.ts +0 -16
  103. package/src/calls/media-stream-output.ts +0 -32
  104. package/src/calls/media-stream-server.ts +30 -82
  105. package/src/calls/media-stream-stt-session.ts +0 -15
  106. package/src/calls/resolve-call-tts-provider.ts +2 -33
  107. package/src/calls/speaker-identification.ts +233 -0
  108. package/src/calls/tts-call-strategy.ts +5 -9
  109. package/src/calls/types.ts +0 -2
  110. package/src/calls/voice-control-protocol.ts +21 -53
  111. package/src/calls/voice-session-bridge.ts +46 -59
  112. package/src/calls/voice-triage-escalate.ts +56 -175
  113. package/src/cli/commands/apps.help.ts +13 -67
  114. package/src/cli/commands/apps.ts +0 -183
  115. package/src/cli/commands/bash.help.ts +13 -8
  116. package/src/cli/commands/bash.ts +25 -7
  117. package/src/cli/commands/channels/index.help.ts +2 -70
  118. package/src/cli/commands/channels/index.ts +3 -16
  119. package/src/cli/commands/credentials.help.ts +2 -6
  120. package/src/cli/commands/credentials.ts +2 -2
  121. package/src/cli/commands/mcp.help.ts +8 -8
  122. package/src/cli/commands/mcp.ts +1 -1
  123. package/src/cli/commands/roadmap.help.ts +1 -5
  124. package/src/cli/commands/roadmap.ts +1 -1
  125. package/src/cli/lib/bundled-marketplace.json +90 -103
  126. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  127. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  128. package/src/config/__tests__/platform-rehydration.test.ts +9 -99
  129. package/src/config/acp-defaults.test.ts +3 -13
  130. package/src/config/acp-defaults.ts +6 -23
  131. package/src/config/acp-schema.ts +2 -30
  132. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  133. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  134. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  135. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  136. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  137. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  138. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  139. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  140. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  141. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  142. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  143. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  144. package/src/config/default-profile-catalog.ts +1 -1
  145. package/src/config/env.ts +2 -2
  146. package/src/config/feature-flag-registry.json +12 -24
  147. package/src/config/loader.ts +0 -1
  148. package/src/config/memory-v3-gate.test.ts +0 -31
  149. package/src/config/memory-v3-gate.ts +16 -20
  150. package/src/config/platform-rehydration.ts +27 -11
  151. package/src/config/schema.ts +2 -2
  152. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  153. package/src/config/schemas/calls.ts +28 -1
  154. package/src/config/schemas/live-voice.ts +173 -1
  155. package/src/config/schemas/mcp.ts +1 -1
  156. package/src/config/schemas/memory-retrospective.ts +0 -10
  157. package/src/config/schemas/services.ts +0 -8
  158. package/src/config/schemas/skills.ts +14 -0
  159. package/src/credential-execution/process-manager.test.ts +19 -18
  160. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  161. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  162. package/src/daemon/app-source-watcher.ts +218 -0
  163. package/src/daemon/config-watcher.ts +194 -13
  164. package/src/daemon/conversation-process.ts +64 -35
  165. package/src/daemon/conversation-skill-tools.ts +31 -13
  166. package/src/daemon/conversation-store.ts +1 -1
  167. package/src/daemon/conversation-surfaces.ts +8 -10
  168. package/src/daemon/conversation-tool-setup.ts +43 -37
  169. package/src/daemon/conversation.ts +3 -0
  170. package/src/daemon/host-bash-proxy.ts +12 -8
  171. package/src/daemon/host-browser-proxy.ts +1 -15
  172. package/src/daemon/host-file-proxy.ts +10 -8
  173. package/src/daemon/host-transfer-proxy.ts +18 -14
  174. package/src/daemon/lifecycle.ts +5 -0
  175. package/src/daemon/mcp-reload-service.ts +5 -5
  176. package/src/daemon/message-types/sync.ts +0 -1
  177. package/src/daemon/orphan-reaper.ts +1 -1
  178. package/src/daemon/providers-setup.ts +1 -1
  179. package/src/daemon/shutdown-handlers.ts +9 -1
  180. package/src/daemon/skill-memory-refresh.ts +0 -64
  181. package/src/daemon/tool-side-effects.ts +18 -7
  182. package/src/home/__tests__/feed-writer.test.ts +0 -22
  183. package/src/home/feed-writer.ts +2 -11
  184. package/src/hooks/types.ts +0 -33
  185. package/src/inbound/platform-callback-registration.ts +5 -4
  186. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  187. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  188. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  189. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  190. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  191. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  192. package/src/live-voice/live-voice-session.ts +693 -213
  193. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  194. package/src/live-voice/protocol.ts +2 -0
  195. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  196. package/src/mcp/effective-config.ts +3 -3
  197. package/src/mcp/mcp-header-store.ts +45 -29
  198. package/src/mcp/startup.ts +4 -8
  199. package/src/messaging/provider.ts +3 -10
  200. package/src/messaging/providers/callback-routing.ts +0 -18
  201. package/src/messaging/providers/channel-transport.ts +0 -56
  202. package/src/messaging/providers/discord/transport.ts +0 -17
  203. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  204. package/src/messaging/providers/slack/adapter.ts +44 -7
  205. package/src/messaging/providers/slack/transport.ts +0 -16
  206. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  207. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  208. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  209. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  210. package/src/messaging/providers/whatsapp/client.ts +29 -0
  211. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  212. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  213. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  214. package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
  215. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  216. package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
  217. package/src/notifications/adapters/macos.ts +0 -3
  218. package/src/notifications/assistant-reply-producer.ts +3 -8
  219. package/src/notifications/conversation-pairing.ts +16 -36
  220. package/src/notifications/delivered-post-record.ts +1 -11
  221. package/src/notifications/guardian-feed-projection.ts +5 -125
  222. package/src/notifications/home-feed-side-effect.ts +7 -55
  223. package/src/notifications/schedule-result-producer.ts +22 -66
  224. package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
  225. package/src/oauth/byo-connection.test.ts +0 -45
  226. package/src/oauth/byo-connection.ts +6 -36
  227. package/src/oauth/connection-resolver.test.ts +0 -73
  228. package/src/oauth/connection-resolver.ts +2 -95
  229. package/src/oauth/seed-providers.ts +0 -165
  230. package/src/outbound-proxy/certs.ts +5 -3
  231. package/src/permissions/checker.test.ts +6 -11
  232. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  233. package/src/persistence/conversation-crud.ts +63 -71
  234. package/src/persistence/conversation-lineage.ts +4 -12
  235. package/src/persistence/conversation-title-service.ts +26 -9
  236. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  237. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  238. package/src/persistence/message-reads.ts +0 -30
  239. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  240. package/src/persistence/schema/memory-core.ts +0 -5
  241. package/src/platform/client.test.ts +2 -4
  242. package/src/platform/client.ts +10 -2
  243. package/src/plugin-api/constants.ts +0 -2
  244. package/src/plugin-api/index.ts +0 -1
  245. package/src/plugin-api/types.ts +0 -2
  246. package/src/plugins/defaults/index.ts +0 -2
  247. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  248. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  249. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  250. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  251. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  252. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  253. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  254. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  255. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  256. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  257. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  258. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  259. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  260. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  261. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  262. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  263. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  264. package/src/plugins/defaults/memory/startup.ts +1 -6
  265. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  266. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  267. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  268. package/src/plugins/defaults/memory/worker.ts +0 -6
  269. package/src/plugins/mcp-servers.ts +75 -15
  270. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  271. package/src/providers/gemini/client.ts +23 -9
  272. package/src/providers/inference/auth.ts +3 -2
  273. package/src/providers/inference/connection-availability.ts +8 -27
  274. package/src/providers/inference/resolve-auth.ts +4 -22
  275. package/src/providers/model-catalog.ts +0 -34
  276. package/src/providers/model-intents.ts +5 -10
  277. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  278. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  279. package/src/providers/openai/chat-completions-provider.ts +78 -192
  280. package/src/providers/openai/codex-models.ts +4 -0
  281. package/src/runtime/AGENTS.md +0 -2
  282. package/src/runtime/auth/same-actor.ts +3 -38
  283. package/src/runtime/http-server.ts +151 -160
  284. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  286. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  287. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  288. package/src/runtime/routes/acp-routes.test.ts +0 -15
  289. package/src/runtime/routes/acp-routes.ts +9 -72
  290. package/src/runtime/routes/app-management-routes.ts +0 -73
  291. package/src/runtime/routes/app-routes.ts +1 -1
  292. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  293. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  294. package/src/runtime/routes/credential-routes.ts +10 -19
  295. package/src/runtime/routes/debug-bash-routes.ts +173 -0
  296. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  297. package/src/runtime/routes/index.ts +2 -4
  298. package/src/runtime/routes/log-export-routes.ts +0 -2
  299. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  300. package/src/runtime/routes/migration-routes.ts +20 -6
  301. package/src/runtime/routes/platform-routes.ts +15 -18
  302. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  303. package/src/runtime/services/conversation-serializer.ts +3 -16
  304. package/src/runtime/sync/resource-sync-events.ts +1 -2
  305. package/src/schedule/worker.ts +1 -1
  306. package/src/security/ces-rpc-record-backend.ts +0 -7
  307. package/src/subagent/consult-context.ts +25 -12
  308. package/src/tools/acp/list-agents.test.ts +1 -3
  309. package/src/tools/acp/spawn.test.ts +11 -146
  310. package/src/tools/acp/spawn.ts +3 -21
  311. package/src/tools/acp/status.ts +3 -40
  312. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  313. package/src/tools/capability-offer.test.ts +2 -22
  314. package/src/tools/capability-offer.ts +1 -16
  315. package/src/tools/computer-use/definitions.ts +2 -4
  316. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  317. package/src/tools/credentials/metadata-store.ts +0 -100
  318. package/src/tools/host-filesystem/edit.test.ts +1 -2
  319. package/src/tools/host-filesystem/edit.ts +2 -2
  320. package/src/tools/host-filesystem/read.test.ts +1 -2
  321. package/src/tools/host-filesystem/read.ts +2 -2
  322. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  323. package/src/tools/host-filesystem/transfer.ts +2 -2
  324. package/src/tools/host-filesystem/write.test.ts +1 -2
  325. package/src/tools/host-filesystem/write.ts +2 -2
  326. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  327. package/src/tools/host-terminal/host-shell.ts +20 -4
  328. package/src/tools/policy-context.ts +7 -6
  329. package/src/tools/subagent/spawn.ts +8 -5
  330. package/src/tools/watch/watch-retro-report.ts +8 -0
  331. package/src/util/__tests__/language-subtag.test.ts +1 -23
  332. package/src/util/fs-watcher-error.ts +36 -0
  333. package/src/util/language-subtag.ts +0 -16
  334. package/src/util/platform.ts +0 -8
  335. package/src/workspace/git-service.ts +54 -5
  336. package/src/workspace/migrations/registry.ts +0 -6
  337. package/scripts/smoke-desktop-window-theme.ts +0 -186
  338. package/src/__tests__/git-porcelain.test.ts +0 -47
  339. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  340. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  341. package/src/__tests__/resolve-app.test.ts +0 -135
  342. package/src/__tests__/service-account-token.test.ts +0 -247
  343. package/src/__tests__/source-fingerprint.test.ts +0 -184
  344. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  345. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  346. package/src/acp/model-config.test.ts +0 -166
  347. package/src/acp/model-config.ts +0 -138
  348. package/src/api/events/acp-session-model-update.test.ts +0 -88
  349. package/src/api/events/acp-session-model-update.ts +0 -46
  350. package/src/apps/resolve-app.ts +0 -81
  351. package/src/apps/source-fingerprint.ts +0 -266
  352. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  353. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  354. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  355. package/src/calls/barge-in-guard.ts +0 -118
  356. package/src/calls/voice-leg-coordinator.ts +0 -189
  357. package/src/calls/voice-progress-cadence.ts +0 -423
  358. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  359. package/src/cli/commands/__tests__/bash.test.ts +0 -53
  360. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  361. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  362. package/src/cli/commands/channels/send.ts +0 -128
  363. package/src/config/__tests__/platform-identity.test.ts +0 -255
  364. package/src/config/acp-agent-write.ts +0 -43
  365. package/src/config/acp-schema.test.ts +0 -81
  366. package/src/config/platform-identity.ts +0 -201
  367. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  368. package/src/config/schemas/voice.ts +0 -150
  369. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  370. package/src/daemon/app-change-notify.ts +0 -45
  371. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  372. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  373. package/src/desktop/desktop-chrome-policy.ts +0 -42
  374. package/src/desktop/desktop-dependencies.test.ts +0 -102
  375. package/src/desktop/desktop-dependencies.ts +0 -306
  376. package/src/desktop/desktop-feature.test.ts +0 -18
  377. package/src/desktop/desktop-feature.ts +0 -19
  378. package/src/desktop/desktop-panel-config.test.ts +0 -135
  379. package/src/desktop/desktop-panel-config.ts +0 -149
  380. package/src/desktop/desktop-session-manager.test.ts +0 -627
  381. package/src/desktop/desktop-session-manager.ts +0 -814
  382. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  383. package/src/desktop/desktop-stream-bridge.ts +0 -214
  384. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  385. package/src/desktop/desktop-wallpaper.ts +0 -113
  386. package/src/desktop/desktop-window-theme.ts +0 -170
  387. package/src/i18n/AGENTS.md +0 -22
  388. package/src/i18n/__tests__/i18n.test.ts +0 -108
  389. package/src/i18n/index.ts +0 -16
  390. package/src/i18n/locales.ts +0 -93
  391. package/src/i18n/messages.ts +0 -61
  392. package/src/i18n/title.ts +0 -25
  393. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  394. package/src/mcp/spec-schema.ts +0 -111
  395. package/src/mcp/workspace-mcp-config.ts +0 -188
  396. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  397. package/src/persistence/conversation-title-placeholders.ts +0 -23
  398. package/src/persistence/message-cursor.ts +0 -78
  399. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  400. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  401. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  402. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  403. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  404. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  405. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  406. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  407. package/src/providers/gemini-thought-signature.test.ts +0 -63
  408. package/src/providers/gemini-thought-signature.ts +0 -44
  409. package/src/providers/inference/service-account-token.ts +0 -248
  410. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  411. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  412. package/src/providers/openai/google-thought-signature.ts +0 -320
  413. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  414. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  415. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  416. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  417. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  418. package/src/runtime/channel-send.ts +0 -316
  419. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  420. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  421. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  422. package/src/runtime/routes/channel-send-routes.ts +0 -85
  423. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  424. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  425. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  426. package/src/security/ces-http-record-backend.ts +0 -120
  427. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  428. package/src/skills/workspace-skill-md-signature.ts +0 -50
  429. package/src/tools/acp/status.test.ts +0 -94
  430. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
  431. package/src/tools/terminal/sanitized-bash.ts +0 -105
  432. package/src/util/ca-bundle.ts +0 -13
  433. package/src/workspace/git-porcelain.ts +0 -66
  434. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  435. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  436. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  437. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  438. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  439. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -1329,171 +1329,6 @@ export const PROVIDER_SEED_DATA: Record<
1329
1329
  identityUrl: "https://api.link.com/userinfo",
1330
1330
  identityResponsePaths: ["email", "phone"],
1331
1331
  },
1332
- shopify: {
1333
- provider: "shopify",
1334
- // Shopify is per-tenant: every OAuth endpoint lives on the merchant's own
1335
- // myshopify.com domain, so these carry a {tenant_host} placeholder the
1336
- // platform fills in from the shop domain the user supplies at connect
1337
- // time. There is no global Shopify authorization host to point at.
1338
- authorizeUrl: "https://{tenant_host}/admin/oauth/authorize",
1339
- tokenExchangeUrl: "https://{tenant_host}/admin/oauth/access_token",
1340
- // Requesting `expiring=1` at token exchange yields a short-lived access
1341
- // token plus a refresh token, which is refreshed against the same
1342
- // per-shop endpoint.
1343
- refreshUrl: "https://{tenant_host}/admin/oauth/access_token",
1344
- pingUrl: "https://{tenant_host}/admin/api/2026-07/shop.json",
1345
- baseUrl: "https://{tenant_host}",
1346
- displayLabel: "Shopify",
1347
- description: "Products, orders, customers, and inventory",
1348
- dashboardUrl: "https://shopify.dev/dashboard",
1349
- clientIdPlaceholder: null,
1350
- logoUrl: "https://cdn.simpleicons.org/shopify",
1351
- // Shopify parses `scope` as a comma-separated list. A space-separated
1352
- // list is read as one malformed scope and the request is rejected.
1353
- scopeSeparator: ",",
1354
- // Admin API scopes only: Storefront, Customer Account and Shop APIs are
1355
- // buyer-facing surfaces an assistant has no use for. Every scope here is
1356
- // grantable without Shopify approval -- the approval-gated ones
1357
- // (read_all_orders, customer payment methods, subscription contracts,
1358
- // dispute evidence) are deliberately absent, because Shopify rejects the
1359
- // whole authorization request if the app cannot grant a requested scope.
1360
- // A write_* grant implies its read_* counterpart, so the response may
1361
- // name only the write scope.
1362
- defaultScopes: [
1363
- "read_products",
1364
- "write_products",
1365
- "read_orders",
1366
- "write_orders",
1367
- "read_draft_orders",
1368
- "write_draft_orders",
1369
- "read_customers",
1370
- "write_customers",
1371
- "read_inventory",
1372
- "read_locations",
1373
- "read_fulfillments",
1374
- "read_discounts",
1375
- "write_discounts",
1376
- "read_price_rules",
1377
- ],
1378
- availableScopes: [
1379
- {
1380
- scope: "read_products",
1381
- description: "Read products, variants, and collections",
1382
- },
1383
- {
1384
- scope: "write_products",
1385
- description: "Create and update products, variants, and collections",
1386
- },
1387
- {
1388
- scope: "read_orders",
1389
- description: "Read orders, transactions, and abandoned checkouts",
1390
- },
1391
- {
1392
- scope: "write_orders",
1393
- description: "Create and update orders and fulfillments",
1394
- },
1395
- { scope: "read_draft_orders", description: "Read draft orders" },
1396
- {
1397
- scope: "write_draft_orders",
1398
- description: "Create and update draft orders",
1399
- },
1400
- {
1401
- scope: "read_customers",
1402
- description: "Read customers, segments, and companies",
1403
- },
1404
- {
1405
- scope: "write_customers",
1406
- description: "Create and update customers and segments",
1407
- },
1408
- {
1409
- scope: "read_inventory",
1410
- description: "Read inventory levels and items",
1411
- },
1412
- {
1413
- scope: "write_inventory",
1414
- description: "Adjust inventory levels and items",
1415
- },
1416
- { scope: "read_locations", description: "Read store locations" },
1417
- { scope: "read_fulfillments", description: "Read fulfillment services" },
1418
- {
1419
- scope: "write_fulfillments",
1420
- description: "Create and update fulfillment services",
1421
- },
1422
- { scope: "read_discounts", description: "Read discounts" },
1423
- { scope: "write_discounts", description: "Create and update discounts" },
1424
- { scope: "read_price_rules", description: "Read price rules" },
1425
- {
1426
- scope: "write_price_rules",
1427
- description: "Create and update price rules",
1428
- },
1429
- { scope: "read_files", description: "Read files uploaded to the store" },
1430
- { scope: "write_files", description: "Upload and update files" },
1431
- { scope: "read_gift_cards", description: "Read gift cards" },
1432
- {
1433
- scope: "write_gift_cards",
1434
- description: "Create and update gift cards",
1435
- },
1436
- { scope: "read_returns", description: "Read returns" },
1437
- { scope: "write_returns", description: "Create and update returns" },
1438
- { scope: "read_markets", description: "Read markets" },
1439
- { scope: "read_locales", description: "Read shop locales" },
1440
- {
1441
- scope: "read_shipping",
1442
- description: "Read shipping and delivery carrier services",
1443
- },
1444
- { scope: "read_analytics", description: "Read analytics and reports" },
1445
- {
1446
- scope: "read_marketing_events",
1447
- description: "Read marketing events and activities",
1448
- },
1449
- { scope: "read_order_edits", description: "Read order edits" },
1450
- {
1451
- scope: "write_order_edits",
1452
- description: "Create and apply order edits",
1453
- },
1454
- {
1455
- scope: "read_metaobjects",
1456
- description: "Read metaobjects and their definitions",
1457
- },
1458
- {
1459
- scope: "write_metaobjects",
1460
- description: "Create and update metaobjects",
1461
- },
1462
- { scope: "read_translations", description: "Read translated content" },
1463
- {
1464
- scope: "read_shopify_payments_payouts",
1465
- description: "Read Shopify Payments payouts and balance",
1466
- },
1467
- {
1468
- scope: "read_shopify_payments_disputes",
1469
- description: "Read Shopify Payments disputes",
1470
- },
1471
- ],
1472
- loopbackPort: 17341,
1473
- managedServiceConfigKey: "shopify-oauth",
1474
- injectionTemplates: [
1475
- {
1476
- hostPattern: "*.myshopify.com",
1477
- injectionType: "header",
1478
- headerName: "X-Shopify-Access-Token",
1479
- valuePrefix: "",
1480
- },
1481
- ],
1482
- appType: "App",
1483
- setupNotes: [
1484
- "Shopify OAuth is per-shop: the authorize, token and API endpoints all live on the merchant's own myshopify.com domain, so connecting requires the shop domain (for example your-store.myshopify.com) alongside the usual credentials.",
1485
- "The Admin API authenticates with the X-Shopify-Access-Token header rather than an Authorization Bearer header.",
1486
- ],
1487
- // The Admin API's shop endpoint nests its payload under "shop"; the
1488
- // myshopify domain is the stable, human-recognisable handle.
1489
- identityUrl: "https://{tenant_host}/admin/api/2026-07/shop.json",
1490
- identityResponsePaths: ["shop.myshopify_domain", "shop.name"],
1491
- // Gated like monday/figma/stripe-link: the platform side and the
1492
- // shop-domain input in the clients land separately, and until both are
1493
- // in place a visible Shopify tile would offer a connect flow that
1494
- // cannot complete.
1495
- featureFlag: "shopify-oauth",
1496
- },
1497
1332
  };
1498
1333
 
1499
1334
  export const SEEDED_PROVIDER_KEYS = new Set(Object.keys(PROVIDER_SEED_DATA));
@@ -2,8 +2,6 @@ import { X509Certificate } from "node:crypto";
2
2
  import { chmod, mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
 
5
- import { writeCombinedCABundle } from "../util/ca-bundle.js";
6
-
7
5
  const CA_CERT_FILENAME = "ca.pem";
8
6
  const CA_KEY_FILENAME = "ca-key.pem";
9
7
  const COMBINED_CA_FILENAME = "combined-ca-bundle.pem";
@@ -265,7 +263,11 @@ export async function ensureCombinedCABundle(
265
263
  }
266
264
 
267
265
  try {
268
- await writeCombinedCABundle(systemBundlePath, caCertPath, combinedPath);
266
+ const [systemCAs, proxyCACert] = await Promise.all([
267
+ readFile(systemBundlePath, "utf-8"),
268
+ readFile(caCertPath, "utf-8"),
269
+ ]);
270
+ await writeFile(combinedPath, systemCAs + "\n" + proxyCACert);
269
271
  return combinedPath;
270
272
  } catch {
271
273
  return null;
@@ -15,16 +15,13 @@ mock.module("../config/assistant-feature-flags.js", () => ({
15
15
  }));
16
16
 
17
17
  // `buildPolicyContext` (used by the integration tests below) precomputes the
18
- // retrospective skill-improvement gate. Keep the v3 tier active while the
19
- // dedicated gate changes so the production threading test covers the new
20
- // opt-out rather than the old v3 condition.
18
+ // proc-to-skills gate via `isV3TierActive`. Drive it through this slot so a
19
+ // test can put the production threading path in the active / inactive state.
21
20
  let mockV3TierActive = true;
22
- let mockSkillImprovementActive = true;
23
21
  mock.module("../config/memory-v3-gate.js", () => ({
24
22
  isMemoryEnabled: (config?: { memory?: { enabled?: boolean } }) =>
25
23
  config?.memory?.enabled !== false,
26
24
  isV3TierActive: () => mockV3TierActive,
27
- isSkillImprovementActive: () => mockSkillImprovementActive,
28
25
  isMemoryV3Live: () => mockV3TierActive,
29
26
  usesConceptPageMemory: (memory?: {
30
27
  enabled?: boolean;
@@ -222,7 +219,6 @@ describe("Permission Checker (gateway IPC)", () => {
222
219
  mockCachedThreshold = "low";
223
220
  mockRefreshedThreshold = null;
224
221
  mockV3TierActive = true;
225
- mockSkillImprovementActive = true;
226
222
  thresholdCallLog.length = 0;
227
223
  mockSkillCatalog = [];
228
224
  mockResolvedSkill = null;
@@ -1177,11 +1173,10 @@ describe("Permission Checker (gateway IPC)", () => {
1177
1173
  });
1178
1174
 
1179
1175
  test("grant does NOT fire when proc-to-skills is inactive, even for the retrospective turn", async () => {
1180
- // The retrospective keeps its live v3 tier, but this independent flag is
1181
- // off. buildPolicyContext stamps `procToSkillsActive: false`, so the
1182
- // grant is dead and the high-risk scaffold prompts.
1183
- mockV3TierActive = true;
1184
- mockSkillImprovementActive = false;
1176
+ // Same retrospective ToolContext, but the feature is inactive (flag off
1177
+ // or v3 not live). buildPolicyContext stamps `procToSkillsActive: false`,
1178
+ // so the grant is dead and the high-risk scaffold prompts.
1179
+ mockV3TierActive = false;
1185
1180
  mockIpcClassifyRiskResult = {
1186
1181
  risk: "high",
1187
1182
  reason: "Skill scaffold",
@@ -26,7 +26,6 @@
26
26
  */
27
27
  import type { AgentEvent } from "../agent/loop.js";
28
28
  import { getConfig } from "../config/loader.js";
29
- import { resolvePlatformAssistantIdOrNull } from "../config/platform-identity.js";
30
29
  import type { CompactionLogsClickHouseConfig } from "../config/schemas/compaction-logs.js";
31
30
  import { credentialKey } from "../security/credential-key.js";
32
31
  import { getSecureKeyAsync } from "../security/secure-keys.js";
@@ -137,7 +136,7 @@ export interface ClickHouseCompactionLogStoreDeps {
137
136
  resolveUrl?: () => Promise<string | null>;
138
137
  /** Override the credential read for `clickhouse:password`. */
139
138
  resolvePassword?: () => Promise<string | null>;
140
- /** Override the in-memory platform assistant id. */
139
+ /** Override the credential read for `vellum:platform_assistant_id`. */
141
140
  resolveAssistantId?: () => Promise<string | null>;
142
141
  /** Override fetch for testing. */
143
142
  fetchImpl?: CompactionLogFetch;
@@ -164,7 +163,8 @@ export class ClickHouseCompactionLogStore {
164
163
  deps.resolvePassword ??
165
164
  (() => readCredentialOrNull("clickhouse", "password"));
166
165
  this.resolveAssistantId =
167
- deps.resolveAssistantId ?? resolvePlatformAssistantIdOrNull;
166
+ deps.resolveAssistantId ??
167
+ (() => readCredentialOrNull("vellum", "platform_assistant_id"));
168
168
  this.fetchImpl = deps.fetchImpl ?? globalThis.fetch.bind(globalThis);
169
169
  }
170
170
 
@@ -35,10 +35,7 @@ import { findConversation } from "../daemon/conversation-registry.js";
35
35
  import { conversationMetadataSyncTag } from "../daemon/message-types/sync.js";
36
36
  import type { TrustContext } from "../daemon/trust-context-types.js";
37
37
  import { clearAllConversationIds } from "../home/feed-writer.js";
38
- import type {
39
- ConversationDeletedInputContext,
40
- MessageDeletedInputContext,
41
- } from "../hooks/types.js";
38
+ import type { ConversationDeletedInputContext } from "../hooks/types.js";
42
39
  import { readProviderMetadata } from "../messaging/read-provider-metadata.js";
43
40
  import { HOOKS } from "../plugin-api/constants.js";
44
41
  import { forkConversationMemory } from "../plugins/defaults/memory/fork-conversation-memory.js";
@@ -84,6 +81,7 @@ import { ensureDisplayOrderMigration } from "./conversation-display-order-migrat
84
81
  import { ensureGroupMigration } from "./conversation-group-migration.js";
85
82
  import {
86
83
  isReferentialFork,
84
+ type LineageBound,
87
85
  type LineageConversationRow,
88
86
  lineageMessageFilter,
89
87
  lineageMessagesAfterFilter,
@@ -123,11 +121,6 @@ import {
123
121
  } from "./job-handlers/message-lexical.js";
124
122
  import { buildLifecycleTelemetryEvent } from "./lifecycle-events-store.js";
125
123
  import { resolveMessageContentBlocks } from "./message-content-file.js";
126
- import {
127
- loadMessageBound,
128
- type MessagesAfterRef,
129
- resolveMessagesAfterBound,
130
- } from "./message-cursor.js";
131
124
  import { mergeMessageMetadata } from "./message-metadata.js";
132
125
  import {
133
126
  rawAll,
@@ -2648,6 +2641,16 @@ function loadLineageRow(id: string): LineageConversationRow | null {
2648
2641
  return row ?? null;
2649
2642
  }
2650
2643
 
2644
+ /** The `(createdAt, id)` bound of a single message, or null when it is gone. */
2645
+ function loadMessageBound(messageId: string): LineageBound | null {
2646
+ const row = getDb()
2647
+ .select({ createdAt: messages.createdAt, id: messages.id })
2648
+ .from(messages)
2649
+ .where(eq(messages.id, messageId))
2650
+ .get();
2651
+ return row ?? null;
2652
+ }
2653
+
2651
2654
  /**
2652
2655
  * The segments making up a conversation's logical message set. Non-forks and
2653
2656
  * copied forks resolve to a single unbounded segment after one row lookup, so
@@ -2969,67 +2972,81 @@ export function selectNewestSightFrameCapture(
2969
2972
  }
2970
2973
 
2971
2974
  /**
2972
- * Count messages in a conversation created strictly after the `after`
2973
- * reference. `null`, `""`, or a cursor with an empty id counts every message.
2974
- * The reference resolves through `resolveMessagesAfterBound`: a live row's
2975
- * `(createdAt, id)` is authoritative, a `MessageCursor` whose row has been
2976
- * deleted still bounds the count from the `createdAt` it carries, and a bare
2977
- * id whose row is gone counts 0: a caller holding only an id has no
2978
- * defensible starting point, and the conservative answer is "no new work."
2975
+ * Count messages in a conversation that were created strictly after the
2976
+ * `afterMessageId` reference message. If `afterMessageId` is `null` or empty,
2977
+ * counts all messages in the conversation. If the referenced message no
2978
+ * longer exists (e.g. deleted by a separate flow), returns 0 — callers
2979
+ * decide how to react to a vanished reference, and the conservative answer
2980
+ * here is "no new work."
2979
2981
  *
2980
2982
  * Used by the memory-retrospective trigger check to decide whether to fire
2981
2983
  * the message-count trigger without loading message bodies.
2982
2984
  */
2983
2985
  export function countMessagesAfter(
2984
2986
  conversationId: string,
2985
- after: MessagesAfterRef,
2987
+ afterMessageId: string | null,
2986
2988
  ): number {
2987
2989
  const db = getDb();
2988
2990
  const segments = resolveLineage(conversationId);
2989
- const start = resolveMessagesAfterBound(after);
2990
- if (start.kind === "vanished") {
2991
+ if (afterMessageId === null || afterMessageId === "") {
2992
+ const row = db
2993
+ .select({ c: count() })
2994
+ .from(messages)
2995
+ .where(lineageMessageFilter(segments))
2996
+ .get();
2997
+ return row?.c ?? 0;
2998
+ }
2999
+ const ref = loadMessageBound(afterMessageId);
3000
+ if (!ref) {
2991
3001
  return 0;
2992
3002
  }
3003
+ // Tie-breaker on `messages.id` so rows that share a millisecond timestamp
3004
+ // with the reference are not permanently skipped. Mirrors the
3005
+ // `(createdAt, id)` cursor pattern used by the backfill job-handler and
3006
+ // turn-events-store.
2993
3007
  const row = db
2994
3008
  .select({ c: count() })
2995
3009
  .from(messages)
2996
- .where(
2997
- start.kind === "all"
2998
- ? lineageMessageFilter(segments)
2999
- : lineageMessagesAfterFilter(segments, start.bound),
3000
- )
3010
+ .where(lineageMessagesAfterFilter(segments, ref))
3001
3011
  .get();
3002
3012
  return row?.c ?? 0;
3003
3013
  }
3004
3014
 
3005
3015
  /**
3006
- * Return messages in a conversation created strictly after the `after`
3007
- * reference, in `(createdAt, id)` order. Resolves the reference exactly as
3008
- * `countMessagesAfter` does, returning an empty array where that returns 0.
3009
- * Used by the memory-retrospective job handler to load the message slice it
3010
- * processes.
3016
+ * Return messages in a conversation created strictly after the
3017
+ * `afterMessageId` reference. If the reference is `null`/empty, returns all
3018
+ * messages. If the reference doesn't exist, returns an empty array (mirrors
3019
+ * `countMessagesAfter`'s conservative semantics). Used by the
3020
+ * memory-retrospective job handler to load the message slice it processes.
3011
3021
  */
3012
3022
  export function getMessagesAfter(
3013
3023
  conversationId: string,
3014
- after: MessagesAfterRef,
3024
+ afterMessageId: string | null,
3015
3025
  ): MessageRow[] {
3016
3026
  const db = getDb();
3017
3027
  const segments = resolveLineage(conversationId);
3018
- const start = resolveMessagesAfterBound(after);
3019
- if (start.kind === "vanished") {
3028
+ if (afterMessageId === null || afterMessageId === "") {
3029
+ // Secondary `asc(messages.id)` matches the non-null path's cursor
3030
+ // ordering, so callers tracking `cutoffMessageId` across runs see a
3031
+ // consistent ordering when multiple rows share a millisecond timestamp.
3032
+ return db
3033
+ .select()
3034
+ .from(messages)
3035
+ .where(lineageMessageFilter(segments))
3036
+ .orderBy(asc(messages.createdAt), asc(messages.id))
3037
+ .all()
3038
+ .map(parseMessage);
3039
+ }
3040
+ const ref = loadMessageBound(afterMessageId);
3041
+ if (!ref) {
3020
3042
  return [];
3021
3043
  }
3022
- // Secondary `asc(messages.id)` mirrors the bound's tie-breaker, so callers
3023
- // tracking `cutoffMessageId` across runs see a consistent ordering when
3024
- // multiple rows share a millisecond timestamp.
3044
+ // Same `(createdAt, id)` cursor as `countMessagesAfter` — rows sharing
3045
+ // the reference's millisecond timestamp would otherwise be skipped.
3025
3046
  return db
3026
3047
  .select()
3027
3048
  .from(messages)
3028
- .where(
3029
- start.kind === "all"
3030
- ? lineageMessageFilter(segments)
3031
- : lineageMessagesAfterFilter(segments, start.bound),
3032
- )
3049
+ .where(lineageMessagesAfterFilter(segments, ref))
3033
3050
  .orderBy(asc(messages.createdAt), asc(messages.id))
3034
3051
  .all()
3035
3052
  .map(parseMessage);
@@ -4287,12 +4304,12 @@ export function deleteLastExchange(conversationId: string): number {
4287
4304
 
4288
4305
  // Collect attachment IDs linked to the messages being deleted so we can
4289
4306
  // scope orphan cleanup to only those candidates (not freshly uploaded ones).
4290
- const deletedRows = db
4291
- .select({ id: messages.id, createdAt: messages.createdAt })
4307
+ const messageIds = db
4308
+ .select({ id: messages.id })
4292
4309
  .from(messages)
4293
4310
  .where(condition)
4294
- .all();
4295
- const messageIds = deletedRows.map((r) => r.id);
4311
+ .all()
4312
+ .map((r) => r.id);
4296
4313
  const candidateAttachmentIds =
4297
4314
  messageIds.length > 0
4298
4315
  ? db
@@ -4339,17 +4356,6 @@ export function deleteLastExchange(conversationId: string): number {
4339
4356
  enqueueDeleteMessageLexical(deletedMessageId);
4340
4357
  }
4341
4358
 
4342
- // Notify `message-deleted` hooks for each removed row, as the
4343
- // single-message primitive does: undo removes the same tail a regenerate
4344
- // does, and a hook keeping a cursor on one of these rows needs its position.
4345
- for (const row of deletedRows) {
4346
- void runHook(HOOKS.MESSAGE_DELETED, {
4347
- conversationId,
4348
- messageId: row.id,
4349
- createdAt: row.createdAt,
4350
- } satisfies MessageDeletedInputContext);
4351
- }
4352
-
4353
4359
  return deleted;
4354
4360
  }
4355
4361
 
@@ -4588,13 +4594,9 @@ export function deleteMessageById(
4588
4594
  .map((r) => r.attachmentId)
4589
4595
  .filter((id): id is string => id !== undefined);
4590
4596
 
4591
- // Look up the conversation before the transaction so we can recalculate
4592
- // lastMessageAt, and the row's createdAt for the `message-deleted` hook.
4597
+ // Look up the conversation before the transaction so we can recalculate lastMessageAt.
4593
4598
  const msgRow = db
4594
- .select({
4595
- conversationId: messages.conversationId,
4596
- createdAt: messages.createdAt,
4597
- })
4599
+ .select({ conversationId: messages.conversationId })
4598
4600
  .from(messages)
4599
4601
  .where(eq(messages.id, messageId))
4600
4602
  .get();
@@ -4650,16 +4652,6 @@ export function deleteMessageById(
4650
4652
  // not go through the conversation-level purge.
4651
4653
  if (msgRow) {
4652
4654
  enqueueDeleteMessageLexical(messageId);
4653
-
4654
- // Notify `message-deleted` hooks (e.g. the memory plugin stamping a
4655
- // retrospective cursor that sat on this row). Fire-and-forget from this
4656
- // synchronous primitive, like `conversation-deleted`; the context carries
4657
- // the row's position because the row itself is gone.
4658
- void runHook(HOOKS.MESSAGE_DELETED, {
4659
- conversationId: msgRow.conversationId,
4660
- messageId,
4661
- createdAt: msgRow.createdAt,
4662
- } satisfies MessageDeletedInputContext);
4663
4655
  }
4664
4656
 
4665
4657
  return result;
@@ -227,17 +227,9 @@ export function lineageMessagesAfterFilter(
227
227
  ): SQL {
228
228
  return and(
229
229
  lineageMessageFilter(segments),
230
- messagesAfterBoundFilter(after),
231
- ) as SQL;
232
- }
233
-
234
- /**
235
- * Predicate for rows strictly after a `(createdAt, id)` bound, with the id
236
- * tie-breaker so rows sharing the bound's millisecond keep their order.
237
- */
238
- export function messagesAfterBoundFilter(after: LineageBound): SQL {
239
- return or(
240
- gt(messages.createdAt, after.createdAt),
241
- and(eq(messages.createdAt, after.createdAt), gt(messages.id, after.id)),
230
+ or(
231
+ gt(messages.createdAt, after.createdAt),
232
+ and(eq(messages.createdAt, after.createdAt), gt(messages.id, after.id)),
233
+ ),
242
234
  ) as SQL;
243
235
  }
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import { SEND_USER_MESSAGE_DELIVERED_ACK } from "../config/send-user-message-constants.js";
11
- import { MESSAGE_KEYS } from "../i18n/index.js";
12
11
  import {
13
12
  requestShortLabel,
14
13
  type ShortLabelTool,
@@ -25,11 +24,8 @@ import {
25
24
  type MessageRow,
26
25
  updateConversationTitle,
27
26
  } from "./conversation-crud.js";
28
- import { isReplaceableTitle } from "./conversation-title-placeholders.js";
29
27
  import { projectPersistedAssistantContent } from "./user-facing-content.js";
30
28
 
31
- export { isReplaceableTitle } from "./conversation-title-placeholders.js";
32
-
33
29
  const log = getLogger("conversation-title-service");
34
30
 
35
31
  // ── Types ────────────────────────────────────────────────────────────
@@ -69,8 +65,8 @@ export interface TitleContext {
69
65
 
70
66
  // ── Placeholder / loading state ──────────────────────────────────────
71
67
 
72
- export const GENERATING_TITLE = MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING;
73
- const UNTITLED_FALLBACK = MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED;
68
+ export const GENERATING_TITLE = "Generating title...";
69
+ const UNTITLED_FALLBACK = "Untitled Conversation";
74
70
 
75
71
  // ── `conversations.isAutoTitle` values ───────────────────────────────
76
72
  //
@@ -87,6 +83,28 @@ export const AUTO_TITLE_LLM = 1;
87
83
  */
88
84
  export const AUTO_TITLE_DETERMINISTIC = 2;
89
85
 
86
+ // ── Replaceability check ─────────────────────────────────────────────
87
+
88
+ const REPLACEABLE_PATTERNS = [
89
+ /^Runtime:\s/,
90
+ /^New Conversation$/,
91
+ /^Untitled$/,
92
+ /^Untitled Conversation$/,
93
+ /^Generating title\.\.\.$/,
94
+ ];
95
+
96
+ /**
97
+ * Check whether a title is a system-generated placeholder that can be
98
+ * safely overwritten by auto-generated titles. Returns `false` for
99
+ * user-provided custom titles.
100
+ */
101
+ export function isReplaceableTitle(title: string | null): boolean {
102
+ if (title == null || title.trim() === "") {
103
+ return true;
104
+ }
105
+ return REPLACEABLE_PATTERNS.some((pattern) => pattern.test(title));
106
+ }
107
+
90
108
  /**
91
109
  * Whether auto-generation may overwrite the conversation's current title.
92
110
  * True for placeholder/system titles and for deterministic bootstrap titles
@@ -237,7 +255,7 @@ function settleForDeterministicTitle(
237
255
  * conversation creation (e.g. 5 new chats in quick succession) fires N
238
256
  * concurrent requests that can hit provider rate limits or contend for
239
257
  * API capacity, causing later calls to time out and fall back to
240
- * the untitled catalog key.
258
+ * "Untitled Conversation".
241
259
  *
242
260
  * A serial queue ensures at most one title-generation LLM call is
243
261
  * in-flight at a time. Each call is lightweight (~1–3 s for a ≤5-word
@@ -271,7 +289,7 @@ export function queueGenerateConversationTitle(
271
289
  // Replace loading placeholder with a retryable fallback.
272
290
  try {
273
291
  const conversation = getConversation(params.conversationId);
274
- if (conversation && isReplaceableTitle(conversation.title)) {
292
+ if (conversation && conversation.title === GENERATING_TITLE) {
275
293
  const fallback =
276
294
  deriveFallbackTitle(params.context) ?? UNTITLED_FALLBACK;
277
295
  updateConversationTitle(
@@ -406,7 +424,6 @@ function buildTitleSystemPrompt(): string {
406
424
  "- Think: what would make a scannable sidebar label?",
407
425
  "- Do NOT echo back what the user asked you to do",
408
426
  "- Do NOT respond to the conversation content",
409
- "- Write the title in the same language as the conversation content",
410
427
  "- Do NOT assess feasibility or comment on capabilities",
411
428
  "- If input is sparse or references external context, extract a topic from the words that ARE present (e.g. 'so about that t-shirt...' → 'T-Shirt Discussion'). Never describe the absence, emptiness, or insufficiency of context — titles like 'Missing Context', 'Unclear Request', 'No Topic' are forbidden",
412
429
  ].join("\n");
@@ -30,7 +30,6 @@
30
30
  * can never abort a turn.
31
31
  */
32
32
  import { getConfigReadOnly } from "../config/loader.js";
33
- import { resolvePlatformAssistantIdOrNull } from "../config/platform-identity.js";
34
33
  import type { LlmRequestLogsClickHouseConfig } from "../config/schemas/llm-request-logs.js";
35
34
  import { credentialKey } from "../security/credential-key.js";
36
35
  import { getSecureKeyAsync } from "../security/secure-keys.js";
@@ -72,7 +71,7 @@ export interface ClickHouseLlmRequestLogSinkDeps {
72
71
  resolveUrl?: () => Promise<string | null>;
73
72
  /** Override the credential read for `clickhouse:password`. */
74
73
  resolvePassword?: () => Promise<string | null>;
75
- /** Override the in-memory platform assistant id. */
74
+ /** Override the credential read for `vellum:platform_assistant_id`. */
76
75
  resolveAssistantId?: () => Promise<string | null>;
77
76
  /** Override fetch for testing. */
78
77
  fetchImpl?: ClickHouseSinkFetch;
@@ -99,7 +98,8 @@ export class ClickHouseLlmRequestLogSink implements LlmRequestLogWriter {
99
98
  deps.resolvePassword ??
100
99
  (() => readCredentialOrNull("clickhouse", "password"));
101
100
  this.resolveAssistantId =
102
- deps.resolveAssistantId ?? resolvePlatformAssistantIdOrNull;
101
+ deps.resolveAssistantId ??
102
+ (() => readCredentialOrNull("vellum", "platform_assistant_id"));
103
103
  this.fetchImpl = deps.fetchImpl ?? globalThis.fetch.bind(globalThis);
104
104
  }
105
105
 
@@ -15,7 +15,6 @@
15
15
  * than the local source. Acceptable for the "internal use while we
16
16
  * finetune prompts" use case; revisit when mirror updates are added.
17
17
  */
18
- import { resolvePlatformAssistantIdOrNull } from "../config/platform-identity.js";
19
18
  import type { LlmRequestLogsClickHouseConfig } from "../config/schemas/llm-request-logs.js";
20
19
  import { credentialKey } from "../security/credential-key.js";
21
20
  import { getSecureKeyAsync } from "../security/secure-keys.js";
@@ -105,7 +104,7 @@ export interface ClickHouseLlmRequestLogSourceDeps {
105
104
  resolveUrl?: () => Promise<string | null>;
106
105
  /** Override the credential read for `clickhouse:password`. */
107
106
  resolvePassword?: () => Promise<string | null>;
108
- /** Override the in-memory platform assistant id. */
107
+ /** Override the credential read for `vellum:platform_assistant_id`. */
109
108
  resolveAssistantId?: () => Promise<string | null>;
110
109
  /** Override the turn-id resolver (default: `getAssistantMessageIdsInTurn`). */
111
110
  resolveTurnMessageIds?: (messageId: string) => string[];
@@ -139,7 +138,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
139
138
  deps.resolvePassword ??
140
139
  (() => readCredentialOrNull("clickhouse", "password"));
141
140
  this.resolveAssistantId =
142
- deps.resolveAssistantId ?? resolvePlatformAssistantIdOrNull;
141
+ deps.resolveAssistantId ??
142
+ (() => readCredentialOrNull("vellum", "platform_assistant_id"));
143
143
  this.resolveTurnMessageIds =
144
144
  deps.resolveTurnMessageIds ?? getAssistantMessageIdsInTurn;
145
145
  this.resolveMessage = deps.resolveMessage ?? getMessageById;