mixdog 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -1,20 +1,15 @@
1
1
  import { createRequire } from 'node:module';
2
- import { createHash } from 'crypto';
3
2
  import { loadConfig } from '../config.mjs';
4
- import { shouldFallbackTransport, withRetry } from './retry-classifier.mjs';
3
+ import { withRetry } from './retry-classifier.mjs';
5
4
  import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
6
5
  import { sendViaWebSocket } from './openai-oauth-ws.mjs';
7
6
  import {
8
7
  consumeCompatChatCompletionStream,
9
8
  consumeCompatResponsesStream,
10
- parseCompletedToolCallArgumentsJson,
11
9
  } from './openai-compat-stream.mjs';
12
10
  import { enrichModels, getModelMetadataSync } from './model-catalog.mjs';
11
+ import { sanitizeModelList } from './model-list-sanitize.mjs';
13
12
  import { appendAgentTrace, traceAgentUsage } from '../agent-trace.mjs';
14
- import {
15
- resolveProviderCacheKey,
16
- resolveProviderPromptCacheLane,
17
- } from '../agent-runtime/cache-strategy.mjs';
18
13
  import {
19
14
  PROVIDER_FIRST_BYTE_TIMEOUT_MS,
20
15
  PROVIDER_GENERATE_TOTAL_TIMEOUT_MS,
@@ -22,17 +17,40 @@ import {
22
17
  createPassthroughSignal,
23
18
  resolveTimeoutMs,
24
19
  } from '../stall-policy.mjs';
25
- import { traceHash, stableTraceStringify, summarizeTraceTools, traceTextShape } from './trace-utils.mjs';
20
+ import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
26
21
  import {
27
- normalizeContentForOpenAIChat,
28
- normalizeContentForOpenAIResponses,
29
- splitToolContentForOpenAIChat,
30
- splitToolContentForOpenAIResponses,
31
- } from './media-normalization.mjs';
22
+ summarizeTraceMessages,
23
+ extractCompatCachedTokens,
24
+ } from './openai-compat-trace.mjs';
32
25
  import {
33
- customToolCallFromResponseItem,
34
- } from './custom-tool-wire.mjs';
35
- import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
26
+ resolveCompatMaxOutputTokens,
27
+ toOpenAIMessages,
28
+ toOpenAITools,
29
+ toResponsesTools,
30
+ nativeResponsesTools,
31
+ knownToolNamesFromOpenAITools,
32
+ knownToolNamesFromResponsesTools,
33
+ parseToolCalls,
34
+ parseResponsesToolCalls,
35
+ responseOutputText,
36
+ collectCompatResponseSearchSources,
37
+ xaiSystemInstructions,
38
+ toXaiResponsesInput,
39
+ } from './openai-compat-wire.mjs';
40
+ import {
41
+ xaiCacheRouting,
42
+ xaiResponsesCacheRouting,
43
+ normalizeXaiReasoningEffort,
44
+ normalizeOpencodeGoReasoningEffort,
45
+ useXaiResponsesApi,
46
+ useXaiResponsesWebSocket,
47
+ useXaiResponsesWebSocketWarmup,
48
+ _shouldFallbackXaiWsToHttp,
49
+ withXaiResponsesCacheLane,
50
+ writeCompatCacheTrace,
51
+ traceXaiResponsesCacheContext,
52
+ writeXaiResponsesCacheTrace,
53
+ } from './openai-compat-xai.mjs';
36
54
 
37
55
  const requireOpenAI = createRequire(import.meta.url);
38
56
  let _OpenAI = null;
@@ -79,1129 +97,11 @@ function assertSafeBaseURL(rawURL, providerName) {
79
97
  }
80
98
 
81
99
 
