@vellumai/assistant 0.12.2 → 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 (772) hide show
  1. package/.env.example +2 -0
  2. package/AGENTS.md +1 -1
  3. package/ARCHITECTURE.md +43 -29
  4. package/Dockerfile +63 -0
  5. package/docs/architecture/integrations.md +7 -0
  6. package/docs/architecture/memory.md +17 -2
  7. package/docs/architecture/security.md +10 -5
  8. package/docs/architecture/turn-actor.md +2 -0
  9. package/docs/credential-execution-service.md +1 -1
  10. package/docs/desktop-browser-cli.md +50 -5
  11. package/docs/live-voice-task-outcomes.md +100 -0
  12. package/docs/mode-sessions.md +183 -0
  13. package/docs/voice-input-diagnostics.md +58 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  19. package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
  20. package/node_modules/@vellumai/environments/src/shell.ts +24 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  25. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  26. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
  28. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  29. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  30. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  31. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  32. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  33. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  34. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  35. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  36. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  37. package/openapi.yaml +716 -16
  38. package/package.json +1 -1
  39. package/scripts/smoke-desktop-browser-cli.ts +11 -26
  40. package/scripts/smoke-desktop-window-theme.ts +2 -3
  41. package/scripts/sync-llm-catalog.ts +3 -0
  42. package/scripts/voice-ttft-spike.ts +2 -2
  43. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  44. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  45. package/src/__tests__/agent-loop.test.ts +259 -0
  46. package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
  47. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  48. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  49. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  50. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  51. package/src/__tests__/anthropic-provider.test.ts +55 -0
  52. package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
  53. package/src/__tests__/approval-message-composer.test.ts +1 -118
  54. package/src/__tests__/approval-routes-http.test.ts +3 -0
  55. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  56. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  57. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  58. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  59. package/src/__tests__/attachments-store.test.ts +22 -3
  60. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  61. package/src/__tests__/btw-routes.test.ts +4 -1
  62. package/src/__tests__/build-persisted-content.test.ts +1 -1
  63. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  64. package/src/__tests__/call-recovery.test.ts +7 -2
  65. package/src/__tests__/call-routes-http.test.ts +12 -0
  66. package/src/__tests__/call-store.test.ts +7 -2
  67. package/src/__tests__/channel-approval.test.ts +9 -14
  68. package/src/__tests__/channel-reply-delivery.test.ts +56 -0
  69. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  70. package/src/__tests__/chat-credential-redaction.test.ts +23 -0
  71. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  72. package/src/__tests__/checker.test.ts +11 -3
  73. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  74. package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
  75. package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
  76. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  77. package/src/__tests__/computer-use-tools.test.ts +1 -233
  78. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  79. package/src/__tests__/config-schema.test.ts +16 -0
  80. package/src/__tests__/config-watcher.test.ts +24 -0
  81. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  82. package/src/__tests__/context-search-fanout.test.ts +1 -1
  83. package/src/__tests__/context-search-types.test.ts +7 -7
  84. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  85. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  86. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  87. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  88. package/src/__tests__/conversation-agent-loop.test.ts +797 -16
  89. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  90. package/src/__tests__/conversation-attachments.test.ts +106 -0
  91. package/src/__tests__/conversation-evictor.test.ts +62 -37
  92. package/src/__tests__/conversation-fork-crud.test.ts +191 -0
  93. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  94. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  95. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  96. package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
  97. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  98. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  99. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  100. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  101. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  102. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  103. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  104. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  105. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  106. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  107. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  108. package/src/__tests__/conversation-undo.test.ts +61 -0
  109. package/src/__tests__/credential-execution-client.test.ts +72 -55
  110. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  111. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  112. package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
  113. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  114. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  115. package/src/__tests__/dm-backfill.test.ts +91 -1
  116. package/src/__tests__/document-create-route.test.ts +88 -0
  117. package/src/__tests__/document-sync-tags.test.ts +27 -0
  118. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  119. package/src/__tests__/events-tail-route.test.ts +58 -0
  120. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  121. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  122. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  123. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  124. package/src/__tests__/heartbeat-service.test.ts +2 -59
  125. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  126. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  127. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  128. package/src/__tests__/history-repair.test.ts +53 -0
  129. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  130. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  131. package/src/__tests__/image-credentials.test.ts +49 -0
  132. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  133. package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
  134. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  135. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  136. package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
  137. package/src/__tests__/llm-catalog-parity.test.ts +27 -4
  138. package/src/__tests__/media-generate-image.test.ts +49 -1
  139. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  140. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  141. package/src/__tests__/mode-session-history.test.ts +245 -0
  142. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  143. package/src/__tests__/mode-session-wire.test.ts +141 -0
  144. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  145. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  146. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  147. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  148. package/src/__tests__/oauth-commands-routes.test.ts +114 -101
  149. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  150. package/src/__tests__/oauth-provider-serializer.test.ts +13 -0
  151. package/src/__tests__/oauth-providers-routes.test.ts +3 -0
  152. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  153. package/src/__tests__/persist-media-references.test.ts +50 -0
  154. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  155. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  156. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  157. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -1
  158. package/src/__tests__/process-message-display-content.test.ts +56 -0
  159. package/src/__tests__/reaction-persistence.test.ts +5 -0
  160. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  161. package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
  162. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  163. package/src/__tests__/schedule-routes.test.ts +20 -0
  164. package/src/__tests__/scheduler-result-notification.test.ts +23 -4
  165. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  166. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  167. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  168. package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
  169. package/src/__tests__/secret-routes-scrub.test.ts +22 -27
  170. package/src/__tests__/secret-scanner.test.ts +20 -0
  171. package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
  172. package/src/__tests__/secure-keys.test.ts +50 -4
  173. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  174. package/src/__tests__/server-history-render.test.ts +91 -1
  175. package/src/__tests__/server-tool-pairing.test.ts +107 -0
  176. package/src/__tests__/settings-routes.test.ts +50 -1
  177. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  178. package/src/__tests__/skills.test.ts +5 -4
  179. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  180. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  181. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  182. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  183. package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
  184. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  185. package/src/__tests__/terminal-tools.test.ts +8 -0
  186. package/src/__tests__/thread-backfill.test.ts +53 -0
  187. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  188. package/src/__tests__/tool-result-follow-up.test.ts +99 -0
  189. package/src/__tests__/tool-result-metadata-plumbing.test.ts +66 -1
  190. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  191. package/src/__tests__/twilio-routes.test.ts +1 -0
  192. package/src/__tests__/unicode.test.ts +36 -0
  193. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  194. package/src/activation/progress-store.test.ts +12 -7
  195. package/src/agent/loop.ts +136 -53
  196. package/src/agent/tool-result-follow-up.ts +70 -0
  197. package/src/api/attachment-provenance.test.ts +68 -0
  198. package/src/api/computer-use-tool.test.ts +55 -0
  199. package/src/api/computer-use-tool.ts +27 -0
  200. package/src/api/events/assistant-outbound-attachment.ts +3 -0
  201. package/src/api/events/assistant-turn-start.ts +3 -0
  202. package/src/api/events/desktop-activity-changed.ts +10 -0
  203. package/src/api/events/generation-handoff.ts +2 -0
  204. package/src/api/events/message-complete.ts +2 -0
  205. package/src/api/events/notification-conversation-created.ts +14 -16
  206. package/src/api/events/notification-intent.ts +4 -2
  207. package/src/api/events/question-request.ts +1 -0
  208. package/src/api/events/tool-result.ts +85 -0
  209. package/src/api/events/tool-use-start.ts +3 -0
  210. package/src/api/events/user-message-echo.ts +3 -0
  211. package/src/api/index.ts +42 -6
  212. package/src/api/mode-session.test.ts +131 -0
  213. package/src/api/mode-session.ts +148 -0
  214. package/src/api/remember-tool.ts +15 -0
  215. package/src/api/responses/conversation-message.ts +9 -0
  216. package/src/approvals/AGENTS.md +4 -2
  217. package/src/approvals/approval-primitive.ts +5 -2
  218. package/src/approvals/scoped-approval-grants.ts +6 -2
  219. package/src/browser/__tests__/operations.test.ts +29 -0
  220. package/src/browser/operation-meta.ts +2 -1
  221. package/src/browser/operations.ts +29 -0
  222. package/src/browser/virtual-desktop-target.ts +2 -9
  223. package/src/bundler/bundle-signer.ts +1 -3
  224. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  225. package/src/calls/__tests__/call-controller.test.ts +94 -0
  226. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  227. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  228. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  229. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  230. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  231. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  232. package/src/calls/barge-in-guard.ts +5 -0
  233. package/src/calls/call-controller.ts +196 -1
  234. package/src/calls/call-domain.ts +4 -0
  235. package/src/calls/call-store.ts +87 -2
  236. package/src/calls/guardian-action-sweep.ts +22 -8
  237. package/src/calls/media-stream-audio-transcode.ts +1 -3
  238. package/src/calls/typesafe-noul.ts +156 -0
  239. package/src/calls/voice-control-protocol.ts +6 -0
  240. package/src/calls/voice-escalation-judge.ts +145 -0
  241. package/src/calls/voice-escalation-target.ts +10 -0
  242. package/src/calls/voice-leg-coordinator.ts +121 -3
  243. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  244. package/src/calls/voice-session-bridge.ts +256 -112
  245. package/src/calls/voice-triage-escalate.ts +7 -0
  246. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  247. package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
  248. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  249. package/src/cli/commands/__tests__/plugins.test.ts +20 -1
  250. package/src/cli/commands/__tests__/schedules.test.ts +14 -0
  251. package/src/cli/commands/bash.help.ts +4 -3
  252. package/src/cli/commands/browser.help.ts +9 -6
  253. package/src/cli/commands/browser.ts +0 -1
  254. package/src/cli/commands/clients.help.ts +46 -0
  255. package/src/cli/commands/clients.ts +116 -0
  256. package/src/cli/commands/credentials.help.ts +9 -4
  257. package/src/cli/commands/notifications.help.ts +16 -2
  258. package/src/cli/commands/notifications.ts +30 -8
  259. package/src/cli/commands/oauth/index.help.ts +10 -0
  260. package/src/cli/commands/oauth/providers.ts +12 -0
  261. package/src/cli/commands/oauth/request.test.ts +179 -2
  262. package/src/cli/commands/oauth/request.ts +41 -10
  263. package/src/cli/commands/plugins.ts +12 -5
  264. package/src/cli/commands/roadmap.help.ts +18 -21
  265. package/src/cli/commands/roadmap.ts +4 -21
  266. package/src/cli/commands/schedules.ts +2 -0
  267. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  268. package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
  269. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  270. package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
  271. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
  272. package/src/cli/lib/bundled-marketplace.json +906 -1
  273. package/src/cli/lib/bundled-plugin-packages.json +783 -1
  274. package/src/cli/lib/inspect-plugin.ts +11 -4
  275. package/src/cli/lib/plugin-marketplace.ts +8 -0
  276. package/src/cli/lib/publish-plugin.ts +1 -2
  277. package/src/cli/lib/upgrade-plugin.ts +19 -11
  278. package/src/cli/output.ts +18 -4
  279. package/src/config/__tests__/default-provider.test.ts +6 -0
  280. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  281. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  282. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  283. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  284. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  285. package/src/config/bundled-skills/schedule/SKILL.md +6 -6
  286. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  287. package/src/config/call-site-defaults.ts +9 -0
  288. package/src/config/feature-flag-registry.json +16 -0
  289. package/src/config/loader.ts +9 -17
  290. package/src/config/platform-identity.ts +8 -8
  291. package/src/config/profile-text-generation.test.ts +51 -0
  292. package/src/config/profile-text-generation.ts +51 -0
  293. package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
  294. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  295. package/src/config/schemas/call-site-catalog.ts +14 -0
  296. package/src/config/schemas/channels.ts +0 -18
  297. package/src/config/schemas/llm.ts +9 -2
  298. package/src/config/schemas/mcp.ts +6 -1
  299. package/src/config/schemas/memory-retrospective.ts +11 -2
  300. package/src/config/schemas/memory-v3.ts +12 -0
  301. package/src/config/schemas/services.ts +15 -6
  302. package/src/config/schemas/tools.ts +10 -0
  303. package/src/config/session-groups-gate.ts +21 -0
  304. package/src/config/skills.ts +2 -6
  305. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  306. package/src/conversations/message-consolidation.ts +102 -5
  307. package/src/credential-execution/ces-connect.test.ts +69 -0
  308. package/src/credential-execution/ces-connect.ts +115 -0
  309. package/src/credential-execution/ces-runtime.ts +55 -93
  310. package/src/credential-execution/executable-discovery.ts +34 -142
  311. package/src/credential-execution/process-manager.test.ts +20 -24
  312. package/src/credential-execution/process-manager.ts +24 -19
  313. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  314. package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
  315. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  316. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
  317. package/src/daemon/assistant-attachments.ts +20 -12
  318. package/src/daemon/browser-mode-session.test.ts +402 -0
  319. package/src/daemon/browser-mode-session.ts +186 -0
  320. package/src/daemon/chat-credential-redaction.ts +16 -1
  321. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  322. package/src/daemon/computer-use-mode-session.ts +105 -0
  323. package/src/daemon/config-watcher.ts +10 -0
  324. package/src/daemon/conversation-agent-loop-handlers.ts +132 -13
  325. package/src/daemon/conversation-agent-loop.ts +231 -25
  326. package/src/daemon/conversation-attachments.ts +93 -12
  327. package/src/daemon/conversation-evictor.ts +5 -24
  328. package/src/daemon/conversation-history.ts +10 -0
  329. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  330. package/src/daemon/conversation-interrupt.ts +7 -11
  331. package/src/daemon/conversation-messaging.ts +58 -5
  332. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  333. package/src/daemon/conversation-mode-session.ts +1008 -0
  334. package/src/daemon/conversation-process.ts +5 -0
  335. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  336. package/src/daemon/conversation-skill-tools.ts +1 -0
  337. package/src/daemon/conversation-store.ts +4 -4
  338. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  339. package/src/daemon/conversation-surfaces.ts +113 -5
  340. package/src/daemon/conversation-tool-setup.ts +79 -6
  341. package/src/daemon/conversation-turn-finalize.ts +26 -13
  342. package/src/daemon/conversation.ts +215 -58
  343. package/src/daemon/daemon-control.ts +3 -7
  344. package/src/daemon/handlers/config-model.ts +7 -5
  345. package/src/daemon/handlers/shared.ts +55 -16
  346. package/src/daemon/host-cu-proxy.ts +51 -22
  347. package/src/daemon/lifecycle.ts +30 -13
  348. package/src/daemon/mcp-reload-service.ts +1 -1
  349. package/src/daemon/message-protocol.ts +0 -1
  350. package/src/daemon/message-provenance.ts +28 -4
  351. package/src/daemon/message-types/shared.ts +1 -0
  352. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  353. package/src/daemon/mode-session-tracking.ts +54 -0
  354. package/src/daemon/orphan-reaper.ts +4 -3
  355. package/src/daemon/persist-media-references.ts +20 -3
  356. package/src/daemon/process-message.ts +13 -4
  357. package/src/daemon/tool-setup-types.ts +6 -0
  358. package/src/daemon/virtual-desktop-context.ts +21 -0
  359. package/src/daemon/wake-conversation-ops.ts +50 -15
  360. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  361. package/src/desktop/desktop-accessibility-script.ts +88 -0
  362. package/src/desktop/desktop-accessibility.test.ts +160 -0
  363. package/src/desktop/desktop-accessibility.ts +169 -0
  364. package/src/desktop/desktop-automation-lease.test.ts +302 -64
  365. package/src/desktop/desktop-automation-lease.ts +129 -54
  366. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  367. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  368. package/src/desktop/desktop-computer-use.test.ts +449 -0
  369. package/src/desktop/desktop-computer-use.ts +502 -0
  370. package/src/desktop/desktop-dependencies.ts +47 -293
  371. package/src/desktop/desktop-display.ts +2 -2
  372. package/src/desktop/desktop-help.ts +15 -0
  373. package/src/desktop/desktop-panel-config.test.ts +13 -3
  374. package/src/desktop/desktop-panel-config.ts +109 -15
  375. package/src/desktop/desktop-session-manager.test.ts +69 -21
  376. package/src/desktop/desktop-session-manager.ts +76 -22
  377. package/src/desktop/desktop-stream-bridge.test.ts +76 -132
  378. package/src/desktop/desktop-stream-bridge.ts +9 -11
  379. package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
  380. package/src/desktop/desktop-wallpaper-worker.ts +17 -0
  381. package/src/desktop/desktop-wallpaper.test.ts +48 -7
  382. package/src/desktop/desktop-wallpaper.ts +34 -102
  383. package/src/desktop/desktop-window-theme.ts +5 -25
  384. package/src/desktop/virtual-desktop-feature.ts +53 -3
  385. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  386. package/src/documents/document-store.ts +23 -0
  387. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  388. package/src/heartbeat/heartbeat-service.ts +5 -33
  389. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  390. package/src/ipc/assistant-server.ts +2 -0
  391. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  392. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  393. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  394. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  395. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  396. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  397. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  398. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  399. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  400. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  401. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  402. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  403. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  404. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  405. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  406. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  407. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  408. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  409. package/src/live-voice/camera-mode-session.test.ts +265 -0
  410. package/src/live-voice/camera-mode-session.ts +277 -0
  411. package/src/live-voice/continuation-judge.ts +123 -0
  412. package/src/live-voice/live-activity-reporter.ts +115 -6
  413. package/src/live-voice/live-voice-manager.ts +36 -1
  414. package/src/live-voice/live-voice-photo.ts +88 -7
  415. package/src/live-voice/live-voice-session-manager.ts +15 -0
  416. package/src/live-voice/live-voice-session.ts +1443 -747
  417. package/src/live-voice/live-voice-tts.ts +22 -4
  418. package/src/live-voice/microphone-idle-gate.ts +56 -0
  419. package/src/live-voice/protocol.ts +171 -4
  420. package/src/live-voice/session-controls.ts +11 -3
  421. package/src/live-voice/task-outcomes.ts +131 -0
  422. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  423. package/src/mcp/__tests__/manager-tool-caps.test.ts +156 -0
  424. package/src/mcp/__tests__/startup.test.ts +30 -7
  425. package/src/mcp/__tests__/tool-caps.test.ts +134 -0
  426. package/src/mcp/manager.ts +171 -101
  427. package/src/mcp/startup.ts +33 -13
  428. package/src/mcp/tool-caps.ts +184 -0
  429. package/src/media/__tests__/image-models.test.ts +58 -0
  430. package/src/media/image-credentials.ts +5 -0
  431. package/src/media/image-models.ts +52 -0
  432. package/src/media/image-service.ts +8 -0
  433. package/src/media/openrouter-image-service.test.ts +133 -0
  434. package/src/media/openrouter-image-service.ts +153 -0
  435. package/src/media/types.ts +1 -1
  436. package/src/messaging/provider-message-metadata.ts +3 -3
  437. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  438. package/src/messaging/providers/discord/send.ts +4 -17
  439. package/src/messaging/providers/discord/transport.ts +2 -2
  440. package/src/messaging/providers/outlook/client.ts +16 -22
  441. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  442. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  443. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  444. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  445. package/src/monitoring/__tests__/control.test.ts +17 -5
  446. package/src/monitoring/control.ts +15 -5
  447. package/src/notifications/AGENTS.md +1 -1
  448. package/src/notifications/README.md +83 -99
  449. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  450. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  451. package/src/notifications/__tests__/copy-composer.test.ts +71 -1
  452. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  453. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  454. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  455. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  456. package/src/notifications/adapters/macos.ts +37 -46
  457. package/src/notifications/broadcaster.ts +5 -6
  458. package/src/notifications/channel-allowlist.ts +44 -0
  459. package/src/notifications/copy-composer.ts +11 -26
  460. package/src/notifications/decision-engine.ts +24 -14
  461. package/src/notifications/deterministic-checks.ts +1 -1
  462. package/src/notifications/emit-signal.ts +123 -37
  463. package/src/notifications/home-feed-side-effect.ts +9 -7
  464. package/src/notifications/notification-utils.ts +19 -0
  465. package/src/notifications/schedule-result-producer.ts +4 -6
  466. package/src/notifications/signal.ts +4 -19
  467. package/src/notifications/types.ts +15 -0
  468. package/src/oauth/AGENTS.md +2 -0
  469. package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
  470. package/src/oauth/connection-resolver.ts +1 -2
  471. package/src/oauth/identity-verifier.ts +24 -0
  472. package/src/oauth/oauth-store.ts +12 -4
  473. package/src/oauth/provider-serializer.ts +8 -0
  474. package/src/oauth/seed-providers.ts +42 -0
  475. package/src/onboarding/onboarding-events-store.ts +99 -12
  476. package/src/permissions/confirmation-guardian-request.test.ts +16 -0
  477. package/src/permissions/confirmation-guardian-request.ts +2 -3
  478. package/src/permissions/question-prompter.test.ts +31 -0
  479. package/src/permissions/question-prompter.ts +2 -0
  480. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  481. package/src/persistence/attachments-store.ts +84 -44
  482. package/src/persistence/client-connection-events-store.ts +377 -0
  483. package/src/persistence/conversation-crud.ts +166 -26
  484. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  485. package/src/persistence/conversation-mode-sessions.ts +460 -0
  486. package/src/persistence/conversation-plugin-facade.ts +30 -0
  487. package/src/persistence/conversation-queries.ts +6 -19
  488. package/src/persistence/conversation-tool-surface.ts +135 -0
  489. package/src/persistence/conversation-types.test.ts +32 -0
  490. package/src/persistence/conversation-types.ts +26 -4
  491. package/src/persistence/llm-usage-store.ts +3 -9
  492. package/src/persistence/message-metadata.ts +25 -0
  493. package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
  494. package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
  495. package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
  496. package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
  497. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
  498. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
  499. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  500. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  501. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  502. package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
  503. package/src/persistence/schema/conversations.ts +67 -0
  504. package/src/persistence/schema/index.ts +1 -0
  505. package/src/persistence/schema/infrastructure.ts +29 -0
  506. package/src/persistence/schema/oauth.ts +1 -0
  507. package/src/persistence/steps.ts +41 -3
  508. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  509. package/src/plugin-api/conversation-turn.ts +31 -7
  510. package/src/plugin-api/effective-context-window.ts +33 -0
  511. package/src/plugin-api/index.ts +35 -6
  512. package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
  513. package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
  514. package/src/plugin-api/plugin-name-env.ts +125 -0
  515. package/src/plugin-api/resolve-credential.ts +79 -20
  516. package/src/plugin-api/vision-support.test.ts +22 -0
  517. package/src/plugin-api/vision-support.ts +17 -5
  518. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  519. package/src/plugins/active-plugin-env.ts +51 -0
  520. package/src/plugins/defaults/index.ts +24 -0
  521. package/src/plugins/defaults/memory/AGENTS.md +39 -5
  522. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  523. package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
  524. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
  525. package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
  526. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
  527. package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
  528. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
  529. package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
  530. package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
  531. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  532. package/src/plugins/defaults/memory/buffer-file.ts +354 -0
  533. package/src/plugins/defaults/memory/buffer-format.ts +40 -0
  534. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  535. package/src/plugins/defaults/memory/context-search/agent-runner.ts +77 -22
  536. package/src/plugins/defaults/memory/context-search/format.ts +53 -8
  537. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  538. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  539. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  540. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  541. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
  542. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
  543. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  544. package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
  545. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  546. package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
  547. package/src/plugins/defaults/memory/graph/tool-handlers.ts +13 -63
  548. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  549. package/src/plugins/defaults/memory/host-utils.ts +0 -10
  550. package/src/plugins/defaults/memory/injectors.ts +4 -3
  551. package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
  552. package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
  553. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  554. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +6 -1
  555. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  556. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
  557. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
  558. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
  559. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
  560. package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
  561. package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
  562. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +91 -51
  563. package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
  564. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  565. package/src/plugins/defaults/memory/tools.ts +38 -11
  566. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
  567. package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
  568. package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
  569. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
  570. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
  571. package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
  572. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
  573. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
  574. package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
  575. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +332 -15
  576. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  577. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +148 -28
  578. package/src/plugins/defaults/memory/v3/card.ts +2 -1
  579. package/src/plugins/defaults/memory/v3/injector.ts +212 -178
  580. package/src/plugins/defaults/memory/v3/orchestrate.ts +159 -32
  581. package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
  582. package/src/plugins/defaults/memory/v3/pool-log-store.ts +275 -37
  583. package/src/plugins/defaults/memory/v3/pool-select.test.ts +47 -5
  584. package/src/plugins/defaults/memory/v3/pool-select.ts +710 -59
  585. package/src/plugins/defaults/memory/v3/sections.ts +2 -1
  586. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
  587. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  588. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  589. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  590. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  591. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  592. package/src/plugins/defaults/task-progress/package.json +14 -0
  593. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  594. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  595. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
  596. package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
  597. package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
  598. package/src/prompts/delegation-gate.ts +57 -0
  599. package/src/prompts/system-prompt.ts +14 -32
  600. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  601. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  602. package/src/providers/anthropic/client.ts +10 -1
  603. package/src/providers/anthropic-gateway-shared.ts +6 -0
  604. package/src/providers/fetch-provider-catalog.ts +11 -0
  605. package/src/providers/gemini/client.ts +25 -8
  606. package/src/providers/inference/adapter-factory.ts +6 -0
  607. package/src/providers/jev/client.test.ts +270 -0
  608. package/src/providers/jev/client.ts +534 -0
  609. package/src/providers/model-catalog.ts +59 -3
  610. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  611. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  612. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  613. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  614. package/src/providers/openai/chat-completions-provider.ts +100 -29
  615. package/src/providers/openai/google-thought-signature.ts +0 -6
  616. package/src/providers/openai/responses-provider.ts +23 -1
  617. package/src/providers/ratelimit.ts +1 -3
  618. package/src/providers/search-provider-catalog.ts +24 -0
  619. package/src/providers/server-tool-pairing.ts +16 -7
  620. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  621. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  622. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  623. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  624. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  625. package/src/providers/types.ts +5 -0
  626. package/src/routes/control.ts +7 -5
  627. package/src/runtime/AGENTS.md +6 -4
  628. package/src/runtime/__tests__/agent-wake.test.ts +94 -1
  629. package/src/runtime/actor-trust-resolver.ts +3 -3
  630. package/src/runtime/agent-wake.ts +32 -5
  631. package/src/runtime/approval-message-composer.ts +3 -69
  632. package/src/runtime/assistant-event-hub.ts +73 -75
  633. package/src/runtime/assistant-event-publish-options.ts +7 -0
  634. package/src/runtime/assistant-event-targeting.ts +79 -0
  635. package/src/runtime/assistant-stream-state.ts +13 -100
  636. package/src/runtime/background-job-runner.ts +5 -5
  637. package/src/runtime/channel-retry-sweep.ts +1 -0
  638. package/src/runtime/guardian-action-message-composer.ts +8 -161
  639. package/src/runtime/guardian-action-service.ts +2 -17
  640. package/src/runtime/guardian-reply-router.ts +29 -40
  641. package/src/runtime/http-server.ts +2 -2
  642. package/src/runtime/http-types.ts +6 -0
  643. package/src/runtime/message-composer-types.ts +3 -61
  644. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  645. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
  646. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  647. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  648. package/src/runtime/migrations/vbundle-import-policy.ts +43 -11
  649. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  650. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  651. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  652. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  653. package/src/runtime/pending-interactions.ts +29 -0
  654. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  655. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  656. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
  657. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  658. package/src/runtime/routes/browser-context.ts +31 -11
  659. package/src/runtime/routes/browser-routes.ts +43 -4
  660. package/src/runtime/routes/btw-routes.ts +9 -4
  661. package/src/runtime/routes/channel-route-shared.ts +1 -9
  662. package/src/runtime/routes/client-routes.ts +108 -0
  663. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  664. package/src/runtime/routes/conversation-routes.ts +125 -14
  665. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  666. package/src/runtime/routes/credential-routes.ts +1 -2
  667. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  668. package/src/runtime/routes/desktop-setup-routes.ts +8 -7
  669. package/src/runtime/routes/documents-routes.ts +51 -0
  670. package/src/runtime/routes/email-routes.ts +3 -7
  671. package/src/runtime/routes/events-routes.ts +75 -54
  672. package/src/runtime/routes/guardian-approval-interception.ts +24 -0
  673. package/src/runtime/routes/identity-routes.ts +1 -5
  674. package/src/runtime/routes/image-generation-routes.ts +8 -14
  675. package/src/runtime/routes/inbound-message-handler.ts +37 -41
  676. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  677. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  678. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +39 -10
  679. package/src/runtime/routes/inbound-stages/background-dispatch.ts +9 -9
  680. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  681. package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
  682. package/src/runtime/routes/inference-profiles-routes.ts +9 -0
  683. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  684. package/src/runtime/routes/migration-routes.ts +73 -2
  685. package/src/runtime/routes/notification-routes.ts +10 -0
  686. package/src/runtime/routes/oauth-commands-routes.ts +57 -54
  687. package/src/runtime/routes/oauth-providers.ts +11 -0
  688. package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
  689. package/src/runtime/routes/oauth-request-hints.ts +149 -0
  690. package/src/runtime/routes/plugins-routes.ts +6 -0
  691. package/src/runtime/routes/roadmap-routes.ts +47 -8
  692. package/src/runtime/routes/schedule-routes.ts +2 -0
  693. package/src/runtime/routes/secret-routes.ts +99 -119
  694. package/src/runtime/routes/settings-routes.ts +10 -4
  695. package/src/runtime/routes/watcher-routes.ts +1 -3
  696. package/src/runtime/trust-class.test.ts +7 -6
  697. package/src/runtime/trust-class.ts +9 -17
  698. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  699. package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
  700. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
  701. package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
  702. package/src/schedule/run-script.ts +2 -2
  703. package/src/schedule/scheduler.ts +47 -11
  704. package/src/schedule/tool-surface-readiness.ts +65 -0
  705. package/src/schedule/worker-control.ts +15 -5
  706. package/src/schedule/worker-mcp.ts +85 -0
  707. package/src/schedule/worker.ts +9 -31
  708. package/src/security/secure-keys.ts +74 -112
  709. package/src/skills/clawhub.ts +3 -8
  710. package/src/skills/managed-store.ts +1 -5
  711. package/src/stt/types.ts +4 -0
  712. package/src/subagent/notify.ts +54 -34
  713. package/src/subagent/parent-notification.ts +6 -0
  714. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  715. package/src/telemetry/phone-call-funnel.ts +144 -0
  716. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  717. package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
  718. package/src/tools/ask-question/ask-question-tool.ts +120 -22
  719. package/src/tools/browser/browser-execution.ts +29 -60
  720. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  721. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  722. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  723. package/src/tools/client-os.ts +15 -0
  724. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  725. package/src/tools/computer-use/target.ts +42 -0
  726. package/src/tools/document/document-tool.ts +13 -18
  727. package/src/tools/execution-target.ts +14 -16
  728. package/src/tools/host-terminal/host-shell.ts +16 -7
  729. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  730. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  731. package/src/tools/network/web-fetch.ts +274 -7
  732. package/src/tools/network/web-search.ts +468 -6
  733. package/src/tools/permission-checker.ts +3 -3
  734. package/src/tools/policy-context.ts +3 -1
  735. package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
  736. package/src/tools/shared/shell-output.test.ts +10 -0
  737. package/src/tools/shared/shell-output.ts +14 -2
  738. package/src/tools/skills/load.ts +2 -1
  739. package/src/tools/skills/sandbox-runner.ts +24 -4
  740. package/src/tools/skills/scaffold-managed.ts +2 -1
  741. package/src/tools/skills/skill-script-runner.ts +7 -0
  742. package/src/tools/skills/skill-tool-factory.ts +15 -1
  743. package/src/tools/terminal/__tests__/safe-env.test.ts +44 -4
  744. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
  745. package/src/tools/terminal/safe-env.ts +39 -24
  746. package/src/tools/terminal/sanitized-bash.ts +15 -2
  747. package/src/tools/terminal/shell-launch.test.ts +162 -0
  748. package/src/tools/terminal/shell.test.ts +29 -0
  749. package/src/tools/terminal/shell.ts +15 -7
  750. package/src/tools/tool-approval-handler.ts +5 -1
  751. package/src/tools/tool-types.ts +3 -3
  752. package/src/tools/types.ts +8 -2
  753. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  754. package/src/util/abort-reasons.ts +18 -26
  755. package/src/util/browser-human-verification.ts +14 -0
  756. package/src/util/canonicalize-identity.test.ts +21 -0
  757. package/src/util/canonicalize-identity.ts +16 -0
  758. package/src/util/errors.ts +13 -3
  759. package/src/util/host-process.test.ts +17 -1
  760. package/src/util/host-process.ts +24 -0
  761. package/src/util/provider-error-patterns.ts +3 -0
  762. package/src/util/unicode.ts +29 -0
  763. package/src/util/worker-process.ts +77 -35
  764. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  765. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  766. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  767. package/src/workspace/migrations/registry.ts +2 -0
  768. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  769. package/src/api/events/heartbeat-alert.ts +0 -20
  770. package/src/daemon/message-types/web-activity.ts +0 -65
  771. package/src/desktop/desktop-dependencies.test.ts +0 -131
  772. package/src/tools/computer-use/definitions.ts +0 -607
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Memory v3 — single pool selector.
3
3
  *
