mixdog 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (415) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/provider-pingpong-live.mjs +509 -0
  4. package/src/agents/advisor/AGENT.md +1 -1
  5. package/src/agents/advisor/agent.json +1 -1
  6. package/src/defaults/shipped-history.json +5 -0
  7. package/src/defaults/skills/setup/SKILL.md +6 -1
  8. package/src/defaults/skills/setup/references/actions.md +44 -22
  9. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  10. package/src/headless-exec.mjs +15 -8
  11. package/src/lib/keychain-cjs.cjs +7 -4
  12. package/src/output-styles/common.md +11 -10
  13. package/src/rules/agent/AGENT.md +5 -5
  14. package/src/rules/lead/LEAD.md +5 -5
  15. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  16. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  17. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  18. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  19. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  21. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  22. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  23. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
  24. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
  25. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
  28. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  30. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  31. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  32. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  33. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  34. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  35. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  37. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
  39. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
  40. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
  41. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  42. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
  43. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
  45. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
  49. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  50. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
  52. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  53. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  54. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  55. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  56. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  57. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  59. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
  60. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
  61. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  62. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
  63. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
  64. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
  65. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
  66. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
  67. package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
  68. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
  70. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  71. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  72. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  73. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  74. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  75. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  76. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  77. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  78. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  80. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  81. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  82. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  83. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  84. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  85. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  86. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +121 -61
  89. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  90. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
  91. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  92. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  93. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +42 -24
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +53 -5
  103. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
  108. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
  109. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
  110. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  111. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +25 -4
  112. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  113. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  114. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  117. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
  119. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
  121. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
  122. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  123. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  124. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  125. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  126. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  127. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  129. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  130. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +11 -3
  131. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  132. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  133. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  134. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +147 -8
  135. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  136. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
  137. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  138. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  139. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  140. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  141. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  142. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  143. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  147. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  148. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  150. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  151. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  152. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  154. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  155. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
  156. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  157. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  158. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  159. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  160. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  161. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  162. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  163. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  164. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  165. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  167. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  168. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  169. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  170. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  171. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  172. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +63 -20
  173. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
  174. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  175. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  179. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +4 -1
  180. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  181. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +16 -0
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  184. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  185. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  187. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  188. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
  189. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  190. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  192. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  193. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  195. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  196. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -0
  197. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  198. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  199. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  200. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  201. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  203. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  204. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  205. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  206. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  207. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  208. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
  209. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
  210. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  211. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  212. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  213. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  214. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  215. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  216. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  217. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  218. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  221. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  223. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  224. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  225. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  226. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  227. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  228. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  229. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  230. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  231. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  232. package/src/runtime/github/tool.mjs +92 -2
  233. package/src/runtime/memory/index.mjs +1 -0
  234. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  235. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  236. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  237. package/src/runtime/memory/lib/pg/process.mjs +31 -26
  238. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
  240. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  241. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  242. package/src/runtime/office/README.md +1 -1
  243. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  244. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  245. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  246. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  247. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  248. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  249. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  250. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  251. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  252. package/src/runtime/office/core/office-sessions.mjs +5 -0
  253. package/src/runtime/office/core/office-transactions.mjs +3 -0
  254. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  255. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  256. package/src/runtime/office/index.mjs +30 -0
  257. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  258. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  259. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  260. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  261. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  262. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  263. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  264. package/src/runtime/shared/buffered-appender.mjs +18 -0
  265. package/src/runtime/shared/config.mjs +78 -18
  266. package/src/runtime/shared/err-text.mjs +6 -0
  267. package/src/runtime/shared/file-permissions.d.mts +6 -1
  268. package/src/runtime/shared/file-permissions.mjs +54 -6
  269. package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
  270. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  271. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  272. package/src/runtime/shared/loopback-listener.mjs +9 -0
  273. package/src/runtime/shared/provider-api-key.mjs +2 -0
  274. package/src/runtime/shared/review-diff.mjs +52 -0
  275. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  276. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  277. package/src/runtime/shared/slow-span.mjs +114 -0
  278. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  279. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  280. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  281. package/src/runtime/shared/tool-card-model.mjs +5 -2
  282. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  283. package/src/runtime/shared/tool-primitives.mjs +13 -0
  284. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  285. package/src/runtime/shared/tool-surface.mjs +18 -3
  286. package/src/runtime/shared/tool-work-units.mjs +40 -4
  287. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  288. package/src/runtime/shared/transcript-writer.mjs +10 -2
  289. package/src/runtime/shared/turn-changes.mjs +268 -100
  290. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  291. package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
  292. package/src/runtime/shared/turn-snapshot.mjs +857 -683
  293. package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
  294. package/src/runtime/shared/user-data-guard.mjs +262 -49
  295. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  296. package/src/runtime/shared/worktrees/contract.mjs +34 -0
  297. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  298. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  299. package/src/runtime/shared/worktrees/git.mjs +8 -3
  300. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  301. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  302. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  303. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  304. package/src/runtime/terminal-bridge/client.mjs +44 -11
  305. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  306. package/src/runtime/tidy/apply.mjs +47 -2
  307. package/src/runtime/tidy/run-engines.mjs +11 -5
  308. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  309. package/src/session-runtime/boot/apis.mjs +13 -5
  310. package/src/session-runtime/boot/facade.mjs +1 -0
  311. package/src/session-runtime/boot/infrastructure.mjs +6 -1
  312. package/src/session-runtime/boot/session.mjs +0 -1
  313. package/src/session-runtime/boot/tools.mjs +4 -1
  314. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  315. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  316. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  317. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  318. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  319. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  320. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  321. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  322. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -3
  323. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  324. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  325. package/src/session-runtime/lifecycle/teardown.mjs +72 -7
  326. package/src/session-runtime/lifecycle-api.mjs +1 -0
  327. package/src/session-runtime/mcp-glue/connect.mjs +17 -5
  328. package/src/session-runtime/model-route/set-route.mjs +2 -2
  329. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  330. package/src/session-runtime/native-web-search.mjs +10 -1
  331. package/src/session-runtime/new-session-config.mjs +4 -2
  332. package/src/session-runtime/provider-auth/login.mjs +4 -0
  333. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  334. package/src/session-runtime/runtime-review-api.mjs +8 -2
  335. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  336. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  337. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  338. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  339. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  340. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  341. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  342. package/src/session-runtime/services/agent-tool.mjs +3 -0
  343. package/src/session-runtime/services/channel-admin.mjs +19 -3
  344. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  345. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  346. package/src/session-runtime/services/provider-admin.mjs +81 -15
  347. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  348. package/src/session-runtime/session-turn-api.mjs +14 -5
  349. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  350. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  351. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  352. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  353. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  354. package/src/session-runtime/skills-api.mjs +3 -5
  355. package/src/session-runtime/tool-surface.mjs +5 -1
  356. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  357. package/src/session-runtime/turn/session-ops.mjs +7 -5
  358. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  359. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  360. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  361. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  362. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  363. package/src/session-runtime/turn/turn-run.mjs +25 -9
  364. package/src/session-runtime/usage-stats-api.mjs +43 -11
  365. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  366. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  367. package/src/session-runtime/workspace/transitions.mjs +190 -15
  368. package/src/standalone/daemon-agent-control.mjs +9 -1
  369. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  370. package/src/standalone/daemon-owner.mjs +17 -4
  371. package/src/standalone/daemon-pg-stop.mjs +51 -0
  372. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  373. package/src/standalone/daemon-stop-budget.mjs +18 -0
  374. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  375. package/src/standalone/daemon-telemetry.mjs +19 -0
  376. package/src/standalone/daemon.mjs +61 -1
  377. package/src/standalone/desktop-service-registry.mjs +5 -0
  378. package/src/standalone/local-session-runtime.mjs +70 -19
  379. package/src/standalone/session-client.mjs +42 -0
  380. package/src/standalone/session-protocol.mjs +7 -0
  381. package/src/standalone/session-proxy/projection.mjs +41 -4
  382. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  383. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  384. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  385. package/src/standalone/session-service-api.mjs +6 -1
  386. package/src/standalone/session-state-patch.d.mts +2 -1
  387. package/src/standalone/session-state-patch.mjs +41 -1
  388. package/src/tui/App.jsx +25 -0
  389. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  390. package/src/tui/app/btw-controller.mjs +118 -0
  391. package/src/tui/app/create-app-pickers.mjs +2 -0
  392. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  393. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  394. package/src/tui/app/slash-commands.mjs +13 -3
  395. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  396. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  397. package/src/tui/app/use-global-key-input.mjs +5 -12
  398. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  399. package/src/tui/components/BtwPanel.jsx +53 -0
  400. package/src/tui/components/QuestionPanel.jsx +275 -0
  401. package/src/tui/components/question-panel-model.mjs +156 -0
  402. package/src/tui/dist/index.mjs +2413 -1465
  403. package/src/tui/session/context-state.mjs +4 -2
  404. package/src/tui/session/session-api/commands.mjs +10 -0
  405. package/src/tui/session/session-api/controls.mjs +2 -2
  406. package/src/tui/session/session-api/integrations.mjs +9 -2
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  408. package/src/tui/session/session-api/lifecycle.mjs +2 -1
  409. package/src/tui/session/session-api/settings.mjs +6 -0
  410. package/src/tui/session/session-api.mjs +1 -0
  411. package/src/tui/session/tool-approval.mjs +10 -7
  412. package/src/tui/session/transcript-spill.mjs +14 -7
  413. package/src/tui/session/turn-stream.mjs +174 -31
  414. package/src/tui/session/turn.mjs +12 -5
  415. package/src/workflows/default/WORKFLOW.md +3 -1
