@vellumai/assistant 0.12.0 → 0.12.1-dev.202609141913.5e16607

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 (441) hide show
  1. package/AGENTS.md +5 -1
  2. package/ARCHITECTURE.md +11 -12
  3. package/docs/architecture/turn-actor.md +15 -8
  4. package/docs/guardian-request-flow.md +16 -0
  5. package/openapi.yaml +292 -50
  6. package/package.json +1 -1
  7. package/scripts/smoke-desktop-window-theme.ts +186 -0
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  9. package/src/__tests__/app-compiler.test.ts +6 -0
  10. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  11. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  12. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  13. package/src/__tests__/computer-use-tools.test.ts +17 -5
  14. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  15. package/src/__tests__/config-schema.test.ts +28 -29
  16. package/src/__tests__/config-watcher.test.ts +23 -8
  17. package/src/__tests__/connection-model-compat.test.ts +2 -2
  18. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  19. package/src/__tests__/conversation-pairing.test.ts +26 -0
  20. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  21. package/src/__tests__/conversation-queue.test.ts +48 -207
  22. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  23. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  24. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  25. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  26. package/src/__tests__/conversation-title-service.test.ts +14 -10
  27. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  28. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  29. package/src/__tests__/credential-record-retire.test.ts +171 -1
  30. package/src/__tests__/credential-routes.test.ts +212 -3
  31. package/src/__tests__/credential-security-invariants.test.ts +4 -2
  32. package/src/__tests__/cu-unified-flow.test.ts +8 -2
  33. package/src/__tests__/document-sync-tags.test.ts +25 -0
  34. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  35. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  36. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  37. package/src/__tests__/git-porcelain.test.ts +47 -0
  38. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  39. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  40. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  42. package/src/__tests__/host-file-proxy.test.ts +11 -23
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  44. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  45. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  47. package/src/__tests__/mcp-health-check.test.ts +8 -11
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  49. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  50. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  51. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  52. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  53. package/src/__tests__/model-intents.test.ts +3 -0
  54. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  55. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  56. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  57. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  58. package/src/__tests__/plugin-import-boundary-guard.test.ts +13 -0
  59. package/src/__tests__/resolve-app.test.ts +135 -0
  60. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  61. package/src/__tests__/service-account-token.test.ts +247 -0
  62. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  63. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  64. package/src/__tests__/source-fingerprint.test.ts +184 -0
  65. package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
  66. package/src/__tests__/subagent-tools.test.ts +3 -2
  67. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  68. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  69. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  70. package/src/__tests__/workspace-git-service.test.ts +26 -0
  71. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  72. package/src/acp/__tests__/agent-process.test.ts +258 -4
  73. package/src/acp/__tests__/auth-required.test.ts +96 -0
  74. package/src/acp/__tests__/client-handler.test.ts +90 -2
  75. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  76. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  77. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  78. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  79. package/src/acp/__tests__/session-manager.test.ts +895 -4
  80. package/src/acp/agent-process.test.ts +4 -1
  81. package/src/acp/agent-process.ts +94 -9
  82. package/src/acp/auth-required.ts +50 -1
  83. package/src/acp/auto-install.test.ts +9 -7
  84. package/src/acp/client-handler.ts +16 -3
  85. package/src/acp/model-config.test.ts +166 -0
  86. package/src/acp/model-config.ts +138 -0
  87. package/src/acp/resolve-agent.test.ts +220 -6
  88. package/src/acp/resolve-agent.ts +88 -23
  89. package/src/acp/session-manager.test.ts +196 -3
  90. package/src/acp/session-manager.ts +441 -18
  91. package/src/acp/types.ts +50 -5
  92. package/src/api/events/acp-session-model-update.test.ts +88 -0
  93. package/src/api/events/acp-session-model-update.ts +46 -0
  94. package/src/api/events/app-files-changed.ts +4 -3
  95. package/src/api/events/notification-intent.ts +2 -0
  96. package/src/api/index.ts +6 -0
  97. package/src/apps/app-store.ts +1 -1
  98. package/src/apps/resolve-app.ts +81 -0
  99. package/src/apps/source-fingerprint.ts +266 -0
  100. package/src/bundler/app-compiler.ts +15 -0
  101. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  102. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  103. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  104. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  105. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  106. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  107. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  108. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  109. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  110. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  111. package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
  112. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  113. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  114. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  115. package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
  116. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  117. package/src/calls/barge-in-guard.ts +118 -0
  118. package/src/calls/call-constants.ts +24 -4
  119. package/src/calls/call-controller.ts +499 -199
  120. package/src/calls/call-transport.ts +16 -0
  121. package/src/calls/media-stream-output.ts +32 -0
  122. package/src/calls/media-stream-server.ts +82 -30
  123. package/src/calls/media-stream-stt-session.ts +15 -0
  124. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  125. package/src/calls/resolve-call-tts-provider.ts +33 -2
  126. package/src/calls/tts-call-strategy.ts +9 -5
  127. package/src/calls/types.ts +2 -0
  128. package/src/calls/voice-control-protocol.ts +53 -21
  129. package/src/calls/voice-leg-coordinator.ts +189 -0
  130. package/src/calls/voice-progress-cadence.ts +423 -0
  131. package/src/calls/voice-session-bridge.ts +59 -46
  132. package/src/calls/voice-triage-escalate.ts +175 -56
  133. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  134. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  135. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  136. package/src/cli/commands/apps.help.ts +67 -13
  137. package/src/cli/commands/apps.ts +183 -0
  138. package/src/cli/commands/bash.help.ts +8 -13
  139. package/src/cli/commands/bash.ts +7 -25
  140. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  141. package/src/cli/commands/channels/index.help.ts +70 -2
  142. package/src/cli/commands/channels/index.ts +16 -3
  143. package/src/cli/commands/channels/send.ts +128 -0
  144. package/src/cli/commands/credentials.help.ts +6 -2
  145. package/src/cli/commands/credentials.ts +2 -2
  146. package/src/cli/commands/mcp.help.ts +8 -8
  147. package/src/cli/commands/mcp.ts +1 -1
  148. package/src/cli/commands/roadmap.help.ts +5 -1
  149. package/src/cli/commands/roadmap.ts +1 -1
  150. package/src/cli/lib/bundled-marketplace.json +103 -90
  151. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  152. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  153. package/src/config/__tests__/platform-identity.test.ts +255 -0
  154. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  155. package/src/config/acp-agent-write.ts +43 -0
  156. package/src/config/acp-defaults.test.ts +13 -3
  157. package/src/config/acp-defaults.ts +23 -6
  158. package/src/config/acp-schema.test.ts +81 -0
  159. package/src/config/acp-schema.ts +30 -2
  160. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  161. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  162. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  163. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  164. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  165. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  166. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  167. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  168. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  169. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  170. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  171. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  172. package/src/config/default-profile-catalog.ts +1 -1
  173. package/src/config/env.ts +2 -2
  174. package/src/config/feature-flag-registry.json +24 -12
  175. package/src/config/loader.ts +1 -0
  176. package/src/config/memory-v3-gate.test.ts +31 -0
  177. package/src/config/memory-v3-gate.ts +20 -16
  178. package/src/config/platform-identity.ts +201 -0
  179. package/src/config/platform-rehydration.ts +11 -27
  180. package/src/config/schema.ts +2 -2
  181. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  182. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  183. package/src/config/schemas/calls.ts +1 -28
  184. package/src/config/schemas/live-voice.ts +1 -173
  185. package/src/config/schemas/mcp.ts +1 -1
  186. package/src/config/schemas/memory-retrospective.ts +10 -0
  187. package/src/config/schemas/services.ts +8 -0
  188. package/src/config/schemas/skills.ts +0 -14
  189. package/src/config/schemas/voice.ts +150 -0
  190. package/src/credential-execution/process-manager.test.ts +18 -19
  191. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  192. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  193. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  194. package/src/daemon/app-change-notify.ts +45 -0
  195. package/src/daemon/config-watcher.ts +13 -194
  196. package/src/daemon/conversation-process.ts +35 -64
  197. package/src/daemon/conversation-skill-tools.ts +13 -31
  198. package/src/daemon/conversation-store.ts +1 -1
  199. package/src/daemon/conversation-surfaces.ts +10 -8
  200. package/src/daemon/conversation-tool-setup.ts +37 -43
  201. package/src/daemon/conversation.ts +0 -3
  202. package/src/daemon/host-bash-proxy.ts +8 -12
  203. package/src/daemon/host-browser-proxy.ts +15 -1
  204. package/src/daemon/host-file-proxy.ts +8 -10
  205. package/src/daemon/host-transfer-proxy.ts +14 -18
  206. package/src/daemon/lifecycle.ts +0 -5
  207. package/src/daemon/mcp-reload-service.ts +5 -5
  208. package/src/daemon/message-types/sync.ts +1 -0
  209. package/src/daemon/orphan-reaper.ts +1 -1
  210. package/src/daemon/providers-setup.ts +1 -1
  211. package/src/daemon/shutdown-handlers.ts +1 -9
  212. package/src/daemon/skill-memory-refresh.ts +64 -0
  213. package/src/daemon/tool-side-effects.ts +7 -18
  214. package/src/desktop/__tests__/fake-desktop.ts +119 -0
  215. package/src/desktop/desktop-chrome-policy.test.ts +71 -0
  216. package/src/desktop/desktop-chrome-policy.ts +42 -0
  217. package/src/desktop/desktop-dependencies.test.ts +102 -0
  218. package/src/desktop/desktop-dependencies.ts +306 -0
  219. package/src/desktop/desktop-feature.test.ts +18 -0
  220. package/src/desktop/desktop-feature.ts +19 -0
  221. package/src/desktop/desktop-panel-config.test.ts +135 -0
  222. package/src/desktop/desktop-panel-config.ts +149 -0
  223. package/src/desktop/desktop-session-manager.test.ts +627 -0
  224. package/src/desktop/desktop-session-manager.ts +814 -0
  225. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  226. package/src/desktop/desktop-stream-bridge.ts +214 -0
  227. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  228. package/src/desktop/desktop-wallpaper.ts +113 -0
  229. package/src/desktop/desktop-window-theme.ts +170 -0
  230. package/src/home/__tests__/feed-writer.test.ts +22 -0
  231. package/src/home/feed-writer.ts +11 -2
  232. package/src/hooks/types.ts +33 -0
  233. package/src/i18n/AGENTS.md +22 -0
  234. package/src/i18n/__tests__/i18n.test.ts +108 -0
  235. package/src/i18n/index.ts +16 -0
  236. package/src/i18n/locales.ts +93 -0
  237. package/src/i18n/messages.ts +61 -0
  238. package/src/i18n/title.ts +25 -0
  239. package/src/inbound/platform-callback-registration.ts +4 -5
  240. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  241. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  242. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  243. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  244. package/src/live-voice/live-voice-session.ts +213 -693
  245. package/src/live-voice/protocol.ts +0 -2
  246. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  247. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  248. package/src/mcp/effective-config.ts +3 -3
  249. package/src/mcp/mcp-header-store.ts +29 -45
  250. package/src/mcp/spec-schema.ts +111 -0
  251. package/src/mcp/startup.ts +8 -4
  252. package/src/mcp/workspace-mcp-config.ts +188 -0
  253. package/src/messaging/provider.ts +10 -3
  254. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  255. package/src/messaging/providers/callback-routing.ts +18 -0
  256. package/src/messaging/providers/channel-transport.ts +56 -0
  257. package/src/messaging/providers/discord/transport.ts +17 -0
  258. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  259. package/src/messaging/providers/slack/adapter.ts +7 -44
  260. package/src/messaging/providers/slack/transport.ts +16 -0
  261. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  262. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  263. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  264. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  265. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  266. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  267. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  268. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  269. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  270. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  271. package/src/notifications/adapters/macos.ts +3 -0
  272. package/src/notifications/assistant-reply-producer.ts +8 -3
  273. package/src/notifications/conversation-pairing.ts +36 -16
  274. package/src/notifications/delivered-post-record.ts +11 -1
  275. package/src/notifications/guardian-feed-projection.ts +125 -5
  276. package/src/notifications/home-feed-side-effect.ts +55 -7
  277. package/src/notifications/schedule-result-producer.ts +66 -22
  278. package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
  279. package/src/oauth/byo-connection.test.ts +45 -0
  280. package/src/oauth/byo-connection.ts +36 -6
  281. package/src/oauth/connection-resolver.test.ts +73 -0
  282. package/src/oauth/connection-resolver.ts +95 -2
  283. package/src/oauth/seed-providers.ts +165 -0
  284. package/src/outbound-proxy/certs.ts +3 -5
  285. package/src/permissions/checker.test.ts +11 -6
  286. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  287. package/src/persistence/conversation-crud.ts +71 -63
  288. package/src/persistence/conversation-lineage.ts +12 -4
  289. package/src/persistence/conversation-title-placeholders.ts +23 -0
  290. package/src/persistence/conversation-title-service.ts +9 -26
  291. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  292. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  293. package/src/persistence/message-cursor.ts +78 -0
  294. package/src/persistence/message-reads.ts +30 -0
  295. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  296. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  297. package/src/persistence/schema/memory-core.ts +5 -0
  298. package/src/platform/client.test.ts +4 -2
  299. package/src/platform/client.ts +2 -10
  300. package/src/plugin-api/constants.ts +2 -0
  301. package/src/plugin-api/index.ts +1 -0
  302. package/src/plugin-api/types.ts +2 -0
  303. package/src/plugins/defaults/index.ts +2 -0
  304. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  305. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  306. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  307. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  308. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  309. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  310. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  311. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  312. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  313. package/src/plugins/defaults/memory/graph/capability-seed.ts +4 -6
  314. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  315. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  316. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  317. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  318. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  319. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  320. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  321. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  322. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  323. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  324. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  325. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  326. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  327. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  328. package/src/plugins/defaults/memory/startup.ts +6 -1
  329. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  330. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  331. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +89 -0
  332. package/src/plugins/defaults/memory/worker.ts +6 -0
  333. package/src/plugins/mcp-servers.ts +15 -75
  334. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  335. package/src/providers/gemini/client.ts +9 -23
  336. package/src/providers/gemini-thought-signature.test.ts +63 -0
  337. package/src/providers/gemini-thought-signature.ts +44 -0
  338. package/src/providers/inference/auth.ts +2 -3
  339. package/src/providers/inference/connection-availability.ts +27 -8
  340. package/src/providers/inference/resolve-auth.ts +22 -4
  341. package/src/providers/inference/service-account-token.ts +248 -0
  342. package/src/providers/model-catalog.ts +34 -0
  343. package/src/providers/model-intents.ts +10 -5
  344. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  345. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  346. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  347. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  348. package/src/providers/openai/chat-completions-provider.ts +192 -78
  349. package/src/providers/openai/codex-models.ts +0 -4
  350. package/src/providers/openai/google-thought-signature.ts +320 -0
  351. package/src/runtime/AGENTS.md +2 -0
  352. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  353. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  354. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  355. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  356. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  357. package/src/runtime/auth/same-actor.ts +38 -3
  358. package/src/runtime/channel-send.ts +316 -0
  359. package/src/runtime/http-server.ts +160 -151
  360. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  361. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
  362. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  363. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  364. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  365. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  366. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  367. package/src/runtime/routes/acp-routes.test.ts +15 -0
  368. package/src/runtime/routes/acp-routes.ts +72 -9
  369. package/src/runtime/routes/app-management-routes.ts +73 -0
  370. package/src/runtime/routes/app-routes.ts +1 -1
  371. package/src/runtime/routes/channel-send-routes.ts +85 -0
  372. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  373. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  374. package/src/runtime/routes/credential-routes.ts +19 -10
  375. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  376. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  377. package/src/runtime/routes/diagnostics-routes.ts +55 -3
  378. package/src/runtime/routes/index.ts +4 -2
  379. package/src/runtime/routes/log-export-routes.ts +2 -0
  380. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  381. package/src/runtime/routes/migration-routes.ts +6 -20
  382. package/src/runtime/routes/platform-routes.ts +18 -15
  383. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  384. package/src/runtime/services/conversation-serializer.ts +16 -3
  385. package/src/runtime/sync/resource-sync-events.ts +2 -1
  386. package/src/schedule/worker.ts +1 -1
  387. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  388. package/src/security/ces-http-record-backend.ts +120 -0
  389. package/src/security/ces-rpc-record-backend.ts +7 -0
  390. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  391. package/src/skills/workspace-skill-md-signature.ts +50 -0
  392. package/src/subagent/consult-context.ts +12 -25
  393. package/src/tools/acp/list-agents.test.ts +3 -1
  394. package/src/tools/acp/spawn.test.ts +146 -11
  395. package/src/tools/acp/spawn.ts +21 -3
  396. package/src/tools/acp/status.test.ts +94 -0
  397. package/src/tools/acp/status.ts +40 -3
  398. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  399. package/src/tools/capability-offer.test.ts +22 -2
  400. package/src/tools/capability-offer.ts +16 -1
  401. package/src/tools/computer-use/definitions.ts +4 -2
  402. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  403. package/src/tools/credentials/metadata-store.ts +100 -0
  404. package/src/tools/host-filesystem/edit.test.ts +2 -1
  405. package/src/tools/host-filesystem/edit.ts +2 -2
  406. package/src/tools/host-filesystem/read.test.ts +2 -1
  407. package/src/tools/host-filesystem/read.ts +2 -2
  408. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  409. package/src/tools/host-filesystem/transfer.ts +2 -2
  410. package/src/tools/host-filesystem/write.test.ts +2 -1
  411. package/src/tools/host-filesystem/write.ts +2 -2
  412. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  413. package/src/tools/host-terminal/host-shell.ts +4 -20
  414. package/src/tools/policy-context.ts +6 -7
  415. package/src/tools/subagent/spawn.ts +5 -8
  416. package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
  417. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  418. package/src/tools/terminal/safe-env.ts +23 -3
  419. package/src/tools/terminal/sanitized-bash.ts +105 -0
  420. package/src/tools/watch/watch-retro-report.ts +0 -8
  421. package/src/util/__tests__/language-subtag.test.ts +23 -1
  422. package/src/util/ca-bundle.ts +13 -0
  423. package/src/util/language-subtag.ts +16 -0
  424. package/src/util/platform.ts +8 -0
  425. package/src/workspace/git-porcelain.ts +66 -0
  426. package/src/workspace/git-service.ts +5 -54
  427. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  428. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  429. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  430. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  431. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  432. package/src/workspace/migrations/registry.ts +6 -0
  433. package/src/__tests__/app-source-watcher.test.ts +0 -218
  434. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  435. package/src/__tests__/speaker-identification.test.ts +0 -56
  436. package/src/calls/speaker-identification.ts +0 -233
  437. package/src/daemon/app-source-watcher.ts +0 -218
  438. package/src/messaging/providers/whatsapp/client.ts +0 -29
  439. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  440. package/src/util/fs-watcher-error.ts +0 -36
  441. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -17,6 +17,7 @@ import {
17
17
  mediaSourceByteLength,
18
18
  resolveMediaReferences,
19
19
  } from "../media-resolve.js";
