@vellumai/assistant 0.8.3 → 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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -0,0 +1,254 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ mock,
7
+ test,
8
+ } from "bun:test";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Mocks
12
+ // ---------------------------------------------------------------------------
13
+
14
+ mock.module("../../../util/logger.js", () => ({
15
+ getLogger: () =>
16
+ new Proxy({} as Record<string, unknown>, {
17
+ get: () => () => {},
18
+ }),
19
+ }));
20
+
21
+ const mockGetSecureKey = mock<(key: string) => Promise<string | undefined>>(
22
+ async () => undefined,
23
+ );
24
+ const mockSetSecureKey = mock<(key: string, value: string) => Promise<boolean>>(
25
+ async () => true,
26
+ );
27
+
28
+ mock.module("../../../security/secure-keys.js", () => ({
29
+ getSecureKeyAsync: mockGetSecureKey,
30
+ setSecureKeyAsync: mockSetSecureKey,
31
+ }));
32
+
33
+ const mockRefreshOAuth2Token = mock<
34
+ (...args: unknown[]) => Promise<{
35
+ accessToken: string;
36
+ refreshToken?: string;
37
+ expiresIn?: number;
38
+ }>
39
+ >(async () => ({
40
+ accessToken: "new-access-token",
41
+ refreshToken: "new-refresh-token",
42
+ expiresIn: 3600,
43
+ }));
44
+
45
+ mock.module("../../../security/oauth2.js", () => ({
46
+ refreshOAuth2Token: mockRefreshOAuth2Token,
47
+ }));
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Import under test (after mocks)
51
+ // ---------------------------------------------------------------------------
52
+
53
+ import {
54
+ _resetRefreshMutex,
55
+ getValidCodexAccessToken,
56
+ } from "../codex-token-refresh.js";
57
+
58
+ const PREFIX = "credential/openai-codex";
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Helpers
62
+ // ---------------------------------------------------------------------------
63
+
64
+ function setSecureKeyMap(map: Record<string, string>): void {
65
+ mockGetSecureKey.mockImplementation(async (key: string) => map[key]);
66
+ }
67
+
68
+ function futureTimestamp(secondsFromNow: number): string {
69
+ return String(Math.floor(Date.now() / 1000) + secondsFromNow);
70
+ }
71
+
72
+ function pastTimestamp(secondsAgo: number): string {
73
+ return String(Math.floor(Date.now() / 1000) - secondsAgo);
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Tests
78
+ // ---------------------------------------------------------------------------
79
+
80
+ describe("getValidCodexAccessToken", () => {
81
+ beforeEach(() => {
82
+ mockGetSecureKey.mockReset();
83
+ mockSetSecureKey.mockReset();
84
+ mockRefreshOAuth2Token.mockReset();
85
+ _resetRefreshMutex();
86
+
87
+ mockSetSecureKey.mockImplementation(async () => true);
88
+ mockRefreshOAuth2Token.mockImplementation(async () => ({
89
+ accessToken: "new-access-token",
90
+ refreshToken: "new-refresh-token",
91
+ expiresIn: 3600,
92
+ }));
93
+ });
94
+
95
+ afterEach(() => {
96
+ _resetRefreshMutex();
97
+ });
98
+
99
+ test("returns null when no access token is stored", async () => {
100
+ setSecureKeyMap({});
101
+ const result = await getValidCodexAccessToken(PREFIX);
102
+ expect(result).toBeNull();
103
+ });
104
+
105
+ test("returns access token when no expires_at is stored (graceful degradation)", async () => {
106
+ setSecureKeyMap({
107
+ [`${PREFIX}/access_token`]: "my-token",
108
+ });
109
+ const result = await getValidCodexAccessToken(PREFIX);
110
+ expect(result).toBe("my-token");
111
+ expect(mockRefreshOAuth2Token).not.toHaveBeenCalled();
112
+ });
113
+
114
+ test("returns access token when not expired", async () => {
115
+ setSecureKeyMap({
116
+ [`${PREFIX}/access_token`]: "my-token",
117
+ [`${PREFIX}/expires_at`]: futureTimestamp(600), // 10 minutes from now
118
+ });
119
+ const result = await getValidCodexAccessToken(PREFIX);
120
+ expect(result).toBe("my-token");
121
+ expect(mockRefreshOAuth2Token).not.toHaveBeenCalled();
122
+ });
123
+
124
+ test("refreshes token when expired", async () => {
125
+ const keys: Record<string, string> = {
126
+ [`${PREFIX}/access_token`]: "old-token",
127
+ [`${PREFIX}/refresh_token`]: "old-refresh",
128
+ [`${PREFIX}/expires_at`]: pastTimestamp(60), // expired 1 minute ago
129
+ };
130
+ setSecureKeyMap(keys);
131
+
132
+ const result = await getValidCodexAccessToken(PREFIX);
133
+
134
+ expect(result).toBe("new-access-token");
135
+ expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
136
+ expect(mockRefreshOAuth2Token).toHaveBeenCalledWith(
137
+ "https://auth.openai.com/oauth/token",
138
+ "app_EMoamEEZ73f0CkXaXp7hrann",
139
+ "old-refresh",
140
+ );
141
+
142
+ // Verify new tokens are stored
143
+ expect(mockSetSecureKey).toHaveBeenCalledWith(
144
+ `${PREFIX}/access_token`,
145
+ "new-access-token",
146
+ );
147
+ expect(mockSetSecureKey).toHaveBeenCalledWith(
148
+ `${PREFIX}/refresh_token`,
149
+ "new-refresh-token",
150
+ );
151
+ // expires_at should be stored as well
152
+ const expiresAtCall = mockSetSecureKey.mock.calls.find(
153
+ (c) => c[0] === `${PREFIX}/expires_at`,
154
+ );
155
+ expect(expiresAtCall).toBeDefined();
156
+ const storedExpiresAt = Number(expiresAtCall![1]);
157
+ const now = Math.floor(Date.now() / 1000);
158
+ // Should be approximately now + 3600 (within 5 seconds tolerance)
159
+ expect(storedExpiresAt).toBeGreaterThan(now + 3590);
160
+ expect(storedExpiresAt).toBeLessThanOrEqual(now + 3610);
161
+ });
162
+
163
+ test("refreshes token when about to expire (within 5-minute margin)", async () => {
164
+ setSecureKeyMap({
165
+ [`${PREFIX}/access_token`]: "old-token",
166
+ [`${PREFIX}/refresh_token`]: "old-refresh",
167
+ [`${PREFIX}/expires_at`]: futureTimestamp(60), // only 1 minute left
168
+ });
169
+
170
+ const result = await getValidCodexAccessToken(PREFIX);
171
+
172
+ expect(result).toBe("new-access-token");
173
+ expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
174
+ });
175
+
176
+ test("concurrent refresh calls are deduplicated (mutex)", async () => {
177
+ setSecureKeyMap({
178
+ [`${PREFIX}/access_token`]: "old-token",
179
+ [`${PREFIX}/refresh_token`]: "old-refresh",
180
+ [`${PREFIX}/expires_at`]: pastTimestamp(60),
181
+ });
182
+
183
+ // Use a deferred promise to control when the refresh completes.
184
+ // We create it upfront so the mock captures it synchronously.
185
+ let resolveRefresh!: (v: {
186
+ accessToken: string;
187
+ refreshToken: string;
188
+ expiresIn: number;
189
+ }) => void;
190
+ const refreshPromise = new Promise<{
191
+ accessToken: string;
192
+ refreshToken: string;
193
+ expiresIn: number;
194
+ }>((resolve) => {
195
+ resolveRefresh = resolve;
196
+ });
197
+
198
+ mockRefreshOAuth2Token.mockImplementation(() => refreshPromise);
199
+
200
+ // Fire two concurrent refreshes
201
+ const p1 = getValidCodexAccessToken(PREFIX);
202
+ const p2 = getValidCodexAccessToken(PREFIX);
203
+
204
+ // Allow the async get-key calls to settle before resolving refresh
205
+ await new Promise((r) => setTimeout(r, 10));
206
+
207
+ // Resolve the single in-flight refresh
208
+ resolveRefresh({
209
+ accessToken: "shared-new-token",
210
+ refreshToken: "shared-new-refresh",
211
+ expiresIn: 3600,
212
+ });
213
+
214
+ const [r1, r2] = await Promise.all([p1, p2]);
215
+
216
+ expect(r1).toBe("shared-new-token");
217
+ expect(r2).toBe("shared-new-token");
218
+ // Only one refresh call should have been made
219
+ expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
220
+ });
221
+
222
+ test("falls back to existing token when refresh fails", async () => {
223
+ setSecureKeyMap({
224
+ [`${PREFIX}/access_token`]: "old-token",
225
+ [`${PREFIX}/refresh_token`]: "old-refresh",
226
+ [`${PREFIX}/expires_at`]: pastTimestamp(60),
227
+ });
228
+
229
+ mockRefreshOAuth2Token.mockImplementation(async () => {
230
+ throw new Error("OAuth2 token refresh failed (HTTP 400: invalid_grant)");
231
+ });
232
+
233
+ const result = await getValidCodexAccessToken(PREFIX);
234
+
235
+ // Should fall back to the existing access token
236
+ expect(result).toBe("old-token");
237
+ expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
238
+ });
239
+
240
+ test("falls back to existing token when no refresh token available", async () => {
241
+ setSecureKeyMap({
242
+ [`${PREFIX}/access_token`]: "old-token",
243
+ // no refresh_token
244
+ [`${PREFIX}/expires_at`]: pastTimestamp(60),
245
+ });
246
+
247
+ const result = await getValidCodexAccessToken(PREFIX);
248
+
249
+ // Should return the existing access token
250
+ expect(result).toBe("old-token");
251
+ // Should not attempt a refresh
252
+ expect(mockRefreshOAuth2Token).not.toHaveBeenCalled();
253
+ });
254
+ });
@@ -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";
@@ -41,6 +42,8 @@ export interface AdapterCreateOpts {
41
42
  baseURL?: string;
42
43
  /** Forwarded to providers that wire native provider-side web search. */
43
44
  useNativeWebSearch: boolean;
45
+ /** When true, the OpenAI adapter targets the Codex subscription endpoint. */
46
+ codexSubscription?: boolean;
44
47
  }
45
48
 
46
49
  type AdapterFactory = (opts: AdapterCreateOpts) => Provider;
@@ -65,10 +68,18 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
65
68
  streamTimeoutMs,
66
69
  ...(baseURL ? { baseURL } : {}),
67
70
  }),
