@vellumai/assistant 0.8.2 → 0.8.4

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 (503) hide show
  1. package/ARCHITECTURE.md +11 -12
  2. package/docker-entrypoint.sh +13 -2
  3. package/docker-init-apt-root.sh +79 -6
  4. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  5. package/openapi.yaml +945 -36
  6. package/package.json +1 -1
  7. package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
  8. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  9. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +272 -0
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  14. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  15. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  16. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  17. package/src/__tests__/compaction-events.test.ts +1 -1
  18. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  19. package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
  20. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  22. package/src/__tests__/config-watcher.test.ts +1 -1
  23. package/src/__tests__/context-token-estimator.test.ts +112 -57
  24. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  27. package/src/__tests__/conversation-agent-loop.test.ts +77 -3
  28. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  29. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  30. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  32. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  33. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  34. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  35. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  36. package/src/__tests__/conversation-pairing.test.ts +2 -2
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  39. package/src/__tests__/conversation-queue.test.ts +1 -1
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
  41. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  42. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  43. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  44. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  45. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  46. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  47. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  48. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  49. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  50. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  51. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  52. package/src/__tests__/date-context.test.ts +45 -0
  53. package/src/__tests__/dm-backfill.test.ts +64 -0
  54. package/src/__tests__/dm-persistence.test.ts +33 -0
  55. package/src/__tests__/document-find-replace.test.ts +501 -0
  56. package/src/__tests__/external-plugin-loader.test.ts +91 -19
  57. package/src/__tests__/first-greeting.test.ts +23 -2
  58. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  59. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  60. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  61. package/src/__tests__/heartbeat-service.test.ts +24 -164
  62. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  63. package/src/__tests__/host-app-control-proxy.test.ts +241 -0
  64. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  66. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  67. package/src/__tests__/host-file-proxy.test.ts +8 -1
  68. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  69. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  70. package/src/__tests__/identity-routes.test.ts +57 -0
  71. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  72. package/src/__tests__/injector-background-turn.test.ts +153 -0
  73. package/src/__tests__/injector-chain.test.ts +7 -0
  74. package/src/__tests__/injector-document-comments.test.ts +378 -0
  75. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  76. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
  77. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  78. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  79. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  80. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  81. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  82. package/src/__tests__/llm-catalog-parity.test.ts +3 -0
  83. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  84. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  85. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  86. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
  87. package/src/__tests__/llm-resolver.test.ts +340 -3
  88. package/src/__tests__/log-export-routes.test.ts +99 -2
  89. package/src/__tests__/managed-profile-guard.test.ts +10 -0
  90. package/src/__tests__/message-queue-steer.test.ts +114 -0
  91. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  92. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  93. package/src/__tests__/notification-deep-link.test.ts +15 -0
  94. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  95. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  96. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  97. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  98. package/src/__tests__/openai-provider.test.ts +323 -3
  99. package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
  100. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  101. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  102. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  103. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  104. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  105. package/src/__tests__/platform-proxy-context.test.ts +6 -1
  106. package/src/__tests__/platform.test.ts +0 -3
  107. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  108. package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
  109. package/src/__tests__/plugin-types.test.ts +2 -2
  110. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  111. package/src/__tests__/process-message-display-content.test.ts +21 -16
  112. package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
  113. package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
  115. package/src/__tests__/server-history-render.test.ts +83 -4
  116. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  117. package/src/__tests__/system-prompt.test.ts +57 -101
  118. package/src/__tests__/terminal-tools.test.ts +11 -1
  119. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  120. package/src/__tests__/thread-backfill.test.ts +370 -22
  121. package/src/__tests__/tool-executor.test.ts +90 -1
  122. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  123. package/src/__tests__/twilio-routes.test.ts +1 -1
  124. package/src/__tests__/web-fetch.test.ts +2 -2
  125. package/src/__tests__/workspace-git-service.test.ts +88 -5
  126. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  127. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  128. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  129. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  130. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  131. package/src/a2a/__tests__/task-store.test.ts +246 -0
  132. package/src/a2a/agent-card.ts +58 -0
  133. package/src/a2a/feature-gate.ts +8 -0
  134. package/src/a2a/protocol-constants.ts +21 -0
  135. package/src/a2a/protocol-errors.ts +50 -0
  136. package/src/a2a/protocol-types.ts +162 -0
  137. package/src/a2a/task-store.ts +168 -0
  138. package/src/agent/attachments.ts +1 -0
  139. package/src/agent/loop.ts +208 -22
  140. package/src/background-wake/next-wake.test.ts +289 -0
  141. package/src/background-wake/next-wake.ts +172 -0
  142. package/src/browser/operations.ts +15 -0
  143. package/src/channels/config.ts +9 -0
  144. package/src/channels/types.ts +14 -0
  145. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  146. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  147. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  148. package/src/cli/commands/__tests__/schedules.test.ts +469 -0
  149. package/src/cli/commands/conversations.ts +128 -1
  150. package/src/cli/commands/inference-providers.ts +147 -1
  151. package/src/cli/commands/memory-v2.ts +308 -0
  152. package/src/cli/commands/notifications.ts +89 -37
  153. package/src/cli/commands/plugins.ts +67 -0
  154. package/src/cli/commands/schedules.ts +297 -5
  155. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  156. package/src/cli/lib/install-from-github.ts +8 -9
  157. package/src/cli/lib/search-plugins.ts +163 -0
  158. package/src/cli/program.ts +14 -0
  159. package/src/cli/utils/conversation-id.ts +17 -5
  160. package/src/config/assistant-feature-flags.ts +24 -54
  161. package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
  162. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  163. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  164. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  165. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  166. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  167. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  168. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  169. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  170. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  171. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  172. package/src/config/bundled-tool-registry.ts +22 -12
  173. package/src/config/call-site-defaults.ts +124 -0
  174. package/src/config/feature-flag-registry.json +111 -23
  175. package/src/config/llm-resolver.ts +66 -1
  176. package/src/config/schema.ts +2 -0
  177. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
  178. package/src/config/schemas/call-site-catalog.ts +21 -0
  179. package/src/config/schemas/channels.ts +9 -0
  180. package/src/config/schemas/conversations.ts +10 -0
  181. package/src/config/schemas/heartbeat.ts +14 -0
  182. package/src/config/schemas/llm.ts +4 -3
  183. package/src/config/schemas/memory-retrospective.ts +1 -1
  184. package/src/config/schemas/memory-v2.ts +51 -4
  185. package/src/config/schemas/memory.ts +3 -1
  186. package/src/config/seed-inference-profiles.ts +99 -29
  187. package/src/context/compactor.ts +80 -13
  188. package/src/context/token-estimator.ts +72 -31
  189. package/src/context/window-manager.ts +25 -0
  190. package/src/credential-health/credential-health-service.ts +34 -19
  191. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
  192. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  193. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  194. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  195. package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
  196. package/src/daemon/conversation-agent-loop.ts +252 -56
  197. package/src/daemon/conversation-lifecycle.ts +142 -116
  198. package/src/daemon/conversation-messaging.ts +3 -0
  199. package/src/daemon/conversation-process.ts +273 -0
  200. package/src/daemon/conversation-queue-manager.ts +14 -0
  201. package/src/daemon/conversation-runtime-assembly.ts +144 -75
  202. package/src/daemon/conversation-slash.ts +37 -5
  203. package/src/daemon/conversation-surfaces.ts +45 -2
  204. package/src/daemon/conversation-tool-setup.ts +7 -0
  205. package/src/daemon/conversation.ts +42 -12
  206. package/src/daemon/date-context.ts +40 -0
  207. package/src/daemon/first-greeting.ts +10 -0
  208. package/src/daemon/guardian-action-generators.ts +1 -125
  209. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  210. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  211. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  212. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  213. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  214. package/src/daemon/handlers/config-a2a.ts +449 -0
  215. package/src/daemon/handlers/config-model.test.ts +1 -0
  216. package/src/daemon/handlers/conversations.ts +80 -0
  217. package/src/daemon/handlers/shared.ts +92 -29
  218. package/src/daemon/host-app-control-proxy.ts +69 -18
  219. package/src/daemon/host-bash-proxy.ts +1 -1
  220. package/src/daemon/host-cu-proxy.ts +1 -1
  221. package/src/daemon/host-file-proxy.ts +1 -1
  222. package/src/daemon/host-proxy-preactivation.ts +85 -18
  223. package/src/daemon/host-transfer-proxy.ts +1 -1
  224. package/src/daemon/lifecycle.ts +67 -65
  225. package/src/daemon/memory-v2-startup.ts +49 -13
  226. package/src/daemon/message-protocol.ts +4 -0
  227. package/src/daemon/message-types/conversations.ts +8 -0
  228. package/src/daemon/message-types/document-comments.ts +50 -0
  229. package/src/daemon/message-types/messages.ts +68 -1
  230. package/src/daemon/message-types/notifications.ts +21 -0
  231. package/src/daemon/message-types/surfaces.ts +3 -1
  232. package/src/daemon/message-types/web-activity.ts +57 -0
  233. package/src/daemon/pkb-reminder-builder.test.ts +10 -53
  234. package/src/daemon/pkb-reminder-builder.ts +4 -19
  235. package/src/daemon/plugin-source-watcher.ts +135 -3
  236. package/src/daemon/process-message.ts +72 -12
  237. package/src/daemon/query-complexity-router.ts +75 -0
  238. package/src/daemon/skill-memory-refresh.ts +5 -1
  239. package/src/daemon/trust-context.ts +6 -0
  240. package/src/daemon/wake-target-adapter.ts +2 -0
  241. package/src/documents/document-comments-store.test.ts +338 -0
  242. package/src/documents/document-comments-store.ts +237 -0
  243. package/src/documents/document-store.ts +202 -0
  244. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  245. package/src/export/transcript-formatter.ts +54 -20
  246. package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
  247. package/src/heartbeat/heartbeat-service.ts +35 -191
  248. package/src/home/__tests__/feed-types.test.ts +40 -0
  249. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  250. package/src/home/feed-types.ts +20 -3
  251. package/src/home/home-content-refresh.ts +52 -0
  252. package/src/home/home-greeting-cache.ts +69 -0
  253. package/src/home/home-greeting.ts +94 -0
  254. package/src/home/suggested-prompts.ts +177 -9
  255. package/src/ipc/cli-client.ts +147 -45
  256. package/src/memory/__tests__/conversation-queries.test.ts +220 -0
  257. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  258. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  259. package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
  260. package/src/memory/conversation-crud.ts +133 -43
  261. package/src/memory/conversation-queries.ts +87 -1
  262. package/src/memory/conversation-title-service.ts +26 -4
  263. package/src/memory/db-init.ts +22 -0
  264. package/src/memory/delivery-crud.ts +41 -0
  265. package/src/memory/delivery-status.ts +141 -15
  266. package/src/memory/external-conversation-store.ts +32 -1
  267. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
  268. package/src/memory/graph/conversation-graph-memory.ts +18 -6
  269. package/src/memory/graph/tools.ts +6 -37
  270. package/src/memory/invite-store.ts +53 -0
  271. package/src/memory/jobs-worker.ts +21 -1
  272. package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
  273. package/src/memory/llm-request-log-store.ts +92 -1
  274. package/src/memory/memory-retrospective-constants.ts +28 -0
  275. package/src/memory/memory-retrospective-enqueue.ts +4 -22
  276. package/src/memory/memory-retrospective-job.ts +438 -21
  277. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  278. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  279. package/src/memory/migrations/100-core-tables.ts +1 -0
  280. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  281. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  282. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  283. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  284. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  285. package/src/memory/migrations/253-document-comments.ts +47 -0
  286. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  287. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  288. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  289. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  290. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  291. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  292. package/src/memory/migrations/index.ts +20 -0
  293. package/src/memory/migrations/registry.ts +33 -0
  294. package/src/memory/onboarding-events-store.ts +7 -0
  295. package/src/memory/schema/a2a.ts +15 -0
  296. package/src/memory/schema/calls.ts +1 -0
  297. package/src/memory/schema/conversations.ts +3 -0
  298. package/src/memory/schema/index.ts +1 -0
  299. package/src/memory/schema/inference.ts +2 -0
  300. package/src/memory/schema/infrastructure.ts +2 -0
  301. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  302. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  303. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  304. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  305. package/src/memory/v2/__tests__/injection.test.ts +221 -17
  306. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  307. package/src/memory/v2/__tests__/router.test.ts +489 -1
  308. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  309. package/src/memory/v2/activation-store.ts +14 -16
  310. package/src/memory/v2/cli-command-content.ts +19 -0
  311. package/src/memory/v2/cli-command-store.ts +304 -0
  312. package/src/memory/v2/consolidation-job.ts +14 -0
  313. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  314. package/src/memory/v2/injection-events.ts +101 -0
  315. package/src/memory/v2/injection.ts +69 -29
  316. package/src/memory/v2/page-index.ts +246 -19
  317. package/src/memory/v2/page-store.ts +18 -0
  318. package/src/memory/v2/router.ts +209 -55
  319. package/src/memory/v2/static-context.ts +4 -4
  320. package/src/memory/v2/types.ts +23 -0
  321. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  322. package/src/messaging/providers/a2a/deliver.ts +156 -0
  323. package/src/messaging/providers/gmail/client.ts +9 -2
  324. package/src/messaging/providers/index.ts +18 -3
  325. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  327. package/src/messaging/providers/slack/adapter.ts +178 -25
  328. package/src/messaging/providers/slack/api.test.ts +54 -0
  329. package/src/messaging/providers/slack/api.ts +119 -3
  330. package/src/messaging/providers/slack/client.ts +12 -0
  331. package/src/messaging/providers/slack/deep-link.ts +20 -1
  332. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  333. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  334. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  335. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  336. package/src/messaging/providers/slack/send.test.ts +77 -0
  337. package/src/messaging/providers/slack/send.ts +8 -2
  338. package/src/messaging/providers/slack/types.ts +14 -0
  339. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  340. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  341. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  342. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
  343. package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
  344. package/src/notifications/adapters/macos.ts +12 -2
  345. package/src/notifications/broadcaster.ts +29 -4
  346. package/src/notifications/conversation-seed-composer.ts +14 -2
  347. package/src/notifications/copy-composer.ts +17 -64
  348. package/src/notifications/decision-engine.ts +111 -44
  349. package/src/notifications/deferred-emit.ts +135 -0
  350. package/src/notifications/deterministic-checks.ts +96 -0
  351. package/src/notifications/emit-signal.ts +10 -1
  352. package/src/notifications/home-feed-side-effect.ts +136 -27
  353. package/src/notifications/signal.ts +0 -4
  354. package/src/notifications/types.ts +8 -0
  355. package/src/oauth/connect-orchestrator.ts +3 -0
  356. package/src/oauth/credential-token-resolver.ts +2 -0
  357. package/src/oauth/manual-token-connection.ts +19 -0
  358. package/src/oauth/oauth-store.ts +12 -0
  359. package/src/oauth/platform-connection.test.ts +43 -3
  360. package/src/oauth/platform-connection.ts +13 -4
  361. package/src/oauth/seed-providers.ts +22 -0
  362. package/src/permissions/prompter.ts +5 -2
  363. package/src/permissions/secret-prompter.ts +4 -1
  364. package/src/plugins/defaults/injectors.ts +118 -26
  365. package/src/plugins/external-plugin-loader.ts +82 -10
  366. package/src/plugins/types.ts +16 -7
  367. package/src/prompts/__tests__/system-prompt.test.ts +44 -45
  368. package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
  369. package/src/prompts/normalize-onboarding.ts +40 -0
  370. package/src/prompts/sections.ts +32 -14
  371. package/src/prompts/system-prompt.ts +105 -76
  372. package/src/prompts/template-detection.ts +37 -0
  373. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  374. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  375. package/src/prompts/templates/VOICE.md +3 -0
  376. package/src/prompts/templates/system-sections.ts +51 -10
  377. package/src/providers/__tests__/inference.test.ts +2 -0
  378. package/src/providers/anthropic/client.ts +132 -5
  379. package/src/providers/call-site-routing.ts +24 -6
  380. package/src/providers/connection-resolution.ts +63 -13
  381. package/src/providers/fireworks/client.ts +20 -2
  382. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  383. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  384. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  385. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  386. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  387. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  388. package/src/providers/inference/adapter-factory.ts +24 -21
  389. package/src/providers/inference/auth.ts +15 -3
  390. package/src/providers/inference/backfill.ts +14 -1
  391. package/src/providers/inference/codex-token-refresh.ts +128 -0
  392. package/src/providers/inference/connections.ts +85 -5
  393. package/src/providers/inference/resolve-auth.ts +50 -5
  394. package/src/providers/model-catalog.ts +244 -242
  395. package/src/providers/model-intents.ts +3 -3
  396. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  397. package/src/providers/openai/chat-completions-provider.ts +215 -25
  398. package/src/providers/openai/responses-provider.ts +9 -3
  399. package/src/providers/openrouter/client.ts +46 -4
  400. package/src/providers/platform-proxy/constants.ts +3 -4
  401. package/src/providers/provider-catalog-visibility.ts +3 -1
  402. package/src/providers/provider-send-message.ts +27 -12
  403. package/src/providers/registry.ts +30 -1
  404. package/src/providers/types.ts +25 -0
  405. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  406. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  407. package/src/runtime/agent-wake.ts +212 -57
  408. package/src/runtime/auth/route-policy.ts +20 -3
  409. package/src/runtime/background-job-runner.ts +26 -0
  410. package/src/runtime/channel-reply-delivery.ts +182 -47
  411. package/src/runtime/channel-retry-sweep.ts +141 -16
  412. package/src/runtime/http-server.ts +7 -16
  413. package/src/runtime/http-types.ts +7 -51
  414. package/src/runtime/pending-interactions.ts +51 -8
  415. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  416. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  417. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
  418. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  419. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  420. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  421. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  422. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  423. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  424. package/src/runtime/routes/approval-routes.ts +4 -1
  425. package/src/runtime/routes/channel-availability-routes.ts +5 -0
  426. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  427. package/src/runtime/routes/consolidation-routes.ts +100 -0
  428. package/src/runtime/routes/content-source-routes.ts +78 -0
  429. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  430. package/src/runtime/routes/conversation-query-routes.ts +130 -12
  431. package/src/runtime/routes/conversation-routes.ts +288 -76
  432. package/src/runtime/routes/document-comments-routes.ts +287 -0
  433. package/src/runtime/routes/documents-routes.ts +33 -0
  434. package/src/runtime/routes/home-feed-routes.ts +6 -3
  435. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  436. package/src/runtime/routes/host-browser-routes.ts +8 -1
  437. package/src/runtime/routes/identity-routes.ts +21 -0
  438. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  439. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  440. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  441. package/src/runtime/routes/index.ts +14 -4
  442. package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
  443. package/src/runtime/routes/integrations/a2a.ts +294 -0
  444. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  445. package/src/runtime/routes/log-export-routes.ts +39 -0
  446. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  447. package/src/runtime/routes/notification-routes.ts +19 -2
  448. package/src/runtime/routes/question-routes.ts +4 -1
  449. package/src/runtime/routes/sanity-routes.ts +159 -0
  450. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  451. package/src/runtime/routes/subagents-routes.ts +41 -0
  452. package/src/runtime/services/conversation-serializer.ts +30 -4
  453. package/src/schedule/integration-status.ts +3 -1
  454. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  455. package/src/security/oauth2-device-code.ts +307 -0
  456. package/src/security/oauth2.ts +26 -9
  457. package/src/security/secure-keys.ts +5 -0
  458. package/src/skills/catalog-install.ts +6 -2
  459. package/src/subagent/manager.ts +2 -0
  460. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  461. package/src/tools/browser/browser-execution.ts +93 -0
  462. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  463. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  464. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  465. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  466. package/src/tools/browser/cdp-client/factory.ts +87 -3
  467. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  468. package/src/tools/browser/cdp-client/types.ts +36 -0
  469. package/src/tools/browser/pinned-tabs.ts +90 -0
  470. package/src/tools/document/document-comment-tool.test.ts +379 -0
  471. package/src/tools/document/document-comment-tool.ts +156 -0
  472. package/src/tools/document/document-tool.ts +128 -2
  473. package/src/tools/memory/register.ts +1 -9
  474. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  475. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  476. package/src/tools/network/domain-normalize.ts +17 -0
  477. package/src/tools/network/web-fetch.ts +213 -64
  478. package/src/tools/network/web-search.ts +191 -66
  479. package/src/tools/registry.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +3 -2
  481. package/src/tools/tool-approval-handler.ts +19 -12
  482. package/src/tools/types.ts +41 -2
  483. package/src/tools/ui-surface/definitions.ts +3 -1
  484. package/src/types/onboarding-context.ts +4 -0
  485. package/src/util/__tests__/favicon.test.ts +84 -0
  486. package/src/util/favicon.ts +40 -0
  487. package/src/util/platform.ts +0 -5
  488. package/src/workspace/git-service.ts +75 -4
  489. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  490. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  491. package/src/workspace/migrations/registry.ts +4 -0
  492. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  493. package/src/config/bundled-skills/document/SKILL.md +0 -54
  494. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  495. package/src/daemon/seed-files.ts +0 -18
  496. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  497. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
  498. package/src/runtime/routes/interface-routes.ts +0 -43
  499. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  500. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  501. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  502. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  503. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -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
