@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +642 -5
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -60,6 +60,8 @@ export interface OpenAIChatCompletionsProviderOptions {
60
60
  providerName?: string;
61
61
  providerLabel?: string;
62
62
  streamTimeoutMs?: number;
63
+ /** Provider-level request headers merged into every API request. */
64
+ requestHeaders?: Record<string, string>;
63
65
  /** Extra params spread into every chat.completions.create call (e.g. reasoning). */
64
66
  extraCreateParams?: Record<string, unknown>;
65
67
  /** Upper bound for `reasoning_effort` sent on the wire. Defaults to "xhigh"
@@ -67,6 +69,10 @@ export interface OpenAIChatCompletionsProviderOptions {
67
69
  * document `low|medium|high` (e.g. Fireworks) should set this to "high" so
68
70
  * Vellum's `xhigh`/`max` tiers don't 4xx upstream. */
69
71
  maxReasoningEffort?: "high" | "xhigh";
72
+ /** Parse `<think>...</think>` tags from the content stream into thinking
73
+ * blocks. MiniMax and similar providers embed reasoning inside XML-style
74
+ * tags in the regular content field rather than using `reasoning_content`. */
75
+ parseThinkTags?: boolean;
70
76
  }
71
77
 
72
78
  /** Map our internal effort values to OpenAI's reasoning_effort parameter.
@@ -74,7 +80,7 @@ export interface OpenAIChatCompletionsProviderOptions {
74
80
  * passed through explicitly because OpenAI defaults `reasoning_effort` to
75
81
  * "medium" when the field is omitted — the user's opt-out is only honored
76
82
  * when we send it on the wire. */
77
- const EFFORT_TO_REASONING_EFFORT: Record<
83
+ export const EFFORT_TO_REASONING_EFFORT: Record<
78
84
  string,
79
85
  NonNullable<
80
86
  OpenAI.Chat.Completions.ChatCompletionCreateParams["reasoning_effort"]
@@ -95,6 +101,13 @@ const OPENAI_SUPPORTED_IMAGE_TYPES = new Set([
95
101
  "image/webp",
96
102
  ]);
97
103
 
