mixdog 0.9.3 → 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 (329) hide show
  1. package/package.json +7 -3
  2. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  3. package/scripts/bench/lead-review-tasks.json +20 -0
  4. package/scripts/bench/r4-mixed-tasks.json +20 -0
  5. package/scripts/bench/review-tasks.json +20 -0
  6. package/scripts/bench/round-codex.json +114 -0
  7. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  8. package/scripts/bench/round-mixdog-lead.json +269 -0
  9. package/scripts/bench/round-mixdog.json +126 -0
  10. package/scripts/bench/round-r10-bigsample.json +679 -0
  11. package/scripts/bench/round-r11-codexalign.json +257 -0
  12. package/scripts/bench/round-r4-codex.json +114 -0
  13. package/scripts/bench/round-r4-mixed.json +225 -0
  14. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  15. package/scripts/bench/round-r6-codex.json +114 -0
  16. package/scripts/bench/round-r6-solo.json +257 -0
  17. package/scripts/bench/round-r7-full.json +254 -0
  18. package/scripts/bench/round-r8-fulldefault.json +255 -0
  19. package/scripts/bench-run.mjs +215 -29
  20. package/scripts/freevar-smoke.mjs +95 -0
  21. package/scripts/internal-comms-bench.mjs +1 -0
  22. package/scripts/internal-comms-smoke.mjs +10 -9
  23. package/scripts/mouse-probe.mjs +45 -0
  24. package/scripts/output-style-bench.mjs +13 -6
  25. package/scripts/output-style-smoke.mjs +4 -4
  26. package/scripts/provider-toolcall-test.mjs +7 -3
  27. package/scripts/recall-usecase-cases.json +18 -0
  28. package/scripts/recall-usecase-probe.json +6 -0
  29. package/scripts/session-bench.mjs +152 -6
  30. package/scripts/tool-smoke.mjs +23 -63
  31. package/scripts/tui-render-smoke.mjs +90 -0
  32. package/scripts/webhook-smoke.mjs +208 -0
  33. package/src/agents/debugger/AGENT.md +4 -1
  34. package/src/agents/heavy-worker/AGENT.md +6 -5
  35. package/src/agents/maintainer/AGENT.md +4 -0
  36. package/src/agents/reviewer/AGENT.md +2 -1
  37. package/src/agents/worker/AGENT.md +8 -4
  38. package/src/lib/rules-builder.cjs +4 -0
  39. package/src/mixdog-session-runtime.mjs +632 -2042
  40. package/src/output-styles/default.md +34 -9
  41. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  42. package/src/output-styles/minimal.md +4 -1
  43. package/src/output-styles/simple.md +22 -7
  44. package/src/rules/agent/00-common.md +2 -0
  45. package/src/rules/lead/lead-brief.md +12 -0
  46. package/src/rules/lead/lead-tool.md +0 -11
  47. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  49. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  50. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  51. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  52. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  53. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  54. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  55. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  60. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  62. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  63. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  65. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  66. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  67. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  68. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
  77. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  78. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  79. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  80. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  83. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  84. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  85. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  87. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  88. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  89. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  90. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  91. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  93. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  94. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  95. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  97. package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
  98. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  99. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  100. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  101. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  102. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  103. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  104. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  105. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  106. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  109. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
  110. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  118. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  120. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  122. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  123. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  125. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  126. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  127. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  128. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  129. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  130. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  131. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  132. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  133. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  134. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  135. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  136. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  137. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  138. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  139. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  140. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  141. package/src/runtime/channels/backends/discord.mjs +12 -292
  142. package/src/runtime/channels/index.mjs +229 -663
  143. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  144. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  145. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  146. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  147. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  148. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  149. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  150. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  151. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  152. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  153. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  154. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  155. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  156. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  157. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  158. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  159. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  160. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  161. package/src/runtime/channels/lib/webhook.mjs +36 -570
  162. package/src/runtime/channels/tool-defs.mjs +11 -130
  163. package/src/runtime/memory/index.mjs +201 -1948
  164. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  165. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  166. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  167. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  168. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  169. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  170. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  171. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  172. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  173. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  174. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  175. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  177. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  178. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  179. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  180. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  181. package/src/runtime/memory/tool-defs.mjs +5 -13
  182. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  183. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  184. package/src/runtime/search/lib/web-tools.mjs +24 -602
  185. package/src/runtime/shared/atomic-file.mjs +26 -1
  186. package/src/runtime/shared/launcher-control.mjs +2 -2
  187. package/src/runtime/shared/tool-primitives.mjs +308 -0
  188. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  189. package/src/runtime/shared/tool-surface.mjs +80 -898
  190. package/src/runtime/shared/transcript-writer.mjs +23 -0
  191. package/src/runtime/shared/update-checker.mjs +7 -4
  192. package/src/session-runtime/config-helpers.mjs +84 -2
  193. package/src/session-runtime/config-lifecycle.mjs +232 -0
  194. package/src/session-runtime/cwd-plugins.mjs +226 -0
  195. package/src/session-runtime/mcp-glue.mjs +177 -0
  196. package/src/session-runtime/model-recency.mjs +111 -0
  197. package/src/session-runtime/native-search.mjs +247 -0
  198. package/src/session-runtime/output-styles.mjs +11 -9
  199. package/src/session-runtime/prewarm.mjs +142 -0
  200. package/src/session-runtime/provider-models.mjs +278 -0
  201. package/src/session-runtime/provider-usage.mjs +120 -0
  202. package/src/session-runtime/quick-model-rows.mjs +170 -0
  203. package/src/session-runtime/quick-search-models.mjs +46 -0
  204. package/src/session-runtime/session-hooks.mjs +93 -0
  205. package/src/session-runtime/settings-api.mjs +319 -0
  206. package/src/session-runtime/tool-catalog.mjs +29 -29
  207. package/src/session-runtime/tool-defs.mjs +84 -0
  208. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  209. package/src/standalone/agent-tool/helpers.mjs +237 -0
  210. package/src/standalone/agent-tool/notify.mjs +107 -0
  211. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  212. package/src/standalone/agent-tool/render.mjs +152 -0
  213. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  214. package/src/standalone/agent-tool.mjs +110 -671
  215. package/src/standalone/channel-worker.mjs +4 -7
  216. package/src/standalone/explore-tool.mjs +30 -9
  217. package/src/standalone/hook-bus/config.mjs +207 -0
  218. package/src/standalone/hook-bus/constants.mjs +90 -0
  219. package/src/standalone/hook-bus/handlers.mjs +481 -0
  220. package/src/standalone/hook-bus/payload.mjs +31 -0
  221. package/src/standalone/hook-bus/rules.mjs +77 -0
  222. package/src/standalone/hook-bus.mjs +77 -870
  223. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  224. package/src/standalone/opencode-go-login.mjs +5 -1
  225. package/src/standalone/provider-admin.mjs +1 -16
  226. package/src/standalone/usage-dashboard.mjs +3 -1
  227. package/src/tui/App.jsx +945 -8094
  228. package/src/tui/app/app-format.mjs +206 -0
  229. package/src/tui/app/channel-pickers.mjs +510 -0
  230. package/src/tui/app/clipboard.mjs +67 -0
  231. package/src/tui/app/core-memory-picker.mjs +210 -0
  232. package/src/tui/app/extension-pickers.mjs +506 -0
  233. package/src/tui/app/input-parsers.mjs +193 -0
  234. package/src/tui/app/maintenance-pickers.mjs +324 -0
  235. package/src/tui/app/model-options.mjs +330 -0
  236. package/src/tui/app/model-picker.mjs +365 -0
  237. package/src/tui/app/onboarding-steps.mjs +400 -0
  238. package/src/tui/app/project-picker.mjs +247 -0
  239. package/src/tui/app/provider-setup-picker.mjs +580 -0
  240. package/src/tui/app/resume-picker.mjs +55 -0
  241. package/src/tui/app/route-pickers.mjs +419 -0
  242. package/src/tui/app/settings-picker.mjs +490 -0
  243. package/src/tui/app/slash-commands.mjs +101 -0
  244. package/src/tui/app/slash-dispatch.mjs +427 -0
  245. package/src/tui/app/text-layout.mjs +46 -0
  246. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  247. package/src/tui/app/transcript-window.mjs +671 -0
  248. package/src/tui/app/use-mouse-input.mjs +460 -0
  249. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  250. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  251. package/src/tui/app/use-transcript-window.mjs +589 -0
  252. package/src/tui/components/ConfirmBar.jsx +1 -1
  253. package/src/tui/components/Picker.jsx +32 -4
  254. package/src/tui/components/PromptInput.jsx +23 -101
  255. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  256. package/src/tui/components/StatusLine.jsx +63 -12
  257. package/src/tui/components/TextEntryPanel.jsx +11 -0
  258. package/src/tui/components/ToolExecution.jsx +52 -594
  259. package/src/tui/components/TranscriptItem.jsx +105 -0
  260. package/src/tui/components/UsagePanel.jsx +18 -4
  261. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  262. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  263. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  264. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  265. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  266. package/src/tui/display-width.mjs +20 -3
  267. package/src/tui/dist/index.mjs +19652 -18630
  268. package/src/tui/engine/agent-job-feed.mjs +133 -0
  269. package/src/tui/engine/notification-plan.mjs +76 -0
  270. package/src/tui/engine/render-timing.mjs +17 -0
  271. package/src/tui/engine/tool-approval.mjs +94 -0
  272. package/src/tui/engine/tool-card-results.mjs +234 -0
  273. package/src/tui/engine/tool-result-status.mjs +135 -0
  274. package/src/tui/engine.mjs +122 -562
  275. package/src/tui/figures.mjs +5 -0
  276. package/src/tui/index.jsx +105 -0
  277. package/src/tui/input-editing.mjs +2 -2
  278. package/src/tui/markdown/format-token.mjs +4 -1
  279. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  280. package/src/tui/theme.mjs +6 -0
  281. package/src/ui/statusline-agents.mjs +213 -0
  282. package/src/ui/statusline-format.mjs +146 -0
  283. package/src/ui/statusline-segments.mjs +148 -0
  284. package/src/ui/statusline.mjs +67 -501
  285. package/src/ui/tool-card.mjs +0 -1
  286. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  287. package/src/workflows/default/WORKFLOW.md +1 -1
  288. package/src/workflows/sequential/WORKFLOW.md +1 -1
  289. package/vendor/ink/build/display-width.js +19 -3
  290. package/vendor/ink/build/ink.js +103 -6
  291. package/vendor/ink/build/log-update.js +17 -3
  292. package/vendor/ink/build/wrap-text.js +125 -0
  293. package/scripts/_test-folder-dialog.mjs +0 -30
  294. package/scripts/fix-brief-fn.mjs +0 -35
  295. package/scripts/fix-format-tool-surface.mjs +0 -24
  296. package/scripts/fix-tool-exec-visible.mjs +0 -42
  297. package/scripts/patch-agent-brief.mjs +0 -48
  298. package/scripts/patch-app.mjs +0 -21
  299. package/scripts/patch-app2.mjs +0 -18
  300. package/scripts/patch-dist-brief.mjs +0 -96
  301. package/scripts/patch-tool-exec.mjs +0 -70
  302. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  303. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  304. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  305. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  306. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  307. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  308. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  309. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  310. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  311. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  312. package/src/standalone/agent-task-status.test.mjs +0 -76
  313. package/src/tui/components/tool-output-format.test.mjs +0 -399
  314. package/src/tui/display-width.test.mjs +0 -35
  315. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  316. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  317. package/src/tui/input-editing.selection.test.mjs +0 -75
  318. package/src/tui/markdown/format-token.test.mjs +0 -354
  319. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  320. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  321. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  322. package/src/tui/paste-fix.test.mjs +0 -119
  323. package/src/tui/prompt-history-store.test.mjs +0 -52
  324. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  325. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  326. package/src/ui/markdown.test.mjs +0 -70
  327. package/src/ui/statusline-context-label.test.mjs +0 -15
  328. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  329. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,760 @@