+ }
@@ -1,4 +1,5 @@
1
1
  import { and, eq, isNull } from "drizzle-orm";
2
+ import { z } from "zod";
2
3
 
3
4
  import type { DrizzleDb } from "../../memory/db-connection.js";
4
5
  import { providerConnections } from "../../memory/schema/inference.js";
@@ -6,6 +7,8 @@ import { clearConnectionProviderCache } from "../registry.js";
6
7
  import {
7
8
  type Auth,
8
9
  AuthSchema,
10
+ type ConnectionModel,
11
+ ConnectionModelSchema,
9
12
  type ConnectionProvider,
10
13
  ConnectionProviderSchema,
11
14
  type ConnectionStatus,
@@ -14,6 +17,23 @@ import {
14
17
  VALID_CONNECTION_PROVIDERS,
15
18
  } from "./auth.js";
16
19
 
20
+ // ---------------------------------------------------------------------------
21
+ // Helpers
22
+ // ---------------------------------------------------------------------------
23
+
24
+ function parseModelsColumn(raw: string | null): ConnectionModel[] | null {
25
+ if (raw === null || raw === "") return null;
26
+ try {
27
+ const parsed = z.array(ConnectionModelSchema).safeParse(JSON.parse(raw));
28
+ return parsed.success ? parsed.data : null;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ export const PROVIDERS_REQUIRING_BASE_URL_AND_MODELS: ReadonlySet<string> =
35
+ new Set(["openai-compatible"]);
36
+
17
37
  // ---------------------------------------------------------------------------
18
38
  // Read
19
39
  // ---------------------------------------------------------------------------
@@ -46,6 +66,8 @@ export function listConnections(
46
66
  provider: provider.data,
47
67
  status,
48
68
  label: row.label ?? null,
69
+ baseUrl: row.baseUrl ?? null,
70
+ models: parseModelsColumn(row.models),
49
71
  isManaged: MANAGED_CONNECTION_NAMES.has(row.name),
50
72
  },
51
73
  ];
@@ -77,6 +99,8 @@ export function getConnection(
77
99
  provider: provider.data,
78
100
  status,
79
101
  label: row.label ?? null,
102
+ baseUrl: row.baseUrl ?? null,
103
+ models: parseModelsColumn(row.models),
80
104
  isManaged: MANAGED_CONNECTION_NAMES.has(row.name),
81
105
  };
82
106
  }
@@ -91,22 +115,30 @@ export type CreateConnectionInput = {
91
115
  auth: Auth;
92
116
  status?: ConnectionStatus;
93
117
  label?: string | null;
118
+ baseUrl?: string | null;
119
+ models?: ConnectionModel[] | null;
94
120
  };
95
121
 
96
122
  export type UpdateConnectionInput = {
97
123
  auth: Auth;
98
124
  status?: ConnectionStatus;
99
125
  label?: string | null;
126
+ baseUrl?: string | null;
127
+ models?: ConnectionModel[] | null;
100
128
  };
101
129
 
102
130
  export type ConnectionCreateError =
103
131
  | { code: "already_exists" }
104
132
  | { code: "invalid_provider"; provider: string }
105
- | { code: "invalid_auth" };
133
+ | { code: "invalid_auth" }
134
+ | { code: "base_url_required" }
135
+ | { code: "models_required" };
106
136
 
107
137
  export type ConnectionUpdateError =
108
138
  | { code: "not_found" }
109
- | { code: "invalid_auth" };
139
+ | { code: "invalid_auth" }
140
+ | { code: "base_url_required" }
141
+ | { code: "models_required" };
110
142
 
111
143
  export type ConnectionDeleteError =
112
144
  | { code: "not_found" }
@@ -143,6 +175,15 @@ export function createConnection(
143
175
 
144
176
  const status = input.status ?? "active";
145
177
  const label = input.label ?? null;
178
+ const baseUrl = input.baseUrl ?? null;
179
+ const models = input.models ?? null;
180
+
181
+ if (PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
182
+ if (!baseUrl) return { ok: false, error: { code: "base_url_required" } };
183
+ if (!models || models.length === 0) {
184
+ return { ok: false, error: { code: "models_required" } };
185
+ }
186
+ }
146
187
 
147
188
  const now = Date.now();
148
189
  db.insert(providerConnections)
@@ -152,6 +193,8 @@ export function createConnection(
152
193
  auth: JSON.stringify(authResult.data),
153
194
  status,
154
195
  label,
196
+ baseUrl,
197
+ models: models === null ? null : JSON.stringify(models),
155
198
  createdAt: now,
156
199
  updatedAt: now,
157
200
  })
@@ -169,6 +212,8 @@ export function createConnection(
169
212
  auth: authResult.data,
170
213
  status,
171
214
  label,
215
+ baseUrl,
216
+ models,
172
217
  createdAt: now,
173
218
  updatedAt: now,
174
219
  isManaged: MANAGED_CONNECTION_NAMES.has(input.name),
@@ -193,15 +238,34 @@ export function updateConnection(
193
238
  return { ok: false, error: { code: "invalid_auth" } };
194
239
  }
195
240
 
241
+ const nextBaseUrl =
242
+ input.baseUrl !== undefined ? input.baseUrl : existing.baseUrl;
243
+ const nextModels =
244
+ input.models !== undefined ? input.models : existing.models;
245
+
246
+ if (PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(existing.provider)) {
247
+ if (!nextBaseUrl)
248
+ return { ok: false, error: { code: "base_url_required" } };
249
+ if (!nextModels || nextModels.length === 0) {
250
+ return { ok: false, error: { code: "models_required" } };
251
+ }
252
+ }
253
+
196
254
  const now = Date.now();
197
255
  const setClause: {
198
256
  auth: string;
199
257
  updatedAt: number;
200
258
  status?: string;
201
259
  label?: string | null;
260
+ baseUrl?: string | null;
261
+ models?: string | null;
202
262
  } = { auth: JSON.stringify(authResult.data), updatedAt: now };
203
263
  if (input.status !== undefined) setClause.status = input.status;
204
264
  if (input.label !== undefined) setClause.label = input.label;
265
+ if (input.baseUrl !== undefined) setClause.baseUrl = input.baseUrl;
266
+ if (input.models !== undefined)
267
+ setClause.models =
268
+ input.models === null ? null : JSON.stringify(input.models);
205
269
 
206
270
  db.update(providerConnections)
207
271
  .set(setClause)
@@ -218,6 +282,8 @@ export function updateConnection(
218
282
  auth: authResult.data,
219
283
  status: input.status !== undefined ? input.status : existing.status,
220
284
  label: input.label !== undefined ? input.label : existing.label,
285
+ baseUrl: nextBaseUrl,
286
+ models: nextModels,
221
287
  updatedAt: now,
222
288
  },
223
289
  };
@@ -294,6 +360,12 @@ const CANONICAL_CONNECTIONS: Array<{
294
360
  auth: { type: "platform" },
295
361
  label: "Google Gemini",
296
362
  },
363
+ {
364
+ name: "fireworks-managed",
365
+ provider: "fireworks",
366
+ auth: { type: "platform" },
367
+ label: "Fireworks",
368
+ },
297
369
  ];