104
+ function partialTagSuffix(text: string, tag: string): number {
105
+ for (let len = Math.min(text.length, tag.length - 1); len > 0; len--) {
106
+ if (text.endsWith(tag.substring(0, len))) return len;
107
+ }
108
+ return 0;
109
+ }
110
+
98
111
  /**
99
112
  * OpenAI-compatible chat-completions transport.
100
113
  *
@@ -110,6 +123,8 @@ export class OpenAIChatCompletionsProvider implements Provider {
110
123
  private streamTimeoutMs: number;
111
124
  private extraCreateParams: Record<string, unknown>;
112
125
  private maxReasoningEffort: "high" | "xhigh";
126
+ private requestHeaders: Record<string, string>;
127
+ private parseThinkTags: boolean;
113
128
 
114
129
  constructor(
115
130
  apiKey: string,
@@ -126,6 +141,8 @@ export class OpenAIChatCompletionsProvider implements Provider {
126
141
  this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
127
142
  this.extraCreateParams = options.extraCreateParams ?? {};
128
143
  this.maxReasoningEffort = options.maxReasoningEffort ?? "xhigh";
144
+ this.requestHeaders = options.requestHeaders ?? {};
145
+ this.parseThinkTags = options.parseThinkTags ?? false;
129
146
  }
130
147
 
131
148
  async sendMessage(
@@ -159,10 +176,17 @@ export class OpenAIChatCompletionsProvider implements Provider {
159
176
  params.max_completion_tokens = maxTokens;
160
177
  }
161
178
 
179
+ // Subclasses (OpenRouter) may already have nested effort under
180
+ // `reasoning.effort` via `buildExtraCreateParams`. Skip the flat
181
+ // `reasoning_effort` assignment in that case to avoid sending both forms,
182
+ // which OpenRouter rejects on reasoning models.
183
+ const nestedReasoningEffort = (
184
+ params as { reasoning?: { effort?: unknown } }
185
+ ).reasoning?.effort;
162
186
  const reasoningEffort = effort
163
187
  ? EFFORT_TO_REASONING_EFFORT[effort]
164
188
  : undefined;
165
- if (reasoningEffort) {
189
+ if (reasoningEffort && typeof nestedReasoningEffort !== "string") {
166
190
  params.reasoning_effort =
167
191
  reasoningEffort === "xhigh" && this.maxReasoningEffort === "high"
168
192
  ? "high"
@@ -185,6 +209,68 @@ export class OpenAIChatCompletionsProvider implements Provider {
185
209
 
186
210
  // Accumulate the response from chunks
187
211
  let contentText = "";
212
+ let reasoningText = "";
213
+ let insideThinkBlock = false;
214
+ let pendingContent = "";
215
+
216
+ const flushPendingContent = (final: boolean): void => {
217
+ while (pendingContent.length > 0) {
218
+ if (insideThinkBlock) {
219
+ const closeIdx = pendingContent.indexOf("</think>");
220
+ if (closeIdx >= 0) {
221
+ const thinking = pendingContent.substring(0, closeIdx);
222
+ if (thinking) {
223
+ reasoningText += thinking;
224
+ onEvent?.({ type: "thinking_delta", thinking });
225
+ }
226
+ insideThinkBlock = false;
227
+ pendingContent = pendingContent.substring(
228
+ closeIdx + "</think>".length,
229
+ );
230
+ } else {
231
+ const partial = final
232
+ ? 0
233
+ : partialTagSuffix(pendingContent, "</think>");
234
+ const safeLen = pendingContent.length - partial;
235
+ if (safeLen > 0) {
236
+ const thinking = pendingContent.substring(0, safeLen);
237
+ reasoningText += thinking;
238
+ onEvent?.({ type: "thinking_delta", thinking });
239
+ }
240
+ pendingContent =
241
+ partial > 0 ? pendingContent.substring(safeLen) : "";
242
+ break;
243
+ }
244
+ } else {
245
+ const openIdx = pendingContent.indexOf("<think>");
246
+ if (openIdx >= 0) {
247
+ const text = pendingContent.substring(0, openIdx);
248
+ if (text) {
249
+ contentText += text;
250
+ onEvent?.({ type: "text_delta", text });
251
+ }
252
+ insideThinkBlock = true;
253
+ pendingContent = pendingContent.substring(
254
+ openIdx + "<think>".length,
255
+ );
256
+ } else {
257
+ const partial = final
258
+ ? 0
259
+ : partialTagSuffix(pendingContent, "<think>");
260
+ const safeLen = pendingContent.length - partial;
261
+ if (safeLen > 0) {
262
+ const t = pendingContent.substring(0, safeLen);
263
+ contentText += t;
264
+ onEvent?.({ type: "text_delta", text: t });
265
+ }
266
+ pendingContent =
267
+ partial > 0 ? pendingContent.substring(safeLen) : "";
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ };
273
+
188
274
  const toolCallMap = new Map<
189
275
  number,
190
276
  { id: string; name: string; args: string }
@@ -197,10 +283,14 @@ export class OpenAIChatCompletionsProvider implements Provider {
197
283
  let cachedPromptTokens = 0;
198
284
 
199
285
  try {
286
+ const requestHeaders = {
287
+ ...this.requestHeaders,
288
+ ...(usageAttributionHeaders ?? {}),
289
+ };
200
290
  const stream = await this.client.chat.completions.create(params, {
201
291
  signal: timeoutSignal,
202
- ...(usageAttributionHeaders
203
- ? { headers: usageAttributionHeaders }
292
+ ...(Object.keys(requestHeaders).length > 0
293
+ ? { headers: requestHeaders }
204
294
  : {}),
205
295
  });
206
296
 
@@ -208,8 +298,62 @@ export class OpenAIChatCompletionsProvider implements Provider {
208
298
  const choice = chunk.choices[0];
209
299
  if (choice) {
210
300
  if (choice.delta.content) {
211
- contentText += choice.delta.content;
212
- onEvent?.({ type: "text_delta", text: choice.delta.content });
301
+ if (this.parseThinkTags) {
302
+ pendingContent += choice.delta.content;
303
+ flushPendingContent(false);
304
+ } else {
305
+ contentText += choice.delta.content;
306
+ onEvent?.({ type: "text_delta", text: choice.delta.content });
307
+ }
308
+ }
309
+
310
+ // Compatibility providers disagree on the field name: Fireworks /
311
+ // DeepSeek / Together / Groq stream `reasoning_content`; OpenRouter
312
+ // (per its ChatAssistantMessage spec) streams `reasoning`, and for
313
+ // reasoning summaries (e.g. Kimi K2.6) also populates
314
+ // `delta.reasoning_details[]` (entries are `reasoning.summary`,
315
+ // `reasoning.text`, or opaque `reasoning.encrypted`).
316
+ //
317
+ // Kimi K2.6 mirrors the same token into BOTH `delta.reasoning` and
318
+ // `delta.reasoning_details[].text` per chunk — prefer details when
319
+ // they carry visible text, otherwise fall through to the flat
320
+ // field. The encrypted-only case must fall through too, so the
321
+ // flat `reasoning` field isn't silently dropped.
322
+ const deltaWithReasoning = choice.delta as {
323
+ reasoning?: string | null;
324
+ reasoning_content?: string | null;
325
+ reasoning_details?: Array<{
326
+ type?: string;
327
+ summary?: string | null;
328
+ text?: string | null;
329
+ }> | null;
330
+ };
331
+
332
+ let sawVisibleDetail = false;
333
+ const reasoningDetails = deltaWithReasoning.reasoning_details;
334
+ if (Array.isArray(reasoningDetails)) {
335
+ for (const entry of reasoningDetails) {
336
+ if (entry.type === "reasoning.encrypted") continue;
337
+ const piece = entry.summary ?? entry.text;
338
+ if (piece) {
339
+ sawVisibleDetail = true;
340
+ reasoningText += piece;
341
+ onEvent?.({ type: "thinking_delta", thinking: piece });
342
+ }
343
+ }
344
+ }
345
+
346
+ if (!sawVisibleDetail) {
347
+ const reasoningContent =
348
+ deltaWithReasoning.reasoning_content ??
349
+ deltaWithReasoning.reasoning;
350
+ if (reasoningContent) {
351
+ reasoningText += reasoningContent;
352
+ onEvent?.({
353
+ type: "thinking_delta",
354
+ thinking: reasoningContent,
355
+ });
356
+ }
213
357
  }
214
358
 
215
359
  if (choice.delta.tool_calls) {
@@ -252,10 +396,27 @@ export class OpenAIChatCompletionsProvider implements Provider {
252
396
  cleanupTimeout();
253
397
  }
254
398
 
399
+ if (this.parseThinkTags && pendingContent) {
400
+ flushPendingContent(true);
401
+ }
402
+
255
403
  // Build content blocks
404
+ const finalReasoning = this.parseThinkTags
405
+ ? reasoningText.trim()
406
+ : reasoningText;
407
+ const finalContent = this.parseThinkTags
408
+ ? contentText.trim()
409
+ : contentText;
256
410
  const content: ContentBlock[] = [];
257
- if (contentText) {
258
- content.push({ type: "text", text: contentText });
411
+ if (finalReasoning) {
412
+ content.push({
413
+ type: "thinking",
414
+ thinking: finalReasoning,
415
+ signature: "",
416
+ });
417
+ }
418
+ if (finalContent) {
419
+ content.push({ type: "text", text: finalContent });
259
420
  }
260
421
  for (const [, tc] of toolCallMap) {
261
422
  let input: Record<string, unknown>;
@@ -1,6 +1,9 @@
1
1
  import { ProviderError } from "../../util/errors.js";
2
2
  import { AnthropicProvider } from "../anthropic/client.js";
3
- import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
3
+ import {
4
+ EFFORT_TO_REASONING_EFFORT,
5
+ OpenAIChatCompletionsProvider,
6
+ } from "../openai/chat-completions-provider.js";
4
7
  import { isThinkingConfigEnabled } from "../thinking-config.js";
5
8
  import type {
6
9
  Message,
@@ -18,6 +21,11 @@ export interface OpenRouterProviderOptions {
18
21
  }
19
22
 
20
23
  const DEFAULT_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
24
+ const OPENROUTER_APP_ATTRIBUTION_HEADERS = {
25
+ "HTTP-Referer": "https://www.vellum.ai",
26
+ "X-OpenRouter-Title": "Vellum Assistant",
27
+ "X-OpenRouter-Categories": "personal-agent,cli-agent",
28
+ };
21
29
 
22
30
  // Models on OpenRouter prefixed `anthropic/` are routed through OpenRouter's
23
31
  // Anthropic-compatible Messages API at `<root>/v1/messages` (where `<root>` is
@@ -48,6 +56,25 @@ export function extractOnlyList(config: unknown): string[] {
48
56
  return only.filter((x): x is string => typeof x === "string" && x.length > 0);
49
57
  }
50
58
 
59
+ // OpenRouter's `reasoning.summary` field controls whether reasoning models emit
60
+ // a human-readable summary alongside (or instead of) encrypted reasoning blocks.
61
+ // Models like Kimi K2.6 return only encrypted `reasoning_details` unless a
62
+ // summary level is requested, so the stream carries no visible thinking content.
63
+ // Default to "detailed" so users see thinking by default; allow per-call
64
+ // override via `config.openrouter.reasoning.summary`. Per OpenRouter's
65
+ // ChatRequestReasoning schema, valid values are "auto" | "concise" | "detailed".
66
+ const VALID_REASONING_SUMMARIES = new Set(["auto", "concise", "detailed"]);
67
+
68
+ function extractReasoningSummaryOverride(config: unknown): string | undefined {
69
+ const cfg = config as
70
+ | { openrouter?: { reasoning?: { summary?: unknown } } }
71
+ | undefined;
72
+ const summary = cfg?.openrouter?.reasoning?.summary;
73
+ return typeof summary === "string" && VALID_REASONING_SUMMARIES.has(summary)
74
+ ? summary
75
+ : undefined;
76
+ }
77
+
51
78
  /**
52
79
  * Rewrite `options.config` for the Anthropic-compat path so OpenRouter's
53
80
  * `provider: { only: [...] }` body field travels through `AnthropicProvider`'s
@@ -93,6 +120,7 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
93
120
  providerName: "openrouter",
94
121
  providerLabel: "OpenRouter",
95
122
  streamTimeoutMs: options.streamTimeoutMs,
123
+ requestHeaders: OPENROUTER_APP_ATTRIBUTION_HEADERS,
96
124
  });
97
125
  this.openRouterApiKey = apiKey;
98
126
  this.defaultModel = model;
@@ -154,14 +182,30 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
154
182
  // OpenRouter's unified `reasoning` parameter controls extended thinking on
155
183
  // its OpenAI-compatible endpoint. Anthropic models skip this path entirely and
156
184
  // go through AnthropicProvider, which receives the native `thinking` object.
185
+ //
186
+ // `effort` nests under `reasoning` here (rather than flat `reasoning_effort`)
187
+ // because OpenRouter's documented `ChatRequestReasoning` shape is the union of
188
+ // { effort, summary }. `summary` is required for models like Kimi K2.6 that
189
+ // would otherwise return only encrypted reasoning blocks; we default to
190
+ // "detailed" and let callers override via `config.openrouter.reasoning.summary`.
157
191
  protected override buildExtraCreateParams(
158
192
  options?: SendMessageOptions,
159
193
  ): Record<string, unknown> {
160
194
  const config = options?.config as Record<string, unknown> | undefined;
161
195
  const thinkingEnabled = isThinkingConfigEnabled(config?.thinking);
162
- const extras: Record<string, unknown> = {
163
- reasoning: { enabled: thinkingEnabled },
164
- };
196
+ const effort = config?.effort as string | undefined;
197
+ const mappedEffort = effort
198
+ ? EFFORT_TO_REASONING_EFFORT[effort]
199
+ : undefined;
200
+ const summaryOverride = extractReasoningSummaryOverride(config);
201
+ const reasoning: Record<string, unknown> = { enabled: thinkingEnabled };
202
+ if (mappedEffort) {
203
+ reasoning.effort = mappedEffort;
204
+ }
205
+ if (thinkingEnabled) {
206
+ reasoning.summary = summaryOverride ?? "detailed";
207
+ }
208
+ const extras: Record<string, unknown> = { reasoning };
165
209
  const only = extractOnlyList(config);
166
210
  if (only.length > 0) {
167
211
  const existingProvider = (config?.provider ?? {}) as Record<
@@ -192,6 +236,7 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
192
236
  streamTimeoutMs: this.providerStreamTimeoutMs,
193
237
  authToken: this.openRouterApiKey,
194
238
  useNativeWebSearch: this.useNativeWebSearch,
239
+ requestHeaders: OPENROUTER_APP_ATTRIBUTION_HEADERS,
195
240
  },
196
241
  );
197
242
  }
@@ -25,7 +25,7 @@ export interface ManagedProviderMeta {
25
25
  * managed credentials are present; that policy lives in the registry/context
26
26
  * fallback allowlists.
27
27
  */
