mixdog 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -22,6 +22,9 @@ import {
22
22
  } from './anthropic-effort.mjs';
23
23
  import { normalizeContentForAnthropic } from './media-normalization.mjs';
24
24
  import { enrichModels } from './model-catalog.mjs';
25
+ import { sanitizeModelList } from './model-list-sanitize.mjs';
26
+ import { makeModelCache } from './model-cache.mjs';
27
+ import { resolveAnthropicMaxTokens } from './anthropic-max-tokens.mjs';
25
28
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
26
29
 
27
30
  const require = createRequire(import.meta.url);
@@ -79,12 +82,30 @@ function resolveCacheTtls(opts) {
79
82
  if (v === 'none') return null;
80
83
  return fallback;
81
84
  };
82
- return {
85
+ const resolved = {
83
86
  tools: pick('tools', null),
84
87
  system: pick('system', CACHE_TTL_STABLE),
85
88
  tier3: pick('tier3', CACHE_TTL_STABLE),
86
89
  messages: pick('messages', CACHE_TTL_STABLE),
87
90
  };
91
+ // A partial cacheStrategy override (e.g. {system:'5m'} while tier3/
92
+ // messages default to '1h') can put a longer TTL after a shorter one in
93
+ // request order, which Anthropic rejects: 1h breakpoints must all appear
94
+ // before any 5m breakpoint. Normalize left-to-right in wire order
95
+ // (system -> tier3 -> messages; tools is emitted before system and is
96
+ // excluded from the run) so a later layer is downgraded to the earliest
97
+ // shorter TTL seen so far — never re-promoted. Layers set to null ('none')
98
+ // emit no breakpoint at all, so they neither violate nor constrain
99
+ // ordering and are skipped. Mirrors anthropic-oauth.mjs.
100
+ const ttlRank = (ttl) => (ttl === CACHE_TTL_STABLE ? 2 : 1); // 1h=2, 5m=1
101
+ let minRank = Infinity;
102
+ for (const layer of ['system', 'tier3', 'messages']) {
103
+ if (!resolved[layer]) continue;
104
+ const rank = ttlRank(resolved[layer]);
105
+ if (rank > minRank) resolved[layer] = CACHE_TTL_VOLATILE;
106
+ else minRank = rank;
107
+ }
108
+ return resolved;
88
109
  }
89
110
 
90
111
  function buildSystemBlocks(systemMsgs, systemTtl, tier3Ttl) {
@@ -100,10 +121,20 @@ function buildSystemBlocks(systemMsgs, systemTtl, tier3Ttl) {
100
121
  }))
101
122
  .filter(it => it.text)
102
123
  : [];
124
+ // Anthropic caps cache_control breakpoints at 4 per request; defensively
125
+ // cap it here too so an unexpectedly large systemMsgs array can never
126
+ // mark more than 4 blocks (extras keep their text, just lose the
127
+ // cache_control breakpoint, not the block itself). Mirrors
128
+ // anthropic-oauth.mjs.
129
+ const MAX_SYSTEM_BREAKPOINTS = 4;
130
+ let bpCount = 0;
103
131
  return items.map(it => {
104
132
  const block = { type: 'text', text: it.text };
105
133
  const ttl = it.tier === 'tier3' ? tier3Ttl : systemTtl;
106
- if (ttl) block.cache_control = ttl;
134
+ if (ttl && bpCount < MAX_SYSTEM_BREAKPOINTS) {
135
+ block.cache_control = ttl;
136
+ bpCount++;
137
+ }
107
138
  return block;
108
139
  });
109
140
  }
@@ -159,14 +190,49 @@ function _normalizeAnthropicModel(raw, provider = 'anthropic') {
159
190
  }
160
191
  // Family-based heuristic so new model ids (including custom user-configured
161
192
  // ones) resolve a sensible max_tokens without requiring a code change.