298
370
 
299
371
  /**
@@ -330,7 +402,7 @@ export const MANAGED_CONNECTION_NAMES: ReadonlySet<string> = new Set(
330
402
  *
331
403
  * Status handling: the upsert never touches `status` so user customization
332
404
  * is preserved across reboots. New rows default to `status: "active"` via the
333
- * column default. Off-platform installs flip the three canonical rows to
405
+ * column default. Off-platform installs flip canonical managed rows to
334
406
  * `status: "disabled"` ONCE at hatch time via
335
407
  * `disableManagedConnectionsForByokHatch` (called from `seedInferenceProfiles`
336
408
  * when `isHatch && !isPlatform`); subsequent boots leave whatever the user
@@ -374,7 +446,7 @@ export function seedCanonicalConnections(db: DrizzleDb): void {
374
446
  }
375
447
 
376
448
  /**
377
- * Flip the three canonical managed connections to `status: "disabled"` at
449
+ * Flip canonical managed connections to `status: "disabled"` at
378
450
  * hatch time on BYOK (off-platform) installs.
379
451
  *
380
452
  * Why hatch-time only: managed connections need platform auth that a fresh
@@ -389,10 +461,18 @@ export function seedCanonicalConnections(db: DrizzleDb): void {
389
461
  *
390
462
  * Idempotent: a second hatch (workspace reset) re-disables the rows, which
391
463
  * is the right call — re-hatch means re-onboard.
464
+ *
465
+ * When onboarding explicitly selected a managed profile, callers may exclude
466
+ * that selected connection so the managed route remains usable for the first
467
+ * post-onboarding message.
392
468
  */