1
+ /**
2
+ * openai-compat-xai.mjs — xAI/Grok Responses-API routing, prompt-cache lanes
3
+ * and cache tracing for the OpenAI-compat provider.
4
+ *
5
+ * Extracted from openai-compat.mjs. Owns the xAI cache-lane singletons
6
+ * (xaiResponsesCacheLanes) plus cache routing/fingerprint/trace helpers and
7
+ * the compat cache trace writer shared by chat-completions and Responses
8
+ * paths. openai-compat.mjs imports the routing/lane/trace entry points.
9
+ */
10
+ import { createHash } from 'crypto';
11
+ import { appendAgentTrace } from '../agent-trace.mjs';
12
+ import {
13
+ resolveProviderCacheKey,
14
+ resolveProviderPromptCacheLane,
15
+ } from '../agent-runtime/cache-strategy.mjs';
16
+ import { shouldFallbackTransport } from './retry-classifier.mjs';
17
+ import { traceHash, stableTraceStringify, summarizeTraceTools, traceTextShape } from './trace-utils.mjs';
18
+ import { summarizeTraceMessages, extractCompatCachedTokens } from './openai-compat-trace.mjs';
19
+
20
+ export function xaiPrefixSeed({ opts, params, rawTools, model }) {
21
+ const providerKey = resolveProviderCacheKey(opts, 'xai');
22
+ const systemMessages = (params?.messages || [])
23
+ .filter(m => m?.role === 'system')
24
+ .map(m => String(m?.content ?? ''));
25
+ return stableTraceStringify({
26
+ scope: 'xai-prefix-model-system-tools',
27
+ providerKey: String(providerKey),
28
+ model: model || null,
29
+ systemMessages,
30
+ tools: summarizeTraceTools(rawTools),
31
+ });
32
+ }
33
+
34
+ export function xaiCacheRouting(opts, params, rawTools, model) {
35
+ const sessionId = String(opts?.sessionId || opts?.session?.id || '').trim();
36
+ const providerKey = resolveProviderCacheKey(opts, 'xai');
37
+ const prefixSeed = xaiPrefixSeed({ opts, params, rawTools, model });
38
+ const prefixHash = traceHash(prefixSeed);
39
+ const routingSeed = stableTraceStringify({
40
+ scope: 'xai-chat-session-v1',
41
+ providerKey: String(providerKey),
42
+ model: model || null,
43
+ sessionId: sessionId || `ephemeral:${process.pid}`,
44
+ });
45
+ return {
46
+ key: deterministicUuidFromKey(routingSeed),
47
+ mode: sessionId ? 'session' : 'ephemeral',
48
+ seedHash: traceHash(routingSeed),
49
+ prefixHash,
50
+ ownerSessionHash: sessionId ? traceHash(sessionId) : null,
51
+ };
52
+ }
53
+
54
+ export function xaiResponsesCacheRouting(opts, params, rawTools, model) {
55
+ // Default to 'prefix' so parallel workers sharing the same model + system
56
+ // + tools land on a common prompt_cache_key, letting xAI's server-side
57
+ // prefix cache hit across sessions instead of cold-starting per worker.
58
+ // Override with 'session' (env or opts) for legacy session-isolated lanes.
59
+ const scope = String(opts?.xaiResponsesCacheScope || process.env.MIXDOG_XAI_RESPONSES_CACHE_SCOPE || 'prefix')
60
+ .trim()
61
+ .toLowerCase();
62
+ if (scope !== 'prefix') {
63
+ return xaiCacheRouting(opts, params, rawTools, model);
64
+ }
65
+ const sessionId = String(opts?.sessionId || opts?.session?.id || '').trim();
66
+ const providerKey = resolveProviderCacheKey(opts, 'xai');
67
+ const prefixSeed = xaiPrefixSeed({ opts, params, rawTools, model });
68
+ const prefixHash = traceHash(prefixSeed);
69
+ const routingSeed = stableTraceStringify({
70
+ scope: 'xai-responses-prefix-v1',
71
+ providerKey: String(providerKey),
72
+ model: model || null,
73
+ prefixHash,
74
+ });
75
+ return {
76
+ key: deterministicUuidFromKey(routingSeed),
77
+ mode: 'prefix',
78
+ seedHash: traceHash(routingSeed),
79
+ prefixHash,
80
+ ownerSessionHash: sessionId ? traceHash(sessionId) : null,
81
+ };
82
+ }
83
+
84
+ export function normalizeXaiReasoningEffort(value) {
85
+ const effort = String(value || '').trim().toLowerCase();
86
+ return ['none', 'low', 'medium', 'high'].includes(effort) ? effort : null;
87
+ }
88
+
89
+ export function opencodeGoReasoningEffortValues(modelInfo) {
90
+ const effort = (modelInfo?.reasoningOptions || []).find((option) => option?.type === 'effort');
91
+ return Array.isArray(effort?.values)
92
+ ? effort.values.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean)
93
+ : [];
94
+ }
95
+
96
+ export function normalizeOpencodeGoReasoningEffort(value, modelInfo) {
97
+ const allowed = opencodeGoReasoningEffortValues(modelInfo);
98
+ if (!allowed.length) return null;
99
+ const effort = String(value || '').trim().toLowerCase();
100
+ if (allowed.includes(effort)) return effort;
101
+ if ((effort === 'max' || effort === 'xhigh') && allowed.includes('max')) return 'max';
102
+ if (['high', 'medium', 'low'].includes(effort) && allowed.includes('high')) return 'high';
103
+ return null;
104
+ }
105
+
106
+ export function useXaiResponsesApi(opts, config) {
107
+ const raw = opts?.xaiApiMode
108
+ ?? config?.apiMode
109
+ ?? config?.xaiApiMode
110
+ ?? process.env.MIXDOG_XAI_API_MODE
111
+ ?? process.env.MIXDOG_XAI_RESPONSES;
112
+ if (raw == null || raw === '') return true;
113
+ const mode = String(raw).trim().toLowerCase();
114
+ return !['0', 'false', 'off', 'chat', 'chat-completions', 'chat_completions'].includes(mode);
115
+ }
116
+
117
+ export function useXaiResponsesWebSocket(opts, config) {
118
+ const raw = opts?.xaiResponsesTransport
119
+ ?? opts?.xaiTransport
120
+ ?? config?.responsesTransport
121
+ ?? config?.transport
122
+ ?? process.env.MIXDOG_XAI_RESPONSES_TRANSPORT
123
+ ?? process.env.MIXDOG_XAI_TRANSPORT;
124
+ if (raw == null || raw === '') return true;
125
+ const transport = String(raw).trim().toLowerCase();
126
+ return !['0', 'false', 'off', 'http', 'https', 'responses-http', 'sdk'].includes(transport);
127
+ }
128
+
129
+ function _envFlag(name, fallback = true) {
130
+ const raw = process.env[name];
131
+ if (raw == null || raw === '') return fallback;
132
+ return !['0', 'false', 'off', 'no'].includes(String(raw).toLowerCase());
133
+ }
134
+
135
+ // xAI WS→HTTP transport fallback → shared shouldFallbackTransport
136
+ // (retry-classifier.mjs). Identical deny-order + allow-list; the per-provider
137
+ // env flag is computed here and passed via `enabled`.
138
+ export function _shouldFallbackXaiWsToHttp(err, signal) {
139
+ return shouldFallbackTransport(err, {
140
+ signal,
141
+ enabled: _envFlag('MIXDOG_XAI_WS_HTTP_FALLBACK', true),
142
+ });
143
+ }
144
+
145
+ export function useXaiResponsesWebSocketWarmup(opts, config, { previousResponseId, instructions, rawTools }) {
146
+ if (previousResponseId) return false;
147
+ const raw = opts?.xaiResponsesWarmup
148
+ ?? opts?.xaiWsWarmup
149
+ ?? config?.responsesWarmup
150
+ ?? config?.wsWarmup
151
+ ?? process.env.MIXDOG_XAI_RESPONSES_WARMUP
152
+ ?? process.env.MIXDOG_XAI_WS_WARMUP;
153
+ if (raw != null && raw !== '') {
154
+ const mode = String(raw).trim().toLowerCase();
155
+ if (['0', 'false', 'off', 'none', 'disabled'].includes(mode)) return false;
156
+ if (['1', 'true', 'on', 'always', 'force'].includes(mode)) return true;
157
+ }
158
+ return String(instructions || '').length >= 2048 || (Array.isArray(rawTools) && rawTools.length >= 10);
159
+ }
160
+
161
+ // Codex-aligned default: no compat cache lane/serialization. Keep the override
162
+ // knobs so live probes can opt back into a bounded lane if a provider needs it.
163
+ const XAI_RESPONSES_CACHE_LANE_DEFAULT_MAX_IN_FLIGHT = 0;
164
+ const xaiResponsesCacheLanes = new Map();
165
+
166
+ function parseXaiPositiveInt(value, fallback) {
167
+ if (value == null || value === '') return fallback;
168
+ const text = String(value).trim().toLowerCase();
169
+ if (['0', 'false', 'off', 'none', 'disabled', 'unlimited', 'unbounded', 'auto'].includes(text)) return 0;
170
+ const n = Number(text);
171
+ if (!Number.isFinite(n)) return fallback;
172
+ return Math.max(0, Math.floor(n));
173
+ }
174
+
175
+ function xaiResponsesCacheLaneMaxInFlight(opts, config) {
176
+ return parseXaiPositiveInt(
177
+ opts?.xaiCacheMaxInFlight
178
+ ?? opts?.xaiResponsesCacheMaxInFlight
179
+ ?? opts?.grokCacheMaxInFlight
180
+ ?? opts?.grokResponsesCacheMaxInFlight
181
+ ?? config?.xaiCacheMaxInFlight
182
+ ?? config?.xaiResponsesCacheMaxInFlight
183
+ ?? config?.grokCacheMaxInFlight
184
+ ?? config?.grokResponsesCacheMaxInFlight
185
+ ?? process.env.MIXDOG_XAI_CACHE_MAX_INFLIGHT
186
+ ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_MAX_INFLIGHT
187
+ ?? process.env.MIXDOG_GROK_CACHE_MAX_INFLIGHT
188
+ ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_MAX_INFLIGHT
189
+ ?? process.env.MIXDOG_GROK_OAUTH_CACHE_MAX_INFLIGHT
190
+ ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_MAX_INFLIGHT,
191
+ XAI_RESPONSES_CACHE_LANE_DEFAULT_MAX_IN_FLIGHT,
192
+ );
193
+ }
194
+
195
+ function xaiResponsesCacheLaneQueueTimeoutMs(opts, config) {
196
+ return parseXaiPositiveInt(
197
+ opts?.xaiCacheQueueTimeoutMs
198
+ ?? opts?.xaiResponsesCacheQueueTimeoutMs
199
+ ?? opts?.grokCacheQueueTimeoutMs
200
+ ?? opts?.grokResponsesCacheQueueTimeoutMs
201
+ ?? config?.xaiCacheQueueTimeoutMs
202
+ ?? config?.xaiResponsesCacheQueueTimeoutMs
203
+ ?? config?.grokCacheQueueTimeoutMs
204
+ ?? config?.grokResponsesCacheQueueTimeoutMs
205
+ ?? process.env.MIXDOG_XAI_CACHE_QUEUE_TIMEOUT_MS
206
+ ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_QUEUE_TIMEOUT_MS
207
+ ?? process.env.MIXDOG_GROK_CACHE_QUEUE_TIMEOUT_MS
208
+ ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_QUEUE_TIMEOUT_MS
209
+ ?? process.env.MIXDOG_GROK_OAUTH_CACHE_QUEUE_TIMEOUT_MS
210
+ ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_QUEUE_TIMEOUT_MS,
211
+ 0,
212
+ );
213
+ }
214
+
215
+ function xaiResponsesPromptCacheLane(opts, config, cacheRouting) {
216
+ const shardOverride =
217
+ opts?.xaiCacheLaneShards
218
+ ?? opts?.xaiResponsesCacheLaneShards
219
+ ?? opts?.xaiCacheMaxParallel
220
+ ?? opts?.xaiResponsesCacheMaxParallel
221
+ ?? opts?.grokCacheLaneShards
222
+ ?? opts?.grokResponsesCacheLaneShards
223
+ ?? opts?.grokCacheMaxParallel
224
+ ?? opts?.grokResponsesCacheMaxParallel
225
+ ?? config?.xaiCacheLaneShards
226
+ ?? config?.xaiResponsesCacheLaneShards
227
+ ?? config?.xaiCacheMaxParallel
228
+ ?? config?.xaiResponsesCacheMaxParallel
229
+ ?? config?.grokCacheLaneShards
230
+ ?? config?.grokResponsesCacheLaneShards
231
+ ?? config?.grokCacheMaxParallel
232
+ ?? config?.grokResponsesCacheMaxParallel
233
+ ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_MAX_PARALLEL
234
+ ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_LANE_SHARDS
235
+ ?? process.env.MIXDOG_GROK_CACHE_MAX_PARALLEL
236
+ ?? process.env.MIXDOG_GROK_CACHE_LANE_SHARDS
237
+ ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_MAX_PARALLEL
238
+ ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_LANE_SHARDS
239
+ ?? process.env.MIXDOG_GROK_OAUTH_CACHE_MAX_PARALLEL
240
+ ?? process.env.MIXDOG_GROK_OAUTH_CACHE_LANE_SHARDS
241
+ ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_MAX_PARALLEL
242
+ ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_LANE_SHARDS;
243
+ const autoOverride =
244
+ opts?.xaiCacheLaneAuto
245
+ ?? opts?.xaiResponsesCacheLaneAuto
246
+ ?? opts?.grokCacheLaneAuto
247
+ ?? opts?.grokResponsesCacheLaneAuto
248
+ ?? config?.xaiCacheLaneAuto
249
+ ?? config?.xaiResponsesCacheLaneAuto
250
+ ?? config?.grokCacheLaneAuto
251
+ ?? config?.grokResponsesCacheLaneAuto
252
+ ?? process.env.MIXDOG_XAI_RESPONSES_CACHE_LANE_AUTO
253
+ ?? process.env.MIXDOG_GROK_CACHE_LANE_AUTO
254
+ ?? process.env.MIXDOG_GROK_RESPONSES_CACHE_LANE_AUTO
255
+ ?? process.env.MIXDOG_GROK_OAUTH_CACHE_LANE_AUTO
256
+ ?? process.env.MIXDOG_GROK_OAUTH_RESPONSES_CACHE_LANE_AUTO;
257
+ const slotOverride =
258
+ opts?.xaiCacheLaneSlot
259
+ ?? opts?.xaiResponsesCacheLaneSlot
260
+ ?? opts?.grokCacheLaneSlot
261
+ ?? opts?.grokResponsesCacheLaneSlot;
262
+ const seed = String(
263
+ opts?.xaiCacheLaneSeed
264
+ ?? opts?.xaiResponsesCacheLaneSeed
265
+ ?? opts?.grokCacheLaneSeed
266
+ ?? opts?.grokResponsesCacheLaneSeed
267
+ ?? opts?.promptCacheLaneSeed
268
+ ?? opts?.sessionId
269
+ ?? opts?.session?.id
270
+ ?? cacheRouting?.ownerSessionHash
271
+ ?? cacheRouting?.key
272
+ ?? '',
273
+ );
274
+ return resolveProviderPromptCacheLane('xai', {
275
+ ...opts,
276
+ ...(shardOverride !== undefined ? { promptCacheLaneShards: shardOverride } : {}),
277
+ ...(autoOverride !== undefined ? { promptCacheLaneAuto: autoOverride } : {}),
278
+ ...(slotOverride !== undefined ? { promptCacheLaneSlot: slotOverride } : {}),
279
+ promptCacheLaneSeed: seed || 'xai-cache-lane',
280
+ }, config);
281
+ }
282
+
283
+ function xaiResponsesCacheLaneKey({ model, cacheRouting, opts, config }) {
284
+ const prefix = cacheRouting?.prefixHash || cacheRouting?.seedHash || cacheRouting?.key || 'unknown-prefix';
285
+ const lane = xaiResponsesPromptCacheLane(opts, config, cacheRouting);
286
+ const shard = Number.isFinite(Number(lane?.slot)) ? Number(lane.slot) : 0;
287
+ return {
288
+ key: `xai-responses:${model || 'default'}:${prefix}:shard-${shard}`,
289
+ shard,
290
+ lane,
291
+ };
292
+ }
293
+
294
+ function getXaiResponsesCacheLaneState(key, maxInFlight) {
295
+ let state = xaiResponsesCacheLanes.get(key);
296
+ if (!state) {
297
+ state = { key, active: 0, queue: [], maxInFlight, nextId: 0 };
298
+ xaiResponsesCacheLanes.set(key, state);
299
+ }
300
+ state.maxInFlight = maxInFlight;
301
+ return state;
302
+ }
303
+
304
+ function cleanupXaiResponsesCacheLane(state) {
305
+ if (state.active === 0 && state.queue.length === 0) {
306
+ xaiResponsesCacheLanes.delete(state.key);
307
+ }
308
+ }
309
+
310
+ function removeQueuedXaiCacheLaneRequest(state, request) {
311
+ const index = state.queue.indexOf(request);
312
+ if (index >= 0) state.queue.splice(index, 1);
313
+ cleanupXaiResponsesCacheLane(state);
314
+ }
315
+
316
+ function makeXaiCacheLaneHandle(state, requestId, enqueuedAt) {
317
+ let released = false;
318
+ return {
319
+ requestId,
320
+ waitedMs: Date.now() - enqueuedAt,
321
+ activeCount: state.active,
322
+ queueDepth: state.queue.length,
323
+ release() {
324
+ if (released) return;
325
+ released = true;
326
+ releaseXaiResponsesCacheLane(state);
327
+ },
328
+ };
329
+ }
330
+
331
+ function releaseXaiResponsesCacheLane(state) {
332
+ state.active = Math.max(0, state.active - 1);
333
+ while (state.queue.length > 0 && state.active < state.maxInFlight) {
334
+ const next = state.queue.shift();
335
+ next.cleanup?.();
336
+ state.active += 1;
337
+ next.resolve(makeXaiCacheLaneHandle(state, next.requestId, next.enqueuedAt));
338
+ }
339
+ cleanupXaiResponsesCacheLane(state);
340
+ }
341
+
342
+ function acquireXaiResponsesCacheLane({ key, maxInFlight, signal, timeoutMs }) {
343
+ const state = getXaiResponsesCacheLaneState(key, maxInFlight);
344
+ const requestId = ++state.nextId;
345
+ const enqueuedAt = Date.now();
346
+ if (state.active < state.maxInFlight) {
347
+ state.active += 1;
348
+ return Promise.resolve(makeXaiCacheLaneHandle(state, requestId, enqueuedAt));
349
+ }
350
+ return new Promise((resolve, reject) => {
351
+ const request = {
352
+ requestId,
353
+ enqueuedAt,
354
+ resolve,
355
+ reject,
356
+ cleanup: null,
357
+ };
358
+ const cleanup = () => {
359
+ if (request.timer) clearTimeout(request.timer);
360
+ if (signal && request.abortListener) signal.removeEventListener('abort', request.abortListener);
361
+ };
362
+ request.cleanup = cleanup;
363
+ request.abortListener = () => {
364
+ cleanup();
365
+ removeQueuedXaiCacheLaneRequest(state, request);
366
+ const reason = signal?.reason;
367
+ reject(reason instanceof Error ? reason : new Error('xAI cache lane wait aborted'));
368
+ };
369
+ if (signal?.aborted) {
370
+ request.abortListener();
371
+ return;
372
+ }
373
+ if (signal) signal.addEventListener('abort', request.abortListener, { once: true });
374
+ if (timeoutMs > 0) {
375
+ request.timer = setTimeout(() => {
376
+ cleanup();
377
+ removeQueuedXaiCacheLaneRequest(state, request);
378
+ reject(new Error(`xAI cache lane wait timed out after ${timeoutMs}ms`));
379
+ }, timeoutMs);
380
+ request.timer.unref?.();
381
+ }
382
+ state.queue.push(request);
383
+ });
384
+ }
385
+
386
+ function traceXaiCacheLane(opts, payload) {
387
+ if (!compatCacheTraceEnabled('xai')) return;
388
+ try {
389
+ appendAgentTrace({
390
+ sessionId: opts?.sessionId || opts?.session?.id || null,
391
+ iteration: Number.isFinite(Number(opts?.iteration)) ? Number(opts.iteration) : null,
392
+ kind: 'cache_lane',
393
+ ...payload,
394
+ payload,
395
+ });
396
+ } catch {}
397
+ }
398
+
399
+ export async function withXaiResponsesCacheLane({ opts, config, cacheRouting, model, transport, previousResponseId, inputCount, signal }, fn) {
400
+ const maxInFlight = xaiResponsesCacheLaneMaxInFlight(opts, config);
401
+ if (maxInFlight <= 0) {
402
+ const laneMeta = { enabled: false, maxInFlight: 0 };
403
+ return { value: await fn(laneMeta), laneMeta };
404
+ }
405
+ const { key: laneKey, shard, lane } = xaiResponsesCacheLaneKey({ model, cacheRouting, opts, config });
406
+ const timeoutMs = xaiResponsesCacheLaneQueueTimeoutMs(opts, config);
407
+ const state = getXaiResponsesCacheLaneState(laneKey, maxInFlight);
408
+ const queued = state.active >= state.maxInFlight;
409
+ if (queued) {
410
+ traceXaiCacheLane(opts, {
411
+ provider: 'xai',
412
+ api: 'responses',
413
+ transport,
414
+ event: 'queued',
415
+ lane_key_hash: traceHash(laneKey),
416
+ lane_shard: shard,
417
+ lane_shards: Number.isFinite(Number(lane?.shards)) ? Number(lane.shards) : null,
418
+ lane_auto: lane?.auto === true,
419
+ lane_seed_hash: lane?.seedHash || null,
420
+ max_in_flight: maxInFlight,
421
+ active: state.active,
422
+ queue_depth: state.queue.length,
423
+ previous_response_used: !!previousResponseId,
424
+ input_count: inputCount,
425
+ });
426
+ }
427
+ const handle = await acquireXaiResponsesCacheLane({ key: laneKey, maxInFlight, signal, timeoutMs });
428
+ const laneMeta = {
429
+ enabled: true,
430
+ laneKeyHash: traceHash(laneKey),
431
+ shard,
432
+ shards: Number.isFinite(Number(lane?.shards)) ? Number(lane.shards) : null,
433
+ auto: lane?.auto === true,
434
+ seedHash: lane?.seedHash || null,
435
+ maxInFlight,
436
+ queued,
437
+ waitMs: handle.waitedMs,
438
+ activeAfterAcquire: handle.activeCount,
439
+ queueDepthAfterAcquire: handle.queueDepth,
440
+ };
441
+ traceXaiCacheLane(opts, {
442
+ provider: 'xai',
443
+ api: 'responses',
444
+ transport,
445
+ event: 'acquired',
446
+ lane_key_hash: laneMeta.laneKeyHash,
447
+ lane_shard: shard,
448
+ lane_shards: laneMeta.shards,
449
+ lane_auto: laneMeta.auto,
450
+ lane_seed_hash: laneMeta.seedHash,
451
+ max_in_flight: maxInFlight,
452
+ wait_ms: laneMeta.waitMs,
453
+ active: laneMeta.activeAfterAcquire,
454
+ queue_depth: laneMeta.queueDepthAfterAcquire,
455
+ previous_response_used: !!previousResponseId,
456
+ input_count: inputCount,
457
+ });
458
+ const startedAt = Date.now();
459
+ try {
460
+ return { value: await fn(laneMeta), laneMeta };
461
+ } finally {
462
+ handle.release();
463
+ traceXaiCacheLane(opts, {
464
+ provider: 'xai',
465
+ api: 'responses',
466
+ transport,
467
+ event: 'released',
468
+ lane_key_hash: laneMeta.laneKeyHash,
469
+ lane_shard: shard,
470
+ lane_shards: laneMeta.shards,
471
+ lane_auto: laneMeta.auto,
472
+ lane_seed_hash: laneMeta.seedHash,
473
+ max_in_flight: maxInFlight,
474
+ held_ms: Date.now() - startedAt,
475
+ previous_response_used: !!previousResponseId,
476
+ input_count: inputCount,
477
+ });
478
+ }
479
+ }
480
+
481
+ export function deterministicUuidFromKey(key) {
482
+ const hex = createHash('sha256').update(String(key ?? '')).digest('hex');
483
+ const variant = ((Number.parseInt(hex[16], 16) & 0x3) | 0x8).toString(16);
484
+ return [
485
+ hex.slice(0, 8),
486
+ hex.slice(8, 12),
487
+ '4' + hex.slice(13, 16),
488
+ variant + hex.slice(17, 20),
489
+ hex.slice(20, 32),
490
+ ].join('-');
491
+ }
492
+
493
+ export function compatCacheTraceEnabled(provider) {
494
+ return process.env.MIXDOG_COMPAT_CACHE_TRACE === '1'
495
+ || process.env.MIXDOG_PROVIDER_CACHE_TRACE === '1'
496
+ || (provider === 'xai' && process.env.MIXDOG_XAI_CACHE_TRACE === '1');
497
+ }
498
+
499
+ export function writeCompatCacheTrace({ provider, model, opts, params, rawTools, response, cacheRoutingKey, cacheRouting }) {
500
+ if (!compatCacheTraceEnabled(provider)) return;
501
+ try {
502
+ const usage = response?.usage || {};
503
+ const inputTokens = Number(usage.prompt_tokens ?? usage.input_tokens ?? 0);
504
+ const cachedTokens = extractCompatCachedTokens(usage);
505
+ const toolShape = summarizeTraceTools(rawTools);
506
+ const traceMessages = Array.isArray(params?.messages) ? params.messages : [];
507
+ const trace = {
508
+ event: 'chat.completions',
509
+ provider,
510
+ model,
511
+ responseModel: response?.model || null,
512
+ owner: opts?.session?.owner || null,
513
+ role: opts?.session?.role || opts?.role || null,
514
+ permission: opts?.session?.permission || null,
515
+ toolPermission: opts?.session?.toolPermission || null,
516
+ profileId: opts?.session?.profileId || null,
517
+ sourceType: opts?.session?.sourceType || null,
518
+ sourceName: opts?.session?.sourceName || null,
519
+ sessionIdHash: opts?.sessionId ? traceHash(opts.sessionId) : null,
520
+ providerCacheKeyHash: opts?.providerCacheKey ? traceHash(opts.providerCacheKey) : null,
521
+ promptCacheKeyHash: opts?.promptCacheKey ? traceHash(opts.promptCacheKey) : null,
522
+ xGrokConvIdHash: provider === 'xai' && cacheRoutingKey ? traceHash(cacheRoutingKey) : null,
523
+ xGrokConvIdSeedHash: provider === 'xai' ? cacheRouting?.seedHash || null : null,
524
+ xGrokPromptPrefixHash: provider === 'xai' ? cacheRouting?.prefixHash || null : null,
525
+ xGrokConvIdMode: provider === 'xai' ? cacheRouting?.mode || null : null,
526
+ xGrokConvIdLaneIndex: provider === 'xai' ? cacheRouting?.laneIndex ?? null : null,
527
+ xGrokConvIdActiveLanes: provider === 'xai' ? cacheRouting?.activeLanes ?? null : null,
528
+ xGrokConvIdIdleLanes: provider === 'xai' ? cacheRouting?.idleLanes ?? null : null,
529
+ xGrokConvIdOwnerSessionHash: provider === 'xai' ? cacheRouting?.ownerSessionHash || null : null,
530
+ xaiReasoningEffort: provider === 'xai' ? params?.reasoning_effort || null : null,
531
+ messageCount: traceMessages.length,
532
+ messageFullHash: traceHash(stableTraceStringify(traceMessages)),
533
+ messagePrefixHash: traceHash(stableTraceStringify(traceMessages.slice(0, -1))),
534
+ lastMessageHash: traceMessages.length ? traceHash(stableTraceStringify(traceMessages.at(-1))) : null,
535
+ messages: summarizeTraceMessages(traceMessages),
536
+ toolCount: Array.isArray(rawTools) ? rawTools.length : 0,
537
+ toolSchemaHash: traceHash(stableTraceStringify(toolShape)),
538
+ usageKeys: Object.keys(usage || {}).sort(),
539
+ promptTokenDetailsKeys: Object.keys(usage?.prompt_tokens_details || {}).sort(),
540
+ inputTokenDetailsKeys: Object.keys(usage?.input_tokens_details || {}).sort(),
541
+ choiceMessageKeys: Object.keys(response?.choices?.[0]?.message || {}).sort(),
542
+ responseReasoningContent: typeof response?.choices?.[0]?.message?.reasoning_content === 'string'
543
+ ? traceTextShape(response.choices[0].message.reasoning_content)
544
+ : null,
545
+ responseReasoningTokens: Number(usage?.completion_tokens_details?.reasoning_tokens ?? 0),
546
+ inputTokens,
547
+ outputTokens: Number(usage.completion_tokens ?? usage.output_tokens ?? 0),
548
+ cachedTokens,
549
+ cacheHitRate: inputTokens > 0 ? Number((cachedTokens / inputTokens).toFixed(6)) : null,
550
+ costInUsdTicks: typeof usage.cost_in_usd_ticks === 'number' ? usage.cost_in_usd_ticks : null,
551
+ };
552
+ process.stderr.write(`[compat-cache-trace] ${JSON.stringify(trace)}\n`);
553
+ } catch (err) {
554
+ process.stderr.write(`[compat-cache-trace] failed: ${err?.message || err}\n`);
555
+ }
556
+ }
557
+
558
+ function summarizeResponsesInput(input) {
559
+ return (input || []).map((item, index) => ({
560
+ index,
561
+ type: item?.type || null,
562
+ role: item?.role || null,
563
+ callIdHash: item?.call_id ? traceHash(item.call_id) : null,
564
+ name: item?.name || null,
565
+ content: typeof item?.content === 'string'
566
+ ? { type: 'text', ...traceTextShape(item.content) }
567
+ : { type: item?.content == null ? 'null' : typeof item.content, hash: traceHash(stableTraceStringify(item?.content ?? null)) },
568
+ output: typeof item?.output === 'string' ? traceTextShape(item.output) : null,
569
+ }));
570
+ }
571
+
572
+ function xaiUsageStats(usage) {
573
+ const inputTokens = Number(usage?.input_tokens ?? usage?.prompt_tokens ?? 0);
574
+ const outputTokens = Number(usage?.output_tokens ?? usage?.completion_tokens ?? 0);
575
+ const cachedTokens = extractCompatCachedTokens(usage);
576
+ const hitRate = inputTokens > 0 ? Number((cachedTokens / inputTokens).toFixed(6)) : null;
577
+ return { inputTokens, outputTokens, cachedTokens, hitRate };
578
+ }
579
+
580
+ function xaiSanitizedRequestSansInput(params) {
581
+ const { input: _input, ...rest } = params || {};
582
+ const out = { ...rest };
583
+ if (out.prompt_cache_key) out.prompt_cache_key = traceHash(out.prompt_cache_key);
584
+ if (out.previous_response_id) out.previous_response_id = traceHash(out.previous_response_id);
585
+ if (typeof out.instructions === 'string') out.instructions = traceHash(out.instructions);
586
+ return out;
587
+ }
588
+
589
+ function xaiResponsesFingerprintPayload({ model, opts, params, rawTools, response, cacheRouting, previousResponseId, inputStartIndex, continuationResetReason, transport, cacheLane }) {
590
+ const usage = response?.usage || {};
591
+ const { inputTokens, outputTokens, cachedTokens, hitRate } = xaiUsageStats(usage);
592
+ const toolShape = summarizeTraceTools(rawTools);
593
+ const instructions = typeof params?.instructions === 'string' ? params.instructions : '';
594
+ const requestSansInput = xaiSanitizedRequestSansInput(params);
595
+ const contextShape = {
596
+ provider: 'xai',
597
+ api: 'responses',
598
+ model: model || null,
599
+ promptCacheKeyHash: params?.prompt_cache_key ? traceHash(params.prompt_cache_key) : null,
600
+ instructions,
601
+ tools: toolShape,
602
+ reasoning: params?.reasoning || null,
603
+ store: params?.store ?? null,
604
+ };
605
+ const previousResponseUsed = Boolean(previousResponseId);
606
+ const midTurnCold = previousResponseUsed
607
+ && inputTokens >= 1024
608
+ && (cachedTokens <= 512 || (hitRate != null && hitRate < 0.1));
609
+ return {
610
+ provider: 'xai',
611
+ api: 'responses',
612
+ transport: transport || null,
613
+ model: model || null,
614
+ response_model: response?.model || null,
615
+ session_id_hash: opts?.sessionId ? traceHash(opts.sessionId) : null,
616
+ provider_cache_key_hash: opts?.providerCacheKey ? traceHash(opts.providerCacheKey) : null,
617
+ prompt_cache_key_option_hash: opts?.promptCacheKey ? traceHash(opts.promptCacheKey) : null,
618
+ prompt_cache_key_hash: params?.prompt_cache_key ? traceHash(params.prompt_cache_key) : null,
619
+ xai_prompt_prefix_hash: cacheRouting?.prefixHash || null,
620
+ xai_cache_mode: cacheRouting?.mode || null,
621
+ xai_cache_seed_hash: cacheRouting?.seedHash || null,
622
+ owner_session_hash: cacheRouting?.ownerSessionHash || null,
623
+ response_id_hash: response?.id ? traceHash(response.id) : null,
624
+ previous_response_id_hash: previousResponseId ? traceHash(previousResponseId) : null,
625
+ previous_response_used: previousResponseUsed,
626
+ continuation_reset_reason: continuationResetReason || null,
627
+ input_start_index: inputStartIndex,
628
+ input_count: Array.isArray(params?.input) ? params.input.length : 0,
629
+ input_hash: traceHash(stableTraceStringify(params?.input || [])),
630
+ request_sans_input_hash: traceHash(stableTraceStringify(requestSansInput)),
631
+ context_prefix_hash: traceHash(stableTraceStringify(contextShape)),
632
+ has_instructions: instructions.length > 0,
633
+ instructions_chars: instructions.length,
634
+ instructions_hash: instructions ? traceHash(instructions) : null,
635
+ reasoning_effort: params?.reasoning?.effort || null,
636
+ tool_count: Array.isArray(rawTools) ? rawTools.length : 0,
637
+ tool_schema_hash: traceHash(stableTraceStringify(toolShape)),
638
+ tool_names_hash: traceHash(stableTraceStringify(toolShape.map(t => t?.name || null))),
639
+ xai_cache_lane_enabled: cacheLane?.enabled === true,
640
+ xai_cache_lane_hash: cacheLane?.laneKeyHash || null,
641
+ xai_cache_lane_shard: Number.isFinite(Number(cacheLane?.shard)) ? Number(cacheLane.shard) : null,
642
+ xai_cache_lane_shards: Number.isFinite(Number(cacheLane?.shards)) ? Number(cacheLane.shards) : null,
643
+ xai_cache_lane_auto: cacheLane?.auto === true,
644
+ xai_cache_lane_seed_hash: cacheLane?.seedHash || null,
645
+ xai_cache_lane_max_in_flight: Number.isFinite(Number(cacheLane?.maxInFlight)) ? Number(cacheLane.maxInFlight) : null,
646
+ xai_cache_lane_wait_ms: Number.isFinite(Number(cacheLane?.waitMs)) ? Number(cacheLane.waitMs) : null,
647
+ xai_cache_lane_queued: cacheLane?.queued === true,
648
+ input_tokens: inputTokens,
649
+ output_tokens: outputTokens,
650
+ cached_tokens: cachedTokens,
651
+ cache_hit_rate: hitRate,
652
+ mid_turn_cold: midTurnCold,
653
+ };
654
+ }
655
+
656
+ export function traceXaiResponsesCacheContext(args) {
657
+ if (!compatCacheTraceEnabled('xai')) return;
658
+ try {
659
+ const payload = xaiResponsesFingerprintPayload(args);
660
+ const sessionId = args?.opts?.sessionId || args?.opts?.session?.id || null;
661
+ const iteration = Number.isFinite(Number(args?.opts?.iteration)) ? Number(args.opts.iteration) : null;
662
+ appendAgentTrace({
663
+ sessionId,
664
+ iteration,
665
+ kind: 'cache_context',
666
+ ...payload,
667
+ payload,
668
+ });
669
+ if (payload.mid_turn_cold) {
670
+ const anomalyPayload = {
671
+ ...payload,
672
+ anomaly: 'xai_mid_turn_cold_cache',
673
+ reason: 'previous_response_id_present_but_cached_tokens_low',
674
+ };
675
+ appendAgentTrace({
676
+ sessionId,
677
+ iteration,
678
+ kind: 'cache_anomaly',
679
+ ...anomalyPayload,
680
+ payload: anomalyPayload,
681
+ });
682
+ }
683
+ } catch (err) {
684
+ process.stderr.write(`[compat-cache-trace] xai context trace failed: ${err?.message || err}\n`);
685
+ }
686
+ }
687
+
688
+ export function writeXaiResponsesCacheTrace({ model, opts, params, rawTools, response, cacheRouting, previousResponseId, inputStartIndex, continuationResetReason, transport, cacheLane }) {
689
+ if (!compatCacheTraceEnabled('xai')) return;
690
+ try {
691
+ const usage = response?.usage || {};
692
+ const fingerprint = xaiResponsesFingerprintPayload({
693
+ model,
694
+ opts,
695
+ params,
696
+ rawTools,
697
+ response,
698
+ cacheRouting,
699
+ previousResponseId,
700
+ inputStartIndex,
701
+ continuationResetReason,
702
+ transport,
703
+ cacheLane,
704
+ });
705
+ const inputTokens = fingerprint.input_tokens;
706
+ const cachedTokens = fingerprint.cached_tokens;
707
+ const toolShape = summarizeTraceTools(rawTools);
708
+ const trace = {
709
+ event: 'responses',
710
+ provider: 'xai',
711
+ transport: transport || null,
712
+ model,
713
+ responseModel: response?.model || null,
714
+ responseIdHash: response?.id ? traceHash(response.id) : null,
715
+ previousResponseIdHash: previousResponseId ? traceHash(previousResponseId) : null,
716
+ owner: opts?.session?.owner || null,
717
+ role: opts?.session?.role || opts?.role || null,
718
+ permission: opts?.session?.permission || null,
719
+ toolPermission: opts?.session?.toolPermission || null,
720
+ profileId: opts?.session?.profileId || null,
721
+ sourceType: opts?.session?.sourceType || null,
722
+ sourceName: opts?.session?.sourceName || null,
723
+ sessionIdHash: opts?.sessionId ? traceHash(opts.sessionId) : null,
724
+ promptCacheKeyHash: params?.prompt_cache_key ? traceHash(params.prompt_cache_key) : null,
725
+ xGrokPromptPrefixHash: cacheRouting?.prefixHash || null,
726
+ xGrokConvIdMode: cacheRouting?.mode || null,
727
+ xaiReasoningEffort: params?.reasoning?.effort || null,
728
+ previousResponseUsed: Boolean(previousResponseId),
729
+ inputStartIndex,
730
+ inputCount: Array.isArray(params?.input) ? params.input.length : 0,
731
+ cacheLaneEnabled: fingerprint.xai_cache_lane_enabled,
732
+ cacheLaneHash: fingerprint.xai_cache_lane_hash,
733
+ cacheLaneShard: fingerprint.xai_cache_lane_shard,
734
+ cacheLaneMaxInFlight: fingerprint.xai_cache_lane_max_in_flight,
735
+ cacheLaneWaitMs: fingerprint.xai_cache_lane_wait_ms,
736
+ cacheLaneQueued: fingerprint.xai_cache_lane_queued,
737
+ input: summarizeResponsesInput(params?.input || []),
738
+ toolCount: Array.isArray(rawTools) ? rawTools.length : 0,
739
+ toolSchemaHash: traceHash(stableTraceStringify(toolShape)),
740
+ toolNamesHash: fingerprint.tool_names_hash,
741
+ requestSansInputHash: fingerprint.request_sans_input_hash,
742
+ contextPrefixHash: fingerprint.context_prefix_hash,
743
+ instructionsHash: fingerprint.instructions_hash,
744
+ instructionsChars: fingerprint.instructions_chars,
745
+ usageKeys: Object.keys(usage || {}).sort(),
746
+ inputTokenDetailsKeys: Object.keys(usage?.input_tokens_details || {}).sort(),
747
+ outputTokenDetailsKeys: Object.keys(usage?.output_tokens_details || {}).sort(),
748
+ outputTypes: (response?.output || []).map(item => item?.type || null),
749
+ inputTokens,
750
+ outputTokens: fingerprint.output_tokens,
751
+ cachedTokens,
752
+ cacheHitRate: fingerprint.cache_hit_rate,
753
+ midTurnCold: fingerprint.mid_turn_cold,
754
+ costInUsdTicks: typeof usage.cost_in_usd_ticks === 'number' ? usage.cost_in_usd_ticks : null,
755
+ };
756
+ process.stderr.write(`[compat-cache-trace] ${JSON.stringify(trace)}\n`);
757
+ } catch (err) {
758
+ process.stderr.write(`[compat-cache-trace] failed: ${err?.message || err}\n`);
759
+ }
760
+ }