@vellumai/assistant 0.8.5 → 0.8.6

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 (544) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/bunfig.toml +6 -1
  4. package/docs/credential-execution-service.md +6 -6
  5. package/docs/plugins.md +4 -3
  6. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  7. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  9. package/openapi.yaml +1900 -166
  10. package/package.json +1 -1
  11. package/src/__tests__/actor-token-service.test.ts +3 -2
  12. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  13. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  14. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  15. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  16. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  17. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/approval-routes-http.test.ts +9 -13
  20. package/src/__tests__/assert-not-live-db.ts +79 -0
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
  22. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  23. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  24. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  25. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  26. package/src/__tests__/btw-routes.test.ts +3 -2
  27. package/src/__tests__/call-controller.test.ts +3 -2
  28. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  29. package/src/__tests__/channel-guardian.test.ts +3 -2
  30. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  31. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  32. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  33. package/src/__tests__/checker.test.ts +12 -12
  34. package/src/__tests__/compaction-events.test.ts +1 -0
  35. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  36. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  37. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  38. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  39. package/src/__tests__/computer-use-tools.test.ts +12 -14
  40. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  41. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  42. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  43. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  44. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  45. package/src/__tests__/config-schema.test.ts +10 -10
  46. package/src/__tests__/connection-model-compat.test.ts +83 -0
  47. package/src/__tests__/contacts-tools.test.ts +3 -2
  48. package/src/__tests__/context-token-estimator.test.ts +22 -0
  49. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  50. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  52. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
  54. package/src/__tests__/conversation-agent-loop.test.ts +488 -2
  55. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  56. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  57. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  58. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  61. package/src/__tests__/conversation-error.test.ts +30 -0
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -8
  63. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  64. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  65. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  66. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  67. package/src/__tests__/conversation-lifecycle.test.ts +1 -0
  68. package/src/__tests__/conversation-list-source.test.ts +3 -2
  69. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  70. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
  71. package/src/__tests__/conversation-pairing.test.ts +53 -0
  72. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  73. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  74. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
  75. package/src/__tests__/conversation-queue.test.ts +333 -291
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  80. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  81. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  82. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  83. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  85. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  90. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  91. package/src/__tests__/conversation-title-service.test.ts +1 -0
  92. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  93. package/src/__tests__/conversation-usage.test.ts +1 -0
  94. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  95. package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
  96. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
  97. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  98. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  99. package/src/__tests__/credential-execution-client.test.ts +72 -1
  100. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  101. package/src/__tests__/credential-health-service.test.ts +252 -3
  102. package/src/__tests__/credential-security-invariants.test.ts +5 -5
  103. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  104. package/src/__tests__/credential-vault.test.ts +5 -5
  105. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  106. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  107. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  108. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  109. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  110. package/src/__tests__/db-test-helpers.ts +58 -0
  111. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  112. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  113. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  114. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  115. package/src/__tests__/dm-persistence.test.ts +26 -40
  116. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  117. package/src/__tests__/document-find-replace.test.ts +3 -2
  118. package/src/__tests__/document-tool-security.test.ts +81 -2
  119. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  120. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  121. package/src/__tests__/encrypted-store.test.ts +11 -9
  122. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  123. package/src/__tests__/filing-service.test.ts +1 -0
  124. package/src/__tests__/first-greeting.test.ts +62 -12
  125. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  126. package/src/__tests__/gemini-provider.test.ts +26 -0
  127. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  128. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  129. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  130. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
  131. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  132. package/src/__tests__/heartbeat-service.test.ts +1 -0
  133. package/src/__tests__/helpers/mock-logger.ts +26 -0
  134. package/src/__tests__/host-bash-routes.test.ts +1 -0
  135. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  136. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  137. package/src/__tests__/host-shell-tool.test.ts +5 -4
  138. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  139. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  140. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  141. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  142. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  143. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  145. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  146. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  148. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  149. package/src/__tests__/llm-resolver.test.ts +331 -0
  150. package/src/__tests__/llm-schema.test.ts +1 -1
  151. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  152. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  153. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  154. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  155. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  156. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  157. package/src/__tests__/model-intents.test.ts +3 -3
  158. package/src/__tests__/native-web-search.test.ts +30 -2
  159. package/src/__tests__/notification-deep-link.test.ts +62 -0
  160. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  161. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  162. package/src/__tests__/oauth-store.test.ts +3 -2
  163. package/src/__tests__/onboarding-template-contract.test.ts +3 -2
  164. package/src/__tests__/openai-provider.test.ts +8 -9
  165. package/src/__tests__/openai-responses-provider.test.ts +70 -10
  166. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  167. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  168. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  169. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  170. package/src/__tests__/plugin-bootstrap.test.ts +9 -11
  171. package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
  172. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  173. package/src/__tests__/process-message-display-content.test.ts +19 -22
  174. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  175. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  176. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  177. package/src/__tests__/recording-handler.test.ts +1 -0
  178. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  179. package/src/__tests__/registry.test.ts +82 -76
  180. package/src/__tests__/relay-server.test.ts +10 -10
  181. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  182. package/src/__tests__/schedule-store.test.ts +16 -1
  183. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  184. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  185. package/src/__tests__/secure-keys.test.ts +3 -3
  186. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  187. package/src/__tests__/server-history-render.test.ts +4 -1
  188. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  189. package/src/__tests__/skill-feature-flags.test.ts +14 -16
  190. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  191. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  192. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  193. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  194. package/src/__tests__/slack-channel-config.test.ts +3 -3
  195. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  196. package/src/__tests__/subagent-disposal.test.ts +27 -8
  197. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  198. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  199. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  200. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  201. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  202. package/src/__tests__/subagent-tools.test.ts +2 -1
  203. package/src/__tests__/suggestion-routes.test.ts +1 -0
  204. package/src/__tests__/system-prompt.test.ts +38 -0
  205. package/src/__tests__/test-preload-verifier.ts +68 -0
  206. package/src/__tests__/test-preload.ts +32 -39
  207. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  208. package/src/__tests__/tool-executor.test.ts +55 -10
  209. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  210. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  211. package/src/__tests__/twilio-routes.test.ts +3 -2
  212. package/src/__tests__/validate-input.test.ts +381 -0
  213. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  214. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  215. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  216. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  217. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  218. package/src/acp/session-manager.ts +5 -6
  219. package/src/agent/loop.ts +80 -0
  220. package/src/api/README.md +124 -2
  221. package/src/api/constants/call-sites.ts +27 -0
  222. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  223. package/src/api/events/assistant-text-delta.ts +32 -0
  224. package/src/api/events/assistant-turn-start.ts +33 -0
  225. package/src/api/events/document-comment-created.ts +48 -0
  226. package/src/api/events/document-comment-deleted.ts +24 -0
  227. package/src/api/events/document-comment-reopened.ts +25 -0
  228. package/src/api/events/document-comment-resolved.ts +27 -0
  229. package/src/api/events/generation-cancelled.ts +24 -0
  230. package/src/api/events/generation-handoff.ts +41 -0
  231. package/src/api/events/message-complete.ts +42 -0
  232. package/src/api/events/open-url.ts +30 -0
  233. package/src/{events → api/events}/relationship-state-updated.ts +3 -3
  234. package/src/api/events/tool-use-start.ts +32 -0
  235. package/src/api/index.ts +128 -3
  236. package/src/api/responses/llm-context-response.ts +39 -0
  237. package/src/api/responses/llm-request-log-entry.ts +93 -0
  238. package/src/api/responses/memory-recall-log.ts +65 -0
  239. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  240. package/src/background-wake/background-wake-routes.test.ts +687 -52
  241. package/src/background-wake/platform-client.test.ts +308 -0
  242. package/src/background-wake/platform-client.ts +167 -0
  243. package/src/background-wake/publisher.ts +91 -0
  244. package/src/background-wake/runtime-registry.ts +2 -2
  245. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  246. package/src/calls/guardian-dispatch.ts +1 -0
  247. package/src/calls/voice-session-bridge.ts +4 -4
  248. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  249. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  250. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  251. package/src/cli/commands/channels/index.ts +229 -0
  252. package/src/cli/commands/memory-v3-render.ts +147 -0
  253. package/src/cli/commands/memory-v3.ts +255 -4
  254. package/src/cli/commands/notifications.ts +365 -55
  255. package/src/cli/lib/open-browser.ts +7 -2
  256. package/src/cli/program.ts +2 -0
  257. package/src/config/assistant-feature-flags.ts +23 -42
  258. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  259. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  260. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  261. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  262. package/src/config/call-site-defaults.ts +1 -1
  263. package/src/config/feature-flag-cache.ts +86 -0
  264. package/src/config/feature-flag-registry.json +17 -17
  265. package/src/config/llm-context-resolution.ts +10 -1
  266. package/src/config/llm-resolver.ts +121 -15
  267. package/src/config/loader.ts +4 -5
  268. package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
  269. package/src/config/schemas/heartbeat.ts +1 -1
  270. package/src/config/schemas/llm.ts +90 -1
  271. package/src/config/schemas/memory-v2.ts +26 -0
  272. package/src/config/schemas/services.ts +6 -2
  273. package/src/config/seed-inference-profiles.ts +36 -16
  274. package/src/context/token-estimator.ts +10 -5
  275. package/src/credential-execution/executable-discovery.ts +40 -0
  276. package/src/credential-execution/process-manager.ts +6 -2
  277. package/src/credential-health/credential-health-service.ts +125 -40
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  279. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  280. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  281. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  282. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  283. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  284. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  285. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  286. package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
  287. package/src/daemon/conversation-agent-loop.ts +132 -28
  288. package/src/daemon/conversation-error.ts +33 -5
  289. package/src/daemon/conversation-messaging.ts +84 -43
  290. package/src/daemon/conversation-process.ts +74 -37
  291. package/src/daemon/conversation-runtime-assembly.ts +29 -9
  292. package/src/daemon/conversation-skill-tools.ts +14 -30
  293. package/src/daemon/conversation-surfaces.ts +69 -34
  294. package/src/daemon/conversation-tool-setup.ts +33 -48
  295. package/src/daemon/conversation.ts +26 -46
  296. package/src/daemon/daemon-control.ts +1 -1
  297. package/src/daemon/daemon-skill-host.ts +9 -2
  298. package/src/daemon/disk-pressure-guard.ts +27 -29
  299. package/src/daemon/first-greeting.ts +31 -13
  300. package/src/daemon/handlers/shared.ts +6 -1
  301. package/src/daemon/lifecycle.ts +12 -12
  302. package/src/daemon/mcp-reload-service.ts +1 -1
  303. package/src/daemon/meet-manifest-loader.ts +10 -17
  304. package/src/daemon/message-types/conversations.ts +20 -22
  305. package/src/daemon/message-types/document-comments.ts +8 -44
  306. package/src/daemon/message-types/home.ts +2 -2
  307. package/src/daemon/message-types/integrations.ts +2 -7
  308. package/src/daemon/message-types/messages.ts +23 -38
  309. package/src/daemon/message-types/subagents.ts +6 -0
  310. package/src/daemon/process-message.ts +9 -9
  311. package/src/daemon/providers-setup.ts +1 -1
  312. package/src/daemon/server.ts +16 -0
  313. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  314. package/src/daemon/tool-setup-types.ts +0 -6
  315. package/src/daemon/wake-target-adapter.ts +10 -0
  316. package/src/documents/document-store.ts +38 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  318. package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
  319. package/src/heartbeat/heartbeat-service.ts +63 -0
  320. package/src/home/__tests__/feed-writer.test.ts +161 -0
  321. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  322. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  323. package/src/home/feed-writer.ts +146 -7
  324. package/src/home/suggested-prompts.ts +27 -145
  325. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  326. package/src/ipc/gateway-client.test.ts +4 -1
  327. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  328. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  329. package/src/ipc/skill-routes/memory.ts +4 -3
  330. package/src/ipc/skill-routes/registries.ts +28 -29
  331. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  332. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  333. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  334. package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
  335. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  336. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  337. package/src/memory/conversation-attention-store.ts +17 -3
  338. package/src/memory/conversation-crud.ts +352 -112
  339. package/src/memory/db-connection.ts +29 -19
  340. package/src/memory/db-init.ts +4 -0
  341. package/src/memory/db-singleton.ts +77 -0
  342. package/src/memory/delivery-channels.ts +82 -0
  343. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  344. package/src/memory/graph/retriever.test.ts +3 -3
  345. package/src/memory/job-handlers/embedding.test.ts +3 -2
  346. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  347. package/src/memory/jobs-worker.ts +12 -1
  348. package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
  349. package/src/memory/llm-request-log-source-local.ts +24 -0
  350. package/src/memory/llm-request-log-source.ts +31 -0
  351. package/src/memory/llm-request-log-store.ts +188 -3
  352. package/src/memory/memory-v2-activation-log-store.ts +95 -1
  353. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  354. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  355. package/src/memory/migrations/index.ts +2 -0
  356. package/src/memory/schema/conversations.ts +9 -1
  357. package/src/memory/schema/inference.ts +0 -1
  358. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  359. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  362. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  363. package/src/memory/v2/harness/metrics.ts +5 -1
  364. package/src/memory/v2/harness/replay-input.ts +19 -3
  365. package/src/memory/v2/harness/runner.ts +6 -0
  366. package/src/memory/v2/harness/trace.ts +6 -0
  367. package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
  368. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  369. package/src/memory/v3/__tests__/edges.test.ts +144 -1
  370. package/src/memory/v3/__tests__/filter.test.ts +48 -0
  371. package/src/memory/v3/__tests__/gate.test.ts +96 -33
  372. package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
  373. package/src/memory/v3/__tests__/loop.test.ts +250 -5
  374. package/src/memory/v3/__tests__/scouts.test.ts +49 -0
  375. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  376. package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
  377. package/src/memory/v3/__tests__/traversal.test.ts +39 -0
  378. package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
  379. package/src/memory/v3/__tests__/validate.test.ts +32 -0
  380. package/src/memory/v3/coretrieval-seed.ts +240 -0
  381. package/src/memory/v3/edges.ts +58 -21
  382. package/src/memory/v3/filter.ts +27 -22
  383. package/src/memory/v3/gate.ts +51 -36
  384. package/src/memory/v3/index-composition.ts +18 -5
  385. package/src/memory/v3/loop.ts +65 -17
  386. package/src/memory/v3/scouts.ts +15 -4
  387. package/src/memory/v3/shadow-diff.ts +287 -0
  388. package/src/memory/v3/shadow-middleware.ts +44 -2
  389. package/src/memory/v3/traversal.ts +6 -1
  390. package/src/memory/v3/tree-walk.ts +6 -1
  391. package/src/memory/v3/validate.ts +56 -33
  392. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  393. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  394. package/src/notifications/adapters/slack.ts +45 -11
  395. package/src/notifications/broadcaster.ts +114 -63
  396. package/src/notifications/conversation-pairing.ts +23 -3
  397. package/src/notifications/decisions-store.ts +32 -1
  398. package/src/notifications/deliveries-store.ts +45 -0
  399. package/src/notifications/edit-notification.ts +201 -0
  400. package/src/notifications/emit-signal.ts +11 -1
  401. package/src/notifications/signal.ts +10 -0
  402. package/src/notifications/types.ts +37 -0
  403. package/src/oauth/byo-connection.test.ts +67 -3
  404. package/src/oauth/byo-connection.ts +32 -5
  405. package/src/oauth/connect-orchestrator.ts +9 -0
  406. package/src/oauth/connection-resolver.test.ts +76 -0
  407. package/src/oauth/connection-resolver.ts +49 -10
  408. package/src/oauth/manual-token-connection.ts +51 -3
  409. package/src/oauth/seed-providers.ts +3 -0
  410. package/src/permissions/approval-policy.test.ts +19 -5
  411. package/src/permissions/approval-policy.ts +14 -3
  412. package/src/permissions/checker.ts +21 -8
  413. package/src/platform/client.test.ts +24 -1
  414. package/src/platform/client.ts +8 -0
  415. package/src/platform/feature-gate.ts +15 -0
  416. package/src/plugins/defaults/injectors.ts +2 -8
  417. package/src/plugins/defaults/persistence.ts +25 -6
  418. package/src/plugins/types.ts +57 -13
  419. package/src/proactive-artifact/job.test.ts +1 -0
  420. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  421. package/src/prompts/system-prompt.ts +38 -40
  422. package/src/prompts/template-detection.ts +10 -4
  423. package/src/prompts/templates/BOOTSTRAP.md +7 -11
  424. package/src/prompts/templates/IDENTITY.md +0 -2
  425. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  426. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  427. package/src/providers/call-site-routing.ts +33 -9
  428. package/src/providers/connection-model-compat.ts +23 -0
  429. package/src/providers/connection-resolution.ts +39 -20
  430. package/src/providers/fireworks/client.ts +1 -0
  431. package/src/providers/gemini/client.ts +24 -3
  432. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  433. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  434. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  435. package/src/providers/inference/auth.ts +0 -8
  436. package/src/providers/inference/connections.ts +3 -66
  437. package/src/providers/inference/resolve-auth.ts +2 -3
  438. package/src/providers/model-catalog.ts +35 -1
  439. package/src/providers/model-intents.ts +3 -3
  440. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  441. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  442. package/src/providers/openai/chat-completions-provider.ts +110 -12
  443. package/src/providers/openai/codex-models.ts +2 -0
  444. package/src/providers/openai/responses-provider.ts +53 -53
  445. package/src/providers/openrouter/client.ts +13 -8
  446. package/src/providers/provider-send-message.ts +18 -9
  447. package/src/providers/registry.ts +48 -8
  448. package/src/providers/retry.ts +16 -4
  449. package/src/providers/search-provider-catalog.ts +17 -9
  450. package/src/providers/types.ts +9 -0
  451. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  452. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  453. package/src/runtime/access-request-helper.ts +1 -0
  454. package/src/runtime/auth/route-policy.ts +10 -0
  455. package/src/runtime/channel-readiness-service.ts +68 -0
  456. package/src/runtime/channel-reply-delivery.ts +23 -0
  457. package/src/runtime/channel-retry-sweep.ts +47 -14
  458. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  459. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  460. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  461. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  462. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  463. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  464. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  465. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  466. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
  467. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  468. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  469. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  470. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  471. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  472. package/src/runtime/routes/app-management-routes.ts +111 -4
  473. package/src/runtime/routes/background-wake-routes.ts +188 -20
  474. package/src/runtime/routes/btw-routes.ts +4 -4
  475. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  476. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  477. package/src/runtime/routes/conversation-list-routes.ts +147 -0
  478. package/src/runtime/routes/conversation-management-routes.ts +39 -14
  479. package/src/runtime/routes/conversation-query-routes.ts +60 -10
  480. package/src/runtime/routes/conversation-routes.ts +186 -140
  481. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  482. package/src/runtime/routes/documents-routes.ts +10 -1
  483. package/src/runtime/routes/group-routes.ts +11 -0
  484. package/src/runtime/routes/home-feed-routes.ts +129 -0
  485. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  486. package/src/runtime/routes/identity-routes.ts +30 -9
  487. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  488. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  489. package/src/runtime/routes/index.ts +2 -0
  490. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  491. package/src/runtime/routes/integrations/vercel.ts +15 -0
  492. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  493. package/src/runtime/routes/memory-v3-routes.ts +160 -2
  494. package/src/runtime/routes/migration-routes.ts +20 -13
  495. package/src/runtime/routes/notification-routes.ts +63 -1
  496. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  497. package/src/runtime/routes/surface-action-routes.ts +1 -38
  498. package/src/runtime/routes/surface-content-routes.ts +12 -5
  499. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  500. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  501. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  502. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  503. package/src/runtime/sync/resource-sync-events.ts +1 -1
  504. package/src/runtime/tool-grant-request-helper.ts +1 -0
  505. package/src/schedule/schedule-store.ts +8 -1
  506. package/src/schedule/scheduler.ts +111 -15
  507. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  508. package/src/security/encrypted-store.ts +7 -16
  509. package/src/security/store-path-override.ts +61 -0
  510. package/src/signals/user-message.ts +5 -8
  511. package/src/skills/validate-input.ts +177 -0
  512. package/src/subagent/manager.ts +13 -13
  513. package/src/subagent/types.ts +6 -0
  514. package/src/tasks/tool-sanitizer.ts +2 -2
  515. package/src/tools/apps/definitions.ts +35 -21
  516. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  517. package/src/tools/computer-use/definitions.ts +268 -266
  518. package/src/tools/document/document-tool.ts +131 -8
  519. package/src/tools/execution-target.ts +2 -5
  520. package/src/tools/executor.ts +18 -55
  521. package/src/tools/host-filesystem/edit.test.ts +1 -0
  522. package/src/tools/host-filesystem/read.test.ts +1 -0
  523. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  524. package/src/tools/host-filesystem/write.test.ts +1 -0
  525. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  526. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  527. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  528. package/src/tools/network/managed-search-proxy.ts +183 -0
  529. package/src/tools/network/web-search.ts +199 -44
  530. package/src/tools/policy-context.ts +3 -1
  531. package/src/tools/registry.ts +146 -103
  532. package/src/tools/schedule/create.ts +1 -1
  533. package/src/tools/skills/skill-tool-factory.ts +17 -36
  534. package/src/tools/subagent/spawn.ts +3 -0
  535. package/src/tools/tool-approval-handler.ts +10 -4
  536. package/src/tools/tool-name-aliases.ts +72 -14
  537. package/src/tools/types.ts +17 -15
  538. package/src/tools/ui-surface/definitions.ts +98 -86
  539. package/src/types/onboarding-context.ts +6 -0
  540. package/src/usage/attribution.ts +32 -1
  541. package/src/util/browser.ts +7 -2
  542. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  543. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  544. package/src/workspace/migrations/registry.ts +4 -0