4
- * Runs a SINGLE forced-tool call over one unified candidate pool rendered in
4
+ * Runs a SINGLE selector call over one unified candidate pool rendered in
5
5
  * two segments that share one numbering:
6
6
  *
7
7
  * 1. STABLE PREFIX — the core+hot lane pages as FULL CARDS (head section +
@@ -42,22 +42,34 @@
42
42
  * - infrastructure failure (selector provider unavailable — e.g. a transient
43
43
  * CES credential blip drops the API key — or no usable `tool_use` / schema
44
44
  * mismatch surviving the short re-prompt retry) → throw
45
- * {@link MemoryV3RetrievalUnavailableError}. The live injector treats this
46
- * as a logged memory miss for the turn; shadow/observation callers swallow
47
- * it so v2 retrieval can serve the turn.
45
+ * {@link MemoryV3RetrievalUnavailableError}. The orchestrator keeps the
46
+ * stable prefix unjudged for the turn and drops the finder candidates,
47
+ * recording the pool with `selector_ran = 0`; the live injector renders
48
+ * that prefix and queues a notice that the turn drew on core memories
49
+ * only.
48
50
  */
49
51
 
50
52
  import type {
51
53
  ContentBlock,
52
54
  Message,
55
+ Provider,
56
+ ProviderResponse,
53
57
  ToolUseContent,
54
58
  } from "@vellumai/plugin-api";
55
- import { getConfiguredProvider } from "@vellumai/plugin-api";
59
+ import {
60
+ getConfiguredProvider,
61
+ getEffectiveContextWindow,
62
+ safeStringSlice,
63
+ } from "@vellumai/plugin-api";
56
64
  import { z } from "zod";
57
65
 
66
+ import {
67
+ estimatePromptTokensWithTools,
68
+ estimateTextTokens,
69
+ } from "../../../../context/token-estimator.js";
58
70
  import { classifyConversationError } from "../../../../daemon/conversation-error.js";
59
71
  import type { PendingConversationNotice } from "../../../../daemon/conversation-notices.js";
60
- import { redactLogString, safeStringSlice, truncate } from "../host-utils.js";
72
+ import { redactLogString, truncate } from "../host-utils.js";
61
73
  import {
62
74
  cachedTextBlock,
63
75
  extractToolUse,
@@ -69,6 +81,7 @@ import { retryForResult } from "./llm-retry.js";
69
81
  import { findTerm } from "./section-needle.js";
70
82
  import { sectionBody } from "./sections.js";
71
83
  import {
84
+ type FinderLane,
72
85
  type MemoryRoutingTurn,
73
86
  type Section,
74
87
  sectionKey,
@@ -90,12 +103,16 @@ const log = getLogger("memory-v3-pool-select");
90
103
  */
91
104
  export class MemoryV3RetrievalUnavailableError extends Error {
92
105
  readonly conversationNotice?: PendingConversationNotice;
106
+ readonly pool?: SelectorPool;
107
+ readonly turn?: MemoryRoutingTurn;
93
108
 
94
109
  constructor(
95
110
  message: string,
96
111
  options?: {
97
112
  cause?: unknown;
98
113
  conversationNotice?: PendingConversationNotice;
114
+ pool?: SelectorPool;
115
+ turn?: MemoryRoutingTurn;
99
116
  },
100
117
  ) {
101
118
  super(
@@ -104,6 +121,8 @@ export class MemoryV3RetrievalUnavailableError extends Error {
104
121
  );
105
122
  this.name = "MemoryV3RetrievalUnavailableError";
106
123
  this.conversationNotice = options?.conversationNotice;
124
+ this.pool = options?.pool;
125
+ this.turn = options?.turn;
107
126
  }
108
127
  }
109
128
 
@@ -127,7 +146,7 @@ function providerBillingNoticeFromError(
127
146
  /** A dynamic-tail (finder) candidate: the slug plus the descriptor that
128
147
  * justifies it (a matched section's text for a needle/dense hit, or a
129
148
  * curated link description for an edge page), rendered as a one-line
130
- * snippet and prefixed with the surfacing lane when one is supplied. A
149
+ * snippet and prefixed with the surfacing lane. A
131
150
  * candidate that also carries its matched `section` and the query `terms`
132
151
  * that scored it (best first) renders a keyword-in-context snippet instead:
133
152
  * a window of the section body around the first of those terms that occurs
@@ -138,7 +157,7 @@ function providerBillingNoticeFromError(
138
157
  export interface PoolCandidate {
139
158
  slug: Slug;
140
159
  descriptor: string;
141
- lane?: string;
160
+ lane: FinderLane;
142
161
  section?: Section;
143
162
  terms?: string[];
144
163
  }
@@ -147,9 +166,12 @@ export interface PoolCandidate {
147
166
  * (`renderCard` output — head section + TOC). Cards must be byte-stable
148
167
  * across turns for the prefix to ride the provider KV cache, which is why
149
168
  * callers pre-render them at lane init rather than per turn. */
169
+ export type StableCandidateLane = "core" | "hot" | "fresh" | "always";
170
+
150
171
  export interface StableCandidate {
151
172
  slug: Slug;
152
173
  card: string;
174
+ lane: StableCandidateLane;
153
175
  }
154
176
 
155
177
  /**
@@ -168,6 +190,7 @@ export interface SelectorPool {
168
190
  /** Tool name forced via `tool_choice`. Shared constant so tests can match it. */
169
191
  const SELECT_PAGES_TOOL_NAME = "select_pages";
170
192
  const MEMORY_V3_SELECT_CALL_SITE = "memoryV3SelectL2" as const;
193
+ const SELECTOR_CONTEXT_BUDGET_RATIO = 0.8;
171
194
 
172
195
  /** Finder-line snippets are truncated to keep the dynamic tail compact. */
173
196
  const SNIPPET_MAX_CHARS = 300;
@@ -177,7 +200,8 @@ type PoolSelectorAttemptFailureReason =
177
200
  | "provider_error"
178
201
  | "missing_tool_use"
179
202
  | "unexpected_tool_name"
180
- | "schema_mismatch";
203
+ | "schema_mismatch"
204
+ | "unusable_answers";
181
205
 
182
206
  interface PoolSelectorAttemptFailure {
183
207
  attempt: number;
@@ -394,34 +418,39 @@ function renderCardSegment(stable: StableCandidate[]): string {
394
418
  }
395
419
 
396
420
  /** A finder line's lane tag: `(lane) `, or `(rare: word) ` for a rare-term
397
- * line, keyed on the one word it carries as its term; empty for a candidate
398
- * without a lane. */
421
+ * line, keyed on the one word it carries as its term. */
399
422
  function laneTag(candidate: PoolCandidate): string {
400
- if (candidate.lane === undefined) {
401
- return "";
402
- }
403
423
  const word = candidate.lane === "rare" ? candidate.terms?.[0] : undefined;
404
424
  return word === undefined
405
425
  ? `(${candidate.lane}) `
406
426
  : `(${candidate.lane}: ${word}) `;
407
427
  }
408
428
 
429
+ /**
430
+ * One finder line as the selector sees it, minus its pool number: the lane
431
+ * tag (naming the keyed word for a rare-term line, `(rare: turnip)`), the
432
+ * slug, and the snippet after a dash
433
+ * (no dash for a candidate with an empty descriptor). Shared with the pool
434
+ * input capture (`pool-log-store.ts`), so the persisted text is exactly the
435
+ * rendered line.
436
+ */
437
+ export function renderFinderLine(candidate: PoolCandidate): string {
438
+ const snippet = renderSnippet(candidate);
439
+ const lane = laneTag(candidate);
440
+ return snippet.length > 0
441
+ ? `${lane}${candidate.slug} — ${snippet}`
442
+ : `${lane}${candidate.slug}`;
443
+ }
444
+
409
445
  /**
410
446
  * Render the finder tail: one `[m+i] (lane) slug — snippet` line per
411
- * candidate, numbered continuing after the `offset` stable-prefix cards. The
412
- * lane tag is omitted for a candidate without one and names the keyed word
413
- * for a rare-term line (`(rare: turnip)`); a candidate with an empty
414
- * descriptor renders without the dash.
447
+ * candidate ({@link renderFinderLine}), numbered continuing after the
448
+ * `offset` stable-prefix cards.
415
449
  */
416
450
  function renderFinderSegment(finder: PoolCandidate[], offset: number): string {
417
- const lines = finder.map((c, i) => {
418
- const snippet = renderSnippet(c);
419
- const id = offset + i + 1;
420
- const lane = laneTag(c);
421
- return snippet.length > 0
422
- ? `[${id}] ${lane}${c.slug} — ${snippet}`
423
- : `[${id}] ${lane}${c.slug}`;
424
- });
451
+ const lines = finder.map(
452
+ (c, i) => `[${offset + i + 1}] ${renderFinderLine(c)}`,
453
+ );
425
454
  return `<candidates>\n${lines.join("\n")}\n</candidates>`;
426
455
  }
427
456
 
@@ -482,9 +511,563 @@ export function selectAllPoolCandidates(pool: SelectorPool): SelectedPage[] {
482
511
  );
483
512
  }
484
513
 
514
+ /**
515
+ * Inclusive noul threshold for TypeSafe pool selection. 0.5 is calibrated
516
+ * equal yes/no. This sits slightly below that so a plausible candidate is
517
+ * kept, matching the selector's recall-heavy rule.
518
+ */
519
+ export const TYPE_SAFE_POOL_KEEP_NOUL = 0.4;
520
+
521
+ /** Catalog id of the TypeSafe System One provider. */
522
+ const TYPE_SAFE_PROVIDER_ID = "typesafe";
523
+
524
+ type TypesafeNoulQuestion = {
525
+ type: "noul";
526
+ instructions: string;
527
+ criteria: { yes: string; no: string };
528
+ };
529
+
530
+ function isRecord(value: unknown): value is Record<string, unknown> {
531
+ return typeof value === "object" && value !== null && !Array.isArray(value);
532
+ }
533
+
534
+ function noulFromAnswer(answer: unknown): number | undefined {
535
+ if (typeof answer === "number" && Number.isFinite(answer)) {
536
+ return answer;
537
+ }
538
+ if (
539
+ isRecord(answer) &&
540
+ typeof answer.noul === "number" &&
541
+ Number.isFinite(answer.noul)
542
+ ) {
543
+ return answer.noul;
544
+ }
545
+ return undefined;
546
+ }
547
+
548
+ function typesafeCandidateEntries(
549
+ pool: SelectorPool,
550
+ ): Record<string, { slug: Slug; text: string }> {
551
+ const entries: Record<string, { slug: Slug; text: string }> = {};
552
+ pool.stable.forEach((candidate, index) => {
553
+ entries[String(index + 1)] = {
554
+ slug: candidate.slug,
555
+ text: candidate.card,
556
+ };
557
+ });
558
+ pool.finder.forEach((candidate, index) => {
559
+ entries[String(pool.stable.length + index + 1)] = {
560
+ slug: candidate.slug,
561
+ text: renderFinderLine(candidate),
562
+ };
563
+ });
564
+ return entries;
565
+ }
566
+
567
+ function typesafeKeepQuestion(id: string): TypesafeNoulQuestion {
568
+ return {
569
+ type: "noul",
570
+ instructions:
571
+ `Would the upcoming assistant reply draw on \`candidates.${id}\`? ` +
572
+ "Lean inclusive. Facts, current task and event state, register, " +
573
+ "framing, calibration, and relationship texture all count. True when " +
574
+ "the candidate could plausibly inform the reply.",
575
+ criteria: {
576
+ yes: "The reply would draw on this candidate.",
577
+ no: "The reply would not draw on this candidate.",
578
+ },
579
+ };
580
+ }
581
+
582
+ function buildTypesafeSelectorPayload(
583
+ pool: SelectorPool,
584
+ turn: MemoryRoutingTurn,
585
+ systemPrompt: string,
586
+ ): {
587
+ state: Record<string, unknown>;
588
+ questions: Record<string, TypesafeNoulQuestion>;
589
+ } {
590
+ const candidates = typesafeCandidateEntries(pool);
591
+ const questions: Record<string, TypesafeNoulQuestion> = {};
592
+ for (const id of Object.keys(candidates)) {
593
+ questions[id] = typesafeKeepQuestion(id);
594
+ }
595
+ return {
596
+ state: {
597
+ selector_instructions: systemPrompt,
598
+ candidates,
599
+ ...(turn.situationalContext
600
+ ? { situation: turn.situationalContext }
601
+ : {}),
602
+ recent_context: turn.recentContext,
603
+ current_message: turn.currentMessage,
604
+ },
605
+ questions,
606
+ };
607
+ }
608
+
609
+ function buildGenericSelectorMessage(
610
+ pool: SelectorPool,
611
+ turn: MemoryRoutingTurn,
612
+ ): Message {
613
+ const content: ContentBlock[] = [];
614
+ if (pool.stable.length > 0) {
615
+ content.push(cachedTextBlock(renderCardSegment(pool.stable)));
616
+ }
617
+ const tailParts: string[] = [];
618
+ if (pool.finder.length > 0) {
619
+ tailParts.push(renderFinderSegment(pool.finder, pool.stable.length));
620
+ }
621
+ if (turn.situationalContext) {
622
+ tailParts.push(`<situation>${turn.situationalContext}</situation>`);
623
+ }
624
+ tailParts.push(`<recent_context>${turn.recentContext}</recent_context>`);
625
+ tailParts.push(`<current_message>${turn.currentMessage}</current_message>`);
626
+ content.push({ type: "text", text: tailParts.join("\n") });
627
+ return { role: "user", content };
628
+ }
629
+
630
+ const STABLE_CANDIDATE_PRIORITY: Record<StableCandidateLane, number> = {
631
+ core: 0,
632
+ hot: 5,
633
+ fresh: 6,
634
+ always: 7,
635
+ };
636
+
637
+ const FINDER_CANDIDATE_PRIORITY: Record<FinderLane, number> = {
638
+ needle: 1,
639
+ dense: 2,
640
+ reply: 3,
641
+ span: 4,
642
+ entity: 8,
643
+ rare: 9,
644
+ edge: 10,
645
+ learned: 11,
646
+ };
647
+
648
+ function highestPriorityCandidates(
649
+ pool: SelectorPool,
650
+ retainedCount: number,
651
+ ): SelectorPool {
652
+ const ranked = [
653
+ ...pool.stable.map((candidate, index) => ({
654
+ segment: "stable" as const,
655
+ index,
656
+ poolIndex: index,
657
+ priority: STABLE_CANDIDATE_PRIORITY[candidate.lane],
658
+ })),
659
+ ...pool.finder.map((candidate, index) => ({
660
+ segment: "finder" as const,
661
+ index,
662
+ poolIndex: pool.stable.length + index,
663
+ priority: FINDER_CANDIDATE_PRIORITY[candidate.lane],
664
+ })),
665
+ ]
666
+ .sort(
667
+ (left, right) =>
668
+ left.priority - right.priority || left.poolIndex - right.poolIndex,
669
+ )
670
+ .slice(0, retainedCount);
671
+ const retainedStable = new Set(
672
+ ranked
673
+ .filter((candidate) => candidate.segment === "stable")
674
+ .map((candidate) => candidate.index),
675
+ );
676
+ const retainedFinder = new Set(
677
+ ranked
678
+ .filter((candidate) => candidate.segment === "finder")
679
+ .map((candidate) => candidate.index),
680
+ );
681
+ return {
682
+ stable: pool.stable.filter((_, index) => retainedStable.has(index)),
683
+ finder: pool.finder.filter((_, index) => retainedFinder.has(index)),
684
+ };
685
+ }
686
+
687
+ function withoutTrailingCurrentMessage(
688
+ turn: MemoryRoutingTurn,
689
+ ): MemoryRoutingTurn {
690
+ if (turn.recentContext === turn.currentMessage) {
691
+ return { ...turn, recentContext: "" };
692
+ }
693
+ const duplicateSuffix = `\n${turn.currentMessage}`;
694
+ if (
695
+ turn.currentMessage.length === 0 ||
696
+ !turn.recentContext.endsWith(duplicateSuffix)
697
+ ) {
698
+ return turn;
699
+ }
700
+ return {
701
+ ...turn,
702
+ recentContext: turn.recentContext.slice(0, -duplicateSuffix.length),
703
+ };
704
+ }
705
+
706
+ function latestTurnContextSuffix(
707
+ turn: MemoryRoutingTurn,
708
+ retainedChars: number,
709
+ ): MemoryRoutingTurn {
710
+ let remaining = retainedChars;
711
+ const takeSuffix = (value: string): string => {
712
+ const count = Math.min(remaining, value.length);
713
+ remaining -= count;
714
+ return safeStringSlice(value, value.length - count);
715
+ };
716
+ const currentMessage = takeSuffix(turn.currentMessage);
717
+ const recentContext = takeSuffix(turn.recentContext);
718
+ const situationalContext = turn.situationalContext
719
+ ? takeSuffix(turn.situationalContext) || undefined
720
+ : undefined;
721
+ return {
722
+ ...turn,
723
+ currentMessage,
724
+ recentContext,
725
+ situationalContext,
726
+ };
727
+ }
728
+
729
+ function turnContextChars(turn: MemoryRoutingTurn): number {
730
+ return (
731
+ turn.currentMessage.length +
732
+ turn.recentContext.length +
733
+ (turn.situationalContext?.length ?? 0)
734
+ );
735
+ }
736
+
737
+ function estimateSelectorInputTokens({
738
+ pool,
739
+ turn,
740
+ systemPrompt,
741
+ provider,
742
+ model,
743
+ }: {
744
+ pool: SelectorPool;
745
+ turn: MemoryRoutingTurn;
746
+ systemPrompt: string;
747
+ provider: Provider;
748
+ model: string;
749
+ }): number {
750
+ if (provider.name === TYPE_SAFE_PROVIDER_ID) {
751
+ const payload = buildTypesafeSelectorPayload(pool, turn, systemPrompt);
752
+ return estimateTextTokens(
753
+ JSON.stringify({
754
+ state: payload.state,
755
+ model,
756
+ questions: payload.questions,
757
+ }),
758
+ );
759
+ }
760
+ return estimatePromptTokensWithTools(
761
+ [buildGenericSelectorMessage(pool, turn)],
762
+ systemPrompt,
763
+ [SELECT_PAGES_TOOL],
764
+ provider.tokenEstimationProvider ?? provider.name,
765
+ );
766
+ }
767
+
768
+ interface SelectorPoolBudget {
769
+ pool: SelectorPool;
770
+ turn: MemoryRoutingTurn;
771
+ budgetTokens: number;
772
+ estimatedInputTokens: number;
773
+ originalEstimatedInputTokens: number;
774
+ }
775
+
776
+ function budgetSelectorPool({
777
+ pool,
778
+ turn,
779
+ systemPrompt,
780
+ provider,
781
+ model,
782
+ maxInputTokens,
783
+ }: {
784
+ pool: SelectorPool;
785
+ turn: MemoryRoutingTurn;
786
+ systemPrompt: string;
787
+ provider: Provider;
788
+ model: string;
789
+ maxInputTokens: number;
790
+ }): SelectorPoolBudget {
791
+ const budgetTokens = Math.max(
792
+ 1,
793
+ Math.floor(maxInputTokens * SELECTOR_CONTEXT_BUDGET_RATIO),
794
+ );
795
+ const estimate = (
796
+ candidatePool: SelectorPool,
797
+ candidateTurn: MemoryRoutingTurn,
798
+ ): number =>
799
+ estimateSelectorInputTokens({
800
+ pool: candidatePool,
801
+ turn: candidateTurn,
802
+ systemPrompt,
803
+ provider,
804
+ model,
805
+ });
806
+ const originalEstimatedInputTokens = estimate(pool, turn);
807
+ if (originalEstimatedInputTokens <= budgetTokens) {
808
+ return {
809
+ pool,
810
+ turn,
811
+ budgetTokens,
812
+ estimatedInputTokens: originalEstimatedInputTokens,
813
+ originalEstimatedInputTokens,
814
+ };
815
+ }
816
+
817
+ const deduplicatedTurn = withoutTrailingCurrentMessage(turn);
818
+ const deduplicatedEstimatedInputTokens = estimate(pool, deduplicatedTurn);
819
+ if (deduplicatedEstimatedInputTokens <= budgetTokens) {
820
+ return {
821
+ pool,
822
+ turn: deduplicatedTurn,
823
+ budgetTokens,
824
+ estimatedInputTokens: deduplicatedEstimatedInputTokens,
825
+ originalEstimatedInputTokens,
826
+ };
827
+ }
828
+
829
+ const minimumTurn = latestTurnContextSuffix(
830
+ deduplicatedTurn,
831
+ deduplicatedTurn.currentMessage.length,
832
+ );
833
+ const fittingPool: SelectorPool = {
834
+ stable: pool.stable.filter(
835
+ (candidate) =>
836
+ estimate({ stable: [candidate], finder: [] }, minimumTurn) <=
837
+ budgetTokens,
838
+ ),
839
+ finder: pool.finder.filter(
840
+ (candidate) =>
841
+ estimate({ stable: [], finder: [candidate] }, minimumTurn) <=
842
+ budgetTokens,
843
+ ),
844
+ };
845
+ const candidateCount = fittingPool.stable.length + fittingPool.finder.length;
846
+ if (candidateCount === 0) {
847
+ const emptyPool: SelectorPool = { stable: [], finder: [] };
848
+ return {
849
+ pool: emptyPool,
850
+ turn: minimumTurn,
851
+ budgetTokens,
852
+ estimatedInputTokens: estimate(emptyPool, minimumTurn),
853
+ originalEstimatedInputTokens,
854
+ };
855
+ }
856
+
857
+ const preferredCandidatePool = highestPriorityCandidates(fittingPool, 1);
858
+ let budgetedTurn = deduplicatedTurn;
859
+ if (estimate(preferredCandidatePool, budgetedTurn) > budgetTokens) {
860
+ let low = deduplicatedTurn.currentMessage.length;
861
+ let high = turnContextChars(deduplicatedTurn);
862
+ while (low < high) {
863
+ const retainedChars = Math.ceil((low + high) / 2);
864
+ const candidateTurn = latestTurnContextSuffix(
865
+ deduplicatedTurn,
866
+ retainedChars,
867
+ );
868
+ if (estimate(preferredCandidatePool, candidateTurn) <= budgetTokens) {
869
+ low = retainedChars;
870
+ } else {
871
+ high = retainedChars - 1;
872
+ }
873
+ }
874
+ budgetedTurn = latestTurnContextSuffix(deduplicatedTurn, low);
875
+ }
876
+
877
+ let low = 1;
878
+ let high = candidateCount;
879
+ while (low < high) {
880
+ const retainedCount = Math.ceil((low + high) / 2);
881
+ const candidatePool = highestPriorityCandidates(fittingPool, retainedCount);
882
+ if (estimate(candidatePool, budgetedTurn) <= budgetTokens) {
883
+ low = retainedCount;
884
+ } else {
885
+ high = retainedCount - 1;
886
+ }
887
+ }
888
+
889
+ const budgetedPool = highestPriorityCandidates(fittingPool, low);
890
+ return {
891
+ pool: budgetedPool,
892
+ turn: budgetedTurn,
893
+ budgetTokens,
894
+ estimatedInputTokens: estimate(budgetedPool, budgetedTurn),
895
+ originalEstimatedInputTokens,
896
+ };
897
+ }
898
+
899
+ function answersFromSelectorResponse(
900
+ response: ProviderResponse,
901
+ ): Record<string, unknown> | null {
902
+ const raw = response.rawResponse;
903
+ if (isRecord(raw) && isRecord(raw.answers)) {
904
+ return raw.answers;
905
+ }
906
+ const textBlock = response.content.find((block) => block.type === "text");
907
+ if (!textBlock || textBlock.type !== "text") {
908
+ return null;
909
+ }
910
+ try {
911
+ const parsed: unknown = JSON.parse(textBlock.text);
912
+ return isRecord(parsed) ? parsed : null;
913
+ } catch {
914
+ return null;
915
+ }
916
+ }
917
+
918
+ async function selectPoolWithTypesafe(
919
+ pool: SelectorPool,
920
+ turn: MemoryRoutingTurn,
921
+ ordered: PoolLine[],
922
+ systemPrompt: string,
923
+ provider: Provider,
924
+ ): Promise<PoolSelection> {
925
+ const payload = buildTypesafeSelectorPayload(pool, turn, systemPrompt);
926
+ const userMsg: Message = {
927
+ role: "user",
928
+ content: [{ type: "text", text: JSON.stringify(payload) }],
929
+ };
930
+
931
+ const failures: PoolSelectorAttemptFailure[] = [];
932
+ let attempt = 0;
933
+ const recordFailure = (
934
+ failure: Omit<
935
+ PoolSelectorAttemptFailure,
936
+ | "callSite"
937
+ | "providerName"
938
+ | "candidateCount"
939
+ | "stableCount"
940
+ | "finderCount"
941
+ >,
942
+ ): void => {
943
+ const diagnostic: PoolSelectorAttemptFailure = {
944
+ ...failure,
945
+ callSite: MEMORY_V3_SELECT_CALL_SITE,
946
+ providerName: provider.name,
947
+ candidateCount: ordered.length,
948
+ stableCount: pool.stable.length,
949
+ finderCount: pool.finder.length,
950
+ };
951
+ failures.push(diagnostic);
952
+ log.warn(diagnostic, "pool selector attempt failed");
953
+ };
954
+
955
+ let lastError: unknown = null;
956
+ const parsed = await retryForResult(async () => {
957
+ attempt += 1;
958
+ let response: Awaited<ReturnType<typeof provider.sendMessage>>;
959
+ try {
960
+ response = await provider.sendMessage([userMsg], {
961
+ config: {
962
+ callSite: MEMORY_V3_SELECT_CALL_SITE,
963
+ conversationId: turn.conversationId,
964
+ selectionSeed: turn.conversationId,
965
+ disableTurnStartCache: true,
966
+ },
967
+ });
968
+ lastError = null;
969
+ } catch (error) {
970
+ lastError = error;
971
+ recordFailure({
972
+ attempt,
973
+ reason: "provider_error",
974
+ error: summarizeError(error),
975
+ });
976
+ throw error;
977
+ }
978
+ const answers = answersFromSelectorResponse(response);
979
+ if (!answers) {
980
+ recordFailure({
981
+ attempt,
982
+ reason: "unusable_answers",
983
+ response: summarizeResponse(response),
984
+ });
985
+ return null;
986
+ }
987
+ const picked: number[] = [];
988
+ let parsedCount = 0;
989
+ for (let index = 0; index < ordered.length; index++) {
990
+ const noul = noulFromAnswer(answers[String(index + 1)]);
991
+ if (noul === undefined) {
992
+ continue;
993
+ }
994
+ parsedCount += 1;
995
+ if (noul >= TYPE_SAFE_POOL_KEEP_NOUL) {
996
+ picked.push(index);
997
+ }
998
+ }
999
+ if (parsedCount === 0) {
1000
+ recordFailure({
1001
+ attempt,
1002
+ reason: "unusable_answers",
1003
+ response: summarizeResponse(response),
1004
+ });
1005
+ return null;
1006
+ }
1007
+ return {
1008
+ pages: mergeSelectedLines(ordered, picked),
1009
+ keptAll: false,
1010
+ pool,
1011
+ turn,
1012
+ };
1013
+ });
1014
+
1015
+ if (parsed === null) {
1016
+ if (lastError !== null) {
1017
+ const detail =
1018
+ lastError instanceof Error ? lastError.message : String(lastError);
1019
+ const redactedDetail = truncate(
1020
+ redactLogString(detail),
1021
+ ERROR_MESSAGE_MAX_CHARS,
1022
+ );
1023
+ log.warn(
1024
+ {
1025
+ candidateCount: ordered.length,
1026
+ stableCount: pool.stable.length,
1027
+ finderCount: pool.finder.length,
1028
+ callSite: MEMORY_V3_SELECT_CALL_SITE,
1029
+ providerName: provider.name,
1030
+ failures,
1031
+ },
1032
+ "pool selector provider call failed after retries",
1033
+ );
1034
+ throw new MemoryV3RetrievalUnavailableError(
1035
+ `memory-v3 pool selector provider call failed after retries: ${redactedDetail}`,
1036
+ {
1037
+ cause: lastError,
1038
+ conversationNotice: providerBillingNoticeFromError(lastError),
1039
+ pool,
1040
+ turn,
1041
+ },
1042
+ );
1043
+ }
1044
+ log.warn(
1045
+ {
1046
+ candidateCount: ordered.length,
1047
+ stableCount: pool.stable.length,
1048
+ finderCount: pool.finder.length,
1049
+ callSite: MEMORY_V3_SELECT_CALL_SITE,
1050
+ providerName: provider.name,
1051
+ failures,
1052
+ },
1053
+ "pool selector returned no usable TypeSafe answers after retries",
1054
+ );
1055
+ throw new MemoryV3RetrievalUnavailableError(
1056
+ "memory-v3 pool selector returned no usable selection after retries",
1057
+ { pool, turn },
1058
+ );
1059
+ }
1060
+
1061
+ return parsed;
1062
+ }
1063
+
485
1064
  /** A selection plus whether it came from the recall-safe keep-all fallback. */
486
1065
  export interface PoolSelection {
487
1066
  pages: SelectedPage[];
1067
+ /** The exact candidate pool rendered for the selector request. */
1068
+ pool: SelectorPool;
1069
+ /** The exact context strings rendered for the selector request. */
1070
+ turn: MemoryRoutingTurn;
488
1071
  /** True only when the model OMITTED `ids` and every candidate was kept as the
489
1072
  * recall-safe fallback — NOT when it explicitly selected a large set. The two
490
1073
  * produce the same pages but mean different things (the model gave up judging
@@ -493,15 +1076,18 @@ export interface PoolSelection {
493
1076
  }
494
1077
 
495
1078
  /**
496
- * Run the single forced-tool selector over the unified candidate pool. Returns
1079
+ * Run the single selector over the unified candidate pool. Returns
497
1080
  * the pages to inject, merged per slug (a page selected as a card and on
498
1081
  * finder lines yields one entry carrying every selected section), plus a
499
1082
  * `keptAll` flag marking the recall-safe fallback.
500
1083
  *
501
- * An omitted `ids` keeps ALL candidates (the recall-safe "all of these are
502
- * relevant" signal, `keptAll: true`); an explicit `[]` keeps none; an
503
- * infrastructure failure (after a short re-prompt retry) keeps none, degrading
504
- * to the deterministic recall lanes the orchestrator unions in.
1084
+ * On a chat model, an omitted `ids` keeps ALL candidates (the recall-safe
1085
+ * "all of these are relevant" signal, `keptAll: true`); an explicit `[]`
1086
+ * keeps none. TypeSafe answers one noul per candidate and never omits ids,
1087
+ * so `keptAll` is always false on that path. An infrastructure failure
1088
+ * (after a short re-prompt retry) throws
1089
+ * {@link MemoryV3RetrievalUnavailableError}, and the orchestrator keeps the
1090
+ * stable prefix unjudged in its place.
505
1091
  *
506
1092
  * `systemPrompt` is the selector's instruction scaffold; it defaults to the
507
1093
  * bundled {@link SYSTEM_PROMPT} and is overridable via `memory.v3.selectorPromptPath`
@@ -512,17 +1098,19 @@ export async function selectPool(
512
1098
  turn: MemoryRoutingTurn,
513
1099
  systemPrompt: string = SYSTEM_PROMPT,
514
1100
  ): Promise<PoolSelection> {
515
- const ordered = orderedLines(pool);
516
- if (ordered.length === 0) {
517
- return { pages: [], keptAll: false };
1101
+ const originalCandidateCount = pool.stable.length + pool.finder.length;
1102
+ if (originalCandidateCount === 0) {
1103
+ return { pages: [], keptAll: false, pool, turn };
518
1104
  }
519
1105
 
520
- const provider = await getConfiguredProvider(MEMORY_V3_SELECT_CALL_SITE);
1106
+ const provider = await getConfiguredProvider(MEMORY_V3_SELECT_CALL_SITE, {
1107
+ selectionSeed: turn.conversationId,
1108
+ });
521
1109
  if (!provider) {
522
1110
  log.warn(
523
1111
  {
524
1112
  callSite: MEMORY_V3_SELECT_CALL_SITE,
525
- candidateCount: ordered.length,
1113
+ candidateCount: originalCandidateCount,
526
1114
  stableCount: pool.stable.length,
527
1115
  finderCount: pool.finder.length,
528
1116
  },
@@ -530,28 +1118,77 @@ export async function selectPool(
530
1118
  );
531
1119
  throw new MemoryV3RetrievalUnavailableError(
532
1120
  "memory-v3 pool selector provider unavailable",
1121
+ { pool, turn },
533
1122
  );
534
1123
  }
535
1124
 
536
- // Two content blocks: the stable prefix (cards) carries the cache
537
- // breakpoint; the dynamic tail (finder lines + per-turn context) does not.
538
- // See the module doc for the cache contract.
539
- const content: ContentBlock[] = [];
540
- if (pool.stable.length > 0) {
541
- content.push(cachedTextBlock(renderCardSegment(pool.stable)));
1125
+ const effectiveContextWindow = getEffectiveContextWindow(
1126
+ MEMORY_V3_SELECT_CALL_SITE,
1127
+ { selectionSeed: turn.conversationId },
1128
+ );
1129
+ const budget = budgetSelectorPool({
1130
+ pool,
1131
+ turn,
1132
+ systemPrompt,
1133
+ provider,
1134
+ model: effectiveContextWindow.model,
1135
+ maxInputTokens: effectiveContextWindow.maxInputTokens,
1136
+ });
1137
+ const budgetedPool = budget.pool;
1138
+ const budgetedTurn = budget.turn;
1139
+ const ordered = orderedLines(budgetedPool);
1140
+ if (
1141
+ ordered.length < originalCandidateCount ||
1142
+ turnContextChars(budgetedTurn) < turnContextChars(turn)
1143
+ ) {
1144
+ log.info(
1145
+ {
1146
+ callSite: MEMORY_V3_SELECT_CALL_SITE,
1147
+ providerName: provider.name,
1148
+ model: effectiveContextWindow.model,
1149
+ maxInputTokens: effectiveContextWindow.maxInputTokens,
1150
+ budgetRatio: SELECTOR_CONTEXT_BUDGET_RATIO,
1151
+ budgetTokens: budget.budgetTokens,
1152
+ originalEstimatedInputTokens: budget.originalEstimatedInputTokens,
1153
+ estimatedInputTokens: budget.estimatedInputTokens,
1154
+ originalStableCount: pool.stable.length,
1155
+ retainedStableCount: budgetedPool.stable.length,
1156
+ originalFinderCount: pool.finder.length,
1157
+ retainedFinderCount: budgetedPool.finder.length,
1158
+ originalTurnContextChars: turnContextChars(turn),
1159
+ retainedTurnContextChars: turnContextChars(budgetedTurn),
1160
+ },
1161
+ "trimmed pool selector input to the context budget",
1162
+ );
542
1163
  }
543
- const tailParts: string[] = [];
544
- if (pool.finder.length > 0) {
545
- tailParts.push(renderFinderSegment(pool.finder, pool.stable.length));
1164
+ if (ordered.length === 0) {
1165
+ log.warn(
1166
+ {
1167
+ callSite: MEMORY_V3_SELECT_CALL_SITE,
1168
+ providerName: provider.name,
1169
+ model: effectiveContextWindow.model,
1170
+ maxInputTokens: effectiveContextWindow.maxInputTokens,
1171
+ candidateCount: originalCandidateCount,
1172
+ },
1173
+ "no pool selector candidate fits the context budget",
1174
+ );
1175
+ throw new MemoryV3RetrievalUnavailableError(
1176
+ "memory-v3 pool selector has no candidate that fits the context budget",
1177
+ { pool: budgetedPool, turn: budgetedTurn },
1178
+ );
546
1179
  }
547
- if (turn.situationalContext) {
548
- tailParts.push(`<situation>${turn.situationalContext}</situation>`);
1180
+
1181
+ if (provider.name === TYPE_SAFE_PROVIDER_ID) {
1182
+ return selectPoolWithTypesafe(
1183
+ budgetedPool,
1184
+ budgetedTurn,
1185
+ ordered,
1186
+ systemPrompt,
1187
+ provider,
1188
+ );
549
1189
  }
550
- tailParts.push(`<recent_context>${turn.recentContext}</recent_context>`);
551
- tailParts.push(`<current_message>${turn.currentMessage}</current_message>`);
552
- content.push({ type: "text", text: tailParts.join("\n") });
553
1190
 
554
- const userMsg: Message = { role: "user", content };
1191
+ const userMsg = buildGenericSelectorMessage(budgetedPool, budgetedTurn);
555
1192
  const failures: PoolSelectorAttemptFailure[] = [];
556
1193
  let attempt = 0;
557
1194
  const recordFailure = (
@@ -569,8 +1206,8 @@ export async function selectPool(
569
1206
  callSite: MEMORY_V3_SELECT_CALL_SITE,
570
1207
  providerName: provider.name,
571
1208
  candidateCount: ordered.length,
572
- stableCount: pool.stable.length,
573
- finderCount: pool.finder.length,
1209
+ stableCount: budgetedPool.stable.length,
1210
+ finderCount: budgetedPool.finder.length,
574
1211
  };
575
1212
  failures.push(diagnostic);
576
1213
  log.warn(diagnostic, "pool selector attempt failed");
@@ -597,6 +1234,7 @@ export async function selectPool(
597
1234
  config: {
598
1235
  callSite: MEMORY_V3_SELECT_CALL_SITE,
599
1236
  conversationId: turn.conversationId,
1237
+ selectionSeed: turn.conversationId,
600
1238
  tool_choice: { type: "tool" as const, name: SELECT_PAGES_TOOL_NAME },
601
1239
  // The last block of this one-shot message varies every turn; the
602
1240
  // provider's auto-applied turn-start breakpoint would land on it and
@@ -663,8 +1301,8 @@ export async function selectPool(
663
1301
  log.warn(
664
1302
  {
665
1303
  candidateCount: ordered.length,
666
- stableCount: pool.stable.length,
667
- finderCount: pool.finder.length,
1304
+ stableCount: budgetedPool.stable.length,
1305
+ finderCount: budgetedPool.finder.length,
668
1306
  callSite: MEMORY_V3_SELECT_CALL_SITE,
669
1307
  providerName: provider.name,
670
1308
  failures,
@@ -676,14 +1314,16 @@ export async function selectPool(
676
1314
  {
677
1315
  cause: lastError,
678
1316
  conversationNotice: providerBillingNoticeFromError(lastError),
1317
+ pool: budgetedPool,
1318
+ turn: budgetedTurn,
679
1319
  },
680
1320
  );
681
1321
  }
682
1322
  log.warn(
683
1323
  {
684
1324
  candidateCount: ordered.length,
685
- stableCount: pool.stable.length,
686
- finderCount: pool.finder.length,
1325
+ stableCount: budgetedPool.stable.length,
1326
+ finderCount: budgetedPool.finder.length,
687
1327
  callSite: MEMORY_V3_SELECT_CALL_SITE,
688
1328
  providerName: provider.name,
689
1329
  failures,
@@ -692,12 +1332,18 @@ export async function selectPool(
692
1332
  );
693
1333
  throw new MemoryV3RetrievalUnavailableError(
694
1334
  "memory-v3 pool selector returned no usable selection after retries",
1335
+ { pool: budgetedPool, turn: budgetedTurn },
695
1336
  );
696
1337
  }
697
1338
 
698
1339
  // Omitted `ids` is the recall-safe "keep all candidates" signal.
699
1340
  if (parsed.ids === undefined) {
700
- return { pages: selectAllPoolCandidates(pool), keptAll: true };
1341
+ return {
1342
+ pages: selectAllPoolCandidates(budgetedPool),
1343
+ keptAll: true,
1344
+ pool: budgetedPool,
1345
+ turn: budgetedTurn,
1346
+ };
701
1347
  }
702
1348
 
703
1349
  // Map 1-based IDs over the concatenated numbering, dropping out-of-range
@@ -705,5 +1351,10 @@ export async function selectPool(
705
1351
  const picked = parsed.ids
706
1352
  .filter((id) => id >= 1 && id <= ordered.length)
707
1353
  .map((id) => id - 1);
708
- return { pages: mergeSelectedLines(ordered, picked), keptAll: false };
1354
+ return {
1355
+ pages: mergeSelectedLines(ordered, picked),
1356
+ keptAll: false,
1357
+ pool: budgetedPool,
1358
+ turn: budgetedTurn,
1359
+ };
709
1360
  }