68
- openai: ({ apiKey, model, streamTimeoutMs, baseURL, useNativeWebSearch }) =>
71
+ openai: ({
72
+ apiKey,
73
+ model,
74
+ streamTimeoutMs,
75
+ baseURL,
76
+ useNativeWebSearch,
77
+ codexSubscription,
78
+ }) =>
69
79
  new OpenAIResponsesProvider(apiKey, model, {
70
80
  useNativeWebSearch,
71
81
  streamTimeoutMs,
82
+ codexSubscription,
72
83
  ...(baseURL ? { baseURL } : {}),
73
84
  }),
74
85
  gemini: ({ apiKey, model, streamTimeoutMs, baseURL }) =>
@@ -101,6 +112,8 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
101
112
  streamTimeoutMs,
102
113
  ...(baseURL ? { baseURL } : {}),
103
114
  }),
115
+ minimax: ({ apiKey, model, streamTimeoutMs }) =>
116
+ new MinimaxProvider(apiKey, model, { streamTimeoutMs }),
104
117
  };
105
118
 
106
119
  /**
@@ -176,12 +189,16 @@ export function createAdapterFromConnection(
176
189
  const baseURL =
177
190
  resolvedAuth.kind === "header" ? resolvedAuth.baseUrl : undefined;
178
191
 
192
+ const codexSubscription =
193
+ connection.auth.type === "oauth_subscription" && provider === "openai";
194
+
179
195
  const adapter = buildProviderAdapter(provider, {
180
196
  apiKey,
181
197
  model: opts.model,
182
198
  streamTimeoutMs: opts.streamTimeoutMs ?? 1_800_000,
183
199
  baseURL,
184
200
  useNativeWebSearch: opts.useNativeWebSearch ?? false,
201
+ codexSubscription,
185
202
  });
186
203
  if (!adapter) return null;
187
204
 
@@ -9,13 +9,13 @@ import { PROVIDER_CATALOG } from "../model-catalog.js";
9
9
  /**
10
10
  * Auth configuration stored in the `provider_connections` table.
11
11
  *
12
- * v1 runtime-supported variants:
12
+ * Runtime-supported variants:
13
13
  * - api_key: look up `credential` in vault, inject as bearer/provider header.
14
14
  * - platform: route via Vellum managed proxy; no client-side credential.
15
15
  * - none: no auth (e.g. Ollama running locally).
16
+ * - oauth_subscription: OAuth-based subscription auth (e.g. ChatGPT Codex).
16
17
  *
17
- * v2 schema-accepted variants (runtime rejects with a clear "not yet shipped" error):
18
- * - oauth_subscription: OAuth-based subscription auth.
18
+ * Schema-accepted variants (runtime rejects with a clear "not yet shipped" error):
19
19
  * - service_account: service-account credentials (Vertex AI, Bedrock).
20
20
  */
