@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
@@ -23,10 +23,7 @@ import {
23
23
  getMessageById,
24
24
  updateMessageContent,
25
25
  } from "../persistence/conversation-crud.js";
26
- import {
27
- ASSISTANT_INITIATED_SOURCE,
28
- isBackgroundConversationType,
29
- } from "../persistence/conversation-types.js";
26
+ import { isBackgroundConversationType } from "../persistence/conversation-types.js";
30
27
  import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
31
28
  import { getLogger } from "../util/logger.js";
32
29
  import { normalizeTitle, stripMarkdown } from "../util/short-title.js";
@@ -275,11 +272,6 @@ async function resolveOwnedConversationMessageId(
275
272
  sourceConversationId: string | undefined,
276
273
  summary: string,
277
274
  ): Promise<string | undefined> {
278
- // The completed reply is already the source conversation's canonical row.
279
- // This signal's body is a compact push preview, not conversation content.
280
- if (signal.sourceEventName === "chat.assistant_reply") {
281
- return undefined;
282
- }
283
275
  if (vellumDelivery?.conversationId) {
284
276
  return vellumDelivery.conversationId === sourceConversationId
285
277
  ? vellumDelivery.messageId
@@ -503,18 +495,6 @@ function deriveDetailPanelKind(
503
495
  * `isAsyncBackground` hint. `chat.assistant_reply` also mirrors: it is the
504
496
  * durable in-app record for the push sent after a user leaves a chat, while
505
497
  * retaining the normal interactive conversation as its navigation target.
506
- *
507
- * A delivery that materialized an assistant-initiated thread never mirrors.
508
- * Under the `assistant-initiated-threads` flag, `conversation-pairing.ts`
509
- * promotes a background `assistant.share` into a standard conversation
510
- * stamped {@link ASSISTANT_INITIATED_SOURCE}, and that thread's row in the
511
- * sidebar's assistant section is the share's one surface. Mirroring it here
512
- * as well put the same share in the bell a second time, labeled by its
513
- * source channel ("Heartbeat"). The check reads the paired conversation's
514
- * `source` rather than re-deriving the promotion rule, so it holds however
515
- * that rule narrows or widens. Guardian requests are checked first on
516
- * purpose: the bell is their canonical home whatever conversation they pair
517
- * with.
518
498
  */
519
499
  function resolveHomeFeedMirror(
520
500
  signal: NotificationSignal,
@@ -545,15 +525,6 @@ function resolveHomeFeedMirror(
545
525
  : fallbackConversationId;
546
526
  const sourceScheduleJobId = sourceRow?.scheduleJobId ?? undefined;
547
527
 
548
- // Guardian requests always project into the feed: the "Needs
549
- // attention" item is the request's canonical home, and the request
550
- // blocks on the guardian whatever kind of conversation raised it.
551
- if (isGuardianRequestSignalEvent(signal.sourceEventName)) {
552
- return { mirror: true, sourceConversationId, sourceScheduleJobId };
553
- }
554
- if (isAssistantInitiatedThreadDelivery(fallbackConversationId)) {
555
- return { mirror: false };
556
- }
557
528
  if (
558
529
  signal.sourceChannel === "assistant_tool" ||
559
530
  signal.sourceEventName === "chat.assistant_reply"
@@ -563,37 +534,18 @@ function resolveHomeFeedMirror(
563
534
  if (signal.attentionHints.isAsyncBackground) {
564
535
  return { mirror: true, sourceConversationId, sourceScheduleJobId };
565
536
  }
537
+ // Guardian requests always project into the feed: the "Needs
538
+ // attention" item is the request's canonical home, and the request
539
+ // blocks on the guardian whatever kind of conversation raised it.
540
+ if (isGuardianRequestSignalEvent(signal.sourceEventName)) {
541
+ return { mirror: true, sourceConversationId, sourceScheduleJobId };
542
+ }
566
543
  if (isBackgroundConversationType(sourceRow?.conversationType)) {
567
544
  return { mirror: true, sourceConversationId, sourceScheduleJobId };
568
545
  }
569
546
  return { mirror: false };
570
547
  }
571
548
 
572
- /**
573
- * Whether the vellum delivery this signal produced landed in a thread the
574
- * sidebar's assistant section already shows.
575
- *
576
- * Best-effort like the source lookup above: a missing id, a missing row, or
577
- * a lookup failure all read as "not an assistant thread", so a storage
578
- * hiccup degrades to the pre-existing behavior (a bell row) rather than
579
- * dropping the notification everywhere.
580
- */
581
- function isAssistantInitiatedThreadDelivery(
582
- deliveryConversationId: string | undefined,
583
- ): boolean {
584
- if (!deliveryConversationId) {
585
- return false;
586
- }
587
- try {
588
- return (
589
- getConversation(deliveryConversationId)?.source ===
590
- ASSISTANT_INITIATED_SOURCE
591
- );
592
- } catch {
593
- return false;
594
- }
595
- }
596
-
597
549
  /**
598
550
  * Routing-intent enforcement can prune `selectedChannels` without also
599
551
  * pruning `renderedCopy`, so iterating `renderedCopy` directly risks
@@ -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 platform assistant id (in-memory identity, then vault). */
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 platform assistant id (in-memory identity, then vault). */
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 platform assistant id (in-memory identity, then vault). */
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;
@@ -190,36 +190,6 @@ export function existingMessageIds(
190
190
  );
191
191
  }
192
192
 
193
- /**
194
- * `createdAt` of each of `messageIds` that exists as a row, keyed by id, in
195
- * any state.
196
- *
197
- * Any-state deliberately: a row's `createdAt` is fixed at insert, so its
198
- * completeness does not change the answer, and the callers (retrospective
199
- * cursor bookkeeping) hold ids taken from rows they have already reviewed.
200
- * An empty input returns an empty map without querying.
201
- */
202
- export function messageCreatedAtByIds(
203
- messageIds: readonly string[],
204
- opts?: { db?: MessageReadHandle },
205
- ): Map<string, number> {
206
- const db = opts?.db ?? getDb();
207
- const result = new Map<string, number>();
208
- // Chunked so a whole-conversation id list stays under SQLite's bound
209
- // parameter limit.
210
- for (let offset = 0; offset < messageIds.length; offset += 500) {
211
- const rows = db
212
- .select({ id: messages.id, createdAt: messages.createdAt })
213
- .from(messages)
214
- .where(inArray(messages.id, messageIds.slice(offset, offset + 500)))
215
- .all();
216
- for (const row of rows) {
217
- result.set(row.id, row.createdAt);
218
- }
219
- }
220
- return result;
221
- }
222
-
223
193
  /**
224
194
  * The conversation that owns `messageId`, in any state, or null when the
225
195
  * message does not exist.