@vellumai/assistant 0.12.3-staging.1 → 0.12.3-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 (560) hide show
  1. package/ARCHITECTURE.md +43 -29
  2. package/Dockerfile +63 -0
  3. package/docs/architecture/security.md +10 -5
  4. package/docs/architecture/turn-actor.md +2 -0
  5. package/docs/desktop-browser-cli.md +45 -4
  6. package/docs/live-voice-task-outcomes.md +100 -0
  7. package/docs/mode-sessions.md +183 -0
  8. package/docs/voice-input-diagnostics.md +58 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  17. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  18. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  19. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  20. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  22. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  23. package/openapi.yaml +665 -14
  24. package/package.json +1 -1
  25. package/scripts/smoke-desktop-browser-cli.ts +10 -26
  26. package/scripts/smoke-desktop-window-theme.ts +2 -3
  27. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  28. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  29. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  30. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  31. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  32. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  33. package/src/__tests__/approval-message-composer.test.ts +1 -118
  34. package/src/__tests__/approval-routes-http.test.ts +3 -0
  35. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  37. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  38. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  39. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  40. package/src/__tests__/btw-routes.test.ts +4 -1
  41. package/src/__tests__/build-persisted-content.test.ts +1 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  43. package/src/__tests__/call-recovery.test.ts +7 -2
  44. package/src/__tests__/call-routes-http.test.ts +12 -0
  45. package/src/__tests__/call-store.test.ts +7 -2
  46. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  47. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  48. package/src/__tests__/checker.test.ts +11 -3
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  50. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  51. package/src/__tests__/computer-use-tools.test.ts +1 -233
  52. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  53. package/src/__tests__/config-schema.test.ts +16 -0
  54. package/src/__tests__/config-watcher.test.ts +24 -0
  55. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  56. package/src/__tests__/context-search-fanout.test.ts +1 -1
  57. package/src/__tests__/context-search-types.test.ts +7 -7
  58. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  59. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  60. package/src/__tests__/conversation-agent-loop.test.ts +546 -16
  61. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  62. package/src/__tests__/conversation-evictor.test.ts +62 -37
  63. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  64. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  65. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  66. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  67. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  68. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  69. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  70. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  73. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  74. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  75. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  76. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  77. package/src/__tests__/conversation-undo.test.ts +61 -0
  78. package/src/__tests__/credential-execution-client.test.ts +6 -6
  79. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  80. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  81. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  83. package/src/__tests__/dm-backfill.test.ts +91 -1
  84. package/src/__tests__/document-create-route.test.ts +88 -0
  85. package/src/__tests__/document-sync-tags.test.ts +27 -0
  86. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  87. package/src/__tests__/events-tail-route.test.ts +25 -0
  88. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  89. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  90. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  91. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  92. package/src/__tests__/heartbeat-service.test.ts +2 -59
  93. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  94. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  95. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  96. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  97. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  98. package/src/__tests__/image-credentials.test.ts +49 -0
  99. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  100. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  101. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  102. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  103. package/src/__tests__/llm-catalog-parity.test.ts +2 -2
  104. package/src/__tests__/media-generate-image.test.ts +49 -1
  105. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  106. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  107. package/src/__tests__/mode-session-history.test.ts +245 -0
  108. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  109. package/src/__tests__/mode-session-wire.test.ts +141 -0
  110. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  111. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  112. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  115. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  116. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  118. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  119. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
  120. package/src/__tests__/process-message-display-content.test.ts +56 -0
  121. package/src/__tests__/reaction-persistence.test.ts +5 -0
  122. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  123. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  124. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  125. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  126. package/src/__tests__/secure-keys.test.ts +43 -1
  127. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  128. package/src/__tests__/server-history-render.test.ts +91 -1
  129. package/src/__tests__/settings-routes.test.ts +50 -1
  130. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  132. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  133. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  134. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  135. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  136. package/src/__tests__/thread-backfill.test.ts +53 -0
  137. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  138. package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
  139. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  140. package/src/__tests__/twilio-routes.test.ts +1 -0
  141. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  142. package/src/activation/progress-store.test.ts +12 -7
  143. package/src/agent/loop.ts +99 -41
  144. package/src/api/events/assistant-turn-start.ts +3 -0
  145. package/src/api/events/generation-handoff.ts +2 -0
  146. package/src/api/events/message-complete.ts +2 -0
  147. package/src/api/events/notification-conversation-created.ts +14 -16
  148. package/src/api/events/notification-intent.ts +4 -2
  149. package/src/api/events/tool-result.ts +85 -0
  150. package/src/api/events/tool-use-start.ts +3 -0
  151. package/src/api/events/user-message-echo.ts +3 -0
  152. package/src/api/index.ts +31 -6
  153. package/src/api/mode-session.test.ts +131 -0
  154. package/src/api/mode-session.ts +148 -0
  155. package/src/api/remember-tool.ts +15 -0
  156. package/src/api/responses/conversation-message.ts +6 -0
  157. package/src/approvals/AGENTS.md +4 -2
  158. package/src/browser/__tests__/operations.test.ts +29 -0
  159. package/src/browser/operation-meta.ts +2 -1
  160. package/src/browser/operations.ts +29 -0
  161. package/src/browser/virtual-desktop-target.ts +2 -8
  162. package/src/bundler/bundle-signer.ts +1 -3
  163. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  164. package/src/calls/__tests__/call-controller.test.ts +94 -0
  165. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  166. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  167. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  168. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  169. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  170. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  171. package/src/calls/barge-in-guard.ts +5 -0
  172. package/src/calls/call-controller.ts +196 -1
  173. package/src/calls/call-domain.ts +4 -0
  174. package/src/calls/call-store.ts +87 -2
  175. package/src/calls/guardian-action-sweep.ts +22 -8
  176. package/src/calls/media-stream-audio-transcode.ts +1 -3
  177. package/src/calls/typesafe-noul.ts +156 -0
  178. package/src/calls/voice-control-protocol.ts +6 -0
  179. package/src/calls/voice-escalation-judge.ts +145 -0
  180. package/src/calls/voice-escalation-target.ts +10 -0
  181. package/src/calls/voice-leg-coordinator.ts +121 -3
  182. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  183. package/src/calls/voice-session-bridge.ts +256 -112
  184. package/src/calls/voice-triage-escalate.ts +7 -0
  185. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  186. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  187. package/src/cli/commands/browser.help.ts +4 -5
  188. package/src/cli/commands/browser.ts +0 -1
  189. package/src/cli/commands/clients.help.ts +46 -0
  190. package/src/cli/commands/clients.ts +116 -0
  191. package/src/cli/commands/credentials.help.ts +6 -1
  192. package/src/cli/commands/notifications.help.ts +16 -2
  193. package/src/cli/commands/notifications.ts +30 -8
  194. package/src/cli/commands/roadmap.help.ts +18 -21
  195. package/src/cli/commands/roadmap.ts +4 -21
  196. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  197. package/src/cli/lib/bundled-marketplace.json +84 -34
  198. package/src/cli/lib/plugin-marketplace.ts +8 -0
  199. package/src/cli/lib/publish-plugin.ts +1 -2
  200. package/src/config/__tests__/default-provider.test.ts +3 -1
  201. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  202. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  203. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  204. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  205. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  206. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  207. package/src/config/call-site-defaults.ts +9 -0
  208. package/src/config/feature-flag-registry.json +16 -0
  209. package/src/config/profile-text-generation.test.ts +2 -2
  210. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  211. package/src/config/schemas/call-site-catalog.ts +14 -0
  212. package/src/config/schemas/channels.ts +0 -18
  213. package/src/config/schemas/llm.ts +3 -1
  214. package/src/config/schemas/mcp.ts +2 -1
  215. package/src/config/schemas/memory-retrospective.ts +11 -2
  216. package/src/config/schemas/services.ts +15 -6
  217. package/src/config/schemas/tools.ts +10 -0
  218. package/src/config/session-groups-gate.ts +21 -0
  219. package/src/config/skills.ts +2 -6
  220. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  221. package/src/conversations/message-consolidation.ts +102 -5
  222. package/src/credential-execution/ces-connect.test.ts +69 -0
  223. package/src/credential-execution/ces-connect.ts +115 -0
  224. package/src/credential-execution/ces-runtime.ts +55 -93
  225. package/src/credential-execution/executable-discovery.ts +1 -11
  226. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  227. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  228. package/src/daemon/browser-mode-session.test.ts +402 -0
  229. package/src/daemon/browser-mode-session.ts +186 -0
  230. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  231. package/src/daemon/computer-use-mode-session.ts +105 -0
  232. package/src/daemon/config-watcher.ts +10 -0
  233. package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
  234. package/src/daemon/conversation-agent-loop.ts +219 -25
  235. package/src/daemon/conversation-evictor.ts +5 -24
  236. package/src/daemon/conversation-history.ts +10 -0
  237. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  238. package/src/daemon/conversation-interrupt.ts +7 -11
  239. package/src/daemon/conversation-messaging.ts +58 -5
  240. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  241. package/src/daemon/conversation-mode-session.ts +1008 -0
  242. package/src/daemon/conversation-process.ts +5 -0
  243. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  244. package/src/daemon/conversation-skill-tools.ts +1 -0
  245. package/src/daemon/conversation-store.ts +4 -4
  246. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  247. package/src/daemon/conversation-surfaces.ts +113 -5
  248. package/src/daemon/conversation-tool-setup.ts +8 -3
  249. package/src/daemon/conversation.ts +143 -41
  250. package/src/daemon/handlers/config-model.ts +7 -5
  251. package/src/daemon/handlers/shared.ts +55 -16
  252. package/src/daemon/host-cu-proxy.ts +51 -22
  253. package/src/daemon/lifecycle.ts +30 -13
  254. package/src/daemon/message-protocol.ts +0 -1
  255. package/src/daemon/message-provenance.ts +28 -4
  256. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  257. package/src/daemon/mode-session-tracking.ts +54 -0
  258. package/src/daemon/process-message.ts +13 -4
  259. package/src/daemon/virtual-desktop-context.ts +21 -0
  260. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  261. package/src/desktop/desktop-accessibility-script.ts +88 -0
  262. package/src/desktop/desktop-accessibility.test.ts +160 -0
  263. package/src/desktop/desktop-accessibility.ts +169 -0
  264. package/src/desktop/desktop-automation-lease.test.ts +76 -68
  265. package/src/desktop/desktop-automation-lease.ts +41 -50
  266. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  267. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  268. package/src/desktop/desktop-computer-use.test.ts +449 -0
  269. package/src/desktop/desktop-computer-use.ts +502 -0
  270. package/src/desktop/desktop-dependencies.ts +47 -293
  271. package/src/desktop/desktop-display.ts +2 -2
  272. package/src/desktop/desktop-panel-config.test.ts +13 -3
  273. package/src/desktop/desktop-panel-config.ts +109 -15
  274. package/src/desktop/desktop-session-manager.test.ts +69 -21
  275. package/src/desktop/desktop-session-manager.ts +76 -22
  276. package/src/desktop/desktop-stream-bridge.test.ts +11 -122
  277. package/src/desktop/desktop-stream-bridge.ts +6 -8
  278. package/src/desktop/desktop-window-theme.ts +5 -25
  279. package/src/desktop/virtual-desktop-feature.ts +52 -2
  280. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  281. package/src/documents/document-store.ts +23 -0
  282. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  283. package/src/heartbeat/heartbeat-service.ts +5 -33
  284. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  285. package/src/ipc/assistant-server.ts +2 -0
  286. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  287. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  288. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  289. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  290. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  291. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  292. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  293. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  294. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  295. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  296. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  297. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  298. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  299. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  300. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  301. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  302. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  303. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  304. package/src/live-voice/camera-mode-session.test.ts +265 -0
  305. package/src/live-voice/camera-mode-session.ts +277 -0
  306. package/src/live-voice/continuation-judge.ts +123 -0
  307. package/src/live-voice/live-activity-reporter.ts +115 -6
  308. package/src/live-voice/live-voice-manager.ts +36 -1
  309. package/src/live-voice/live-voice-photo.ts +88 -7
  310. package/src/live-voice/live-voice-session-manager.ts +15 -0
  311. package/src/live-voice/live-voice-session.ts +1443 -747
  312. package/src/live-voice/live-voice-tts.ts +22 -4
  313. package/src/live-voice/microphone-idle-gate.ts +56 -0
  314. package/src/live-voice/protocol.ts +171 -4
  315. package/src/live-voice/session-controls.ts +11 -3
  316. package/src/live-voice/task-outcomes.ts +131 -0
  317. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  318. package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
  319. package/src/mcp/__tests__/tool-caps.test.ts +28 -1
  320. package/src/mcp/manager.ts +3 -0
  321. package/src/mcp/tool-caps.ts +12 -4
  322. package/src/media/__tests__/image-models.test.ts +58 -0
  323. package/src/media/image-credentials.ts +5 -0
  324. package/src/media/image-models.ts +52 -0
  325. package/src/media/image-service.ts +8 -0
  326. package/src/media/openrouter-image-service.test.ts +133 -0
  327. package/src/media/openrouter-image-service.ts +153 -0
  328. package/src/media/types.ts +1 -1
  329. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  330. package/src/messaging/providers/discord/send.ts +4 -17
  331. package/src/messaging/providers/discord/transport.ts +2 -2
  332. package/src/messaging/providers/outlook/client.ts +16 -22
  333. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  334. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  335. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  336. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  337. package/src/monitoring/__tests__/control.test.ts +17 -5
  338. package/src/monitoring/control.ts +15 -5
  339. package/src/notifications/AGENTS.md +1 -1
  340. package/src/notifications/README.md +83 -99
  341. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  342. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  343. package/src/notifications/__tests__/copy-composer.test.ts +1 -1
  344. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  345. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  346. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  347. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  348. package/src/notifications/adapters/macos.ts +37 -46
  349. package/src/notifications/broadcaster.ts +5 -6
  350. package/src/notifications/channel-allowlist.ts +44 -0
  351. package/src/notifications/copy-composer.ts +0 -23
  352. package/src/notifications/decision-engine.ts +24 -14
  353. package/src/notifications/deterministic-checks.ts +1 -1
  354. package/src/notifications/emit-signal.ts +123 -37
  355. package/src/notifications/home-feed-side-effect.ts +9 -7
  356. package/src/notifications/notification-utils.ts +19 -0
  357. package/src/notifications/signal.ts +4 -19
  358. package/src/notifications/types.ts +15 -0
  359. package/src/oauth/connection-resolver.ts +1 -2
  360. package/src/oauth/oauth-store.ts +1 -3
  361. package/src/oauth/provider-serializer.ts +7 -0
  362. package/src/oauth/seed-providers.ts +30 -0
  363. package/src/onboarding/onboarding-events-store.ts +99 -12
  364. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  365. package/src/persistence/client-connection-events-store.ts +377 -0
  366. package/src/persistence/conversation-crud.ts +55 -6
  367. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  368. package/src/persistence/conversation-mode-sessions.ts +460 -0
  369. package/src/persistence/conversation-queries.ts +6 -19
  370. package/src/persistence/llm-usage-store.ts +3 -9
  371. package/src/persistence/message-metadata.ts +25 -0
  372. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  373. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  374. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  375. package/src/persistence/schema/conversations.ts +67 -0
  376. package/src/persistence/schema/infrastructure.ts +29 -0
  377. package/src/persistence/steps.ts +17 -0
  378. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  379. package/src/plugin-api/effective-context-window.ts +33 -0
  380. package/src/plugin-api/index.ts +24 -5
  381. package/src/plugin-api/plugin-name-env.ts +125 -0
  382. package/src/plugin-api/resolve-credential.ts +79 -20
  383. package/src/plugin-api/vision-support.test.ts +22 -0
  384. package/src/plugin-api/vision-support.ts +17 -5
  385. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  386. package/src/plugins/active-plugin-env.ts +51 -0
  387. package/src/plugins/defaults/index.ts +24 -0
  388. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  389. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  390. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  391. package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
  392. package/src/plugins/defaults/memory/context-search/format.ts +51 -7
  393. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  394. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  395. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  396. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  397. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  398. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  399. package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
  400. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  401. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  402. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
  403. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  404. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
  405. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  406. package/src/plugins/defaults/memory/tools.ts +38 -11
  407. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
  408. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  409. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
  410. package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
  411. package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
  412. package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
  413. package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
  414. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  415. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  416. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  417. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  418. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  419. package/src/plugins/defaults/task-progress/package.json +14 -0
  420. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  421. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  422. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  423. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  424. package/src/providers/anthropic/client.ts +10 -1
  425. package/src/providers/anthropic-gateway-shared.ts +6 -0
  426. package/src/providers/fetch-provider-catalog.ts +11 -0
  427. package/src/providers/gemini/client.ts +25 -8
  428. package/src/providers/inference/adapter-factory.ts +1 -1
  429. package/src/providers/jev/client.test.ts +10 -0
  430. package/src/providers/jev/client.ts +20 -4
  431. package/src/providers/model-catalog.ts +4 -3
  432. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  433. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  434. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  435. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  436. package/src/providers/openai/chat-completions-provider.ts +100 -29
  437. package/src/providers/openai/google-thought-signature.ts +0 -6
  438. package/src/providers/openai/responses-provider.ts +23 -1
  439. package/src/providers/ratelimit.ts +1 -3
  440. package/src/providers/search-provider-catalog.ts +24 -0
  441. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  442. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  443. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  444. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  445. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  446. package/src/providers/types.ts +5 -0
  447. package/src/routes/control.ts +7 -5
  448. package/src/runtime/AGENTS.md +4 -2
  449. package/src/runtime/actor-trust-resolver.ts +3 -3
  450. package/src/runtime/agent-wake.ts +1 -1
  451. package/src/runtime/approval-message-composer.ts +3 -69
  452. package/src/runtime/assistant-event-hub.ts +73 -75
  453. package/src/runtime/assistant-event-publish-options.ts +7 -0
  454. package/src/runtime/assistant-event-targeting.ts +79 -0
  455. package/src/runtime/assistant-stream-state.ts +13 -100
  456. package/src/runtime/background-job-runner.ts +5 -5
  457. package/src/runtime/channel-retry-sweep.ts +1 -0
  458. package/src/runtime/guardian-action-message-composer.ts +8 -161
  459. package/src/runtime/guardian-reply-router.ts +28 -32
  460. package/src/runtime/http-types.ts +6 -0
  461. package/src/runtime/message-composer-types.ts +3 -61
  462. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  463. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  464. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  465. package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
  466. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  467. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  468. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  469. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  470. package/src/runtime/pending-interactions.ts +29 -0
  471. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  472. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  473. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
  474. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  475. package/src/runtime/routes/browser-context.ts +31 -11
  476. package/src/runtime/routes/browser-routes.ts +43 -4
  477. package/src/runtime/routes/btw-routes.ts +9 -4
  478. package/src/runtime/routes/client-routes.ts +108 -0
  479. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  480. package/src/runtime/routes/conversation-routes.ts +113 -27
  481. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  482. package/src/runtime/routes/credential-routes.ts +1 -2
  483. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  484. package/src/runtime/routes/desktop-setup-routes.ts +4 -6
  485. package/src/runtime/routes/documents-routes.ts +51 -0
  486. package/src/runtime/routes/email-routes.ts +3 -7
  487. package/src/runtime/routes/events-routes.ts +75 -54
  488. package/src/runtime/routes/identity-routes.ts +1 -5
  489. package/src/runtime/routes/image-generation-routes.ts +8 -14
  490. package/src/runtime/routes/inbound-message-handler.ts +35 -38
  491. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  492. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  493. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
  494. package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
  495. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  496. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  497. package/src/runtime/routes/migration-routes.ts +73 -2
  498. package/src/runtime/routes/notification-routes.ts +10 -0
  499. package/src/runtime/routes/oauth-providers.ts +7 -0
  500. package/src/runtime/routes/plugins-routes.ts +6 -0
  501. package/src/runtime/routes/roadmap-routes.ts +47 -8
  502. package/src/runtime/routes/secret-routes.ts +1 -1
  503. package/src/runtime/routes/settings-routes.ts +10 -4
  504. package/src/runtime/routes/watcher-routes.ts +1 -3
  505. package/src/runtime/trust-class.test.ts +7 -6
  506. package/src/runtime/trust-class.ts +9 -17
  507. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  508. package/src/schedule/worker-control.ts +15 -5
  509. package/src/security/secure-keys.ts +53 -103
  510. package/src/skills/clawhub.ts +3 -8
  511. package/src/skills/managed-store.ts +1 -5
  512. package/src/stt/types.ts +4 -0
  513. package/src/subagent/notify.ts +54 -34
  514. package/src/subagent/parent-notification.ts +6 -0
  515. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  516. package/src/telemetry/phone-call-funnel.ts +144 -0
  517. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  518. package/src/tools/ask-question/ask-question-tool.ts +6 -4
  519. package/src/tools/browser/browser-execution.ts +22 -59
  520. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  521. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  522. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  523. package/src/tools/client-os.ts +15 -0
  524. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  525. package/src/tools/computer-use/target.ts +42 -0
  526. package/src/tools/document/document-tool.ts +13 -18
  527. package/src/tools/execution-target.ts +14 -16
  528. package/src/tools/host-terminal/host-shell.ts +4 -1
  529. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  530. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  531. package/src/tools/network/web-fetch.ts +274 -7
  532. package/src/tools/network/web-search.ts +468 -6
  533. package/src/tools/permission-checker.ts +3 -3
  534. package/src/tools/policy-context.ts +3 -1
  535. package/src/tools/skills/load.ts +2 -1
  536. package/src/tools/skills/sandbox-runner.ts +11 -2
  537. package/src/tools/skills/skill-script-runner.ts +7 -0
  538. package/src/tools/skills/skill-tool-factory.ts +15 -1
  539. package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
  540. package/src/tools/terminal/safe-env.ts +4 -0
  541. package/src/tools/terminal/shell.ts +2 -0
  542. package/src/tools/tool-approval-handler.ts +5 -1
  543. package/src/tools/tool-types.ts +3 -3
  544. package/src/tools/types.ts +8 -2
  545. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  546. package/src/util/abort-reasons.ts +18 -26
  547. package/src/util/canonicalize-identity.test.ts +21 -0
  548. package/src/util/canonicalize-identity.ts +16 -0
  549. package/src/util/errors.ts +13 -3
  550. package/src/util/provider-error-patterns.ts +3 -0
  551. package/src/util/worker-process.ts +77 -35
  552. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  553. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  554. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  555. package/src/workspace/migrations/registry.ts +2 -0
  556. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  557. package/src/api/events/heartbeat-alert.ts +0 -20
  558. package/src/daemon/message-types/web-activity.ts +0 -65
  559. package/src/desktop/desktop-dependencies.test.ts +0 -131
  560. package/src/tools/computer-use/definitions.ts +0 -607
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Durable client connection history.
3
+ *
4
+ * The event hub writes one row per client subscribe/dispose. Readers
5
+ * coalesce short reconnects into flaps so a watchdog cycle is not an
6
+ * outage. Writes are best-effort: a missing or unready database never
7
+ * throws back into the hub.
8
+ */
9
+
10
+ import { and, asc, eq, lt, lte, type SQL } from "drizzle-orm";
11
+ import { v4 as uuid } from "uuid";
12
+
13
+ import type { InterfaceId } from "../channels/types.js";
14
+ import { getLogger } from "../util/logger.js";
15
+ import type { DrizzleDb } from "./db-connection.js";
16
+ import * as dbConnection from "./db-connection.js";
17
+ import { clientConnectionEvents } from "./schema/index.js";
18
+
19
+ const log = getLogger("client-connection-events-store");
20
+
21
+ /** How long a reconnect gap can be and still count as a flap, not an outage. */
22
+ export const CLIENT_CONNECTION_FLAP_WINDOW_MS = 60_000;
23
+
24
+ /** Rows older than this are deleted on each successful write. */
25
+ export const CLIENT_CONNECTION_EVENT_RETENTION_MS = 90 * 24 * 60 * 60 * 1000;
26
+
27
+ export const CLIENT_CONNECTION_EVENT_REASONS = [
28
+ "sse_open",
29
+ "sse_close",
30
+ "stale_replaced",
31
+ "shed_backpressure",
32
+ "force_disconnect",
33
+ "cap_evicted",
34
+ ] as const;
35
+
36
+ export type ClientConnectionEventReason =
37
+ (typeof CLIENT_CONNECTION_EVENT_REASONS)[number];
38
+
39
+ /** Session-only close when a later open arrives with no recorded close. */
40
+ export type ClientConnectionSessionCloseReason =
41
+ | ClientConnectionEventReason
42
+ | "implicit_replaced";
43
+
44
+ const CLOSE_REASONS = new Set<ClientConnectionEventReason>([
45
+ "sse_close",
46
+ "stale_replaced",
47
+ "shed_backpressure",
48
+ "force_disconnect",
49
+ "cap_evicted",
50
+ ]);
51
+
52
+ export interface ClientConnectionEventInput {
53
+ clientId: string;
54
+ interfaceId: InterfaceId | string;
55
+ connectionId: string;
56
+ reason: ClientConnectionEventReason;
57
+ occurredAt?: number;
58
+ actorPrincipalId?: string;
59
+ clientVersion?: string;
60
+ sseWatchdog?: boolean;
61
+ machineName?: string;
62
+ }
63
+
64
+ export interface ClientConnectionEvent {
65
+ id: string;
66
+ clientId: string;
67
+ interfaceId: string;
68
+ connectionId: string;
69
+ reason: ClientConnectionEventReason;
70
+ occurredAt: number;
71
+ actorPrincipalId: string | null;
72
+ clientVersion: string | null;
73
+ sseWatchdog: boolean | null;
74
+ machineName: string | null;
75
+ }
76
+
77
+ export interface ClientConnectionSession {
78
+ clientId: string;
79
+ interfaceId: string;
80
+ startedAt: number;
81
+ endedAt: number | null;
82
+ durationMs: number | null;
83
+ flapCount: number;
84
+ openReason: ClientConnectionEventReason;
85
+ closeReason: ClientConnectionSessionCloseReason | null;
86
+ clientVersion: string | null;
87
+ sseWatchdog: boolean | null;
88
+ machineName: string | null;
89
+ connectionIds: string[];
90
+ }
91
+
92
+ export interface ListClientConnectionHistoryParams {
93
+ clientId?: string;
94
+ interfaceId?: string;
95
+ actorPrincipalId?: string;
96
+ since?: number;
97
+ until?: number;
98
+ /** Max sessions to return after coalescing. Clamped to 1-500. */
99
+ limit?: number;
100
+ now?: number;
101
+ }
102
+
103
+ export interface ClientConnectionHistory {
104
+ events: ClientConnectionEvent[];
105
+ sessions: ClientConnectionSession[];
106
+ }
107
+
108
+ const DEFAULT_SESSION_LIMIT = 50;
109
+ const MAX_SESSION_LIMIT = 500;
110
+
111
+ function resolveDb(database?: DrizzleDb): DrizzleDb | null {
112
+ if (database) {
113
+ return database;
114
+ }
115
+ // Namespace import so a test mock of db-connection that omits `isDbOpen`
116
+ // still loads. Incomplete mocks skip the probe and try `getDb()`.
117
+ if (
118
+ typeof dbConnection.isDbOpen === "function" &&
119
+ !dbConnection.isDbOpen()
120
+ ) {
121
+ return null;
122
+ }
123
+ try {
124
+ return dbConnection.getDb();
125
+ } catch (err) {
126
+ log.debug({ err }, "client connection history skipped: database unready");
127
+ return null;
128
+ }
129
+ }
130
+
131
+ function rowToEvent(
132
+ row: typeof clientConnectionEvents.$inferSelect,
133
+ ): ClientConnectionEvent {
134
+ return {
135
+ id: row.id,
136
+ clientId: row.clientId,
137
+ interfaceId: row.interfaceId,
138
+ connectionId: row.connectionId,
139
+ reason: row.reason as ClientConnectionEventReason,
140
+ occurredAt: row.occurredAt,
141
+ actorPrincipalId: row.actorPrincipalId ?? null,
142
+ clientVersion: row.clientVersion ?? null,
143
+ sseWatchdog: row.sseWatchdog ?? null,
144
+ machineName: row.machineName ?? null,
145
+ };
146
+ }
147
+
148
+ function pruneExpired(db: DrizzleDb, now: number): void {
149
+ const cutoff = now - CLIENT_CONNECTION_EVENT_RETENTION_MS;
150
+ db.delete(clientConnectionEvents)
151
+ .where(lt(clientConnectionEvents.occurredAt, cutoff))
152
+ .run();
153
+ }
154
+
155
+ /**
156
+ * Append one hub lifecycle event. Returns null when the main database is
157
+ * not open or the write fails. Never throws.
158
+ */
159
+ export function recordClientConnectionEvent(
160
+ input: ClientConnectionEventInput,
161
+ database?: DrizzleDb,
162
+ ): ClientConnectionEvent | null {
163
+ const db = resolveDb(database);
164
+ if (!db) {
165
+ return null;
166
+ }
167
+ const now = input.occurredAt ?? Date.now();
168
+ const row = {
169
+ id: uuid(),
170
+ clientId: input.clientId,
171
+ interfaceId: input.interfaceId,
172
+ connectionId: input.connectionId,
173
+ reason: input.reason,
174
+ occurredAt: now,
175
+ actorPrincipalId: input.actorPrincipalId ?? null,
176
+ clientVersion: input.clientVersion ?? null,
177
+ sseWatchdog: input.sseWatchdog ?? null,
178
+ machineName: input.machineName ?? null,
179
+ };
180
+ try {
181
+ db.insert(clientConnectionEvents).values(row).run();
182
+ pruneExpired(db, now);
183
+ return rowToEvent(row);
184
+ } catch (err) {
185
+ log.debug({ err }, "failed to record client connection event");
186
+ return null;
187
+ }
188
+ }
189
+
190
+ function clampLimit(limit: number | undefined): number {
191
+ if (limit == null || !Number.isFinite(limit)) {
192
+ return DEFAULT_SESSION_LIMIT;
193
+ }
194
+ return Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(limit)));
195
+ }
196
+
197
+ function startSession(event: ClientConnectionEvent): ClientConnectionSession {
198
+ return {
199
+ clientId: event.clientId,
200
+ interfaceId: event.interfaceId,
201
+ startedAt: event.occurredAt,
202
+ endedAt: event.reason === "sse_open" ? null : event.occurredAt,
203
+ durationMs: event.reason === "sse_open" ? null : 0,
204
+ flapCount: 0,
205
+ openReason: event.reason,
206
+ closeReason: event.reason === "sse_open" ? null : event.reason,
207
+ clientVersion: event.clientVersion,
208
+ sseWatchdog: event.sseWatchdog,
209
+ machineName: event.machineName,
210
+ connectionIds: [event.connectionId],
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Coalesce raw events into sessions. A close followed by an open within
216
+ * {@link CLIENT_CONNECTION_FLAP_WINDOW_MS} continues the same session and
217
+ * increments `flapCount`.
218
+ */
219
+ export function coalesceClientConnectionSessions(
220
+ events: ClientConnectionEvent[],
221
+ now: number = Date.now(),
222
+ ): ClientConnectionSession[] {
223
+ const byClient = new Map<string, ClientConnectionEvent[]>();
224
+ for (const event of events) {
225
+ const list = byClient.get(event.clientId) ?? [];
226
+ list.push(event);
227
+ byClient.set(event.clientId, list);
228
+ }
229
+
230
+ const sessions: ClientConnectionSession[] = [];
231
+ for (const clientEvents of byClient.values()) {
232
+ clientEvents.sort((a, b) => a.occurredAt - b.occurredAt);
233
+ let current: ClientConnectionSession | null = null;
234
+
235
+ const finalize = () => {
236
+ if (!current) {
237
+ return;
238
+ }
239
+ if (current.endedAt == null) {
240
+ current.durationMs = Math.max(0, now - current.startedAt);
241
+ } else {
242
+ current.durationMs = Math.max(0, current.endedAt - current.startedAt);
243
+ }
244
+ sessions.push(current);
245
+ current = null;
246
+ };
247
+
248
+ for (const event of clientEvents) {
249
+ if (event.reason === "sse_open") {
250
+ if (
251
+ current &&
252
+ current.endedAt != null &&
253
+ event.occurredAt - current.endedAt < CLIENT_CONNECTION_FLAP_WINDOW_MS
254
+ ) {
255
+ current.endedAt = null;
256
+ current.closeReason = null;
257
+ current.flapCount += 1;
258
+ current.connectionIds.push(event.connectionId);
259
+ current.clientVersion = event.clientVersion ?? current.clientVersion;
260
+ current.sseWatchdog = event.sseWatchdog ?? current.sseWatchdog;
261
+ current.machineName = event.machineName ?? current.machineName;
262
+ continue;
263
+ }
264
+ if (current && current.endedAt == null) {
265
+ current.endedAt = event.occurredAt;
266
+ current.closeReason = "implicit_replaced";
267
+ }
268
+ finalize();
269
+ current = startSession(event);
270
+ continue;
271
+ }
272
+
273
+ if (!CLOSE_REASONS.has(event.reason)) {
274
+ continue;
275
+ }
276
+
277
+ if (!current) {
278
+ current = startSession(event);
279
+ continue;
280
+ }
281
+
282
+ current.endedAt = event.occurredAt;
283
+ current.closeReason = event.reason;
284
+ if (!current.connectionIds.includes(event.connectionId)) {
285
+ current.connectionIds.push(event.connectionId);
286
+ }
287
+ }
288
+ finalize();
289
+ }
290
+
291
+ sessions.sort((a, b) => b.startedAt - a.startedAt);
292
+ return sessions;
293
+ }
294
+
295
+ /**
296
+ * Read raw events and coalesced sessions. Returns empty lists when the
297
+ * database is not open.
298
+ */
299
+ export function listClientConnectionHistory(
300
+ params: ListClientConnectionHistoryParams = {},
301
+ database?: DrizzleDb,
302
+ ): ClientConnectionHistory {
303
+ const db = resolveDb(database);
304
+ if (!db) {
305
+ return { events: [], sessions: [] };
306
+ }
307
+
308
+ const now = params.now ?? Date.now();
309
+ const limit = clampLimit(params.limit);
310
+ const filters: SQL[] = [];
311
+ if (params.clientId) {
312
+ filters.push(eq(clientConnectionEvents.clientId, params.clientId));
313
+ }
314
+ if (params.interfaceId) {
315
+ filters.push(eq(clientConnectionEvents.interfaceId, params.interfaceId));
316
+ }
317
+ if (params.actorPrincipalId) {
318
+ filters.push(
319
+ eq(clientConnectionEvents.actorPrincipalId, params.actorPrincipalId),
320
+ );
321
+ }
322
+ if (params.until != null) {
323
+ filters.push(lte(clientConnectionEvents.occurredAt, params.until));
324
+ }
325
+
326
+ try {
327
+ const query = db
328
+ .select()
329
+ .from(clientConnectionEvents)
330
+ .orderBy(asc(clientConnectionEvents.occurredAt));
331
+ const rows =
332
+ filters.length > 0 ? query.where(and(...filters)).all() : query.all();
333
+ const events = rows.map(rowToEvent);
334
+ const sessions = coalesceClientConnectionSessions(events, now)
335
+ .filter((session) => {
336
+ return sessionOverlapsWindow(session, params.since, params.until);
337
+ })
338
+ .slice(0, limit);
339
+ return {
340
+ events: events.filter((event) => {
341
+ return eventInWindow(event, params.since, params.until);
342
+ }),
343
+ sessions,
344
+ };
345
+ } catch (err) {
346
+ log.debug({ err }, "failed to list client connection history");
347
+ return { events: [], sessions: [] };
348
+ }
349
+ }
350
+
351
+ function sessionOverlapsWindow(
352
+ session: ClientConnectionSession,
353
+ since?: number,
354
+ until?: number,
355
+ ): boolean {
356
+ if (since != null && session.endedAt != null && session.endedAt < since) {
357
+ return false;
358
+ }
359
+ if (until != null && session.startedAt > until) {
360
+ return false;
361
+ }
362
+ return true;
363
+ }
364
+
365
+ function eventInWindow(
366
+ event: ClientConnectionEvent,
367
+ since?: number,
368
+ until?: number,
369
+ ): boolean {
370
+ if (since != null && event.occurredAt < since) {
371
+ return false;
372
+ }
373
+ if (until != null && event.occurredAt > until) {
374
+ return false;
375
+ }
376
+ return true;
377
+ }
@@ -1,5 +1,6 @@
1
1
  import { mkdirSync, rmSync } from "node:fs";
2
2
 
3
+ import { TrustClassSchema } from "@vellumai/gateway-client";
3
4
  import {
4
5
  and,
5
6
  asc,
@@ -26,6 +27,7 @@ import {
26
27
  forgetActivationConversation,
27
28
  forgetAllActivationConversations,
28
29
  } from "../activation/progress-store.js";
30
+ import { TolerantModeSessionSchema } from "../api/mode-session.js";
29
31
  import type { ChannelId, InterfaceId } from "../channels/types.js";
30
32
  import { parseChannelId, parseInterfaceId } from "../channels/types.js";
31
33
  import { CHANNEL_IDS, isChannelId } from "../channels/types.js";
@@ -47,7 +49,6 @@ import { runHook } from "../plugins/pipeline.js";
47
49
  import type { ContentBlock } from "../providers/types.js";
48
50
  import { getCurrentSeq } from "../runtime/assistant-stream-state.js";
49
51
  import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
50
- import { trustClassSchema } from "../runtime/trust-class.js";
51
52
  import { UserError } from "../util/errors.js";
52
53
  import { getLogger } from "../util/logger.js";
53
54
  import { getLogsDbPath } from "../util/logs-db-path.js";
@@ -91,6 +92,7 @@ import {
91
92
  REFERENTIAL_FORK_STRATEGY,
92
93
  resolveConversationLineage,
93
94
  } from "./conversation-lineage.js";
95
+ import { repairConversationModeSessionBoundaries } from "./conversation-mode-sessions.js";
94
96
  import { deleteConversationRowsInBatches } from "./conversation-row-batch-delete.js";
95
97
  import {
96
98
  BACKGROUND_CONVERSATION_TYPES,
@@ -333,6 +335,8 @@ const backgroundToolCompletionMetadataSchema = z.object({
333
335
 
334
336
  export const messageMetadataSchema = z
335
337
  .object({
338
+ /** Immutable ownership of this transcript row by a recorded mode session. */
339
+ modeSession: TolerantModeSessionSchema,
336
340
  /**
337
341
  * Epoch ms the content actually happened, when that differs from when
338
342
  * the row was written. Set wherever persistence lags the event: a queued
@@ -383,7 +387,7 @@ export const messageMetadataSchema = z
383
387
  * trust status changes later. Used by the memory write gate (indexer)
384
388
  * and read gate (conversation history loading) to enforce trust-aware access.
385
389
  */
386
- provenanceTrustClass: trustClassSchema.optional(),
390
+ provenanceTrustClass: TrustClassSchema.optional(),
387
391
  /**
388
392
  * Model that actually served this assistant row, carried on the agent
389
393
  * loop's `message_complete` event (the provider's `response.model`, the
@@ -398,6 +402,22 @@ export const messageMetadataSchema = z
398
402
  provenanceSourceChannel: channelIdSchema.optional(),
399
403
  provenanceGuardianExternalUserId: z.string().optional(),
400
404
  provenanceRequesterIdentifier: z.string().optional(),
405
+ /**
406
+ * Contact id of the person who wrote this row, from the gateway trust
407
+ * verdict at persist time. Stamped only on a person's own message or
408
+ * reaction (`actorAuthorProvenance`), never on rows the assistant writes
409
+ * during their turn: the other `provenance*` fields describe the turn,
410
+ * this one the author. Absent when the author resolved to no contact.
411
+ */
412
+ provenanceContactId: z.string().optional(),
413
+ /**
414
+ * Set on a backfilled row whose sender was looked up but no usable gateway
415
+ * verdict came back, so its trust class is the guardian-address fallback
416
+ * and it names no author. Distinguishes that row from a sender the gateway
417
+ * resolved as a stranger, so the lookup can be re-run later. Live ingress
418
+ * never persists such a row: it denies a turn whose verdict failed.
419
+ */
420
+ provenanceLookupFailed: z.boolean().optional(),
401
421
  automated: z.boolean().optional(),
402
422
  /**
403
423
  * Transcript-suppression flag: the row is a machine signal (e.g. the
@@ -469,10 +489,10 @@ export const messageMetadataSchema = z
469
489
  */
470
490
  attachmentStoredPaths: z.record(z.string(), z.string()).optional(),
471
491
  /**
472
- * Marks a role-`"user"` row whose arrival interrupted a turn that had made
473
- * no tool call yet. `loadFromDb` rebuilds the LLM-facing
474
- * `<interrupted_turn>` note from it; the row's own content is exactly what
475
- * the user sent, so clients render nothing extra.
492
+ * Marks a role-`"user"` row whose arrival interrupted a running turn.
493
+ * `loadFromDb` rebuilds the LLM-facing `<interrupted_turn>` note from it;
494
+ * the row's own content is exactly what the user sent, so clients render
495
+ * nothing extra.
476
496
  */
477
497
  interruptedPriorTurn: z.boolean().optional(),
478
498
  memoryInjectedBlock: z.string().optional(),
@@ -577,6 +597,12 @@ export function isProviderErrorMetadata(
577
597
  * assistant rows, and turn grouping closes on them, so display merging and
578
598
  * the turn resolver agree on boundaries. Takes the raw persisted `metadata`
579
599
  * JSON string; malformed JSON and non-assistant roles are never standalone.
600
+ *
601
+ * The web folds adjacent assistant rows again after pagination and reads the
602
+ * same rule off the wire projection in its own `isStandaloneAssistantMessage`
603
+ * (clients/web/src/domains/chat/utils/is-standalone-assistant-message.ts). A
604
+ * kind added here without a matching flag and check there merges on the
605
+ * client anyway.
580
606
  */
581
607
  export function isStandaloneAssistantMessage(
582
608
  role: string,
@@ -4337,6 +4363,24 @@ export function purgeConversationSegments(
4337
4363
  return segmentIds;
4338
4364
  }
4339
4365
 
4366
+ /** Repair derived mode-session boundaries without changing delete success. */
4367
+ function repairModeSessionBoundariesAfterDelete(conversationId: string): void {
4368
+ const maxAttempts = 2;
4369
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
4370
+ try {
4371
+ repairConversationModeSessionBoundaries(conversationId);
4372
+ return;
4373
+ } catch (err) {
4374
+ if (attempt === maxAttempts) {
4375
+ log.warn(
4376
+ { err, conversationId, attempts: maxAttempts },
4377
+ "Failed to repair mode session boundaries after message deletion; continuing",
4378
+ );
4379
+ }
4380
+ }
4381
+ }
4382
+ }
4383
+
4340
4384
  export function deleteLastExchange(conversationId: string): number {
4341
4385
  const db = getDb();
4342
4386
 
@@ -4441,6 +4485,10 @@ export function deleteLastExchange(conversationId: string): number {
4441
4485
  } satisfies MessageDeletedInputContext);
4442
4486
  }
4443
4487
 
4488
+ if (deleted > 0) {
4489
+ repairModeSessionBoundariesAfterDelete(conversationId);
4490
+ }
4491
+
4444
4492
  return deleted;
4445
4493
  }
4446
4494
 
@@ -4751,6 +4799,7 @@ export function deleteMessageById(
4751
4799
  messageId,
4752
4800
  createdAt: msgRow.createdAt,
4753
4801
  } satisfies MessageDeletedInputContext);
4802
+ repairModeSessionBoundariesAfterDelete(msgRow.conversationId);
4754
4803
  }
4755
4804
 
4756
4805
  return result;