mixdog 0.9.63 → 0.9.65

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 (408) hide show
  1. package/README.md +3 -0
  2. package/package.json +9 -2
  3. package/scripts/code-graph-description-contract.mjs +3 -3
  4. package/scripts/verify-release-assets.mjs +2 -2
  5. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
  6. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
  7. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  8. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  9. package/src/runtime/agent/orchestrator/config.mjs +19 -124
  10. package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
  11. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
  12. package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
  13. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
  14. package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
  15. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
  16. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
  17. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
  18. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
  19. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
  20. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
  24. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
  25. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
  27. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
  30. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
  32. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
  37. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
  39. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
  41. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
  46. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
  49. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
  50. package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
  51. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
  54. package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
  57. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
  59. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
  60. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
  61. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
  62. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
  64. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
  66. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
  68. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
  69. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
  70. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
  73. package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
  74. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
  75. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
  77. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  79. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  81. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
  83. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
  91. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
  92. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
  93. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
  96. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
  97. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
  99. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
  102. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
  105. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
  106. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
  107. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
  108. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
  109. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
  110. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
  111. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
  112. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
  115. package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
  116. package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
  119. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
  120. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
  121. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  123. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
  124. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
  127. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
  129. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
  130. package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
  131. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  132. package/src/runtime/channels/lib/telegram-format.mjs +1 -1
  133. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
  134. package/src/runtime/channels/tool-defs.mjs +2 -3
  135. package/src/runtime/memory/index.mjs +1 -5
  136. package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
  137. package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
  138. package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
  139. package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
  140. package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
  141. package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
  142. package/src/runtime/memory/lib/ko-morph.mjs +2 -2
  143. package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
  144. package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
  145. package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
  146. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
  147. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
  148. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
  149. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
  150. package/src/runtime/memory/lib/memory-embed.mjs +2 -6
  151. package/src/runtime/memory/lib/memory-log.mjs +8 -0
  152. package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
  153. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
  154. package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
  155. package/src/runtime/memory/lib/memory-score.mjs +1 -1
  156. package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
  157. package/src/runtime/memory/lib/memory.mjs +2 -6
  158. package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
  159. package/src/runtime/memory/lib/pg/process.mjs +1 -5
  160. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
  161. package/src/runtime/memory/lib/recall-format.mjs +2 -2
  162. package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
  163. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
  164. package/src/runtime/memory/lib/trace-store.mjs +2 -6
  165. package/src/runtime/search/lib/http-fetch.mjs +6 -6
  166. package/src/runtime/search/lib/web-tools.mjs +2 -2
  167. package/src/runtime/shared/buffered-appender.mjs +1 -1
  168. package/src/runtime/shared/child-guardian.mjs +1 -1
  169. package/src/runtime/shared/child-spawn-gate.mjs +1 -1
  170. package/src/runtime/shared/config.mjs +4 -4
  171. package/src/runtime/shared/launcher-control.mjs +9 -10
  172. package/src/runtime/shared/llm/index.mjs +5 -20
  173. package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
  174. package/src/runtime/shared/memory-snapshot.mjs +4 -4
  175. package/src/runtime/shared/pristine-execution.mjs +1 -1
  176. package/src/runtime/shared/process-lifecycle.mjs +0 -1
  177. package/src/runtime/shared/process-shutdown.mjs +1 -1
  178. package/src/runtime/shared/resource-admission.mjs +2 -2
  179. package/src/runtime/shared/schedule-session-run.mjs +1 -1
  180. package/src/runtime/shared/schedules-db.mjs +1 -1
  181. package/src/runtime/shared/service-discovery.mjs +2 -2
  182. package/src/runtime/shared/staged-update.mjs +6 -6
  183. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  184. package/src/runtime/shared/tool-primitives.mjs +1 -1
  185. package/src/runtime/shared/tool-surface.mjs +2 -2
  186. package/src/runtime/shared/turn-snapshot.mjs +184 -0
  187. package/src/runtime/shared/update-checker.mjs +1 -1
  188. package/src/runtime/shared/user-cwd.mjs +3 -3
  189. package/src/runtime/shared/user-data-guard.mjs +4 -4
  190. package/src/runtime/shared/workspace-router.mjs +5 -5
  191. package/src/session-runtime/config-helpers.mjs +2 -3
  192. package/src/session-runtime/config-lifecycle.mjs +1 -1
  193. package/src/session-runtime/hitch-profile.mjs +1 -1
  194. package/src/session-runtime/lifecycle-api.mjs +6 -4
  195. package/src/session-runtime/model-recency.mjs +5 -5
  196. package/src/session-runtime/plugin-mcp.mjs +1 -1
  197. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  198. package/src/session-runtime/runtime-core.mjs +4 -0
  199. package/src/session-runtime/session-text.mjs +4 -4
  200. package/src/session-runtime/session-turn-api.mjs +7 -0
  201. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  202. package/src/session-runtime/tool-catalog.mjs +6 -483
  203. package/src/session-runtime/tool-defs.mjs +1 -1
  204. package/src/standalone/agent-task-status.mjs +1 -1
  205. package/src/standalone/agent-tool/helpers.mjs +2 -2
  206. package/src/standalone/channel-admin.mjs +4 -4
  207. package/src/standalone/explore-tool.mjs +3 -3
  208. package/src/standalone/hook-bus/config.mjs +1 -1
  209. package/src/standalone/hook-bus/constants.mjs +1 -1
  210. package/src/standalone/hook-bus/rules.mjs +1 -1
  211. package/src/standalone/projects.mjs +1 -1
  212. package/src/standalone/provider-admin.mjs +2 -2
  213. package/src/tui/App.jsx +1 -1
  214. package/src/tui/app/app-format.mjs +2 -2
  215. package/src/tui/app/clipboard.mjs +3 -3
  216. package/src/tui/app/input-parsers.mjs +1 -1
  217. package/src/tui/app/text-layout.mjs +1 -1
  218. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  219. package/src/tui/app/transcript-window.mjs +5 -347
  220. package/src/tui/app/use-mouse-input.mjs +1 -1
  221. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  222. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  223. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  224. package/src/tui/components/tool-output-format.mjs +7 -7
  225. package/src/tui/display-width.mjs +4 -4
  226. package/src/tui/dist/index.mjs +187 -99
  227. package/src/tui/engine/agent-envelope.mjs +5 -5
  228. package/src/tui/engine/live-share.mjs +100 -0
  229. package/src/tui/engine/notification-plan.mjs +1 -1
  230. package/src/tui/engine/queue-helpers.mjs +3 -3
  231. package/src/tui/engine/render-timing.mjs +4 -5
  232. package/src/tui/engine/session-api-ext.mjs +6 -3
  233. package/src/tui/engine/tool-result-status.mjs +5 -5
  234. package/src/tui/engine/tool-result-text.mjs +1 -1
  235. package/src/tui/engine/transcript-spill.mjs +585 -0
  236. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  237. package/src/tui/engine.mjs +29 -474
  238. package/src/tui/lib/voice-setup.mjs +1 -1
  239. package/src/tui/markdown/format-token.mjs +1 -1
  240. package/src/tui/markdown/render-ansi.mjs +1 -1
  241. package/src/tui/markdown/table-layout.mjs +4 -4
  242. package/src/tui/paste-attachments.mjs +1 -1
  243. package/src/tui/prompt-history-store.mjs +3 -3
  244. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  245. package/src/tui/theme.mjs +1 -1
  246. package/src/tui/transcript-tool-failures.mjs +2 -2
  247. package/src/ui/ansi.mjs +0 -9
  248. package/src/ui/statusline-format.mjs +2 -4
  249. package/scripts/_bench-cwc.json +0 -20
  250. package/scripts/_jitter-fuzz.jsx +0 -42
  251. package/scripts/_jitter-fuzz2.jsx +0 -30
  252. package/scripts/_jitter-probe.jsx +0 -35
  253. package/scripts/_jp2.jsx +0 -16
  254. package/scripts/_smoke_wd.mjs +0 -7
  255. package/scripts/abort-recovery-test.mjs +0 -175
  256. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  257. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  258. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  259. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  260. package/scripts/agent-loop-policy-test.mjs +0 -37
  261. package/scripts/agent-model-liveness-test.mjs +0 -763
  262. package/scripts/agent-parallel-smoke.mjs +0 -435
  263. package/scripts/agent-route-batch-test.mjs +0 -40
  264. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  265. package/scripts/agent-terminal-reap-test.mjs +0 -252
  266. package/scripts/agent-trace-io-test.mjs +0 -133
  267. package/scripts/ansi-color-capability-test.mjs +0 -90
  268. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  269. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  270. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  271. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  272. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  273. package/scripts/arg-guard-test.mjs +0 -93
  274. package/scripts/async-notify-settlement-test.mjs +0 -99
  275. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  276. package/scripts/background-task-meta-smoke.mjs +0 -38
  277. package/scripts/bench-run.mjs +0 -508
  278. package/scripts/boot-smoke.mjs +0 -137
  279. package/scripts/build-runtime-windows.ps1 +0 -242
  280. package/scripts/channel-daemon-smoke.mjs +0 -1103
  281. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  282. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  283. package/scripts/code-graph-dispatch-test.mjs +0 -96
  284. package/scripts/code-graph-root-federation-test.mjs +0 -273
  285. package/scripts/compact-active-turn-test.mjs +0 -68
  286. package/scripts/compact-file-reattach-test.mjs +0 -88
  287. package/scripts/compact-pressure-test.mjs +0 -788
  288. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  289. package/scripts/compact-recall-digest-test.mjs +0 -57
  290. package/scripts/compact-smoke.mjs +0 -1017
  291. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  292. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  293. package/scripts/context-mcp-metering-test.mjs +0 -1406
  294. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  295. package/scripts/deferred-tool-loading-test.mjs +0 -250
  296. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  297. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  298. package/scripts/embedding-worker-exit-test.mjs +0 -76
  299. package/scripts/execution-completion-dedup-test.mjs +0 -205
  300. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  301. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  302. package/scripts/explore-bench-tmp.mjs +0 -36
  303. package/scripts/explore-bench.mjs +0 -248
  304. package/scripts/explore-prompt-policy-test.mjs +0 -256
  305. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  306. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  307. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  308. package/scripts/freevar-smoke.mjs +0 -98
  309. package/scripts/gemini-provider-test.mjs +0 -1448
  310. package/scripts/generate-oc-icons.mjs +0 -11
  311. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  312. package/scripts/headless-pristine-execution-test.mjs +0 -614
  313. package/scripts/hook-bus-test.mjs +0 -552
  314. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  315. package/scripts/internal-comms-bench-test.mjs +0 -226
  316. package/scripts/internal-comms-bench.mjs +0 -853
  317. package/scripts/internal-comms-smoke.mjs +0 -277
  318. package/scripts/internal-tools-normalization-test.mjs +0 -62
  319. package/scripts/interrupted-turn-history-test.mjs +0 -399
  320. package/scripts/lead-workflow-smoke.mjs +0 -592
  321. package/scripts/lifecycle-api-test.mjs +0 -137
  322. package/scripts/live-share-test.mjs +0 -148
  323. package/scripts/live-worker-smoke.mjs +0 -333
  324. package/scripts/log-writer-guard-smoke.mjs +0 -131
  325. package/scripts/maintenance-default-routes-test.mjs +0 -164
  326. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  327. package/scripts/max-output-recovery-test.mjs +0 -368
  328. package/scripts/mcp-client-normalization-test.mjs +0 -45
  329. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  330. package/scripts/memory-core-input-test.mjs +0 -167
  331. package/scripts/memory-cycle-routing-test.mjs +0 -111
  332. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  333. package/scripts/memory-pg-recovery-test.mjs +0 -59
  334. package/scripts/memory-rule-contract-test.mjs +0 -93
  335. package/scripts/model-list-sanitize-test.mjs +0 -37
  336. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  337. package/scripts/native-edit-wire-test.mjs +0 -152
  338. package/scripts/notify-completion-mirror-test.mjs +0 -73
  339. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  340. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  341. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  342. package/scripts/output-style-bench.mjs +0 -292
  343. package/scripts/output-style-smoke.mjs +0 -145
  344. package/scripts/parent-abort-link-test.mjs +0 -90
  345. package/scripts/patch-binary-cache-test.mjs +0 -181
  346. package/scripts/path-suffix-test.mjs +0 -57
  347. package/scripts/pending-completion-drop-test.mjs +0 -239
  348. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  349. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  350. package/scripts/process-lifecycle-test.mjs +0 -461
  351. package/scripts/prompt-immediate-render-test.mjs +0 -89
  352. package/scripts/prompt-input-parity-test.mjs +0 -145
  353. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  354. package/scripts/provider-contract-test.mjs +0 -632
  355. package/scripts/provider-stream-stall-test.mjs +0 -276
  356. package/scripts/provider-toolcall-test.mjs +0 -3063
  357. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  358. package/scripts/recall-bench.mjs +0 -359
  359. package/scripts/resource-admission-test.mjs +0 -792
  360. package/scripts/result-classification-test.mjs +0 -75
  361. package/scripts/rg-runner-test.mjs +0 -240
  362. package/scripts/routing-corpus-test.mjs +0 -349
  363. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  364. package/scripts/session-bench-cache-break-test.mjs +0 -102
  365. package/scripts/session-bench.mjs +0 -1708
  366. package/scripts/session-context-bench.mjs +0 -344
  367. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  368. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  369. package/scripts/session-ingest-smoke.mjs +0 -177
  370. package/scripts/session-orphan-sweep-test.mjs +0 -109
  371. package/scripts/set-effort-config-test.mjs +0 -41
  372. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  373. package/scripts/shell-hardening-test.mjs +0 -209
  374. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  375. package/scripts/ship-mode-test.mjs +0 -98
  376. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  377. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  378. package/scripts/smoke-loop-report.mjs +0 -221
  379. package/scripts/smoke-loop.mjs +0 -202
  380. package/scripts/smoke-runtime-negative.ps1 +0 -106
  381. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  382. package/scripts/steering-drain-buckets-test.mjs +0 -467
  383. package/scripts/steering-fold-provenance-test.mjs +0 -71
  384. package/scripts/streaming-tail-window-test.mjs +0 -175
  385. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  386. package/scripts/task-bench.mjs +0 -205
  387. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  388. package/scripts/termio-input-smoke.mjs +0 -199
  389. package/scripts/tool-result-hook-test.mjs +0 -48
  390. package/scripts/tool-smoke.mjs +0 -2453
  391. package/scripts/tool-tui-presentation-test.mjs +0 -286
  392. package/scripts/toolcall-args-test.mjs +0 -239
  393. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  394. package/scripts/tui-background-failure-smoke.mjs +0 -73
  395. package/scripts/tui-perf-run.ps1 +0 -26
  396. package/scripts/tui-render-smoke.mjs +0 -90
  397. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  398. package/scripts/tui-runtime-load-bench.mjs +0 -50
  399. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  400. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  401. package/scripts/tui-transcript-perf-test.mjs +0 -934
  402. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  403. package/scripts/verify-release-assets-test.mjs +0 -281
  404. package/scripts/web-fetch-routing-test.mjs +0 -158
  405. package/scripts/webhook-smoke.mjs +0 -201
  406. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  407. package/scripts/worker-notify-rejection-test.mjs +0 -51
  408. package/scripts/write-backpressure-test.mjs +0 -147