28
- export const MANAGED_PROVIDER_META: Record<string, ManagedProviderMeta> = {
28
+ export const PLATFORM_PROVIDER_META: Record<string, ManagedProviderMeta> = {
29
29
  openai: {
30
30
  name: "openai",
31
31
  managed: true,
@@ -43,11 +43,13 @@ export const MANAGED_PROVIDER_META: Record<string, ManagedProviderMeta> = {
43
43
  },
44
44
  fireworks: {
45
45
  name: "fireworks",
46
- managed: false,
46
+ managed: true,
47
+ proxyPath: "/v1/runtime-proxy/fireworks",
47
48
  },
48
49
  openrouter: {
49
50
  name: "openrouter",
50
51
  managed: false,
51
52
  },
52
53
  ollama: { name: "ollama", managed: false },
54
+ "openai-compatible": { name: "openai-compatible", managed: false },
53
55
  };
@@ -12,7 +12,7 @@
12
12
  import { getPlatformBaseUrl } from "../../config/env.js";
13
13
  import { credentialKey } from "../../security/credential-key.js";
14
14
  import { getSecureKeyAsync } from "../../security/secure-keys.js";
15
- import { MANAGED_PROVIDER_META } from "./constants.js";
15
+ import { PLATFORM_PROVIDER_META } from "./constants.js";
16
16
 
17
17
  /** Storage key for the assistant API key credential. */
18
18
  const ASSISTANT_API_KEY_STORAGE_KEY = credentialKey(
@@ -70,7 +70,7 @@ export async function hasManagedProxyPrereqs(): Promise<boolean> {
70
70
  export async function buildManagedBaseUrl(
71
71
  provider: string,
72
72
  ): Promise<string | undefined> {
73
- const meta = MANAGED_PROVIDER_META[provider];
73
+ const meta = PLATFORM_PROVIDER_META[provider];
74
74
  if (!meta?.managed || !meta.proxyPath) return undefined;
75
75
 
76
76
  const ctx = await resolveManagedProxyContext();
@@ -88,7 +88,7 @@ export async function buildManagedBaseUrl(
88
88
  export async function managedFallbackEnabledFor(
89
89
  provider: string,
90
90
  ): Promise<boolean> {
91
- const meta = MANAGED_PROVIDER_META[provider];
91
+ const meta = PLATFORM_PROVIDER_META[provider];
92
92
  if (!meta?.managed) return false;
93
93
  return await hasManagedProxyPrereqs();
94
94
  }
@@ -5,9 +5,11 @@
5
5
  * environment variable fallbacks, and managed proxy availability.
6
6
  */
7
7
 
8
- import { API_KEY_PROVIDERS } from "../config/loader.js";
8
+ import { API_KEY_PROVIDERS, getConfig } from "../config/loader.js";
9
9
  import { getProviderKeyAsync } from "../security/secure-keys.js";
10
- import { managedFallbackEnabledFor } from "./managed-proxy/context.js";
10
+ import { PROVIDER_CATALOG } from "./model-catalog.js";
11
+ import { managedFallbackEnabledFor } from "./platform-proxy/context.js";
12
+ import { getVisibleProviderCatalog } from "./provider-catalog-visibility.js";
11
13
 
12
14
  /**
13
15
  * Check whether a single provider is usable — via a user-provided key
@@ -25,11 +27,24 @@ export async function isProviderAvailable(provider: string): Promise<boolean> {
25
27
  /**
26
28
  * Build the list of providers that are usable — via a user-provided key
27
29
  * (secure storage or env var) or via the managed proxy fallback.
30
+ * Feature-flagged LLM providers that are currently disabled are excluded.
28
31
  * Ollama is always included because it does not require an API key.
29
32
  */
30
33
  export async function getConfiguredProviders(): Promise<string[]> {
34
+ // Build the set of LLM providers hidden by feature flags so we can
35
+ // exclude them while leaving non-LLM providers (search, STT, TTS)
36
+ // in API_KEY_PROVIDERS unchanged.
37
+ const allLlmIds = new Set(PROVIDER_CATALOG.map((p) => p.id));
38
+ const visibleLlmIds = new Set(
39
+ getVisibleProviderCatalog(getConfig()).map((p) => p.id),
40
+ );
41
+ const hiddenLlmIds = new Set(
42
+ [...allLlmIds].filter((id) => !visibleLlmIds.has(id)),
43
+ );
44
+
31
45
  const configured: string[] = [];
32
46
  for (const p of API_KEY_PROVIDERS) {
47
+ if (hiddenLlmIds.has(p)) continue;
33
48
  if (await isProviderAvailable(p)) {
34
49
  configured.push(p);
35
50
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Feature-flag-aware provider catalog filtering.
3
+ *
4
+ * User-facing catalog consumers (model info, slash commands, provider
5
+ * availability) use `getVisibleProviderCatalog()` to hide providers and
6
+ * models gated behind disabled feature flags. Internal consumers
7
+ * (adapter-factory, pricing, auth) continue using the unfiltered
8
+ * `PROVIDER_CATALOG` directly.
9
+ */
10
+
11
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
12
+ import type { AssistantConfig } from "../config/schema.js";
13
+ import {
14
+ PROVIDER_CATALOG,
15
+ type ProviderCatalogEntry,
16
+ } from "./model-catalog.js";
17
+
18
+ export function getVisibleProviderCatalog(
19
+ config: AssistantConfig,
20
+ ): ProviderCatalogEntry[] {
21
+ return PROVIDER_CATALOG.filter(
22
+ (entry) =>
23
+ !entry.featureFlag ||
24
+ isAssistantFeatureFlagEnabled(entry.featureFlag, config),
25
+ )
26
+ .map((entry) => {
27
+ const visibleModels = entry.models.filter(
28
+ (m) =>
29
+ !m.featureFlag ||
30
+ isAssistantFeatureFlagEnabled(m.featureFlag, config),
31
+ );
32
+ if (visibleModels.length === entry.models.length) return entry;
33
+ return { ...entry, models: visibleModels };
34
+ })
35
+ .filter(
36
+ (entry) => entry.models.length > 0 || entry.defaultModel === "",
37
+ );
38
+ }
@@ -7,8 +7,10 @@
7
7
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
8
8
  import { getConfig } from "../config/loader.js";
9
9
  import type { LLMCallSite } from "../config/schemas/llm.js";
10
+ import { getDb } from "../memory/db-connection.js";
10
11
  import { getLogger } from "../util/logger.js";
11
12
  import { tryResolveProviderForConnectionName } from "./connection-resolution.js";
13
+ import { listConnections } from "./inference/connections.js";
12
14
  import { initializeProviders, listProviders } from "./registry.js";
13
15
  import type {
14
16
  ContentBlock,
@@ -110,22 +112,35 @@ export async function resolveConfiguredProvider(
110
112
 
111
113
  const resolved = resolveCallSiteConfig(callSite, config.llm, opts);
112
114
  const inferenceProvider = resolved.provider;
113
- const connectionName = resolved.provider_connection;
115
+ let connectionName = resolved.provider_connection;
114
116
 
115
117
  // Connection-aware path: every dispatch goes through `provider_connection`.
116
118
  // The boot-time backfill ensures every profile has one in production.
117
- // When unset (test envs that skip backfill, freshly-installed configs
118
- // not yet backfilled, or users who manually cleared the field), we
119
- // return null so callsites with deterministic fallbacks (invite
120
- // instructions, telegram username resolution, etc.) keep working.
121
- // Hard config errors — connection lookup failure, provider mismatch —
122
- // still throw via `tryResolveProviderForConnectionName` below.
119
+ // When unset (profile set provider with "Any active" connection, test envs
120
+ // that skip backfill, freshly-installed configs not yet backfilled, or
121
+ // users who manually cleared the field), try to auto-resolve from the
122
+ // provider before falling back to null.
123
123
  if (!connectionName) {
124
- log.debug(
125
- { callSite, inferenceProvider },
126
- "resolveCallSiteConfig yielded no provider_connection — returning null so callsite can fall back",
127
- );
128
- return null;
124
+ if (inferenceProvider) {
125
+ try {
126
+ const candidates = listConnections(getDb(), {
127
+ provider: inferenceProvider,
128
+ });
129
+ const active = candidates.find((c) => c.status === "active");
130
+ if (active) {
131
+ connectionName = active.name;
132
+ }
133
+ } catch {
134
+ // DB not available — fall through to the existing null-return path.
135
+ }
136
+ }
137
+ if (!connectionName) {
138
+ log.debug(
139
+ { callSite, inferenceProvider },
140
+ "resolveCallSiteConfig yielded no provider_connection — returning null so callsite can fall back",
141
+ );
142
+ return null;
143
+ }
129
144
  }
130
145
 
131
146
  const connectionProvider = await tryResolveProviderForConnectionName(
@@ -1,4 +1,6 @@
1
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
1
2
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
3
+ import type { AssistantConfig } from "../config/schema.js";
2
4
  import { type LLMConfig } from "../config/schemas/llm.js";
3
5
  import { getProviderKeyAsync } from "../security/secure-keys.js";
4
6
  import { ProviderNotConfiguredError } from "../util/errors.js";
@@ -12,12 +14,12 @@ import {
12
14
  // ---------------------------------------------------------------------------
13
15
  import type { ProviderConnection } from "./inference/auth.js";
14
16
  import { resolveAuth } from "./inference/resolve-auth.js";
17
+ import { isModelInCatalog, PROVIDER_CATALOG } from "./model-catalog.js";
18
+ import { getProviderDefaultModel } from "./model-intents.js";
15
19
  import {
16
20
  buildManagedBaseUrl,
17
21
  resolveManagedProxyContext,
18
- } from "./managed-proxy/context.js";
19
- import { isModelInCatalog, PROVIDER_CATALOG } from "./model-catalog.js";
20
- import { getProviderDefaultModel } from "./model-intents.js";
22
+ } from "./platform-proxy/context.js";
21
23
  import { RetryProvider } from "./retry.js";
22
24
  import type { Provider } from "./types.js";
23
25
  import { UsageTrackingProvider } from "./usage-tracking.js";
@@ -26,6 +28,7 @@ const log = getLogger("provider-registry");
26
28
 
27
29
  const providers = new Map<string, Provider>();
28
30
  const routingSources = new Map<string, "user-key" | "managed-proxy">();
31
+ const OPENAI_COMPATIBLE_ENDPOINTS_FLAG = "openai-compatible-endpoints";
29
32
 
30
33
  /** Per-connection provider cache, keyed by connection name. */
31
34
  const connectionProviders = new Map<string, Provider>();
@@ -69,6 +72,16 @@ export interface ProvidersConfig {
69
72
  timeouts?: { providerStreamTimeoutSec?: number };
70
73
  }
71
74
 
75
+ function isProviderFeatureFlagEnabled(
76
+ key: string,
77
+ config: ProvidersConfig,
78
+ ): boolean {
79
+ return isAssistantFeatureFlagEnabled(
80
+ key,
81
+ config as unknown as AssistantConfig,
82
+ );
83
+ }
84
+
72
85
  function resolveModel(config: ProvidersConfig, providerName: string): string {
73
86
  const resolved = resolveCallSiteConfig("mainAgent", config.llm);
74
87
  const inferenceProvider = resolved.provider;
@@ -92,9 +105,7 @@ function resolveModel(config: ProvidersConfig, providerName: string): string {
92
105
  * The routing decision is now derived from credential availability rather than
93
106
  * the removed `services.inference.mode` config field.
94
107
  */
95
- async function resolveProviderCredentials(
96
- providerName: string,
97
- ): Promise<{
108
+ async function resolveProviderCredentials(providerName: string): Promise<{
98
109
  apiKey: string;
99
110
  baseURL?: string;
100
111
  source: "user-key" | "managed-proxy";
@@ -106,7 +117,11 @@ async function resolveProviderCredentials(
106
117
  const managedBaseUrl = await buildManagedBaseUrl(providerName);
107
118
  if (managedBaseUrl) {
108
119
  const ctx = await resolveManagedProxyContext();
109
- return { apiKey: ctx.assistantApiKey, baseURL: managedBaseUrl, source: "managed-proxy" };
120
+ return {
121
+ apiKey: ctx.assistantApiKey,
122
+ baseURL: managedBaseUrl,
123
+ source: "managed-proxy",
124
+ };
110
125
  }
111
126
  return null;
112
127
  }
@@ -122,10 +137,19 @@ export async function initializeProviders(
122
137
  (config.timeouts?.providerStreamTimeoutSec ?? 1800) * 1000;
123
138
  const useNativeWebSearch =
124
139
  config.services["web-search"].provider === "inference-provider-native";
125
- const mainAgentProvider = resolveCallSiteConfig("mainAgent", config.llm)
126
- .provider;
140
+ const mainAgentProvider = resolveCallSiteConfig(
141
+ "mainAgent",
142
+ config.llm,
143
+ ).provider;
127
144
 
128
145
  for (const entry of PROVIDER_CATALOG) {
146
+ if (
147
+ entry.featureFlag &&
148
+ !isProviderFeatureFlagEnabled(entry.featureFlag, config)
149
+ ) {
150
+ continue;
151
+ }
152
+
129
153
  const isKeyless = entry.setupMode === "keyless";
130
154
 
131
155
  // Credential resolution: user key first, managed proxy second. Keyless
@@ -198,10 +222,19 @@ export async function resolveProviderFromConnection(
198
222
  connection: ProviderConnection,
199
223
  config: ProvidersConfig,
200
224
  ): Promise<Provider | null> {
225
+ if (
226
+ connection.provider === "openai-compatible" &&
227
+ !isProviderFeatureFlagEnabled(OPENAI_COMPATIBLE_ENDPOINTS_FLAG, config)
228
+ ) {
229
+ return null;
230
+ }
231
+
201
232
  const cached = connectionProviders.get(connection.name);
202
233
  if (cached) return cached;
203
234
 
204
- const authResult = await resolveAuth(connection.auth, connection.provider);
235
+ const authResult = await resolveAuth(connection.auth, connection.provider, {
236
+ baseUrl: connection.baseUrl,
237
+ });
205
238
  if (!authResult.ok) {
206
239
  const err = authResult.error;
207
240
  if (err.code === "not_implemented") {
@@ -230,11 +263,15 @@ export async function resolveProviderFromConnection(
230
263
  config.services["web-search"].provider === "inference-provider-native";
231
264
  const model = resolveModel(config, connection.provider);
232
265
 
233
- const provider = createAdapterFromConnection(connection, authResult.resolved, {
234
- model,
235
- streamTimeoutMs,
236
- useNativeWebSearch,
237
- });
266
+ const provider = createAdapterFromConnection(
267
+ connection,
268
+ authResult.resolved,
269
+ {
270
+ model,
271
+ streamTimeoutMs,
272
+ useNativeWebSearch,
273
+ },
274
+ );
238
275
 
239
276
  if (provider) {
240
277
  connectionProviders.set(connection.name, provider);