82
- function summarizeTraceMessages(messages) {
83
- const summaries = (messages || []).map((m, index) => {
84
- const content = typeof m?.content === 'string'
85
- ? { type: 'text', ...traceTextShape(m.content) }
86
- : { type: m?.content == null ? 'null' : typeof m.content, hash: traceHash(stableTraceStringify(m?.content ?? null)) };
87
- const toolCalls = Array.isArray(m?.tool_calls)
88
- ? m.tool_calls.map(tc => ({
89
- name: tc?.function?.name || null,
90
- argsHash: traceHash(tc?.function?.arguments || ''),
91
- }))
92
- : [];
93
- return {
94
- index,
95
- role: m?.role || null,
96
- content,
97
- ...(typeof m?.reasoning_content === 'string'
98
- ? { reasoningContent: traceTextShape(m.reasoning_content) }
99
- : {}),
100
- toolCallCount: toolCalls.length,
101
- ...(toolCalls.length ? { toolCalls } : {}),
102
- };
103
- });
104
- if (summaries.length <= 12) return summaries;
105
- return [
106
- ...summaries.slice(0, 8),
107
- { omittedTurns: summaries.length - 12 },
108
- ...summaries.slice(-4),
109
- ];
110
- }
111
-
112
-
113
- function extractCompatCachedTokens(usage) {
114
- const candidates = [
115
- usage?.prompt_tokens_details?.cached_tokens,
116
- usage?.input_tokens_details?.cached_tokens,
117
- usage?.prompt_cache_hit_tokens,
118
- usage?.cached_prompt_text_tokens,
119
- ];
120
- for (const v of candidates) {
121
- const n = Number(v);
122
- if (Number.isFinite(n) && n > 0) return n;
123
- }
124
- for (const v of candidates) {
125
- const n = Number(v);
126
- if (Number.isFinite(n)) return n;
127
- }
128
- return 0;
129
- }
130
-
131
- function positiveTokenInt(value) {
132
- const n = Number(value);
133
- return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
134
- }
135
-
136
- function resolveCompatMaxOutputTokens(opts = {}) {
137
- return positiveTokenInt(
138
- opts.maxOutputTokens
139
- ?? opts.outputTokens
140
- ?? opts.max_output_tokens
141
- ?? opts.maxTokens
142
- ?? opts.max_tokens,
143
- );
144
- }
145
-
146
- function xaiPrefixSeed({ opts, params, rawTools, model }) {
147
- const providerKey = resolveProviderCacheKey(opts, 'xai');
148
- const systemMessages = (params?.messages || [])
149
- .filter(m => m?.role === 'system')
150
- .map(m => String(m?.content ?? ''));
151
- return stableTraceStringify({
152
- scope: 'xai-prefix-model-system-tools',
153
- providerKey: String(providerKey),
154
- model: model || null,
155
- systemMessages,
156
- tools: summarizeTraceTools(rawTools),
157
- });
158
- }
159
-
160
- function xaiCacheRouting(opts, params, rawTools, model) {
161
- const sessionId = String(opts?.sessionId || opts?.session?.id || '').trim();
162
- const providerKey = resolveProviderCacheKey(opts, 'xai');
163
- const prefixSeed = xaiPrefixSeed({ opts, params, rawTools, model });
164
- const prefixHash = traceHash(prefixSeed);
165
- const routingSeed = stableTraceStringify({
166
- scope: 'xai-chat-session-v1',
167
- providerKey: String(providerKey),
168
- model: model || null,
169
- sessionId: sessionId || `ephemeral:${process.pid}`,
170
- });
171
- return {
172
- key: deterministicUuidFromKey(routingSeed),
173
- mode: sessionId ? 'session' : 'ephemeral',
174
- seedHash: traceHash(routingSeed),
175
- prefixHash,
176
- ownerSessionHash: sessionId ? traceHash(sessionId) : null,
177
- };
178
- }
179
-
180
- function xaiResponsesCacheRouting(opts, params, rawTools, model) {
181
- // Default to 'prefix' so parallel workers sharing the same model + system
182
- // + tools land on a common prompt_cache_key, letting xAI's server-side
183
- // prefix cache hit across sessions instead of cold-starting per worker.
184
- // Override with 'session' (env or opts) for legacy session-isolated lanes.
185
- const scope = String(opts?.xaiResponsesCacheScope || process.env.MIXDOG_XAI_RESPONSES_CACHE_SCOPE || 'prefix')
186
- .trim()
187
- .toLowerCase();
188
- if (scope !== 'prefix') {
189
- return xaiCacheRouting(opts, params, rawTools, model);
190
- }
191
- const sessionId = String(opts?.sessionId || opts?.session?.id || '').trim();
192
- const providerKey = resolveProviderCacheKey(opts, 'xai');
193
- const prefixSeed = xaiPrefixSeed({ opts, params, rawTools, model });
194
- const prefixHash = traceHash(prefixSeed);
195
- const routingSeed = stableTraceStringify({
196
- scope: 'xai-responses-prefix-v1',
197
- providerKey: String(providerKey),
198
- model: model || null,
199
- prefixHash,
200
- });
201
- return {
202
- key: deterministicUuidFromKey(routingSeed),
203
- mode: 'prefix',
204
- seedHash: traceHash(routingSeed),
205
- prefixHash,
206
- ownerSessionHash: sessionId ? traceHash(sessionId) : null,
207
- };
208
- }
209
-
210
- function normalizeXaiReasoningEffort(value) {
211
- const effort = String(value || '').trim().toLowerCase();
212
- return ['none', 'low', 'medium', 'high'].includes(effort) ? effort : null;
213
- }
214
-
215
- function opencodeGoReasoningEffortValues(modelInfo) {
216
- const effort = (modelInfo?.reasoningOptions || []).find((option) => option?.type === 'effort');
217
- return Array.isArray(effort?.values)
218
- ? effort.values.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean)
219
- : [];
220
- }
221
-
222
- function normalizeOpencodeGoReasoningEffort(value, modelInfo) {
223
- const allowed = opencodeGoReasoningEffortValues(modelInfo);
224
- if (!allowed.length) return null;
225
- const effort = String(value || '').trim().toLowerCase();
226
- if (allowed.includes(effort)) return effort;
227
- if ((effort === 'max' || effort === 'xhigh') && allowed.includes('max')) return 'max';
228
- if (['high', 'medium', 'low'].includes(effort) && allowed.includes('high')) return 'high';
229
- return null;
230
- }
231
-
232
- function useXaiResponsesApi(opts, config) {
233
- const raw = opts?.xaiApiMode
234
- ?? config?.apiMode
235
- ?? config?.xaiApiMode
236
- ?? process.env.MIXDOG_XAI_API_MODE
237
- ?? process.env.MIXDOG_XAI_RESPONSES;
238
- if (raw == null || raw === '') return true;
239
- const mode = String(raw).trim().toLowerCase();
240
- return !['0', 'false', 'off', 'chat', 'chat-completions', 'chat_completions'].includes(mode);
241
- }
242
-
243
- function useXaiResponsesWebSocket(opts, config) {
244
- const raw = opts?.xaiResponsesTransport
245
- ?? opts?.xaiTransport
246
- ?? config?.responsesTransport
247
- ?? config?.transport
248
- ?? process.env.MIXDOG_XAI_RESPONSES_TRANSPORT
249
- ?? process.env.MIXDOG_XAI_TRANSPORT;
250
- if (raw == null || raw === '') return true;
251
- const transport = String(raw).trim().toLowerCase();
252
- return !['0', 'false', 'off', 'http', 'https', 'responses-http', 'sdk'].includes(transport);
253
- }
254
-
255
- function _envFlag(name, fallback = true) {
256
- const raw = process.env[name];
257
- if (raw == null || raw === '') return fallback;
258
- return !['0', 'false', 'off', 'no'].includes(String(raw).toLowerCase());
259
- }
260
-
261
- // xAI WS→HTTP transport fallback → shared shouldFallbackTransport
262
- // (retry-classifier.mjs). Identical deny-order + allow-list; the per-provider
263
- // env flag is computed here and passed via `enabled`.
264
- function _shouldFallbackXaiWsToHttp(err, signal) {
265
- return shouldFallbackTransport(err, {
266
- signal,
267
- enabled: _envFlag('MIXDOG_XAI_WS_HTTP_FALLBACK', true),
268
- });
269
- }
270
-
271
- function useXaiResponsesWebSocketWarmup(opts, config, { previousResponseId, instructions, rawTools }) {
272
- if (previousResponseId) return false;
273
- const raw = opts?.xaiResponsesWarmup
274
- ?? opts?.xaiWsWarmup
275
- ?? config?.responsesWarmup
276
- ?? config?.wsWarmup
277
- ?? process.env.MIXDOG_XAI_RESPONSES_WARMUP
278
- ?? process.env.MIXDOG_XAI_WS_WARMUP;
279
- if (raw != null && raw !== '') {
280
- const mode = String(raw).trim().toLowerCase();
281
- if (['0', 'false', 'off', 'none', 'disabled'].includes(mode)) return false;
282
- if (['1', 'true', 'on', 'always', 'force'].includes(mode)) return true;
283
- }
284
- return String(instructions || '').length >= 2048 || (Array.isArray(rawTools) && rawTools.length >= 10);
285
- }
286
-
287
- // Match OpenAI OAuth/API cache-lane semantics: default to 12 stable shards (via
288
- // resolveProviderPromptCacheLane) and serialize each final shard. This gives
289
- // Grok/xAI 10+ worker fanout without concurrent same-key cache contention.
290
- const XAI_RESPONSES_CACHE_LANE_DEFAULT_MAX_IN_FLIGHT = 1;
291
- const xaiResponsesCacheLanes = new Map();
292
-
293
- function parseXaiPositiveInt(value, fallback) {
294
- if (value == null || value === '') return fallback;
295
- const text = String(value).trim().toLowerCase();
296
- if (['0', 'false', 'off', 'none', 'disabled', 'unlimited', 'unbounded', 'auto'].includes(text)) return 0;
297
- const n = Number(text);
298
- if (!Number.isFinite(n)) return fallback;
299
- return Math.max(0, Math.floor(n));
300
- }
301
-
302
- function xaiResponsesCacheLaneMaxInFlight(opts, config) {
303
- return parseXaiPositiveInt(
304
- opts?.xaiCacheMaxInFlight
305
- ?? opts?.xaiResponsesCacheMaxInFlight
306
- ?? opts?.grokCacheMaxInFlight
307
- ?? opts?.grokResponsesCacheMaxInFlight
308
- ?? config?.xaiCacheMaxInFlight
309
- ?? config?.xaiResponsesCacheMaxInFlight
310
- ?? config?.grokCacheMaxInFlight
311
- ?? config?.grokResponsesCacheMaxInFlight
312
- ?? process.env.MIXDOG_XAI_CACHE_MAX_INFLIGHT
313
- ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_MAX_INFLIGHT
314
- ?? process.env.MIXDOG_GROK_CACHE_MAX_INFLIGHT
315
- ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_MAX_INFLIGHT
316
- ?? process.env.MIXDOG_GROK_OAUTH_CACHE_MAX_INFLIGHT
317
- ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_MAX_INFLIGHT,
318
- XAI_RESPONSES_CACHE_LANE_DEFAULT_MAX_IN_FLIGHT,
319
- );
320
- }
321
-
322
- function xaiResponsesCacheLaneQueueTimeoutMs(opts, config) {
323
- return parseXaiPositiveInt(
324
- opts?.xaiCacheQueueTimeoutMs
325
- ?? opts?.xaiResponsesCacheQueueTimeoutMs
326
- ?? opts?.grokCacheQueueTimeoutMs
327
- ?? opts?.grokResponsesCacheQueueTimeoutMs
328
- ?? config?.xaiCacheQueueTimeoutMs
329
- ?? config?.xaiResponsesCacheQueueTimeoutMs
330
- ?? config?.grokCacheQueueTimeoutMs
331
- ?? config?.grokResponsesCacheQueueTimeoutMs
332
- ?? process.env.MIXDOG_XAI_CACHE_QUEUE_TIMEOUT_MS
333
- ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_QUEUE_TIMEOUT_MS
334
- ?? process.env.MIXDOG_GROK_CACHE_QUEUE_TIMEOUT_MS
335
- ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_QUEUE_TIMEOUT_MS
336
- ?? process.env.MIXDOG_GROK_OAUTH_CACHE_QUEUE_TIMEOUT_MS
337
- ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_QUEUE_TIMEOUT_MS,
338
- 0,
339
- );
340
- }
341
-
342
- function xaiResponsesPromptCacheLane(opts, config, cacheRouting) {
343
- const shardOverride =
344
- opts?.xaiCacheLaneShards
345
- ?? opts?.xaiResponsesCacheLaneShards
346
- ?? opts?.xaiCacheMaxParallel
347
- ?? opts?.xaiResponsesCacheMaxParallel
348
- ?? opts?.grokCacheLaneShards
349
- ?? opts?.grokResponsesCacheLaneShards
350
- ?? opts?.grokCacheMaxParallel
351
- ?? opts?.grokResponsesCacheMaxParallel
352
- ?? config?.xaiCacheLaneShards
353
- ?? config?.xaiResponsesCacheLaneShards
354
- ?? config?.xaiCacheMaxParallel
355
- ?? config?.xaiResponsesCacheMaxParallel
356
- ?? config?.grokCacheLaneShards
357
- ?? config?.grokResponsesCacheLaneShards
358
- ?? config?.grokCacheMaxParallel
359
- ?? config?.grokResponsesCacheMaxParallel
360
- ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_MAX_PARALLEL
361
- ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_LANE_SHARDS
362
- ?? process.env.MIXDOG_GROK_CACHE_MAX_PARALLEL
363
- ?? process.env.MIXDOG_GROK_CACHE_LANE_SHARDS
364
- ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_MAX_PARALLEL
365
- ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_LANE_SHARDS
366
- ?? process.env.MIXDOG_GROK_OAUTH_CACHE_MAX_PARALLEL
367
- ?? process.env.MIXDOG_GROK_OAUTH_CACHE_LANE_SHARDS
368
- ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_MAX_PARALLEL
369
- ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_LANE_SHARDS;
370
- const autoOverride =
371
- opts?.xaiCacheLaneAuto
372
- ?? opts?.xaiResponsesCacheLaneAuto
373
- ?? opts?.grokCacheLaneAuto
374
- ?? opts?.grokResponsesCacheLaneAuto
375
- ?? config?.xaiCacheLaneAuto
376
- ?? config?.xaiResponsesCacheLaneAuto
377
- ?? config?.grokCacheLaneAuto
378
- ?? config?.grokResponsesCacheLaneAuto
379
- ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_LANE_AUTO
380
- ?? process.env.MIXDOG_GROK_CACHE_LANE_AUTO
381
- ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_LANE_AUTO
382
- ?? process.env.MIXDOG_GROK_OAUTH_CACHE_LANE_AUTO
383
- ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_LANE_AUTO;
384
- const slotOverride =
385
- opts?.xaiCacheLaneSlot
386
- ?? opts?.xaiResponsesCacheLaneSlot
387
- ?? opts?.grokCacheLaneSlot
388
- ?? opts?.grokResponsesCacheLaneSlot;
389
- const seed = String(
390
- opts?.xaiCacheLaneSeed
391
- ?? opts?.xaiResponsesCacheLaneSeed
392
- ?? opts?.grokCacheLaneSeed
393
- ?? opts?.grokResponsesCacheLaneSeed
394
- ?? opts?.promptCacheLaneSeed
395
- ?? opts?.sessionId
396
- ?? opts?.session?.id
397
- ?? cacheRouting?.ownerSessionHash
398
- ?? cacheRouting?.key
399
- ?? '',
400
- );
401
- return resolveProviderPromptCacheLane('xai', {
402
- ...opts,
403
- ...(shardOverride !== undefined ? { promptCacheLaneShards: shardOverride } : {}),
404
- ...(autoOverride !== undefined ? { promptCacheLaneAuto: autoOverride } : {}),
405
- ...(slotOverride !== undefined ? { promptCacheLaneSlot: slotOverride } : {}),
406
- promptCacheLaneSeed: seed || 'xai-cache-lane',
407
- }, config);
408
- }
409
-
410
- function xaiResponsesCacheLaneKey({ model, cacheRouting, opts, config }) {
411
- const prefix = cacheRouting?.prefixHash || cacheRouting?.seedHash || cacheRouting?.key || 'unknown-prefix';
412
- const lane = xaiResponsesPromptCacheLane(opts, config, cacheRouting);
413
- const shard = Number.isFinite(Number(lane?.slot)) ? Number(lane.slot) : 0;
414
- return {
415
- key: `xai-responses:${model || 'default'}:${prefix}:shard-${shard}`,
416
- shard,
417
- lane,
418
- };
419
- }
420
-
421
- function getXaiResponsesCacheLaneState(key, maxInFlight) {
422
- let state = xaiResponsesCacheLanes.get(key);
423
- if (!state) {
424
- state = { key, active: 0, queue: [], maxInFlight, nextId: 0 };
425
- xaiResponsesCacheLanes.set(key, state);
426
- }
427
- state.maxInFlight = maxInFlight;
428
- return state;
429
- }
430
-
431
- function cleanupXaiResponsesCacheLane(state) {
432
- if (state.active === 0 && state.queue.length === 0) {
433
- xaiResponsesCacheLanes.delete(state.key);
434
- }
435
- }
436
-
437
- function removeQueuedXaiCacheLaneRequest(state, request) {
438
- const index = state.queue.indexOf(request);
439
- if (index >= 0) state.queue.splice(index, 1);
440
- cleanupXaiResponsesCacheLane(state);
441
- }
442
-
443
- function makeXaiCacheLaneHandle(state, requestId, enqueuedAt) {
444
- let released = false;
445
- return {
446
- requestId,
447
- waitedMs: Date.now() - enqueuedAt,
448
- activeCount: state.active,
449
- queueDepth: state.queue.length,
450
- release() {
451
- if (released) return;
452
- released = true;
453
- releaseXaiResponsesCacheLane(state);
454
- },
455
- };
456
- }
457
-
458
- function releaseXaiResponsesCacheLane(state) {
459
- state.active = Math.max(0, state.active - 1);
460
- while (state.queue.length > 0 && state.active < state.maxInFlight) {
461
- const next = state.queue.shift();
462
- next.cleanup?.();
463
- state.active += 1;
464
- next.resolve(makeXaiCacheLaneHandle(state, next.requestId, next.enqueuedAt));
465
- }
466
- cleanupXaiResponsesCacheLane(state);
467
- }
468
-
469
- function acquireXaiResponsesCacheLane({ key, maxInFlight, signal, timeoutMs }) {
470
- const state = getXaiResponsesCacheLaneState(key, maxInFlight);
471
- const requestId = ++state.nextId;
472
- const enqueuedAt = Date.now();
473
- if (state.active < state.maxInFlight) {
474
- state.active += 1;
475
- return Promise.resolve(makeXaiCacheLaneHandle(state, requestId, enqueuedAt));
476
- }
477
- return new Promise((resolve, reject) => {
478
- const request = {
479
- requestId,
480
- enqueuedAt,
481
- resolve,
482
- reject,
483
- cleanup: null,
484
- };
485
- const cleanup = () => {
486
- if (request.timer) clearTimeout(request.timer);
487
- if (signal && request.abortListener) signal.removeEventListener('abort', request.abortListener);
488
- };
489
- request.cleanup = cleanup;
490
- request.abortListener = () => {
491
- cleanup();
492
- removeQueuedXaiCacheLaneRequest(state, request);
493
- const reason = signal?.reason;
494
- reject(reason instanceof Error ? reason : new Error('xAI cache lane wait aborted'));
495
- };
496
- if (signal?.aborted) {
497
- request.abortListener();
498
- return;
499
- }
500
- if (signal) signal.addEventListener('abort', request.abortListener, { once: true });
501
- if (timeoutMs > 0) {
502
- request.timer = setTimeout(() => {
503
- cleanup();
504
- removeQueuedXaiCacheLaneRequest(state, request);
505
- reject(new Error(`xAI cache lane wait timed out after ${timeoutMs}ms`));
506
- }, timeoutMs);
507
- request.timer.unref?.();
508
- }
509
- state.queue.push(request);
510
- });
511
- }
512
-
513
- function traceXaiCacheLane(opts, payload) {
514
- if (!compatCacheTraceEnabled('xai')) return;
515
- try {
516
- appendAgentTrace({
517
- sessionId: opts?.sessionId || opts?.session?.id || null,
518
- iteration: Number.isFinite(Number(opts?.iteration)) ? Number(opts.iteration) : null,
519
- kind: 'cache_lane',
520
- ...payload,
521
- payload,
522
- });
523
- } catch {}
524
- }
525
-
526
- async function withXaiResponsesCacheLane({ opts, config, cacheRouting, model, transport, previousResponseId, inputCount, signal }, fn) {
527
- const maxInFlight = xaiResponsesCacheLaneMaxInFlight(opts, config);
528
- if (maxInFlight <= 0) {
529
- const laneMeta = { enabled: false, maxInFlight: 0 };
530
- return { value: await fn(laneMeta), laneMeta };
531
- }
532
- const { key: laneKey, shard, lane } = xaiResponsesCacheLaneKey({ model, cacheRouting, opts, config });
533
- const timeoutMs = xaiResponsesCacheLaneQueueTimeoutMs(opts, config);
534
- const state = getXaiResponsesCacheLaneState(laneKey, maxInFlight);
535
- const queued = state.active >= state.maxInFlight;
536
- if (queued) {
537
- traceXaiCacheLane(opts, {
538
- provider: 'xai',
539
- api: 'responses',
540
- transport,
541
- event: 'queued',
542
- lane_key_hash: traceHash(laneKey),
543
- lane_shard: shard,
544
- lane_shards: Number.isFinite(Number(lane?.shards)) ? Number(lane.shards) : null,
545
- lane_auto: lane?.auto === true,
546
- lane_seed_hash: lane?.seedHash || null,
547
- max_in_flight: maxInFlight,
548
- active: state.active,
549
- queue_depth: state.queue.length,
550
- previous_response_used: !!previousResponseId,
551
- input_count: inputCount,
552
- });
553
- }
554
- const handle = await acquireXaiResponsesCacheLane({ key: laneKey, maxInFlight, signal, timeoutMs });
555
- const laneMeta = {
556
- enabled: true,
557
- laneKeyHash: traceHash(laneKey),
558
- shard,
559
- shards: Number.isFinite(Number(lane?.shards)) ? Number(lane.shards) : null,
560
- auto: lane?.auto === true,
561
- seedHash: lane?.seedHash || null,
562
- maxInFlight,
563
- queued,
564
- waitMs: handle.waitedMs,
565
- activeAfterAcquire: handle.activeCount,
566
- queueDepthAfterAcquire: handle.queueDepth,
567
- };
568
- traceXaiCacheLane(opts, {
569
- provider: 'xai',
570
- api: 'responses',
571
- transport,
572
- event: 'acquired',
573
- lane_key_hash: laneMeta.laneKeyHash,
574
- lane_shard: shard,
575
- lane_shards: laneMeta.shards,
576
- lane_auto: laneMeta.auto,
577
- lane_seed_hash: laneMeta.seedHash,
578
- max_in_flight: maxInFlight,
579
- wait_ms: laneMeta.waitMs,
580
- active: laneMeta.activeAfterAcquire,
581
- queue_depth: laneMeta.queueDepthAfterAcquire,
582
- previous_response_used: !!previousResponseId,
583
- input_count: inputCount,
584
- });
585
- const startedAt = Date.now();
586
- try {
587
- return { value: await fn(laneMeta), laneMeta };
588
- } finally {
589
- handle.release();
590
- traceXaiCacheLane(opts, {
591
- provider: 'xai',
592
- api: 'responses',
593
- transport,
594
- event: 'released',
595
- lane_key_hash: laneMeta.laneKeyHash,
596
- lane_shard: shard,
597
- lane_shards: laneMeta.shards,
598
- lane_auto: laneMeta.auto,
599
- lane_seed_hash: laneMeta.seedHash,
600
- max_in_flight: maxInFlight,
601
- held_ms: Date.now() - startedAt,
602
- previous_response_used: !!previousResponseId,
603
- input_count: inputCount,
604
- });
605
- }
606
- }
607
-
608
- function deterministicUuidFromKey(key) {
609
- const hex = createHash('sha256').update(String(key ?? '')).digest('hex');
610
- const variant = ((Number.parseInt(hex[16], 16) & 0x3) | 0x8).toString(16);
611
- return [
612
- hex.slice(0, 8),
613
- hex.slice(8, 12),
614
- '4' + hex.slice(13, 16),
615
- variant + hex.slice(17, 20),
616
- hex.slice(20, 32),
617
- ].join('-');
618
- }
619
-
620
- function compatCacheTraceEnabled(provider) {
621
- return process.env.MIXDOG_COMPAT_CACHE_TRACE === '1'
622
- || process.env.MIXDOG_PROVIDER_CACHE_TRACE === '1'
623
- || (provider === 'xai' && process.env.MIXDOG_XAI_CACHE_TRACE === '1');
624
- }
625
-
626
- function writeCompatCacheTrace({ provider, model, opts, params, rawTools, response, cacheRoutingKey, cacheRouting }) {
627
- if (!compatCacheTraceEnabled(provider)) return;
628
- try {
629
- const usage = response?.usage || {};
630
- const inputTokens = Number(usage.prompt_tokens ?? usage.input_tokens ?? 0);
631
- const cachedTokens = extractCompatCachedTokens(usage);
632
- const toolShape = summarizeTraceTools(rawTools);
633
- const traceMessages = Array.isArray(params?.messages) ? params.messages : [];
634
- const trace = {
635
- event: 'chat.completions',
636
- provider,
637
- model,
638
- responseModel: response?.model || null,
639
- owner: opts?.session?.owner || null,
640
- role: opts?.session?.role || opts?.role || null,
641
- permission: opts?.session?.permission || null,
642
- toolPermission: opts?.session?.toolPermission || null,
643
- profileId: opts?.session?.profileId || null,
644
- sourceType: opts?.session?.sourceType || null,
645
- sourceName: opts?.session?.sourceName || null,
646
- sessionIdHash: opts?.sessionId ? traceHash(opts.sessionId) : null,
647
- providerCacheKeyHash: opts?.providerCacheKey ? traceHash(opts.providerCacheKey) : null,
648
- promptCacheKeyHash: opts?.promptCacheKey ? traceHash(opts.promptCacheKey) : null,
649
- xGrokConvIdHash: provider === 'xai' && cacheRoutingKey ? traceHash(cacheRoutingKey) : null,
650
- xGrokConvIdSeedHash: provider === 'xai' ? cacheRouting?.seedHash || null : null,
651
- xGrokPromptPrefixHash: provider === 'xai' ? cacheRouting?.prefixHash || null : null,
652
- xGrokConvIdMode: provider === 'xai' ? cacheRouting?.mode || null : null,
653
- xGrokConvIdLaneIndex: provider === 'xai' ? cacheRouting?.laneIndex ?? null : null,
654
- xGrokConvIdActiveLanes: provider === 'xai' ? cacheRouting?.activeLanes ?? null : null,
655
- xGrokConvIdIdleLanes: provider === 'xai' ? cacheRouting?.idleLanes ?? null : null,
656
- xGrokConvIdOwnerSessionHash: provider === 'xai' ? cacheRouting?.ownerSessionHash || null : null,
657
- xaiReasoningEffort: provider === 'xai' ? params?.reasoning_effort || null : null,
658
- messageCount: traceMessages.length,
659
- messageFullHash: traceHash(stableTraceStringify(traceMessages)),
660
- messagePrefixHash: traceHash(stableTraceStringify(traceMessages.slice(0, -1))),
661
- lastMessageHash: traceMessages.length ? traceHash(stableTraceStringify(traceMessages.at(-1))) : null,
662
- messages: summarizeTraceMessages(traceMessages),
663
- toolCount: Array.isArray(rawTools) ? rawTools.length : 0,
664
- toolSchemaHash: traceHash(stableTraceStringify(toolShape)),
665
- usageKeys: Object.keys(usage || {}).sort(),
666
- promptTokenDetailsKeys: Object.keys(usage?.prompt_tokens_details || {}).sort(),
667
- inputTokenDetailsKeys: Object.keys(usage?.input_tokens_details || {}).sort(),
668
- choiceMessageKeys: Object.keys(response?.choices?.[0]?.message || {}).sort(),
669
- responseReasoningContent: typeof response?.choices?.[0]?.message?.reasoning_content === 'string'
670
- ? traceTextShape(response.choices[0].message.reasoning_content)
671
- : null,
672
- responseReasoningTokens: Number(usage?.completion_tokens_details?.reasoning_tokens ?? 0),
673
- inputTokens,
674
- outputTokens: Number(usage.completion_tokens ?? usage.output_tokens ?? 0),
675
- cachedTokens,
676
- cacheHitRate: inputTokens > 0 ? Number((cachedTokens / inputTokens).toFixed(6)) : null,
677
- costInUsdTicks: typeof usage.cost_in_usd_ticks === 'number' ? usage.cost_in_usd_ticks : null,
678
- };
679
- process.stderr.write(`[compat-cache-trace] ${JSON.stringify(trace)}\n`);
680
- } catch (err) {
681
- process.stderr.write(`[compat-cache-trace] failed: ${err?.message || err}\n`);
682
- }
683
- }
684
-
685
- function summarizeResponsesInput(input) {
686
- return (input || []).map((item, index) => ({
687
- index,
688
- type: item?.type || null,
689
- role: item?.role || null,
690
- callIdHash: item?.call_id ? traceHash(item.call_id) : null,
691
- name: item?.name || null,
692
- content: typeof item?.content === 'string'
693
- ? { type: 'text', ...traceTextShape(item.content) }
694
- : { type: item?.content == null ? 'null' : typeof item.content, hash: traceHash(stableTraceStringify(item?.content ?? null)) },
695
- output: typeof item?.output === 'string' ? traceTextShape(item.output) : null,
696
- }));
697
- }
698
-
699
- function xaiUsageStats(usage) {
700
- const inputTokens = Number(usage?.input_tokens ?? usage?.prompt_tokens ?? 0);
701
- const outputTokens = Number(usage?.output_tokens ?? usage?.completion_tokens ?? 0);
702
- const cachedTokens = extractCompatCachedTokens(usage);
703
- const hitRate = inputTokens > 0 ? Number((cachedTokens / inputTokens).toFixed(6)) : null;
704
- return { inputTokens, outputTokens, cachedTokens, hitRate };
705
- }
706
-
707
- function xaiSanitizedRequestSansInput(params) {
708
- const { input: _input, ...rest } = params || {};
709
- const out = { ...rest };
710
- if (out.prompt_cache_key) out.prompt_cache_key = traceHash(out.prompt_cache_key);
711
- if (out.previous_response_id) out.previous_response_id = traceHash(out.previous_response_id);
712
- if (typeof out.instructions === 'string') out.instructions = traceHash(out.instructions);
713
- return out;
714
- }
715
-
716
- function xaiResponsesFingerprintPayload({ model, opts, params, rawTools, response, cacheRouting, previousResponseId, inputStartIndex, continuationResetReason, transport, cacheLane }) {
717
- const usage = response?.usage || {};
718
- const { inputTokens, outputTokens, cachedTokens, hitRate } = xaiUsageStats(usage);
719
- const toolShape = summarizeTraceTools(rawTools);
720
- const instructions = typeof params?.instructions === 'string' ? params.instructions : '';
721
- const requestSansInput = xaiSanitizedRequestSansInput(params);
722
- const contextShape = {
723
- provider: 'xai',
724
- api: 'responses',
725
- model: model || null,
726
- promptCacheKeyHash: params?.prompt_cache_key ? traceHash(params.prompt_cache_key) : null,
727
- instructions,
728
- tools: toolShape,
729
- reasoning: params?.reasoning || null,
730
- store: params?.store ?? null,
731
- };
732
- const previousResponseUsed = Boolean(previousResponseId);
733
- const midTurnCold = previousResponseUsed
734
- && inputTokens >= 1024
735
- && (cachedTokens <= 512 || (hitRate != null && hitRate < 0.1));
736
- return {
737
- provider: 'xai',
738
- api: 'responses',
739
- transport: transport || null,
740
- model: model || null,
741
- response_model: response?.model || null,
742
- session_id_hash: opts?.sessionId ? traceHash(opts.sessionId) : null,
743
- provider_cache_key_hash: opts?.providerCacheKey ? traceHash(opts.providerCacheKey) : null,
744
- prompt_cache_key_option_hash: opts?.promptCacheKey ? traceHash(opts.promptCacheKey) : null,
745
- prompt_cache_key_hash: params?.prompt_cache_key ? traceHash(params.prompt_cache_key) : null,
746
- xai_prompt_prefix_hash: cacheRouting?.prefixHash || null,
747
- xai_cache_mode: cacheRouting?.mode || null,
748
- xai_cache_seed_hash: cacheRouting?.seedHash || null,
749
- owner_session_hash: cacheRouting?.ownerSessionHash || null,
750
- response_id_hash: response?.id ? traceHash(response.id) : null,
751
- previous_response_id_hash: previousResponseId ? traceHash(previousResponseId) : null,
752
- previous_response_used: previousResponseUsed,
753
- continuation_reset_reason: continuationResetReason || null,
754
- input_start_index: inputStartIndex,
755
- input_count: Array.isArray(params?.input) ? params.input.length : 0,
756
- input_hash: traceHash(stableTraceStringify(params?.input || [])),
757
- request_sans_input_hash: traceHash(stableTraceStringify(requestSansInput)),
758
- context_prefix_hash: traceHash(stableTraceStringify(contextShape)),
759
- has_instructions: instructions.length > 0,
760
- instructions_chars: instructions.length,
761
- instructions_hash: instructions ? traceHash(instructions) : null,
762
- reasoning_effort: params?.reasoning?.effort || null,
763
- tool_count: Array.isArray(rawTools) ? rawTools.length : 0,
764
- tool_schema_hash: traceHash(stableTraceStringify(toolShape)),
765
- tool_names_hash: traceHash(stableTraceStringify(toolShape.map(t => t?.name || null))),
766
- xai_cache_lane_enabled: cacheLane?.enabled === true,
767
- xai_cache_lane_hash: cacheLane?.laneKeyHash || null,
768
- xai_cache_lane_shard: Number.isFinite(Number(cacheLane?.shard)) ? Number(cacheLane.shard) : null,
769
- xai_cache_lane_shards: Number.isFinite(Number(cacheLane?.shards)) ? Number(cacheLane.shards) : null,
770
- xai_cache_lane_auto: cacheLane?.auto === true,
771
- xai_cache_lane_seed_hash: cacheLane?.seedHash || null,
772
- xai_cache_lane_max_in_flight: Number.isFinite(Number(cacheLane?.maxInFlight)) ? Number(cacheLane.maxInFlight) : null,
773
- xai_cache_lane_wait_ms: Number.isFinite(Number(cacheLane?.waitMs)) ? Number(cacheLane.waitMs) : null,
774
- xai_cache_lane_queued: cacheLane?.queued === true,
775
- input_tokens: inputTokens,
776
- output_tokens: outputTokens,
777
- cached_tokens: cachedTokens,
778
- cache_hit_rate: hitRate,
779
- mid_turn_cold: midTurnCold,
780
- };
781
- }
782
-
783
- function traceXaiResponsesCacheContext(args) {
784
- if (!compatCacheTraceEnabled('xai')) return;
785
- try {
786
- const payload = xaiResponsesFingerprintPayload(args);
787
- const sessionId = args?.opts?.sessionId || args?.opts?.session?.id || null;
788
- const iteration = Number.isFinite(Number(args?.opts?.iteration)) ? Number(args.opts.iteration) : null;
789
- appendAgentTrace({
790
- sessionId,
791
- iteration,
792
- kind: 'cache_context',
793
- ...payload,
794
- payload,
795
- });
796
- if (payload.mid_turn_cold) {
797
- const anomalyPayload = {
798
- ...payload,
799
- anomaly: 'xai_mid_turn_cold_cache',
800
- reason: 'previous_response_id_present_but_cached_tokens_low',
801
- };
802
- appendAgentTrace({
803
- sessionId,
804
- iteration,
805
- kind: 'cache_anomaly',
806
- ...anomalyPayload,
807
- payload: anomalyPayload,
808
- });
809
- }
810
- } catch (err) {
811
- process.stderr.write(`[compat-cache-trace] xai context trace failed: ${err?.message || err}\n`);
812
- }
813
- }
814
-
815
- function writeXaiResponsesCacheTrace({ model, opts, params, rawTools, response, cacheRouting, previousResponseId, inputStartIndex, continuationResetReason, transport, cacheLane }) {
816
- if (!compatCacheTraceEnabled('xai')) return;
817
- try {
818
- const usage = response?.usage || {};
819
- const fingerprint = xaiResponsesFingerprintPayload({
820
- model,
821
- opts,
822
- params,
823
- rawTools,
824
- response,
825
- cacheRouting,
826
- previousResponseId,
827
- inputStartIndex,
828
- continuationResetReason,
829
- transport,
830
- cacheLane,
831
- });
832
- const inputTokens = fingerprint.input_tokens;
833
- const cachedTokens = fingerprint.cached_tokens;
834
- const toolShape = summarizeTraceTools(rawTools);
835
- const trace = {
836
- event: 'responses',
837
- provider: 'xai',
838
- transport: transport || null,
839
- model,
840
- responseModel: response?.model || null,
841
- responseIdHash: response?.id ? traceHash(response.id) : null,
842
- previousResponseIdHash: previousResponseId ? traceHash(previousResponseId) : null,
843
- owner: opts?.session?.owner || null,
844
- role: opts?.session?.role || opts?.role || null,
845
- permission: opts?.session?.permission || null,
846
- toolPermission: opts?.session?.toolPermission || null,
847
- profileId: opts?.session?.profileId || null,
848
- sourceType: opts?.session?.sourceType || null,
849
- sourceName: opts?.session?.sourceName || null,
850
- sessionIdHash: opts?.sessionId ? traceHash(opts.sessionId) : null,
851
- promptCacheKeyHash: params?.prompt_cache_key ? traceHash(params.prompt_cache_key) : null,
852
- xGrokPromptPrefixHash: cacheRouting?.prefixHash || null,
853
- xGrokConvIdMode: cacheRouting?.mode || null,
854
- xaiReasoningEffort: params?.reasoning?.effort || null,
855
- previousResponseUsed: Boolean(previousResponseId),
856
- inputStartIndex,
857
- inputCount: Array.isArray(params?.input) ? params.input.length : 0,
858
- cacheLaneEnabled: fingerprint.xai_cache_lane_enabled,
859
- cacheLaneHash: fingerprint.xai_cache_lane_hash,
860
- cacheLaneShard: fingerprint.xai_cache_lane_shard,
861
- cacheLaneMaxInFlight: fingerprint.xai_cache_lane_max_in_flight,
862
- cacheLaneWaitMs: fingerprint.xai_cache_lane_wait_ms,
863
- cacheLaneQueued: fingerprint.xai_cache_lane_queued,
864
- input: summarizeResponsesInput(params?.input || []),
865
- toolCount: Array.isArray(rawTools) ? rawTools.length : 0,
866
- toolSchemaHash: traceHash(stableTraceStringify(toolShape)),
867
- toolNamesHash: fingerprint.tool_names_hash,
868
- requestSansInputHash: fingerprint.request_sans_input_hash,
869
- contextPrefixHash: fingerprint.context_prefix_hash,
870
- instructionsHash: fingerprint.instructions_hash,
871
- instructionsChars: fingerprint.instructions_chars,
872
- usageKeys: Object.keys(usage || {}).sort(),
873
- inputTokenDetailsKeys: Object.keys(usage?.input_tokens_details || {}).sort(),
874
- outputTokenDetailsKeys: Object.keys(usage?.output_tokens_details || {}).sort(),
875
- outputTypes: (response?.output || []).map(item => item?.type || null),
876
- inputTokens,
877
- outputTokens: fingerprint.output_tokens,
878
- cachedTokens,
879
- cacheHitRate: fingerprint.cache_hit_rate,
880
- midTurnCold: fingerprint.mid_turn_cold,
881
- costInUsdTicks: typeof usage.cost_in_usd_ticks === 'number' ? usage.cost_in_usd_ticks : null,
882
- };
883
- process.stderr.write(`[compat-cache-trace] ${JSON.stringify(trace)}\n`);
884
- } catch (err) {
885
- process.stderr.write(`[compat-cache-trace] failed: ${err?.message || err}\n`);
886
- }
887
- }
888
-
889
- function toOpenAIMessages(messages, providerName, options = {}) {
890
- // NOTE: chat.completions has no equivalent slot for replaying reasoning
891
- // encrypted_content the way the Responses API does (no `type:'reasoning'`
892
- // input item). Whatever reasoningItems may be attached to assistant
893
- // messages by the openai-oauth provider is intentionally dropped here —
894
- // strict providers (xai) reject unknown roles/types and would 400 the
895
- // request. Documented in v0.1.160 (GPT reasoning replay).
896
- //
897
- // DeepSeek thinking models require the prior turn's `reasoning_content`
898
- // string to be echoed back inside the assistant message, otherwise the API
899
- // returns 400. xAI reasoning models also preserve their official multi-turn
900
- // shape and cache prefix stability when prior assistant reasoning_content
901
- // is replayed; reasoning_effort itself remains caller/user-selected.
902
- const replaysReasoningContent = options.replaysReasoningContent === true
903
- || providerName === 'deepseek'
904
- || providerName === 'xai';
905
- const out = [];
906
- const pendingToolMedia = [];
907
- const flushToolMedia = () => {
908
- if (!pendingToolMedia.length) return;
909
- out.push({ role: 'user', content: pendingToolMedia.splice(0) });
910
- };
911
- for (const m of messages) {
912
- if (m.role === 'tool') {
913
- const { output, mediaContent } = splitToolContentForOpenAIChat(m.content);
914
- out.push({
915
- role: 'tool',
916
- tool_call_id: m.toolCallId || '',
917
- content: output,
918
- });
919
- if (mediaContent) pendingToolMedia.push(...mediaContent);
920
- continue;
921
- }
922
- flushToolMedia();
923
- if (m.role === 'assistant' && m.toolCalls?.length) {
924
- const msg = {
925
- role: 'assistant',
926
- content: normalizeContentForOpenAIChat(m.content, { role: 'assistant' }) || null,
927
- tool_calls: m.toolCalls.map((tc) => ({
928
- id: tc.id,
929
- type: 'function',
930
- function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },
931
- })),
932
- };
933
- if (replaysReasoningContent && m.reasoningContent) msg.reasoning_content = m.reasoningContent;
934
- out.push(msg);
935
- continue;
936
- }
937
- if (m.role === 'assistant' && replaysReasoningContent && m.reasoningContent) {
938
- out.push({ role: m.role, content: normalizeContentForOpenAIChat(m.content, { role: 'assistant' }), reasoning_content: m.reasoningContent });
939
- continue;
940
- }
941
- out.push({ role: m.role, content: normalizeContentForOpenAIChat(m.content, { role: m.role }) });
942
- }
943
- flushToolMedia();
944
- return out;
945
- }
946
-
947
- function toOpenAITools(tools) {
948
- return tools.map((t) => ({
949
- type: 'function',
950
- function: {
951
- name: t.name,
952
- description: t.description,
953
- parameters: t.inputSchema,
954
- },
955
- }));
956
- }
957
- function toResponsesTools(tools) {
958
- return tools.map((t) => {
959
- if (t?.name === 'tool_search') {
960
- return {
961
- type: 'tool_search',
962
- execution: 'client',
963
- description: t.description,
964
- parameters: t.inputSchema,
965
- };
966
- }
967
- // xAI/Grok Responses rejects the OpenAI-only `type:'custom'` freeform
968
- // variant ("unknown variant 'custom'"). Serialize freeform/grammar
969
- // tools (e.g. apply_patch) as ordinary function tools instead. Grammar
970
- // tools may carry no usable inputSchema, so fall back to a permissive
971
- // object schema so grok still registers a valid function tool.
972
- return {
973
- type: 'function',
974
- name: t.name,
975
- description: t.description,
976
- parameters: t.inputSchema || { type: 'object', additionalProperties: true },
977
- };
978
- });
979
- }
980
- function nativeResponsesTools(opts) {
981
- return Array.isArray(opts?.nativeTools)
982
- ? opts.nativeTools.filter(t => t && typeof t === 'object')
983
- : [];
984
- }
985
- // Known tool-name sets for the leaked-tool-call guard, derived from the exact
986
- // request body so a recovered leaked call is only synthesized when it names a
987
- // tool the model was actually offered. Chat tools nest the name under
988
- // `function.name`; Responses tools carry a top-level `name`.
989
- function knownToolNamesFromOpenAITools(tools) {
990
- return new Set(
991
- (Array.isArray(tools) ? tools : [])
992
- .map((t) => (typeof t?.function?.name === 'string' ? t.function.name
993
- : typeof t?.name === 'string' ? t.name : null))
994
- .filter(Boolean),
995
- );
996
- }
997
- function knownToolNamesFromResponsesTools(tools) {
998
- return new Set(
999
- (Array.isArray(tools) ? tools : [])
1000
- .map((t) => (typeof t?.name === 'string' ? t.name : null))
1001
- .filter(Boolean),
1002
- );
1003
- }
1004
- export function parseToolCalls(choice, label) {
1005
- const calls = choice.message?.tool_calls;
1006
- if (!calls?.length)
1007
- return undefined;
1008
- // finish_reason present ⇒ the turn completed; a JSON.parse failure on the
1009
- // arguments is deterministic bad JSON (permanent), not stream truncation.
1010
- const finishReason = choice.finish_reason || null;
1011
- return calls
1012
- .filter((tc) => tc.type === 'function')
1013
- .map((tc) => ({
1014
- id: tc.id,
1015
- name: tc.function.name,
1016
- arguments: parseCompletedToolCallArgumentsJson(tc.function.arguments, label, { id: tc.id, name: tc.function.name, finishReason }),
1017
- }));
1018
- }
1019
- export function parseResponsesToolCalls(response, label) {
1020
- const out = [];
1021
- // A Responses tool call is only parsed off a completed/done item, so any
1022
- // malformed-JSON failure here is deterministic, not mid-stream truncation.
1023
- const finishReason = response?.status || 'completed';
1024
- for (const item of response?.output || []) {
1025
- if (item?.type === 'function_call') {
1026
- out.push({
1027
- id: item.call_id || item.id,
1028
- name: item.name,
1029
- arguments: parseCompletedToolCallArgumentsJson(item.arguments, label, { id: item.call_id || item.id, name: item.name, finishReason }),
1030
- });
1031
- } else if (item?.type === 'custom_tool_call') {
1032
- const call = customToolCallFromResponseItem(item);
1033
- if (call) out.push(call);
1034
- } else if (item?.type === 'tool_search_call') {
1035
- out.push({
1036
- id: item.call_id || item.id,
1037
- name: 'tool_search',
1038
- arguments: item.arguments && typeof item.arguments === 'object'
1039
- ? item.arguments
1040
- : parseCompletedToolCallArgumentsJson(item.arguments || '{}', label, { id: item.call_id || item.id, name: 'tool_search', finishReason }),
1041
- nativeType: 'tool_search_call',
1042
- });
1043
- }
1044
- }
1045
- return out.length ? out : undefined;
1046
- }
1047
- function responseOutputText(response) {
1048
- if (typeof response?.output_text === 'string') return response.output_text;
1049
- const chunks = [];
1050
- for (const item of response?.output || []) {
1051
- if (item?.type !== 'message' || !Array.isArray(item.content)) continue;
1052
- for (const part of item.content) {
1053
- if (part?.type === 'output_text' && typeof part.text === 'string') chunks.push(part.text);
1054
- }
1055
- }
1056
- return chunks.join('');
1057
- }
1058
- function collectCompatResponseSearchSources(response) {
1059
- const citations = [];
1060
- const webSearchCalls = [];
1061
- const seen = new Set();
1062
- const addCitation = (source, fallback = {}) => {
1063
- if (!source) return;
1064
- if (typeof source === 'string') {
1065
- const url = source.trim();
1066
- if (!url || seen.has(url)) return;
1067
- seen.add(url);
1068
- citations.push({ title: url, url, snippet: '', source: fallback.source || 'citation', provider: 'xai' });
1069
- return;
1070
- }
1071
- if (typeof source !== 'object') return;
1072
- const url = String(
1073
- source.url
1074
- || source.uri
1075
- || source.href
1076
- || source.source_url
1077
- || source.url_citation?.url
1078
- || '',
1079
- ).trim();
1080
- if (!url || seen.has(url)) return;
1081
- seen.add(url);
1082
- citations.push({
1083
- title: String(source.title || source.name || source.query || source.url_citation?.title || fallback.title || url).trim(),
1084
- url,
1085
- snippet: String(source.snippet || source.text || source.description || '').trim(),
1086
- source: source.source || fallback.source || 'citation',
1087
- provider: source.provider || 'xai',
1088
- });
1089
- };
1090
- for (const citation of Array.isArray(response?.citations) ? response.citations : []) addCitation(citation);
1091
- for (const item of Array.isArray(response?.output) ? response.output : []) {
1092
- if (item?.type === 'web_search_call') {
1093
- webSearchCalls.push({ id: item.id || '', status: item.status || '', action: item.action || null });
1094
- const action = item.action || {};
1095
- for (const source of Array.isArray(action.sources) ? action.sources : []) addCitation(source, { title: action.query || '', source: 'web_search_call' });
1096
- if (action.url) addCitation({ url: action.url, title: action.query || '' }, { source: 'web_search_call' });
1097
- for (const url of Array.isArray(action.urls) ? action.urls : []) addCitation({ url, title: action.query || '' }, { source: 'web_search_call' });
1098
- }
1099
- for (const citation of Array.isArray(item?.citations) ? item.citations : []) addCitation(citation);
1100
- for (const part of Array.isArray(item?.content) ? item.content : []) {
1101
- for (const annotation of Array.isArray(part?.annotations) ? part.annotations : []) {
1102
- addCitation(annotation, { source: 'annotation' });
1103
- }
1104
- }
1105
- }
1106
- return { citations, webSearchCalls };
1107
- }
1108
- function toResponsesInputMessage(m, pendingToolMedia = null, customToolCallNameById = null) {
1109
- if (m.role === 'tool') {
1110
- if (Array.isArray(m.nativeToolSearch?.openaiTools)) {
1111
- return {
1112
- type: 'tool_search_output',
1113
- call_id: m.toolCallId || '',
1114
- status: 'completed',
1115
- execution: 'client',
1116
- tools: m.nativeToolSearch.openaiTools,
1117
- };
1118
- }
1119
- const { output, mediaContent } = splitToolContentForOpenAIResponses(m.content);
1120
- // xai path: never emit `custom_tool_call_output` (the `custom` variant
1121
- // is rejected by grok). Replay prior tool outputs as the standard
1122
- // `function_call_output` item regardless of original native type.
1123
- const item = {
1124
- type: 'function_call_output',
1125
- call_id: m.toolCallId || '',
1126
- output: output,
1127
- };
1128
- if (mediaContent && pendingToolMedia) pendingToolMedia.push(...mediaContent);
1129
- return item;
1130
- }
1131
- if (m.role === 'assistant' && Array.isArray(m.toolCalls) && m.toolCalls.length > 0) {
1132
- const items = [];
1133
- if (m.content) items.push({ role: 'assistant', content: normalizeContentForOpenAIResponses(m.content, { role: 'assistant' }) });
1134
- for (const tc of m.toolCalls) {
1135
- if (tc.nativeType === 'tool_search_call' || tc.name === 'tool_search') {
1136
- items.push({
1137
- type: 'tool_search_call',
1138
- call_id: tc.id,
1139
- execution: 'client',
1140
- arguments: tc.arguments || {},
1141
- });
1142
- } else {
1143
- // xai path: prior native `custom_tool_call` history is replayed
1144
- // as a standard `function_call` (grok rejects the `custom`
1145
- // variant). tc.arguments already holds the recovered object
1146
- // form, so the same stringify path as regular calls applies.
1147
- items.push({
1148
- type: 'function_call',
1149
- call_id: tc.id,
1150
- name: tc.name,
1151
- arguments: JSON.stringify(tc.arguments || {}),
1152
- });
1153
- }
1154
- }
1155
- return items;
1156
- }
1157
- return { role: m.role, content: normalizeContentForOpenAIResponses(m.content || '', { role: m.role }) };
1158
- }
1159
- function xaiSystemInstructions(messages) {
1160
- const instructions = (messages || [])
1161
- .filter(m => m?.role === 'system')
1162
- .map(m => String(m.content || ''))
1163
- .filter(Boolean)
1164
- .join('\n\n');
1165
- return instructions || undefined;
1166
- }
1167
- function toXaiResponsesInput(messages, providerState, options = {}) {
1168
- const includeSystem = options.includeSystem !== false;
1169
- const state = providerState?.xaiResponses || null;
1170
- let startIndex = 0;
1171
- let resetReason = null;
1172
- let previousResponseId = typeof state?.previousResponseId === 'string' ? state.previousResponseId : null;
1173
- const expectedModel = options.model ? String(options.model) : '';
1174
- const stateModel = state?.model ? String(state.model) : '';
1175
- const seen = Number.isInteger(state?.seenMessageCount) ? state.seenMessageCount : null;
1176
- if (previousResponseId && expectedModel && stateModel && stateModel !== expectedModel) {
1177
- previousResponseId = null;
1178
- resetReason = 'model_changed';
1179
- }
1180
- if (previousResponseId && (seen == null || seen < 0 || seen > messages.length)) {
1181
- previousResponseId = null;
1182
- resetReason = seen == null ? 'missing_seen_message_count' : 'seen_message_count_out_of_range';
1183
- }
1184
- if (previousResponseId) {
1185
- startIndex = Math.max(0, Math.min(seen, messages.length));
1186
- if (messages[startIndex]?.role === 'assistant') startIndex += 1;
1187
- }
1188
- const input = [];
1189
- const pendingToolMedia = [];
1190
- const customToolCallNameById = new Map();
1191
- const flushToolMedia = () => {
1192
- if (!pendingToolMedia.length) return;
1193
- input.push({ role: 'user', content: pendingToolMedia.splice(0) });
1194
- };
1195
- for (const m of messages.slice(startIndex)) {
1196
- if (!includeSystem && m.role === 'system') continue;
1197
- if (m.role !== 'tool') flushToolMedia();
1198
- const converted = toResponsesInputMessage(m, pendingToolMedia, customToolCallNameById);
1199
- if (Array.isArray(converted)) input.push(...converted);
1200
- else input.push(converted);
1201
- }
1202
- flushToolMedia();
1203
- return { input, previousResponseId, startIndex, continuationResetReason: resetReason };
1204
- }
100
+ // summarizeTraceMessages / extractCompatCachedTokens → openai-compat-trace.mjs
101
+ // resolveCompatMaxOutputTokens + message/tool wire converters and response
102
+ // parsers openai-compat-wire.mjs. Re-exported below for existing importers.
103
+ export { summarizeTraceMessages, extractCompatCachedTokens } from './openai-compat-trace.mjs';
104
+ export { parseToolCalls, parseResponsesToolCalls } from './openai-compat-wire.mjs';
1205
105
  export class OpenAICompatProvider {
1206
106
  // Chat Completions prompt_tokens is already the total (includes cached).
1207
107
  // Covers grok-oauth and all OPENAI_COMPAT_PRESETS. See registry.mjs.
@@ -1284,8 +184,7 @@ export class OpenAICompatProvider {
1284
184
  const opts = sendOpts || {};
1285
185
  // Re-warm a kept-alive socket to the provider origin before the turn so
1286
186
  // the request hot path lands on a live socket instead of paying a cold
1287
- // TLS handshake after an idle gap. Fire-and-forget; never awaited. This
1288
- // mirrors anthropic-oauth's send()-start preconnect.
187
+ // TLS handshake after an idle gap. Fire-and-forget; never awaited.
1289
188
  preconnect(this.baseURL);
1290
189
  if (this.name === 'xai' && useXaiResponsesApi(opts, this.config)) {
1291
190
  if (useXaiResponsesWebSocket(opts, this.config)) {
@@ -1896,7 +795,7 @@ export class OpenAICompatProvider {
1896
795
  });
1897
796
  }
1898
797
  const filtered = models.filter(m => m.id);
1899
- const enriched = await enrichModels(filtered);
798
+ const enriched = sanitizeModelList(await enrichModels(filtered), { provider: this.name });
1900
799
  this._enrichedModels = enriched;
1901
800
  return enriched;
1902
801
  }