193
+ // The API-key provider has no catalog cache of its own — it reads the same
194
+ // anthropic-oauth-models.json disk cache (read-only) that the OAuth provider
195
+ // maintains. Both providers hit the same Anthropic /v1/models catalog, so a
196
+ // per-model outputTokens entry is valid regardless of which auth path wrote
197
+ // it. If this provider is ever run standalone without the OAuth provider
198
+ // ever having populated the cache, loadSync() simply returns null and we
199
+ // fall through to the shared static heuristic in anthropic-max-tokens.mjs.
200
+ const ANTHROPIC_OAUTH_MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
201
+ const _sharedOAuthModelCache = makeModelCache({
202
+ fileName: 'anthropic-oauth-models.json',
203
+ ttlMs: ANTHROPIC_OAUTH_MODEL_CACHE_TTL_MS,
204
+ version: 1,
205
+ });
206
+
207
+ // In-memory mirror populated by this provider's own listModels() fetch.
208
+ // API-key-only installs never have the OAuth provider write the shared disk
209
+ // cache, so without this mirror catalog outputTokens would stay invisible to
210
+ // resolveMaxTokens until an OAuth session runs. listModels() results flow in
211
+ // here (memory only — the disk cache stays OAuth-owned/read-only for us).
212
+ let _apiKeyCatalogMirror = null;
213
+
214
+ function _catalogOutputTokensFromSharedCache(model) {
215
+ if (!model) return null;
216
+ try {
217
+ const models = Array.isArray(_apiKeyCatalogMirror)
218
+ ? _apiKeyCatalogMirror
219
+ : _sharedOAuthModelCache.loadSync();
220
+ if (!Array.isArray(models)) return null;
221
+ const entry = models.find(m => m?.id === model);
222
+ const out = Number(entry?.outputTokens);
223
+ return Number.isFinite(out) && out > 0 ? out : null;
224
+ } catch {
225
+ return null;
226
+ }
227
+ }
228
+
162
229
  function resolveMaxTokens(model) {
163
- const id = String(model || '').toLowerCase();
164
- if (id.includes('opus')) return 32768;
165
- if (id.includes('sonnet')) return 16384;
166
- if (id.includes('haiku')) return 8192;
167
- return 8192;
230
+ return resolveAnthropicMaxTokens(model, { catalogLookup: _catalogOutputTokensFromSharedCache });
168
231
  }
169
232
 
233
+ // Test-only escape hatch for scripts/anthropic-maxtokens-test.mjs.
234
+ export const _test = { resolveMaxTokens };
235
+
170
236
  const MIN_THINKING_BUDGET = 1024;
171
237
  const THINKING_OUTPUT_RESERVE = 1024;
172
238
 