@@ -21,25 +21,12 @@ import {
21
21
  } from "../util/platform.js";
22
22
  import { stripCommentLines } from "../util/strip-comment-lines.js";
23
23
  import { cleanupBootstrapFiles } from "./bootstrap-cleanup.js";
24
- import {
25
- resolveGuardianPersona,
26
- resolveUserSlug,
27
- } from "./persona-resolver.js";
24
+ import { resolveGuardianPersona, resolveUserSlug } from "./persona-resolver.js";
28
25
  import { renderWorkspaceSections } from "./sections.js";
29
26
  import { isTemplateContent } from "./template-detection.js";
30
27
 
31
28
  export { isTemplateContent };
32
29
 
33
- /**
34
- * Maps onboarding cohort identifiers to their cohort-specific bootstrap
35
- * template filenames. When a cohort key is present in OnboardingContext,
36
- * `maybeReseedBootstrapForCohort` swaps the generic BOOTSTRAP.md with the
37
- * cohort-specific variant — but only if the workspace file is still pristine.
38
- */
39
- const COHORT_BOOTSTRAP_TEMPLATES: Record<string, string> = {
40
- "content-automation": "BOOTSTRAP-CONTENT-AUTOMATION.md",
41
- };
42
-
43
30
  const log = getLogger("system-prompt");
