@vellumai/assistant 0.8.4 → 0.8.5

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 (438) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docs/browser-use-architecture-phase2.md +1 -1
  3. package/knip.json +2 -1
  4. package/openapi.yaml +809 -11
  5. package/package.json +1 -1
  6. package/src/__tests__/anthropic-provider.test.ts +34 -37
  7. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  8. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  9. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
  11. package/src/__tests__/btw-routes.test.ts +2 -3
  12. package/src/__tests__/call-controller.test.ts +0 -1
  13. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  14. package/src/__tests__/channel-guardian.test.ts +3 -3
  15. package/src/__tests__/checker.test.ts +6 -15
  16. package/src/__tests__/compaction-events.test.ts +1 -0
  17. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  18. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  19. package/src/__tests__/computer-use-tools.test.ts +2 -4
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  21. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  22. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
  24. package/src/__tests__/conversation-agent-loop.test.ts +163 -122
  25. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  26. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  27. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  29. package/src/__tests__/conversation-error.test.ts +31 -0
  30. package/src/__tests__/conversation-fork-crud.test.ts +178 -15
  31. package/src/__tests__/conversation-lifecycle.test.ts +52 -11
  32. package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
  33. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  34. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  35. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  36. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  37. package/src/__tests__/conversation-store.test.ts +1 -1
  38. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  40. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  42. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  43. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  44. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  45. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  46. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  47. package/src/__tests__/email-html-renderer.test.ts +12 -0
  48. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  49. package/src/__tests__/gemini-provider.test.ts +78 -0
  50. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  51. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  52. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  53. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  54. package/src/__tests__/heartbeat-service.test.ts +4 -0
  55. package/src/__tests__/host-shell-tool.test.ts +1 -1
  56. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  57. package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
  58. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  59. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  60. package/src/__tests__/llm-resolver.test.ts +77 -9
  61. package/src/__tests__/llm-usage-store.test.ts +66 -0
  62. package/src/__tests__/logger.test.ts +89 -0
  63. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  64. package/src/__tests__/media-generate-image.test.ts +31 -0
  65. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  66. package/src/__tests__/model-intents.test.ts +2 -4
  67. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  68. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  69. package/src/__tests__/openai-provider.test.ts +46 -0
  70. package/src/__tests__/openai-responses-provider.test.ts +114 -12
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
  72. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  73. package/src/__tests__/platform.test.ts +2 -2
  74. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  75. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  76. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  77. package/src/__tests__/plugin-types.test.ts +3 -2
  78. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  79. package/src/__tests__/pricing.test.ts +12 -0
  80. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  81. package/src/__tests__/registry.test.ts +2 -8
  82. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  83. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  84. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  85. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  86. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  87. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  88. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  89. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  90. package/src/__tests__/suggestion-routes.test.ts +1 -0
  91. package/src/__tests__/sync-message-contract.test.ts +59 -0
  92. package/src/__tests__/system-prompt.test.ts +145 -131
  93. package/src/__tests__/terminal-tools.test.ts +1 -1
  94. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  95. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  96. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  98. package/src/__tests__/tool-executor.test.ts +9 -62
  99. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  100. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  101. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  102. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  103. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  104. package/src/__tests__/usage-routes.test.ts +3 -0
  105. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  106. package/src/__tests__/workspace-git-service.test.ts +6 -5
  107. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  108. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  109. package/src/acp/prepare-agent-env.ts +78 -0
  110. package/src/acp/session-manager.ts +1 -1
  111. package/src/agent/loop.ts +8 -0
  112. package/src/api/README.md +5 -0
  113. package/src/api/index.ts +4 -0
  114. package/src/api/package.json +10 -0
  115. package/src/background-wake/background-wake-routes.test.ts +233 -0
  116. package/src/background-wake/runtime-registry.ts +24 -0
  117. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  118. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  119. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  120. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  121. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  122. package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
  123. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  124. package/src/cli/commands/browser.ts +247 -0
  125. package/src/cli/commands/domain.ts +91 -41
  126. package/src/cli/commands/inference.ts +93 -40
  127. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  128. package/src/cli/commands/memory-v2.ts +176 -1
  129. package/src/cli/commands/memory-v3-render.ts +344 -0
  130. package/src/cli/commands/memory-v3.ts +316 -0
  131. package/src/cli/program.ts +2 -0
  132. package/src/config/assistant-feature-flags.ts +21 -9
  133. package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
  134. package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
  135. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  136. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  137. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  138. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  139. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  140. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  141. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  142. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  143. package/src/config/bundled-tool-registry.ts +2 -0
  144. package/src/config/call-site-defaults.ts +7 -6
  145. package/src/config/feature-flag-registry.json +16 -0
  146. package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
  147. package/src/config/schemas/call-site-catalog.ts +21 -7
  148. package/src/config/schemas/llm.ts +12 -1
  149. package/src/config/schemas/memory-v2.ts +246 -0
  150. package/src/config/schemas/memory.ts +2 -1
  151. package/src/context/compactor.ts +52 -0
  152. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  153. package/src/conversations/message-consolidation.ts +404 -0
  154. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  155. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  156. package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
  157. package/src/daemon/conversation-agent-loop.ts +126 -76
  158. package/src/daemon/conversation-error.ts +31 -1
  159. package/src/daemon/conversation-lifecycle.ts +27 -22
  160. package/src/daemon/conversation-runtime-assembly.ts +10 -9
  161. package/src/daemon/conversation-tool-setup.ts +63 -3
  162. package/src/daemon/conversation-usage.ts +2 -0
  163. package/src/daemon/conversation.ts +14 -29
  164. package/src/daemon/disk-pressure-guard.ts +14 -2
  165. package/src/daemon/handlers/config-model.test.ts +1 -0
  166. package/src/daemon/handlers/conversations.ts +11 -3
  167. package/src/daemon/host-browser-proxy.ts +5 -5
  168. package/src/daemon/host-cu-proxy.ts +4 -4
  169. package/src/daemon/host-file-proxy.ts +4 -4
  170. package/src/daemon/host-proxy-base.ts +4 -4
  171. package/src/daemon/host-transfer-proxy.ts +10 -10
  172. package/src/daemon/lifecycle.ts +23 -20
  173. package/src/daemon/meet-manifest-loader.ts +1 -7
  174. package/src/daemon/message-types/conversations.ts +6 -9
  175. package/src/daemon/message-types/home.ts +1 -13
  176. package/src/daemon/message-types/messages.ts +6 -14
  177. package/src/daemon/message-types/sync.ts +14 -0
  178. package/src/daemon/shutdown-handlers.ts +24 -5
  179. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  180. package/src/daemon/tool-setup-types.ts +13 -0
  181. package/src/events/relationship-state-updated.ts +25 -0
  182. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
  183. package/src/home/home-greeting.ts +0 -9
  184. package/src/home/suggested-prompts.ts +0 -9
  185. package/src/ipc/gateway-flag-listener.ts +123 -0
  186. package/src/ipc/skill-routes/registries.ts +8 -12
  187. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  188. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  189. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  190. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  191. package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
  192. package/src/memory/auto-analysis-enqueue.ts +5 -1
  193. package/src/memory/conversation-crud.ts +71 -70
  194. package/src/memory/conversation-starters-cadence.ts +3 -1
  195. package/src/memory/conversation-title-service.ts +19 -3
  196. package/src/memory/db-async-query.ts +214 -0
  197. package/src/memory/db-init.ts +10 -0
  198. package/src/memory/db-maintenance.ts +30 -21
  199. package/src/memory/graph/bootstrap.ts +8 -1
  200. package/src/memory/graph/capability-seed.ts +7 -3
  201. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  202. package/src/memory/graph/extraction.ts +1 -5
  203. package/src/memory/graph/graph-search.ts +7 -1
  204. package/src/memory/indexer.ts +28 -18
  205. package/src/memory/job-handlers/cleanup.ts +76 -18
  206. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  207. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  208. package/src/memory/jobs-store.ts +14 -0
  209. package/src/memory/jobs-worker.ts +55 -22
  210. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  211. package/src/memory/llm-request-log-source-local.ts +7 -0
  212. package/src/memory/llm-request-log-source.ts +9 -2
  213. package/src/memory/llm-request-log-store.ts +43 -1
  214. package/src/memory/llm-usage-store.ts +24 -0
  215. package/src/memory/memory-retrospective-enqueue.ts +8 -1
  216. package/src/memory/memory-retrospective-job.ts +5 -0
  217. package/src/memory/memory-v2-activation-log-store.ts +15 -6
  218. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  219. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  220. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  221. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  222. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  223. package/src/memory/migrations/index.ts +17 -0
  224. package/src/memory/migrations/registry.ts +33 -0
  225. package/src/memory/schema/conversations.ts +1 -1
  226. package/src/memory/schema/infrastructure.ts +21 -0
  227. package/src/memory/tool-usage-store.ts +36 -8
  228. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  229. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  230. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  231. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  232. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  233. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  234. package/src/memory/v2/__tests__/injection.test.ts +127 -98
  235. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  236. package/src/memory/v2/__tests__/router.test.ts +171 -3
  237. package/src/memory/v2/harness/compare.ts +57 -0
  238. package/src/memory/v2/harness/metrics.ts +124 -0
  239. package/src/memory/v2/harness/oracle.ts +145 -0
  240. package/src/memory/v2/harness/replay-input.ts +224 -0
  241. package/src/memory/v2/harness/retriever.ts +74 -0
  242. package/src/memory/v2/harness/router-retriever.ts +43 -0
  243. package/src/memory/v2/harness/runner.ts +106 -0
  244. package/src/memory/v2/harness/trace.ts +58 -0
  245. package/src/memory/v2/injection.ts +21 -15
  246. package/src/memory/v2/prompts/router.ts +26 -1
  247. package/src/memory/v2/qdrant.ts +14 -2
  248. package/src/memory/v2/router.ts +171 -18
  249. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  250. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  251. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  252. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  253. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  254. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  255. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  256. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  257. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  258. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  259. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  260. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  261. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  262. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  263. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  264. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  265. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  266. package/src/memory/v3/auto-edges.ts +223 -0
  267. package/src/memory/v3/coactivation-store.ts +124 -0
  268. package/src/memory/v3/consolidation-job.ts +323 -0
  269. package/src/memory/v3/edge-learning-job.ts +160 -0
  270. package/src/memory/v3/edges.ts +249 -0
  271. package/src/memory/v3/filter.ts +281 -0
  272. package/src/memory/v3/gate.ts +334 -0
  273. package/src/memory/v3/index-composition.ts +113 -0
  274. package/src/memory/v3/llm-capture.ts +46 -0
  275. package/src/memory/v3/loop.ts +382 -0
  276. package/src/memory/v3/maintenance.ts +144 -0
  277. package/src/memory/v3/prompt-context.ts +33 -0
  278. package/src/memory/v3/prompts/consolidation.ts +458 -0
  279. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  280. package/src/memory/v3/retriever.ts +33 -0
  281. package/src/memory/v3/scouts.ts +420 -0
  282. package/src/memory/v3/shadow-middleware.ts +305 -0
  283. package/src/memory/v3/traversal.ts +206 -0
  284. package/src/memory/v3/tree-index.ts +237 -0
  285. package/src/memory/v3/tree-store.ts +394 -0
  286. package/src/memory/v3/tree-walk.ts +351 -0
  287. package/src/memory/v3/types.ts +65 -0
  288. package/src/memory/v3/validate.ts +300 -0
  289. package/src/notifications/adapters/macos.ts +18 -1
  290. package/src/notifications/adapters/platform.ts +1 -1
  291. package/src/notifications/decision-engine.ts +1 -4
  292. package/src/notifications/emit-signal.ts +29 -49
  293. package/src/permissions/prompter.ts +3 -3
  294. package/src/permissions/question-prompter.ts +5 -2
  295. package/src/permissions/secret-prompter.ts +2 -2
  296. package/src/plugin-api/index.ts +4 -0
  297. package/src/plugin-api/types.ts +7 -33
  298. package/src/plugins/defaults/index.ts +6 -0
  299. package/src/plugins/defaults/injectors.ts +18 -11
  300. package/src/plugins/external-plugin-loader.ts +5 -68
  301. package/src/plugins/types.ts +11 -16
  302. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  303. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  304. package/src/prompts/persona-resolver.ts +36 -21
  305. package/src/prompts/sections.ts +39 -7
  306. package/src/prompts/system-prompt.ts +50 -185
  307. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  308. package/src/prompts/templates/system-sections.ts +230 -8
  309. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  310. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  311. package/src/providers/anthropic/client.ts +32 -66
  312. package/src/providers/call-site-routing.ts +14 -2
  313. package/src/providers/connection-model-compat.ts +38 -0
  314. package/src/providers/connection-resolution.ts +16 -2
  315. package/src/providers/gemini/client.ts +49 -6
  316. package/src/providers/inference/adapter-factory.ts +3 -0
  317. package/src/providers/minimax/client.ts +106 -0
  318. package/src/providers/model-catalog.ts +43 -0
  319. package/src/providers/model-intents.ts +1 -1
  320. package/src/providers/openai/chat-completions-provider.ts +6 -3
  321. package/src/providers/openai/codex-models.ts +18 -0
  322. package/src/providers/openai/responses-provider.ts +78 -21
  323. package/src/providers/provider-send-message.ts +7 -1
  324. package/src/providers/retry.ts +34 -3
  325. package/src/providers/thinking-config.ts +26 -1
  326. package/src/providers/usage-tracking.ts +2 -0
  327. package/src/runtime/AGENTS.md +2 -2
  328. package/src/runtime/agent-wake.ts +1 -0
  329. package/src/runtime/assistant-event-hub.ts +76 -6
  330. package/src/runtime/auth/route-policy.ts +36 -0
  331. package/src/runtime/btw-sidechain.ts +0 -6
  332. package/src/runtime/http-types.ts +0 -2
  333. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  334. package/src/runtime/pending-interactions.ts +0 -1
  335. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
  336. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
  337. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  338. package/src/runtime/routes/acp-routes.test.ts +255 -6
  339. package/src/runtime/routes/acp-routes.ts +8 -1
  340. package/src/runtime/routes/avatar-routes.ts +10 -10
  341. package/src/runtime/routes/background-wake-routes.ts +188 -0
  342. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  343. package/src/runtime/routes/btw-routes.ts +0 -6
  344. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  345. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  346. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  347. package/src/runtime/routes/conversation-query-routes.ts +142 -36
  348. package/src/runtime/routes/conversation-routes.ts +252 -410
  349. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  350. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  351. package/src/runtime/routes/domain-routes.ts +60 -10
  352. package/src/runtime/routes/email-routes.ts +5 -2
  353. package/src/runtime/routes/events-routes.ts +54 -10
  354. package/src/runtime/routes/group-routes.ts +24 -8
  355. package/src/runtime/routes/host-browser-routes.ts +10 -2
  356. package/src/runtime/routes/host-cu-routes.ts +2 -2
  357. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  358. package/src/runtime/routes/index.ts +8 -0
  359. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  360. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  361. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  362. package/src/runtime/routes/memory-item-routes.ts +8 -3
  363. package/src/runtime/routes/memory-v2-routes.ts +215 -5
  364. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  365. package/src/runtime/routes/migration-routes.ts +21 -24
  366. package/src/runtime/routes/plugins-routes.ts +337 -0
  367. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  368. package/src/runtime/routes/secret-routes.ts +25 -5
  369. package/src/runtime/routes/settings-routes.ts +12 -11
  370. package/src/runtime/routes/slack-channel-routes.ts +5 -4
  371. package/src/runtime/routes/workspace-routes.ts +25 -10
  372. package/src/runtime/sync/resource-sync-events.ts +106 -38
  373. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  374. package/src/runtime/sync/sync-publisher.ts +2 -1
  375. package/src/runtime/verification-outbound-actions.ts +73 -1
  376. package/src/telemetry/types.ts +12 -0
  377. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  378. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  379. package/src/tools/acp/spawn.test.ts +119 -0
  380. package/src/tools/acp/spawn.ts +15 -2
  381. package/src/tools/apps/definitions.ts +2 -8
  382. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  383. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  384. package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
  385. package/src/tools/browser/browser-execution.ts +16 -3
  386. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  387. package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
  388. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
  389. package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
  390. package/src/tools/browser/cdp-client/factory.ts +100 -17
  391. package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
  392. package/src/tools/browser/cdp-client/types.ts +65 -0
  393. package/src/tools/browser/pinned-tabs.ts +96 -40
  394. package/src/tools/computer-use/definitions.ts +22 -78
  395. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  396. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  397. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  398. package/src/tools/credentials/vault.ts +3 -9
  399. package/src/tools/document/document-tool.ts +59 -0
  400. package/src/tools/execution-target.ts +21 -23
  401. package/src/tools/executor.ts +6 -1
  402. package/src/tools/filesystem/edit.ts +3 -9
  403. package/src/tools/filesystem/list.ts +3 -9
  404. package/src/tools/filesystem/read.ts +3 -9
  405. package/src/tools/filesystem/write.ts +3 -9
  406. package/src/tools/host-filesystem/edit.ts +3 -9
  407. package/src/tools/host-filesystem/read.ts +3 -9
  408. package/src/tools/host-filesystem/transfer.ts +3 -9
  409. package/src/tools/host-filesystem/write.ts +3 -9
  410. package/src/tools/host-terminal/host-shell.ts +3 -9
  411. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  412. package/src/tools/memory/register.test.ts +1 -1
  413. package/src/tools/memory/register.ts +4 -9
  414. package/src/tools/network/web-fetch.ts +3 -9
  415. package/src/tools/network/web-search.ts +25 -32
  416. package/src/tools/registry.ts +7 -23
  417. package/src/tools/schema-transforms.ts +1 -1
  418. package/src/tools/skills/execute.ts +3 -9
  419. package/src/tools/skills/load.ts +3 -9
  420. package/src/tools/skills/skill-tool-factory.ts +1 -8
  421. package/src/tools/subagent/notify-parent.ts +3 -9
  422. package/src/tools/system/request-permission.ts +3 -9
  423. package/src/tools/terminal/shell.ts +3 -9
  424. package/src/tools/tool-defaults.ts +94 -0
  425. package/src/tools/types.ts +27 -98
  426. package/src/tools/ui-surface/definitions.ts +6 -22
  427. package/src/usage/pricing.ts +23 -0
  428. package/src/usage/types.ts +12 -0
  429. package/src/util/logger.ts +16 -7
  430. package/src/util/platform.ts +7 -2
  431. package/src/util/sqlite3-runtime.ts +65 -0
  432. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  433. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  434. package/src/workspace/migrations/registry.ts +2 -0
  435. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  436. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  437. package/src/daemon/query-complexity-router.ts +0 -75
  438. package/src/prompts/cache-boundary.ts +0 -8