@@ -14,16 +14,36 @@ import {
14
14
  toOpenAITools,
15
15
  parseToolCalls,
16
16
  knownToolNamesFromOpenAITools,
17
- deepseekReplaysReasoningContent,
18
17
  } from './openai-compat-wire.mjs';
19
- import { applyCompatProviderChatOptions } from './openai-compat-options.mjs';
18
+ import { applyCompatProviderChatOptions, deepseekThinkingMode } from './openai-compat-options.mjs';
20
19
  import { normalizeCompatChatResponse } from './openai-compat-response-normalization.mjs';
21
20
  import { applyCompatToolChoice, compatStreamRetryReporter } from './compat-request-policy.mjs';
22
21
  import { ensureChatToolPairs } from './lib/wire-pairing.mjs';
23
22
  import { xaiCacheRouting } from './openai-compat-xai.mjs';
24
23
  import { compatRetryOptions, withCompatRetryProfile } from './openai-compat-retry-profile.mjs';
25
24
  import { preparePdfTextForProvider } from './media-normalization.mjs';
26
- import { isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
25
+ import { customWireFormat, isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
26
+ import { geminiAcceptedTools } from './gemini-schema.mjs';
27
+ import { acceptedWireTools } from './lib/claude-bridge-schema.mjs';
28
+ import { isGeminiFamilyModel } from './gemini-family.mjs';
29
+ import { compatReasoningShape, isTogetherBaseURL, readChatMessage } from './openai-compat-reasoning-field.mjs';
30
+
31
+ // Endpoint families with their own reasoning field (Together, vLLM, Groq,
32
+ // Mistral, OpenRouter); a custom provider's row may declare the vLLM format.
33
+ function reasoningShapeFor(provider, modelInfo) {
34
+ return compatReasoningShape({
35
+ providerName: provider.name,
36
+ baseURL: provider.baseURL,
37
+ wireFormat: isCustomProviderName(provider.name) ? customWireFormat(modelInfo, provider.baseURL) : undefined,
38
+ });
39
+ }
40
+
41
+ // The `developer` role is documented by MiMo and by Together's gpt-oss models;
42
+ // every other chat endpoint only knows `system`.
43
+ function keepsDeveloperRole(provider, profile, useModel) {
44
+ if (profile?.developerRole === true) return true;
45
+ return isTogetherBaseURL(provider.baseURL) && /gpt-oss/i.test(String(useModel || ''));
46
+ }
27
47
 
28
48
  export async function sendCompatChat(provider, messages, useModel, tools, opts) {
29
49
  const signal = opts.signal || null;
@@ -49,13 +69,17 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
49
69
  protocol: 'openai-chat',
50
70
  model: modelInfo,
51
71
  effort: opts.effort ?? provider.config?.reasoningEffort,
72
+ baseURL: provider.baseURL,
52
73
  })
53
74
  : null;
75
+ // DeepSeek thinking models need the prior reasoning_content back once tools
76
+ // are in play and ignore it otherwise, so it always goes back.
54
77
  const replaysReasoningContent = isCustom
55
78
  ? customReasoning.replay
56
79
  : modelInfo?.reasoningContentField === 'reasoning_content' ||
57
80
  profile?.replaysReasoningContent === true ||
58
- (provider.name === 'deepseek' && deepseekReplaysReasoningContent(useModel));
81
+ provider.name === 'deepseek';
82
+ const reasoningShape = reasoningShapeFor(provider, modelInfo);
59
83
  messages = await preparePdfTextForProvider(messages, provider);
60
84
  const params = {
61
85
  model: useModel,
@@ -65,82 +89,113 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
65
89
  messages: ensureChatToolPairs(
66
90
  toOpenAIMessages(messages, provider.name, {
67
91
  replaysReasoningContent,
92
+ ...(reasoningShape ? { reasoningShape } : {}),
68
93
  ...(isCustom ? { customReplay: customReasoning.replay } : {}),
94
+ ...(keepsDeveloperRole(provider, profile, useModel) ? { developerRole: true } : {}),
69
95
  nativePdf: provider.nativePdf,
96
+ cacheControl: provider.chatCacheControl === true,
70
97
  })
71
98
  ),
72
99
  };
73
100
  const maxOutputTokens = resolveCompatMaxOutputTokens(opts);
74
101
  if (maxOutputTokens) params.max_tokens = maxOutputTokens;
75
- if (tools?.length) params.tools = toOpenAITools(tools);
76
- applyCompatToolChoice(params, opts);
102
+ // A Gemini-family model behind this route (google/gemini-* on OpenRouter)
103
+ // fails the whole request on one schema Google rejects; such a tool is left
104
+ // out and reported like on the native Gemini routes.
105
+ // Every other model's `parameters` must be a valid 2020-12 object schema;
106
+ // a draft-07 one is lowered and a broken one left out and reported.
107
+ let sendTools = tools;
108
+ if (tools?.length) {
109
+ sendTools = isGeminiFamilyModel(useModel)
110
+ ? geminiAcceptedTools(tools, opts.onToolExcluded)
111
+ : acceptedWireTools(tools, 'openai', opts.onToolExcluded, `[${provider.name}] tool`);
112
+ }
113
+ if (sendTools?.length) params.tools = toOpenAITools(sendTools);
114
+ // A forced tool_choice goes out only on the documented providers; DeepSeek
115
+ // gets it only with thinking explicitly disabled (never silently in thinking
116
+ // mode, and never as an error for a turn whose thinking is enabled).
117
+ applyCompatToolChoice(params, opts, {
118
+ provider: provider.name,
119
+ baseURL: provider.baseURL,
120
+ wire: 'chat',
121
+ thinking:
122
+ provider.name === 'deepseek' && deepseekThinkingMode(opts, provider.config) === 'disabled' ? 'disabled' : null,
123
+ });
77
124
  applyCompatProviderChatOptions(params, provider.name, opts, provider.config, modelInfo);
78
125
  profile?.applyParams?.(params, { opts, config: provider.config, modelInfo });
79
126
  const firstByteTimeoutMs = profile?.firstByteTimeoutMs || PROVIDER_FIRST_BYTE_TIMEOUT_MS;
80
127
  const totalSignal = createPassthroughSignal(signal);
81
128
  const cacheRouting = provider.name === 'xai' ? xaiCacheRouting(opts, params, tools || [], useModel) : null;
82
129
  const cacheRoutingKey = cacheRouting?.key || null;
130
+ // xAI Chat pins a conversation to the server holding its cached prefix with
131
+ // the x-grok-conv-id header (the Responses equivalent is prompt_cache_key).
132
+ const requestHeaders = cacheRoutingKey
133
+ ? { 'x-grok-conv-id': cacheRoutingKey, ...(opts.requestHeaders || {}) }
134
+ : opts.requestHeaders;
83
135
  params.stream = true;
84
136
  params.stream_options = { include_usage: true };
85
137
  let assembled;
86
138
  try {
87
139
  try {
88
140
  assembled = await withRetry(
89
- withCompatRetryProfile(provider.name, refineAttemptErrors(profile, async ({ signal: attemptSignal }) => {
90
- try {
91
- opts.onStageChange?.('requesting');
92
- } catch {
93
- /* heartbeat best-effort */
94
- }
95
- const stream = await withRetry(
96
- ({ signal: openSignal }) =>
97
- provider.client.chat.completions.create(params, {
98
- signal: openSignal,
99
- ...(opts.requestHeaders ? { headers: opts.requestHeaders } : {}),
100
- }),
101
- {
102
- signal: attemptSignal,
103
- // Single attempt: this inner wrapper exists only to
104
- // apply the first-byte per-attempt timeout. Retry is
105
- // owned by the outer withRetry — nesting retry loops
106
- // here multiplied tail latency (5x5).
107
- maxAttempts: 1,
108
- perAttemptTimeoutMs: firstByteTimeoutMs,
109
- perAttemptLabel: `${provider.name} first byte`,
141
+ withCompatRetryProfile(
142
+ provider.name,
143
+ refineAttemptErrors(profile, async ({ signal: attemptSignal }) => {
144
+ try {
145
+ opts.onStageChange?.('requesting');
146
+ } catch {
147
+ /* heartbeat best-effort */
110
148
  }
111
- );
112
- try {
113
- opts.onStageChange?.('streaming');
114
- } catch {
115
- /* heartbeat best-effort */
116
- }
117
- try {
118
- return await consumeCompatChatCompletionStream(stream, {
119
- signal: attemptSignal,
120
- label: provider.name,
121
- providerName: provider.name,
122
- onStreamDelta: opts.onStreamDelta,
123
- onToolCall: opts.onToolCall,
124
- onTextDelta: opts.onTextDelta,
125
- parseToolCalls,
126
- // Known tool names for the leaked-tool-call guard:
127
- // recovered leaked calls only synthesize when they name
128
- // a tool actually offered to this request.
129
- knownToolNames: knownToolNamesFromOpenAITools(params.tools),
130
- ...(profile?.firstByteTimeoutMs ? { firstByteTimeoutMs: profile.firstByteTimeoutMs } : {}),
131
- ...(profile?.semanticIdleTimeoutMs ? { semanticIdleTimeoutMs: profile.semanticIdleTimeoutMs } : {}),
132
- });
133
- } catch (error) {
134
- // Grok's reference sampler treats content-idle as a
135
- // terminal sampling decision, not a resample signal.
136
- if (provider.name === 'xai' && error?.streamStalled === true) {
137
- throw markProviderRecoveryExhausted(error, {
138
- owner: 'xai-content-idle-policy',
149
+ const stream = await withRetry(
150
+ ({ signal: openSignal }) =>
151
+ provider.client.chat.completions.create(params, {
152
+ signal: openSignal,
153
+ ...(requestHeaders ? { headers: requestHeaders } : {}),
154
+ }),
155
+ {
156
+ signal: attemptSignal,
157
+ // Single attempt: this inner wrapper exists only to
158
+ // apply the first-byte per-attempt timeout. Retry is
159
+ // owned by the outer withRetry — nesting retry loops
160
+ // here multiplied tail latency (5x5).
161
+ maxAttempts: 1,
162
+ perAttemptTimeoutMs: firstByteTimeoutMs,
163
+ perAttemptLabel: `${provider.name} first byte`,
164
+ }
165
+ );
166
+ try {
167
+ opts.onStageChange?.('streaming');
168
+ } catch {
169
+ /* heartbeat best-effort */
170
+ }
171
+ try {
172
+ return await consumeCompatChatCompletionStream(stream, {
173
+ signal: attemptSignal,
174
+ label: provider.name,
175
+ providerName: provider.name,
176
+ onStreamDelta: opts.onStreamDelta,
177
+ onToolCall: opts.onToolCall,
178
+ onTextDelta: opts.onTextDelta,
179
+ parseToolCalls,
180
+ // Known tool names for the leaked-tool-call guard:
181
+ // recovered leaked calls only synthesize when they name
182
+ // a tool actually offered to this request.
183
+ knownToolNames: knownToolNamesFromOpenAITools(params.tools),
184
+ ...(profile?.firstByteTimeoutMs ? { firstByteTimeoutMs: profile.firstByteTimeoutMs } : {}),
185
+ ...(profile?.semanticIdleTimeoutMs ? { semanticIdleTimeoutMs: profile.semanticIdleTimeoutMs } : {}),
139
186
  });
187
+ } catch (error) {
188
+ // Grok's reference sampler treats content-idle as a
189
+ // terminal sampling decision, not a resample signal.
190
+ if (provider.name === 'xai' && error?.streamStalled === true) {
191
+ throw markProviderRecoveryExhausted(error, {
192
+ owner: 'xai-content-idle-policy',
193
+ });
194
+ }
195
+ throw error;
140
196
  }
141
- throw error;
142
- }
143
- })),
197
+ })
198
+ ),
144
199
  {
145
200
  signal: totalSignal.signal,
146
201
  onRetry: compatStreamRetryReporter(provider.name, opts),
@@ -152,7 +207,7 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
152
207
  const recovered = await provider._recoverCompatNonStreaming({
153
208
  streamErr,
154
209
  params,
155
- opts,
210
+ opts: requestHeaders === opts.requestHeaders ? opts : { ...opts, requestHeaders },
156
211
  signal: totalSignal.signal,
157
212
  useModel,
158
213
  });
@@ -172,6 +227,7 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
172
227
  cacheRoutingKey,
173
228
  cacheRouting,
174
229
  replaysReasoningContent,
230
+ reasoningShape,
175
231
  });
176
232
  }
177
233
 
@@ -236,13 +292,17 @@ export async function recoverCompatNonStreaming(provider, { streamErr, params, o
236
292
  /* best-effort */
237
293
  }
238
294
  traceNonStreamingFallback({ provider: provider.name, model: useModel, opts, streamErr });
295
+ const { content, reasoningContent, reasoningSource } = readChatMessage(message, {
296
+ shape: reasoningShapeFor(provider, provider.getCachedModelInfo?.(useModel) || null),
297
+ });
239
298
  return {
240
299
  response,
241
300
  model: response.model || useModel,
242
- content: typeof message.content === 'string' ? message.content : '',
301
+ content,
243
302
  toolCalls,
244
303
  stopReason: choice.finish_reason || null,
245
- reasoningContent: typeof message.reasoning_content === 'string' ? message.reasoning_content : null,
304
+ reasoningContent,
305
+ reasoningSource,
246
306
  rawUsage: response.usage || null,
247
307
  };
248
308
  }
@@ -4,7 +4,18 @@
4
4
  * reasoning aliases, tool_calls deltas, finish reason and usage.
5
5
  */
6
6
  import { mergeToolCallDelta } from './tool-call-acc.mjs';
7
- import { appendText, relayText, reportProgress, reportTransport } from './stream-state.mjs';
7
+ import { appendText, drainLeakedCalls, relayText, reportProgress, reportTransport } from './stream-state.mjs';
8
+ import { chatContentParts } from '../openai-compat-reasoning-field.mjs';
9
+
10
+ function appendReasoning(state, text, source) {
11
+ state.sawReasoningContent = true;
12
+ state.reasoningSource = source;
13
+ state.reasoningContent += text;
14
+ if (text) {
15
+ state.emittedReasoning = true;
16
+ reportProgress(state, 'reasoning');
17
+ }
18
+ }
8
19
 
9
20
  function applyReasoning(state, delta) {
10
21
  if (Array.isArray(delta?.reasoning_details) && delta.reasoning_details.length) {
@@ -12,14 +23,22 @@ function applyReasoning(state, delta) {
12
23
  reportProgress(state, 'reasoning');
13
24
  }
14
25
  // These provider fields are aliases, never independent text segments.
15
- const reasoningDelta =
16
- [delta?.reasoning_content, delta?.reasoning, delta?.thinking].find((value) => typeof value === 'string') ?? null;
17
- if (reasoningDelta === null) return;
18
- state.sawReasoningContent = true;
19
- state.reasoningContent += reasoningDelta;
20
- if (reasoningDelta) {
21
- state.emittedReasoning = true;
22
- reportProgress(state, 'reasoning');
26
+ const source = ['reasoning_content', 'reasoning', 'thinking'].find((key) => typeof delta?.[key] === 'string');
27
+ if (source === undefined) return;
28
+ appendReasoning(state, delta[source], source);
29
+ }
30
+
31
+ function applyContent(state, content) {
32
+ if (state.leakGuard.enabled) relayText(state, content);
33
+ else appendText(state, content);
34
+ }
35
+
36
+ // Mistral-style typed chunk lists: thinking → reasoning, text → assistant
37
+ // text, applied in arrival order.
38
+ function applyContentParts(state, parts) {
39
+ for (const part of parts) {
40
+ if (part.kind === 'reasoning') appendReasoning(state, part.text, 'content_chunks');
41
+ else if (part.text) applyContent(state, part.text);
23
42
  }
24
43
  }
25
44
 
@@ -32,15 +51,17 @@ export function applyCompatChunk(state, chunk) {
32
51
  if (typeof delta?.role === 'string' && delta.role) {
33
52
  reportProgress(state, 'semantic');
34
53
  }
35
- if (delta?.content) {
36
- if (state.leakGuard.enabled) relayText(state, delta.content);
37
- else appendText(state, delta.content);
38
- }
54
+ const contentParts = chatContentParts(delta?.content);
55
+ if (contentParts) applyContentParts(state, contentParts);
56
+ else if (delta?.content) applyContent(state, delta.content);
39
57
  applyReasoning(state, delta);
40
58
  if (Array.isArray(delta?.tool_calls) && delta.tool_calls.length) {
41
59
  reportProgress(state, 'tool');
42
60
  }
43
61
  mergeToolCallDelta(state.toolAcc, delta?.tool_calls);
44
- if (choice?.finish_reason) state.stopReason = choice.finish_reason;
62
+ if (choice?.finish_reason) {
63
+ state.stopReason = choice.finish_reason;
64
+ drainLeakedCalls(state);
65
+ }
45
66
  if (chunk?.usage) state.rawUsage = chunk.usage;
46
67
  }
@@ -13,7 +13,7 @@ import {
13
13
  markUnsafeRetryIfToolEmitted,
14
14
  } from '../openai-compat-stream-common.mjs';
15
15
  import { orderedToolCalls, toolCallsFromStreamAcc } from './tool-call-acc.mjs';
16
- import { attachPartial, attachReportedUsage, stampCompatOutcome } from './stream-state.mjs';
16
+ import { attachPartial, attachReportedUsage, isIncompleteFinishReason, stampCompatOutcome } from './stream-state.mjs';
17
17
 
18
18
  /** Rejection for a stream that ended without a terminal event. */
19
19
  export function incompleteStreamError(state, label) {
@@ -86,7 +86,9 @@ export function settleCompatStream(state, { label, parseToolCalls }) {
86
86
  if (failing) throw finishReasonInterruption(state, failing);
87
87
  const response = buildResponse(state);
88
88
  let toolCalls = parseAccumulatedToolCalls(state, { label, parseToolCalls });
89
- if (Array.isArray(toolCalls) && toolCalls.length) {
89
+ // A length-truncated or content-filtered call is a partial result that
90
+ // normalizeCompatChatResponse rejects: it must never reach the executor.
91
+ if (!isIncompleteFinishReason(state.stopReason) && Array.isArray(toolCalls) && toolCalls.length) {
90
92
  for (const call of toolCalls) emitCompatToolCallOnce(state.streamEmitState, call, state.onToolCall);
91
93
  }
92
94
  if (state.leakedCalls.length) {
@@ -99,6 +101,7 @@ export function settleCompatStream(state, { label, parseToolCalls }) {
99
101
  toolCalls,
100
102
  stopReason: state.stopReason,
101
103
  reasoningContent: state.sawReasoningContent ? state.reasoningContent : null,
104
+ reasoningSource: state.sawReasoningContent ? state.reasoningSource : null,
102
105
  reasoningDetails: state.reasoningDetails.length ? state.reasoningDetails : null,
103
106
  rawUsage: state.rawUsage,
104
107
  };
@@ -6,7 +6,11 @@
6
6
  */
7
7
  import { stampStreamOutcome, STREAM_TRANSPORTS } from '../lib/stream-outcome.mjs';
8
8
  import { createLeakGuard, createToolCallDedupe } from '../lib/leaked-toolcall.mjs';
9
- import { emitCompatToolCallOnce, synthLeakedOpenAICall } from '../openai-compat-stream-common.mjs';
9
+ import {
10
+ emitCompatToolCallOnce,
11
+ failingFinishReasonError,
12
+ synthLeakedOpenAICall,
13
+ } from '../openai-compat-stream-common.mjs';
10
14
  import { chatCompletionUsage } from '../openai-compat-response-normalization.mjs';
11
15
  import { noteAbandonedUsage } from '../../../../shared/llm/usage-context.mjs';
12
16
  import { createToolCallAccumulator } from './tool-call-acc.mjs';
@@ -31,6 +35,8 @@ export function createCompatStreamState({
31
35
  content: '',
32
36
  reasoningContent: '',
33
37
  sawReasoningContent: false,
38
+ // Where the last reasoning text came from: a delta field name or 'content_chunks'.
39
+ reasoningSource: null,
34
40
  reasoningDetails: [],
35
41
  // Both text and reasoning exposure prohibit resampling the request.
36
42
  emittedText: false,
@@ -43,6 +49,7 @@ export function createCompatStreamState({
43
49
  streamEmitState: { emittedToolCallKeys: new Set(), emittedToolCall: false, _toolDedupe: toolDedupe },
44
50
  leakGuard: createLeakGuard({ knownToolNames, harmony: true }),
45
51
  leakedCalls: [],
52
+ pendingLeakedCalls: [],
46
53
  };
47
54
  }
48
55
 
@@ -71,16 +78,39 @@ export function appendText(state, text) {
71
78
  }
72
79
  }
73
80
 
74
- function dispatchLeakedCall(state, recovered) {
81
+ /** A length-truncated or content-filtered turn is a partial result:
82
+ * normalizeCompatChatResponse rejects it, so no tool call of it may execute. */
83
+ export function isIncompleteFinishReason(finishReason) {
84
+ return finishReason === 'length' || finishReason === 'content_filter';
85
+ }
86
+
87
+ /** Whether `finish_reason` confirms a normal end of the turn: present, not an
88
+ * incomplete outcome and not a gateway-reported failure (network_error/error). */
89
+ export function isNormalFinishReason(finishReason) {
90
+ return Boolean(finishReason) && !isIncompleteFinishReason(finishReason) && !failingFinishReasonError(finishReason, '');
91
+ }
92
+
93
+ /** Dispatch held leaked calls once a normal finish_reason confirms the turn
94
+ * ended. Otherwise they stay on `leakedCalls` (partial tool calls) and never
95
+ * reach the executor. */
96
+ export function drainLeakedCalls(state) {
97
+ if (!isNormalFinishReason(state.stopReason)) return;
98
+ for (const call of state.pendingLeakedCalls.splice(0)) {
99
+ emitCompatToolCallOnce(state.streamEmitState, call, state.onToolCall);
100
+ }
101
+ }
102
+
103
+ function recordLeakedCall(state, recovered) {
75
104
  const call = synthLeakedOpenAICall(recovered);
76
- emitCompatToolCallOnce(state.streamEmitState, call, state.onToolCall);
105
+ state.pendingLeakedCalls.push(call);
77
106
  reportProgress(state, 'tool');
78
107
  return call;
79
108
  }
80
109
 
81
110
  function relayGuarded(state, { text, calls }) {
82
111
  if (text) appendText(state, text);
83
- for (const c of calls) state.leakedCalls.push(dispatchLeakedCall(state, c));
112
+ for (const c of calls) state.leakedCalls.push(recordLeakedCall(state, c));
113
+ drainLeakedCalls(state);
84
114
  }
85
115
 
86
116
  /** Route a text delta through the leak guard, dispatching rescued calls. */
@@ -113,9 +143,9 @@ export function noteReportedUsage(state) {
113
143
  }
114
144
 
115
145
  /** Keep the partial output on the error for upstream salvage: the text, the
116
- * recovered leaked calls already dispatched (native calls only dispatch once
117
- * the stream finishes) and whether a native call's arguments were still
118
- * streaming. */
146
+ * recovered leaked calls (none dispatched unless a normal finish_reason
147
+ * arrived; native calls only dispatch once the stream finishes) and whether a
148
+ * native call's arguments were still streaming. */
119
149
  export function attachPartial(state, err) {
120
150
  try {
121
151
  err.partialContent = state.content;
@@ -35,15 +35,8 @@ export function mergeToolCallDelta(acc, deltaCalls) {
35
35
  if (tc.id) prev.id = tc.id;
36
36
  if (tc.type) prev.type = tc.type;
37
37
  if (tc.function?.name && !prev.function.name) prev.function.name = tc.function.name;
38
- if (tc.function?.arguments) {
39
- const delta = tc.function.arguments;
40
- // Preserve support for providers that repeat cumulative arguments.
41
- if (prev.function.arguments && delta.startsWith(prev.function.arguments)) {
42
- prev.function.arguments = delta;
43
- } else {
44
- prev.function.arguments += delta;
45
- }
46
- }
38
+ // Argument fragments are deltas: always append, never infer snapshots.
39
+ if (tc.function?.arguments) prev.function.arguments += tc.function.arguments;
47
40
  }
48
41
  }
49
42
 
@@ -14,7 +14,7 @@ function resetAtFrom(wire, now) {
14
14
  if (wire?.resets_in_seconds != null && Number.isFinite(inSeconds) && inSeconds >= 0) {
15
15
  return now + Math.ceil(inSeconds * 1000);
16
16
  }
17
- const raw = wire?.resets_at ?? wire?.reset_at;
17
+ const raw = wire?.resets_at ?? wire?.reset_at ?? wire?.metadata?.resets_at;
18
18
  if (raw == null || raw === '') return null;
19
19
  const numeric = Number(raw);
20
20
  if (Number.isFinite(numeric) && numeric > 0) return numeric < 1e12 ? Math.round(numeric * 1000) : Math.round(numeric);
@@ -34,6 +34,25 @@ export async function fetchCompatModelItems(provider, { signal = null, keyless =
34
34
  }
35
35
  }
36
36
 
37
+ // Provider-native chat-ness: Together `type`, Mistral `capabilities`. Returns a
38
+ // sanitize-compatible mode, or null when the listing says nothing.
39
+ function nativeMode(m) {
40
+ const type = typeof m?.type === 'string' ? m.type.trim().toLowerCase() : '';
41
+ if (type === 'chat') return 'chat';
42
+ if (type === 'language' || type === 'code') return 'completion';
43
+ if (['embedding', 'embeddings', 'image', 'audio', 'rerank', 'moderation', 'video', 'transcribe'].includes(type)) {
44
+ return type;
45
+ }
46
+ // OpenRouter: a model that cannot emit text (image/audio generation) is no chat model.
47
+ const outputs = m?.architecture?.output_modalities;
48
+ if (Array.isArray(outputs) && outputs.length && !outputs.includes('text')) return 'non_chat';
49
+ const caps = m?.capabilities;
50
+ if (caps && typeof caps === 'object' && typeof caps.completion_chat === 'boolean') {
51
+ return caps.completion_chat ? 'chat' : 'non_chat';
52
+ }
53
+ return null;
54
+ }
55
+
37
56
  export async function listCompatModels(provider, { throwOnError = false, signal = null } = {}) {
38
57
  try {
39
58
  const list = await provider._fetchModelItems({ signal });
@@ -45,10 +64,23 @@ export async function listCompatModels(provider, { throwOnError = false, signal
45
64
  m?.max_input_tokens ??
46
65
  m?.max_model_len ??
47
66
  m?.context_length ??
67
+ m?.max_context_length ??
68
+ m?.top_provider?.context_length ??
48
69
  m?.contextWindow ??
49
70
  0
50
71
  );
51
- const outputTokens = Number(m?.max_output_tokens ?? m?.output_tokens ?? m?.maxOutputTokens ?? 0);
72
+ const outputTokens = Number(
73
+ m?.max_output_tokens ??
74
+ m?.output_tokens ??
75
+ m?.maxOutputTokens ??
76
+ m?.top_provider?.max_completion_tokens ??
77
+ 0
78
+ );
79
+ const mode = nativeMode(m);
80
+ // A catalog alias (OpenRouter `~anthropic/claude-opus-latest`) keeps its
81
+ // own slug for routing; the slug it currently points at is retained for
82
+ // capability lookup only.
83
+ const aliasTarget = typeof m?.alias_target === 'string' ? m.alias_target : m?.alias_target?.slug;
52
84
  models.push({
53
85
  id: m?.id,
54
86
  name: m?.id,
@@ -56,10 +88,23 @@ export async function listCompatModels(provider, { throwOnError = false, signal
56
88
  contextWindow: Number.isFinite(contextWindow) && contextWindow > 0 ? contextWindow : 0,
57
89
  outputTokens: Number.isFinite(outputTokens) && outputTokens > 0 ? outputTokens : null,
58
90
  created: typeof m?.created === 'number' ? m.created : null,
91
+ ...(mode ? { mode } : {}),
92
+ ...(typeof aliasTarget === 'string' && aliasTarget ? { aliasTarget } : {}),
59
93
  });
60
94
  }
61
95
  const filtered = models.filter((m) => m.id);
62
- const enriched = sanitizeModelList(await enrichModels(filtered), { provider: provider.name });
96
+ // The catalog fills gaps only: native limits and chat-ness are restored
97
+ // over whatever enrichment derived.
98
+ const enrichedRows = (await enrichModels(filtered)).map((row, i) => {
99
+ const native = filtered[i];
100
+ return {
101
+ ...row,
102
+ ...(native.contextWindow ? { contextWindow: native.contextWindow } : {}),
103
+ ...(native.outputTokens ? { outputTokens: native.outputTokens } : {}),
104
+ ...(native.mode ? { mode: native.mode } : {}),
105
+ };
106
+ });
107
+ const enriched = sanitizeModelList(enrichedRows, { provider: provider.name });
63
108
  provider._enrichedModels = enriched;
64
109
  return enriched;
65
110
  } catch (error) {
@@ -12,6 +12,41 @@ function normalizeReasoningEffort(value, allowed) {
12
12
  return allowed.includes(effort) ? effort : null;
13
13
  }
14
14
 
15
+ /**
16
+ * OpenRouter's unified `reasoning` field, shared by its Chat and Responses
17
+ * wires: `openRouterReasoningEffort`, else the turn effort, else the configured
18
+ * effort; `none` disables reasoning. Null when no effort applies.
19
+ */
20
+ export function openRouterReasoning(opts = {}, config = {}) {
21
+ const effort = normalizeReasoningEffort(opts.openRouterReasoningEffort ?? opts.effort ?? config?.reasoningEffort, [
22
+ 'none',
23
+ 'minimal',
24
+ 'low',
25
+ 'medium',
26
+ 'high',
27
+ 'xhigh',
28
+ 'max',
29
+ ]);
30
+ if (effort === 'none') return { enabled: false };
31
+ return effort ? { enabled: true, effort } : null;
32
+ }
33
+
34
+ function deepseekRawEffort(opts, config) {
35
+ return opts.deepseekReasoningEffort ?? opts.effort ?? config?.reasoningEffort;
36
+ }
37
+
38
+ /** 'enabled' | 'disabled' as the DeepSeek request will carry it, or null when nothing sets it. */
39
+ export function deepseekThinkingMode(opts = {}, config = {}) {
40
+ const rawThinking = opts.deepseekThinking ?? opts.thinking ?? config?.thinking;
41
+ const rawEffort = deepseekRawEffort(opts, config);
42
+ if (rawThinking === undefined && rawEffort === undefined) return null;
43
+ const thinkingMode = String(rawThinking?.type ?? rawThinking ?? rawEffort)
44
+ .trim()
45
+ .toLowerCase();
46
+ const disabled = rawThinking === false || thinkingMode === 'disabled' || thinkingMode === 'none';
47
+ return disabled ? 'disabled' : 'enabled';
48
+ }
49
+
15
50
  // Keep provider extensions isolated: fields accepted by one OpenAI-compatible
16
51
  // backend are frequently rejected by another even when the core Chat schema
17
52
  // is shared.
@@ -23,22 +58,14 @@ export function applyCompatProviderChatOptions(params, providerName, opts = {},
23
58
  protocol: 'openai-chat',
24
59
  model: modelInfo,
25
60
  effort: opts.effort ?? config?.reasoningEffort,
61
+ baseURL: config?.baseURL,
26
62
  });
27
63
  Object.assign(params, fields);
28
64
  return params;
29
65
  }
30
66
  if (providerName === 'openrouter') {
31
- const effort = normalizeReasoningEffort(opts.openRouterReasoningEffort ?? opts.effort ?? config?.reasoningEffort, [
32
- 'none',
33
- 'minimal',
34
- 'low',
35
- 'medium',
36
- 'high',
37
- 'xhigh',
38
- 'max',
39
- ]);
40
- if (effort === 'none') params.reasoning = { enabled: false };
41
- else if (effort) params.reasoning = { enabled: true, effort };
67
+ const reasoning = openRouterReasoning(opts, config);
68
+ if (reasoning) params.reasoning = reasoning;
42
69
  return params;
43
70
  }
44
71
  if (providerName === 'xai') {
@@ -53,22 +80,17 @@ export function applyCompatProviderChatOptions(params, providerName, opts = {},
53
80
  return params;
54
81
  }
55
82
  if (providerName === 'deepseek') {
56
- const rawThinking = opts.deepseekThinking ?? opts.thinking ?? config?.thinking;
57
- const rawEffort = opts.deepseekReasoningEffort ?? opts.effort ?? config?.reasoningEffort;
58
- if (rawThinking !== undefined || rawEffort !== undefined) {
59
- const thinkingMode = String(rawThinking?.type ?? rawThinking ?? rawEffort)
60
- .trim()
61
- .toLowerCase();
62
- const disabled = rawThinking === false || thinkingMode === 'disabled' || thinkingMode === 'none';
63
- params.thinking = { type: disabled ? 'disabled' : 'enabled' };
64
- if (!disabled) {
65
- const effort = String(rawEffort ?? '')
83
+ const mode = deepseekThinkingMode(opts, config);
84
+ if (mode) {
85
+ params.thinking = { type: mode };
86
+ if (mode === 'enabled') {
87
+ const effort = String(deepseekRawEffort(opts, config) ?? '')
66
88
  .trim()
67
89
  .toLowerCase();
68
- if (effort === 'max') params.reasoning_effort = 'max';
69
- // DeepSeek documents low/high/max; `medium` is its own alias for
70
- // high, and `low` is a real level that must not be promoted.
71
- else if (effort === 'low') params.reasoning_effort = 'low';
90
+ // DeepSeek documents low/high/max; `ultra` is its alias for max,
91
+ // `minimal` for low, `medium`/`xhigh` for high; `low` must not be promoted.
92
+ if (effort === 'max' || effort === 'ultra') params.reasoning_effort = 'max';
93
+ else if (effort === 'low' || effort === 'minimal') params.reasoning_effort = 'low';
72
94
  else if (['medium', 'high', 'xhigh'].includes(effort)) params.reasoning_effort = 'high';
73
95
  }
74
96
  }