@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
@@ -2,7 +2,6 @@ import type { StreamPlan, StreamPlanStep } from "@vellumai/gateway-client";
2
2
  import { ChannelDeliveryError } from "@vellumai/gateway-client/http-delivery";
3
3
 
4
4
  import { getLogger } from "../../../util/logger.js";
5
- import { directDeliveryContext } from "../callback-routing.js";
6
5
  import type {
7
6
  CallbackContext,
8
7
  ChannelTransport,
@@ -67,25 +66,6 @@ function mintDraftId(): number {
67
66
  export const telegramTransport: ChannelTransport = {
68
67
  channel: "telegram",
69
68
 
70
- /**
71
- * A chat is a chat id, with `threadId` naming a forum topic. A person's DM
72
- * chat id is their user id on Telegram, so reaching one is naming that
73
- * chat and needs no resolution of its own.
74
- */
75
- addressFor(target) {
76
- const threadId = target.threadId?.trim();
77
- return {
78
- ctx: directDeliveryContext("telegram", threadId ? { threadId } : {}),
79
- chatId: target.chatId,
80
- ...(threadId ? { threadId } : {}),
81
- };
82
- },
83
-
84
- // A Telegram chat's inbound conversation is keyed per chat and can be
85
- // reset or deleted between sends; a proactive post re-binds it so the
86
- // next inbound from the chat lands where the post lives.
87
- bindsChatOnProactiveSend: true,
88
-
89
69
  // Telegram clears a chat action after about five seconds.
90
70
  activityRefreshMs: 4_000,
91
71
 
@@ -5,6 +5,8 @@
5
5
  */
6
6
 
7
7
  import type { OAuthConnection } from "../../../oauth/connection.js";
8
+ import { getOrCreateConversation } from "../../../persistence/conversation-key-store.js";
9
+ import { upsertOutboundBinding } from "../../../persistence/external-conversation-store.js";
8
10
  import { credentialKey } from "../../../security/credential-key.js";
9
11
  import { getSecureKeyAsync } from "../../../security/secure-keys.js";
10
12
  import type { MessagingProvider } from "../../provider.js";
@@ -16,7 +18,10 @@ import type {
16
18
  Message,
17
19
  SearchOptions,
18
20
  SearchResult,
21
+ SendOptions,
22
+ SendResult,
19
23
  } from "../../provider-types.js";
24
+ import * as whatsapp from "./client.js";
20
25
 
21
26
  /** Check whether WhatsApp credentials are stored. */
22
27
  async function hasWhatsAppCredentials(): Promise<boolean> {
@@ -36,7 +41,7 @@ export const whatsappMessagingProvider: MessagingProvider = {
36
41
  id: "whatsapp",
37
42
  displayName: "WhatsApp",
38
43
  credentialService: "whatsapp",
39
- capabilities: new Set(),
44
+ capabilities: new Set(["send"]),
40
45
 
41
46
  async isConnected(): Promise<boolean> {
42
47
  return hasWhatsAppCredentials();
@@ -69,6 +74,48 @@ export const whatsappMessagingProvider: MessagingProvider = {
69
74
  };
70
75
  },
71
76
 
77
+ async sendMessage(
78
+ _connection: OAuthConnection | undefined,
79
+ conversationId: string,
80
+ text: string,
81
+ options?: SendOptions,
82
+ ): Promise<SendResult> {
83
+ const assistantId = options?.assistantId;
84
+
85
+ const sent = await whatsapp.sendMessage(conversationId, text);
86
+ if (!sent.messageId) {
87
+ throw new Error(
88
+ "WhatsApp accepted the message but returned no message id",
89
+ );
90
+ }
91
+
92
+ // Upsert external conversation binding so the conversation key mapping
93
+ // exists for the next inbound WhatsApp message from this number.
94
+ try {
95
+ const sourceChannel = "whatsapp";
96
+ const conversationKey = `asst:${assistantId ?? "self"}:${sourceChannel}:${conversationId}`;
97
+ const { conversationId: internalId } =
98
+ getOrCreateConversation(conversationKey);
99
+ if (!assistantId || assistantId === "self") {
100
+ upsertOutboundBinding({
101
+ conversationId: internalId,
102
+ sourceChannel,
103
+ externalChatId: conversationId,
104
+ });
105
+ }
106
+ } catch {
107
+ // Best-effort — don't fail the send if binding upsert fails
108
+ }
109
+
110
+ // The id is the one the Cloud API assigned, so a later reaction or
111
+ // delete carrying that id resolves to this send.
112
+ return {
113
+ id: sent.messageId,
114
+ timestamp: Date.now(),
115
+ conversationId,
116
+ };
117
+ },
118
+
72
119
  async listConversations(
73
120
  _connection?: OAuthConnection,
74
121
  _options?: ListOptions,
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Low-level WhatsApp operations.
3
+ *
4
+ * Calls the Meta Cloud API directly via ./api.ts — no gateway proxy hop.
5
+ */
6
+
7
+ import { sendWhatsAppTextMessage } from "./api.js";
8
+
9
+ /** Result returned by sendMessage. */
10
+ export interface WhatsAppSendResult {
11
+ ok: boolean;
12
+ /**
13
+ * The id the Cloud API assigned to the message (`messages[0].id`). Meta
14
+ * returns it on every accepted send, so this is absent only when the API
15
+ * answered with an unexpected shape.
16
+ */
17
+ messageId?: string;
18
+ }
19
+
20
+ /**
21
+ * Send a WhatsApp text message via the Meta Cloud API.
22
+ */
23
+ export async function sendMessage(
24
+ to: string,
25
+ text: string,
26
+ ): Promise<WhatsAppSendResult> {
27
+ const result = await sendWhatsAppTextMessage(to, text);
28
+ return { ok: true, messageId: result.messages?.[0]?.id };
29
+ }
@@ -1,7 +1,6 @@
1
1
  import { ChannelDeliveryError } from "@vellumai/gateway-client/http-delivery";
2
2
 
3
3
  import { getLogger } from "../../../util/logger.js";
4
- import { directDeliveryContext } from "../callback-routing.js";
5
4
  import type { ChannelTransport } from "../channel-transport.js";
6
5
  import { sendWhatsAppAttachments, sendWhatsAppReply } from "./send.js";
7
6
 
@@ -10,23 +9,6 @@ const log = getLogger("whatsapp-transport");
10
9
  export const whatsappTransport: ChannelTransport = {
11
10
  channel: "whatsapp",
12
11
 
13
- /**
14
- * A chat is the phone number, which is also how a person is named, so
15
- * reaching one needs no resolution of its own. There are no threads, so a
16
- * target's thread is not carried and the post lands in the chat itself.
17
- */
18
- addressFor(target) {
19
- return {
20
- ctx: directDeliveryContext("whatsapp"),
21
- chatId: target.chatId,
22
- };
23
- },
24
-
25
- // A WhatsApp number's inbound conversation is keyed per chat and can be
26
- // reset between sends; a proactive post re-binds it so the next inbound
27
- // from the number lands where the post lives.
28
- bindsChatOnProactiveSend: true,
29
-
30
12
  async deliver(_ctx, payload) {
31
13
  const { chatId, text, attachments, approval } = payload;
32
14
 
@@ -54,12 +54,4 @@ describe("workspace git heartbeat on the monitoring worker", () => {
54
54
  expect(worker).toContain("await workspaceGitHeartbeat?.stop()");
55
55
  expect(lifecycle).not.toContain("startWorkspaceHeartbeatService");
56
56
  });
57
-
58
- test("daemon lifecycle does not stop the in-process heartbeat singleton", () => {
59
- const shutdown = readFileSync(
60
- join(SRC_ROOT, "daemon/shutdown-handlers.ts"),
61
- "utf8",
62
- );
63
- expect(shutdown).not.toContain("stopWorkspaceHeartbeatService");
64
- });
65
57
  });
@@ -5,7 +5,6 @@
5
5
  import { beforeEach, describe, expect, mock, test } from "bun:test";
6
6
 
7
7
  import { setOverridesForTesting } from "../../__tests__/feature-flag-test-helpers.js";
8
- import { MESSAGE_KEYS } from "../../i18n/index.js";
9
8
  import type { AttentionState } from "../../persistence/conversation-attention-store.js";
10
9
  import type {
11
10
  ConversationRow,
@@ -590,60 +589,6 @@ describe("emitAssistantReplyNotification", () => {
590
589
  });
591
590
  });
592
591
 
593
- // A conversation whose title is still being written stores the message key
594
- // itself, which is a non-empty string the sanitizer has no reason to reject.
595
- // Sending it would put a raw key on the lock screen, so it counts as absent.
596
- test("omits requestedTitle while the title is still generating", async () => {
597
- conversationRow = makeConversation({
598
- title: MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING,
599
- });
600
-
601
- await run();
602
-
603
- expect(emitCalls).toHaveLength(1);
604
- expect(emitCalls[0].contextPayload).toEqual({
605
- requestedMessage: "Sure, here is the plan.",
606
- });
607
- });
608
-
609
- test("omits requestedTitle for the legacy generating placeholder", async () => {
610
- conversationRow = makeConversation({ title: "Generating title..." });
611
-
612
- await run();
613
-
614
- expect(emitCalls).toHaveLength(1);
615
- expect(emitCalls[0].contextPayload).toEqual({
616
- requestedMessage: "Sure, here is the plan.",
617
- });
618
- });
619
-
620
- test("omits requestedTitle for the untitled placeholder key", async () => {
621
- conversationRow = makeConversation({
622
- title: MESSAGE_KEYS.CONVERSATION_TITLE_UNTITLED,
623
- });
624
-
625
- await run();
626
-
627
- expect(emitCalls).toHaveLength(1);
628
- expect(emitCalls[0].contextPayload).toEqual({
629
- requestedMessage: "Sure, here is the plan.",
630
- });
631
- });
632
-
633
- // Only an exact key is a system constant. A title the user or the model
634
- // wrote that happens to contain one is their copy and travels as written.
635
- test("keeps a title that merely contains a message key", async () => {
636
- conversationRow = makeConversation({
637
- title: `notes on ${MESSAGE_KEYS.CONVERSATION_TITLE_GENERATING}`,
638
- });
639
-
640
- await run();
641
-
642
- expect(emitCalls[0].contextPayload.requestedTitle).toBe(
643
- "notes on conversation.title.generating",
644
- );
645
- });
646
-
647
592
  test("caps the preview at 200 chars", async () => {
648
593
  assistantRow = makeAssistantRow([
649
594
  { type: "text", text: "a".repeat(300) },
@@ -26,15 +26,6 @@ const messageRewrites: Array<{ messageId: string; content: string }> = [];
26
26
  /** messageId -> the conversation it belongs to, for the scoped lookup. */
27
27
  const messageOwners = new Map<string, string>();
28
28
  let conversationRow: { conversationType: string } | null = null;
29
- /**
30
- * Rows for specific ids, consulted before the catch-all `conversationRow`,
31
- * so a test can hand the signal's source and the delivery's paired
32
- * conversation different shapes.
33
- */
34
- const conversationRowsById = new Map<
35
- string,
36
- { conversationType: string; source?: string }
37
- >();
38
29
  let conversationLookupShouldThrow = false;
39
30
  let messageAppendShouldThrow = false;
40
31
  let messageRewriteShouldThrow = false;
@@ -80,7 +71,7 @@ mock.module("../../persistence/conversation-crud.js", () => ({
80
71
  if (conversationLookupShouldThrow) {
81
72
  throw new Error("simulated conversation lookup failure");
82
73
  }
83
- return conversationRowsById.get(id) ?? conversationRow;
74
+ return conversationRow;
84
75
  },
85
76
  addMessage: async (
86
77
  conversationId: string,
@@ -173,7 +164,6 @@ beforeEach(() => {
173
164
  messageRewrites.length = 0;
174
165
  messageOwners.clear();
175
166
  conversationRow = null;
176
- conversationRowsById.clear();
177
167
  conversationLookupShouldThrow = false;
178
168
  messageAppendShouldThrow = false;
179
169
  messageRewriteShouldThrow = false;
@@ -268,11 +258,7 @@ describe("writeHomeFeedItemForSignal", () => {
268
258
  },
269
259
  });
270
260
 
271
- const item = await writeHomeFeedItemForSignal(
272
- signal,
273
- decision,
274
- makeVellumDelivery(),
275
- );
261
+ const item = await writeHomeFeedItemForSignal(signal, decision);
276
262
 
277
263
  expect(item).not.toBeNull();
278
264
  expect(appendCalls).toHaveLength(1);
@@ -281,10 +267,7 @@ describe("writeHomeFeedItemForSignal", () => {
281
267
  "The deployment completed successfully.",
282
268
  );
283
269
  expect(appendCalls[0]!.conversationId).toBe("conv-source-1");
284
- expect(appendCalls[0]!.metadata?.notificationConversationMessageId).toBeUndefined();
285
- expect(messageAppends).toEqual([]);
286
- expect(messagesInvalidated).toEqual([]);
287
- expect(conversationLookups).toEqual(["conv-source-1", "conv-source-1"]);
270
+ expect(conversationLookups).toEqual(["conv-source-1"]);
288
271
  });
289
272
 
290
273
  test("isAsyncBackground hint writes even when sourceContextId does not resolve", async () => {
@@ -419,139 +402,6 @@ describe("writeHomeFeedItemForSignal", () => {
419
402
  expect(appendCalls[0]!.conversationId).toBe("paired-delivery-conv-id");
420
403
  });
421
404
 
422
- describe("assistant-initiated thread deliveries", () => {
423
- // Under the assistant-initiated-threads flag, conversation pairing
424
- // promotes a background assistant.share into a standard conversation
425
- // stamped `assistant_initiated`, which the sidebar's assistant section
426
- // renders. That thread is the share's surface; a feed item for the
427
- // same signal put it in the bell a second time.
428
- const assistantThreadDelivery = () =>
429
- makeVellumDelivery({ conversationId: "assistant-thread-1" });
430
-
431
- function stampAssistantThread(): void {
432
- conversationRowsById.set("assistant-thread-1", {
433
- conversationType: "standard",
434
- source: "assistant_initiated",
435
- });
436
- }
437
-
438
- function shareFromHeartbeat(): NotificationSignal {
439
- return makeSignal({
440
- sourceChannel: "assistant_tool",
441
- sourceEventName: "assistant.share",
442
- sourceContextId: "conv-source-1",
443
- contextPayload: { title: "Doctor checklist built" },
444
- });
445
- }
446
-
447
- const shareDecision = () =>
448
- makeDecision({
449
- selectedChannels: ["vellum"],
450
- renderedCopy: {
451
- vellum: {
452
- title: "Doctor checklist built",
453
- body: "Your doctor-visit one-pager is ready.",
454
- },
455
- },
456
- });
457
-
458
- test("a background share promoted into an assistant thread does not mirror", async () => {
459
- conversationRow = { conversationType: "background" };
460
- stampAssistantThread();
461
-
462
- const item = await writeHomeFeedItemForSignal(
463
- shareFromHeartbeat(),
464
- shareDecision(),
465
- assistantThreadDelivery(),
466
- );
467
-
468
- expect(item).toBeNull();
469
- expect(appendCalls).toHaveLength(0);
470
- expect(messageAppends).toHaveLength(0);
471
- });
472
-
473
- test("the async-background hint does not override the assistant-thread suppression", async () => {
474
- conversationRow = null;
475
- stampAssistantThread();
476
- const signal = makeSignal({
477
- sourceChannel: "assistant_tool",
478
- sourceEventName: "assistant.share",
479
- sourceContextId: "heartbeat",
480
- contextPayload: { title: "Bilbao solo trip is planned" },
481
- attentionHints: {
482
- requiresAction: false,
483
- urgency: "medium",
484
- isAsyncBackground: true,
485
- visibleInSourceNow: false,
486
- },
487
- });
488
-
489
- const item = await writeHomeFeedItemForSignal(
490
- signal,
491
- shareDecision(),
492
- assistantThreadDelivery(),
493
- );
494
-
495
- expect(item).toBeNull();
496
- expect(appendCalls).toHaveLength(0);
497
- });
498
-
499
- test("a share paired with an ordinary notification thread still mirrors", async () => {
500
- // Flag off, or any producer that did not promote: pairing writes a
501
- // `notification`-sourced row, which stays in Chats and the bell.
502
- conversationRow = { conversationType: "background" };
503
- conversationRowsById.set("assistant-thread-1", {
504
- conversationType: "standard",
505
- source: "notification",
506
- });
507
-
508
- const item = await writeHomeFeedItemForSignal(
509
- shareFromHeartbeat(),
510
- shareDecision(),
511
- assistantThreadDelivery(),
512
- );
513
-
514
- expect(item).not.toBeNull();
515
- expect(appendCalls).toHaveLength(1);
516
- });
517
-
518
- test("a guardian request keeps its bell item even when paired with an assistant thread", async () => {
519
- // The bell is the request's canonical home: nothing else renders the
520
- // pending card, so the assistant-thread suppression must not reach it.
521
- conversationRow = { conversationType: "standard" };
522
- stampAssistantThread();
523
- const signal = makeSignal({
524
- sourceChannel: "assistant_tool",
525
- sourceEventName: "guardian.question",
526
- contextPayload: {
527
- title: "Approve this?",
528
- questionText: "May I send the email?",
529
- requestId: "req-1",
530
- },
531
- attentionHints: {
532
- requiresAction: true,
533
- urgency: "medium",
534
- isAsyncBackground: false,
535
- visibleInSourceNow: false,
536
- },
537
- });
538
-
539
- const item = await writeHomeFeedItemForSignal(
540
- signal,
541
- makeDecision({
542
- selectedChannels: ["vellum"],
543
- renderedCopy: {
544
- vellum: { title: "Approve this?", body: "May I send the email?" },
545
- },
546
- }),
547
- assistantThreadDelivery(),
548
- );
549
-
550
- expect(item).not.toBeNull();
551
- expect(appendCalls).toHaveLength(1);
552
- });
553
- });
554
-
555
405
  test("source conversation id wins over the paired delivery fallback when both are available", async () => {
556
406
  // When the producer's `sourceContextId` already points at a real
557
407
  // conversation (the canonical "where the work happened"), prefer it
@@ -22,7 +22,6 @@ import {
22
22
  getMessageById,
23
23
  parseMessageMetadata,
24
24
  } from "../persistence/conversation-crud.js";
25
- import { isReplaceableTitle } from "../persistence/conversation-title-placeholders.js";
26
25
  import {
27
26
  isDesktopOriginatedUserMessage,
28
27
  isReplyPushIneligibleUserMessage,
@@ -248,13 +247,9 @@ export async function emitAssistantReplyNotification(params: {
248
247
  // lock screen. Absent `requestedTitle` lets the decision branch derive a
249
248
  // title from the body, which reads better than an empty or placeholder
250
249
  // conversation title.
251
- //
252
- // Placeholder titles are plausible non-empty strings and survive
253
- // sanitizing. They count as absent so the body supplies the title instead.
254
- const storedTitle = conversation.title?.trim() ?? "";
255
- const requestedTitle = isReplaceableTitle(storedTitle)
256
- ? ""
257
- : sanitizeNotificationTitle(flattenTitleWhitespace(storedTitle));
250
+ const requestedTitle = sanitizeNotificationTitle(
251
+ flattenTitleWhitespace(conversation.title ?? ""),
252
+ );
258
253
 
259
254
  // Read as close to the emit as possible: nothing short-circuits on it.
260
255
  // Presence only speaks for a turn the desktop itself opened, on that row's
@@ -130,7 +130,6 @@ export interface PairingOptions {
130
130
  * transactional request or a system alert.
131
131
  */
132
132
  const ASSISTANT_SHARE_EVENT = "assistant.share";
133
- const ASSISTANT_REPLY_EVENT = "chat.assistant_reply";
134
133
 
135
134
  /**
136
135
  * Promote a background share into an assistant-initiated thread, under the
@@ -264,25 +263,7 @@ export async function pairDeliveryWithConversation(
264
263
  // notification can appear. The home feed aims its "Go to Conversation"
265
264
  // button at the same row whenever it mirrors the signal.
266
265
  //
267
- // `chat.assistant_reply` already has its complete reply in that transcript.
268
- // Its notification body is a lock-screen preview, so appending it would
269
- // create a second, truncated assistant row. Keep the conversation target
270
- // for deep links without writing the preview into the transcript.
271
- if (
272
- strategy === "start_new_conversation" &&
273
- !signal.requiresConversation &&
274
- signal.sourceEventName === ASSISTANT_REPLY_EVENT
275
- ) {
276
- return {
277
- conversationId: resolveSourceConversationId(signal),
278
- messageId: null,
279
- strategy,
280
- createdNewConversation: false,
281
- conversationFallbackUsed: false,
282
- };
283
- }
284
-
285
- // This is deliberately not gated on home-feed eligibility: a signal the
266
+ // So this is deliberately not gated on home-feed eligibility: a signal the
286
267
  // feed declines to mirror still reaches the user through the banner, and
287
268
  // this row is what makes that landing honest.
288
269
  if (strategy === "start_new_conversation" && !signal.requiresConversation) {
@@ -767,45 +748,44 @@ async function resolveChannelDeliveryHome(params: {
767
748
  * Indexing is skipped for parity with the other notification write paths:
768
749
  * notification copy is delivery audit, not conversational memory.
769
750
  */
770
- function resolveSourceConversationId(signal: NotificationSignal): string | null {
771
- if (!signal.sourceContextId) {
751
+ async function appendBodyToSourceConversation(
752
+ signal: NotificationSignal,
753
+ channel: NotificationChannel,
754
+ messageContent: string,
755
+ ): Promise<{ conversationId: string; messageId: string } | null> {
756
+ const sourceContextId = signal.sourceContextId;
757
+ if (!sourceContextId) {
772
758
  return null;
773
759
  }
760
+
761
+ let existing: ReturnType<typeof getConversation>;
774
762
  try {
775
- return getConversation(signal.sourceContextId)?.id ?? null;
763
+ existing = getConversation(sourceContextId);
776
764
  } catch {
777
765
  return null;
778
766
  }
779
- }
780
-
781
- async function appendBodyToSourceConversation(
782
- signal: NotificationSignal,
783
- channel: NotificationChannel,
784
- messageContent: string,
785
- ): Promise<{ conversationId: string; messageId: string } | null> {
786
- const conversationId = resolveSourceConversationId(signal);
787
- if (!conversationId) {
767
+ if (!existing) {
788
768
  return null;
789
769
  }
790
770
 
791
- const message = await addMessage(conversationId, "assistant", messageContent, {
771
+ const message = await addMessage(existing.id, "assistant", messageContent, {
792
772
  skipIndexing: true,
793
773
  });
794
774
  // `addMessage` projects attention metadata alone, so a client with this
795
775
  // conversation open needs the messages tag to refetch the transcript. A
796
776
  // notification the user taps through to has every chance of landing on an
797
777
  // already-open conversation.
798
- publishConversationMessagesChanged(conversationId);
778
+ publishConversationMessagesChanged(existing.id);
799
779
 
800
780
  log.info(
801
781
  {
802
782
  signalId: signal.signalId,
803
783
  channel,
804
- conversationId,
784
+ conversationId: existing.id,
805
785
  messageId: message.id,
806
786
  },
807
787
  "Appended notification body to producing conversation",
808
788
  );
809
789
 
810
- return { conversationId, messageId: message.id };
790
+ return { conversationId: existing.id, messageId: message.id };
811
791
  }
@@ -39,12 +39,6 @@ export interface DeliveredChannelPost {
39
39
  text: string;
40
40
  /** The message id the channel assigned when it acknowledged the post. */
41
41
  providerMessageId: string;
42
- /**
43
- * The ids of the further posts the same text became when the channel
44
- * split it, in send order. Each is reconciled onto the row like the first,
45
- * so a reaction, edit, or delete naming any chunk resolves to this row.
46
- */
47
- additionalProviderMessageIds?: readonly string[];
48
42
  /**
49
43
  * The conversation whose turn made the post, when a different one from
50
44
  * the home it is recorded in (the messaging tool sending from a scheduled
@@ -81,11 +75,7 @@ export async function recordDeliveredChannelPost(
81
75
  : {}),
82
76
  },
83
77
  });
84
- const reconcile = makeSentMessageIdReconciler(row.id);
85
- await reconcile(post.providerMessageId);
86
- for (const id of post.additionalProviderMessageIds ?? []) {
87
- await reconcile(id);
88
- }
78
+ await makeSentMessageIdReconciler(row.id)(post.providerMessageId);
89
79
  // A resident conversation reloads its history on the next turn so the
90
80
  // post is in context; the client refetches the transcript.
91
81
  findConversation(post.conversationId)?.markHistoryStale();