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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (439) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/docs/guardian-request-flow.md +0 -16
  5. package/openapi.yaml +49 -291
  6. package/package.json +1 -1
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  8. package/src/__tests__/app-compiler.test.ts +0 -6
  9. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  10. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  11. package/src/__tests__/app-source-watcher.test.ts +218 -0
  12. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  13. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  14. package/src/__tests__/computer-use-tools.test.ts +5 -17
  15. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  16. package/src/__tests__/config-schema.test.ts +29 -28
  17. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  18. package/src/__tests__/config-watcher.test.ts +8 -23
  19. package/src/__tests__/connection-model-compat.test.ts +2 -2
  20. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  21. package/src/__tests__/conversation-pairing.test.ts +0 -26
  22. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  23. package/src/__tests__/conversation-queue.test.ts +207 -48
  24. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  25. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  26. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  27. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  28. package/src/__tests__/conversation-title-service.test.ts +10 -14
  29. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  30. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  31. package/src/__tests__/credential-record-retire.test.ts +1 -171
  32. package/src/__tests__/credential-routes.test.ts +3 -212
  33. package/src/__tests__/credential-security-invariants.test.ts +2 -4
  34. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  35. package/src/__tests__/document-sync-tags.test.ts +0 -25
  36. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  37. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  38. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  39. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  40. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  42. package/src/__tests__/host-file-proxy.test.ts +23 -11
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  45. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  47. package/src/__tests__/mcp-health-check.test.ts +11 -8
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  49. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  51. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  52. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  53. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  54. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  55. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  56. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  57. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  58. package/src/__tests__/model-intents.test.ts +0 -3
  59. package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
  60. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  61. package/src/__tests__/platform-callback-registration.test.ts +16 -8
  62. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  63. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  64. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  66. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  67. package/src/__tests__/speaker-identification.test.ts +56 -0
  68. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  69. package/src/__tests__/subagent-tools.test.ts +2 -3
  70. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  71. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  72. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  73. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  74. package/src/__tests__/workspace-git-service.test.ts +0 -26
  75. package/src/acp/__tests__/agent-process.test.ts +4 -258
  76. package/src/acp/__tests__/auth-required.test.ts +0 -96
  77. package/src/acp/__tests__/client-handler.test.ts +2 -90
  78. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  79. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  80. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  81. package/src/acp/__tests__/session-manager.test.ts +4 -895
  82. package/src/acp/agent-process.test.ts +1 -4
  83. package/src/acp/agent-process.ts +9 -94
  84. package/src/acp/auth-required.ts +1 -50
  85. package/src/acp/auto-install.test.ts +7 -9
  86. package/src/acp/client-handler.ts +3 -16
  87. package/src/acp/resolve-agent.test.ts +6 -220
  88. package/src/acp/resolve-agent.ts +23 -88
  89. package/src/acp/session-manager.test.ts +3 -196
  90. package/src/acp/session-manager.ts +18 -441
  91. package/src/acp/types.ts +5 -50
  92. package/src/api/events/app-files-changed.ts +3 -4
  93. package/src/api/events/notification-intent.ts +0 -2
  94. package/src/api/index.ts +0 -6
  95. package/src/apps/app-store.ts +1 -1
  96. package/src/bundler/app-compiler.ts +0 -15
  97. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  98. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  99. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  100. package/src/calls/call-constants.ts +4 -24
  101. package/src/calls/call-controller.ts +199 -499
  102. package/src/calls/call-transport.ts +0 -16
  103. package/src/calls/media-stream-output.ts +0 -32
  104. package/src/calls/media-stream-server.ts +30 -82
  105. package/src/calls/media-stream-stt-session.ts +0 -15
  106. package/src/calls/resolve-call-tts-provider.ts +2 -33
  107. package/src/calls/speaker-identification.ts +233 -0
  108. package/src/calls/tts-call-strategy.ts +5 -9
  109. package/src/calls/types.ts +0 -2
  110. package/src/calls/voice-control-protocol.ts +21 -53
  111. package/src/calls/voice-session-bridge.ts +46 -59
  112. package/src/calls/voice-triage-escalate.ts +56 -175
  113. package/src/cli/commands/apps.help.ts +13 -67
  114. package/src/cli/commands/apps.ts +0 -183
  115. package/src/cli/commands/bash.help.ts +13 -8
  116. package/src/cli/commands/bash.ts +25 -7
  117. package/src/cli/commands/channels/index.help.ts +2 -70
  118. package/src/cli/commands/channels/index.ts +3 -16
  119. package/src/cli/commands/credentials.help.ts +2 -6
  120. package/src/cli/commands/credentials.ts +2 -2
  121. package/src/cli/commands/mcp.help.ts +8 -8
  122. package/src/cli/commands/mcp.ts +1 -1
  123. package/src/cli/commands/roadmap.help.ts +1 -5
  124. package/src/cli/commands/roadmap.ts +1 -1
  125. package/src/cli/lib/bundled-marketplace.json +90 -103
  126. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  127. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  128. package/src/config/__tests__/platform-rehydration.test.ts +9 -99
  129. package/src/config/acp-defaults.test.ts +3 -13
  130. package/src/config/acp-defaults.ts +6 -23
  131. package/src/config/acp-schema.ts +2 -30
  132. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  133. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  134. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  135. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  136. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  137. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  138. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  139. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  140. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  141. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  142. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  143. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  144. package/src/config/default-profile-catalog.ts +1 -1
  145. package/src/config/env.ts +2 -2
  146. package/src/config/feature-flag-registry.json +12 -24
  147. package/src/config/loader.ts +0 -1
  148. package/src/config/memory-v3-gate.test.ts +0 -31
  149. package/src/config/memory-v3-gate.ts +16 -20
  150. package/src/config/platform-rehydration.ts +27 -11
  151. package/src/config/schema.ts +2 -2
  152. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  153. package/src/config/schemas/calls.ts +28 -1
  154. package/src/config/schemas/live-voice.ts +173 -1
  155. package/src/config/schemas/mcp.ts +1 -1
  156. package/src/config/schemas/memory-retrospective.ts +0 -10
  157. package/src/config/schemas/services.ts +0 -8
  158. package/src/config/schemas/skills.ts +14 -0
  159. package/src/credential-execution/process-manager.test.ts +19 -18
  160. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  161. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  162. package/src/daemon/app-source-watcher.ts +218 -0
  163. package/src/daemon/config-watcher.ts +194 -13
  164. package/src/daemon/conversation-process.ts +64 -35
  165. package/src/daemon/conversation-skill-tools.ts +31 -13
  166. package/src/daemon/conversation-store.ts +1 -1
  167. package/src/daemon/conversation-surfaces.ts +8 -10
  168. package/src/daemon/conversation-tool-setup.ts +43 -37
  169. package/src/daemon/conversation.ts +3 -0
  170. package/src/daemon/host-bash-proxy.ts +12 -8
  171. package/src/daemon/host-browser-proxy.ts +1 -15
  172. package/src/daemon/host-file-proxy.ts +10 -8
  173. package/src/daemon/host-transfer-proxy.ts +18 -14
  174. package/src/daemon/lifecycle.ts +5 -0
  175. package/src/daemon/mcp-reload-service.ts +5 -5
  176. package/src/daemon/message-types/sync.ts +0 -1
  177. package/src/daemon/orphan-reaper.ts +1 -1
  178. package/src/daemon/providers-setup.ts +1 -1
  179. package/src/daemon/shutdown-handlers.ts +9 -1
  180. package/src/daemon/skill-memory-refresh.ts +0 -64
  181. package/src/daemon/tool-side-effects.ts +18 -7
  182. package/src/home/__tests__/feed-writer.test.ts +0 -22
  183. package/src/home/feed-writer.ts +2 -11
  184. package/src/hooks/types.ts +0 -33
  185. package/src/inbound/platform-callback-registration.ts +5 -4
  186. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  187. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  188. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  189. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  190. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  191. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  192. package/src/live-voice/live-voice-session.ts +693 -213
  193. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  194. package/src/live-voice/protocol.ts +2 -0
  195. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  196. package/src/mcp/effective-config.ts +3 -3
  197. package/src/mcp/mcp-header-store.ts +45 -29
  198. package/src/mcp/startup.ts +4 -8
  199. package/src/messaging/provider.ts +3 -10
  200. package/src/messaging/providers/callback-routing.ts +0 -18
  201. package/src/messaging/providers/channel-transport.ts +0 -56
  202. package/src/messaging/providers/discord/transport.ts +0 -17
  203. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  204. package/src/messaging/providers/slack/adapter.ts +44 -7
  205. package/src/messaging/providers/slack/transport.ts +0 -16
  206. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  207. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  208. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  209. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  210. package/src/messaging/providers/whatsapp/client.ts +29 -0
  211. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  212. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  213. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  214. package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
  215. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  216. package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
  217. package/src/notifications/adapters/macos.ts +0 -3
  218. package/src/notifications/assistant-reply-producer.ts +3 -8
  219. package/src/notifications/conversation-pairing.ts +16 -36
  220. package/src/notifications/delivered-post-record.ts +1 -11
  221. package/src/notifications/guardian-feed-projection.ts +5 -125
  222. package/src/notifications/home-feed-side-effect.ts +7 -55
  223. package/src/notifications/schedule-result-producer.ts +22 -66
  224. package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
  225. package/src/oauth/byo-connection.test.ts +0 -45
  226. package/src/oauth/byo-connection.ts +6 -36
  227. package/src/oauth/connection-resolver.test.ts +0 -73
  228. package/src/oauth/connection-resolver.ts +2 -95
  229. package/src/oauth/seed-providers.ts +0 -165
  230. package/src/outbound-proxy/certs.ts +5 -3
  231. package/src/permissions/checker.test.ts +6 -11
  232. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  233. package/src/persistence/conversation-crud.ts +63 -71
  234. package/src/persistence/conversation-lineage.ts +4 -12
  235. package/src/persistence/conversation-title-service.ts +26 -9
  236. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  237. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  238. package/src/persistence/message-reads.ts +0 -30
  239. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  240. package/src/persistence/schema/memory-core.ts +0 -5
  241. package/src/platform/client.test.ts +2 -4
  242. package/src/platform/client.ts +10 -2
  243. package/src/plugin-api/constants.ts +0 -2
  244. package/src/plugin-api/index.ts +0 -1
  245. package/src/plugin-api/types.ts +0 -2
  246. package/src/plugins/defaults/index.ts +0 -2
  247. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  248. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  249. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  250. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  251. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  252. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  253. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  254. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  255. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  256. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  257. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  258. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  259. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  260. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  261. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  262. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  263. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  264. package/src/plugins/defaults/memory/startup.ts +1 -6
  265. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  266. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  267. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  268. package/src/plugins/defaults/memory/worker.ts +0 -6
  269. package/src/plugins/mcp-servers.ts +75 -15
  270. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  271. package/src/providers/gemini/client.ts +23 -9
  272. package/src/providers/inference/auth.ts +3 -2
  273. package/src/providers/inference/connection-availability.ts +8 -27
  274. package/src/providers/inference/resolve-auth.ts +4 -22
  275. package/src/providers/model-catalog.ts +0 -34
  276. package/src/providers/model-intents.ts +5 -10
  277. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  278. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  279. package/src/providers/openai/chat-completions-provider.ts +78 -192
  280. package/src/providers/openai/codex-models.ts +4 -0
  281. package/src/runtime/AGENTS.md +0 -2
  282. package/src/runtime/auth/same-actor.ts +3 -38
  283. package/src/runtime/http-server.ts +151 -160
  284. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  286. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  287. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  288. package/src/runtime/routes/acp-routes.test.ts +0 -15
  289. package/src/runtime/routes/acp-routes.ts +9 -72
  290. package/src/runtime/routes/app-management-routes.ts +0 -73
  291. package/src/runtime/routes/app-routes.ts +1 -1
  292. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  293. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  294. package/src/runtime/routes/credential-routes.ts +10 -19
  295. package/src/runtime/routes/debug-bash-routes.ts +173 -0
  296. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  297. package/src/runtime/routes/index.ts +2 -4
  298. package/src/runtime/routes/log-export-routes.ts +0 -2
  299. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  300. package/src/runtime/routes/migration-routes.ts +20 -6
  301. package/src/runtime/routes/platform-routes.ts +15 -18
  302. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  303. package/src/runtime/services/conversation-serializer.ts +3 -16
  304. package/src/runtime/sync/resource-sync-events.ts +1 -2
  305. package/src/schedule/worker.ts +1 -1
  306. package/src/security/ces-rpc-record-backend.ts +0 -7
  307. package/src/subagent/consult-context.ts +25 -12
  308. package/src/tools/acp/list-agents.test.ts +1 -3
  309. package/src/tools/acp/spawn.test.ts +11 -146
  310. package/src/tools/acp/spawn.ts +3 -21
  311. package/src/tools/acp/status.ts +3 -40
  312. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  313. package/src/tools/capability-offer.test.ts +2 -22
  314. package/src/tools/capability-offer.ts +1 -16
  315. package/src/tools/computer-use/definitions.ts +2 -4
  316. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  317. package/src/tools/credentials/metadata-store.ts +0 -100
  318. package/src/tools/host-filesystem/edit.test.ts +1 -2
  319. package/src/tools/host-filesystem/edit.ts +2 -2
  320. package/src/tools/host-filesystem/read.test.ts +1 -2
  321. package/src/tools/host-filesystem/read.ts +2 -2
  322. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  323. package/src/tools/host-filesystem/transfer.ts +2 -2
  324. package/src/tools/host-filesystem/write.test.ts +1 -2
  325. package/src/tools/host-filesystem/write.ts +2 -2
  326. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  327. package/src/tools/host-terminal/host-shell.ts +20 -4
  328. package/src/tools/policy-context.ts +7 -6
  329. package/src/tools/subagent/spawn.ts +8 -5
  330. package/src/tools/watch/watch-retro-report.ts +8 -0
  331. package/src/util/__tests__/language-subtag.test.ts +1 -23
  332. package/src/util/fs-watcher-error.ts +36 -0
  333. package/src/util/language-subtag.ts +0 -16
  334. package/src/util/platform.ts +0 -8
  335. package/src/workspace/git-service.ts +54 -5
  336. package/src/workspace/migrations/registry.ts +0 -6
  337. package/scripts/smoke-desktop-window-theme.ts +0 -186
  338. package/src/__tests__/git-porcelain.test.ts +0 -47
  339. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  340. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  341. package/src/__tests__/resolve-app.test.ts +0 -135
  342. package/src/__tests__/service-account-token.test.ts +0 -247
  343. package/src/__tests__/source-fingerprint.test.ts +0 -184
  344. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  345. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  346. package/src/acp/model-config.test.ts +0 -166
  347. package/src/acp/model-config.ts +0 -138
  348. package/src/api/events/acp-session-model-update.test.ts +0 -88
  349. package/src/api/events/acp-session-model-update.ts +0 -46
  350. package/src/apps/resolve-app.ts +0 -81
  351. package/src/apps/source-fingerprint.ts +0 -266
  352. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  353. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  354. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  355. package/src/calls/barge-in-guard.ts +0 -118
  356. package/src/calls/voice-leg-coordinator.ts +0 -189
  357. package/src/calls/voice-progress-cadence.ts +0 -423
  358. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  359. package/src/cli/commands/__tests__/bash.test.ts +0 -53
  360. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  361. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  362. package/src/cli/commands/channels/send.ts +0 -128
  363. package/src/config/__tests__/platform-identity.test.ts +0 -255
  364. package/src/config/acp-agent-write.ts +0 -43
  365. package/src/config/acp-schema.test.ts +0 -81
  366. package/src/config/platform-identity.ts +0 -201
  367. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  368. package/src/config/schemas/voice.ts +0 -150
  369. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  370. package/src/daemon/app-change-notify.ts +0 -45
  371. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  372. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  373. package/src/desktop/desktop-chrome-policy.ts +0 -42
  374. package/src/desktop/desktop-dependencies.test.ts +0 -102
  375. package/src/desktop/desktop-dependencies.ts +0 -306
  376. package/src/desktop/desktop-feature.test.ts +0 -18
  377. package/src/desktop/desktop-feature.ts +0 -19
  378. package/src/desktop/desktop-panel-config.test.ts +0 -135
  379. package/src/desktop/desktop-panel-config.ts +0 -149
  380. package/src/desktop/desktop-session-manager.test.ts +0 -627
  381. package/src/desktop/desktop-session-manager.ts +0 -814
  382. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  383. package/src/desktop/desktop-stream-bridge.ts +0 -214
  384. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  385. package/src/desktop/desktop-wallpaper.ts +0 -113
  386. package/src/desktop/desktop-window-theme.ts +0 -170
  387. package/src/i18n/AGENTS.md +0 -22
  388. package/src/i18n/__tests__/i18n.test.ts +0 -108
  389. package/src/i18n/index.ts +0 -16
  390. package/src/i18n/locales.ts +0 -93
  391. package/src/i18n/messages.ts +0 -61
  392. package/src/i18n/title.ts +0 -25
  393. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  394. package/src/mcp/spec-schema.ts +0 -111
  395. package/src/mcp/workspace-mcp-config.ts +0 -188
  396. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  397. package/src/persistence/conversation-title-placeholders.ts +0 -23
  398. package/src/persistence/message-cursor.ts +0 -78
  399. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  400. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  401. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  402. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  403. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  404. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  405. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  406. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  407. package/src/providers/gemini-thought-signature.test.ts +0 -63
  408. package/src/providers/gemini-thought-signature.ts +0 -44
  409. package/src/providers/inference/service-account-token.ts +0 -248
  410. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  411. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  412. package/src/providers/openai/google-thought-signature.ts +0 -320
  413. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  414. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  415. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  416. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  417. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  418. package/src/runtime/channel-send.ts +0 -316
  419. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  420. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  421. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  422. package/src/runtime/routes/channel-send-routes.ts +0 -85
  423. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  424. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  425. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  426. package/src/security/ces-http-record-backend.ts +0 -120
  427. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  428. package/src/skills/workspace-skill-md-signature.ts +0 -50
  429. package/src/tools/acp/status.test.ts +0 -94
  430. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
  431. package/src/tools/terminal/sanitized-bash.ts +0 -105
  432. package/src/util/ca-bundle.ts +0 -13
  433. package/src/workspace/git-porcelain.ts +0 -66
  434. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  435. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  436. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  437. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  438. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  439. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -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) },