21
21
  export const AuthSchema = z.discriminatedUnion("type", [
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Automatic token refresh for ChatGPT Codex OAuth (subscription auth).
3
+ *
4
+ * OpenAI rotates refresh tokens on every use — concurrent refreshes will
5
+ * invalidate one token. A module-level mutex prevents this race.
6
+ */
7
+
8
+ import { refreshOAuth2Token } from "../../security/oauth2.js";
9
+ import {
10
+ getSecureKeyAsync,
11
+ setSecureKeyAsync,
12
+ } from "../../security/secure-keys.js";
13
+ import { getLogger } from "../../util/logger.js";
14
+
15
+ const log = getLogger("codex-token-refresh");
16
+
17
+ const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
18
+ const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
19
+
20
+ /** Refresh 5 minutes before expiry to avoid using a nearly-expired token. */
21
+ const REFRESH_MARGIN_SECONDS = 300;
22
+
23
+ /**
24
+ * Module-level mutex to prevent concurrent refresh races.
25
+ * OpenAI rotates refresh tokens on every use — two concurrent refreshes
26
+ * will invalidate one token.
27
+ */
28
+ let refreshInFlight: Promise<string | null> | null = null;
29
+
30
+ /**
31
+ * Return a valid Codex access token, refreshing transparently if expired.
32
+ *
33
+ * @param credentialPrefix - Credential key prefix, e.g. `"credential/chatgpt"`.
34
+ * The function reads `<prefix>/access_token`, `<prefix>/refresh_token`,
35
+ * and `<prefix>/expires_at` from the credential store.
36
+ * @returns The access token string, or `null` if no token is stored.
37
+ */
38
+ export async function getValidCodexAccessToken(
39
+ credentialPrefix: string,
40
+ ): Promise<string | null> {
41
+ const accessToken = await getSecureKeyAsync(
42
+ `${credentialPrefix}/access_token`,
43
+ );
44
+ if (!accessToken) return null;
45
+
46
+ const expiresAtStr = await getSecureKeyAsync(
47
+ `${credentialPrefix}/expires_at`,
48
+ );
49
+ if (!expiresAtStr) return accessToken; // no expiry info — use token as-is
50
+
51
+ const expiresAt = Number(expiresAtStr);
52
+ const now = Date.now() / 1000;
53
+
54
+ if (now < expiresAt - REFRESH_MARGIN_SECONDS) {
55
+ return accessToken; // token is still fresh
56
+ }
57
+
58
+ // Token is expired or about to expire — refresh it.
59
+ // Use mutex to prevent concurrent refresh races.
60
+ if (refreshInFlight) {
61
+ return await refreshInFlight;
62
+ }
63
+
64
+ refreshInFlight = doRefresh(credentialPrefix);
65
+ try {
66
+ return await refreshInFlight;
67
+ } finally {
68
+ refreshInFlight = null;
69
+ }
70
+ }
71
+
72
+ async function doRefresh(credentialPrefix: string): Promise<string | null> {
73
+ const refreshToken = await getSecureKeyAsync(
74
+ `${credentialPrefix}/refresh_token`,
75
+ );
76
+ if (!refreshToken) {
77
+ log.warn("No refresh token available for Codex OAuth");
78
+ // Return the existing access token — it might still work even if expired
79
+ return (
80
+ (await getSecureKeyAsync(`${credentialPrefix}/access_token`)) ?? null
81
+ );
82
+ }
83
+
84
+ try {
85
+ const result = await refreshOAuth2Token(
86
+ CODEX_TOKEN_URL,
87
+ CODEX_CLIENT_ID,
88
+ refreshToken,
89
+ );
90
+
91
+ // Store the new tokens
92
+ await setSecureKeyAsync(
93
+ `${credentialPrefix}/access_token`,
94
+ result.accessToken,
95
+ );
96
+ if (result.refreshToken) {
97
+ await setSecureKeyAsync(
98
+ `${credentialPrefix}/refresh_token`,
99
+ result.refreshToken,
100
+ );
101
+ }
102
+ if (result.expiresIn) {
103
+ const newExpiresAt = Math.floor(Date.now() / 1000 + result.expiresIn);
104
+ await setSecureKeyAsync(
105
+ `${credentialPrefix}/expires_at`,
106
+ String(newExpiresAt),
107
+ );
108
+ }
109
+
110
+ log.info("Codex OAuth token refreshed successfully");
111
+ return result.accessToken;
112
+ } catch (err) {
113
+ log.error({ err }, "Codex OAuth token refresh failed");
114
+ // Return the existing access token as fallback
115
+ return (
116
+ (await getSecureKeyAsync(`${credentialPrefix}/access_token`)) ?? null
117
+ );
118
+ }
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // Test helpers
123
+ // ---------------------------------------------------------------------------
124
+
125
+ /** @internal Test-only: reset the in-flight refresh mutex. */
126
+ export function _resetRefreshMutex(): void {
127
+ refreshInFlight = null;
128
+ }
@@ -2,10 +2,11 @@
2
2
  * Resolves an `Auth` config into a `ResolvedAuth` that adapters consume.
3
3
  *
4
4
  * Resolution rules:
5
- * - api_key → fetch credential from vault → inject as bearer header
6
- * - platform → build managed proxy URL and fetch the platform API key
7
- * - none → pass through with no auth headers
8
- * - oauth_subscription / service_account reject (v2 not yet shipped)
5
+ * - api_key → fetch credential from vault → inject as bearer header
6
+ * - platform → build managed proxy URL and fetch the platform API key
7
+ * - none → pass through with no auth headers
8
+ * - oauth_subscription fetch OAuth token from vault (with auto-refresh) inject as bearer header
9
+ * - service_account → reject (v2 not yet shipped)
9
10
  */
10
11
 
11
12
  import {
@@ -13,7 +14,11 @@ import {
13
14
  resolveManagedProxyContext,
14
15
  } from "../../providers/platform-proxy/context.js";
15
16
  import { getSecureKeyAsync } from "../../security/secure-keys.js";
17
+ import { getLogger } from "../../util/logger.js";
16
18
  import type { Auth, ResolvedAuth } from "./auth.js";
19
+ import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "./connections.js";
20
+
21
+ const log = getLogger("resolve-auth");
17
22
 
18
23
  export type ResolveAuthError =
19
24
  | { code: "credential_not_found"; credential: string }
@@ -27,6 +32,19 @@ export async function resolveAuth(
27
32
  ): Promise<
28
33
  { ok: true; resolved: ResolvedAuth } | { ok: false; error: ResolveAuthError }
29
34
  > {
35
+ // Defense-in-depth: strip baseUrl for providers that should not accept one.
36
+ // The route layer rejects base_url for non-openai-compatible providers, but
37
+ // this guard catches any code path that bypasses route validation (e.g.
38
+ // corrupted DB rows, direct calls from internal code).
39
+ let safeBaseUrl = opts.baseUrl;
40
+ if (safeBaseUrl && !PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
41
+ log.warn(
42
+ { provider, baseUrl: safeBaseUrl },
43
+ `Stripping baseUrl for provider "${provider}" — base_url is only valid for openai-compatible providers.`,
44
+ );
45
+ safeBaseUrl = null;
46
+ }
47
+
30
48
  switch (auth.type) {
31
49
  case "api_key": {
32
50
  const value = await getSecureKeyAsync(auth.credential);
@@ -41,7 +59,7 @@ export async function resolveAuth(
41
59
  resolved: {
42
60
  kind: "header",
43
61
  headers: { Authorization: `Bearer ${value}` },
44
- ...(opts.baseUrl ? { baseUrl: opts.baseUrl } : {}),
62
+ ...(safeBaseUrl ? { baseUrl: safeBaseUrl } : {}),
45
63
  },
46
64
  };
47
65
  }
@@ -65,7 +83,32 @@ export async function resolveAuth(
65
83
  case "none":
66
84
  return { ok: true, resolved: { kind: "none" } };
67
85
 
68
- case "oauth_subscription":
86
+ case "oauth_subscription": {
87
+ // Extract the credential prefix from the credential key.
88
+ // The credential field stores "credential/openai-codex/access_token";
89
+ // we need the prefix "credential/openai-codex" for the refresh logic.
90
+ const credentialPrefix = auth.credential.replace(/\/access_token$/, "");
91
+
92
+ const { getValidCodexAccessToken } = await import(
93
+ "./codex-token-refresh.js"
94
+ );
95
+ const token = await getValidCodexAccessToken(credentialPrefix);
96
+
97
+ if (!token) {
98
+ return {
99
+ ok: false,
100
+ error: { code: "credential_not_found", credential: auth.credential },
101
+ };
102
+ }
103
+ return {
104
+ ok: true,
105
+ resolved: {
106
+ kind: "header",
107
+ headers: { Authorization: `Bearer ${token}` },
108
+ },
109
+ };
110
+ }
111
+
69
112
  case "service_account":
70
113
  return {
71
114
  ok: false,
@@ -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
+ }