@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
@@ -1,7 +1,6 @@
1
1
  import type * as genai from "@google/genai";
2
- import { ApiError, GoogleGenAI } from "@google/genai";
2
+ import { ApiError, GoogleGenAI, ThinkingLevel } from "@google/genai";
3
3
 
4
- import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../../prompts/cache-boundary.js";
5
4
  import { isAbortReason } from "../../util/abort-reasons.js";
6
5
  import { ProviderError } from "../../util/errors.js";
7
6
  import { getLogger } from "../../util/logger.js";
@@ -34,6 +33,47 @@ function isGemini3Model(model: string): boolean {
34
33
  return model.startsWith("gemini-3") || model.startsWith("models/gemini-3");
35
34
  }
36
35
 
36
+ const THINKING_LEVEL_BY_NAME: Record<string, ThinkingLevel> = {
37
+ minimal: ThinkingLevel.MINIMAL,
38
+ low: ThinkingLevel.LOW,
39
+ medium: ThinkingLevel.MEDIUM,
40
+ high: ThinkingLevel.HIGH,
41
+ };
42
+
43
+ /**
44
+ * Translate the resolved wire-shape `thinking` config into Gemini's
45
+ * `thinkingConfig`. Returns `undefined` when no thinking config was supplied,
46
+ * which lets Google's per-model default apply (e.g. `gemini-3.5-flash`
47
+ * defaults to dynamic medium-level thinking).
48
+ *
49
+ * `enabled: false` maps to `thinkingLevel: MINIMAL` because Gemini 3.x cannot
50
+ * fully disable thinking — `"minimal"` is the floor. `includeThoughts` is
51
+ * gated on `streamThinking` so callers that opted out of streaming thoughts
52
+ * don't pay for thought tokens in the response.
53
+ */
54
+ function buildThinkingConfig(
55
+ thinking: Record<string, unknown> | undefined,
56
+ ): genai.ThinkingConfig | undefined {
57
+ if (!thinking) return undefined;
58
+ if (thinking.type === "disabled") {
59
+ return {
60
+ thinkingLevel: ThinkingLevel.MINIMAL,
61
+ includeThoughts: false,
62
+ };
63
+ }
64
+ if (thinking.type !== "adaptive") return undefined;
65
+
66
+ const result: genai.ThinkingConfig = {};
67
+ if (typeof thinking.level === "string") {
68
+ const mapped = THINKING_LEVEL_BY_NAME[thinking.level];
69
+ if (mapped) result.thinkingLevel = mapped;
70
+ }
71
+ if (typeof thinking.streamThinking === "boolean") {
72
+ result.includeThoughts = thinking.streamThinking;
73
+ }
74
+ return Object.keys(result).length > 0 ? result : undefined;
75
+ }
76
+
37
77
  function stripGeminiHttpOptions(
38
78
  config: genai.GenerateContentConfig,
39
79
  ): genai.GenerateContentConfig {
@@ -175,6 +215,9 @@ export class GeminiProvider implements Provider {
175
215
  const usageAttributionHeaders = configObj?.usageAttributionHeaders as
176
216
  | Record<string, string>
177
217
  | undefined;
218
+ const thinkingConfig = buildThinkingConfig(
219
+ configObj?.thinking as Record<string, unknown> | undefined,
220
+ );
178
221
  const activeModel = modelOverride ?? this.model;
179
222
 
180
223
  try {
@@ -183,14 +226,14 @@ export class GeminiProvider implements Provider {
183
226
  const geminiConfig: genai.GenerateContentConfig = {};
184
227
 
185
228
  if (systemPrompt) {
186
- geminiConfig.systemInstruction = systemPrompt.replaceAll(
187
- SYSTEM_PROMPT_CACHE_BOUNDARY,
188
- "\n",
189
- );
229
+ geminiConfig.systemInstruction = systemPrompt;
190
230
  }
191
231
  if (maxTokens) {
192
232
  geminiConfig.maxOutputTokens = maxTokens;
193
233
  }
234
+ if (thinkingConfig) {
235
+ geminiConfig.thinkingConfig = thinkingConfig;
236
+ }
194
237
  if (tools && tools.length > 0) {
195
238
  geminiConfig.tools = [
196
239
  {
@@ -21,6 +21,7 @@
21
21
  import { AnthropicProvider } from "../anthropic/client.js";
22
22
  import { FireworksProvider } from "../fireworks/client.js";
23
23
  import { GeminiProvider } from "../gemini/client.js";
24
+ import { MinimaxProvider } from "../minimax/client.js";
24
25
  import { PROVIDER_CATALOG } from "../model-catalog.js";
25
26
  import { OllamaProvider } from "../ollama/client.js";
26
27
  import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
@@ -111,6 +112,8 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
111
112
  streamTimeoutMs,
112
113
  ...(baseURL ? { baseURL } : {}),
113
114
  }),
115
+ minimax: ({ apiKey, model, streamTimeoutMs }) =>
116
+ new MinimaxProvider(apiKey, model, { streamTimeoutMs }),
114
117
  };
115
118
 
116
119
  /**
@@ -0,0 +1,106 @@
1
+ import OpenAI from "openai";
2
+
3
+ import { getLogger } from "../../util/logger.js";
4
+ import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
5
+
6
+ const log = getLogger("minimax-client");
7
+
8
+ /** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
9
+ const VALIDATION_TIMEOUT_MS = 10_000;
10
+
11
+ export interface MinimaxProviderOptions {
12
+ apiKey?: string;
13
+ baseURL?: string;
14
+ streamTimeoutMs?: number;
15
+ }
16
+
17
+ const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
18
+ const FALLBACK_MINIMAX_BASE_URL = "https://api.minimaxi.com/v1";
19
+
20
+ /**
21
+ * Validate a MiniMax API key by testing against the default URL first,
22
+ * then the fallback URL if the default fails. Both URLs must fail with
23
+ * definitive errors (401/403) for the key to be rejected. Transient errors
24
+ * (429, 5xx, network) allow the key to be stored.
25
+ */
26
+ export async function validateMinimaxApiKey(
27
+ apiKey: string,
28
+ ): Promise<{ valid: true } | { valid: false; reason: string }> {
29
+ // Try default URL first
30
+ const defaultResult = await tryValidate(apiKey, DEFAULT_MINIMAX_BASE_URL);
31
+ if (defaultResult.valid && !defaultResult.transient) {
32
+ return { valid: true };
33
+ }
34
+
35
+ // Default failed or was transient — try fallback URL
36
+ const fallbackResult = await tryValidate(apiKey, FALLBACK_MINIMAX_BASE_URL);
37
+ if (fallbackResult.valid) {
38
+ return { valid: true };
39
+ }
40
+
41
+ // Both URLs failed definitively — reject the key
42
+ return { valid: false, reason: fallbackResult.reason };
43
+ }
44
+
45
+ async function tryValidate(
46
+ apiKey: string,
47
+ baseURL: string,
48
+ ): Promise<
49
+ | { valid: true; transient: false }
50
+ | { valid: true; transient: true }
51
+ | { valid: false; reason: string }
52
+ > {
53
+ try {
54
+ const client = new OpenAI({
55
+ apiKey,
56
+ baseURL,
57
+ timeout: VALIDATION_TIMEOUT_MS,
58
+ maxRetries: 0,
59
+ });
60
+ await client.models.list();
61
+ return { valid: true, transient: false };
62
+ } catch (error) {
63
+ if (error instanceof OpenAI.APIError) {
64
+ if (error.status === 401) {
65
+ return { valid: false, reason: "API key is invalid or expired." };
66
+ }
67
+ if (error.status === 403) {
68
+ return {
69
+ valid: false,
70
+ reason: `MiniMax API error (${error.status}): ${error.message}`,
71
+ };
72
+ }
73
+ // Transient errors (429, 5xx, etc.) — try the other URL
74
+ log.warn(
75
+ { status: error.status, baseURL },
76
+ "MiniMax API returned a transient error during key validation — trying fallback",
77
+ );
78
+ return { valid: true, transient: true };
79
+ }
80
+ // Network errors — try the other URL
81
+ log.warn(
82
+ {
83
+ error: error instanceof Error ? error.message : String(error),
84
+ baseURL,
85
+ },
86
+ "Network error during MiniMax key validation — trying fallback",
87
+ );
88
+ return { valid: true, transient: true };
89
+ }
90
+ }
91
+
92
+ export class MinimaxProvider extends OpenAIChatCompletionsProvider {
93
+ constructor(
94
+ apiKey: string,
95
+ model: string,
96
+ options: MinimaxProviderOptions = {},
97
+ ) {
98
+ const baseURL = options.baseURL?.trim() || DEFAULT_MINIMAX_BASE_URL;
99
+ super(apiKey, model, {
100
+ baseURL,
101
+ providerName: "minimax",
102
+ providerLabel: "MiniMax",
103
+ streamTimeoutMs: options.streamTimeoutMs,
104
+ });
105
+ }
106
+ }
@@ -493,6 +493,21 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
493
493
  cacheReadPer1mTokens: 0.025,
494
494
  },
495
495
  },
496
+ {
497
+ id: "gemini-3.1-flash-lite",
498
+ displayName: "Gemini 3.1 Flash-Lite",
499
+ contextWindowTokens: 1048576,
500
+ maxOutputTokens: 65536,
501
+ supportsThinking: true,
502
+ supportsCaching: true,
503
+ supportsVision: true,
504
+ supportsToolUse: true,
505
+ pricing: {
506
+ inputPer1mTokens: 0.25,
507
+ outputPer1mTokens: 1.5,
508
+ cacheReadPer1mTokens: 0.025,
509
+ },
510
+ },
496
511
  {
497
512
  id: "gemini-2.5-flash",
498
513
  displayName: "Gemini 2.5 Flash",
@@ -1122,6 +1137,34 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
1122
1137
  models: [],
1123
1138
  defaultModel: "",
1124
1139
  },
1140
+ {
1141
+ id: "minimax",
1142
+ displayName: "MiniMax",
1143
+ subtitle: "MiniMax AI models. Requires a MiniMax API key.",
1144
+ setupMode: "api-key",
1145
+ setupHint: "Enter your MiniMax API key to enable MiniMax models.",
1146
+ envVar: "MINIMAX_API_KEY",
1147
+ credentialsGuide: {
1148
+ description: "Sign in to the MiniMax dashboard and create an API key.",
1149
+ url: "https://platform.minimax.io/",
1150
+ linkLabel: "Open MiniMax Dashboard",
1151
+ },
1152
+ models: [
1153
+ {
1154
+ id: "MiniMax-M2.7",
1155
+ displayName: "MiniMax M2.7",
1156
+ contextWindowTokens: 200000,
1157
+ maxOutputTokens: 16384,
1158
+ supportsThinking: true,
1159
+ supportsCaching: true,
1160
+ supportsVision: false,
1161
+ supportsToolUse: true,
1162
+ },
1163
+ ],
1164
+ defaultModel: "MiniMax-M2.7",
1165
+ apiKeyUrl: "https://platform.minimax.io/",
1166
+ apiKeyPlaceholder: "sk-cp-...",
1167
+ },
1125
1168
  ];
1126
1169
 
1127
1170
  export const PROVIDER_CATALOG: ProviderCatalogEntry[] =
@@ -24,7 +24,7 @@ const PROVIDER_MODEL_INTENTS: Record<string, Record<ModelIntent, string>> = {
24
24
  },
25
25
  gemini: {
26
26
  balanced: "gemini-3-flash-preview",
27
- "latency-optimized": "gemini-3.1-flash-lite-preview",
27
+ "latency-optimized": "gemini-3.1-flash-lite",
28
28
  "quality-optimized": "gemini-3.1-pro-preview",
29
29
  "vision-optimized": "gemini-3-flash-preview",
30
30
  },
@@ -1,6 +1,5 @@
1
1
  import OpenAI from "openai";
2
2
 
3
- import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../../prompts/cache-boundary.js";
4
3
  import { isAbortReason } from "../../util/abort-reasons.js";
5
4
  import { ProviderError } from "../../util/errors.js";
6
5
  import { extractRetryAfterMs } from "../../util/retry.js";
@@ -155,12 +154,16 @@ export class OpenAIChatCompletionsProvider implements Provider {
155
154
  ) {
156
155
  this.name = options.providerName ?? "openai";
157
156
  this.providerLabel = options.providerLabel ?? "OpenAI";
157
+ this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
158
+ // Keep the SDK deadline behind our provider stream timeout so
159
+ // createStreamTimeout owns the user-facing timeout error.
160
+ const sdkTimeoutMs = this.streamTimeoutMs + 60_000;
158
161
  this.client = new OpenAI({
159
162
  apiKey,
160
163
  baseURL: options.baseURL,
164
+ timeout: sdkTimeoutMs,
161
165
  });
162
166
  this.model = model;
163
- this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
164
167
  this.extraCreateParams = options.extraCreateParams ?? {};
165
168
  this.maxReasoningEffort = options.maxReasoningEffort ?? "xhigh";
166
169
  this.requestHeaders = options.requestHeaders ?? {};
@@ -593,7 +596,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
593
596
  if (systemPrompt) {
594
597
  result.push({
595
598
  role: "system",
596
- content: systemPrompt.replaceAll(SYSTEM_PROMPT_CACHE_BOUNDARY, "\n"),
599
+ content: systemPrompt,
597
600
  });
598
601
  }
599
602
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Model IDs accepted by the ChatGPT Codex subscription endpoint
3
+ * (`https://chatgpt.com/backend-api/codex`).
4
+ *
5
+ * `oauth_subscription` OpenAI connections hard-route every request to that
6
+ * endpoint, which rejects any model outside this set with HTTP 400. The set
7
+ * gates whether such a connection may serve a given model during auto-
8
+ * resolution of an "Any active OpenAI connection" profile.
9
+ */
10
+ export const CODEX_SUBSCRIPTION_MODEL_IDS: ReadonlySet<string> = new Set([
11
+ "gpt-5.4",
12
+ "gpt-5.3-codex",
13
+ ]);
14
+
15
+ /** True when `model` is accepted by the Codex subscription endpoint. */
16
+ export function isCodexSubscriptionModel(model: string): boolean {
17
+ return CODEX_SUBSCRIPTION_MODEL_IDS.has(model);
18
+ }
@@ -1,8 +1,8 @@
1
1
  import OpenAI from "openai";
2
2
 
3
- import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../../prompts/cache-boundary.js";
4
3
  import { isAbortReason } from "../../util/abort-reasons.js";
5
4
  import { ProviderError } from "../../util/errors.js";
5
+ import { getLogger } from "../../util/logger.js";
6
6
  import { extractRetryAfterMs } from "../../util/retry.js";
7
7
  import { escapeXmlAttr } from "../../util/xml.js";
8
8
  import { createStreamTimeout } from "../stream-timeout.js";
@@ -17,6 +17,8 @@ import type {
17
17
  import { ContextOverflowError } from "../types.js";
18
18
  import { detectOpenAICompatibleContextOverflow } from "./chat-completions-provider.js";
19
19
 
20
+ const log = getLogger("openai-responses");
21
+
20
22
  export interface OpenAIResponsesProviderOptions {
21
23
  baseURL?: string;
22
24
  providerName?: string;
@@ -24,7 +26,7 @@ export interface OpenAIResponsesProviderOptions {
24
26
  streamTimeoutMs?: number;
25
27
  useNativeWebSearch?: boolean;
26
28
  /** When true, target the Codex subscription endpoint and strip fields it
27
- * rejects (`max_output_tokens`, `metadata`). */
29
+ * rejects (`max_output_tokens`, `reasoning`, `text`, `tools`). */
28
30
  codexSubscription?: boolean;
29
31
  }
30
32
 
@@ -107,6 +109,7 @@ export class OpenAIResponsesProvider implements Provider {
107
109
  private streamTimeoutMs: number;
108
110
  private useNativeWebSearch: boolean;
109
111
  private codexSubscription: boolean;
112
+ private lastCodexErrorBody: string | undefined;
110
113
 
111
114
  constructor(
112
115
  apiKey: string,
@@ -116,14 +119,39 @@ export class OpenAIResponsesProvider implements Provider {
116
119
  this.name = options.providerName ?? "openai";
117
120
  this.providerLabel = options.providerLabel ?? "OpenAI";
118
121
  this.codexSubscription = options.codexSubscription ?? false;
122
+ this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
123
+ // Keep the SDK deadline behind our provider stream timeout so
124
+ // createStreamTimeout owns the user-facing timeout error.
125
+ const sdkTimeoutMs = this.streamTimeoutMs + 60_000;
119
126
  this.client = new OpenAI({
120
127
  apiKey,
121
128
  baseURL: this.codexSubscription
122
129
  ? "https://chatgpt.com/backend-api/codex"
123
130
  : options.baseURL,
131
+ timeout: sdkTimeoutMs,
132
+ ...(this.codexSubscription
133
+ ? {
134
+ fetch: async (url: RequestInfo | URL, init?: RequestInit) => {
135
+ const res = await globalThis.fetch(url, init);
136
+ if (!res.ok) {
137
+ const body = await res.text();
138
+ this.lastCodexErrorBody = body;
139
+ log.warn(
140
+ { status: res.status, body, url: String(url) },
141
+ "Codex endpoint raw error response",
142
+ );
143
+ return new Response(body, {
144
+ status: res.status,
145
+ statusText: res.statusText,
146
+ headers: res.headers,
147
+ });
148
+ }
149
+ return res;
150
+ },
151
+ }
152
+ : {}),
124
153
  });
125
154
  this.model = model;
126
- this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
127
155
  this.useNativeWebSearch = options.useNativeWebSearch ?? false;
128
156
  }
129
157
 
@@ -149,35 +177,35 @@ export class OpenAIResponsesProvider implements Provider {
149
177
  const params: Record<string, unknown> = {
150
178
  model: modelOverride ?? this.model,
151
179
  input,
180
+ ...(this.codexSubscription ? { store: false } : {}),
152
181
  };
153
182
 
154
183
  if (systemPrompt) {
155
- params.instructions = systemPrompt.replaceAll(
156
- SYSTEM_PROMPT_CACHE_BOUNDARY,
157
- "\n",
158
- );
184
+ params.instructions = systemPrompt;
159
185
  }
160
186
 
161
187
  if (maxTokens && !this.codexSubscription) {
162
188
  params.max_output_tokens = maxTokens;
163
189
  }
164
190
 
165
- const reasoningEffort = effort
166
- ? EFFORT_TO_REASONING_EFFORT[effort]
167
- : undefined;
168
- if (reasoningEffort) {
169
- params.reasoning = { effort: reasoningEffort };
170
- }
191
+ if (!this.codexSubscription) {
192
+ const reasoningEffort = effort
193
+ ? EFFORT_TO_REASONING_EFFORT[effort]
194
+ : undefined;
195
+ if (reasoningEffort) {
196
+ params.reasoning = { effort: reasoningEffort };
197
+ }
171
198
 
172
- if (
173
- verbosity &&
174
- VALID_VERBOSITIES.has(verbosity) &&
175
- modelSupportsVerbosity(modelOverride ?? this.model)
176
- ) {
177
- params.text = { verbosity };
199
+ if (
200
+ verbosity &&
201
+ VALID_VERBOSITIES.has(verbosity) &&
202
+ modelSupportsVerbosity(modelOverride ?? this.model)
203
+ ) {
204
+ params.text = { verbosity };
205
+ }
178
206
  }
179
207
 
180
- if (tools && tools.length > 0) {
208
+ if (tools && tools.length > 0 && !this.codexSubscription) {
181
209
  if (
182
210
  this.useNativeWebSearch &&
183
211
  tools.some((t) => t.name === "web_search")
@@ -410,6 +438,21 @@ export class OpenAIResponsesProvider implements Provider {
410
438
  ? signal.reason
411
439
  : undefined;
412
440
  if (error instanceof OpenAI.APIError) {
441
+ // Temporary diagnostic: log the raw error shape for Codex 400 debugging
442
+ if (this.codexSubscription) {
443
+ log.warn(
444
+ {
445
+ status: error.status,
446
+ message: error.message,
447
+ code: error.code,
448
+ type: error.type,
449
+ param: error.param,
450
+ errorBody: error.error,
451
+ headers: Object.fromEntries(error.headers?.entries?.() ?? []),
452
+ },
453
+ "Codex subscription API error — raw details",
454
+ );
455
+ }
413
456
  const overflow = detectOpenAICompatibleContextOverflow(error);
414
457
  if (overflow) {
415
458
  throw new ContextOverflowError(
@@ -431,8 +474,22 @@ export class OpenAIResponsesProvider implements Provider {
431
474
  if (retryAfterMs !== undefined)
432
475
  errorOptions.retryAfterMs = retryAfterMs;
433
476
  if (abortReason) errorOptions.abortReason = abortReason;
477
+ let errorDetail = error.message;
478
+ if (this.lastCodexErrorBody) {
479
+ try {
480
+ const parsed = JSON.parse(this.lastCodexErrorBody);
481
+ if (parsed.detail) errorDetail = parsed.detail;
482
+ } catch {
483
+ errorDetail = this.lastCodexErrorBody.slice(0, 200);
484
+ }
485
+ this.lastCodexErrorBody = undefined;
486
+ }
487
+ const extras = [error.code, error.type, error.param]
488
+ .filter(Boolean)
489
+ .join(", ");
490
+ const extraSuffix = extras ? ` [${extras}]` : "";
434
491
  throw new ProviderError(
435
- `${this.providerLabel} API error (${error.status}): ${error.message}`,
492
+ `${this.providerLabel} API error (${error.status}): ${errorDetail}${extraSuffix}`,
436
493
  this.name,
437
494
  error.status,
438
495
  Object.keys(errorOptions).length > 0 ? errorOptions : undefined,
@@ -9,6 +9,7 @@ import { getConfig } from "../config/loader.js";
9
9
  import type { LLMCallSite } from "../config/schemas/llm.js";
10
10
  import { getDb } from "../memory/db-connection.js";
11
11
  import { getLogger } from "../util/logger.js";
12
+ import { isConnectionCompatibleWithModel } from "./connection-model-compat.js";
12
13
  import { tryResolveProviderForConnectionName } from "./connection-resolution.js";
13
14
  import { listConnections } from "./inference/connections.js";
14
15
  import { initializeProviders, listProviders } from "./registry.js";
@@ -126,7 +127,11 @@ export async function resolveConfiguredProvider(
126
127
  const candidates = listConnections(getDb(), {
127
128
  provider: inferenceProvider,
128
129
  });
129
- const active = candidates.find((c) => c.status === "active");
130
+ const active = candidates.find(
131
+ (c) =>
132
+ c.status === "active" &&
133
+ isConnectionCompatibleWithModel(c, resolved.model),
134
+ );
130
135
  if (active) {
131
136
  connectionName = active.name;
132
137
  }
@@ -147,6 +152,7 @@ export async function resolveConfiguredProvider(
147
152
  connectionName,
148
153
  config,
149
154
  inferenceProvider,
155
+ resolved.model,
150
156
  );
151
157
  if (!connectionProvider) {
152
158
  // Soft credential failure — the connection resolved to no usable
@@ -47,9 +47,18 @@ const EFFORT_SUPPORTED_PROVIDERS = new Set([
47
47
  /**
48
48
  * Providers that consume the `thinking` config. Anthropic uses it directly on
49
49
  * the wire; OpenRouter either forwards it to its Anthropic-compatible path or
50
- * translates it into the unified `reasoning` parameter on OpenAI-compat calls.
50
+ * translates it into the unified `reasoning` parameter on OpenAI-compat calls;
51
+ * Gemini reads `thinking.level` to populate `thinkingConfig.thinkingLevel`.
51
52
  */
52
- const THINKING_AWARE_PROVIDERS = new Set(["anthropic", "openrouter"]);
53
+ const THINKING_AWARE_PROVIDERS = new Set(["anthropic", "openrouter", "gemini"]);
54
+
55
+ /**
56
+ * Providers that consume Gemini-only thinking extras (`level`,
57
+ * `streamThinking`). For other thinking-aware providers, we scrub these from
58
+ * the normalized wire payload because Anthropic's SDK rejects unknown keys
59
+ * inside the `thinking` object with "Extra inputs are not permitted".
60
+ */
61
+ const THINKING_EXTRA_FIELDS_AWARE_PROVIDERS = new Set(["gemini"]);
53
62
 
54
63
  /**
55
64
  * Providers that consume the `verbosity` config. Currently OpenAI (mapped to
@@ -289,7 +298,8 @@ function normalizeSendMessageOptions(
289
298
  }
290
299
 
291
300
  // thinking is Anthropic-specific on the wire; OpenRouter reads it as a
292
- // signal for its unified reasoning parameter. Strip it for other providers.
301
+ // signal for its unified reasoning parameter; Gemini reads `level` from it.
302
+ // Strip it for other providers.
293
303
  if (
294
304
  !THINKING_AWARE_PROVIDERS.has(providerName) &&
295
305
  nextConfig.thinking !== undefined
@@ -297,6 +307,27 @@ function normalizeSendMessageOptions(
297
307
  delete nextConfig.thinking;
298
308
  }
299
309
 
310
+ // Strip Gemini-only extras (`level`, `streamThinking`) from the wire
311
+ // `thinking` object for providers that don't read them. Anthropic in
312
+ // particular rejects unknown keys inside `thinking` with "Extra inputs are
313
+ // not permitted"; the OpenRouter Anthropic-compat path hits the same SDK.
314
+ if (
315
+ nextConfig.thinking !== undefined &&
316
+ !THINKING_EXTRA_FIELDS_AWARE_PROVIDERS.has(providerName) &&
317
+ typeof nextConfig.thinking === "object" &&
318
+ nextConfig.thinking !== null
319
+ ) {
320
+ const wire = nextConfig.thinking as Record<string, unknown>;
321
+ if (wire.level !== undefined || wire.streamThinking !== undefined) {
322
+ const scrubbed: Record<string, unknown> = {};
323
+ for (const [key, value] of Object.entries(wire)) {
324
+ if (key === "level" || key === "streamThinking") continue;
325
+ scrubbed[key] = value;
326
+ }
327
+ nextConfig.thinking = scrubbed;
328
+ }
329
+ }
330
+
300
331
  // Anthropic (and OpenRouter fronting Anthropic) rejects requests that
301
332
  // combine extended thinking with forced tool use (`tool_choice.type` of
302
333
  // `"tool"` or `"any"`). Strip thinking when both are present so the
@@ -1,20 +1,45 @@
1
+ import { THINKING_LEVELS, type ThinkingLevel } from "../config/schemas/llm.js";
2
+
1
3
  type ThinkingConfigRecord = Record<string, unknown>;
2
4
 
5
+ const THINKING_LEVEL_SET: ReadonlySet<string> = new Set(THINKING_LEVELS);
6
+
3
7
  function isRecord(value: unknown): value is ThinkingConfigRecord {
4
8
  return typeof value === "object" && value !== null && !Array.isArray(value);
5
9
  }
6
10
 
11
+ function pickGeminiExtras(thinking: ThinkingConfigRecord): {
12
+ level?: ThinkingLevel;
13
+ streamThinking?: boolean;
14
+ } {
15
+ const extras: { level?: ThinkingLevel; streamThinking?: boolean } = {};
16
+ if (
17
+ typeof thinking.level === "string" &&
18
+ THINKING_LEVEL_SET.has(thinking.level)
19
+ ) {
20
+ extras.level = thinking.level as ThinkingLevel;
21
+ }
22
+ if (typeof thinking.streamThinking === "boolean") {
23
+ extras.streamThinking = thinking.streamThinking;
24
+ }
25
+ return extras;
26
+ }
27
+
7
28
  export function normalizeThinkingConfigForWire(
8
29
  thinking: unknown,
9
30
  ): ThinkingConfigRecord | undefined {
10
31
  if (!isRecord(thinking)) return undefined;
11
32
 
33
+ // Already in wire shape — preserve as-is so re-normalization is idempotent
34
+ // and Gemini-only fields stay attached for the Gemini provider to read.
12
35
  if (typeof thinking.type === "string") {
13
36
  return thinking;
14
37
  }
15
38
 
39
+ const extras = pickGeminiExtras(thinking);
40
+
16
41
  if (thinking.enabled === true) {
17
- return { type: "adaptive" };
42
+ return { type: "adaptive", ...extras };
18
43
  }
19
44
 
20
45
  if (thinking.enabled === false) {
@@ -2,6 +2,7 @@ import { recordUsageEvent } from "../memory/llm-usage-store.js";
2
2
  import { resolveUsageAttribution } from "../usage/attribution.js";
3
3
  import {
4
4
  buildPricingUsageFromResponse,
5
+ extractRawUsage,
5
6
  resolveStructuredPricing,
6
7
  } from "../usage/pricing.js";
7
8
  import { getLogger } from "../util/logger.js";
@@ -76,6 +77,7 @@ export class UsageTrackingProvider implements Provider {
76
77
  outputTokens: pricingUsage.outputTokens,
77
78
  cacheCreationInputTokens: pricingUsage.cacheCreationInputTokens,
78
79
  cacheReadInputTokens: pricingUsage.cacheReadInputTokens,
80
+ rawUsage: extractRawUsage(response.rawResponse),
79
81
  conversationId: null,
80
82
  runId: null,
81
83
  requestId: null,
@@ -161,10 +161,10 @@ All `/v1/*` endpoints share a per-client-IP sliding-window rate limiter (`middle
161
161
 
162
162
  When the limit is exceeded, the limiter returns 429 and logs a structured warning (module: `rate-limiter`) with the denied endpoint and a breakdown of which endpoints consumed the budget in the current window. This makes it easy to identify whether the cause is rapid conversation switching, polling, or unexpected request volume.
163
163
 
164
- Logs are written to `~/.vellum/workspace/data/logs/vellum.log` by default. If `logFile.dir` is configured, logs rotate daily as `assistant-YYYY-MM-DD.log` in that directory. To watch rate limit events in real time:
164
+ Logs rotate daily into `$VELLUM_WORKSPACE_DIR/data/logs/assistant-YYYY-MM-DD.log` (or into the directory configured via `logFile.dir`). To watch rate limit events in real time:
165
165
 
166
166
  ```bash
167
- tail -f ~/.vellum/workspace/data/logs/vellum.log | grep rate-limit
167
+ tail -f "$VELLUM_WORKSPACE_DIR/data/logs/assistant-$(date -u +%Y-%m-%d).log" | grep rate-limit
168
168
  ```
169
169
 
170
170
  The provider-level rate limiter (`providers/ratelimit.ts`) also logs warnings (module: `rate-limit`) when request rate or token budget limits are enforced.
@@ -640,6 +640,7 @@ export async function wakeAgentForOpportunity(
640
640
  JSON.stringify(record.rawResponse),
641
641
  undefined,
642
642
  record.provider,
643
+ "mainAgent",
643
644
  );
644
645
  } catch (err) {
645
646
  log.warn(