@@ -0,0 +1,460 @@
1
+ // Grok OAuth discovery, token storage/refresh, and proxy identity, extracted from grok-oauth.mjs.
2
+ /**
3
+ * Grok CLI OAuth provider ("Grok Build").
4
+ *
5
+ * Authenticates against xAI's shared OAuth client via PKCE (discovery at
6
+ * https://auth.x.ai/.well-known/openid-configuration). Credentials come from
7
+ * Mixdog's own token store (grok-oauth.json).
8
+ *
9
+ * Every OAuth inference request routes through cli-chat-proxy.grok.com/v1,
10
+ * matching Grok Build's session-auth contract. Model discovery still merges
11
+ * api.x.ai and proxy catalogs because each publishes a different subset.
12
+ *
13
+ * Inference is delegated to an inner OpenAICompatProvider('xai') — the only
14
+ * preset wired for the Responses API — with the proxy URL + CLI headers
15
+ * injected via config.extraHeaders, bearer swapped for the OAuth access token.
16
+ */
17
+ import { createServer } from 'http';
18
+ import { randomBytes, randomUUID, createHash } from 'crypto';
19
+ import { readFileSync, existsSync, mkdirSync, statSync, unlinkSync } from 'fs';
20
+ import { join, resolve } from 'path';
21
+ import { getPluginData } from '../config.mjs';
22
+ import { writeJsonAtomicSync, withFileLock } from '../../../shared/atomic-file.mjs';
23
+ import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
24
+ import { enrichModels, getModelMetadataSync } from './model-catalog.mjs';
25
+ import { sanitizeModelList } from './model-list-sanitize.mjs';
26
+ import { makeModelCache } from './model-cache.mjs';
27
+ import { OpenAICompatProvider } from './openai-compat.mjs';
28
+ import { createTimeoutSignal } from '../stall-policy.mjs';
29
+ import { populateHttpStatusFromMessage } from './retry-classifier.mjs';
30
+ import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
31
+ import { normalizeGrokToolSchemas } from './lib/grok-tool-schema.mjs';
32
+
33
+ // --- Constants ---
34
+ // xAI's shared OAuth client. The consent screen renders this as "Grok Build".
35
+
36
+ export const CLIENT_ID = 'b1a00492-073a-47ea-816f-4c329264a828';
37
+ const ISSUER = 'https://auth.x.ai';
38
+ const DISCOVERY_URL = `${ISSUER}/.well-known/openid-configuration`;
39
+ export const SCOPE = 'openid profile email offline_access grok-cli:access api:access';
40
+ export const CALLBACK_HOST = '127.0.0.1';
41
+ export const CALLBACK_PORT = 56121;
42
+ export const CALLBACK_PATH = '/callback';
43
+ export const REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;
44
+ // Primary inference + search target. The OAuth token's `api:access` scope works
45
+ // against the STANDARD xAI API (GET /models 200, POST /responses 200, web_search
46
+ // 200 with citations). grok-4.x and web search live here. Proxy-only models
47
+ // (grok-build, grok-composer-2.5-fast) are NOT published on api.x.ai — they route
48
+ // to PROXY_BASE_URL below, which is version-gated and needs the Grok CLI client
49
+ // headers (see proxyHeaders).
50
+ export const INFERENCE_BASE_URL = 'https://api.x.ai/v1';
51
+ export const TOKEN_REFRESH_SKEW_MS = 5 * 60_000;
52
+
53
+ // --- grok-build CLI proxy (Composer 2.5, grok-build) ---
54
+ // These models live ONLY on the grok-build proxy, not api.x.ai. /models is
55
+ // readable with the bare OAuth bearer; /responses is version-gated (HTTP 426)
56
+ // and requires the Grok CLI's client headers. We route only proxy-only models
57
+ // here and keep grok-4.x + search on api.x.ai.
58
+ export const PROXY_BASE_URL = 'https://cli-chat-proxy.grok.com/v1';
59
+ const GROK_CLIENT_IDENTIFIER = 'grok-shell';
60
+ const GROK_CLI_VERSION_FALLBACK = '0.2.16';
61
+
62
+ // Route to the proxy: any grok-composer* model, plus the bare `grok-build`
63
+ // coding agent. NOT grok-build-0.1 — that is a real api.x.ai model and must stay
64
+ // on api.x.ai, so we match grok-build exactly rather than by prefix.
65
+ const PROXY_EXACT_MODELS = new Set(['grok-build']);
66
+ export function isProxyOnlyModel(model) {
67
+ const m = String(model || '');
68
+ return /^grok-composer/i.test(m) || PROXY_EXACT_MODELS.has(m);
69
+ }
70
+
71
+ // Use a Mixdog-controlled client version for the proxy version gate.
72
+ let _grokCliVersionCache = null;
73
+ function grokCliVersion() {
74
+ if (_grokCliVersionCache) return _grokCliVersionCache;
75
+ _grokCliVersionCache = String(process.env.MIXDOG_GROK_CLIENT_VERSION || '').trim() || GROK_CLI_VERSION_FALLBACK;
76
+ return _grokCliVersionCache;
77
+ }
78
+
79
+ // Headers the Grok CLI sends to clear the proxy version gate — extracted from
80
+ // the grok binary: x-grok-client-version (the actual 426 gate),
81
+ // x-grok-client-identifier, and a matching User-Agent.
82
+ export function proxyHeaders({ model, sendOpts, userId } = {}) {
83
+ const v = grokCliVersion();
84
+ const headers = {
85
+ 'x-grok-client-version': v,
86
+ 'x-grok-client-identifier': GROK_CLIENT_IDENTIFIER,
87
+ 'User-Agent': `xai-grok-build/${v}`,
88
+ };
89
+ const sessionId = String(sendOpts?.sessionId || sendOpts?.session?.id || '').trim();
90
+ const requestId = String(sendOpts?.requestId || '').trim() || (sendOpts ? randomUUID() : '');
91
+ const turnIndex = sendOpts?.iteration;
92
+ // Mixdog has an authoritative session id but no distinct Grok conversation
93
+ // id. Do not synthesize the latter from the former.
94
+ if (sessionId) headers['x-grok-session-id'] = sessionId;
95
+ if (requestId) headers['x-grok-req-id'] = requestId;
96
+ if (model) headers['x-grok-model-override'] = String(model);
97
+ if (turnIndex != null && Number.isFinite(Number(turnIndex))) {
98
+ headers['x-grok-turn-idx'] = String(turnIndex);
99
+ }
100
+ if (userId) headers['x-grok-user-id'] = String(userId);
101
+ return headers;
102
+ }
103
+
104
+ export function resolveGrokOAuthResponsesTransport() {
105
+ // The OAuth bearer is session auth and must never escape to api.x.ai.
106
+ // Mixdog's xAI WebSocket connector has a fixed api.x.ai endpoint, so OAuth
107
+ // inference is pinned to the proxy's reference HTTP/SSE transport.
108
+ return 'http';
109
+ }
110
+
111
+ // Retired model aliases xAI no longer exposes by their old ids. The live
112
+ // catalog surfaces the coding model as grok-build-0.1; map the legacy ids to
113
+ // it so a stale config selection doesn't hit a model-not-found. Exact table,
114
+ // not a heuristic. Mirrors openclaw extensions/xai/model-definitions.ts.
115
+ const RETIRED_MODEL_ALIASES = Object.freeze({
116
+ 'grok-code-fast-1': 'grok-build-0.1',
117
+ 'grok-code-fast': 'grok-build-0.1',
118
+ 'grok-code-fast-1-0825': 'grok-build-0.1',
119
+ });
120
+ export function normalizeGrokModelId(id) {
121
+ return (id && RETIRED_MODEL_ALIASES[id]) || id;
122
+ }
123
+ export const MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
124
+ // Bump when the on-disk cache shape changes so stale-shape entries are
125
+ // discarded instead of misread.
126
+ export const GROK_MODEL_CACHE_SCHEMA_VERSION = 1;
127
+ const DISCOVERY_TIMEOUT_MS = 15_000;
128
+ export const TOKEN_TIMEOUT_MS = 30_000;
129
+ export const LOGIN_TIMEOUT_MS = 5 * 60_000;
130
+
131
+ // SSRF guard for any endpoint pulled from the discovery document or saved
132
+ // tokens. xAI OAuth endpoints must be https on x.ai / *.x.ai — reject
133
+ // anything else outright so a hostile discovery response can't redirect the
134
+ // token / refresh request. Mirrors openclaw's isTrustedXaiOAuthEndpoint.
135
+ function assertTrustedXaiEndpoint(endpoint, label) {
136
+ let url;
137
+ try {
138
+ url = new URL(String(endpoint));
139
+ } catch {
140
+ throw new Error(`[grok-oauth] invalid ${label}: ${endpoint}`);
141
+ }
142
+ const host = url.hostname.toLowerCase();
143
+ if (url.protocol !== 'https:' || (host !== 'x.ai' && !host.endsWith('.x.ai'))) {
144
+ throw new Error(`[grok-oauth] untrusted ${label}: ${endpoint}`);
145
+ }
146
+ return url.toString();
147
+ }
148
+
149
+ let _discoveryCache = null;
150
+ export async function fetchDiscovery() {
151
+ if (_discoveryCache) return _discoveryCache;
152
+ const timeout = createTimeoutSignal(null, DISCOVERY_TIMEOUT_MS, 'grok-oauth discovery');
153
+ try {
154
+ const res = await fetch(DISCOVERY_URL, {
155
+ headers: { Accept: 'application/json' },
156
+ // No redirect-following: the discovery doc is a fixed well-known URL.
157
+ // A 3xx could bounce the request to an untrusted host before the
158
+ // endpoint trust checks below ever run.
159
+ redirect: 'error',
160
+ signal: timeout.signal,
161
+ dispatcher: getLlmDispatcher(),
162
+ });
163
+ if (!res.ok) throw new Error(`discovery ${res.status}`);
164
+ const j = await res.json();
165
+ const discovery = {
166
+ authorization_endpoint: assertTrustedXaiEndpoint(j?.authorization_endpoint, 'authorization endpoint'),
167
+ token_endpoint: assertTrustedXaiEndpoint(j?.token_endpoint, 'token endpoint'),
168
+ };
169
+ _discoveryCache = discovery;
170
+ return discovery;
171
+ } finally {
172
+ timeout.cleanup();
173
+ }
174
+ }
175
+
176
+ // --- Token store ---
177
+ export function getOwnTokenPath() {
178
+ const bound = boundProviderAuthPath('grok-oauth');
179
+ if (bound) return resolve(bound);
180
+ const explicit = process.env.GROK_OAUTH_CREDENTIALS_PATH;
181
+ if (explicit) return resolve(explicit);
182
+ const dir = getPluginData();
183
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
184
+ return join(dir, 'grok-oauth.json');
185
+ }
186
+
187
+ export function getRefreshLockPath() {
188
+ return `${getOwnTokenPath()}.refresh.lock`;
189
+ }
190
+
191
+ // expires_at may arrive as a unix number or an ISO-8601 string. Normalize both
192
+ // to epoch milliseconds; 0 means unknown.
193
+ export function _normalizeExpiresAt(value) {
194
+ if (typeof value === 'string') {
195
+ const ms = Date.parse(value);
196
+ return Number.isFinite(ms) ? ms : 0;
197
+ }
198
+ const n = Number(value || 0);
199
+ if (!Number.isFinite(n) || n <= 0) return 0;
200
+ return n < 1e12 ? n * 1000 : n;
201
+ }
202
+
203
+ // Fallback expiry from the access_token's JWT `exp` claim (epoch ms) when the
204
+ // store carries no explicit expires_at — without it expires_at stays 0, which
205
+ // ensureAuth reads as "never expires", disabling proactive refresh. Returns 0
206
+ // for opaque (non-JWT) tokens. JWT `exp` is epoch SECONDS (RFC 7519).
207
+ export function _expiryFromAccessToken(token) {
208
+ try {
209
+ const parts = String(token || '').split('.');
210
+ if (parts.length !== 3) return 0;
211
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));
212
+ const exp = Number(payload?.exp);
213
+ return Number.isFinite(exp) && exp > 0 ? exp * 1000 : 0;
214
+ } catch { return 0; }
215
+ }
216
+
217
+ export function _identityFromAccessToken(token) {
218
+ try {
219
+ const parts = String(token || '').split('.');
220
+ if (parts.length !== 3) return {};
221
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));
222
+ const principalId = payload?.principal_id || payload?.principalId || '';
223
+ const principalType = payload?.principal_type || payload?.principalType || '';
224
+ const userId = payload?.user_id
225
+ || payload?.userId
226
+ || principalId
227
+ || payload?.sub
228
+ || '';
229
+ return {
230
+ ...(userId ? { user_id: String(userId) } : {}),
231
+ ...(principalType ? { principal_type: String(principalType) } : {}),
232
+ ...(principalId ? { principal_id: String(principalId) } : {}),
233
+ };
234
+ } catch { return {}; }
235
+ }
236
+
237
+ export function _mtimeMs(path) {
238
+ try { return statSync(path).mtimeMs; } catch { return 0; }
239
+ }
240
+
241
+ // mixdog's own login store (grok-oauth.json). Single writer, accurate
242
+ // numeric expires_at from refresh.
243
+ export function _loadOwnTokens() {
244
+ const path = getOwnTokenPath();
245
+ if (!existsSync(path)) return null;
246
+ try {
247
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
248
+ if (!raw?.access_token || !raw?.refresh_token) return null;
249
+ const identity = _identityFromAccessToken(raw.access_token);
250
+ return {
251
+ access_token: raw.access_token,
252
+ refresh_token: raw.refresh_token,
253
+ expires_at: _normalizeExpiresAt(raw.expires_at ?? raw.expiresAt) || _expiryFromAccessToken(raw.access_token),
254
+ token_endpoint: raw.token_endpoint || null,
255
+ user_id: raw.user_id || raw.userId || identity.user_id || '',
256
+ principal_type: raw.principal_type || raw.principalType || identity.principal_type || '',
257
+ principal_id: raw.principal_id || raw.principalId || identity.principal_id || '',
258
+ source: 'own',
259
+ mtimeMs: _mtimeMs(path),
260
+ };
261
+ } catch { return null; }
262
+ }
263
+
264
+ // Mixdog-owned token store only.
265
+ export function loadTokens() {
266
+ return _loadOwnTokens();
267
+ }
268
+
269
+ export function saveTokens(tokens) {
270
+ const identity = _identityFromAccessToken(tokens.access_token);
271
+ writeJsonAtomicSync(getOwnTokenPath(), {
272
+ access_token: tokens.access_token,
273
+ refresh_token: tokens.refresh_token,
274
+ expires_at: tokens.expires_at || 0,
275
+ token_endpoint: tokens.token_endpoint || null,
276
+ user_id: tokens.user_id || tokens.userId || identity.user_id || undefined,
277
+ principal_type: tokens.principal_type || tokens.principalType || identity.principal_type || undefined,
278
+ principal_id: tokens.principal_id || tokens.principalId || identity.principal_id || undefined,
279
+ }, { lock: true, fsyncDir: true, mode: 0o600, secret: true });
280
+ }
281
+
282
+ export function _scrubTokens(text) {
283
+ return String(text || '')
284
+ .replace(/Bearer [A-Za-z0-9._\-]+/g, 'Bearer [REDACTED]')
285
+ .replace(/"access_token"\s*:\s*"[^"]+"/g, '"access_token":"[REDACTED]"')
286
+ .replace(/"refresh_token"\s*:\s*"[^"]+"/g, '"refresh_token":"[REDACTED]"')
287
+ .replace(/"key"\s*:\s*"[^"]+"/g, '"key":"[REDACTED]"');
288
+ }
289
+
290
+ // Public predicate used by config.buildDefaultConfig — enabled when Mixdog's
291
+ // token store carries credentials. Single truth: same loader the runtime uses.
292
+ export function hasGrokOAuthCredentials() {
293
+ try {
294
+ const tokens = loadTokens();
295
+ return !!(tokens?.access_token && tokens?.refresh_token);
296
+ } catch { return false; }
297
+ }
298
+
299
+ export function describeGrokOAuthCredentials() {
300
+ try {
301
+ const tokens = loadTokens();
302
+ if (!tokens?.access_token) {
303
+ return { authenticated: false, status: 'Not Set', detail: 'Mixdog token store' };
304
+ }
305
+ const hasRefresh = Boolean(tokens.refresh_token);
306
+ const expiresAt = _normalizeExpiresAt(tokens.expires_at);
307
+ const expiring = expiresAt > 0 && expiresAt < Date.now() + TOKEN_REFRESH_SKEW_MS;
308
+ const expired = expiresAt > 0 && expiresAt <= Date.now();
309
+ const detail = tokens.source === 'own' ? 'Mixdog token store' : (tokens.source || 'oauth');
310
+ if (!hasRefresh) {
311
+ return {
312
+ authenticated: expiresAt === 0 || !expired,
313
+ status: expired ? 'Reauth Required' : 'Access Only',
314
+ detail: `${detail}; no refresh token`,
315
+ expiresAt,
316
+ };
317
+ }
318
+ if (expired) return { authenticated: true, status: 'Refresh Required', detail, expiresAt };
319
+ if (expiring) return { authenticated: true, status: 'Refresh Soon', detail, expiresAt };
320
+ return { authenticated: true, status: 'Valid', detail, expiresAt };
321
+ } catch (err) {
322
+ return { authenticated: false, status: 'Error', detail: String(err?.message || err).slice(0, 200) };
323
+ }
324
+ }
325
+
326
+ export function forgetGrokOAuthCredentials() {
327
+ let removed = false;
328
+ const ownPath = getOwnTokenPath();
329
+ if (existsSync(ownPath)) {
330
+ unlinkSync(ownPath);
331
+ removed = true;
332
+ }
333
+ return { removed };
334
+ }
335
+
336
+ export let _refreshInFlight = null;
337
+ export function _getRefreshInFlight() { return _refreshInFlight; }
338
+ export function _setRefreshInFlight(promise) { _refreshInFlight = promise; return promise; }
339
+ async function _postRefresh(tokens) {
340
+ const tokenEndpoint = tokens.token_endpoint
341
+ ? assertTrustedXaiEndpoint(tokens.token_endpoint, 'token endpoint')
342
+ : (await fetchDiscovery()).token_endpoint;
343
+ const timeout = createTimeoutSignal(null, TOKEN_TIMEOUT_MS, 'grok-oauth refresh');
344
+ try {
345
+ const body = new URLSearchParams({
346
+ grant_type: 'refresh_token',
347
+ client_id: CLIENT_ID,
348
+ refresh_token: tokens.refresh_token,
349
+ });
350
+ if (tokens.principal_type) body.set('principal_type', tokens.principal_type);
351
+ if (tokens.principal_id) body.set('principal_id', tokens.principal_id);
352
+ const res = await fetch(tokenEndpoint, {
353
+ method: 'POST',
354
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
355
+ body,
356
+ // Never follow a redirect on a secret-bearing request: a trusted
357
+ // token endpoint that 307/308-redirects would replay the
358
+ // refresh_token to the redirect target. Fail loud instead.
359
+ redirect: 'error',
360
+ signal: timeout.signal,
361
+ dispatcher: getLlmDispatcher(),
362
+ });
363
+ const text = await res.text();
364
+ let json = null;
365
+ try { json = text ? JSON.parse(text) : null; } catch { /* handled below */ }
366
+ if (!res.ok) {
367
+ const oauthError = String(json?.error || '').toLowerCase();
368
+ const isInvalidGrant = oauthError === 'invalid_grant';
369
+ const isTerminalRefresh = isInvalidGrant || oauthError === 'invalid_client';
370
+ throw Object.assign(
371
+ new Error(`[grok-oauth] token refresh ${res.status}: ${_scrubTokens(text).slice(0, 200)}`),
372
+ { isInvalidGrant, isTerminalRefresh, oauthError: oauthError || null },
373
+ );
374
+ }
375
+ const accessToken = json?.access_token;
376
+ if (!accessToken) throw new Error('[grok-oauth] token refresh returned no access token');
377
+ const refreshed = {
378
+ access_token: accessToken,
379
+ // xAI rotates refresh tokens; reuse the prior one only when the
380
+ // response omits it (RFC 6749 permits reuse).
381
+ refresh_token: json?.refresh_token || tokens.refresh_token,
382
+ expires_at: typeof json?.expires_in === 'number'
383
+ ? Date.now() + json.expires_in * 1000
384
+ : _normalizeExpiresAt(json?.expires_at),
385
+ token_endpoint: tokenEndpoint,
386
+ user_id: tokens.user_id || tokens.userId || _identityFromAccessToken(accessToken).user_id || '',
387
+ principal_type: json?.principal_type || tokens.principal_type || tokens.principalType || '',
388
+ principal_id: json?.principal_id || tokens.principal_id || tokens.principalId || '',
389
+ };
390
+ saveTokens(refreshed);
391
+ return { ...refreshed, source: 'own', mtimeMs: _mtimeMs(getOwnTokenPath()) };
392
+ } finally {
393
+ timeout.cleanup();
394
+ }
395
+ }
396
+
397
+ async function _postRefreshWithRetry(tokens) {
398
+ for (let attempt = 0; attempt < 3; attempt += 1) {
399
+ try {
400
+ return await _postRefresh(tokens);
401
+ } catch (err) {
402
+ if (err?.isTerminalRefresh || attempt === 2) throw err;
403
+ const baseMs = Math.min(2_000, 200 * (2 ** attempt));
404
+ const delayMs = Math.round(baseMs + Math.random() * baseMs);
405
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
406
+ }
407
+ }
408
+ throw new Error('[grok-oauth] unreachable refresh retry state');
409
+ }
410
+
411
+ // Mixdog processes share one grok-oauth.json and xAI rotates refresh tokens
412
+ // single-use. Hold a cross-process lease across the re-read, exchange, and
413
+ // atomic save so only one process can spend a generation.
414
+ export async function refreshTokens(tokens, { force = false } = {}) {
415
+ if (!tokens?.refresh_token) {
416
+ throw new Error('[grok-oauth] refresh token not available — open /providers in mixdog to sign in again');
417
+ }
418
+
419
+ return withFileLock(getRefreshLockPath(), async () => {
420
+ const validAfter = Date.now() + (force ? 0 : TOKEN_REFRESH_SKEW_MS);
421
+ const disk = _loadOwnTokens();
422
+ // A waiter that entered with the prior generation adopts the winner's
423
+ // persisted, valid token instead of rotating it again. xAI may rotate
424
+ // only the refresh token while reissuing the same access token.
425
+ const diskGenerationChanged = disk?.access_token
426
+ && (disk.access_token !== tokens.access_token
427
+ || disk.refresh_token !== tokens.refresh_token);
428
+ if (diskGenerationChanged
429
+ && (!disk.expires_at || disk.expires_at >= validAfter)) {
430
+ return disk;
431
+ }
432
+
433
+ const current = disk || tokens;
434
+ try {
435
+ return await _postRefreshWithRetry(current);
436
+ } catch (err) {
437
+ if (err?.isInvalidGrant) {
438
+ // A writer that does not participate in this lease may still
439
+ // have won the rotation while the request was in flight.
440
+ // Adopt its valid generation; only exchange it when it cannot
441
+ // satisfy this caller.
442
+ const rotated = _loadOwnTokens();
443
+ if (rotated?.refresh_token && rotated.refresh_token !== current.refresh_token) {
444
+ if (rotated.access_token
445
+ && (!rotated.expires_at || rotated.expires_at >= validAfter)) {
446
+ return rotated;
447
+ }
448
+ return await _postRefreshWithRetry(rotated);
449
+ }
450
+ }
451
+ throw err;
452
+ }
453
+ }, {
454
+ timeoutMs: 120_000,
455
+ staleMs: 120_000,
456
+ secret: true,
457
+ });
458
+ }
459
+
460
+ // --- Model catalog cache (24h disk TTL) ---