20
+ import { supportsForcedToolChoiceWithThinking } from "../model-catalog.js";
20
21
  import { PLACEHOLDER_EMPTY_TURN } from "../placeholder-sentinels.js";
21
22
  import { recordProviderRequestDiagnostics } from "../request-diagnostics.js";
22
23
  import { createStreamTimeout } from "../stream-timeout.js";
@@ -27,6 +28,7 @@ import type {
27
28
  Provider,
28
29
  ProviderResponse,
29
30
  SendMessageOptions,
31
+ ToolUseContent,
30
32
  } from "../types.js";
31
33
  import {
32
34
  ContextOverflowError,
@@ -51,6 +53,15 @@ import {
51
53
  coerceObjectParamsToJsonString,
52
54
  decodeCoercedObjectArgs,
53
55
  } from "./coerce-object-args.js";
56
+ import {
57
+ applyGemini3UnsignedToolCallFallback,
58
+ attachGoogleThoughtSignatureIfNeeded,
59
+ classifyGoogleThoughtSignatureRetry,
60
+ geminiThoughtSignaturesByToolCallId,
61
+ type GoogleToolCallExtraContent,
62
+ thoughtSignatureFromToolUseMetadata,
63
+ toolUseMetadataFromChatCompletionsDelta,
64
+ } from "./google-thought-signature.js";
54
65
  import {
55
66
  isOpenAICompatInlineAudio,
56
67
  OPENAI_COMPAT_MAX_INLINE_AUDIO_BYTES,
@@ -204,7 +215,13 @@ const log = getLogger("chat-completions");
204
215
  /** Wire-level reasoning_effort values. The OpenAI SDK type doesn't include
205
216
  * `"max"`, but Fireworks accepts it for DeepSeek V4; the assignment to
206
217
  * `params.reasoning_effort` casts through this union. */
207
- export type ReasoningEffortWire = "none" | "low" | "medium" | "high" | "xhigh" | "max";
218
+ export type ReasoningEffortWire =
219
+ | "none"
220
+ | "low"
221
+ | "medium"
222
+ | "high"
223
+ | "xhigh"
224
+ | "max";
208
225
 
209
226
  const REASONING_EFFORT_RANK: Record<ReasoningEffortWire, number> = {
210
227
  none: 0,
@@ -388,9 +405,15 @@ export function isThinkingEnabledOnWire(params: unknown): boolean {
388
405
  * rejected it because thinking/reasoning mode forbids that parameter.
389
406
  * DeepSeek thinking mode 400s with `Thinking mode does not support this
390
407
  * tool_choice` for any explicit value, including `"auto"` and `"none"`.
391
- * One retry without `tool_choice` lets the same provider succeed instead of
392
- * failing over to a different backend.
408
+ * Kimi 400s with `tool_choice 'specified' is incompatible with thinking
409
+ * enabled`. One retry without `tool_choice` lets the same provider succeed
410
+ * instead of failing over to a different backend.
393
411
  */
412
+ const THINKING_MODE_TOOL_CHOICE_REJECTION_PATTERNS: RegExp[] = [
413
+ /does not support this tool_choice/i,
414
+ /tool_choice\s+'specified'\s+is incompatible with thinking/i,
415
+ ];
416
+
394
417
  function isThinkingModeToolChoiceRejection(
395
418
  error: unknown,
396
419
  params: unknown,
@@ -402,8 +425,9 @@ function isThinkingModeToolChoiceRejection(
402
425
  if (!isClientErrorStatus(error)) {
403
426
  return false;
404
427
  }
405
- return /does not support this tool_choice/i.test(
406
- openaiCompatErrorHaystack(error),
428
+ const haystack = openaiCompatErrorHaystack(error);
429
+ return THINKING_MODE_TOOL_CHOICE_REJECTION_PATTERNS.some((pattern) =>
430
+ pattern.test(haystack),
407
431
  );
408
432
  }
409
433
 
@@ -541,8 +565,10 @@ function isMissingReasoningContentRejection(
541
565
 
542
566
  /**
543
567
  * True when the request included an assistant `reasoning` / `reasoning_content`
544
- * extra and the provider rejected it as an unknown message property. One retry
545
- * without those extras lets a strict Chat Completions schema succeed.
568
+ * extra and the provider rejected it as an unknown or unsupported message
569
+ * property. One retry without those extras lets a strict Chat Completions
570
+ * schema succeed. Groq phrases this as
571
+ * `property 'reasoning_content' is unsupported`.
546
572
  */
547
573
  function isUnknownAssistantReasoningFieldRejection(
548
574
  error: unknown,
@@ -561,7 +587,7 @@ function isUnknownAssistantReasoningFieldRejection(
561
587
  if (!haystackNamesAssistantReasoningField(haystack)) {
562
588
  return false;
563
589
  }
564
- return /unknown|unexpected|unrecognized|additional propert|extra (?:field|property)|not (?:a )?valid|invalid (?:argument|parameter|field|property)/i.test(
590
+ return /unknown|unexpected|unrecognized|unsupported|not supported|additional propert|extra (?:field|property)|not (?:a )?valid|invalid (?:argument|parameter|field|property)/i.test(
565
591
  haystack,
566
592
  );
567
593
  }
@@ -634,6 +660,82 @@ function flattenContentPartsToStrings(params: unknown): boolean {
634
660
  return flattened;
635
661
  }
636
662
 
663
+ type OpenAICompatRetryKind =
664
+ | "reasoning-opt-out"
665
+ | "thinking-tool-choice"
666
+ | "missing-reasoning-content"
667
+ | "unknown-reasoning-field"
668
+ | "missing-thought-signature"
669
+ | "unknown-extra-content"
670
+ | "chat-template";
671
+
672
+ function classifyOpenAICompatRetry(
673
+ error: unknown,
674
+ params: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming,
675
+ thoughtSignaturesByCallId: ReadonlyMap<string, string>,
676
+ ): { kind: OpenAICompatRetryKind; message: string; apply: () => void } | null {
677
+ if (isReasoningOptOutRejection(error, params)) {
678
+ return {
679
+ kind: "reasoning-opt-out",
680
+ message:
681
+ "Model rejected the explicit reasoning opt-out; retrying without reasoning params",
682
+ apply: () => {
683
+ delete params.reasoning_effort;
684
+ delete (params as unknown as Record<string, unknown>).reasoning;
685
+ },
686
+ };
687
+ }
688
+ if (isThinkingModeToolChoiceRejection(error, params)) {
689
+ return {
690
+ kind: "thinking-tool-choice",
691
+ message:
692
+ "Upstream rejected tool_choice in thinking mode; retrying without tool_choice",
693
+ apply: () => {
694
+ delete params.tool_choice;
695
+ },
696
+ };
697
+ }
698
+ if (isMissingReasoningContentRejection(error, params)) {
699
+ return {
700
+ kind: "missing-reasoning-content",
701
+ message:
702
+ "Upstream requires reasoning_content round-trip; retrying with empty field on assistant messages",
703
+ apply: () => {
704
+ backfillEmptyReasoningContent(params);
705
+ },
706
+ };
707
+ }
708
+ if (isUnknownAssistantReasoningFieldRejection(error, params)) {
709
+ return {
710
+ kind: "unknown-reasoning-field",
711
+ message:
712
+ "Upstream rejected assistant reasoning field; retrying without it",
713
+ apply: () => {
714
+ stripAssistantReasoningFields(params);
715
+ },
716
+ };
717
+ }
718
+ const thoughtSignatureRetry = classifyGoogleThoughtSignatureRetry(params, {
719
+ isClientError: isClientErrorStatus(error),
720
+ haystack: openaiCompatErrorHaystack(error),
721
+ capturedByCallId: thoughtSignaturesByCallId,
722
+ });
723
+ if (thoughtSignatureRetry) {
724
+ return thoughtSignatureRetry;
725
+ }
726
+ if (isChatTemplateRejection(error, params)) {
727
+ return {
728
+ kind: "chat-template",
729
+ message:
730
+ "Upstream chat template rejected structured message content; retrying with flattened plain-text content",
731
+ apply: () => {
732
+ flattenContentPartsToStrings(params);
733
+ },
734
+ };
735
+ }
736
+ return null;
737
+ }
738
+
637
739
  /**
638
740
  * Translate the neutral (Anthropic-shaped) `tool_choice` carried on the call
639
741
  * config into the OpenAI chat-completions wire format. Callers express
@@ -774,10 +876,13 @@ export class OpenAIChatCompletionsProvider implements Provider {
774
876
  const coercedObjectKeys = new Map<string, string[]>();
775
877
 
776
878
  try {
879
+ const thoughtSignaturesByCallId =
880
+ geminiThoughtSignaturesByToolCallId(messages);
777
881
  const openaiMessages = await this.toOpenAIMessages(
778
882
  messages,
779
883
  systemPrompt,
780
884
  requestSupportsInlineAudio(modelOverride ?? this.model),
885
+ modelOverride ?? this.model,
781
886
  );
782
887
 
783
888
  recordProviderRequestDiagnostics({
@@ -874,7 +979,18 @@ export class OpenAIChatCompletionsProvider implements Provider {
874
979
  const thinkingOn = isThinkingEnabledOnWire(params);
875
980
  const skipAutoDefault = thinkingOn && toolChoice === "auto";
876
981
  const skipAllChoices = thinkingOn && this.omitToolChoiceWhenReasoning;
877
- if (!skipAutoDefault && !skipAllChoices) {
982
+ const skipIncompatibleForcedChoice =
983
+ thinkingOn &&
984
+ !supportsForcedToolChoiceWithThinking(
985
+ this.name,
986
+ modelOverride ?? this.model,
987
+ ) &&
988
+ (toolChoice === "required" || typeof toolChoice === "object");
989
+ if (
990
+ !skipAutoDefault &&
991
+ !skipAllChoices &&
992
+ !skipIncompatibleForcedChoice
993
+ ) {
878
994
  params.tool_choice = toolChoice;
879
995
  }
880
996
  }
@@ -973,7 +1089,12 @@ export class OpenAIChatCompletionsProvider implements Provider {
973
1089
 
974
1090
  const toolCallMap = new Map<
975
1091
  number,
976
- { id: string; name: string; args: string }
1092
+ {
1093
+ id: string;
1094
+ name: string;
1095
+ args: string;
1096
+ providerMetadata?: ToolUseContent["providerMetadata"];
1097
+ }
977
1098
  >();
978
1099
  const toolProgress = createToolProgressEmitter(onEvent);
979
1100
  let finishReason = "unknown";
@@ -1001,68 +1122,31 @@ export class OpenAIChatCompletionsProvider implements Provider {
1001
1122
  ? { headers: requestHeaders }
1002
1123
  : {}),
1003
1124
  });
1125
+ const attemptedCompatRetries = new Set<OpenAICompatRetryKind>();
1004
1126
  let stream: Awaited<ReturnType<typeof createStream>>;
1005
- try {
1006
- stream = await createStream();
1007
- } catch (error) {
1008
- if (isReasoningOptOutRejection(error, params)) {
1009
- log.warn(
1010
- {
1011
- provider: this.name,
1012
- model: modelOverride ?? this.model,
1013
- error: error instanceof Error ? error.message : String(error),
1014
- },
1015
- "Model rejected the explicit reasoning opt-out; retrying without reasoning params",
1016
- );
1017
- delete params.reasoning_effort;
1018
- delete (params as unknown as Record<string, unknown>).reasoning;
1127
+ for (;;) {
1128
+ try {
1019
1129
  stream = await createStream();
1020
- } else if (isThinkingModeToolChoiceRejection(error, params)) {
1021
- log.warn(
1022
- {
1023
- provider: this.name,
1024
- model: modelOverride ?? this.model,
1025
- error: error instanceof Error ? error.message : String(error),
1026
- },
1027
- "Upstream rejected tool_choice in thinking mode; retrying without tool_choice",
1028
- );
1029
- delete params.tool_choice;
1030
- stream = await createStream();
1031
- } else if (isMissingReasoningContentRejection(error, params)) {
1032
- log.warn(
1033
- {
1034
- provider: this.name,
1035
- model: modelOverride ?? this.model,
1036
- error: error instanceof Error ? error.message : String(error),
1037
- },
1038
- "Upstream requires reasoning_content round-trip; retrying with empty field on assistant messages",
1130
+ break;
1131
+ } catch (error) {
1132
+ const retry = classifyOpenAICompatRetry(
1133
+ error,
1134
+ params,
1135
+ thoughtSignaturesByCallId,
1039
1136
  );
1040
- backfillEmptyReasoningContent(params);
1041
- stream = await createStream();
1042
- } else if (isUnknownAssistantReasoningFieldRejection(error, params)) {
1043
- log.warn(
1044
- {
1045
- provider: this.name,
1046
- model: modelOverride ?? this.model,
1047
- error: error instanceof Error ? error.message : String(error),
1048
- },
1049
- "Upstream rejected assistant reasoning field; retrying without it",
1050
- );
1051
- stripAssistantReasoningFields(params);
1052
- stream = await createStream();
1053
- } else if (isChatTemplateRejection(error, params)) {
1137
+ if (!retry || attemptedCompatRetries.has(retry.kind)) {
1138
+ throw error;
1139
+ }
1140
+ attemptedCompatRetries.add(retry.kind);
1054
1141
  log.warn(
1055
1142
  {
1056
1143
  provider: this.name,
1057
1144
  model: modelOverride ?? this.model,
1058
1145
  error: error instanceof Error ? error.message : String(error),
1059
1146
  },
1060
- "Upstream chat template rejected structured message content; retrying with flattened plain-text content",
1147
+ retry.message,
1061
1148
  );
1062
- flattenContentPartsToStrings(params);
1063
- stream = await createStream();
1064
- } else {
1065
- throw error;
1149
+ retry.apply();
1066
1150
  }
1067
1151
  }
1068
1152
 
@@ -1150,6 +1234,14 @@ export class OpenAIChatCompletionsProvider implements Provider {
1150
1234
  entry.args,
1151
1235
  );
1152
1236
  }
1237
+ const providerMetadata =
1238
+ toolUseMetadataFromChatCompletionsDelta(tc);
1239
+ if (
1240
+ providerMetadata &&
1241
+ !thoughtSignatureFromToolUseMetadata(entry.providerMetadata)
1242
+ ) {
1243
+ entry.providerMetadata = providerMetadata;
1244
+ }
1153
1245
  }
1154
1246
  }
1155
1247
 
@@ -1271,6 +1363,9 @@ export class OpenAIChatCompletionsProvider implements Provider {
1271
1363
  id: tc.id,
1272
1364
  name: tc.name,
1273
1365
  input,
1366
+ ...(tc.providerMetadata
1367
+ ? { providerMetadata: tc.providerMetadata }
1368
+ : {}),
1274
1369
  });
1275
1370
  }
1276
1371
 
@@ -1284,11 +1379,19 @@ export class OpenAIChatCompletionsProvider implements Provider {
1284
1379
  content: contentText || null,
1285
1380
  tool_calls:
1286
1381
  toolCallMap.size > 0
1287
- ? Array.from(toolCallMap.values()).map((tc) => ({
1288
- id: tc.id,
1289
- type: "function",
1290
- function: { name: tc.name, arguments: tc.args },
1291
- }))
1382
+ ? Array.from(toolCallMap.values()).map((tc) =>
1383
+ attachGoogleThoughtSignatureIfNeeded(
1384
+ {
1385
+ id: tc.id,
1386
+ type: "function",
1387
+ function: { name: tc.name, arguments: tc.args },
1388
+ },
1389
+ thoughtSignatureFromToolUseMetadata(
1390
+ tc.providerMetadata,
1391
+ ),
1392
+ modelOverride ?? this.model,
1393
+ ),
1394
+ )
1292
1395
  : undefined,
1293
1396
  },
1294
1397
  finish_reason: finishReason,
@@ -1492,6 +1595,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
1492
1595
  messages: Message[],
1493
1596
  systemPrompt?: string,
1494
1597
  audioInputEnabled = false,
1598
+ model = this.model,
1495
1599
  ): Promise<OpenAI.Chat.Completions.ChatCompletionMessageParam[]> {
1496
1600
  // Swap any persisted attachment references back to inline base64 before
1497
1601
  // serializing, so the block transforms below can read `source.data`.
@@ -1513,7 +1617,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
1513
1617
  const emittedToolCallIds = new Set<string>();
1514
1618
  for (const msg of messages) {
1515
1619
  if (msg.role === "assistant") {
1516
- const assistantMessage = this.toOpenAIAssistantMessage(msg);
1620
+ const assistantMessage = this.toOpenAIAssistantMessage(msg, model);
1517
1621
  for (const toolCall of assistantMessage.tool_calls ?? []) {
1518
1622
  emittedToolCallIds.add(toolCall.id);
1519
1623
  }
@@ -1591,11 +1695,14 @@ export class OpenAIChatCompletionsProvider implements Provider {
1591
1695
  /** Convert an assistant message with text + tool_use blocks to OpenAI format. */
1592
1696
  private toOpenAIAssistantMessage(
1593
1697
  msg: Message,
1698
+ model: string,
1594
1699
  ): OpenAI.Chat.Completions.ChatCompletionAssistantMessageParam {
1595
1700
  const textParts: string[] = [];
1596
1701
  const reasoningParts: string[] = [];
1597
- const toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] =
1598
- [];
1702
+ const toolCalls: Array<
1703
+ OpenAI.Chat.Completions.ChatCompletionMessageToolCall &
1704
+ GoogleToolCallExtraContent
1705
+ > = [];
1599
1706
 
1600
1707
  for (const block of msg.content) {
1601
1708
  switch (block.type) {
@@ -1609,14 +1716,20 @@ export class OpenAIChatCompletionsProvider implements Provider {
1609
1716
  }
1610
1717
  break;
1611
1718
  case "tool_use":
1612
- toolCalls.push({
1613
- id: block.id,
1614
- type: "function",
1615
- function: {
1616
- name: block.name,
1617
- arguments: JSON.stringify(block.input),
1618
- },
1619
- });
1719
+ toolCalls.push(
1720
+ attachGoogleThoughtSignatureIfNeeded(
1721
+ {
1722
+ id: block.id,
1723
+ type: "function",
1724
+ function: {
1725
+ name: block.name,
1726
+ arguments: JSON.stringify(block.input),
1727
+ },
1728
+ },
1729
+ thoughtSignatureFromToolUseMetadata(block.providerMetadata),
1730
+ model,
1731
+ ),
1732
+ );
1620
1733
  break;
1621
1734
  case "server_tool_use":
1622
1735
  textParts.push(`[Web search: ${block.name}]`);
@@ -1635,6 +1748,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
1635
1748
  };
1636
1749
 
1637
1750
  if (toolCalls.length > 0) {
1751
+ applyGemini3UnsignedToolCallFallback(toolCalls, model);
1638
1752
  result.tool_calls = toolCalls;
1639
1753
  }
1640
1754
 
@@ -14,10 +14,6 @@ export const CODEX_SUBSCRIPTION_MODEL_IDS: ReadonlySet<string> = new Set([
14
14
  "gpt-5.6-terra",
15
15
  "gpt-5.6-luna",
16
16
  "gpt-5.5",
17
- // OpenAI retires these two from ChatGPT sign-in on 2026-08-31; API-key
18
- // auth is unaffected.
19
- "gpt-5.4",
20
- "gpt-5.4-mini",
21
17
  ]);
22
18
 
23
19
  /** True when `model` is allowlisted for Codex subscription routing. */