44
31
 
45
32
  const PROMPT_FILES = ["IDENTITY.md", "SOUL.md"] as const;
@@ -215,23 +202,34 @@ export function ensurePromptFiles(): void {
215
202
 
216
203
  /**
217
204
  * One-shot swap: if the workspace BOOTSTRAP.md is still the unmodified generic
218
- * template AND a cohort-specific template exists, overwrite the workspace file
219
- * with the cohort variant. No-op when BOOTSTRAP.md has been deleted, modified,
220
- * or the cohort has no mapped template.
205
+ * template AND a matching template file exists in the bundled templates dir,
206
+ * overwrite the workspace file with the specified variant. No-op when
207
+ * BOOTSTRAP.md has been deleted, modified, or the template file is missing.
221
208
  */
222
- export function maybeReseedBootstrapForCohort(cohort: string): void {
223
- const templateFileName = COHORT_BOOTSTRAP_TEMPLATES[cohort];
224
- if (!templateFileName) return;
209
+ export function maybeReseedBootstrap(templateFileName: string): void {
210
+ // Path traversal guard: reject filenames containing directory separators or
211
+ // parent-directory references, and require a `.md` extension.
212
+ if (
213
+ templateFileName.includes("/") ||
214
+ templateFileName.includes("..") ||
215
+ !templateFileName.endsWith(".md")
216
+ ) {
217
+ log.warn(
218
+ { templateFileName },
219
+ "Rejected bootstrap template filename: invalid characters or extension",
220
+ );
221
+ return;
222
+ }
225
223
 
226
224
  const bootstrapPath = getWorkspacePromptPath("BOOTSTRAP.md");
227
225
  if (!existsSync(bootstrapPath)) return;
228
226
 
229
227
  const currentContent = readPromptFile(bootstrapPath);
230
- // Compare against the GENERIC "BOOTSTRAP.md" template, not the cohort-
231
- // specific one. After the swap, the workspace content no longer matches
232
- // the generic template, so this guard returns false on subsequent calls —
233
- // making the swap idempotent. Do NOT change the comparison target to the
234
- // cohort template filename; that would re-swap on every prompt build.
228
+ // Compare against the GENERIC "BOOTSTRAP.md" template, not the specified
229
+ // one. After the swap, the workspace content no longer matches the generic
230
+ // template, so this guard returns false on subsequent calls — making the
231
+ // swap idempotent. Do NOT change the comparison target to the provided
232
+ // template filename; that would re-swap on every prompt build.
235
233
  if (!isTemplateContent(currentContent, "BOOTSTRAP.md")) return;
236
234
 
237
235
  const templatesDir = resolveBundledDir(
@@ -239,26 +237,26 @@ export function maybeReseedBootstrapForCohort(cohort: string): void {
239
237
  "templates",
240
238
  "templates",
241
239
  );
242
- const cohortTemplatePath = join(templatesDir, templateFileName);
243
- if (!existsSync(cohortTemplatePath)) {
240
+ const templatePath = join(templatesDir, templateFileName);
241
+ if (!existsSync(templatePath)) {
244
242
  log.warn(
245
- { cohort, templateFileName },
246
- "Cohort bootstrap template not found, keeping generic BOOTSTRAP.md",
243
+ { templateFileName },
244
+ "Bootstrap template not found, keeping generic BOOTSTRAP.md",
247
245
  );
248
246
  return;
249
247
  }
250
248
 
251
249
  try {
252
- const cohortContent = readFileSync(cohortTemplatePath, "utf-8");
253
- writeFileSync(bootstrapPath, cohortContent, "utf-8");
250
+ const templateContent = readFileSync(templatePath, "utf-8");
251
+ writeFileSync(bootstrapPath, templateContent, "utf-8");
254
252
  log.info(
255
- { cohort, templateFileName },
256
- "Replaced generic BOOTSTRAP.md with cohort-specific template",
253
+ { templateFileName },
254
+ "Replaced generic BOOTSTRAP.md with specified template",
257
255
  );
258
256
  } catch (err) {
259
257
  log.warn(
260
- { err, cohort, templateFileName },
261
- "Failed to reseed BOOTSTRAP.md for cohort",
258
+ { err, templateFileName },
259
+ "Failed to reseed BOOTSTRAP.md with template",
262
260
  );
263
261
  }
264
262
  }
@@ -280,11 +278,11 @@ export interface BuildSystemPromptOptions {
280
278
  * file-backed bodies, and runtime-computed transforms.
281
279
  */
282
280
  export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
283
- // One-shot cohort swap: if the user has a cohort and BOOTSTRAP.md is still
284
- // the generic template, replace it with the cohort-specific variant before
285
- // the prompt reads the file.
286
- if (options?.onboardingContext?.cohort) {
287
- maybeReseedBootstrapForCohort(options.onboardingContext.cohort);
281
+ // One-shot bootstrap swap: if the onboarding context specifies a bootstrap
282
+ // template and BOOTSTRAP.md is still the generic template, replace it with
283
+ // the specified variant before the prompt reads the file.
284
+ if (options?.onboardingContext?.bootstrapTemplate) {
285
+ maybeReseedBootstrap(options.onboardingContext.bootstrapTemplate);
288
286
  }
289
287
 
290
288
  // Slugs used by the persona sections (`10-user-persona`,
@@ -6,9 +6,15 @@ import { stripCommentLines } from "../util/strip-comment-lines.js";
6
6
 
7
7
  /**
8
8
  * Returns true when the prompt file content is still the unmodified template
9
- * shipped with the daemon. Compares the stripped workspace content against
10
- * the stripped bundled template source so the check stays accurate even if
11
- * templates are edited in future releases.
9
+ * shipped with the daemon. Comment-strips BOTH the workspace content and the
10
+ * bundled template source before comparing, so the check stays accurate even
11
+ * if templates are edited in future releases — and regardless of whether the
12
+ * caller passes raw or already-stripped content. Most callers pre-strip via
13
+ * `readPromptFile`/`stripCommentLines`, but the `08-identity` section
14
+ * transform receives the raw workspace body (comment-stripping happens after
15
+ * the transform in `renderSection`); stripping here keeps detection correct
16
+ * for both. `stripCommentLines` is idempotent, so pre-stripped callers are
17
+ * unaffected.
12
18
  *
13
19
  * Kept in a leaf module so the bundled section registry can depend on it
14
20
  * without forming a cycle through `system-prompt.ts → sections.ts →
@@ -30,7 +36,7 @@ export function isTemplateContent(
30
36
  const templateContent = stripCommentLines(
31
37
  readFileSync(templatePath, "utf-8"),
32
38
  );
33
- return content === templateContent;
39
+ return stripCommentLines(content) === templateContent;
34
40
  } catch {
35
41
  return false;
36
42
  }
@@ -36,19 +36,17 @@ Match their energy, not just their format. Lowercase and terse gets lowercase an
36
36
 
37
37
  Don't present options and ask what they'd prefer. That reads as hedging. Given what you know, pick the most useful path and say why. Wrong is recoverable. Vague isn't.
38
38
 
39
- ### Google scan (when connected)
39
+ ### Gmail scan (when connected)
40
40
 
41
- If the First-Run User Context says "Google connected: yes" and the user asks you to scan, you MUST actually call the `subagent_spawn` tool three times — once per service. Do not simulate, summarize, or render progress components without making real tool calls. The scan requires live API access; you cannot know the results without executing the tools.
41
+ If the First-Run User Context says "Google connected: yes" and lists Gmail access, and the user asks you to scan Gmail, you MUST actually call the `subagent_spawn` tool. Do not simulate, summarize, or render progress components without making a real tool call. The scan requires live API access; you cannot know the results without executing the tool.
42
42
 
43
- Call `subagent_spawn` three times with these parameters. Each subagent must produce two clearly separated sections in its output — `## Profile Signals` (structured facts for user modeling) and `## Action Briefing` (narrative, prioritized, noise-filtered):
43
+ Call `subagent_spawn` with `label: "gmail-scan"` and this objective. The subagent must produce two clearly separated sections — `## Profile Signals` (structured facts for user modeling) and `## Action Briefing` (narrative, prioritized, noise-filtered):
44
44
 
45
- 1. `label: "gmail-scan"`, `objective: "Scan my Gmail from the last 7 days. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from email patterns:\n- Top contacts (5-10 people I email most, with relationship context — colleague, manager, client, etc.)\n- Dominant domains/companies appearing in my inbox\n- Initiate-vs-respond ratio (do I start threads or mostly reply?)\n- Recurring topics or threads\n- Role indicators (e.g. manages people, IC, external-facing, sales, engineering)\n\n## Action Briefing\nEmails that need a human response from me, ordered by urgency. Skip marketing, automated notifications, and newsletters entirely. For each actionable email: who sent it, subject, why it needs my attention, and how urgent it is. If nothing needs action, say so — an empty inbox is a valid signal."`
46
- 2. `label: "calendar-scan"`, `objective: "Scan my Google Calendar — 7 days back and 7 days forward. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from calendar patterns:\n- Recurring meeting rhythm (daily standups, weekly 1:1s, bi-weekly syncs, etc.)\n- Meeting type ratio: 1:1 vs group vs external\n- Most-frequent attendees (top 5-10 people)\n- Role signals from meeting patterns (e.g. has direct reports if lots of 1:1s, cross-functional if diverse attendee pools, manager if in skip-levels)\n\n## Action Briefing\nNext 72 hours: prep-worthy meetings (what to prepare, who's attending, context from recent related meetings), scheduling conflicts, and back-to-backs worth noting. Past 7 days: recent meetings with likely pending follow-ups or unresolved action items. Prioritize — don't just list every event."`
47
- 3. `label: "drive-scan"`, `objective: "Scan my Google Drive — focus on shared-with-me activity and folder structure rather than just recently modified files. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from Drive patterns:\n- Top-level folder organization (what categories/projects exist)\n- File type distribution (docs, sheets, slides, etc.)\n- Shared drives and team folders the user belongs to\n- Files shared by others in the last 30 days (who shared them and what types)\n\n## Action Briefing\nFiles shared with me in the last 7 days I haven't opened yet, docs with outstanding comments or suggestions directed at me, and any docs where I'm tagged but haven't responded. If Drive activity is low, say so explicitly — 'not much Drive activity this period' is a valid and useful signal, not something to pad with filler."`
45
+ `objective: "Scan my Gmail from the last 7 days. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from email patterns:\n- Top contacts (5-10 people I email most, with relationship context — colleague, manager, client, etc.)\n- Dominant domains/companies appearing in my inbox\n- Initiate-vs-respond ratio (do I start threads or mostly reply?)\n- Recurring topics or threads\n- Role indicators (e.g. manages people, IC, external-facing, sales, engineering)\n\n## Action Briefing\nEmails that need a human response from me, ordered by urgency. Skip marketing, automated notifications, and newsletters entirely. For each actionable email: who sent it, subject, why it needs my attention, and how urgent it is. If nothing needs action, say so — an empty inbox is a valid signal."`
48
46
 
49
- After spawning, tell the user the scans are running in the background and continue the conversation normally. Do not wait or poll — you will be notified automatically when each subagent completes.
47
+ After spawning, tell the user the scan is running in the background and continue the conversation normally. Do not wait or poll — you will be notified automatically when the subagent completes.
50
48
 
51
- When subagent completion notifications arrive, use `subagent_read` to get results, then synthesize — don't just list. First, merge the `## Profile Signals` sections from all completed scans into an initial picture of the user: their role, key people, work patterns, and communication style. Use this to calibrate your tone and what you reference going forward. Then lead with 1–3 actionable insights from the `## Action Briefing` sections that connect dots across sources, and offer to do something concrete about each one. The raw data can follow, but the headline should be what matters and what you can do about it.
49
+ When the subagent completion notification arrives, use `subagent_read` to get results, then synthesize — don't just list. First, use `## Profile Signals` to form an initial picture of the user: their role, key people, work patterns, and communication style. Use this to calibrate your tone and what you reference going forward. Then lead with 1–3 actionable insights from `## Action Briefing` and offer to do something concrete about each one. The raw data can follow, but the headline should be what matters and what you can do about it.
52
50
 
53
51
  If the user doesn't ask for a scan, don't offer it again. The greeting already mentioned it.
54
52
 
@@ -120,9 +118,7 @@ If nothing comes up, don't force it.
120
118
 
121
119
  ## Assistant migration
122
120
 
123
- If the First-Run User Context lists prior AI assistants, gently mention after the initial greeting and rapport, not as an opener that you can help bring over anything they built with their previous assistant: memory, skills, workflows, integrations. Frame it as an offer, not a push: "I noticed you've used [X] before if you've built anything there you'd like to bring over, I can help with that whenever you're ready." Only proceed if the user expresses interest. Do not load or activate the assistant-migration skill preemptively.
124
-
125
- If no prior assistants are listed, skip this entirely.
121
+ Help first: do the user's actual first task before pivoting to this — the migration offer must never derail or precede real work. Then, at the first natural opening, offer it. This applies to every new assistant, whether onboarding was full, condensed, or absent: land it early in the first conversation, at the first lull or seam, even when the user came in task-first. Keep it to one light offer, a useful shortcut and not an upsell: "If you've already built context, prompts, skills, or workflows somewhere else, I can help port them over." Prior context may live in ChatGPT, Claude, OpenClaw, Hermes, or another assistant or harness — name those when it helps. If they decline, drop it and don't re-offer. Only if they opt in, use the `assistant-migration` skill to inventory and port that context; do not load or activate migration-related skills preemptively.
126
122
 
127
123
  ## Wrap up
128
124
 
@@ -2,8 +2,6 @@ _ Lines starting with _ are comments - they won't appear in the system prompt
2
2
 
3
3
  # IDENTITY.md
4
4
 
5
- This file is yours. Add sections, restructure it, make it reflect who you are. Name, Emoji, Role, Personality are parsed by the app - keep their `- **Label:**` format. Everything else is freeform.
6
-
7
5
  - **Name:** _(not yet chosen)_
8
6
  - **Emoji:** _(not yet chosen)_
9
7
  - **Nature:** _(not yet established)_
@@ -46,13 +46,15 @@ describe("isConnectionCompatibleWithModel", () => {
46
46
  test("oauth_subscription connection is incompatible with a non-Codex model", () => {
47
47
  const conn = { auth: oauthAuth };
48
48
  expect(isConnectionCompatibleWithModel(conn, "gpt-5")).toBe(false);
49
- expect(isConnectionCompatibleWithModel(conn, "gpt-5.5")).toBe(false);
50
49
  expect(isConnectionCompatibleWithModel(conn, "gpt-5.4-nano")).toBe(false);
50
+ expect(isConnectionCompatibleWithModel(conn, "gpt-5.5-pro")).toBe(false);
51
51
  });
52
52
 
53
53
  test("oauth_subscription connection is compatible with a Codex model", () => {
54
54
  const conn = { auth: oauthAuth };
55
+ expect(isConnectionCompatibleWithModel(conn, "gpt-5.5")).toBe(true);
55
56
  expect(isConnectionCompatibleWithModel(conn, "gpt-5.4")).toBe(true);
57
+ expect(isConnectionCompatibleWithModel(conn, "gpt-5.4-mini")).toBe(true);
56
58
  expect(isConnectionCompatibleWithModel(conn, "gpt-5.3-codex")).toBe(true);
57
59
  });
58
60
 
@@ -89,7 +91,6 @@ mock.module("../../memory/db-connection.js", () => ({
89
91
  type Connection = {
90
92
  name: string;
91
93
  provider: string;
92
- status: string;
93
94
  auth: { type: string; credential?: string };
94
95
  };
95
96
 
@@ -140,13 +141,11 @@ function reset(): void {
140
141
  const OPENAI_KEY: Connection = {
141
142
  name: "openai-key",
142
143
  provider: "openai",
143
- status: "active",
144
144
  auth: { type: "api_key", credential: "credential/openai" },
145
145
  };
146
146
  const OPENAI_CODEX: Connection = {
147
147
  name: "openai-codex",
148
148
  provider: "openai",
149
- status: "active",
150
149
  auth: {
151
150
  type: "oauth_subscription",
152
151
  credential: "credential/openai-codex/access_token",
@@ -0,0 +1,122 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { type LLMConfigBase, LLMSchema } from "../../config/schemas/llm.js";
4
+ import type { ProviderConnection } from "../inference/auth.js";
5
+ import type { ProvidersConfig } from "../registry.js";
6
+
7
+ const adapterCalls: Array<{
8
+ connection: ProviderConnection;
9
+ opts: { model: string; useNativeWebSearch?: boolean };
10
+ }> = [];
11
+
12
+ mock.module("../inference/resolve-auth.js", () => ({
13
+ resolveAuth: async () => ({
14
+ ok: true,
15
+ resolved: {
16
+ kind: "header",
17
+ headers: { Authorization: "Bearer test-provider-key" },
18
+ },
19
+ }),
20
+ }));
21
+
22
+ mock.module("../inference/adapter-factory.js", () => ({
23
+ buildProviderAdapter: () => null,
24
+ createAdapterFromConnection: (
25
+ connection: ProviderConnection,
26
+ _resolvedAuth: unknown,
27
+ opts: { model: string; useNativeWebSearch?: boolean },
28
+ ) => {
29
+ adapterCalls.push({ connection, opts });
30
+ return {
31
+ name: connection.provider,
32
+ sendMessage: async () => ({
33
+ content: [],
34
+ model: opts.model,
35
+ usage: { inputTokens: 0, outputTokens: 0 },
36
+ stopReason: "stop",
37
+ }),
38
+ };
39
+ },
40
+ }));
41
+
42
+ import {
43
+ clearConnectionProviderCache,
44
+ resolveProviderFromConnection,
45
+ } from "../registry.js";
46
+
47
+ function makeConfig(): ProvidersConfig {
48
+ const baseLlm = LLMSchema.parse({});
49
+ return {
50
+ services: {
51
+ inference: {},
52
+ "image-generation": {
53
+ mode: "your-own",
54
+ provider: "gemini",
55
+ model: "gemini-3.1-flash-image-preview",
56
+ },
57
+ "web-search": {
58
+ mode: "managed",
59
+ provider: "inference-provider-native",
60
+ },
61
+ },
62
+ llm: {
63
+ ...baseLlm,
64
+ default: {
65
+ ...baseLlm.default,
66
+ provider: "openrouter" as LLMConfigBase["provider"],
67
+ model: "x-ai/grok-4.20-beta",
68
+ },
69
+ },
70
+ };
71
+ }
72
+
73
+ const openRouterConnection: ProviderConnection = {
74
+ name: "openrouter-personal",
75
+ provider: "openrouter",
76
+ auth: { type: "api_key", credential: "credential/openrouter/api_key" },
77
+ label: "OpenRouter",
78
+ baseUrl: null,
79
+ models: null,
80
+ createdAt: 1,
81
+ updatedAt: 1,
82
+ isManaged: false,
83
+ };
84
+
85
+ describe("resolveProviderFromConnection native web search selection", () => {
86
+ beforeEach(() => {
87
+ adapterCalls.length = 0;
88
+ clearConnectionProviderCache();
89
+ });
90
+
91
+ test("uses the routed OpenRouter Anthropic model when enabling native web search", async () => {
92
+ await resolveProviderFromConnection(openRouterConnection, makeConfig(), {
93
+ model: "anthropic/claude-opus-4-7",
94
+ });
95
+
96
+ expect(adapterCalls).toHaveLength(1);
97
+ expect(adapterCalls[0].opts).toMatchObject({
98
+ model: "anthropic/claude-opus-4-7",
99
+ useNativeWebSearch: true,
100
+ });
101
+ });
102
+
103
+ test("keeps OpenRouter native web search model-specific across cached connections", async () => {
104
+ await resolveProviderFromConnection(openRouterConnection, makeConfig(), {
105
+ model: "x-ai/grok-4.20-beta",
106
+ });
107
+ await resolveProviderFromConnection(openRouterConnection, makeConfig(), {
108
+ model: "anthropic/claude-opus-4-7",
109
+ });
110
+
111
+ expect(adapterCalls.map((call) => call.opts)).toEqual([
112
+ expect.objectContaining({
113
+ model: "x-ai/grok-4.20-beta",
114
+ useNativeWebSearch: false,
115
+ }),
116
+ expect.objectContaining({
117
+ model: "anthropic/claude-opus-4-7",
118
+ useNativeWebSearch: true,
119
+ }),
120
+ ]);
121
+ });
122
+ });
@@ -24,7 +24,10 @@ import { AsyncLocalStorage } from "node:async_hooks";
24
24
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
25
25
  import { getConfig } from "../config/loader.js";
26
26
  import { getDb } from "../memory/db-connection.js";
27
- import { isConnectionCompatibleWithModel } from "./connection-model-compat.js";
27
+ import {
28
+ describeSubscriptionModelIncompatibility,
29
+ isConnectionCompatibleWithModel,
30
+ } from "./connection-model-compat.js";
28
31
  import {
29
32
  ConnectionResolutionError,
30
33
  tryResolveProviderForConnectionName,
@@ -146,25 +149,32 @@ export class CallSiteRoutingProvider implements Provider {
146
149
  if (!callSite) return this.defaultProvider;
147
150
 
148
151
  const overrideProfile = options?.config?.overrideProfile;
152
+ // Forward the per-conversation mix seed so transport selection picks the
153
+ // same mix arm as wire-param normalization in `retry.ts` — otherwise a mix
154
+ // spanning providers could route the transport to a different arm than the
155
+ // request params.
156
+ const selectionSeed = options?.config?.selectionSeed;
149
157
  const resolved = resolveCallSiteConfig(callSite, getConfig().llm, {
150
158
  overrideProfile,
159
+ selectionSeed,
151
160
  });
152
161
 
153
162
  let connectionName = resolved.provider_connection;
154
163
 
155
164
  // When no connection is set and the provider differs from the default,
156
- // auto-resolve to an active connection for the provider (handles the
157
- // "Any active X connection" case where the profile set provider but
158
- // not provider_connection, and the merge didn't inherit one).
165
+ // auto-resolve a connection for the provider (handles the case where the
166
+ // profile set provider but not provider_connection, and the merge didn't
167
+ // inherit one).
168
+ let autoResolveCandidates:
169
+ | import("./inference/auth.js").ProviderConnection[]
170
+ | undefined;
159
171
  if (!connectionName && resolved.provider !== this.defaultProvider.name) {
160
172
  try {
161
- const candidates = listConnections(getDb(), {
173
+ autoResolveCandidates = listConnections(getDb(), {
162
174
  provider: resolved.provider,
163
175
  });
164
- const active = candidates.find(
165
- (c) =>
166
- c.status === "active" &&
167
- isConnectionCompatibleWithModel(c, resolved.model),
176
+ const active = autoResolveCandidates.find((c) =>
177
+ isConnectionCompatibleWithModel(c, resolved.model),
168
178
  );
169
179
  if (active) {
170
180
  connectionName = active.name;
@@ -188,6 +198,20 @@ export class CallSiteRoutingProvider implements Provider {
188
198
  return this.defaultProvider;
189
199
  }
190
200
 
201
+ if (autoResolveCandidates) {
202
+ const incompatMsg = describeSubscriptionModelIncompatibility(
203
+ autoResolveCandidates,
204
+ resolved.model,
205
+ );
206
+ if (incompatMsg) {
207
+ throw new ConnectionResolutionError(
208
+ "<resolved-callsite>",
209
+ "model_incompatible",
210
+ incompatMsg,
211
+ );
212
+ }
213
+ }
214
+
191
215
  throw new ConnectionResolutionError(
192
216
  "<resolved-callsite>",
193
217
  "missing_connection",
@@ -36,3 +36,26 @@ export function isConnectionCompatibleWithModel(
36
36
  if (!model) return true;
37
37
  return isCodexSubscriptionModel(model);
38
38
  }
39
+
40
+ /**
41
+ * When auto-resolution found candidates but none were model-compatible,
42
+ * return a user-facing explanation if the incompatibility is specifically
43
+ * due to all candidates being `oauth_subscription` (ChatGPT) connections.
44
+ *
45
+ * Returns `null` when the incompatibility has a different cause (callers
46
+ * should fall through to their existing generic error).
47
+ */
48
+ export function describeSubscriptionModelIncompatibility(
49
+ candidates: Pick<ProviderConnection, "auth">[],
50
+ model: string | undefined,
51
+ ): string | null {
52
+ if (!model || candidates.length === 0) return null;
53
+ if (candidates.some((c) => isConnectionCompatibleWithModel(c, model))) {
54
+ return null;
55
+ }
56
+ const allSubscription = candidates.every(
57
+ (c) => c.auth.type === "oauth_subscription",
58
+ );
59
+ if (!allSubscription) return null;
60
+ return `Model "${model}" isn't available through your ChatGPT subscription. Select a supported model or add an OpenAI API key connection.`;
61
+ }
@@ -30,7 +30,10 @@
30
30
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
31
31
  import { getDb } from "../memory/db-connection.js";
32
32
  import { getLogger } from "../util/logger.js";
33
- import { isConnectionCompatibleWithModel } from "./connection-model-compat.js";
33
+ import {
34
+ describeSubscriptionModelIncompatibility,
35
+ isConnectionCompatibleWithModel,
36
+ } from "./connection-model-compat.js";
34
37
  import { getConnection, listConnections } from "./inference/connections.js";
35
38
  import type { ProvidersConfig } from "./registry.js";
36
39
  import { resolveProviderFromConnection } from "./registry.js";
@@ -52,7 +55,8 @@ export class ConnectionResolutionError extends Error {
52
55
  | "lookup_failed"
53
56
  | "not_found"
54
57
  | "provider_mismatch"
55
- | "missing_connection",
58
+ | "missing_connection"
59
+ | "model_incompatible",
56
60
  message: string,
57
61
  public readonly cause?: unknown,
58
62
  ) {
@@ -116,12 +120,12 @@ export async function tryResolveProviderForConnectionName(
116
120
  // "anthropic-managed" leaked through). Try to find an active connection
117
121
  // for the expected provider before giving up.
118
122
  let resolved = false;
123
+ let mismatchCandidates: import("./inference/auth.js").ProviderConnection[] | undefined;
119
124
  try {
120
125
  const db = getDb();
121
- const candidates = listConnections(db, { provider: expectedProvider });
122
- const active = candidates.find(
123
- (c) =>
124
- c.status === "active" && isConnectionCompatibleWithModel(c, model),
126
+ mismatchCandidates = listConnections(db, { provider: expectedProvider });
127
+ const active = mismatchCandidates.find((c) =>
128
+ isConnectionCompatibleWithModel(c, model),
125
129
  );
126
130
  if (active) {
127
131
  log.info(
@@ -130,7 +134,7 @@ export async function tryResolveProviderForConnectionName(
130
134
  resolvedConnection: active.name,
131
135
  expectedProvider,
132
136
  },
133
- "Auto-resolved stale provider_connection to matching active connection",
137
+ "Auto-resolved stale provider_connection to matching connection",
134
138
  );
135
139
  connection = active;
136
140
  resolved = true;
@@ -139,6 +143,16 @@ export async function tryResolveProviderForConnectionName(
139
143
  // DB not available — fall through to the original error.
140
144
  }
141
145
  if (!resolved) {
146
+ const incompatMsg = mismatchCandidates
147
+ ? describeSubscriptionModelIncompatibility(mismatchCandidates, model)
148
+ : null;
149
+ if (incompatMsg) {
150
+ throw new ConnectionResolutionError(
151
+ connectionName,
152
+ "model_incompatible",
153
+ incompatMsg,
154
+ );
155
+ }
142
156
  throw new ConnectionResolutionError(
143
157
  connectionName,
144
158
  "provider_mismatch",
@@ -146,13 +160,6 @@ export async function tryResolveProviderForConnectionName(
146
160
  );
147
161
  }
148
162
  }
149
- if (connection.status === "disabled") {
150
- log.debug(
151
- { connectionName, provider: connection.provider },
152
- "provider_connection is disabled — returning null",
153
- );
154
- return null;
155
- }
156
163
  // `resolveProviderFromConnection` reaches into auth resolution (credential
157
164
  // reads, managed-proxy context). A transient failure there is a soft
158
165
  // miss — log and return null so the caller can treat it the same as
@@ -160,7 +167,7 @@ export async function tryResolveProviderForConnectionName(
160
167
  // catch is specifically for in-flight failures that should not take
161
168
  // dispatch offline.
162
169
  try {
163
- return await resolveProviderFromConnection(connection, config);
170
+ return await resolveProviderFromConnection(connection, config, { model });
164
171
  } catch (err) {
165
172
  log.warn(
166
173
  { err, connectionName },
@@ -196,15 +203,14 @@ export async function resolveDefaultProvider(
196
203
  // provider without a connection ("Any active" selection), and the merge
197
204
  // cleared or failed to inherit one. Try to find an active connection
198
205
  // for the provider before giving up.
206
+ let autoResolveCandidates: import("./inference/auth.js").ProviderConnection[] | undefined;
199
207
  if (resolved.provider) {
200
208
  try {
201
- const candidates = listConnections(getDb(), {
209
+ autoResolveCandidates = listConnections(getDb(), {
202
210
  provider: resolved.provider,
203
211
  });
204
- const active = candidates.find(
205
- (c) =>
206
- c.status === "active" &&
207
- isConnectionCompatibleWithModel(c, resolved.model),
212
+ const active = autoResolveCandidates.find((c) =>
213
+ isConnectionCompatibleWithModel(c, resolved.model),
208
214
  );
209
215
  if (active) {
210
216
  log.info(
@@ -218,6 +224,19 @@ export async function resolveDefaultProvider(
218
224
  }
219
225
  }
220
226
  if (!connectionName) {
227
+ const incompatMsg = autoResolveCandidates
228
+ ? describeSubscriptionModelIncompatibility(
229
+ autoResolveCandidates,
230
+ resolved.model,
231
+ )
232
+ : null;
233
+ if (incompatMsg) {
234
+ throw new ConnectionResolutionError(
235
+ "<llm.default>",
236
+ "model_incompatible",
237
+ incompatMsg,
238
+ );
239
+ }
221
240
  throw new ConnectionResolutionError(
222
241
  "<llm.default>",
223
242
  "missing_connection",
@@ -34,6 +34,7 @@ export class FireworksProvider extends OpenAIChatCompletionsProvider {
34
34
  // overrides (e.g. DeepSeek V4 → "max") come from
35
35
  // {@link resolveMaxReasoningEffort}.
36
36
  maxReasoningEffort: "high",
37
+ assistantReasoningField: "reasoning_content",
37
38
  });
38
39
  }
39
40
 
@@ -4,6 +4,7 @@ import { ApiError, GoogleGenAI, ThinkingLevel } from "@google/genai";
4
4
  import { isAbortReason } from "../../util/abort-reasons.js";
5
5
  import { ProviderError } from "../../util/errors.js";
6
6
  import { getLogger } from "../../util/logger.js";
7
+ import { PROVIDER_CATALOG } from "../model-catalog.js";
7
8
  import { createStreamTimeout } from "../stream-timeout.js";
8
9
  import type {
9
10
  ContentBlock,
@@ -74,6 +75,24 @@ function buildThinkingConfig(
74
75
  return Object.keys(result).length > 0 ? result : undefined;
75
76
  }
76
77
 
78
+ /**
79
+ * Whether the active Gemini model accepts a `thinkingConfig`. Non-thinking
80
+ * models (e.g. `gemini-2.5-flash-lite`) reject thinking params, and gemini is
81
+ * in `THINKING_AWARE_PROVIDERS` so `providers/retry.ts` no longer strips them —
82
+ * so we gate on the catalog's `supportsThinking` capability here. Unknown or
83
+ * uncatalogued models default to allowing thinking config (preserving prior
84
+ * behavior); only an explicit `supportsThinking: false` suppresses it.
85
+ */
86
+ function geminiModelSupportsThinking(model: string): boolean {
87
+ const normalized = model.startsWith("models/")
88
+ ? model.slice("models/".length)
89
+ : model;
90
+ const catalogModel = PROVIDER_CATALOG.find(
91
+ (provider) => provider.id === "gemini",
92
+ )?.models.find((m) => m.id === normalized);
93
+ return catalogModel?.supportsThinking !== false;
94
+ }
95
+
77
96
  function stripGeminiHttpOptions(
78
97
  config: genai.GenerateContentConfig,
79
98
  ): genai.GenerateContentConfig {
@@ -215,10 +234,12 @@ export class GeminiProvider implements Provider {
215
234
  const usageAttributionHeaders = configObj?.usageAttributionHeaders as
216
235
  | Record<string, string>
217
236
  | undefined;
218
- const thinkingConfig = buildThinkingConfig(
219
- configObj?.thinking as Record<string, unknown> | undefined,
220
- );
221
237
  const activeModel = modelOverride ?? this.model;
238
+ const thinkingConfig = geminiModelSupportsThinking(activeModel)
239
+ ? buildThinkingConfig(
240
+ configObj?.thinking as Record<string, unknown> | undefined,
241
+ )
242
+ : undefined;
222
243
 
223
244
  try {
224
245
  const geminiContents = this.toGeminiContents(messages, activeModel);