@@ -2,7 +2,10 @@ import { and, eq, sql } from "drizzle-orm";
2
2
 
3
3
  import { getDb } from "../../../../memory/db-connection.js";
4
4
  import { getNode, updateNode } from "../../../../memory/graph/store.js";
5
- import { enqueueMemoryJob } from "../../../../memory/jobs-store.js";
5
+ import {
6
+ enqueueMemoryJob,
7
+ isMemoryEnabled,
8
+ } from "../../../../memory/jobs-store.js";
6
9
  import { memoryGraphNodes } from "../../../../memory/schema.js";
7
10
  import type {
8
11
  Playbook,
@@ -123,7 +126,9 @@ export async function executePlaybookUpdate(
123
126
  lastAccessed: Date.now(),
124
127
  });
125
128
 
126
- enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
129
+ if (isMemoryEnabled()) {
130
+ enqueueMemoryJob("embed_graph_node", { nodeId: existing.id });
131
+ }
127
132
 
128
133
  const autonomyLabel =
129
134
  updated.autonomyLevel === "auto"
@@ -58,6 +58,7 @@ import * as documentCreate from "./bundled-skills/document-editor/tools/document
58
58
  import * as documentDelete from "./bundled-skills/document-editor/tools/document-delete.js";
59
59
  import * as documentFind from "./bundled-skills/document-editor/tools/document-find.js";
