@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
@@ -164,6 +164,23 @@ const PAGE_2_LEAD: Section = {
164
164
  text: "y",
165
165
  ordinal: 0,
166
166
  };
167
+ const DEFAULT_FINDER = [
168
+ {
169
+ slug: "page-1",
170
+ section: PAGE_1_LEAD,
171
+ descriptor: "",
172
+ lane: "needle" as const,
173
+ },
174
+ {
175
+ slug: "page-2",
176
+ section: PAGE_2_LEAD,
177
+ descriptor: "",
178
+ lane: "dense" as const,
179
+ },
180
+ { slug: "page-3", descriptor: "", lane: "edge" as const },
181
+ { slug: "page-4", descriptor: "", lane: "reply" as const },
182
+ { slug: "page-5", descriptor: "", lane: "learned" as const },
183
+ ];
167
184
  const orchestrateSpy = mock(
168
185
  async (): Promise<OrchestrateResult> => ({
169
186
  selections: [
@@ -181,20 +198,18 @@ const orchestrateSpy = mock(
181
198
  hot: ["page-hot"],
182
199
  fresh: ["page-fresh"],
183
200
  always: [CAPABILITY_SLUG],
184
- finder: [
185
- {
186
- slug: "page-1",
187
- section: PAGE_1_LEAD,
188
- descriptor: "",
189
- lane: "needle",
190
- },
191
- { slug: "page-2", section: PAGE_2_LEAD, descriptor: "", lane: "dense" },
192
- { slug: "page-3", descriptor: "", lane: "edge" },
193
- { slug: "page-4", descriptor: "", lane: "reply" },
194
- { slug: "page-5", descriptor: "", lane: "learned" },
195
- ],
201
+ finder: DEFAULT_FINDER,
196
202
  },
197
203
  selectorRan: true,
204
+ pool: {
205
+ stable: [
206
+ { slug: "page-core", card: "core card", lane: "core" },
207
+ { slug: "page-hot", card: "hot card", lane: "hot" },
208
+ { slug: "page-fresh", card: "fresh card", lane: "fresh" },
209
+ { slug: CAPABILITY_SLUG, card: "skill card", lane: "always" },
210
+ ],
211
+ finder: DEFAULT_FINDER,
212
+ },
198
213
  }),
199
214
  );
200
215
 
@@ -665,6 +680,11 @@ function chosenBySlug(): Record<string, boolean> {
665
680
  * needle lane, page-2 via dense, page-3 via edge) whose selector kept exactly
666
681
  * `kept`. */
667
682
  function poolOf(kept: string[]): OrchestrateResult {
683
+ const finder = [
684
+ { slug: "page-1", descriptor: "", lane: "needle" as const },
685
+ { slug: "page-2", descriptor: "", lane: "dense" as const },
686
+ { slug: "page-3", descriptor: "", lane: "edge" as const },
687
+ ];
668
688
  return {
669
689
  selections: kept.map((slug) => ({ slug, sections: [] })),
670
690
  lanes: {
@@ -672,13 +692,10 @@ function poolOf(kept: string[]): OrchestrateResult {
672
692
  hot: [],
673
693
  fresh: [],
674
694
  always: [],
675
- finder: [
676
- { slug: "page-1", descriptor: "", lane: "needle" },
677
- { slug: "page-2", descriptor: "", lane: "dense" },
678
- { slug: "page-3", descriptor: "", lane: "edge" },
679
- ],
695
+ finder,
680
696
  },
681
697
  selectorRan: true,
698
+ pool: { stable: [], finder },
682
699
  };
683
700
  }
684
701
 
@@ -893,6 +910,15 @@ describe("memory-v3 engine", () => {
893
910
  text: "x",
894
911
  ordinal: 1,
895
912
  };
913
+ const finder = [
914
+ {
915
+ slug: "page-rare",
916
+ section: inventory,
917
+ terms: ["turnip"],
918
+ descriptor: "",
919
+ lane: "rare" as const,
920
+ },
921
+ ];
896
922
  orchestrateSpy.mockImplementationOnce(async () => ({
897
923
  selections: [{ slug: "page-rare", sections: [inventory] }],
898
924
  lanes: {
@@ -900,17 +926,10 @@ describe("memory-v3 engine", () => {
900
926
  hot: [],
901
927
  fresh: [],
902
928
  always: [],
903
- finder: [
904
- {
905
- slug: "page-rare",
906
- section: inventory,
907
- terms: ["turnip"],
908
- descriptor: "",
909
- lane: "rare",
910
- },
911
- ],
929
+ finder,
912
930
  },
913
931
  selectorRan: true,
932
+ pool: { stable: [], finder },
914
933
  }));
915
934
 
916
935
  await observeTurn("conv-1", 2);
@@ -1023,7 +1042,7 @@ describe("memory-v3 engine", () => {
1023
1042
  keptAll: false,
1024
1043
  gateReason: "dense_pass",
1025
1044
  pool: {
1026
- stable: [{ slug: "page-core", card: "core card" }],
1045
+ stable: [{ slug: "page-core", card: "core card", lane: "core" }],
1027
1046
  finder,
1028
1047
  },
1029
1048
  }));
@@ -67,7 +67,9 @@
67
67
  * 3. A SINGLE forced-tool select (`selectPool`) over the whole pool. The
68
68
  * result is this turn's selections — current turn only. Cross-turn
69
69
  * persistence is the injector's job (net-new blocks frozen into history),
70
- * not a per-turn re-rendered carry set.
70
+ * not a per-turn re-rendered carry set. When the call site resolves to
71
+ * TypeSafe, `selectPool` asks one System One noul per numbered candidate
72
+ * instead of forcing `select_pages`.
71
73
  */
72
74
 
73
75
  import type { AssistantConfig } from "../../../../config/schema.js";
@@ -338,30 +340,34 @@ export interface OrchestrateResult {
338
340
  * carried-forward set unioned in. */
339
341
  selections: SelectedPage[];
340
342
  /** The candidate lanes in cache order; see {@link OrchestrateLanes}. Consumed
341
- * by the selection telemetry (lane attribution), the per-turn pool record
342
- * (`pool-log-store.ts`), and the downstream selector rendering. */
343
+ * by selection telemetry for lane attribution and retained as the complete
344
+ * retrieval diagnostics even when selector budgeting trims `pool`. */
343
345
  lanes: OrchestrateLanes;
344
346
  /** Whether the selector LLM judged a non-empty pool this turn (the
345
347
  * `selector_ran` telemetry field). False when the pool was empty, when the
346
348
  * disabled-selector passthrough kept every candidate, when a closed
347
349
  * injection gate hard-skipped selection, and when the selector's provider
348
- * failed and the stable prefix was kept unjudged (`selectorFailure`). On
349
- * the gate path `lanes` still carries the stable prefix as computed, but
350
- * no pool was ever assembled, so a false value with empty `selections`
351
- * means the selector was given nothing. */
350
+ * failed and the retained stable candidates were kept unjudged
351
+ * (`selectorFailure`). A false value with empty `selections` means the
352
+ * selector was given nothing only when `pool` is absent; a failed attempt
353
+ * can carry a finder-only pool with no fallback selections. */
352
354
  selectorRan: boolean;
353
355
  /** Set when the selector could not be run this turn (provider unavailable,
354
356
  * or no usable tool call after the re-prompt retries). `selections` then
355
- * holds the stable prefix unjudged; finder candidates are dropped because
356
- * they are evidence for a judge, not evidence to inject on their own. The
357
+ * holds the retained stable candidates unjudged; finder candidates are
358
+ * dropped because they are evidence for a judge, not evidence to inject on
359
+ * their own. The
357
360
  * live injector queues a notice so the person knows this turn drew on
358
361
  * core memories only. */
359
362
  selectorFailure?: MemoryV3RetrievalUnavailableError;
360
363
  /** The pool as the selector was given it, in one numbering: the
361
364
  * stable-prefix cards, then the finder lines. Absent when no pool was
362
- * assembled this turn (a closed gate's hard skip). Read by the pool input
363
- * capture (`buildPoolInput` in `pool-log-store.ts`). */
365
+ * assembled this turn (a closed gate's hard skip). Read by the pool record
366
+ * and input capture (`pool-log-store.ts`). */
364
367
  pool?: SelectorPool;
368
+ /** The exact context strings rendered for the selector request. Absent when
369
+ * no selector pool was assembled. */
370
+ selectorTurn?: MemoryRoutingTurn;
365
371
  /** Whether the selector's recall-safe keep-all fallback fired
366
372
  * (`selectPool`'s `keptAll`). Absent when no pool was assembled. */
367
373
  keptAll?: boolean;
@@ -404,7 +410,12 @@ export async function orchestrate(
404
410
  // the byte-stable-prefix contract). Built lazily so the gate's bypass path can
405
411
  // reuse the exact same construction as the normal step-2 pool assembly.
406
412
  const buildStable = (): StableCandidate[] =>
407
- [...core, ...hot, ...fresh, ...always].map((slug) => {
413
+ [
414
+ ...core.map((slug) => ({ slug, lane: "core" as const })),
415
+ ...hot.map((slug) => ({ slug, lane: "hot" as const })),
416
+ ...fresh.map((slug) => ({ slug, lane: "fresh" as const })),
417
+ ...always.map((slug) => ({ slug, lane: "always" as const })),
418
+ ].map(({ slug, lane }) => {
408
419
  const card = deps.prefixCards.get(slug);
409
420
  if (card === undefined) {
410
421
  // Lane init renders a card for every core/hot slug; a hole here means
@@ -415,7 +426,7 @@ export async function orchestrate(
415
426
  `memory-v3: no pre-rendered card for stable-prefix slug "${slug}"`,
416
427
  );
417
428
  }
418
- return { slug, card };
429
+ return { slug, card, lane };
419
430
  });
420
431
 
421
432
  // Run the selector over a pool, or pass its candidates straight through when
@@ -438,39 +449,51 @@ export async function orchestrate(
438
449
  ): Promise<{
439
450
  selections: SelectedPage[];
440
451
  keptAll: boolean;
452
+ pool: SelectorPool;
453
+ selectorTurn: MemoryRoutingTurn;
441
454
  failure?: MemoryV3RetrievalUnavailableError;
442
455
  }> =>
443
456
  timeLatencySubSpan("v3_selection", "Memory selection", async () => {
444
457
  if (deps.selectorEnabled === false) {
445
- return { selections: selectAllPoolCandidates(pool), keptAll: false };
458
+ return {
459
+ selections: selectAllPoolCandidates(pool),
460
+ keptAll: false,
461
+ pool,
462
+ selectorTurn: turn,
463
+ };
446
464
  }
447
465
  try {
448
- const { pages, keptAll } = await selectPool(
449
- pool,
450
- turn,
451
- deps.selectorPrompt,
452
- );
453
- return { selections: pages, keptAll };
466
+ const selection = await selectPool(pool, turn, deps.selectorPrompt);
467
+ return {
468
+ selections: selection.pages,
469
+ keptAll: selection.keptAll,
470
+ pool: selection.pool,
471
+ selectorTurn: selection.turn,
472
+ };
454
473
  } catch (err) {
455
474
  if (!(err instanceof MemoryV3RetrievalUnavailableError)) {
456
475
  throw err;
457
476
  }
477
+ const attemptedPool = err.pool ?? pool;
478
+ const attemptedTurn = err.turn ?? turn;
458
479
  log.warn(
459
480
  {
460
481
  conversationId: turn.conversationId,
461
482
  turnNumber: turn.turnNumber,
462
- stableCount: pool.stable.length,
463
- finderCount: pool.finder.length,
483
+ stableCount: attemptedPool.stable.length,
484
+ finderCount: attemptedPool.finder.length,
464
485
  err: err.message,
465
486
  },
466
487
  "memory-v3 selector unavailable; keeping the stable prefix unjudged",
467
488
  );
468
489
  return {
469
490
  selections: selectAllPoolCandidates({
470
- stable: pool.stable,
491
+ stable: attemptedPool.stable,
471
492
  finder: [],
472
493
  }),
473
494
  keptAll: false,
495
+ pool: attemptedPool,
496
+ selectorTurn: attemptedTurn,
474
497
  failure: err,
475
498
  };
476
499
  }
@@ -910,13 +933,23 @@ export async function orchestrate(
910
933
  selections: SelectedPage[],
911
934
  selectorRan: boolean,
912
935
  selectorFailure?: MemoryV3RetrievalUnavailableError,
913
- judged?: { pool: SelectorPool; keptAll: boolean },
936
+ judged?: {
937
+ pool: SelectorPool;
938
+ selectorTurn: MemoryRoutingTurn;
939
+ keptAll: boolean;
940
+ },
914
941
  ): OrchestrateResult => ({
915
942
  selections,
916
943
  lanes: { core, hot, fresh, always, finder: [] },
917
944
  selectorRan,
918
945
  gateReason: gate.reason,
919
- ...(judged ? { pool: judged.pool, keptAll: judged.keptAll } : {}),
946
+ ...(judged
947
+ ? {
948
+ pool: judged.pool,
949
+ selectorTurn: judged.selectorTurn,
950
+ keptAll: judged.keptAll,
951
+ }
952
+ : {}),
920
953
  ...(selectorFailure ? { selectorFailure } : {}),
921
954
  });
922
955
  if (deps.gateConfig.bypassForCore) {
@@ -936,19 +969,18 @@ export async function orchestrate(
936
969
  const {
937
970
  selections: bypassed,
938
971
  keptAll,
972
+ pool: selectedPool,
973
+ selectorTurn,
939
974
  failure,
940
975
  } = await runSelection(stableOnly);
941
- recordSelection(
942
- bypassed,
943
- stableOnly.stable.length,
944
- keptAll,
945
- failure,
946
- );
976
+ const selectedPoolSize =
977
+ selectedPool.stable.length + selectedPool.finder.length;
978
+ recordSelection(bypassed, selectedPoolSize, keptAll, failure);
947
979
  return closed(
948
980
  bypassed,
949
- selectorRanOver(stableOnly.stable.length, failure),
981
+ selectorRanOver(selectedPoolSize, failure),
950
982
  failure,
951
- { pool: stableOnly, keptAll },
983
+ { pool: selectedPool, selectorTurn, keptAll },
952
984
  );
953
985
  }
954
986
  // Hard skip: the selector is never consulted, so this is a zero
@@ -1054,15 +1086,23 @@ export async function orchestrate(
1054
1086
  "Gate & edge expansion",
1055
1087
  Date.now() - expandStartedAt,
1056
1088
  );
1057
- const poolSize = stable.length + finderTail.length;
1058
- const { selections, keptAll, failure } = await runSelection(pool);
1059
- recordSelection(selections, poolSize, keptAll, failure);
1089
+ const {
1090
+ selections,
1091
+ keptAll,
1092
+ pool: selectedPool,
1093
+ selectorTurn,
1094
+ failure,
1095
+ } = await runSelection(pool);
1096
+ const selectedPoolSize =
1097
+ selectedPool.stable.length + selectedPool.finder.length;
1098
+ recordSelection(selections, selectedPoolSize, keptAll, failure);
1060
1099
 
1061
1100
  return {
1062
1101
  selections,
1063
1102
  lanes: { core, hot, fresh, always, finder },
1064
- selectorRan: selectorRanOver(poolSize, failure),
1065
- pool,
1103
+ selectorRan: selectorRanOver(selectedPoolSize, failure),
1104
+ pool: selectedPool,
1105
+ selectorTurn,
1066
1106
  keptAll,
1067
1107
  ...(gateOutcome ? { gateReason: gateOutcome.reason } : {}),
1068
1108
  ...(failure ? { selectorFailure: failure } : {}),
@@ -106,30 +106,39 @@ interface StoredPool {
106
106
  }
107
107
 
108
108
  /**
109
- * Build the turn's pool record from an orchestrate result. The stable prefix
110
- * comes first in cache order (core, hot, fresh, always-candidate) as
111
- * whole-page cards with no section; the finder tail follows in surfacing
112
- * order, each line tagged with the lane that surfaced it and the section it
113
- * carries. A finder hit on a stable-prefix page therefore appears twice, and
114
- * a page hit on several sections once per section, exactly as the selector
115
- * saw it. A card or a section-less line reads chosen when its page was kept;
109
+ * Build the turn's pool record from the exact selector pool carried by the
110
+ * orchestrate result. The stable prefix comes first in retained cache order as
111
+ * whole-page cards with no section; the finder tail follows in retained
112
+ * surfacing order, each line tagged with the lane that surfaced it and the
113
+ * section it carries. A finder hit on a stable-prefix page therefore appears
114
+ * twice, and a page hit on several sections once per section, exactly as the
115
+ * selector saw it. A card or a section-less line reads chosen when its page
116
+ * was kept;
116
117
  * a line carrying a section reads chosen when that section was selected.
117
118
  *
118
- * When the selector did not run and nothing was selected, no pool reached it:
119
- * either the injection gate hard-skipped selection (the result's lanes still
120
- * carry the stable prefix, which the selector never saw) or the pool was
121
- * empty. The record is then empty rather than a list of candidates marked
122
- * unchosen, which would read as a rejection the selector never made. The
123
- * disabled-selector passthrough keeps every pooled candidate as a selection,
124
- * so it still records its pool.
119
+ * A result without `pool` records no candidates: the injection gate either
120
+ * hard-skipped selection or no pool was assembled. A failed selector attempt
121
+ * still carries the exact pool it received, with retained stable cards chosen
122
+ * by the unjudged fallback and finder lines unchosen. The disabled-selector
123
+ * passthrough also carries and records its pool.
125
124
  */
126
125
  export function buildPoolRecord(result: OrchestrateResult): PoolRecord {
127
- if (!result.selectorRan && result.selections.length === 0) {
126
+ const pool = result.pool;
127
+ if (pool === undefined) {
128
+ if (result.selections.length > 0) {
129
+ log.warn(
130
+ {
131
+ selectorRan: result.selectorRan,
132
+ selectedCount: result.selections.length,
133
+ },
134
+ "memory-v3 pool record: result has selections but no selector pool",
135
+ );
136
+ }
128
137
  return {
129
138
  candidates: [],
130
139
  pool_size: 0,
131
- selected_count: 0,
132
- selector_ran: false,
140
+ selected_count: result.selections.length,
141
+ selector_ran: result.selectorRan,
133
142
  };
134
143
  }
135
144
  const selected = new Map<Slug, Set<string>>(
@@ -138,30 +147,27 @@ export function buildPoolRecord(result: OrchestrateResult): PoolRecord {
138
147
  new Set(s.sections.map((section) => sectionKey(section))),
139
148
  ]),
140
149
  );
141
- const card = (slug: Slug, lane: PoolLane): PoolCandidateRecord => ({
142
- slug,
143
- lane,
144
- section_title: null,
145
- section_key: null,
146
- chosen: selected.has(slug),
147
- });
148
- const { core, hot, fresh, always, finder } = result.lanes;
149
150
  const candidates: PoolCandidateRecord[] = [
150
- ...core.map((slug) => card(slug, "core")),
151
- ...hot.map((slug) => card(slug, "hot")),
152
- ...fresh.map((slug) => card(slug, "fresh")),
153
- ...always.map((slug) => card(slug, "always")),
154
- ...finder.map(({ slug, lane, section }): PoolCandidateRecord => {
155
- const key = section ? sectionKey(section) : null;
151
+ ...pool.stable.map(
152
+ (candidate): PoolCandidateRecord => ({
153
+ slug: candidate.slug,
154
+ lane: candidate.lane,
155
+ section_title: null,
156
+ section_key: null,
157
+ chosen: selected.has(candidate.slug),
158
+ }),
159
+ ),
160
+ ...pool.finder.map((candidate): PoolCandidateRecord => {
161
+ const key = candidate.section ? sectionKey(candidate.section) : null;
156
162
  return {
157
- slug,
158
- lane,
159
- section_title: section?.title ?? null,
163
+ slug: candidate.slug,
164
+ lane: candidate.lane,
165
+ section_title: candidate.section?.title ?? null,
160
166
  section_key: key,
161
167
  chosen:
162
168
  key === null
163
- ? selected.has(slug)
164
- : (selected.get(slug)?.has(key) ?? false),
169
+ ? selected.has(candidate.slug)
170
+ : (selected.get(candidate.slug)?.has(key) ?? false),
165
171
  };
166
172
  }),
167
173
  ];
@@ -348,15 +354,15 @@ export interface PoolInputCapture {
348
354
 
349
355
  /**
350
356
  * Build the turn's input capture from an orchestrate result and the turn it
351
- * ran on. Each candidate text is exactly what the selector was shown minus
352
- * its pool number: a stable-prefix candidate's pre-rendered card, a finder
353
- * candidate's rendered line ({@link renderFinderLine}), in the pool
354
- * record's order ({@link buildPoolRecord}). A turn whose pool record is
355
- * empty (the selector never judged a pool and nothing was selected)
356
- * captures the context strings and no candidate texts, so the hashes stay
357
- * aligned with that record; a result that carries no selector pool, or one
358
- * that disagrees with its lanes on the candidate count, captures no texts
359
- * either and logs the mismatch.
357
+ * ran on. When selector budgeting trimmed the context, the result's
358
+ * `selectorTurn` is the source of truth. Each candidate text is exactly what
359
+ * the selector was shown minus its pool number: a stable-prefix candidate's
360
+ * pre-rendered card, a finder candidate's rendered line
361
+ * ({@link renderFinderLine}), in the pool
362
+ * record's order ({@link buildPoolRecord}). A result that carries no selector
363
+ * pool captures the turn context and no candidate texts, so the hashes stay
364
+ * aligned with the empty record. Failed attempts carry their exact attempted
365
+ * pool and trimmed context even though `selectorRan` is false.
360
366
  */
361
367
  export function buildPoolInput(
362
368
  result: OrchestrateResult,
@@ -365,44 +371,24 @@ export function buildPoolInput(
365
371
  ): PoolInputCapture {
366
372
  const texts = new Map<string, string>();
367
373
  const hashes: string[] = [];
368
- const recordsPool = result.selectorRan || result.selections.length > 0;
369
- if (recordsPool) {
370
- const { core, hot, fresh, always, finder } = result.lanes;
371
- const expected =
372
- core.length + hot.length + fresh.length + always.length + finder.length;
373
- const rendered =
374
- result.pool === undefined
375
- ? undefined
376
- : [
377
- ...result.pool.stable.map((candidate) => candidate.card),
378
- ...result.pool.finder.map((candidate) =>
379
- renderFinderLine(candidate),
380
- ),
381
- ];
382
- if (rendered === undefined || rendered.length !== expected) {
383
- log.warn(
384
- {
385
- conversationId: turn.conversationId,
386
- turnNumber: turn.turnNumber,
387
- rendered: rendered?.length ?? null,
388
- expected,
389
- },
390
- "memory-v3 pool input capture: the result's pool does not match its lanes; capturing no candidate texts",
391
- );
392
- } else {
393
- for (const text of rendered) {
394
- const hash = hashPoolText(text);
395
- hashes.push(hash);
396
- texts.set(hash, text);
397
- }
374
+ if (result.pool !== undefined) {
375
+ const rendered = [
376
+ ...result.pool.stable.map((candidate) => candidate.card),
377
+ ...result.pool.finder.map((candidate) => renderFinderLine(candidate)),
378
+ ];
379
+ for (const text of rendered) {
380
+ const hash = hashPoolText(text);
381
+ hashes.push(hash);
382
+ texts.set(hash, text);
398
383
  }
399
384
  }
385
+ const selectorTurn = result.selectorTurn ?? turn;
400
386
  return {
401
387
  input: {
402
- situational_context: turn.situationalContext ?? null,
403
- recent_context: turn.recentContext,
404
- current_message: turn.currentMessage,
405
- previous_assistant_message: turn.previousAssistantMessage ?? null,
388
+ situational_context: selectorTurn.situationalContext ?? null,
389
+ recent_context: selectorTurn.recentContext,
390
+ current_message: selectorTurn.currentMessage,
391
+ previous_assistant_message: selectorTurn.previousAssistantMessage ?? null,
406
392
  selector_prompt_hash:
407
393
  result.selectorRan && selectorPrompt !== undefined
408
394
  ? hashPoolText(selectorPrompt)
@@ -72,9 +72,11 @@ const turn: MemoryRoutingTurn = {
72
72
  recentContext: "",
73
73
  };
74
74
 
75
- const pool = {
75
+ const pool: Parameters<typeof selectPool>[0] = {
76
76
  stable: [],
77
- finder: [{ slug: "page-a" as Slug, descriptor: "a descriptor" }],
77
+ finder: [
78
+ { slug: "page-a" as Slug, descriptor: "a descriptor", lane: "needle" },
79
+ ],
78
80
  };
79
81
 
80
82
  describe("selectPool", () => {
@@ -133,6 +135,8 @@ describe("selectPool", () => {
133
135
  expect(await selectPool(pool, turn)).toEqual({
134
136
  pages: [{ slug: "page-a", sections: [] }],
135
137
  keptAll: false,
138
+ pool,
139
+ turn,
136
140
  });
137
141
  });
138
142
 
@@ -144,6 +148,8 @@ describe("selectPool", () => {
144
148
  expect(await selectPool(pool, turn)).toEqual({
145
149
  pages: [{ slug: "page-a", sections: [] }],
146
150
  keptAll: true,
151
+ pool,
152
+ turn,
147
153
  });
148
154
  });
149
155
 
@@ -157,14 +163,14 @@ describe("selectPool", () => {
157
163
  input: { ids: [] },
158
164
  },
159
165
  ]);
160
- const candidate = {
166
+ const candidate: Parameters<typeof renderFinderLine>[0] = {
161
167
  slug: "page-a" as Slug,
162
168
  descriptor: "a descriptor",
163
169
  lane: "needle",
164
170
  };
165
171
  await selectPool(
166
172
  {
167
- stable: [{ slug: "page-s" as Slug, card: "card s" }],
173
+ stable: [{ slug: "page-s" as Slug, card: "card s", lane: "core" }],
168
174
  finder: [candidate],
169
175
  },
170
176
  turn,
@@ -177,9 +183,12 @@ describe("selectPool", () => {
177
183
  });
178
184
 
179
185
  test("an empty candidate pool returns no selections", async () => {
180
- expect(await selectPool({ stable: [], finder: [] }, turn)).toEqual({
186
+ const emptyPool = { stable: [], finder: [] };
187
+ expect(await selectPool(emptyPool, turn)).toEqual({
181
188
  pages: [],
182
189
  keptAll: false,
190
+ pool: emptyPool,
191
+ turn,
183
192
  });
184
193
  });
185
194
  });