@vellumai/assistant 0.8.5 → 0.8.6

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 (544) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/bunfig.toml +6 -1
  4. package/docs/credential-execution-service.md +6 -6
  5. package/docs/plugins.md +4 -3
  6. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  7. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  9. package/openapi.yaml +1900 -166
  10. package/package.json +1 -1
  11. package/src/__tests__/actor-token-service.test.ts +3 -2
  12. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  13. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  14. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  15. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  16. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  17. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/approval-routes-http.test.ts +9 -13
  20. package/src/__tests__/assert-not-live-db.ts +79 -0
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
  22. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  23. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  24. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  25. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  26. package/src/__tests__/btw-routes.test.ts +3 -2
  27. package/src/__tests__/call-controller.test.ts +3 -2
  28. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  29. package/src/__tests__/channel-guardian.test.ts +3 -2
  30. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  31. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  32. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  33. package/src/__tests__/checker.test.ts +12 -12
  34. package/src/__tests__/compaction-events.test.ts +1 -0
  35. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  36. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  37. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  38. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  39. package/src/__tests__/computer-use-tools.test.ts +12 -14
  40. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  41. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  42. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  43. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  44. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  45. package/src/__tests__/config-schema.test.ts +10 -10
  46. package/src/__tests__/connection-model-compat.test.ts +83 -0
  47. package/src/__tests__/contacts-tools.test.ts +3 -2
  48. package/src/__tests__/context-token-estimator.test.ts +22 -0
  49. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  50. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  52. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
  54. package/src/__tests__/conversation-agent-loop.test.ts +488 -2
  55. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  56. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  57. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  58. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  61. package/src/__tests__/conversation-error.test.ts +30 -0
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -8
  63. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  64. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  65. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  66. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  67. package/src/__tests__/conversation-lifecycle.test.ts +1 -0
  68. package/src/__tests__/conversation-list-source.test.ts +3 -2
  69. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  70. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
  71. package/src/__tests__/conversation-pairing.test.ts +53 -0
  72. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  73. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  74. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
  75. package/src/__tests__/conversation-queue.test.ts +333 -291
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  80. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  81. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  82. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  83. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  85. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  90. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  91. package/src/__tests__/conversation-title-service.test.ts +1 -0
  92. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  93. package/src/__tests__/conversation-usage.test.ts +1 -0
  94. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  95. package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
  96. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
  97. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  98. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  99. package/src/__tests__/credential-execution-client.test.ts +72 -1
  100. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  101. package/src/__tests__/credential-health-service.test.ts +252 -3
  102. package/src/__tests__/credential-security-invariants.test.ts +5 -5
  103. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  104. package/src/__tests__/credential-vault.test.ts +5 -5
  105. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  106. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  107. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  108. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  109. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  110. package/src/__tests__/db-test-helpers.ts +58 -0
  111. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  112. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  113. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  114. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  115. package/src/__tests__/dm-persistence.test.ts +26 -40
  116. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  117. package/src/__tests__/document-find-replace.test.ts +3 -2
  118. package/src/__tests__/document-tool-security.test.ts +81 -2
  119. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  120. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  121. package/src/__tests__/encrypted-store.test.ts +11 -9
  122. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  123. package/src/__tests__/filing-service.test.ts +1 -0
  124. package/src/__tests__/first-greeting.test.ts +62 -12
  125. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  126. package/src/__tests__/gemini-provider.test.ts +26 -0
  127. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  128. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  129. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  130. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
  131. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  132. package/src/__tests__/heartbeat-service.test.ts +1 -0
  133. package/src/__tests__/helpers/mock-logger.ts +26 -0
  134. package/src/__tests__/host-bash-routes.test.ts +1 -0
  135. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  136. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  137. package/src/__tests__/host-shell-tool.test.ts +5 -4
  138. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  139. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  140. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  141. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  142. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  143. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  145. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  146. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  148. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  149. package/src/__tests__/llm-resolver.test.ts +331 -0
  150. package/src/__tests__/llm-schema.test.ts +1 -1
  151. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  152. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  153. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  154. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  155. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  156. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  157. package/src/__tests__/model-intents.test.ts +3 -3
  158. package/src/__tests__/native-web-search.test.ts +30 -2
  159. package/src/__tests__/notification-deep-link.test.ts +62 -0
  160. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  161. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  162. package/src/__tests__/oauth-store.test.ts +3 -2
  163. package/src/__tests__/onboarding-template-contract.test.ts +3 -2
  164. package/src/__tests__/openai-provider.test.ts +8 -9
  165. package/src/__tests__/openai-responses-provider.test.ts +70 -10
  166. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  167. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  168. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  169. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  170. package/src/__tests__/plugin-bootstrap.test.ts +9 -11
  171. package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
  172. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  173. package/src/__tests__/process-message-display-content.test.ts +19 -22
  174. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  175. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  176. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  177. package/src/__tests__/recording-handler.test.ts +1 -0
  178. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  179. package/src/__tests__/registry.test.ts +82 -76
  180. package/src/__tests__/relay-server.test.ts +10 -10
  181. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  182. package/src/__tests__/schedule-store.test.ts +16 -1
  183. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  184. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  185. package/src/__tests__/secure-keys.test.ts +3 -3
  186. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  187. package/src/__tests__/server-history-render.test.ts +4 -1
  188. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  189. package/src/__tests__/skill-feature-flags.test.ts +14 -16
  190. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  191. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  192. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  193. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  194. package/src/__tests__/slack-channel-config.test.ts +3 -3
  195. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  196. package/src/__tests__/subagent-disposal.test.ts +27 -8
  197. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  198. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  199. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  200. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  201. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  202. package/src/__tests__/subagent-tools.test.ts +2 -1
  203. package/src/__tests__/suggestion-routes.test.ts +1 -0
  204. package/src/__tests__/system-prompt.test.ts +38 -0
  205. package/src/__tests__/test-preload-verifier.ts +68 -0
  206. package/src/__tests__/test-preload.ts +32 -39
  207. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  208. package/src/__tests__/tool-executor.test.ts +55 -10
  209. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  210. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  211. package/src/__tests__/twilio-routes.test.ts +3 -2
  212. package/src/__tests__/validate-input.test.ts +381 -0
  213. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  214. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  215. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  216. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  217. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  218. package/src/acp/session-manager.ts +5 -6
  219. package/src/agent/loop.ts +80 -0
  220. package/src/api/README.md +124 -2
  221. package/src/api/constants/call-sites.ts +27 -0
  222. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  223. package/src/api/events/assistant-text-delta.ts +32 -0
  224. package/src/api/events/assistant-turn-start.ts +33 -0
  225. package/src/api/events/document-comment-created.ts +48 -0
  226. package/src/api/events/document-comment-deleted.ts +24 -0
  227. package/src/api/events/document-comment-reopened.ts +25 -0
  228. package/src/api/events/document-comment-resolved.ts +27 -0
  229. package/src/api/events/generation-cancelled.ts +24 -0
  230. package/src/api/events/generation-handoff.ts +41 -0
  231. package/src/api/events/message-complete.ts +42 -0
  232. package/src/api/events/open-url.ts +30 -0
  233. package/src/{events → api/events}/relationship-state-updated.ts +3 -3
  234. package/src/api/events/tool-use-start.ts +32 -0
  235. package/src/api/index.ts +128 -3
  236. package/src/api/responses/llm-context-response.ts +39 -0
  237. package/src/api/responses/llm-request-log-entry.ts +93 -0
  238. package/src/api/responses/memory-recall-log.ts +65 -0
  239. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  240. package/src/background-wake/background-wake-routes.test.ts +687 -52
  241. package/src/background-wake/platform-client.test.ts +308 -0
  242. package/src/background-wake/platform-client.ts +167 -0
  243. package/src/background-wake/publisher.ts +91 -0
  244. package/src/background-wake/runtime-registry.ts +2 -2
  245. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  246. package/src/calls/guardian-dispatch.ts +1 -0
  247. package/src/calls/voice-session-bridge.ts +4 -4
  248. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  249. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  250. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  251. package/src/cli/commands/channels/index.ts +229 -0
  252. package/src/cli/commands/memory-v3-render.ts +147 -0
  253. package/src/cli/commands/memory-v3.ts +255 -4
  254. package/src/cli/commands/notifications.ts +365 -55
  255. package/src/cli/lib/open-browser.ts +7 -2
  256. package/src/cli/program.ts +2 -0
  257. package/src/config/assistant-feature-flags.ts +23 -42
  258. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  259. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  260. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  261. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  262. package/src/config/call-site-defaults.ts +1 -1
  263. package/src/config/feature-flag-cache.ts +86 -0
  264. package/src/config/feature-flag-registry.json +17 -17
  265. package/src/config/llm-context-resolution.ts +10 -1
  266. package/src/config/llm-resolver.ts +121 -15
  267. package/src/config/loader.ts +4 -5
  268. package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
  269. package/src/config/schemas/heartbeat.ts +1 -1
  270. package/src/config/schemas/llm.ts +90 -1
  271. package/src/config/schemas/memory-v2.ts +26 -0
  272. package/src/config/schemas/services.ts +6 -2
  273. package/src/config/seed-inference-profiles.ts +36 -16
  274. package/src/context/token-estimator.ts +10 -5
  275. package/src/credential-execution/executable-discovery.ts +40 -0
  276. package/src/credential-execution/process-manager.ts +6 -2
  277. package/src/credential-health/credential-health-service.ts +125 -40
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  279. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  280. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  281. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  282. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  283. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  284. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  285. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  286. package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
  287. package/src/daemon/conversation-agent-loop.ts +132 -28
  288. package/src/daemon/conversation-error.ts +33 -5
  289. package/src/daemon/conversation-messaging.ts +84 -43
  290. package/src/daemon/conversation-process.ts +74 -37
  291. package/src/daemon/conversation-runtime-assembly.ts +29 -9
  292. package/src/daemon/conversation-skill-tools.ts +14 -30
  293. package/src/daemon/conversation-surfaces.ts +69 -34
  294. package/src/daemon/conversation-tool-setup.ts +33 -48
  295. package/src/daemon/conversation.ts +26 -46
  296. package/src/daemon/daemon-control.ts +1 -1
  297. package/src/daemon/daemon-skill-host.ts +9 -2
  298. package/src/daemon/disk-pressure-guard.ts +27 -29
  299. package/src/daemon/first-greeting.ts +31 -13
  300. package/src/daemon/handlers/shared.ts +6 -1
  301. package/src/daemon/lifecycle.ts +12 -12
  302. package/src/daemon/mcp-reload-service.ts +1 -1
  303. package/src/daemon/meet-manifest-loader.ts +10 -17
  304. package/src/daemon/message-types/conversations.ts +20 -22
  305. package/src/daemon/message-types/document-comments.ts +8 -44
  306. package/src/daemon/message-types/home.ts +2 -2
  307. package/src/daemon/message-types/integrations.ts +2 -7
  308. package/src/daemon/message-types/messages.ts +23 -38
  309. package/src/daemon/message-types/subagents.ts +6 -0
  310. package/src/daemon/process-message.ts +9 -9
  311. package/src/daemon/providers-setup.ts +1 -1
  312. package/src/daemon/server.ts +16 -0
  313. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  314. package/src/daemon/tool-setup-types.ts +0 -6
  315. package/src/daemon/wake-target-adapter.ts +10 -0
  316. package/src/documents/document-store.ts +38 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  318. package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
  319. package/src/heartbeat/heartbeat-service.ts +63 -0
  320. package/src/home/__tests__/feed-writer.test.ts +161 -0
  321. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  322. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  323. package/src/home/feed-writer.ts +146 -7
  324. package/src/home/suggested-prompts.ts +27 -145
  325. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  326. package/src/ipc/gateway-client.test.ts +4 -1
  327. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  328. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  329. package/src/ipc/skill-routes/memory.ts +4 -3
  330. package/src/ipc/skill-routes/registries.ts +28 -29
  331. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  332. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  333. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  334. package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
  335. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  336. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  337. package/src/memory/conversation-attention-store.ts +17 -3
  338. package/src/memory/conversation-crud.ts +352 -112
  339. package/src/memory/db-connection.ts +29 -19
  340. package/src/memory/db-init.ts +4 -0
  341. package/src/memory/db-singleton.ts +77 -0
  342. package/src/memory/delivery-channels.ts +82 -0
  343. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  344. package/src/memory/graph/retriever.test.ts +3 -3
  345. package/src/memory/job-handlers/embedding.test.ts +3 -2
  346. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  347. package/src/memory/jobs-worker.ts +12 -1
  348. package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
  349. package/src/memory/llm-request-log-source-local.ts +24 -0
  350. package/src/memory/llm-request-log-source.ts +31 -0
  351. package/src/memory/llm-request-log-store.ts +188 -3
  352. package/src/memory/memory-v2-activation-log-store.ts +95 -1
  353. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  354. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  355. package/src/memory/migrations/index.ts +2 -0
  356. package/src/memory/schema/conversations.ts +9 -1
  357. package/src/memory/schema/inference.ts +0 -1
  358. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  359. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  362. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  363. package/src/memory/v2/harness/metrics.ts +5 -1
  364. package/src/memory/v2/harness/replay-input.ts +19 -3
  365. package/src/memory/v2/harness/runner.ts +6 -0
  366. package/src/memory/v2/harness/trace.ts +6 -0
  367. package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
  368. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  369. package/src/memory/v3/__tests__/edges.test.ts +144 -1
  370. package/src/memory/v3/__tests__/filter.test.ts +48 -0
  371. package/src/memory/v3/__tests__/gate.test.ts +96 -33
  372. package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
  373. package/src/memory/v3/__tests__/loop.test.ts +250 -5
  374. package/src/memory/v3/__tests__/scouts.test.ts +49 -0
  375. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  376. package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
  377. package/src/memory/v3/__tests__/traversal.test.ts +39 -0
  378. package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
  379. package/src/memory/v3/__tests__/validate.test.ts +32 -0
  380. package/src/memory/v3/coretrieval-seed.ts +240 -0
  381. package/src/memory/v3/edges.ts +58 -21
  382. package/src/memory/v3/filter.ts +27 -22
  383. package/src/memory/v3/gate.ts +51 -36
  384. package/src/memory/v3/index-composition.ts +18 -5
  385. package/src/memory/v3/loop.ts +65 -17
  386. package/src/memory/v3/scouts.ts +15 -4
  387. package/src/memory/v3/shadow-diff.ts +287 -0
  388. package/src/memory/v3/shadow-middleware.ts +44 -2
  389. package/src/memory/v3/traversal.ts +6 -1
  390. package/src/memory/v3/tree-walk.ts +6 -1
  391. package/src/memory/v3/validate.ts +56 -33
  392. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  393. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  394. package/src/notifications/adapters/slack.ts +45 -11
  395. package/src/notifications/broadcaster.ts +114 -63
  396. package/src/notifications/conversation-pairing.ts +23 -3
  397. package/src/notifications/decisions-store.ts +32 -1
  398. package/src/notifications/deliveries-store.ts +45 -0
  399. package/src/notifications/edit-notification.ts +201 -0
  400. package/src/notifications/emit-signal.ts +11 -1
  401. package/src/notifications/signal.ts +10 -0
  402. package/src/notifications/types.ts +37 -0
  403. package/src/oauth/byo-connection.test.ts +67 -3
  404. package/src/oauth/byo-connection.ts +32 -5
  405. package/src/oauth/connect-orchestrator.ts +9 -0
  406. package/src/oauth/connection-resolver.test.ts +76 -0
  407. package/src/oauth/connection-resolver.ts +49 -10
  408. package/src/oauth/manual-token-connection.ts +51 -3
  409. package/src/oauth/seed-providers.ts +3 -0
  410. package/src/permissions/approval-policy.test.ts +19 -5
  411. package/src/permissions/approval-policy.ts +14 -3
  412. package/src/permissions/checker.ts +21 -8
  413. package/src/platform/client.test.ts +24 -1
  414. package/src/platform/client.ts +8 -0
  415. package/src/platform/feature-gate.ts +15 -0
  416. package/src/plugins/defaults/injectors.ts +2 -8
  417. package/src/plugins/defaults/persistence.ts +25 -6
  418. package/src/plugins/types.ts +57 -13
  419. package/src/proactive-artifact/job.test.ts +1 -0
  420. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  421. package/src/prompts/system-prompt.ts +38 -40
  422. package/src/prompts/template-detection.ts +10 -4
  423. package/src/prompts/templates/BOOTSTRAP.md +7 -11
  424. package/src/prompts/templates/IDENTITY.md +0 -2
  425. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  426. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  427. package/src/providers/call-site-routing.ts +33 -9
  428. package/src/providers/connection-model-compat.ts +23 -0
  429. package/src/providers/connection-resolution.ts +39 -20
  430. package/src/providers/fireworks/client.ts +1 -0
  431. package/src/providers/gemini/client.ts +24 -3
  432. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  433. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  434. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  435. package/src/providers/inference/auth.ts +0 -8
  436. package/src/providers/inference/connections.ts +3 -66
  437. package/src/providers/inference/resolve-auth.ts +2 -3
  438. package/src/providers/model-catalog.ts +35 -1
  439. package/src/providers/model-intents.ts +3 -3
  440. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  441. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  442. package/src/providers/openai/chat-completions-provider.ts +110 -12
  443. package/src/providers/openai/codex-models.ts +2 -0
  444. package/src/providers/openai/responses-provider.ts +53 -53
  445. package/src/providers/openrouter/client.ts +13 -8
  446. package/src/providers/provider-send-message.ts +18 -9
  447. package/src/providers/registry.ts +48 -8
  448. package/src/providers/retry.ts +16 -4
  449. package/src/providers/search-provider-catalog.ts +17 -9
  450. package/src/providers/types.ts +9 -0
  451. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  452. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  453. package/src/runtime/access-request-helper.ts +1 -0
  454. package/src/runtime/auth/route-policy.ts +10 -0
  455. package/src/runtime/channel-readiness-service.ts +68 -0
  456. package/src/runtime/channel-reply-delivery.ts +23 -0
  457. package/src/runtime/channel-retry-sweep.ts +47 -14
  458. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  459. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  460. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  461. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  462. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  463. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  464. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  465. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  466. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
  467. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  468. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  469. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  470. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  471. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  472. package/src/runtime/routes/app-management-routes.ts +111 -4
  473. package/src/runtime/routes/background-wake-routes.ts +188 -20
  474. package/src/runtime/routes/btw-routes.ts +4 -4
  475. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  476. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  477. package/src/runtime/routes/conversation-list-routes.ts +147 -0
  478. package/src/runtime/routes/conversation-management-routes.ts +39 -14
  479. package/src/runtime/routes/conversation-query-routes.ts +60 -10
  480. package/src/runtime/routes/conversation-routes.ts +186 -140
  481. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  482. package/src/runtime/routes/documents-routes.ts +10 -1
  483. package/src/runtime/routes/group-routes.ts +11 -0
  484. package/src/runtime/routes/home-feed-routes.ts +129 -0
  485. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  486. package/src/runtime/routes/identity-routes.ts +30 -9
  487. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  488. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  489. package/src/runtime/routes/index.ts +2 -0
  490. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  491. package/src/runtime/routes/integrations/vercel.ts +15 -0
  492. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  493. package/src/runtime/routes/memory-v3-routes.ts +160 -2
  494. package/src/runtime/routes/migration-routes.ts +20 -13
  495. package/src/runtime/routes/notification-routes.ts +63 -1
  496. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  497. package/src/runtime/routes/surface-action-routes.ts +1 -38
  498. package/src/runtime/routes/surface-content-routes.ts +12 -5
  499. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  500. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  501. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  502. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  503. package/src/runtime/sync/resource-sync-events.ts +1 -1
  504. package/src/runtime/tool-grant-request-helper.ts +1 -0
  505. package/src/schedule/schedule-store.ts +8 -1
  506. package/src/schedule/scheduler.ts +111 -15
  507. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  508. package/src/security/encrypted-store.ts +7 -16
  509. package/src/security/store-path-override.ts +61 -0
  510. package/src/signals/user-message.ts +5 -8
  511. package/src/skills/validate-input.ts +177 -0
  512. package/src/subagent/manager.ts +13 -13
  513. package/src/subagent/types.ts +6 -0
  514. package/src/tasks/tool-sanitizer.ts +2 -2
  515. package/src/tools/apps/definitions.ts +35 -21
  516. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  517. package/src/tools/computer-use/definitions.ts +268 -266
  518. package/src/tools/document/document-tool.ts +131 -8
  519. package/src/tools/execution-target.ts +2 -5
  520. package/src/tools/executor.ts +18 -55
  521. package/src/tools/host-filesystem/edit.test.ts +1 -0
  522. package/src/tools/host-filesystem/read.test.ts +1 -0
  523. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  524. package/src/tools/host-filesystem/write.test.ts +1 -0
  525. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  526. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  527. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  528. package/src/tools/network/managed-search-proxy.ts +183 -0
  529. package/src/tools/network/web-search.ts +199 -44
  530. package/src/tools/policy-context.ts +3 -1
  531. package/src/tools/registry.ts +146 -103
  532. package/src/tools/schedule/create.ts +1 -1
  533. package/src/tools/skills/skill-tool-factory.ts +17 -36
  534. package/src/tools/subagent/spawn.ts +3 -0
  535. package/src/tools/tool-approval-handler.ts +10 -4
  536. package/src/tools/tool-name-aliases.ts +72 -14
  537. package/src/tools/types.ts +17 -15
  538. package/src/tools/ui-surface/definitions.ts +98 -86
  539. package/src/types/onboarding-context.ts +6 -0
  540. package/src/usage/attribution.ts +32 -1
  541. package/src/util/browser.ts +7 -2
  542. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  543. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  544. package/src/workspace/migrations/registry.ts +4 -0