60
60
  import * as documentList from "./bundled-skills/document-editor/tools/document-list.js";
61
+ import * as documentOpen from "./bundled-skills/document-editor/tools/document-open.js";
61
62
  import * as documentRead from "./bundled-skills/document-editor/tools/document-read.js";
62
63
  import * as documentReplaceText from "./bundled-skills/document-editor/tools/document-replace-text.js";
63
64
  import * as documentUpdate from "./bundled-skills/document-editor/tools/document-update.js";
@@ -175,6 +176,7 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
175
176
  ["contacts:tools/google-contacts.ts", googleContacts],
176
177
 
177
178
  // document-editor
179
+ ["document-editor:tools/document-open.ts", documentOpen],
178
180
  ["document-editor:tools/document-create.ts", documentCreate],
179
181
  ["document-editor:tools/document-update.ts", documentUpdate],
180
182
  ["document-editor:tools/document-read.ts", documentRead],
@@ -47,6 +47,13 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
47
47
  memoryV2Migration: { profile: "cost-optimized" },
48
48
  memoryV2Sweep: { profile: "cost-optimized" },
49
49
  memoryV2Consolidation: { profile: "balanced" },
50
+ // memory v3: cheap filter + descent, capable gate. All three are
51
+ // selection/classification calls, not generation — pin temperature to 0 so
52
+ // the same candidate set yields the same keep/descend/select decision instead
53
+ // of sampling-driven variance.
54
+ memoryV3Filter: { profile: "cost-optimized", temperature: 0 },
55
+ memoryV3Descent: { profile: "cost-optimized", temperature: 0 },
56
+ memoryV3Gate: { profile: "balanced", temperature: 0 },
50
57
  conversationSummarization: { profile: "cost-optimized" },