@@ -464,8 +530,7 @@ export class AnthropicProvider {
464
530
  const usedSlots = toolsBpUsed + systemBpUsed;
465
531
  // Env override for BP strategy. ANTHROPIC_MSG_SLOTS=0 disables
466
532
  // message caching entirely. Any value >=1 first marks the previous
467
- // user text turn; a second free slot marks the tail. Mirrors
468
- // anthropic-oauth.mjs for twin parity.
533
+ // user text turn; a second free slot marks the tail.
469
534
  const msgSlotsCap = Number.parseInt(process.env.ANTHROPIC_MSG_SLOTS, 10);
470
535
  const defaultMsgSlots = Math.max(0, 4 - usedSlots);
471
536
  const msgSlots = ttls.messages
@@ -825,7 +890,10 @@ export class AnthropicProvider {
825
890
  const normalized = items
826
891
  .map((m) => _normalizeAnthropicModel(m, this.name))
827
892
  .filter(Boolean);
828
- const enriched = await enrichModels(normalized);
893
+ const enriched = sanitizeModelList(await enrichModels(normalized), { provider: this.name });
894
+ // Feed the resolver-visible mirror so API-key-only installs get
895
+ // catalog outputTokens without depending on the OAuth disk cache.
896
+ if (enriched.length) _apiKeyCatalogMirror = enriched.slice();
829
897
  return enriched.length ? enriched : MODELS;
830
898
  }
831
899
  catch (err) {
@@ -3,6 +3,7 @@ import { join } from 'node:path';
3
3
  import { updateJsonAtomicSync } from '../../../shared/atomic-file.mjs';
4
4
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
5
5
  import { getAgentApiKey, getOpenAIUsageSessionKey } from '../../../shared/config.mjs';
6
+ import { num, round } from './lib/usage-primitives.mjs';
6
7
 
7
8
  const CACHE_FILE = 'api-usage-cache.json';
8
9
  const LIVE_TTL_MS = 5 * 60_000;
@@ -10,19 +11,6 @@ const STALE_TTL_MS = 60 * 60_000;
10
11
  const DISK_JSON_MEMORY_TTL_MS = 1000;
11
12
  let diskJsonCache = { at: 0, file: '', value: null };
12
13
 
13
- function num(value, fallback = null) {
14
- if (value === null || value === undefined || value === '') return fallback;
15
- const n = Number(value);
16
- return Number.isFinite(n) ? n : fallback;
17
- }
18
-
19
- function round(value, digits = 4) {
20
- const n = Number(value);
21
- if (!Number.isFinite(n)) return null;
22
- const scale = 10 ** digits;
23
- return Math.round(n * scale) / scale;
24
- }
25
-
26
14
  function cachePath() {
27
15
  return join(resolvePluginData(), CACHE_FILE);
28
16
  }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * codex-client-meta.mjs — codex client identity headers for the OpenAI OAuth
3
+ * transports.
4
+ *
5
+ * codex-rs sends two client-identity headers on EVERY request including the
6
+ * WS handshake:
7
+ * - `User-Agent: codex_cli_rs/<version> (<os> <ver>; <arch>) <terminal>`
8
+ * (login/src/auth/default_client.rs get_codex_user_agent + default_headers)
9
+ * - `version: <CARGO_PKG_VERSION>` via the built-in provider http_headers
10
+ * (model-provider-info/src/lib.rs:339-343), merged into the WS handshake by
11
+ * merge_request_headers (codex-api/src/endpoint/responses_websocket.rs).
12
+ * The backend uses these for client gating (model catalog visibility measured
13
+ * 2026-07-03) and plausibly for x-codex-turn-state issuance / sticky
14
+ * cache-node routing, so mixdog mirrors both.
15
+ */
16
+ import os from 'node:os';
17
+
18
+ // Offline fallback only; live value refreshes from npm (24h TTL, in-process).
19
+ export const CODEX_CLIENT_VERSION_FLOOR = '0.142.5';
20
+ const VERSION_TTL_MS = 24 * 60 * 60_000;
21
+ let _cache = { value: null, fetchedAt: 0 };
22
+ let _refreshInFlight = null;
23
+
24
+ async function _refresh() {
25
+ try {
26
+ const res = await fetch('https://registry.npmjs.org/@openai/codex/latest', {
27
+ signal: AbortSignal.timeout(5_000),
28
+ });
29
+ if (res.ok) {
30
+ const j = await res.json();
31
+ const v = String(j?.version || '').trim();
32
+ if (/^\d+\.\d+\.\d+/.test(v)) {
33
+ _cache = { value: v, fetchedAt: Date.now() };
34
+ return v;
35
+ }
36
+ }
37
+ } catch { /* offline — keep floor */ }
38
+ _cache = { value: CODEX_CLIENT_VERSION_FLOOR, fetchedAt: Date.now() };
39
+ return CODEX_CLIENT_VERSION_FLOOR;
40
+ }
41
+
42
+ /**
43
+ * Sync accessor for hot request paths: returns the cached npm version when
44
+ * fresh, otherwise kicks a background refresh and returns the floor. The
45
+ * handshake must never await a registry fetch.
46
+ */
47
+ export function codexClientVersionSync() {
48
+ if (_cache.value && Date.now() - _cache.fetchedAt < VERSION_TTL_MS) return _cache.value;
49
+ if (!_refreshInFlight) {
50
+ _refreshInFlight = _refresh().finally(() => { _refreshInFlight = null; });
51
+ }
52
+ return _cache.value || CODEX_CLIENT_VERSION_FLOOR;
53
+ }
54
+
55
+ function _osType() {
56
+ // codex os_info reports "Windows"/"Mac OS"/"Linux"; node os.type() gives
57
+ // Windows_NT/Darwin/Linux. Map to codex's vocabulary.
58
+ const t = os.type();
59
+ if (t === 'Windows_NT') return 'Windows';
60
+ if (t === 'Darwin') return 'Mac OS';
61
+ return t;
62
+ }
63
+
64
+ function _arch() {
65
+ // codex reports rust-style arch tokens.
66
+ const a = os.arch();
67
+ if (a === 'x64') return 'x86_64';
68
+ if (a === 'arm64') return 'aarch64';
69
+ return a;
70
+ }
71
+
72
+ /** codex_cli_rs/<version> (<os> <ver>; <arch>) <terminal> */
73
+ export function codexUserAgent() {
74
+ const terminal = String(process.env.TERM_PROGRAM || 'unknown').trim() || 'unknown';
75
+ return `codex_cli_rs/${codexClientVersionSync()} (${_osType()} ${os.release()}; ${_arch()}) ${terminal}`;
76
+ }
77
+
78
+ /** Bare version header value — codex built-in provider http_headers "version". */
79
+ export function codexVersionHeader() {
80
+ return codexClientVersionSync();
81
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * gemini-cache.mjs — Gemini explicit cachedContents prefix accounting:
3
+ * token estimation, prefix hashing, the process-global cache registry
4
+ * (create de-dupe + prune), cache-state attach, usage resolution, and the
5
+ * cache trace writer.
6
+ *
7
+ * Extracted from gemini.mjs (no behavior change). Owns the module-level
8
+ * geminiGlobalCaches / geminiGlobalCacheCreates maps. gemini.mjs imports
9
+ * these helpers; no re-export needed (all names are internal to the
10
+ * provider — none were part of the module's external surface).
11
+ */
12
+ import { traceHash, stableTraceStringify, summarizeTraceTools, traceTextShape } from './trace-utils.mjs';
13
+
14
+ function summarizeTracePart(part) {
15
+ if (!part || typeof part !== 'object') return { type: typeof part };
16
+ if ('text' in part) {
17
+ return { type: 'text', ...traceTextShape(part.text) };
18
+ }
19
+ if (part.functionCall) {
20
+ return {
21
+ type: 'functionCall',
22
+ name: part.functionCall.name || null,
23
+ argsHash: traceHash(stableTraceStringify(part.functionCall.args || {})),
24
+ };
25
+ }
26
+ if (part.functionResponse) {
27
+ const response = part.functionResponse.response || {};
28
+ const responseShape = stableTraceStringify(response);
29
+ return {
30
+ type: 'functionResponse',
31
+ name: part.functionResponse.name || null,
32
+ responseChars: responseShape.length,
33
+ responseHash: traceHash(responseShape),
34
+ };
35
+ }
36
+ return { type: Object.keys(part).sort().join('|') || 'unknown' };
37
+ }
38
+
39
+ function summarizeTraceContents(contents) {
40
+ const summaries = (contents || []).map((content, index) => ({
41
+ index,
42
+ role: content?.role || null,
43
+ parts: Array.isArray(content?.parts) ? content.parts.map(summarizeTracePart) : [],
44
+ }));
45
+ if (summaries.length <= 12) return summaries;
46
+ return [
47
+ ...summaries.slice(0, 8),
48
+ { omittedTurns: summaries.length - 12 },
49
+ ...summaries.slice(-4),
50
+ ];
51
+ }
52
+
53
+ // Gemini cachedContents API rejects prefixes below the model-specific minimum
54
+ // (Gemini 2.0/2.5 = 2048 tokens, Gemini 3.x = 4096 tokens) with HTTP 400
55
+ // "Cached content is too small". Estimating chars/4 ≈ tokens lets us skip the
56
+ // roundtrip when the prefix cannot satisfy the invariant. Estimate undercount
57
+ // is harmless (we attempt and get the same 400 we would have skipped); overcount
58
+ // is harmless (we skip, fall back to implicit cache).
59
+ export function _estimateGeminiCacheTokens(systemInstruction, geminiTools, contents) {
60
+ let chars = 0;
61
+ if (typeof systemInstruction === 'string') chars += systemInstruction.length;
62
+ if (Array.isArray(geminiTools) && geminiTools.length) {
63
+ try { chars += JSON.stringify(geminiTools).length; } catch {}
64
+ }
65
+ if (Array.isArray(contents) && contents.length > 1) {
66
+ for (let i = 0; i < contents.length - 1; i++) {
67
+ try { chars += JSON.stringify(contents[i]?.parts ?? '').length; } catch {}
68
+ }
69
+ }
70
+ return Math.ceil(chars / 4);
71
+ }
72
+
73
+ export function _geminiCacheMinTokens(model) {
74
+ return /^gemini-3/i.test(String(model || '')) ? 4096 : 2048;
75
+ }
76
+
77
+ export function _geminiCachePrefixCount(contents) {
78
+ return Array.isArray(contents) && contents.length > 1 ? contents.length - 1 : 0;
79
+ }
80
+
81
+ export function _geminiCachePrefixContents(contents, prefixCount) {
82
+ if (!Array.isArray(contents) || prefixCount <= 0) return [];
83
+ return contents.slice(0, prefixCount).map(c => {
84
+ const r = c?.role;
85
+ const safeRole = (r === 'model' || r === 'user') ? r : 'user';
86
+ return {
87
+ role: safeRole,
88
+ parts: Array.isArray(c?.parts) ? c.parts : [],
89
+ };
90
+ });
91
+ }
92
+
93
+ export function _geminiCachePrefixHash({ model, systemInstruction, geminiTools, contents, prefixCount }) {
94
+ return traceHash(stableTraceStringify({
95
+ model: model || null,
96
+ systemInstruction: systemInstruction || '',
97
+ tools: geminiTools || [],
98
+ contents: _geminiCachePrefixContents(contents, prefixCount),
99
+ }));
100
+ }
101
+
102
+ export const GEMINI_GLOBAL_CACHE_MIN_LIVE_MS = 6 * 60 * 1000;
103
+ export const GEMINI_GLOBAL_CACHE_MAX_ENTRIES = 128;
104
+ // Grace window before deleting a superseded cachedContents name (see the
105
+ // cross-session race note at the delete call site in gemini.mjs). Long enough
106
+ // that a concurrent session still mid-flight on the old name has time to finish.
107
+ export const GEMINI_GLOBAL_CACHE_DELETE_GRACE_MS = 2 * 60 * 1000;
108
+ export const geminiGlobalCaches = new Map();
109
+ export const geminiGlobalCacheCreates = new Map();
110
+
111
+ export function _geminiGlobalCacheKey({ apiKey, model, cachePrefixHash, cachePrefixContentCount }) {
112
+ return traceHash(stableTraceStringify({
113
+ apiKeyHash: traceHash(apiKey || ''),
114
+ model: model || null,
115
+ cachePrefixHash,
116
+ cachePrefixContentCount,
117
+ }));
118
+ }
119
+
120
+ export function _pruneGeminiGlobalCaches(now = Date.now()) {
121
+ for (const [key, entry] of geminiGlobalCaches) {
122
+ if (!entry?.cacheName || Number(entry.cacheExpiresAt || 0) <= now) {
123
+ geminiGlobalCaches.delete(key);
124
+ }
125
+ }
126
+ while (geminiGlobalCaches.size > GEMINI_GLOBAL_CACHE_MAX_ENTRIES) {
127
+ const oldest = geminiGlobalCaches.keys().next().value;
128
+ if (oldest === undefined) break;
129
+ geminiGlobalCaches.delete(oldest);
130
+ }
131
+ }
132
+
133
+ export function _getGeminiGlobalCache(key, now = Date.now()) {
134
+ _pruneGeminiGlobalCaches(now);
135
+ const entry = geminiGlobalCaches.get(key);
136
+ if (!entry?.cacheName) return null;
137
+ if ((entry.cacheExpiresAt || 0) - now <= GEMINI_GLOBAL_CACHE_MIN_LIVE_MS) {
138
+ geminiGlobalCaches.delete(key);
139
+ return null;
140
+ }
141
+ entry.lastUsedAt = now;
142
+ return entry;
143
+ }
144
+
145
+ export function _setGeminiGlobalCache(key, entry) {
146
+ if (!key || !entry?.cacheName) return;
147
+ geminiGlobalCaches.set(key, {
148
+ ...entry,
149
+ lastUsedAt: Date.now(),
150
+ });
151
+ _pruneGeminiGlobalCaches();
152
+ }
153
+
154
+ export function _geminiGlobalCacheNameIsLive(cacheName, now = Date.now()) {
155
+ if (!cacheName) return false;
156
+ for (const entry of geminiGlobalCaches.values()) {
157
+ if (entry?.cacheName === cacheName && Number(entry.cacheExpiresAt || 0) > now) return true;
158
+ }
159
+ return false;
160
+ }
161
+
162
+ export function _attachGeminiCacheState(opts, entry, currentIter) {
163
+ if (!entry?.cacheName) return;
164
+ opts.providerState = {
165
+ ...(opts.providerState || {}),
166
+ gemini: {
167
+ cacheName: entry.cacheName,
168
+ cacheCreatedAt: entry.cacheCreatedAt,
169
+ cacheCreatedAtIter: currentIter,
170
+ cacheExpiresAt: entry.cacheExpiresAt,
171
+ cacheModel: entry.cacheModel,
172
+ cacheTokenSize: entry.cacheTokenSize,
173
+ cachePrefixContentCount: entry.cachePrefixContentCount,
174
+ cachePrefixHash: entry.cachePrefixHash,
175
+ },
176
+ };
177
+ }
178
+
179
+ export function _resolveGeminiCacheUsage({ usageMetadata, cachedContent, providerState }) {
180
+ const inputTokens = Number(usageMetadata?.promptTokenCount || usageMetadata?.totalTokenCount || 0) || 0;
181
+ const reportedCachedTokens = Number(usageMetadata?.cachedContentTokenCount || 0) || 0;
182
+ const cachedFallbackTokens = cachedContent
183
+ ? Number(providerState?.gemini?.cacheTokenSize || 0) || 0
184
+ : 0;
185
+ const rawCachedTokens = reportedCachedTokens > 0 ? reportedCachedTokens : cachedFallbackTokens;
186
+ const cachedTokens = inputTokens > 0 ? Math.min(rawCachedTokens, inputTokens) : rawCachedTokens;
187
+ const cacheTokenSource = reportedCachedTokens > 0
188
+ ? 'usage_metadata'
189
+ : (cachedFallbackTokens > 0 ? 'cache_create_fallback' : 'none');
190
+ return {
191
+ inputTokens,
192
+ reportedCachedTokens,
193
+ cachedFallbackTokens,
194
+ cachedTokens,
195
+ cacheTokenSource,
196
+ };
197
+ }
198
+
199
+ export function writeGeminiCacheTrace({ opts, model, systemInstruction, tools, contents, usageMetadata, cachedContent }) {
200
+ if (process.env.MIXDOG_GEMINI_CACHE_TRACE !== '1') return;
201
+ try {
202
+ const session = opts?.session || {};
203
+ const {
204
+ inputTokens,
205
+ reportedCachedTokens,
206
+ cachedFallbackTokens,
207
+ cachedTokens,
208
+ cacheTokenSource,
209
+ } = _resolveGeminiCacheUsage({
210
+ usageMetadata,
211
+ cachedContent,
212
+ providerState: opts?.providerState,
213
+ });
214
+ const toolShape = summarizeTraceTools(tools);
215
+ const trace = {
216
+ event: 'generate',
217
+ provider: 'gemini',
218
+ model,
219
+ owner: session.owner || null,
220
+ agent: session.agent || null,
221
+ permission: session.permission || null,
222
+ toolPermission: session.toolPermission || null,
223
+ profileId: session.profileId || null,
224
+ sourceType: session.sourceType || null,
225
+ sourceName: session.sourceName || null,
226
+ sessionIdHash: opts?.sessionId ? traceHash(opts.sessionId) : null,
227
+ providerCacheKeyHash: opts?.providerCacheKey ? traceHash(opts.providerCacheKey) : null,
228
+ promptCacheKeyHash: opts?.promptCacheKey ? traceHash(opts.promptCacheKey) : null,
229
+ systemChars: systemInstruction ? systemInstruction.length : 0,
230
+ systemHash: systemInstruction ? traceHash(systemInstruction) : null,
231
+ toolCount: Array.isArray(tools) ? tools.length : 0,
232
+ toolSchemaHash: traceHash(stableTraceStringify(toolShape)),
233
+ contentTurnCount: Array.isArray(contents) ? contents.length : 0,
234
+ contents: summarizeTraceContents(contents),
235
+ inputTokens,
236
+ cachedTokens,
237
+ reportedCachedTokens,
238
+ cachedFallbackTokens,
239
+ cacheTokenSource,
240
+ cacheAttached: !!cachedContent,
241
+ cachePrefixContentCount: opts?.providerState?.gemini?.cachePrefixContentCount ?? null,
242
+ cacheHitRate: inputTokens > 0 ? Number((cachedTokens / inputTokens).toFixed(6)) : null,
243
+ };
244
+ process.stderr.write(`[gemini-cache-trace] ${JSON.stringify(trace)}\n`);
245
+ } catch (err) {
246
+ process.stderr.write(`[gemini-cache-trace] failed: ${err?.message || err}\n`);
247
+ }
248
+ }