@@ -12,7 +12,7 @@ import {
12
12
  getConversation,
13
13
  setConversationInferenceProfileSession,
14
14
  } from "../memory/conversation-crud.js";
15
- import { getDb, resetDb } from "../memory/db-connection.js";
15
+ import { getDb } from "../memory/db-connection.js";
16
16
  import { initializeDb } from "../memory/db-init.js";
17
17
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
18
18
  import {
@@ -20,6 +20,7 @@ import {
20
20
  stopInferenceProfileSessionReaper,
21
21
  tickInferenceProfileReaper,
22
22
  } from "../runtime/routes/inference-profile-session-reaper.js";
23
+ import { resetDbForTesting } from "./db-test-helpers.js";
23
24
 
24
25
  initializeDb();
25
26
 
@@ -40,7 +41,7 @@ describe("inference-profile-session-reaper", () => {
40
41
 
41
42
  afterAll(() => {
42
43
  stopInferenceProfileSessionReaper();
43
- resetDb();
44
+ resetDbForTesting();
44
45
  mock.restore();
45
46
  });
46
47
 
@@ -38,9 +38,10 @@ mock.module("../config/loader.js", () => ({
38
38
  }));
39
39
 
40
40
  import { createConversation } from "../memory/conversation-crud.js";
41
- import { getDb, resetDb } from "../memory/db-connection.js";
41
+ import { getDb } from "../memory/db-connection.js";
42
42
  import { initializeDb } from "../memory/db-init.js";
43
43
  import { ROUTES } from "../runtime/routes/inference-profile-session-routes.js";
44
+ import { resetDbForTesting } from "./db-test-helpers.js";
44
45
 
45
46
  initializeDb();
46
47
 
@@ -66,7 +67,7 @@ function clearTables(): void {
66
67
  describe("inference_profile_open IPC op", () => {
67
68
  beforeEach(clearTables);
68
69
  afterAll(() => {
69
- resetDb();
70
+ resetDbForTesting();
70
71
  mock.restore();
71
72
  });
72
73
 
@@ -1,6 +1,5 @@
1
1
  import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
4
3
  import {
5
4
  applyRuntimeInjections,
6
5
  stripInjectionsForCompaction,
@@ -54,10 +53,9 @@ describe("disk-pressure-warning injector", () => {
54
53
  beforeEach(() => {
55
54
  resetPluginRegistryForTests();
56
55
  registerPlugin(defaultInjectorsPlugin);
57
- _setOverridesForTesting({ "safe-storage-limits": true });
58
56
  });
59
57
 
60
- test("emits the exact cleanup prompt while safe storage limits are enabled", async () => {
58
+ test("emits the exact cleanup prompt during disk pressure cleanup mode", async () => {
61
59
  const block = await diskPressureInjector.produce(
62
60
  makeContext({
63
61
  injectionInputs: { diskPressureContext: cleanupContext },
@@ -73,13 +71,13 @@ describe("disk-pressure-warning injector", () => {
73
71
  DEFAULT_INJECTOR_ORDER.diskPressureWarning,
74
72
  );
75
73
  expect(DISK_PRESSURE_WARNING_PROMPT).toBe(`<disk_pressure_warning>
76
- Disk usage is critically low: this assistant is in storage cleanup mode because the workspace volume is at least 95% full.
74
+ Disk usage is critically low: this assistant is in storage cleanup mode because the workspace volume is critically full.
77
75
 
78
76
  In your first paragraph, warn the user that storage is critically low and that normal work is suspended until space is freed.
79
77
 
80
78
  Then help the user clean up storage. Prefer safe inspection steps first, such as checking available space and finding large directories. Ask before deleting files or caches unless the user has already clearly approved the specific cleanup action.
81
79
 
82
- Do not work on unrelated tasks until disk usage drops below the critical threshold or the user explicitly overrides the lock. Background processes and messages from trusted contacts are blocked while this cleanup mode is active.
80
+ Do not work on unrelated tasks until enough space is freed to clear the lock or the user explicitly overrides it. Background processes and messages from trusted contacts are blocked while this cleanup mode is active.
83
81
  </disk_pressure_warning>`);
84
82
  });
85
83
 
@@ -101,18 +99,6 @@ Do not work on unrelated tasks until disk usage drops below the critical thresho
101
99
  ).resolves.toBeNull();
102
100
  });
103
101
 
104
- test("omits the prompt when safe storage limits are disabled", async () => {
105
- _setOverridesForTesting({ "safe-storage-limits": false });
106
-
107
- await expect(
108
- diskPressureInjector.produce(
109
- makeContext({
110
- injectionInputs: { diskPressureContext: cleanupContext },
111
- }),
112
- ),
113
- ).resolves.toBeNull();
114
- });
115
-
116
102
  test("prepends ahead of workspace and unified turn context in full mode", async () => {
117
103
  const runMessages: Message[] = [
118
104
  { role: "user", content: [{ type: "text", text: "clean up space" }] },
@@ -69,10 +69,10 @@ mockIpcResponse("get_global_thresholds", {
69
69
 
70
70
  // ── Imports (after mocks) ─────────────────────────────────────────────────
71
71
 
72
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
73
72
  import { check, generateAllowlistOptions } from "../permissions/checker.js";
74
73
  import { clearRiskCache } from "../permissions/checker.js";
75
74
  import { _clearGlobalCacheForTesting } from "../permissions/gateway-threshold-reader.js";
75
+ import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
76
76
 
77
77
  // ── Helpers ───────────────────────────────────────────────────────────────
78
78
 
@@ -115,7 +115,7 @@ describe("inline-command skill_load permissions", () => {
115
115
  beforeEach(() => {
116
116
  clearRiskCache();
117
117
  _clearGlobalCacheForTesting();
118
- _setOverridesForTesting({});
118
+ setOverridesForTesting({});
119
119
  mockIpcResponse("get_global_thresholds", {
120
120
  interactive: "low",
121
121
  autonomous: "medium",
@@ -135,7 +135,7 @@ describe("inline-command skill_load permissions", () => {
135
135
  });
136
136
 
137
137
  afterEach(() => {
138
- _setOverridesForTesting({});
138
+ setOverridesForTesting({});
139
139
  });
140
140
 
141
141
  // ── Default behavior ─────────────────────────────────────────────────
@@ -197,7 +197,7 @@ describe("inline-command skill_load permissions", () => {
197
197
  writeDynamicSkill("dynamic-flag-off", "Dynamic Flag Off Skill");
198
198
 
199
199
  // Disable the feature flag
200
- _setOverridesForTesting({
200
+ setOverridesForTesting({
201
201
  "inline-skill-commands": false,
202
202
  });
203
203
 
@@ -26,12 +26,14 @@ mock.module("../config/loader.js", () => ({
26
26
  }));
27
27
 
28
28
  import {
29
+ _setPaginationScanCapForTesting,
29
30
  addMessage,
30
31
  createConversation,
31
32
  getMessages,
32
33
  } from "../memory/conversation-crud.js";
33
34
  import { getDb } from "../memory/db-connection.js";
34
35
  import { initializeDb } from "../memory/db-init.js";
36
+ import { messages } from "../memory/schema.js";
35
37
  import { handleListMessages } from "../runtime/routes/conversation-routes.js";
36
38
 
37
39
  initializeDb();
@@ -177,6 +179,84 @@ describe("handleListMessages metadata.hidden filtering", () => {
177
179
  expect(older.hasMore).toBe(true);
178
180
  });
179
181
 
182
+ test("pagination keeps hasMore + a cursor when a >cap hidden block truncates the scan", () => {
183
+ // Exercise cap-truncation with a small cap + a few hundred rows rather than
184
+ // >10k: the large seed made the post-test resetTables DELETE slow enough to
185
+ // time out the next test's beforeEach under parallel CI load.
186
+ // SCAN_CAP must span a couple of PAGINATION_CHUNK_MIN (50) chunks so the cap
187
+ // (not DB exhaustion) stops the loop, and HIDDEN_COUNT sits just past it so
188
+ // the newest cap-worth of rows are all hidden.
189
+ const SCAN_CAP = 100;
190
+ _setPaginationScanCapForTesting(SCAN_CAP);
191
+ try {
192
+ const conv = createConversation();
193
+ const db = getDb();
194
+ const HIDDEN_COUNT = SCAN_CAP + 20; // > scan cap
195
+ const VISIBLE_COUNT = 2;
196
+ let createdAt = 0;
197
+ // Older visible rows first (lowest createdAt), then the hidden block.
198
+ db.transaction((tx) => {
199
+ for (let i = 0; i < VISIBLE_COUNT; i++) {
200
+ createdAt += 1;
201
+ tx.insert(messages)
202
+ .values({
203
+ id: `visible-${i}`,
204
+ conversationId: conv.id,
205
+ role: "user",
206
+ content: JSON.stringify([{ type: "text", text: `visible ${i}` }]),
207
+ createdAt,
208
+ })
209
+ .run();
210
+ }
211
+ for (let i = 0; i < HIDDEN_COUNT; i++) {
212
+ createdAt += 1;
213
+ tx.insert(messages)
214
+ .values({
215
+ id: `hidden-${i}`,
216
+ conversationId: conv.id,
217
+ role: "assistant",
218
+ content: JSON.stringify([{ type: "text", text: `hidden ${i}` }]),
219
+ createdAt,
220
+ metadata: JSON.stringify({ hidden: true }),
221
+ })
222
+ .run();
223
+ }
224
+ });
225
+
226
+ const latest = handleListMessages({
227
+ queryParams: { conversationId: conv.id, page: "latest", limit: "2" },
228
+ }) as {
229
+ messages: MessagePayload[];
230
+ hasMore: boolean;
231
+ oldestTimestamp: number | null;
232
+ };
233
+
234
+ // Page is empty (the cap was consumed entirely by hidden rows) but the
235
+ // client must still be told to keep paginating and given a cursor.
236
+ expect(latest.messages).toHaveLength(0);
237
+ expect(latest.hasMore).toBe(true);
238
+ expect(latest.oldestTimestamp).not.toBeNull();
239
+
240
+ // Resuming from the surfaced cursor drains the remaining hidden rows and
241
+ // surfaces the visible ones below the cap.
242
+ const older = handleListMessages({
243
+ queryParams: {
244
+ conversationId: conv.id,
245
+ beforeTimestamp: String(latest.oldestTimestamp),
246
+ limit: "2",
247
+ },
248
+ }) as { messages: MessagePayload[]; hasMore: boolean };
249
+
250
+ expect(older.messages.map((m) => m.content)).toEqual([
251
+ "visible 0",
252
+ "visible 1",
253
+ ]);
254
+ expect(older.hasMore).toBe(false);
255
+ } finally {
256
+ _setPaginationScanCapForTesting(undefined);
257
+ }
258
+ });
259
+
180
260
  test("pagination drains DB when every row in a page is hidden", async () => {
181
261
  const conv = createConversation();
182
262
  // 5 hidden rows then 2 visible older rows. With limit=2, the naive
@@ -1732,6 +1732,48 @@ describe("normalizeLlmContextPayloads", () => {
1732
1732
  ]);
1733
1733
  });
1734
1734
 
1735
+ test("counts Codex Responses native web_search request tools", () => {
1736
+ const normalized = normalizeLlmContextPayloads({
1737
+ createdAt: 1_742_400_000_032,
1738
+ requestPayload: {
1739
+ model: "gpt-5.4",
1740
+ instructions: "Search the web when needed.",
1741
+ input: [
1742
+ {
1743
+ role: "user",
1744
+ content: [{ type: "input_text", text: "What is the weather?" }],
1745
+ type: "message",
1746
+ },
1747
+ ],
1748
+ tools: [{ type: "web_search", external_web_access: false }],
1749
+ },
1750
+ responsePayload: {
1751
+ model: "gpt-5.4",
1752
+ output: [
1753
+ {
1754
+ type: "message",
1755
+ role: "assistant",
1756
+ content: [
1757
+ { type: "output_text", text: "It is sunny in Boston today." },
1758
+ ],
1759
+ },
1760
+ ],
1761
+ usage: { input_tokens: 30, output_tokens: 15 },
1762
+ status: "completed",
1763
+ },
1764
+ });
1765
+
1766
+ expect(normalized.summary?.requestToolCount).toBe(1);
1767
+ expect(normalized.requestSections).toContainEqual({
1768
+ kind: "tool_definitions",
1769
+ label: "Available tools",
1770
+ data: {
1771
+ tools: [{ type: "web_search", external_web_access: false }],
1772
+ },
1773
+ language: "json",
1774
+ });
1775
+ });
1776
+
1735
1777
  test("normalizes Responses API response with only web_search_call (no message)", () => {
1736
1778
  const normalized = normalizeLlmContextPayloads({
1737
1779
  createdAt: 1_742_400_000_031,
@@ -812,6 +812,299 @@ describe("resolveCallSiteConfig", () => {
812
812
  });
813
813
  });
814
814
 
815
+ describe("mix profiles", () => {
816
+ // A mix that routes 80% to `a` (model-a) and 20% to `b` (model-b).
817
+ const mixLlm = LLMSchema.parse({
818
+ default: fullDefault,
819
+ profiles: {
820
+ a: { model: "model-a", effort: "low" },
821
+ b: { model: "model-b", effort: "high" },
822
+ ab: {
823
+ mix: [
824
+ { profile: "a", weight: 80 },
825
+ { profile: "b", weight: 20 },
826
+ ],
827
+ },
828
+ },
829
+ activeProfile: "ab",
830
+ });
831
+
832
+ test("same seed resolves to the same arm (stable across calls)", () => {
833
+ const first = resolveCallSiteConfig("mainAgent", mixLlm, {
834
+ selectionSeed: "conv-1",
835
+ });
836
+ const second = resolveCallSiteConfig("mainAgent", mixLlm, {
837
+ selectionSeed: "conv-1",
838
+ });
839
+ expect(first.model).toBe(second.model);
840
+ expect(["model-a", "model-b"]).toContain(first.model);
841
+ // The chosen arm's other fields flow through; the other arm's don't.
842
+ if (first.model === "model-a") expect(first.effort).toBe("low");
843
+ else expect(first.effort).toBe("high");
844
+ });
845
+
846
+ test("all dereference spots in a turn agree for the same seed", () => {
847
+ // mainAgent (mix layered as activeProfile) and a non-main call site
848
+ // resolving the same mix as activeProfile must pick the same arm when
849
+ // given the same conversation seed — guards the invariant that every
850
+ // resolver call within a conversation lands on one arm.
851
+ const main = resolveCallSiteConfig("mainAgent", mixLlm, {
852
+ selectionSeed: "conv-xyz",
853
+ });
854
+ const other = resolveCallSiteConfig("memoryExtraction", mixLlm, {
855
+ selectionSeed: "conv-xyz",
856
+ });
857
+ expect(other.model).toBe(main.model);
858
+ });
859
+
860
+ test("different seeds split across both arms by weight", () => {
861
+ let aCount = 0;
862
+ let bCount = 0;
863
+ for (let i = 0; i < 400; i++) {
864
+ const resolved = resolveCallSiteConfig("mainAgent", mixLlm, {
865
+ selectionSeed: `conv-${i}`,
866
+ });
867
+ if (resolved.model === "model-a") aCount++;
868
+ else if (resolved.model === "model-b") bCount++;
869
+ }
870
+ // Both arms must be reachable, and the 80/20 weighting must skew toward
871
+ // `a`. Wide band so the assertion locks weighting without coupling to the
872
+ // exact hash output (deterministic, so never flaky).
873
+ expect(aCount).toBeGreaterThan(bCount);
874
+ expect(bCount).toBeGreaterThan(0);
875
+ expect(aCount).toBeGreaterThan(240); // ~80% of 400 = 320
876
+ expect(aCount).toBeLessThan(390);
877
+ });
878
+
879
+ test("relative weights are normalized by their sum ([80,20] ≡ [4,1])", () => {
880
+ const llm2 = LLMSchema.parse({
881
+ default: fullDefault,
882
+ profiles: {
883
+ a: { model: "model-a" },
884
+ b: { model: "model-b" },
885
+ ab: {
886
+ mix: [
887
+ { profile: "a", weight: 4 },
888
+ { profile: "b", weight: 1 },
889
+ ],
890
+ },
891
+ },
892
+ activeProfile: "ab",
893
+ });
894
+ for (let i = 0; i < 200; i++) {
895
+ const seed = `conv-${i}`;
896
+ expect(
897
+ resolveCallSiteConfig("mainAgent", llm2, { selectionSeed: seed }).model,
898
+ ).toBe(
899
+ resolveCallSiteConfig("mainAgent", mixLlm, { selectionSeed: seed })
900
+ .model,
901
+ );
902
+ }
903
+ });
904
+
905
+ test("mix works as overrideProfile", () => {
906
+ const llm = LLMSchema.parse({
907
+ default: fullDefault,
908
+ profiles: {
909
+ a: { model: "model-a" },
910
+ b: { model: "model-b" },
911
+ ab: {
912
+ mix: [
913
+ { profile: "a", weight: 50 },
914
+ { profile: "b", weight: 50 },
915
+ ],
916
+ },
917
+ },
918
+ });
919
+ const resolved = resolveCallSiteConfig("mainAgent", llm, {
920
+ overrideProfile: "ab",
921
+ selectionSeed: "conv-1",
922
+ });
923
+ expect(["model-a", "model-b"]).toContain(resolved.model);
924
+ });
925
+
926
+ test("mix works as a call-site profile (non-mainAgent and mainAgent)", () => {
927
+ const llm = LLMSchema.parse({
928
+ default: fullDefault,
929
+ profiles: {
930
+ a: { model: "model-a" },
931
+ b: { model: "model-b" },
932
+ ab: {
933
+ mix: [
934
+ { profile: "a", weight: 50 },
935
+ { profile: "b", weight: 50 },
936
+ ],
937
+ },
938
+ },
939
+ callSites: {
940
+ memoryExtraction: { profile: "ab" },
941
+ mainAgent: { profile: "ab" },
942
+ },
943
+ });
944
+ expect(["model-a", "model-b"]).toContain(
945
+ resolveCallSiteConfig("memoryExtraction", llm, { selectionSeed: "c1" })
946
+ .model,
947
+ );
948
+ expect(["model-a", "model-b"]).toContain(
949
+ resolveCallSiteConfig("mainAgent", llm, { selectionSeed: "c1" }).model,
950
+ );
951
+ });
952
+
953
+ test("onMixSelected reports the mix name and the chosen arm matching the resolved model", () => {
954
+ const calls: Array<{ mixProfile: string; chosenProfile: string }> = [];
955
+ const resolved = resolveCallSiteConfig("mainAgent", mixLlm, {
956
+ selectionSeed: "conv-1",
957
+ onMixSelected: (info) => calls.push(info),
958
+ });
959
+ expect(calls).toHaveLength(1);
960
+ expect(calls[0].mixProfile).toBe("ab");
961
+ expect(["a", "b"]).toContain(calls[0].chosenProfile);
962
+ expect(resolved.model).toBe(
963
+ calls[0].chosenProfile === "a" ? "model-a" : "model-b",
964
+ );
965
+ });
966
+
967
+ test("no seed falls back to random selection without throwing (both arms reachable)", () => {
968
+ const seen = new Set<string>();
969
+ for (let i = 0; i < 200; i++) {
970
+ seen.add(resolveCallSiteConfig("mainAgent", mixLlm).model);
971
+ }
972
+ expect(seen.has("model-a")).toBe(true);
973
+ expect(seen.has("model-b")).toBe(true);
974
+ });
975
+ });
976
+
977
+ describe("mix validation (LLMSchema.superRefine)", () => {
978
+ const base = {
979
+ default: fullDefault,
980
+ profiles: {
981
+ a: { model: "model-a" },
982
+ b: { model: "model-b" },
983
+ },
984
+ };
985
+
986
+ test("valid mix parses cleanly", () => {
987
+ expect(() =>
988
+ LLMSchema.parse({
989
+ ...base,
990
+ profiles: {
991
+ ...base.profiles,
992
+ ab: {
993
+ mix: [
994
+ { profile: "a", weight: 80 },
995
+ { profile: "b", weight: 20 },
996
+ ],
997
+ },
998
+ },
999
+ }),
1000
+ ).not.toThrow();
1001
+ });
1002
+
1003
+ test("rejects a mix referencing an undefined profile", () => {
1004
+ expect(() =>
1005
+ LLMSchema.parse({
1006
+ ...base,
1007
+ profiles: {
1008
+ ...base.profiles,
1009
+ ab: {
1010
+ mix: [
1011
+ { profile: "a", weight: 1 },
1012
+ { profile: "ghost", weight: 1 },
1013
+ ],
1014
+ },
1015
+ },
1016
+ }),
1017
+ ).toThrow(/not defined in llm\.profiles/);
1018
+ });
1019
+
1020
+ test("rejects a nested mix (arm references another mix)", () => {
1021
+ expect(() =>
1022
+ LLMSchema.parse({
1023
+ ...base,
1024
+ profiles: {
1025
+ ...base.profiles,
1026
+ ab: {
1027
+ mix: [
1028
+ { profile: "a", weight: 1 },
1029
+ { profile: "b", weight: 1 },
1030
+ ],
1031
+ },
1032
+ outer: {
1033
+ mix: [
1034
+ { profile: "ab", weight: 1 },
1035
+ { profile: "a", weight: 1 },
1036
+ ],
1037
+ },
1038
+ },
1039
+ }),
1040
+ ).toThrow(/cannot be nested/);
1041
+ });
1042
+
1043
+ test("rejects a self-referencing mix", () => {
1044
+ expect(() =>
1045
+ LLMSchema.parse({
1046
+ ...base,
1047
+ profiles: {
1048
+ ...base.profiles,
1049
+ ab: {
1050
+ mix: [
1051
+ { profile: "ab", weight: 1 },
1052
+ { profile: "a", weight: 1 },
1053
+ ],
1054
+ },
1055
+ },
1056
+ }),
1057
+ ).toThrow(/cannot reference itself/);
1058
+ });
1059
+
1060
+ test("rejects a mix that also sets a config field", () => {
1061
+ expect(() =>
1062
+ LLMSchema.parse({
1063
+ ...base,
1064
+ profiles: {
1065
+ ...base.profiles,
1066
+ ab: {
1067
+ model: "model-c",
1068
+ mix: [
1069
+ { profile: "a", weight: 1 },
1070
+ { profile: "b", weight: 1 },
1071
+ ],
1072
+ },
1073
+ },
1074
+ }),
1075
+ ).toThrow(/cannot also set/);
1076
+ });
1077
+
1078
+ test("rejects a mix with fewer than two arms", () => {
1079
+ expect(() =>
1080
+ LLMSchema.parse({
1081
+ ...base,
1082
+ profiles: {
1083
+ ...base.profiles,
1084
+ ab: { mix: [{ profile: "a", weight: 1 }] },
1085
+ },
1086
+ }),
1087
+ ).toThrow();
1088
+ });
1089
+
1090
+ test("rejects a non-positive arm weight", () => {
1091
+ expect(() =>
1092
+ LLMSchema.parse({
1093
+ ...base,
1094
+ profiles: {
1095
+ ...base.profiles,
1096
+ ab: {
1097
+ mix: [
1098
+ { profile: "a", weight: 0 },
1099
+ { profile: "b", weight: 1 },
1100
+ ],
1101
+ },
1102
+ },
1103
+ }),
1104
+ ).toThrow();
1105
+ });
1106
+ });
1107
+
815
1108
  describe("resolveDefaultProfileKey", () => {
816
1109
  test("mainAgent returns activeProfile when set and enabled", () => {
817
1110
  const llm = LLMSchema.parse({
@@ -894,6 +1187,44 @@ describe("resolveDefaultProfileKey", () => {
894
1187
  "custom-cost-optimized",
895
1188
  );
896
1189
  });
1190
+
1191
+ test("mainAgent returns the mix key (not an arm) when activeProfile is a mix", () => {
1192
+ const llm = LLMSchema.parse({
1193
+ default: fullDefault,
1194
+ profiles: {
1195
+ a: { model: "model-a" },
1196
+ b: { model: "model-b" },
1197
+ ab: {
1198
+ mix: [
1199
+ { profile: "a", weight: 1 },
1200
+ { profile: "b", weight: 1 },
1201
+ ],
1202
+ },
1203
+ },
1204
+ activeProfile: "ab",
1205
+ });
1206
+ expect(resolveDefaultProfileKey("mainAgent", llm)).toBe("ab");
1207
+ });
1208
+
1209
+ test("mainAgent falls back to catalog default when the mix activeProfile is disabled", () => {
1210
+ const llm = LLMSchema.parse({
1211
+ default: fullDefault,
1212
+ profiles: {
1213
+ balanced: { provider: "anthropic", model: "claude-sonnet-4-7" },
1214
+ a: { model: "model-a" },
1215
+ b: { model: "model-b" },
1216
+ ab: {
1217
+ status: "disabled",
1218
+ mix: [
1219
+ { profile: "a", weight: 1 },
1220
+ { profile: "b", weight: 1 },
1221
+ ],
1222
+ },
1223
+ },
1224
+ activeProfile: "ab",
1225
+ });
1226
+ expect(resolveDefaultProfileKey("mainAgent", llm)).toBe("balanced");
1227
+ });
897
1228
  });
898
1229
 
899
1230
  describe("memory v3 call sites resolve through the standard resolver", () => {
@@ -72,7 +72,7 @@ describe("LLMSchema", () => {
72
72
  const parsed = LLMSchema.parse({});
73
73
  expect(parsed.default).toEqual({
74
74
  provider: "anthropic",
75
- model: "claude-opus-4-7",
75
+ model: "claude-opus-4-8",
76
76
  maxTokens: 64000,
77
77
  effort: "max",
78
78
  speed: "standard",