51
58
  conversationTitle: { profile: "cost-optimized" },
52
59
  approvalCopy: { profile: "cost-optimized" },
@@ -115,10 +122,4 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
115
122
  effort: "low",
116
123
  thinking: { enabled: false },
117
124
  },
118
- queryComplexityRouter: {
119
- profile: "cost-optimized",
120
- maxTokens: 16,
121
- effort: "low",
122
- thinking: { enabled: false },
123
- },
124
125
  };
@@ -393,6 +393,14 @@
393
393
  "description": "Enable the Notifications tab in settings.",
394
394
  "defaultEnabled": false
395
395
  },
396
+ {
397
+ "id": "preview-channel",
398
+ "scope": "client",
399
+ "key": "preview-channel",
400
+ "label": "Preview Channel",
401
+ "description": "Enable user-facing Preview release channel controls in assistant settings.",
402
+ "defaultEnabled": false
403
+ },
396
404
  {
397
405
  "id": "rollback-enabled",
398
406
  "scope": "assistant",
@@ -424,6 +432,14 @@
424
432
  "label": "Velvet",
425
433
  "description": "Enable the Velvet design theme.",
426
434
  "defaultEnabled": false
435
+ },
436
+ {
437
+ "id": "memory-router-playground",
438
+ "scope": "client",
439
+ "key": "memory-router-playground",
440
+ "label": "Memory Router Playground",
441
+ "description": "Expose the developer-only Memory Router Playground tab in macOS Settings and the /assistant/memory-router-playground web page for dry-running v4 router config overrides against the live page index. Dev-only; default off.",
442
+ "defaultEnabled": false
427
443
  }
428
444
  ]
429
445
  }
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import { MemoryConfigSchema } from "../memory.js";
4
- import { MemoryV2ConfigSchema } from "../memory-v2.js";
4
+ import { MemoryV2ConfigSchema, MemoryV3ConfigSchema } from "../memory-v2.js";
5
5
 
6
6
  describe("MemoryV2ConfigSchema", () => {
7
7
  test("parses an empty object to documented defaults", () => {
@@ -40,6 +40,8 @@ describe("MemoryV2ConfigSchema", () => {
40
40
  batch_size: null,
41
41
  tier1_size: null,
42
42
  tier2_size: null,
43
+ historical_pairs: 1,
44
+ historical_pairs_max_chars: null,
43
45
  },
44
46
  });
45
47
  });
@@ -210,6 +212,216 @@ describe("MemoryV2ConfigSchema", () => {
210
212
  });
211
213
  });
212
214
 