393
- export function disableManagedConnectionsForByokHatch(db: DrizzleDb): void {
469
+ export function disableManagedConnectionsForByokHatch(
470
+ db: DrizzleDb,
471
+ options: { excludeConnection?: string } = {},
472
+ ): void {
394
473
  const now = Date.now();
395
474
  for (const name of MANAGED_CONNECTION_NAMES) {
475
+ if (name === options.excludeConnection) continue;
396
476
  db.update(providerConnections)
397
477
  .set({ status: "disabled", updatedAt: now })
398
478
  .where(eq(providerConnections.name, name))
@@ -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 }
@@ -23,9 +28,23 @@ export type ResolveAuthError =
23
28
  export async function resolveAuth(
24
29
  auth: Auth,
25
30
  provider: string,
31
+ opts: { baseUrl?: string | null } = {},
26
32
  ): Promise<
27
33
  { ok: true; resolved: ResolvedAuth } | { ok: false; error: ResolveAuthError }
28
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
+
29
48
  switch (auth.type) {
30
49
  case "api_key": {
31
50
  const value = await getSecureKeyAsync(auth.credential);
@@ -40,6 +59,7 @@ export async function resolveAuth(
40
59
  resolved: {
41
60
  kind: "header",
42
61
  headers: { Authorization: `Bearer ${value}` },
62
+ ...(safeBaseUrl ? { baseUrl: safeBaseUrl } : {}),
43
63
  },
44
64
  };
45
65
  }
@@ -63,7 +83,32 @@ export async function resolveAuth(
63
83
  case "none":
64
84
  return { ok: true, resolved: { kind: "none" } };
65
85
 
66
- 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
+
67
112
  case "service_account":
68
113
  return {
69
114
  ok: false,