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
@@ -9,7 +9,7 @@ export const CUSTOM_PROVIDER_PROTOCOLS = Object.freeze(['openai-chat', 'openai-r
9
9
  export const CUSTOM_PROVIDER_SCHEMA_VERSION = 2;
10
10
  // Ranked weakest → strongest.
11
11
  export const CUSTOM_REASONING_LEVELS = CUSTOM_EFFORT_LEVELS;
12
- export const CUSTOM_REASONING_WIRE_FORMATS = Object.freeze(['standard', 'deepseek']);
12
+ export const CUSTOM_REASONING_WIRE_FORMATS = Object.freeze(['standard', 'deepseek', 'together', 'vllm']);
13
13
  export const CUSTOM_PROVIDER_AUTH_MODES = Object.freeze(['key', 'none']);
14
14
  export const CUSTOM_MODEL_OVERRIDE_LIMIT = 500;
15
15
 
@@ -74,10 +74,22 @@ const V2_TOP_LEVEL_KEYS = new Set([
74
74
  'auth',
75
75
  'discovery',
76
76
  'modelOverrides',
77
+ 'wires',
78
+ 'modelWires',
77
79
  'id',
78
80
  'apiKey',
81
+ 'apiKeyEnv',
79
82
  'preconnect',
80
83
  ]);
84
+ export const CUSTOM_API_KEY_ENV_RE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
85
+
86
+ // Env var name that supplies the key, or '' when unset (undefined / null).
87
+ function normalizeApiKeyEnv(raw) {
88
+ if (raw === undefined || raw === null) return '';
89
+ if (typeof raw !== 'string' || !CUSTOM_API_KEY_ENV_RE.test(raw))
90
+ fail('apiKeyEnv must be a valid environment variable name');
91
+ return raw;
92
+ }
81
93
  const MAX_BASE_URL = 2048;
82
94
 
83
95
  function onlyKnownKeys(value, allowed, label) {
@@ -229,6 +241,49 @@ function normalizeOverrides(raw, protocol, legacy) {
229
241
  return out;
230
242
  }
231
243
 
244
+ // The wire each OpenAI-compatible protocol speaks (wire-route.mjs WIRES).
245
+ export const CUSTOM_PROTOCOL_WIRES = Object.freeze({
246
+ 'openai-chat': 'chat',
247
+ 'openai-responses': 'responses',
248
+ anthropic: 'messages',
249
+ });
250
+ // Wires a user-defined OpenAI-compatible endpoint can be declared to serve.
251
+ export const CUSTOM_PROVIDER_WIRES = Object.freeze(['chat', 'responses', 'messages']);
252
+
253
+ // `wires`: the wires the endpoint serves (the protocol's own wire included);
254
+ // absent = the protocol's wire only. Anthropic-protocol providers speak one wire.
255
+ function normalizeWires(raw, protocol) {
256
+ if (raw === undefined) return null;
257
+ if (protocol === 'anthropic') fail("wires requires an 'openai-chat' or 'openai-responses' protocol");
258
+ if (!Array.isArray(raw) || raw.length === 0) fail('wires must be a non-empty array');
259
+ const seen = new Set();
260
+ for (const wire of raw) {
261
+ if (!CUSTOM_PROVIDER_WIRES.includes(wire)) fail(`wires has unsupported wire: ${String(wire)}`);
262
+ if (seen.has(wire)) fail(`wires has duplicate wire: ${wire}`);
263
+ seen.add(wire);
264
+ }
265
+ if (!seen.has(CUSTOM_PROTOCOL_WIRES[protocol]))
266
+ fail(`wires must include the protocol wire: ${CUSTOM_PROTOCOL_WIRES[protocol]}`);
267
+ return CUSTOM_PROVIDER_WIRES.filter((wire) => seen.has(wire));
268
+ }
269
+
270
+ // `modelWires`: per-model wire override, each one of the served wires.
271
+ function normalizeModelWires(raw, protocol, wires) {
272
+ if (raw === undefined) return {};
273
+ if (protocol === 'anthropic') fail("modelWires requires an 'openai-chat' or 'openai-responses' protocol");
274
+ if (!isPlainObject(raw)) fail('modelWires must be an object');
275
+ const keys = Object.keys(raw);
276
+ if (keys.length > CUSTOM_MODEL_OVERRIDE_LIMIT) fail(`at most ${CUSTOM_MODEL_OVERRIDE_LIMIT} model wires are allowed`);
277
+ const served = wires || [CUSTOM_PROTOCOL_WIRES[protocol]];
278
+ const out = {};
279
+ for (const key of keys) {
280
+ const id = modelIdKey(key, 'modelWires key');
281
+ if (!served.includes(raw[key])) fail(`modelWires[${id}] must be one of the served wires: ${served.join(', ')}`);
282
+ out[id] = raw[key];
283
+ }
284
+ return out;
285
+ }
286
+
232
287
  // Legacy (pre-v2) `models: [{ id, name?, contextWindow?, maxOutputTokens? }]`
233
288
  // become manual, visible overrides; their presence hides newly discovered ids.
234
289
  function migrateLegacy(input) {
@@ -268,6 +323,8 @@ export function normalizeCustomProviderConfig(input) {
268
323
  // v2 fields without the version would be silently dropped by migration.
269
324
  if (input.modelOverrides !== undefined || input.discovery !== undefined)
270
325
  fail('modelOverrides and discovery require schemaVersion 2');
326
+ if (input.wires !== undefined || input.modelWires !== undefined)
327
+ fail('wires and modelWires require schemaVersion 2');
271
328
  source = migrateLegacy(input);
272
329
  } else if (Number.isInteger(version) && version > CUSTOM_PROVIDER_SCHEMA_VERSION)
273
330
  fail(`unsupported schemaVersion ${version}; update mixdog to use this provider`);
@@ -293,10 +350,13 @@ export function normalizeCustomProviderConfig(input) {
293
350
  if (!CUSTOM_PROVIDER_PROTOCOLS.includes(protocol)) fail(`unsupported protocol: ${protocol}`);
294
351
  const auth = source.auth === undefined || source.auth === null ? 'key' : text(source.auth);
295
352
  if (!CUSTOM_PROVIDER_AUTH_MODES.includes(auth)) fail(`invalid auth: ${String(source.auth)}`);
353
+ const apiKeyEnv = normalizeApiKeyEnv(source.apiKeyEnv);
296
354
  const discovery = source.discovery === undefined ? {} : source.discovery;
297
355
  if (!isPlainObject(discovery)) fail('discovery must be an object');
298
356
  onlyKnownKeys(discovery, DISCOVERY_KEYS, 'discovery');
299
357
  const newModelsHidden = optionalBoolean(discovery.newModelsHidden, 'discovery.newModelsHidden') ?? false;
358
+ const wires = normalizeWires(source.wires, protocol);
359
+ const modelWires = normalizeModelWires(source.modelWires, protocol, wires);
300
360
  return {
301
361
  type: 'custom',
302
362
  schemaVersion: CUSTOM_PROVIDER_SCHEMA_VERSION,
@@ -305,8 +365,11 @@ export function normalizeCustomProviderConfig(input) {
305
365
  baseURL: normalizeBaseURL(source.baseURL),
306
366
  enabled: source.enabled !== false,
307
367
  auth,
368
+ ...(apiKeyEnv && auth === 'key' ? { apiKeyEnv } : {}),
308
369
  discovery: { newModelsHidden },
309
370
  modelOverrides: normalizeOverrides(source.modelOverrides, protocol, legacy),
371
+ ...(wires ? { wires } : {}),
372
+ ...(Object.keys(modelWires).length ? { modelWires } : {}),
310
373
  };
311
374
  }
312
375
 
@@ -5,7 +5,19 @@
5
5
  // under the custom id so model routes and provider identity stay `custom-…`.
6
6
 
7
7
  import { resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
8
- import { effectiveCustomModelRows, normalizeCustomProviderConfig } from './custom-provider-config.mjs';
8
+ import {
9
+ CUSTOM_PROTOCOL_WIRES,
10
+ effectiveCustomModelRows,
11
+ normalizeCustomProviderConfig,
12
+ } from './custom-provider-config.mjs';
13
+ import {
14
+ customWireProfile,
15
+ providerOffersWire,
16
+ registerWireProfile,
17
+ resolveWireRoute,
18
+ sendOnRoute,
19
+ } from './wire-route.mjs';
20
+ import { withInclusiveUsage } from './lib/inclusive-usage.mjs';
9
21
  import { ANTHROPIC_VERSION } from './lib/anthropic-models.mjs';
10
22
  import { enrichModels } from './model-catalog.mjs';
11
23
  import { fetchCompatModelItems, listCompatModels } from './openai-compat-models.mjs';
@@ -45,6 +57,22 @@ function withCustomModels(Base, discover) {
45
57
  _customKey() {
46
58
  return this._customKeyless ? '' : resolveProviderApiKey(this.name, this.config);
47
59
  }
60
+ // Env mode with the variable unset: fail before any request is sent.
61
+ _assertCustomKey() {
62
+ const name = this.customConfig?.apiKeyEnv;
63
+ if (name && !this._customKeyless && !resolveProviderApiKey(this.name, this.config))
64
+ throw new Error(`API key environment variable ${name} is not set`);
65
+ }
66
+ // A 401 reload must not fall back to the stale key once the variable is
67
+ // gone; with a new value the base reload picks it up for the retry.
68
+ reloadApiKey() {
69
+ this._assertCustomKey();
70
+ return super.reloadApiKey();
71
+ }
72
+ async send(...args) {
73
+ this._assertCustomKey();
74
+ return super.send(...args);
75
+ }
48
76
  _customRows() {
49
77
  return effectiveCustomModelRows(this.customConfig, this._customDiscovered || [], { providerId: this.name });
50
78
  }
@@ -57,6 +85,7 @@ function withCustomModels(Base, discover) {
57
85
  async listModels({ signal = null, throwOnError = false } = {}) {
58
86
  let failure = null;
59
87
  try {
88
+ this._assertCustomKey();
60
89
  this._customDiscovered = await discover(this, signal);
61
90
  this.customDiscoveryError = null;
62
91
  } catch (error) {
@@ -112,10 +141,14 @@ export async function createCustomProvider(id, config) {
112
141
  // `auth: 'none'` is authoritative: a supplied/injected key is ignored and
113
142
  // `keyless` makes the engine omit every credential header.
114
143
  const keyless = cfg.auth === 'none';
115
- const apiKey = keyless ? '' : config.apiKey || '';
144
+ // Env mode: the named variable is the only key source.
145
+ let apiKey = '';
146
+ if (cfg.apiKeyEnv) apiKey = process.env[cfg.apiKeyEnv] || '';
147
+ else if (!keyless) apiKey = config.apiKey || '';
116
148
  const engine = {
117
149
  baseURL: cfg.baseURL,
118
150
  apiKey: apiKey || undefined,
151
+ ...(cfg.apiKeyEnv ? { apiKeyEnv: cfg.apiKeyEnv } : {}),
119
152
  ...(keyless ? { keyless: true } : {}),
120
153
  ...(config.preconnect === false ? { preconnect: false } : {}),
121
154
  };
@@ -129,14 +162,41 @@ export async function createCustomProvider(id, config) {
129
162
  provider.nativePdf = false;
130
163
  } else {
131
164
  const { OpenAICompatProvider } = await import('./openai-compat.mjs');
132
- const responses = cfg.protocol === 'openai-responses';
165
+ // The wires the endpoint is declared to serve (default: the protocol's
166
+ // own) and the per-model `modelWires` override pick each model's wire
167
+ // through the shared wire-route table, pinned per session like a gateway.
168
+ registerWireProfile(
169
+ providerId,
170
+ customWireProfile({ protocolWire: CUSTOM_PROTOCOL_WIRES[cfg.protocol], wires: cfg.wires })
171
+ );
172
+ let messagesEngine = null;
173
+ if (providerOffersWire(providerId, 'messages')) {
174
+ const { AnthropicProvider } = await import('./anthropic.mjs');
175
+ // The Anthropic SDK appends /v1/messages itself; the endpoint's /v1 base maps to it.
176
+ messagesEngine = new AnthropicProvider({ ...engine, name: providerId, baseURL: cfg.baseURL.replace(/\/v1$/i, '') });
177
+ messagesEngine.nativePdf = false;
178
+ }
133
179
  class CompatBase extends OpenAICompatProvider {
180
+ messagesEngine = messagesEngine;
134
181
  _fetchModelItems({ signal = null } = {}) {
135
182
  return fetchCompatModelItems(this, { signal, keyless: this._customKeyless });
136
183
  }
137
- _doSend(messages, model, tools, sendOpts) {
138
- if (!responses) return super._doSend(messages, model, tools, sendOpts);
139
- return super._doSend(messages, model, tools, { ...(sendOpts || {}), compatWireApi: 'responses' });
184
+ resolveWireRoute(model, ctx = {}) {
185
+ return resolveWireRoute(this.name, model, { ...ctx, override: cfg.modelWires?.[model] ?? null });
186
+ }
187
+ send(messages, model, tools, sendOpts) {
188
+ const useModel = model || this.defaultModel;
189
+ // The resolved route is the only wire authority: a caller's own wire
190
+ // switches never reach the engine. A custom provider declares the
191
+ // wires its endpoint serves, so a clear "unsupported" refusal moves
192
+ // the send to another declared wire once (sendOnRoute).
193
+ return sendOnRoute(this, useModel, tools, sendOpts, messages, (wire, history, opts) => {
194
+ if (wire === 'messages' && this.messagesEngine) {
195
+ return withInclusiveUsage(() => this.messagesEngine.send(history, useModel, tools, opts));
196
+ }
197
+ if (wire === 'responses') return super.send(history, model, tools, { ...opts, compatWireApi: 'responses' });
198
+ return super.send(history, model, tools, opts);
199
+ });
140
200
  }
141
201
  }
142
202
  const Custom = withCustomModels(CompatBase, (self, signal) =>
@@ -144,6 +204,10 @@ export async function createCustomProvider(id, config) {
144
204
  );
145
205
  provider = new Custom(providerId, engine);
146
206
  }
207
+ // The bound Messages engine reads the parent's model rows, so a routed
208
+ // Messages request carries the same row-driven thinking/effort as the
209
+ // Anthropic protocol.
210
+ if (provider.messagesEngine) provider.messagesEngine.getCachedModelInfo = (model) => provider.getCachedModelInfo(model);
147
211
  provider.customConfig = cfg;
148
212
  provider._customKeyless = keyless;
149
213
  provider._applyCustomRows(provider._customRows());
@@ -4,6 +4,8 @@
4
4
  // effort to a supported level and serializes it for the provider protocol.
5
5
  // First-party providers never route through here.
6
6
 
7
+ import { isTogetherBaseURL } from './openai-compat-reasoning-field.mjs';
8
+
7
9
  export const CUSTOM_EFFORT_LEVELS = Object.freeze(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
8
10
  const RANK = new Map(CUSTOM_EFFORT_LEVELS.map((level, index) => [level, index]));
9
11
 
@@ -63,16 +65,30 @@ export function snapCustomEffort(levels, effort) {
63
65
  }
64
66
 
65
67
  const EMPTY = Object.freeze({ level: null, fields: {}, thinking: null, replay: false });
68
+ // Chat-only extensions on top of the standard `reasoning_effort`.
69
+ const CHAT_ONLY_WIRE_FORMATS = new Set(['deepseek', 'together', 'vllm']);
70
+
71
+ /**
72
+ * The effective wire format of a row: its declared format, else `together`
73
+ * when the provider's own base URL is Together AI (whose hybrid models toggle
74
+ * reasoning with a `reasoning.enabled` flag), else `standard`.
75
+ */
76
+ export function customWireFormat(model, baseURL) {
77
+ const declared = model?.customReasoning?.wireFormat;
78
+ if (CHAT_ONLY_WIRE_FORMATS.has(declared)) return declared;
79
+ return isTogetherBaseURL(baseURL) ? 'together' : 'standard';
80
+ }
66
81
 
67
82
  /**
68
- * @param {{ protocol: 'openai-chat'|'openai-responses'|'anthropic', model: object|null, effort: string|null|undefined }} input
83
+ * @param {{ protocol: 'openai-chat'|'openai-responses'|'anthropic', model: object|null, effort: string|null|undefined, baseURL?: string }} input
69
84
  * @returns {{ level: string|null, fields: object, thinking: 'on'|'off'|null, replay: boolean }}
70
85
  */
71
- export function resolveCustomReasoning({ protocol, model, effort } = {}) {
86
+ export function resolveCustomReasoning({ protocol, model, effort, baseURL } = {}) {
72
87
  const custom = model?.customReasoning || {};
73
- const wireFormat = custom.wireFormat === 'deepseek' ? 'deepseek' : 'standard';
74
- // DeepSeek's `thinking` extension exists only on the Chat wire.
75
- if (wireFormat === 'deepseek' && protocol !== 'openai-chat') return { ...EMPTY, fields: {} };
88
+ const wireFormat = protocol === 'openai-chat' ? customWireFormat(model, baseURL) : 'standard';
89
+ // The `thinking` / `reasoning.enabled` / chat_template_kwargs extensions
90
+ // exist only on the Chat wire.
91
+ if (CHAT_ONLY_WIRE_FORMATS.has(custom.wireFormat) && protocol !== 'openai-chat') return { ...EMPTY, fields: {} };
76
92
  const level = snapCustomEffort(customEffortLevels(model), effort);
77
93
  let thinking = null;
78
94
  if (level !== null) thinking = level === 'none' ? 'off' : 'on';
@@ -86,6 +102,18 @@ export function resolveCustomReasoning({ protocol, model, effort } = {}) {
86
102
  level === 'none'
87
103
  ? { thinking: { type: 'disabled' } }
88
104
  : { thinking: { type: 'enabled' }, reasoning_effort: value };
105
+ } else if (wireFormat === 'together') {
106
+ // Together: `reasoning.enabled` toggles hybrid models (those that can
107
+ // be turned off); `reasoning_effort` sets the depth of the others.
108
+ if (level === 'none') fields = { reasoning: { enabled: false } };
109
+ else {
110
+ fields = { reasoning_effort: value };
111
+ if (customEffortLevels(model)?.includes('none')) fields.reasoning = { enabled: true };
112
+ }
113
+ } else if (wireFormat === 'vllm') {
114
+ // vLLM switches thinking through the chat template.
115
+ fields = { chat_template_kwargs: { enable_thinking: level !== 'none' } };
116
+ if (level !== 'none') fields.reasoning_effort = value;
89
117
  } else {
90
118
  fields = { reasoning_effort: value };
91
119
  }
@@ -101,7 +129,6 @@ export function resolveCustomReasoning({ protocol, model, effort } = {}) {
101
129
  // An explicit replay flag wins over the catalog's reasoning_content field.
102
130
  const replayFlag =
103
131
  typeof custom.replay === 'boolean' ? custom.replay : model?.reasoningContentField === 'reasoning_content';
104
- const replay =
105
- protocol === 'openai-chat' && replayFlag && thinking !== 'off' && !/reasoner/i.test(String(model?.id || ''));
132
+ const replay = protocol === 'openai-chat' && replayFlag && thinking !== 'off';
106
133
  return { level, fields, thinking, replay };
107
134
  }
@@ -42,17 +42,43 @@ function customToolArgumentsFromInput(name, input) {
42
42
  return { input: text };
43
43
  }
44
44
 
45
+ // Responses calls of a tool loaded from a declared namespace carry
46
+ // `namespace` beside `name`. Mixdog declares no namespaces today, so this is
47
+ // parse-side preservation only: a call that carries one keeps it on its
48
+ // record (and in replay), and its dispatch dedupe identity is qualified, so it
49
+ // is never conflated with a same-named top-level tool.
50
+ export function toolCallNamespace(item) {
51
+ return typeof item?.namespace === 'string' && item.namespace ? item.namespace : '';
52
+ }
53
+
54
+ /** Copy the source item's namespace onto a call record (mutates; returns it). */
55
+ export function withToolCallNamespace(call, source) {
56
+ const namespace = toolCallNamespace(source);
57
+ if (call && namespace && !call.namespace) call.namespace = namespace;
58
+ return call;
59
+ }
60
+
61
+ /** Dispatch dedupe identity: the namespace-qualified tool name. */
62
+ export function qualifiedToolCallName(call) {
63
+ const namespace = toolCallNamespace(call);
64
+ const name = call?.name || '';
65
+ return namespace ? `${namespace}.${name}` : name;
66
+ }
67
+
45
68
  export function customToolCallFromResponseItem(item) {
46
69
  if (item?.type !== 'custom_tool_call') return null;
47
70
  const id = item.call_id || item.id || '';
48
71
  const name = item.name || '';
49
72
  if (!id || !name) return null;
50
- return {
51
- id,
52
- name,
53
- arguments: customToolArgumentsFromInput(name, item.input ?? ''),
54
- nativeType: 'custom_tool_call',
55
- };
73
+ return withToolCallNamespace(
74
+ {
75
+ id,
76
+ name,
77
+ arguments: customToolArgumentsFromInput(name, item.input ?? ''),
78
+ nativeType: 'custom_tool_call',
79
+ },
80
+ item
81
+ );
56
82
  }
57
83
 
58
84
  export function isCustomToolCallRecord(call) {
@@ -0,0 +1,67 @@
1
+ /**
2
+ * DeepSeek: Chat Completions at https://api.deepseek.com plus the Anthropic
3
+ * Messages endpoint at https://api.deepseek.com/anthropic (the SDK appends
4
+ * /v1/messages). Per DeepSeek's Anthropic API docs that endpoint carries
5
+ * thinking and tool_use/tool_result blocks and ignores cache_control and
6
+ * anthropic-beta; agent/tool sessions use it, tool-less one-shot requests stay
7
+ * on Chat (wire-route.mjs). DeepSeek has no Responses endpoint.
8
+ */
9
+ import { WireGatewayProvider, messagesBaseFromOpenAIBase } from './wire-gateway.mjs';
10
+ import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
11
+
12
+ export function deepSeekMessagesBaseURL(configuredBaseURL) {
13
+ return `${messagesBaseFromOpenAIBase(configuredBaseURL || OPENAI_COMPAT_PRESETS.deepseek.baseURL)}/anthropic`;
14
+ }
15
+
16
+ // Requested effort → output_config.effort (thinking_mode docs: minimal/low →
17
+ // low, medium/high/xhigh → high, max/ultra → max).
18
+ const DEEPSEEK_EFFORT = Object.freeze({
19
+ minimal: 'low',
20
+ low: 'low',
21
+ medium: 'high',
22
+ high: 'high',
23
+ xhigh: 'high',
24
+ max: 'max',
25
+ ultra: 'max',
26
+ });
27
+ const DEEPSEEK_OFF = new Set(['none', 'off', 'disabled']);
28
+
29
+ /**
30
+ * DeepSeek's own effort contract on its Messages endpoint: thinking is on by
31
+ * default and `budget_tokens` is ignored, so an effort goes in
32
+ * `output_config.effort`; off is `thinking:{type:'disabled'}` together with the
33
+ * documented Anthropic-format toggle `reasoning:{effort:'none'}`. No effort
34
+ * leaves the default (thinking on, effort high). Always handles the request.
35
+ */
36
+ export function deepSeekEffortProjection(body, { effort } = {}) {
37
+ const level = String(effort ?? '')
38
+ .trim()
39
+ .toLowerCase();
40
+ if (DEEPSEEK_OFF.has(level)) {
41
+ body.thinking = { type: 'disabled' };
42
+ body.reasoning = { effort: 'none' };
43
+ return true;
44
+ }
45
+ const mapped = DEEPSEEK_EFFORT[level];
46
+ if (mapped) {
47
+ const existing = body.output_config && typeof body.output_config === 'object' ? body.output_config : {};
48
+ body.output_config = { ...existing, effort: mapped };
49
+ }
50
+ return true;
51
+ }
52
+
53
+ export class DeepSeekProvider extends WireGatewayProvider {
54
+ constructor(config = {}) {
55
+ super('deepseek', config, {
56
+ baseURL: deepSeekMessagesBaseURL(config.baseURL),
57
+ // The endpoint has no document block of any kind (PDFs included).
58
+ nativePdf: false,
59
+ nativeDocuments: false,
60
+ // disable_parallel_tool_use is ignored on every tool_choice.
61
+ supportsDisableParallelToolUse: false,
62
+ // Image `source.type: file` needs the Files API beta, on those requests only.
63
+ fileIdImageBeta: 'files-api-2025-04-14',
64
+ messagesEffortProjection: deepSeekEffortProjection,
65
+ });
66
+ }
67
+ }
@@ -183,7 +183,9 @@ export function autoEffortModel(providerName, providerImpl, model) {
183
183
  // list from falling back to a family ladder.
184
184
  return { id: model, reasoningLevels: anthropicEffortLevelsFor(model), customReasoning: true };
185
185
  }
186
- if (!isCustomProviderName(providerName)) return { id: model };
186
+ // Every other route reads the provider's own cached model row, the source
187
+ // route resolution (lookupModelMeta) uses; a bare id has no levels and
188
+ // skips the judge.
187
189
  return { ...(providerImpl?.getCachedModelInfo?.(model) || {}), id: model };
188
190
  }
189
191
 
@@ -14,7 +14,7 @@
14
14
  import { appendAgentTrace } from '../agent-trace.mjs';
15
15
  import {
16
16
  _estimateGeminiCacheTokens,
17
- _geminiCacheMinTokens,
17
+ geminiExplicitCacheMinTokens,
18
18
  _geminiCachePrefixCount,
19
19
  _geminiCachePrefixContents,
20
20
  _geminiCachePrefixHash,
@@ -146,6 +146,12 @@ export function awaitSharedCreate(task, signal) {
146
146
  // role='user' or 'model'; generateContent uses role='function' for
147
147
  // tool_result turns, so that is collapsed to 'user' (functionResponse parts
148
148
  // remain inside).
149
+ // The model id without its `models/` resource prefix; every URL and body that
150
+ // adds the prefix itself starts from this.
151
+ export function geminiBareModelId(model) {
152
+ return String(model || '').replace(/^models\//i, '');
153
+ }
154
+
149
155
  export function geminiCacheCreateBody({
150
156
  model,
151
157
  ttlSeconds,
@@ -157,7 +163,7 @@ export function geminiCacheCreateBody({
157
163
  }) {
158
164
  const cachePrefixContents = _geminiCachePrefixContents(contents, cachePrefixContentCount);
159
165
  const body = {
160
- model: `models/${model}`,
166
+ model: `models/${geminiBareModelId(model)}`,
161
167
  ttl: `${ttlSeconds}s`,
162
168
  };
163
169
  if (systemInstruction) {
@@ -252,9 +258,10 @@ export function dropRejectedGeminiCache(cachedContent, opts) {
252
258
  // the model-specific minimum. Skip the POST entirely when the estimate
253
259
  // is under threshold so we don't spam 400 responses turn-after-turn.
254
260
  export function geminiPrefixBelowMinimum({ model, systemInstruction, geminiTools, contents, opts, currentIter }) {
255
- const minTokens = _geminiCacheMinTokens();
261
+ // null: the model is excluded from explicit caching, so never create one.
262
+ const minTokens = geminiExplicitCacheMinTokens(model);
256
263
  const estimatedTokens = _estimateGeminiCacheTokens(systemInstruction, geminiTools, contents);
257
- if (estimatedTokens >= minTokens) return false;
264
+ if (minTokens !== null && estimatedTokens >= minTokens) return false;
258
265
  traceGeminiCache(opts, currentIter, 'gemini_cache_skip', {
259
266
  reason: 'prefix_below_min',
260
267
  estimatedTokens,
@@ -66,17 +66,28 @@ export function _estimateGeminiCacheTokens(systemInstruction, geminiTools, conte
66
66
  return Math.ceil(chars / 4);
67
67
  }
68
68
 
69
- // No catalog publishes the cachedContents floor and the published minimums
70
- // differ per model and change between releases, so one floor serves every id:
71
- // the highest published minimum. Skipping a small prefix only falls back to the
72
- // implicit cache, whereas a floor that is too low would POST a create that the
73
- // server rejects on every turn.
69
+ // Explicit-cache eligibility and minimum prefix size per model, from the
70
+ // caching docs: explicit caching works on Gemini 3 and 2.5 and is not supported
71
+ // from Gemini 4 onward; documented minimums are 4096 for Gemini 3.x and 2048
72
+ // for 2.5 Flash/Pro. Other ids keep the conservative 4096 floor (skipping a
73
+ // small prefix only falls back to the implicit cache, whereas a floor that is
74
+ // too low would POST a create the server rejects every turn).
74
75
  const GEMINI_CACHE_MIN_TOKENS = 4096;
75
76
 
76
- export function _geminiCacheMinTokens() {
77
+ // Minimum prefix tokens for an explicit cache, or null when the docs exclude
78
+ // the model from explicit caching.
79
+ export function geminiExplicitCacheMinTokens(model) {
80
+ const bare = String(model || '').replace(/^models\//i, '').toLowerCase();
81
+ const major = /^gemini-(\d+)/.exec(bare);
82
+ if (major && Number(major[1]) >= 4) return null;
83
+ if (/^gemini-2\.5-(?:pro|flash)(?!-lite)(?:-|$)/.test(bare)) return 2048;
77
84
  return GEMINI_CACHE_MIN_TOKENS;
78
85
  }
79
86
 
87
+ export function _geminiCacheMinTokens(model) {
88
+ return geminiExplicitCacheMinTokens(model) ?? GEMINI_CACHE_MIN_TOKENS;
89
+ }
90
+
80
91
  export function _geminiCachePrefixCount(contents) {
81
92
  return Array.isArray(contents) && contents.length > 1 ? contents.length - 1 : 0;
82
93
  }
@@ -128,8 +139,10 @@ export function markGeminiExplicitCacheRefused(model, err) {
128
139
  return true;
129
140
  }
130
141
 
142
+ // True when explicit cache creates must not be attempted: the docs exclude the
143
+ // model, or the server already refused it.
131
144
  export function geminiExplicitCacheRefused(model) {
132
- return geminiExplicitCacheRefusedModels.has(String(model || ''));
145
+ return geminiExplicitCacheMinTokens(model) === null || geminiExplicitCacheRefusedModels.has(String(model || ''));
133
146
  }
134
147
 
135
148
  export function _geminiCredentialFingerprint(apiKey) {
@@ -245,7 +258,11 @@ export function _resolveGeminiCacheUsage({ usageMetadata, cachedContent, provide
245
258
  firstFinite(usageMetadata?.thoughtsTokenCount, usageMetadata?.thoughts_token_count);
246
259
  const hasExplicitPromptTokens =
247
260
  Object.hasOwn(usageMetadata || {}, 'promptTokenCount') || Object.hasOwn(usageMetadata || {}, 'prompt_token_count');
248
- const inputTokens = hasExplicitPromptTokens ? promptTokens : Math.max(0, totalTokens - outputTokens);
261
+ // toolUsePromptTokenCount (tool-result prompt tokens) is billed as input and
262
+ // reported separately from promptTokenCount; cached tokens stay a subset of
263
+ // the prompt portion.
264
+ const toolUseTokens = firstFinite(usageMetadata?.toolUsePromptTokenCount, usageMetadata?.tool_use_prompt_token_count);
265
+ const inputTokens = hasExplicitPromptTokens ? promptTokens + toolUseTokens : Math.max(0, totalTokens - outputTokens);
249
266
  const reportedCachedTokens = firstFinite(
250
267
  // generateContent UsageMetadata field.
251
268
  usageMetadata?.cachedContentTokenCount,
@@ -180,12 +180,94 @@ export function assertNoGeminiInBandError(chunk, label) {
180
180
  if (err) throw err;
181
181
  }
182
182
 
183
- const SAFETY_FINISH = /^(?:PROMPT_)?(?:SAFETY|IMAGE_SAFETY|BLOCKLIST|PROHIBITED_CONTENT|SPII|IMAGE_PROHIBITED_CONTENT)$/;
183
+ const FINISH_KINDS = new Map(
184
+ Object.entries({
185
+ complete: ['STOP'],
186
+ output_limit: ['MAX_TOKENS'],
187
+ // Content refused by policy: deterministic, never re-asked.
188
+ policy: [
189
+ 'SAFETY',
190
+ 'BLOCKLIST',
191
+ 'PROHIBITED_CONTENT',
192
+ 'SPII',
193
+ 'IMAGE_SAFETY',
194
+ 'IMAGE_PROHIBITED_CONTENT',
195
+ 'ESCALATION',
196
+ 'PUP_LIMITED_DISABLED',
197
+ ],
198
+ language: ['LANGUAGE'],
199
+ recitation: ['RECITATION', 'IMAGE_RECITATION'],
200
+ // The model produced no usable call: the same request is re-asked.
201
+ invalid_tool_call: ['MALFORMED_FUNCTION_CALL', 'UNEXPECTED_TOOL_CALL', 'MALFORMED_RESPONSE'],
202
+ // Tool-call terminals a re-ask cannot fix.
203
+ tool_call: ['TOO_MANY_TOOL_CALLS', 'MISSING_THOUGHT_SIGNATURE'],
204
+ continuation: ['CONTINUATION'],
205
+ other: ['OTHER', 'IMAGE_OTHER', 'NO_IMAGE'],
206
+ }).flatMap(([kind, reasons]) => reasons.map((reason) => [reason, kind]))
207
+ );
208
+
209
+ /** Finish (or `PROMPT_`-prefixed block) reason without wire prefixes. */
210
+ export function geminiNormalizeFinishReason(finishReason) {
211
+ return String(finishReason || '')
212
+ .replace(/^FINISH_REASON_/, '')
213
+ .replace(/^PROMPT_/, '')
214
+ .replace(/^BLOCK_REASON_/, '');
215
+ }
216
+
217
+ /**
218
+ * Kind of a documented finishReason / blockReason: 'none' (absent or
219
+ * UNSPECIFIED: the stream is unfinished), 'complete', 'output_limit',
220
+ * 'policy', 'language', 'recitation', 'invalid_tool_call', 'tool_call',
221
+ * 'continuation' or 'other' (OTHER and any undocumented future reason).
222
+ */
223
+ export function geminiFinishReasonKind(finishReason) {
224
+ const reason = geminiNormalizeFinishReason(finishReason);
225
+ if (!reason || reason === 'UNSPECIFIED') return 'none';
226
+ return FINISH_KINDS.get(reason) || 'other';
227
+ }
184
228
 
185
229
  /** Policy-refusal code for a finishReason, or null for any other reason. */
186
230
  export function geminiPolicyFinishCode(finishReason) {
187
- const reason = String(finishReason || '').replace(/^FINISH_REASON_/, '');
188
- if (SAFETY_FINISH.test(reason)) return 'safety';
189
- if (/^(?:PROMPT_)?RECITATION$/.test(reason)) return 'recitation';
190
- return null;
231
+ switch (geminiFinishReasonKind(finishReason)) {
232
+ case 'policy':
233
+ return 'safety';
234
+ case 'recitation':
235
+ return 'recitation';
236
+ case 'language':
237
+ return 'content_filter';
238
+ default:
239
+ return null;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Evidence a terminal response carries beyond its finishReason: candidate and
245
+ * prompt safety ratings, the finish message, the prompt block reason and any
246
+ * continuation token. Only present fields are returned.
247
+ */
248
+ export function geminiFinishErrorFields(response) {
249
+ const candidate = response?.candidates?.[0];
250
+ const feedback = response?.promptFeedback;
251
+ const fields = {
252
+ finishMessage: candidate?.finishMessage,
253
+ safetyRatings: candidate?.safetyRatings,
254
+ blockReason: feedback?.blockReason,
255
+ promptSafetyRatings: feedback?.safetyRatings,
256
+ continuationToken: candidate?.continuationToken,
257
+ };
258
+ return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
259
+ }
260
+
261
+ /**
262
+ * Typed result for a CONTINUATION finish, which this client cannot resume
263
+ * (Gemini direct only). Keeps the original error's partial turn and the
264
+ * token so a caller can continue it out of band.
265
+ */
266
+ export function geminiContinuationUnsupportedError(incomplete, response) {
267
+ return Object.assign(incomplete, geminiFinishErrorFields(response), {
268
+ name: 'GeminiContinuationUnsupportedError',
269
+ code: 'CONTINUATION_UNSUPPORTED',
270
+ continuationUnsupported: true,
271
+ message: 'Gemini response stopped with finishReason=CONTINUATION; continuation is not supported by this client',
272
+ });
191
273
  }