@@ -39,35 +39,15 @@ mock.module("../../channels/gateway-guardian-requests.js", () => ({
39
39
  getGuardianRequest: (id: string) => getGuardianRequestStub(id),
40
40
  }));
41
41
 
42
- // The heal's completion sentinel lives in the daemon checkpoint ledger. An
43
- // in-memory map stands in so the heal runs without a database; it is cleared
44
- // per test so every test starts unhealed. The spread keeps the module's other
45
- // exports real for transitive importers.
46
- const actualCheckpoints = await import("../../persistence/checkpoints.js");
47
- const checkpointStore = new Map<string, string>();
48
- mock.module("../../persistence/checkpoints.js", () => ({
49
- ...actualCheckpoints,
50
- getMemoryCheckpoint: (key: string) => checkpointStore.get(key) ?? null,
51
- setMemoryCheckpoint: (key: string, value: string) => {
52
- checkpointStore.set(key, value);
53
- },
54
- }));
55
-
56
42
  const {
57
43
  buildPendingGuardianProjection,
58
44
  guardianFeedItemId,
59
- healLegacyGuardianReceiptUnread,
60
45
  reconcileGuardianFeedProjections,
61
46
  requestIdFromGuardianFeedItemId,
62
47
  writeGuardianFeedReceipt,
63
48
  } = await import("../guardian-feed-projection.js");