215
+ describe("MemoryV3ConfigSchema", () => {
216
+ test("parses an empty object to documented defaults", () => {
217
+ const parsed = MemoryV3ConfigSchema.parse({});
218
+ expect(parsed).toEqual({
219
+ enabled: false,
220
+ shadow: false,
221
+ passCap: 3,
222
+ breadthBudget: 6,
223
+ maxDepth: 6,
224
+ denseQuota: { activeDomain: 30, offDomain: 8 },
225
+ hotLimit: 50,
226
+ lanes: { hot: true, sparse: true, dense: true, tree: true, edges: true },
227
+ ks: [5, 10, 25, 50],
228
+ write: {
229
+ enabled: false,
230
+ consolidateIntervalMs: 3600000,
231
+ coactivation: false,
232
+ },
233
+ prompts: {
234
+ filter: { override: null, path: null },
235
+ descent: { override: null, path: null },
236
+ gate: { override: null, path: null },
237
+ },
238
+ gateCandidateSummaries: false,
239
+ });
240
+ });
241
+
242
+ test("parses undefined to the same defaults (top-level .default)", () => {
243
+ expect(MemoryV3ConfigSchema.parse(undefined)).toEqual(
244
+ MemoryV3ConfigSchema.parse({}),
245
+ );
246
+ });
247
+
248
+ test("defaults to disabled for backwards compatibility", () => {
249
+ expect(MemoryV3ConfigSchema.parse({}).enabled).toBe(false);
250
+ expect(MemoryV3ConfigSchema.parse({}).shadow).toBe(false);
251
+ });
252
+
253
+ test("accepts explicit scalar overrides", () => {
254
+ const parsed = MemoryV3ConfigSchema.parse({
255
+ enabled: true,
256
+ shadow: true,
257
+ passCap: 5,
258
+ breadthBudget: 10,
259
+ maxDepth: 8,
260
+ });
261
+ expect(parsed.enabled).toBe(true);
262
+ expect(parsed.shadow).toBe(true);
263
+ expect(parsed.passCap).toBe(5);
264
+ expect(parsed.breadthBudget).toBe(10);
265
+ expect(parsed.maxDepth).toBe(8);
266
+ });
267
+
268
+ test("accepts explicit denseQuota override", () => {
269
+ const parsed = MemoryV3ConfigSchema.parse({
270
+ denseQuota: { activeDomain: 50, offDomain: 12 },
271
+ });
272
+ expect(parsed.denseQuota).toEqual({ activeDomain: 50, offDomain: 12 });
273
+ });
274
+
275
+ test("accepts a partial lanes override and defaults the rest", () => {
276
+ const parsed = MemoryV3ConfigSchema.parse({ lanes: { dense: false } });
277
+ expect(parsed.lanes).toEqual({
278
+ hot: true,
279
+ sparse: true,
280
+ dense: false,
281
+ tree: true,
282
+ edges: true,
283
+ });
284
+ });
285
+
286
+ test("accepts an explicit ks override", () => {
287
+ const parsed = MemoryV3ConfigSchema.parse({ ks: [1, 3, 7] });
288
+ expect(parsed.ks).toEqual([1, 3, 7]);
289
+ });
290
+
291
+ test("rejects a non-boolean enabled", () => {
292
+ expect(() => MemoryV3ConfigSchema.parse({ enabled: "yes" })).toThrow();
293
+ });
294
+
295
+ test("rejects a non-integer passCap", () => {
296
+ expect(() => MemoryV3ConfigSchema.parse({ passCap: 2.5 })).toThrow();
297
+ });
298
+
299
+ test("rejects non-number ks entries", () => {
300
+ expect(() => MemoryV3ConfigSchema.parse({ ks: ["a"] })).toThrow();
301
+ });
302
+
303
+ test("parses the write subtree to safe off defaults when omitted", () => {
304
+ const parsed = MemoryV3ConfigSchema.parse({});
305
+ expect(parsed.write).toEqual({
306
+ enabled: false,
307
+ consolidateIntervalMs: 3600000,
308
+ coactivation: false,
309
+ });
310
+ });
311
+
312
+ test("accepts a partial write override and defaults the rest", () => {
313
+ const parsed = MemoryV3ConfigSchema.parse({ write: { enabled: true } });
314
+ expect(parsed.write).toEqual({
315
+ enabled: true,
316
+ consolidateIntervalMs: 3600000,
317
+ coactivation: false,
318
+ });
319
+ });
320
+
321
+ test("rejects a non-integer write.consolidateIntervalMs", () => {
322
+ expect(() =>
323
+ MemoryV3ConfigSchema.parse({ write: { consolidateIntervalMs: 1.5 } }),
324
+ ).toThrow();
325
+ });
326
+
327
+ test("rejects a non-positive write.consolidateIntervalMs", () => {
328
+ // 0 or negative would make the scheduler's `now - lastRun >= interval`
329
+ // check always true, flooding the queue with consolidation jobs.
330
+ expect(() =>
331
+ MemoryV3ConfigSchema.parse({ write: { consolidateIntervalMs: 0 } }),
332
+ ).toThrow();
333
+ expect(() =>
334
+ MemoryV3ConfigSchema.parse({ write: { consolidateIntervalMs: -1000 } }),
335
+ ).toThrow();
336
+ });
337
+
338
+ test("accepts a positive write.consolidateIntervalMs override", () => {
339
+ const parsed = MemoryV3ConfigSchema.parse({
340
+ write: { consolidateIntervalMs: 1800000 },
341
+ });
342
+ expect(parsed.write.consolidateIntervalMs).toBe(1800000);
343
+ });
344
+
345
+ test("parses the prompts subtree to null overrides when omitted", () => {
346
+ const parsed = MemoryV3ConfigSchema.parse({});
347
+ expect(parsed.prompts).toEqual({
348
+ filter: { override: null, path: null },
349
+ descent: { override: null, path: null },
350
+ gate: { override: null, path: null },
351
+ });
352
+ });
353
+
354
+ test("accepts a partial prompts override and defaults the rest", () => {
355
+ const parsed = MemoryV3ConfigSchema.parse({
356
+ prompts: { filter: { override: "custom filter prompt" } },
357
+ });
358
+ expect(parsed.prompts.filter).toEqual({
359
+ override: "custom filter prompt",
360
+ path: null,
361
+ });
362
+ // Lanes not mentioned keep their null defaults.
363
+ expect(parsed.prompts.descent).toEqual({ override: null, path: null });
364
+ expect(parsed.prompts.gate).toEqual({ override: null, path: null });
365
+ });
366
+
367
+ test("accepts a file path override for a prompt lane", () => {
368
+ const parsed = MemoryV3ConfigSchema.parse({
369
+ prompts: { gate: { path: "~/prompts/v3-gate.md" } },
370
+ });
371
+ expect(parsed.prompts.gate).toEqual({
372
+ override: null,
373
+ path: "~/prompts/v3-gate.md",
374
+ });
375
+ });
376
+
377
+ test("rejects a non-string prompts override", () => {
378
+ expect(() =>
379
+ MemoryV3ConfigSchema.parse({ prompts: { filter: { override: 42 } } }),
380
+ ).toThrow();
381
+ });
382
+
383
+ test("rejects a non-string prompts path", () => {
384
+ expect(() =>
385
+ MemoryV3ConfigSchema.parse({ prompts: { descent: { path: 7 } } }),
386
+ ).toThrow();
387
+ });
388
+ });
389
+
390
+ describe("MemoryConfigSchema integration with v3 block", () => {
391
+ test("includes a v3 block defaulting to disabled when v3 is omitted", () => {
392
+ const parsed = MemoryConfigSchema.parse({});
393
+ expect(parsed.v3).toBeDefined();
394
+ expect(parsed.v3.enabled).toBe(false);
395
+ expect(parsed.v3.shadow).toBe(false);
396
+ expect(parsed.v3.passCap).toBe(3);
397
+ expect(parsed.v3.lanes.dense).toBe(true);
398
+ expect(parsed.v3.ks).toEqual([5, 10, 25, 50]);
399
+ expect(parsed.v3.write).toEqual({
400
+ enabled: false,
401
+ consolidateIntervalMs: 3600000,
402
+ coactivation: false,
403
+ });
404
+ });
405
+
406
+ test("leaves pre-existing configs (no v3 key) otherwise unchanged", () => {
407
+ // A config authored before v3 existed parses fine and its v2 block is
408
+ // untouched; the v3 block is purely additive.
409
+ const parsed = MemoryConfigSchema.parse({ v2: { top_k: 50 } });
410
+ expect(parsed.v2.top_k).toBe(50);
411
+ expect(parsed.v3.enabled).toBe(false);
412
+ });
413
+
414
+ test("propagates v3 overrides through MemoryConfigSchema", () => {
415
+ const parsed = MemoryConfigSchema.parse({
416
+ v3: { enabled: true, passCap: 4 },
417
+ });
418
+ expect(parsed.v3.enabled).toBe(true);
419
+ expect(parsed.v3.passCap).toBe(4);
420
+ // Non-overridden v3 fields keep their defaults.
421
+ expect(parsed.v3.maxDepth).toBe(6);
422
+ });
423
+ });
424
+
213
425
  describe("MemoryConfigSchema integration with v2 block", () => {
214
426
  test("parses an empty memory config and includes a v2 block with defaults", () => {
215
427
  const parsed = MemoryConfigSchema.parse({});
@@ -121,6 +121,27 @@ const CATALOG_RECORD: CatalogRecord = {
121
121
  "Selects which concept pages to inject for the next agent turn by routing over a cached page index.",
122
122
  domain: "memory",
123
123
  },
124
+ memoryV3Filter: {
125
+ id: "memoryV3Filter",
126
+ displayName: "Memory V3 Filter",
127
+ description:
128
+ "Cheaply filters the V3 multi-lane candidate set before descent.",
129
+ domain: "memory",
130
+ },
131
+ memoryV3Descent: {
132
+ id: "memoryV3Descent",
133
+ displayName: "Memory V3 Descent",
134
+ description:
135
+ "Drives the V3 bounded-descent traversal through the memory tree.",
136
+ domain: "memory",
137
+ },
138
+ memoryV3Gate: {
139
+ id: "memoryV3Gate",
140
+ displayName: "Memory V3 Gate",
141
+ description:
142
+ "Final capable gate that decides which V3 candidates are injected for the next turn.",
143
+ domain: "memory",
144
+ },
124
145
  memoryV2Consolidation: {
125
146
  id: "memoryV2Consolidation",
126
147
  displayName: "Memory V2 Consolidation",
@@ -314,13 +335,6 @@ const CATALOG_RECORD: CatalogRecord = {
314
335
  "Generates contextual conversation-starter suggestions for the Home page.",
315
336
  domain: "ui",
316
337
  },
317
- queryComplexityRouter: {
318
- id: "queryComplexityRouter",
319
- displayName: "Query Complexity Router",
320
- description:
321
- "Classifies user message complexity to route to the appropriate inference profile.",
322
- domain: "agentLoop",
323
- },
324
338
  };
325
339
 
326
340
  // Source of truth for call-site display metadata. API responses and usage
@@ -49,6 +49,9 @@ export const LLMCallSiteEnum = z.enum([
49
49
  "memoryV2Migration",
50
50
  "memoryV2Sweep",
51
51
  "memoryRouter",
52
+ "memoryV3Filter",
53
+ "memoryV3Descent",
54
+ "memoryV3Gate",
52
55
  "memoryV2Consolidation",
53
56
  "memoryRetrospective",
54
57
  "recall",
@@ -78,7 +81,6 @@ export const LLMCallSiteEnum = z.enum([
78
81
  "proactiveArtifactBuild",
79
82
  "homeGreeting",
80
83
  "homeSuggestedPrompts",
81
- "queryComplexityRouter",
82
84
  ]);
83
85
  export type LLMCallSite = z.infer<typeof LLMCallSiteEnum>;
84
86
 
@@ -152,10 +154,18 @@ const TemperatureSchema = z.number().min(0).max(2).nullable();
152
154
  // defaulted (`ThinkingSchema`) and fragment (`ThinkingFragmentSchema`) views.
153
155
  const ThinkingEnabledSchema = z.boolean();
154
156
  const ThinkingStreamThinkingSchema = z.boolean();
157
+ // Gemini-style thinking depth knob. Maps to Gemini's `thinkingLevel`. Other
158
+ // providers (Anthropic, OpenRouter) ignore this field — they use `effort`
159
+ // instead to size reasoning. Optional with no default so the underlying
160
+ // provider can pick its own default (Gemini 3.x defaults to "medium").
161
+ export const THINKING_LEVELS = ["minimal", "low", "medium", "high"] as const;
162
+ export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
163
+ const ThinkingLevelSchema = z.enum(THINKING_LEVELS);
155
164
 
156
165
  const ThinkingSchema = z.object({
157
166
  enabled: ThinkingEnabledSchema.default(true),
158
167
  streamThinking: ThinkingStreamThinkingSchema.default(true),
168
+ level: ThinkingLevelSchema.optional(),
159
169
  });
160
170
 
161
171
  // Fragment view: every field optional, no defaults injected. Defining this
@@ -165,6 +175,7 @@ const ThinkingSchema = z.object({
165
175
  const ThinkingFragmentSchema = z.object({
166
176
  enabled: ThinkingEnabledSchema.optional(),
167
177
  streamThinking: ThinkingStreamThinkingSchema.optional(),
178
+ level: ThinkingLevelSchema.optional(),
168
179
  });
169
180
 
170
181
  // Leaf primitives for context-overflow recovery.
@@ -322,6 +322,23 @@ export const MemoryV2ConfigSchema = z
322
322
  .describe(
323
323
  "Pool size for the tier-2 'useful' batch. `null` (default) disables tier 2 — pages skip straight from tier 1 to tier 3. When set, the top-M pages by injection-frequency EMA (excluding tier 1) become their own parallel batch ordered by score desc. Pages with score 0 (never selected since EMA tracking began) are ineligible for tier 2 and stay in tier 3 regardless of `tier2_size`. Score is the time-decayed sum `Σ exp(-λ(now - tᵢ))` with 3-day half-life, computed on read from `memory_v2_injection_events`.",
324
324
  ),
325
+ historical_pairs: z
326
+ .number()
327
+ .int()
328
+ .min(1)
329
+ .default(1)
330
+ .describe(
331
+ "Number of recent (assistant, user) turn pairs to render inside the router prompt's `<last_turn>` block. Each pair is the assistant's reply followed by the user message that came after; the most recent pair's user line is the just-arrived turn that triggered the router. `1` (default) shows only the prior assistant reply plus the current user message — bit-identical to pre-knob behavior. Higher values walk further back through conversation history to give the router more dialogue context at the cost of larger per-turn prompt size. Pairs are emitted in chronological order (oldest first).",
332
+ ),
333
+ historical_pairs_max_chars: z
334
+ .number()
335
+ .int()
336
+ .min(1)
337
+ .nullable()
338
+ .default(null)
339
+ .describe(
340
+ "Optional character cap on the total message content rendered inside `<last_turn>`. `null` (default) means no limit — every message inside the configured `historical_pairs` window is included verbatim. When set, the router walks the assembled pairs newest-first; messages are included until the budget is exhausted, at which point the oldest still-includable message is front-truncated with a leading `…` marker. Older pairs whose content does not fit are dropped entirely. The cap counts message content only — framing characters (`[assistant]: `, `[user]: `, newlines) are not deducted from the budget. Set this when raising `historical_pairs` on workspaces with long messages so the router prompt stays bounded.",
341
+ ),
325
342
  })
326
343
  .default({
327
344
  enabled: true,
@@ -330,6 +347,8 @@ export const MemoryV2ConfigSchema = z
330
347
  batch_size: null,
331
348
  tier1_size: null,
332
349
  tier2_size: null,
350
+ historical_pairs: 1,
351
+ historical_pairs_max_chars: null,
333
352
  })
334
353
  .describe(
335
354
  "LLM router configuration. When enabled, a single router LLM call replaces spreading activation for per-turn page selection.",
@@ -369,3 +388,230 @@ export const MemoryV2ConfigSchema = z
369
388
  });
370
389
 
371
390
  export type MemoryV2Config = z.infer<typeof MemoryV2ConfigSchema>;
391
+
392
+ /**
393
+ * Per-lane system-prompt override for a v3 LLM call site. `override` is an
394
+ * inline prompt string (highest precedence); `path` points at a file whose
395
+ * contents replace the bundled prompt. Both default to `null` (use the bundled
396
+ * prompt). Shared by the filter, descent, and gate entries under
397
+ * `memory.v3.prompts`. The whole object is `.default(...)`-wrapped so an
398
+ * omitted lane parses to `{ override: null, path: null }`.
399
+ */
400
+ const V3PromptOverrideSchema = z
401
+ .object({
402
+ override: z
403
+ .string({ error: "memory.v3.prompts.*.override must be a string" })
404
+ .nullable()
405
+ .default(null)
406
+ .describe(
407
+ "Optional inline system-prompt string that replaces the bundled prompt for this lane. Takes precedence over `path`. An empty or whitespace-only string is ignored (falls back to `path` / bundled).",
408
+ ),
409
+ path: z
410
+ .string({ error: "memory.v3.prompts.*.path must be a string" })
411
+ .nullable()
412
+ .default(null)
413
+ .describe(
414
+ "Optional path to a file whose contents replace the bundled prompt for this lane. Absolute paths are used as-is, a leading `~/` expands to the home directory, otherwise the path resolves under the workspace root. If the file is missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
415
+ ),
416
+ })
417
+ .default({ override: null, path: null });
418
+
419
+ /**
420
+ * Memory v3 (multi-lane, bounded-descent retrieval) configuration.
421
+ *
422
+ * Additive scaffolding only — defaults to `enabled: false` so existing
423
+ * configs are untouched and the v3 retrieval loop stays inert until later
424
+ * PRs wire it up. Every field carries a default and the whole block is
425
+ * `.default(...)`-wrapped so a config that omits `memory.v3` entirely still
426
+ * parses to these documented defaults.
427
+ */
428
+ export const MemoryV3ConfigSchema = z
429
+ .object({
430
+ enabled: z
431
+ .boolean({ error: "memory.v3.enabled must be a boolean" })
432
+ .default(false)
433
+ .describe(
434
+ "Whether the v3 memory subsystem (multi-lane bounded-descent retrieval) is enabled. Off by default until the v3 loop is wired up.",
435
+ ),
436
+ shadow: z
437
+ .boolean({ error: "memory.v3.shadow must be a boolean" })
438
+ .default(false)
439
+ .describe(
440
+ "Live-shadow toggle: when on, the v3 retrieval loop runs alongside the active path for comparison without affecting injected context. Consumed by a later PR.",
441
+ ),
442
+ passCap: z
443
+ .number({ error: "memory.v3.passCap must be a number" })
444
+ .int("memory.v3.passCap must be an integer")
445
+ .default(3)
446
+ .describe(
447
+ "Maximum number of retrieval passes (router → descent rounds) the v3 loop may run per turn.",
448
+ ),
449
+ breadthBudget: z
450
+ .number({ error: "memory.v3.breadthBudget must be a number" })
451
+ .int("memory.v3.breadthBudget must be an integer")
452
+ .default(6)
453
+ .describe(
454
+ "Per-pass breadth budget — the number of frontier candidates the v3 loop may expand at each step.",
455
+ ),
456
+ maxDepth: z
457
+ .number({ error: "memory.v3.maxDepth must be a number" })
458
+ .int("memory.v3.maxDepth must be an integer")
459
+ .default(6)
460
+ .describe(
461
+ "Maximum descent depth the v3 loop traverses through the memory tree before stopping.",
462
+ ),
463
+ denseQuota: z
464
+ .object({
465
+ activeDomain: z
466
+ .number({
467
+ error: "memory.v3.denseQuota.activeDomain must be a number",
468
+ })
469
+ .describe(
470
+ "Dense-lane candidate quota allocated to the conversation's active domain.",
471
+ ),
472
+ offDomain: z
473
+ .number({ error: "memory.v3.denseQuota.offDomain must be a number" })
474
+ .describe(
475
+ "Dense-lane candidate quota allocated to off-domain (exploratory) retrieval.",
476
+ ),
477
+ })
478
+ .default({ activeDomain: 30, offDomain: 8 })
479
+ .describe(
480
+ "Dense-lane candidate quotas split between the active domain and off-domain exploration.",
481
+ ),
482
+ hotLimit: z
483
+ .number({ error: "memory.v3.hotLimit must be a number" })
484
+ .int("memory.v3.hotLimit must be an integer")
485
+ .positive("memory.v3.hotLimit must be positive")
486
+ .default(50)
487
+ .describe(
488
+ "Top-N cap on the hot scout lane, ranked by injection-frequency EMA. Hot hits are sticky (kept past the gate), so this bounds how many always-on pages the lane forces into the selection. Without a cap a mature corpus — where nearly every page has been injected at some point — surfaces the entire corpus.",
489
+ ),
490
+ lanes: z
491
+ .object({
492
+ hot: z
493
+ .boolean()
494
+ .default(true)
495
+ .describe("Whether the hot (recently-touched) retrieval lane is on."),
496
+ sparse: z
497
+ .boolean()
498
+ .default(true)
499
+ .describe("Whether the sparse (BM25-style keyword) lane is on."),
500
+ dense: z
501
+ .boolean()
502
+ .default(true)
503
+ .describe("Whether the dense (embedding-similarity) lane is on."),
504
+ tree: z
505
+ .boolean()
506
+ .default(true)
507
+ .describe("Whether the tree (hierarchical descent) lane is on."),
508
+ edges: z
509
+ .boolean()
510
+ .default(true)
511
+ .describe("Whether the edges (graph-adjacency) lane is on."),
512
+ })
513
+ .default({
514
+ hot: true,
515
+ sparse: true,
516
+ dense: true,
517
+ tree: true,
518
+ edges: true,
519
+ })
520
+ .describe(
521
+ "Per-lane on/off toggles for the v3 multi-lane retrieval fanout. All lanes on by default.",
522
+ ),
523
+ ks: z
524
+ .array(z.number({ error: "memory.v3.ks entries must be numbers" }))
525
+ .default([5, 10, 25, 50])
526
+ .describe(
527
+ "Evaluation top-K cutoffs the v3 loop reports metrics at (e.g. recall@K).",
528
+ ),
529
+ write: z
530
+ .object({
531
+ enabled: z
532
+ .boolean({ error: "memory.v3.write.enabled must be a boolean" })
533
+ .default(false)
534
+ .describe(
535
+ "Whether v3 consolidation owns the shared-buffer drain + tree build. Off by default — v2 consolidation stays the sole buffer-drainer. Does NOT introduce a separate buffer.",
536
+ ),
537
+ consolidateIntervalMs: z
538
+ .number({
539
+ error: "memory.v3.write.consolidateIntervalMs must be a number",
540
+ })
541
+ .int("memory.v3.write.consolidateIntervalMs must be an integer")
542
+ .positive("memory.v3.write.consolidateIntervalMs must be positive")
543
+ .default(3600000)
544
+ .describe(
545
+ "Interval, in milliseconds, between scheduled v3 consolidation runs once the v3 write path owns the drain. Default 1 hour.",
546
+ ),
547
+ coactivation: z
548
+ .boolean({ error: "memory.v3.write.coactivation must be a boolean" })
549
+ .default(false)
550
+ .describe(
551
+ "Whether v3 consolidation learns co-activation edges during the tree build. Off by default; consumed by a later PR.",
552
+ ),
553
+ })
554
+ .default({
555
+ enabled: false,
556
+ consolidateIntervalMs: 3600000,
557
+ coactivation: false,
558
+ })
559
+ .describe(
560
+ "Memory v3 write-path configuration. All default-off scaffolding — controls whether v3 consolidation owns the shared-buffer drain + tree build. Consumed by later PRs.",
561
+ ),
562
+ prompts: z
563
+ .object({
564
+ filter: V3PromptOverrideSchema.describe(
565
+ "Override for the dense-hit filter lane's system prompt.",
566
+ ),
567
+ descent: V3PromptOverrideSchema.describe(
568
+ "Override for the tree-walk descent driver's system prompt.",
569
+ ),
570
+ gate: V3PromptOverrideSchema.describe(
571
+ "Override for the selection gate's system prompt.",
572
+ ),
573
+ })
574
+ .default({
575
+ filter: { override: null, path: null },
576
+ descent: { override: null, path: null },
577
+ gate: { override: null, path: null },
578
+ })
579
+ .describe(
580
+ "Per-lane system-prompt overrides for the three v3 LLM call sites (filter, descent, gate). Each entry takes an inline `override` string (highest precedence) and/or a file `path` whose contents replace the bundled prompt; absolute paths are used as-is, a leading `~/` expands to the home directory, otherwise the path resolves under the workspace root. An empty/whitespace inline override or a missing/unreadable/empty file falls back to the bundled prompt. Lets the prompts be iterated at runtime without a rebuild/restart — mirroring `memory.v2.router.router_prompt_path`.",
581
+ ),
582
+ gateCandidateSummaries: z
583
+ .boolean({
584
+ error: "memory.v3.gateCandidateSummaries must be a boolean",
585
+ })
586
+ .default(false)
587
+ .describe(
588
+ "When true, the selection gate sees each candidate as `slug — summary` instead of the bare slug, so it can judge relevance on page content. Off by default: with the bundled (precision-leaning) gate prompt this makes the gate more selective. It pays off paired with a recall-leaning gate prompt override, where the summaries let the gate recognize non-obvious associative/emotional matches it would otherwise pass over. Adds the candidate summaries to the gate prompt (larger input).",
589
+ ),
590
+ })
591
+ .default({
592
+ enabled: false,
593
+ shadow: false,
594
+ passCap: 3,
595
+ breadthBudget: 6,
596
+ maxDepth: 6,
597
+ denseQuota: { activeDomain: 30, offDomain: 8 },
598
+ hotLimit: 50,
599
+ lanes: { hot: true, sparse: true, dense: true, tree: true, edges: true },
600
+ ks: [5, 10, 25, 50],
601
+ write: {
602
+ enabled: false,
603
+ consolidateIntervalMs: 3600000,
604
+ coactivation: false,
605
+ },
606
+ prompts: {
607
+ filter: { override: null, path: null },
608
+ descent: { override: null, path: null },
609
+ gate: { override: null, path: null },
610
+ },
611
+ gateCandidateSummaries: false,
612
+ })
613
+ .describe(
614
+ "Memory v3 — multi-lane bounded-descent retrieval. Additive scaffolding, disabled by default.",
615
+ );
616
+
617
+ export type MemoryV3Config = z.infer<typeof MemoryV3ConfigSchema>;