64
- const {
65
- appendFeedItem,
66
- bulkSetFeedItemStatus,
67
- getHomeFeedPath,
68
- patchFeedItemStatus,
69
- readHomeFeed,
70
- } = await import("../../home/feed-writer.js");
49
+ const { appendFeedItem, bulkSetFeedItemStatus, getHomeFeedPath, readHomeFeed } =
50
+ await import("../../home/feed-writer.js");
71
51
  const { GUARDIAN_TERMINAL_REASON_SUPERSEDED, isPendingGuardianFeedItem } =
72
52
  await import("../../api/responses/home.js");
73
53
  type FeedItem = import("../../api/responses/home.js").FeedItem;
@@ -81,7 +61,6 @@ beforeEach(() => {
81
61
  process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
82
62
  listGuardianRequestsStub = async () => [];
83
63
  getGuardianRequestStub = async () => null;
84
- checkpointStore.clear();
85
64
  });
86
65
 
87
66
  afterEach(() => {
@@ -125,72 +104,6 @@ function pendingGuardianItem(requestId: string): FeedItem {
125
104
  };
126
105
  }
127
106
 
128
- /**
129
- * A terminal receipt that never passed the pending-to-terminal edge:
130
- * terminal projection, urgency `medium`, still `new`.
131
- */
132
- function legacyGuardianReceiptItem(requestId: string): FeedItem {
133
- const item = pendingGuardianItem(requestId);
134
- return {
135
- ...item,
136
- urgency: "medium",
137
- status: "new",
138
- guardianRequest: { ...item.guardianRequest!, status: "approved" },
139
- };
140
- }
141
-
142
- describe("healLegacyGuardianReceiptUnread", () => {
143
- test("clears unread on a terminal receipt that never passed the edge", async () => {
144
- await appendFeedItem(legacyGuardianReceiptItem("legacy-1"));
145
-
146
- await healLegacyGuardianReceiptUnread();
147
-
148
- expect(
149
- readHomeFeed().items.find((i) => i.id === guardianFeedItemId("legacy-1"))
150
- ?.status,
151
- ).toBe("seen");
152
- });
153
-
154
- test("runs once per assistant, so a later mark-unread stands", async () => {
155
- await appendFeedItem(legacyGuardianReceiptItem("legacy-2"));
156
- const itemId = guardianFeedItemId("legacy-2");
157
- await healLegacyGuardianReceiptUnread();
158
-
159
- // The user marks the resolved receipt unread again. The pass has
160
- // already recorded completion, so a second round leaves it alone.
161
- await patchFeedItemStatus(itemId, "new");
162
- await healLegacyGuardianReceiptUnread();
163
-
164
- expect(readHomeFeed().items.find((i) => i.id === itemId)?.status).toBe(
165
- "new",
166
- );
167
- });
168
-
169
- test("never revives a dismissed receipt", async () => {
170
- const item = legacyGuardianReceiptItem("legacy-3");
171
- await appendFeedItem({ ...item, status: "dismissed" });
172
-
173
- await healLegacyGuardianReceiptUnread();
174
-
175
- expect(
176
- readHomeFeed().items.find((i) => i.id === guardianFeedItemId("legacy-3"))
177
- ?.status,
178
- ).toBe("dismissed");
179
- });
180
-
181
- test("leaves a still-pending request alone", async () => {
182
- await appendFeedItem(pendingGuardianItem("still-pending"));
183
-
184
- await healLegacyGuardianReceiptUnread();
185
-
186
- const item = readHomeFeed().items.find(
187
- (i) => i.id === guardianFeedItemId("still-pending"),
188
- );
189
- expect(item?.status).toBe("new");
190
- expect(item && isPendingGuardianFeedItem(item)).toBe(true);
191
- });
192
- });
193
-
194
107
  describe("buildPendingGuardianProjection", () => {
195
108
  test("tool approval projects as a pending approval with source facts", () => {
196
109
  const projection = buildPendingGuardianProjection(toolApprovalPayload);
@@ -351,75 +264,6 @@ describe("writeGuardianFeedReceipt", () => {
351
264
  expect(item?.guardianRequest?.status).toBe("denied");
352
265
  });
353
266
 
354
- test("a receipt stops the item reading as unread", async () => {
355
- await appendFeedItem(pendingGuardianItem("req-6"));
356
- await writeGuardianFeedReceipt({
357
- requestId: "req-6",
358
- status: "approved",
359
- decidedAction: "approve_once",
360
- });
361
-
362
- const item = readHomeFeed().items.find(
363
- (i) => i.id === guardianFeedItemId("req-6"),
364
- );
365
- // The clients' unread test is `status === "new"`, so a receipt left
366
- // at `new` keeps lighting the bell for work nobody can act on.
367
- expect(item?.status).toBe("seen");
368
- });
369
-
370
- test("a terminal status nobody decided also clears unread", async () => {
371
- await appendFeedItem(pendingGuardianItem("req-7"));
372
- await writeGuardianFeedReceipt({ requestId: "req-7", status: "expired" });
373
-
374
- expect(
375
- readHomeFeed().items.find((i) => i.id === guardianFeedItemId("req-7"))
376
- ?.status,
377
- ).toBe("seen");
378
- });
379
-
380
- test("a status the user set survives a re-run of the fan-out", async () => {
381
- await appendFeedItem(pendingGuardianItem("req-8"));
382
- const itemId = guardianFeedItemId("req-8");
383
- await writeGuardianFeedReceipt({ requestId: "req-8", status: "approved" });
384
- // The user clears the receipt, then the withdrawal fan-out retries
385
- // (its per-request receipt is held back until every surface settles).
386
- await patchFeedItemStatus(itemId, "dismissed");
387
- await writeGuardianFeedReceipt({ requestId: "req-8", status: "approved" });
388
-
389
- expect(readHomeFeed().items.find((i) => i.id === itemId)?.status).toBe(
390
- "dismissed",
391
- );
392
- });
393
-
394
- test("a pending request dismissed outright is not revived by its receipt", async () => {
395
- await appendFeedItem(pendingGuardianItem("req-10"));
396
- const itemId = guardianFeedItemId("req-10");
397
- // Bulk dismissal spares a pending guardian item, but a deliberate
398
- // single-item dismissal stays available and is not undone by the
399
- // resolution that follows it.
400
- await patchFeedItemStatus(itemId, "dismissed");
401
- await writeGuardianFeedReceipt({ requestId: "req-10", status: "denied" });
402
-
403
- expect(readHomeFeed().items.find((i) => i.id === itemId)?.status).toBe(
404
- "dismissed",
405
- );
406
- });
407
-
408
- test("a receipt the user marked unread again stays unread", async () => {
409
- await appendFeedItem(pendingGuardianItem("req-9"));
410
- const itemId = guardianFeedItemId("req-9");
411
- await writeGuardianFeedReceipt({ requestId: "req-9", status: "approved" });
412
- // The bell detail's read toggle writes `new` back onto a resolved
413
- // receipt. The clear runs on the edge into terminal, which has
414
- // already passed, so a later receipt must not take it back.
415
- await patchFeedItemStatus(itemId, "new");
416
- await writeGuardianFeedReceipt({ requestId: "req-9", status: "approved" });
417
-
418
- expect(readHomeFeed().items.find((i) => i.id === itemId)?.status).toBe(
419
- "new",
420
- );
421
- });
422
-
423
267
  test("a request with no item resolves true (nothing to retry)", async () => {
424
268
  expect(
425
269
  await writeGuardianFeedReceipt({ requestId: "ghost", status: "expired" }),
@@ -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