mixdog 0.9.3 → 0.9.5

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 (382) hide show
  1. package/README.md +112 -38
  2. package/package.json +10 -3
  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/r5-orchestrated-task.json +7 -0
  7. package/scripts/bench/review-tasks.json +20 -0
  8. package/scripts/bench/round-codex.json +114 -0
  9. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  10. package/scripts/bench/round-mixdog-lead.json +269 -0
  11. package/scripts/bench/round-mixdog.json +126 -0
  12. package/scripts/bench/round-r10-bigsample.json +679 -0
  13. package/scripts/bench/round-r11-codexalign.json +257 -0
  14. package/scripts/bench/round-r13-clientmeta.json +464 -0
  15. package/scripts/bench/round-r14-betafeatures.json +466 -0
  16. package/scripts/bench/round-r15-fulldefault.json +462 -0
  17. package/scripts/bench/round-r16-sessionid.json +466 -0
  18. package/scripts/bench/round-r17-wirebytes.json +456 -0
  19. package/scripts/bench/round-r18-prewarm.json +468 -0
  20. package/scripts/bench/round-r19-clean.json +472 -0
  21. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  22. package/scripts/bench/round-r21-delta-retry.json +473 -0
  23. package/scripts/bench/round-r22-full-probe.json +693 -0
  24. package/scripts/bench/round-r23-itemprobe.json +701 -0
  25. package/scripts/bench/round-r24-shapefix.json +677 -0
  26. package/scripts/bench/round-r25-serial.json +464 -0
  27. package/scripts/bench/round-r26-parallel3.json +671 -0
  28. package/scripts/bench/round-r27-parallel10.json +894 -0
  29. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  30. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  31. package/scripts/bench/round-r30-instid.json +253 -0
  32. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  33. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  34. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  35. package/scripts/bench/round-r34-orchestrated.json +120 -0
  36. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  37. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  38. package/scripts/bench/round-r4-codex.json +114 -0
  39. package/scripts/bench/round-r4-mixed.json +225 -0
  40. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  41. package/scripts/bench/round-r6-codex.json +114 -0
  42. package/scripts/bench/round-r6-solo.json +257 -0
  43. package/scripts/bench/round-r7-full.json +254 -0
  44. package/scripts/bench/round-r8-fulldefault.json +255 -0
  45. package/scripts/bench-run.mjs +251 -32
  46. package/scripts/freevar-smoke.mjs +95 -0
  47. package/scripts/internal-comms-bench.mjs +3 -4
  48. package/scripts/internal-comms-smoke.mjs +10 -9
  49. package/scripts/model-catalog-audit.mjs +209 -0
  50. package/scripts/model-list-sanitize-test.mjs +37 -0
  51. package/scripts/mouse-probe.mjs +45 -0
  52. package/scripts/output-style-bench.mjs +13 -6
  53. package/scripts/output-style-smoke.mjs +4 -4
  54. package/scripts/provider-toolcall-test.mjs +7 -3
  55. package/scripts/recall-bench.mjs +76 -13
  56. package/scripts/recall-quality-cases.json +12 -0
  57. package/scripts/recall-usecase-cases.json +18 -0
  58. package/scripts/session-bench.mjs +152 -6
  59. package/scripts/tool-smoke.mjs +25 -65
  60. package/scripts/tui-render-smoke.mjs +90 -0
  61. package/scripts/webhook-smoke.mjs +208 -0
  62. package/src/agents/debugger/AGENT.md +4 -1
  63. package/src/agents/heavy-worker/AGENT.md +9 -8
  64. package/src/agents/maintainer/AGENT.md +4 -0
  65. package/src/agents/reviewer/AGENT.md +2 -1
  66. package/src/agents/scheduler-task/AGENT.md +2 -3
  67. package/src/agents/webhook-handler/AGENT.md +2 -3
  68. package/src/agents/worker/AGENT.md +10 -7
  69. package/src/app.mjs +12 -1
  70. package/src/headless-role.mjs +7 -1
  71. package/src/lib/rules-builder.cjs +4 -0
  72. package/src/mixdog-session-runtime.mjs +647 -2056
  73. package/src/output-styles/default.md +30 -9
  74. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  75. package/src/output-styles/minimal.md +8 -6
  76. package/src/output-styles/simple.md +21 -7
  77. package/src/rules/agent/00-common.md +6 -3
  78. package/src/rules/agent/30-explorer.md +16 -5
  79. package/src/rules/lead/01-general.md +5 -5
  80. package/src/rules/lead/lead-brief.md +15 -0
  81. package/src/rules/lead/lead-tool.md +6 -15
  82. package/src/rules/shared/01-tool.md +17 -21
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  84. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  85. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  86. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  87. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  88. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  89. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  90. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  91. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  92. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  93. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  95. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  97. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  100. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  101. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  102. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  103. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
  104. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  109. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  110. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
  111. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  112. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
  113. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
  117. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  119. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  120. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  121. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  122. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  123. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  124. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  125. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  126. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  127. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  128. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  129. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
  132. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  133. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  134. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  135. package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
  136. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  137. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  138. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  139. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  140. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  143. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  144. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  145. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  146. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  147. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
  149. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  152. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  155. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  157. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  158. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  159. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  161. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  163. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  164. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  165. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  166. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  167. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  168. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  169. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  170. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  171. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  172. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  173. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  174. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  175. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  176. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  177. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  178. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  179. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  180. package/src/runtime/channels/backends/discord.mjs +27 -318
  181. package/src/runtime/channels/backends/telegram.mjs +8 -12
  182. package/src/runtime/channels/index.mjs +247 -701
  183. package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
  184. package/src/runtime/channels/lib/config.mjs +37 -149
  185. package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
  186. package/src/runtime/channels/lib/event-queue.mjs +78 -13
  187. package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
  188. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  189. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  190. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  191. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  192. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  193. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  194. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  195. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  196. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  197. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  198. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  199. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  200. package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
  201. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  202. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  203. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  204. package/src/runtime/channels/lib/webhook.mjs +43 -616
  205. package/src/runtime/channels/tool-defs.mjs +11 -130
  206. package/src/runtime/memory/index.mjs +210 -1948
  207. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  208. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  209. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  210. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  211. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  212. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  213. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  214. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  215. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  216. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  217. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  218. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  219. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  220. package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
  221. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  222. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  223. package/src/runtime/memory/lib/query-handlers.mjs +802 -0
  224. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  225. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  226. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  227. package/src/runtime/memory/tool-defs.mjs +5 -13
  228. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  229. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  230. package/src/runtime/search/lib/web-tools.mjs +24 -602
  231. package/src/runtime/shared/atomic-file.mjs +26 -1
  232. package/src/runtime/shared/config.mjs +14 -4
  233. package/src/runtime/shared/launcher-control.mjs +2 -2
  234. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  235. package/src/runtime/shared/schedules-store.mjs +13 -3
  236. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  237. package/src/runtime/shared/tool-primitives.mjs +308 -0
  238. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  239. package/src/runtime/shared/tool-surface.mjs +80 -898
  240. package/src/runtime/shared/transcript-writer.mjs +23 -0
  241. package/src/runtime/shared/update-checker.mjs +7 -4
  242. package/src/session-runtime/config-helpers.mjs +119 -2
  243. package/src/session-runtime/config-lifecycle.mjs +232 -0
  244. package/src/session-runtime/cwd-plugins.mjs +226 -0
  245. package/src/session-runtime/mcp-glue.mjs +177 -0
  246. package/src/session-runtime/model-recency.mjs +111 -0
  247. package/src/session-runtime/native-search.mjs +247 -0
  248. package/src/session-runtime/output-styles.mjs +11 -9
  249. package/src/session-runtime/prewarm.mjs +142 -0
  250. package/src/session-runtime/provider-models.mjs +278 -0
  251. package/src/session-runtime/provider-usage.mjs +120 -0
  252. package/src/session-runtime/quick-model-rows.mjs +205 -0
  253. package/src/session-runtime/quick-search-models.mjs +47 -0
  254. package/src/session-runtime/session-hooks.mjs +93 -0
  255. package/src/session-runtime/settings-api.mjs +352 -0
  256. package/src/session-runtime/tool-catalog.mjs +29 -29
  257. package/src/session-runtime/tool-defs.mjs +84 -0
  258. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  259. package/src/session-runtime/workflow.mjs +1 -1
  260. package/src/standalone/agent-tool/helpers.mjs +237 -0
  261. package/src/standalone/agent-tool/notify.mjs +107 -0
  262. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  263. package/src/standalone/agent-tool/render.mjs +152 -0
  264. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  265. package/src/standalone/agent-tool.mjs +138 -669
  266. package/src/standalone/channel-admin.mjs +102 -90
  267. package/src/standalone/channel-worker.mjs +4 -7
  268. package/src/standalone/explore-tool.mjs +64 -14
  269. package/src/standalone/hook-bus/config.mjs +207 -0
  270. package/src/standalone/hook-bus/constants.mjs +90 -0
  271. package/src/standalone/hook-bus/handlers.mjs +481 -0
  272. package/src/standalone/hook-bus/payload.mjs +31 -0
  273. package/src/standalone/hook-bus/rules.mjs +77 -0
  274. package/src/standalone/hook-bus.mjs +77 -870
  275. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  276. package/src/standalone/opencode-go-login.mjs +5 -1
  277. package/src/standalone/provider-admin.mjs +1 -16
  278. package/src/standalone/usage-dashboard.mjs +3 -1
  279. package/src/tui/App.jsx +1059 -8110
  280. package/src/tui/app/app-format.mjs +213 -0
  281. package/src/tui/app/channel-pickers.mjs +508 -0
  282. package/src/tui/app/clipboard.mjs +67 -0
  283. package/src/tui/app/core-memory-picker.mjs +210 -0
  284. package/src/tui/app/extension-pickers.mjs +506 -0
  285. package/src/tui/app/input-parsers.mjs +193 -0
  286. package/src/tui/app/maintenance-pickers.mjs +356 -0
  287. package/src/tui/app/model-options.mjs +334 -0
  288. package/src/tui/app/model-picker.mjs +365 -0
  289. package/src/tui/app/onboarding-steps.mjs +400 -0
  290. package/src/tui/app/project-picker.mjs +247 -0
  291. package/src/tui/app/provider-setup-picker.mjs +580 -0
  292. package/src/tui/app/resume-picker.mjs +55 -0
  293. package/src/tui/app/route-pickers.mjs +419 -0
  294. package/src/tui/app/settings-picker.mjs +489 -0
  295. package/src/tui/app/slash-commands.mjs +101 -0
  296. package/src/tui/app/slash-dispatch.mjs +427 -0
  297. package/src/tui/app/text-layout.mjs +46 -0
  298. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  299. package/src/tui/app/transcript-window.mjs +677 -0
  300. package/src/tui/app/use-mouse-input.mjs +460 -0
  301. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  302. package/src/tui/app/use-transcript-scroll.mjs +512 -0
  303. package/src/tui/app/use-transcript-window.mjs +607 -0
  304. package/src/tui/components/ConfirmBar.jsx +10 -7
  305. package/src/tui/components/Picker.jsx +64 -15
  306. package/src/tui/components/PromptInput.jsx +33 -102
  307. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  308. package/src/tui/components/StatusLine.jsx +69 -15
  309. package/src/tui/components/TextEntryPanel.jsx +11 -0
  310. package/src/tui/components/ToolExecution.jsx +52 -594
  311. package/src/tui/components/TranscriptItem.jsx +105 -0
  312. package/src/tui/components/UsagePanel.jsx +18 -4
  313. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  314. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  315. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  316. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  317. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  318. package/src/tui/display-width.mjs +20 -3
  319. package/src/tui/dist/index.mjs +13553 -12384
  320. package/src/tui/engine/agent-job-feed.mjs +133 -0
  321. package/src/tui/engine/notification-plan.mjs +76 -0
  322. package/src/tui/engine/render-timing.mjs +17 -0
  323. package/src/tui/engine/tool-approval.mjs +94 -0
  324. package/src/tui/engine/tool-card-results.mjs +234 -0
  325. package/src/tui/engine/tool-result-status.mjs +135 -0
  326. package/src/tui/engine.mjs +170 -574
  327. package/src/tui/figures.mjs +5 -0
  328. package/src/tui/index.jsx +65 -1
  329. package/src/tui/input-editing.mjs +2 -2
  330. package/src/tui/markdown/format-token.mjs +4 -1
  331. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  332. package/src/tui/theme.mjs +6 -0
  333. package/src/ui/statusline-agents.mjs +213 -0
  334. package/src/ui/statusline-format.mjs +146 -0
  335. package/src/ui/statusline-segments.mjs +148 -0
  336. package/src/ui/statusline.mjs +77 -501
  337. package/src/ui/tool-card.mjs +0 -1
  338. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  339. package/src/workflows/default/WORKFLOW.md +16 -18
  340. package/src/workflows/sequential/WORKFLOW.md +16 -18
  341. package/vendor/ink/build/display-width.js +19 -3
  342. package/vendor/ink/build/ink.js +112 -7
  343. package/vendor/ink/build/log-update.js +17 -3
  344. package/vendor/ink/build/wrap-text.js +125 -0
  345. package/scripts/_test-folder-dialog.mjs +0 -30
  346. package/scripts/fix-brief-fn.mjs +0 -35
  347. package/scripts/fix-format-tool-surface.mjs +0 -24
  348. package/scripts/fix-tool-exec-visible.mjs +0 -42
  349. package/scripts/patch-agent-brief.mjs +0 -48
  350. package/scripts/patch-app.mjs +0 -21
  351. package/scripts/patch-app2.mjs +0 -18
  352. package/scripts/patch-dist-brief.mjs +0 -96
  353. package/scripts/patch-tool-exec.mjs +0 -70
  354. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  355. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  356. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  357. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  358. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  359. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  360. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  361. package/src/runtime/channels/lib/holidays.mjs +0 -138
  362. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  363. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  364. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  365. package/src/standalone/agent-task-status.test.mjs +0 -76
  366. package/src/tui/components/tool-output-format.test.mjs +0 -399
  367. package/src/tui/display-width.test.mjs +0 -35
  368. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  369. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  370. package/src/tui/input-editing.selection.test.mjs +0 -75
  371. package/src/tui/markdown/format-token.test.mjs +0 -354
  372. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  373. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  374. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  375. package/src/tui/paste-fix.test.mjs +0 -119
  376. package/src/tui/prompt-history-store.test.mjs +0 -52
  377. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  378. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  379. package/src/ui/markdown.test.mjs +0 -70
  380. package/src/ui/statusline-context-label.test.mjs +0 -15
  381. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  382. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -15,6 +15,7 @@
15
15
  import { sendViaWebSocket } from './openai-oauth-ws.mjs';
16
16
  import { buildRequestBody } from './openai-oauth.mjs';
17
17
  import { enrichModels } from './model-catalog.mjs';
18
+ import { sanitizeModelList } from './model-list-sanitize.mjs';
18
19
  import {
19
20
  resolveProviderCacheKey,
20
21
  resolveProviderPromptCacheLane,
@@ -143,7 +144,7 @@ export class OpenAIDirectProvider {
143
144
  // cutoff) can drop deprecated generations.
144
145
  created: typeof m.created === 'number' ? m.created : null,
145
146
  }));
146
- return await enrichModels(models);
147
+ return sanitizeModelList(await enrichModels(models), { provider: 'openai' });
147
148
  } catch {
148
149
  return [];
149
150
  }
@@ -0,0 +1,80 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { getPluginData } from '../config.mjs';
4
+
5
+ const PROVIDER_CACHE_FILES = Object.freeze({
6
+ 'openai-oauth': ['openai-oauth-models.json'],
7
+ 'anthropic-oauth': ['anthropic-oauth-models.json'],
8
+ anthropic: ['anthropic-oauth-models.json'],
9
+ gemini: ['gemini-models.json'],
10
+ 'grok-oauth': ['grok-oauth-models.json'],
11
+ });
12
+
13
+ export function providerUsesEndpointScopedLimits(provider) {
14
+ const p = String(provider || '').toLowerCase();
15
+ return p === 'openai-oauth' || p === 'anthropic-oauth' || p === 'grok-oauth';
16
+ }
17
+
18
+ function num(value) {
19
+ const n = Number(value);
20
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
21
+ }
22
+
23
+ function readModelsFromCacheFile(fileName) {
24
+ try {
25
+ const file = join(getPluginData(), fileName);
26
+ if (!existsSync(file)) return [];
27
+ const raw = JSON.parse(readFileSync(file, 'utf-8'));
28
+ return Array.isArray(raw?.models) ? raw.models : (Array.isArray(raw) ? raw : []);
29
+ } catch {
30
+ return [];
31
+ }
32
+ }
33
+
34
+ function modelAliases(id) {
35
+ const text = String(id || '').trim();
36
+ if (!text) return [];
37
+ const aliases = new Set([text]);
38
+ aliases.add(text.replace(/-\d{4}-\d{2}-\d{2}$/, ''));
39
+ aliases.add(text.replace(/-\d{8}$/, ''));
40
+ return [...aliases].filter(Boolean);
41
+ }
42
+
43
+ function rowId(row) {
44
+ return String(row?.id || row?.name || row?.slug || '').trim();
45
+ }
46
+
47
+ export function getProviderCachedModelSync(provider, model) {
48
+ const p = String(provider || '').toLowerCase();
49
+ const files = PROVIDER_CACHE_FILES[p] || [];
50
+ if (!files.length || !model) return null;
51
+ const aliases = new Set(modelAliases(model));
52
+ for (const fileName of files) {
53
+ for (const row of readModelsFromCacheFile(fileName)) {
54
+ const id = rowId(row);
55
+ if (!id || !aliases.has(id)) continue;
56
+ return row;
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+
62
+ export function providerCachedModelMetadataSync(provider, model) {
63
+ const row = getProviderCachedModelSync(provider, model);
64
+ if (!row) return null;
65
+ return {
66
+ contextWindow: num(row.contextWindow ?? row.context_window ?? row.maxContextWindow ?? row.max_context_window ?? row.max_input_tokens),
67
+ outputTokens: num(row.outputTokens ?? row.maxOutputTokens ?? row.max_output_tokens ?? row.output_token_limit),
68
+ supportsVision: row.supportsVision === true,
69
+ supportsFunctionCalling: row.supportsFunctionCalling === true,
70
+ supportsWebSearch: row.supportsWebSearch === true,
71
+ supportsPromptCaching: row.supportsPromptCaching === true,
72
+ supportsReasoning: row.supportsReasoning === true,
73
+ reasoningOptions: Array.isArray(row.reasoningOptions) ? row.reasoningOptions : [],
74
+ reasoningContentField: row.reasoningContentField || null,
75
+ mode: row.mode || null,
76
+ displayName: row.displayName || row.display || row.name || null,
77
+ name: row.name || row.display || null,
78
+ rawProviderModel: row,
79
+ };
80
+ }
@@ -0,0 +1,288 @@
1
+ // Budget math, tool-output pruning, cycle1 draining, and preserved-fact
2
+ // extraction. Extracted verbatim from compact.mjs (behavior-preserving).
3
+ import { createHash } from 'node:crypto';
4
+ import {
5
+ sanitizeToolPairs,
6
+ dedupToolResultBodies,
7
+ reconcileDedupStubs,
8
+ estimateMessagesTokens,
9
+ DEFAULT_COMPACTION_KEEP_TOKENS,
10
+ } from '../context-utils.mjs';
11
+ import { extractText, summarizeToolCall, redactRawSecretString, TOOL_CALL_FACT_ARGS_MAX_CHARS } from './text-utils.mjs';
12
+
13
+ // Floor for the reserve-adjusted compact budget. When the tool-schema/request
14
+ // reserve rivals the whole budget (huge agent tool surfaces), subtracting the
15
+ // full reserve could leave a degenerate target; keep enough room to attempt a
16
+ // summary and let the final fit check decide. Logged as degraded because a
17
+ // floored budget can still overflow on the next send.
18
+ const MIN_EFFECTIVE_COMPACT_BUDGET_TOKENS = 1024;
19
+ export function effectiveBudget(budgetTokens, opts) {
20
+ if (!(budgetTokens > 0)) throw new Error('compact: budgetTokens must be > 0');
21
+ const reserve = Number(opts?.reserveTokens) || 0;
22
+ if (reserve <= 0) return budgetTokens;
23
+ // Subtract the FULL reserve so an accepted compact actually fits next to
24
+ // the request reserve on the following send. The previous 50%-of-budget cap
25
+ // under-reserved large tool surfaces (agent sessions): a compact could be
26
+ // "accepted" at budget/2 while the true remaining room was smaller, then
27
+ // overflow immediately on the next request.
28
+ const remaining = budgetTokens - reserve;
29
+ if (remaining >= MIN_EFFECTIVE_COMPACT_BUDGET_TOKENS) return remaining;
30
+ const floored = Math.max(1, Math.min(budgetTokens, MIN_EFFECTIVE_COMPACT_BUDGET_TOKENS));
31
+ try { process.stderr.write(`[compact] degraded budget: reserve=${reserve} leaves ${remaining} of budget=${budgetTokens}; flooring to ${floored}\n`); } catch { /* best-effort */ }
32
+ return floored;
33
+ }
34
+
35
+ const PRUNE_TOOL_OUTPUT_MAX_CHARS = 2_000;
36
+ const PRUNE_TOOL_OUTPUT_HEAD_CHARS = 1_000;
37
+ const PRUNE_TOOL_OUTPUT_TAIL_CHARS = 600;
38
+ const PRUNE_TAIL_TURNS = 2;
39
+ export const MIN_PRESERVE_RECENT_TOKENS = 2_000;
40
+ export const PRESERVED_FACTS_MAX_CHARS = 600;
41
+
42
+ // Count raw (unchunked) pending rows still present in a dump_session_roots
43
+ // payload. recall-fasttrack must keep cycle1-chunking until this reaches 0 so
44
+ // the injected root is the chunked summary, not the raw transcript tail.
45
+ export function countRawPendingRows(dumpText) {
46
+ const text = String(dumpText || '');
47
+ const matches = text.match(/(?:^|\n)# raw_pending\s+\d+\s+id=/gi);
48
+ return matches ? matches.length : 0;
49
+ }
50
+
51
+ // Drain a single session's cycle1 in fixed window×concurrency units, looping
52
+ // until no raw rows remain (or a pass stops making progress / the deadline
53
+ // elapses). This replaces the previous single-pass cycle1 so large sessions
54
+ // get fully chunked before their root is injected into the compacted context.
55
+ export async function drainSessionCycle1(runTool, { sessionId, cycleArgs = {}, dumpArgs, maxPasses = 0, deadlineMs = 0 } = {}) {
56
+ if (typeof runTool !== 'function') throw new Error('drainSessionCycle1: runTool is required');
57
+ if (!sessionId) throw new Error('drainSessionCycle1: sessionId is required');
58
+ if (!dumpArgs) throw new Error('drainSessionCycle1: dumpArgs is required');
59
+ const startedAt = Date.now();
60
+ const hardPasses = Math.max(1, Number(maxPasses) || 50);
61
+ const lines = [];
62
+ let cycle1Error = null;
63
+ let recallText = await runTool('memory', dumpArgs);
64
+ let rawRemaining = countRawPendingRows(recallText);
65
+ let pass = 0;
66
+ while (rawRemaining > 0 && pass < hardPasses) {
67
+ if (deadlineMs > 0 && (Date.now() - startedAt) >= deadlineMs) break;
68
+ pass += 1;
69
+ const passDeadline = deadlineMs > 0
70
+ ? Math.max(1, deadlineMs - (Date.now() - startedAt))
71
+ : 0;
72
+ let passText = '';
73
+ try {
74
+ passText = await runTool('memory', {
75
+ action: 'cycle1',
76
+ sessionId,
77
+ ...cycleArgs,
78
+ ...(passDeadline > 0 ? { _callerDeadlineMs: passDeadline } : {}),
79
+ });
80
+ } catch (err) {
81
+ cycle1Error = err?.message ? String(err.message) : String(err);
82
+ lines.push(`cycle1 pass=${pass} error=${cycle1Error}`);
83
+ break;
84
+ }
85
+ if (passText) lines.push(`cycle1 pass=${pass}: ${String(passText).trim()}`);
86
+ recallText = await runTool('memory', dumpArgs);
87
+ const nextRaw = countRawPendingRows(recallText);
88
+ // No forward progress (raw not shrinking) — stop instead of spinning.
89
+ if (nextRaw >= rawRemaining) {
90
+ rawRemaining = nextRaw;
91
+ break;
92
+ }
93
+ rawRemaining = nextRaw;
94
+ }
95
+ return {
96
+ recallText,
97
+ cycle1Text: lines.join('\n'),
98
+ passes: pass,
99
+ rawRemaining,
100
+ error: cycle1Error,
101
+ };
102
+ }
103
+
104
+ function preservedFactHints() {
105
+ return String(process.env.MIXDOG_COMPACT_FACT_HINTS || '')
106
+ .split(/[\n,;]+/u)
107
+ .map(s => s.trim())
108
+ .filter(Boolean)
109
+ .map(s => s.toLocaleLowerCase());
110
+ }
111
+
112
+ const PRESERVED_FACT_HINTS = preservedFactHints();
113
+
114
+ function hasConfiguredPreservedFactHint(text) {
115
+ const lower = String(text || '').toLocaleLowerCase();
116
+ return PRESERVED_FACT_HINTS.some(hint => hint && lower.includes(hint));
117
+ }
118
+
119
+ export function extractPreservedFacts(messages) {
120
+ if (!messages || messages.length === 0) return '';
121
+ const candidates = [];
122
+ const seenSignatures = new Set();
123
+ const add = (prefix, text, score, messageIndex, lineIndex = 0) => {
124
+ const clean = text.replace(/`/g, '').trim();
125
+ if (clean.length < 10) return;
126
+ const sig = clean.slice(0, 80).toLowerCase().replace(/\s+/g, ' ');
127
+ if (seenSignatures.has(sig)) return;
128
+ seenSignatures.add(sig);
129
+ candidates.push({ prefix, text: clean, score, messageIndex, lineIndex });
130
+ };
131
+ const classifyLine = (t) => {
132
+ // Language-neutral structural cues: exact assignments, paths, URLs,
133
+ // symbolic error/constant identifiers, and optional user-configured
134
+ // locale/domain hints. Do not bake human-language keyword lists here.
135
+ if (/[\p{L}\p{N}_$./:-]{2,}\s*=\s*\S+/u.test(t)) return { prefix: '•', score: 100 };
136
+ if (/(?:^|[\s('"`])https?:\/\/[^\s'"`<>]+/iu.test(t)) return { prefix: '•', score: 95 };
137
+ if (/(?:^|[\s('"`])(?:[A-Za-z]:[\\/]|\.{1,2}[\\/]|~?[\\/][^\s'"`<>]+|[\p{L}\p{N}_$.-]+[\\/][^\s'"`<>]+|[\p{L}\p{N}_$.-]+\.(?:mjs|cjs|js|jsx|ts|tsx|json|md|rs|go|py|java|kt|cs|cpp|c|h|hpp|css|html|yml|yaml|toml|lock|sh|ps1)\b)/iu.test(t)) return { prefix: '•', score: 95 };
138
+ if (/\b[A-Z][A-Z0-9_:-]{2,}\b/.test(t)) return { prefix: '•', score: 90 };
139
+ if (hasConfiguredPreservedFactHint(t)) return { prefix: '!', score: 70 };
140
+ return null;
141
+ };
142
+ for (let mi = 0; mi < messages.length; mi += 1) {
143
+ const m = messages[mi];
144
+ const text = extractText(m);
145
+ if (!text) continue;
146
+ const lines = text.split('\n');
147
+ for (let li = 0; li < lines.length; li += 1) {
148
+ const line = lines[li];
149
+ const t = line.trim();
150
+ if (t.length < 10 || t.length > 250) continue;
151
+ const cls = classifyLine(t);
152
+ if (cls) add(cls.prefix, redactRawSecretString(t), cls.score, mi, li);
153
+ }
154
+ if (m?.role === 'assistant' && Array.isArray(m.toolCalls)) {
155
+ for (const tc of m.toolCalls) {
156
+ const name = tc?.function?.name || tc?.name || '';
157
+ if (name) {
158
+ const summary = summarizeToolCall(tc, TOOL_CALL_FACT_ARGS_MAX_CHARS);
159
+ const sig = `tool:${summary.slice(0, 160).toLowerCase()}`;
160
+ if (seenSignatures.has(sig)) continue;
161
+ seenSignatures.add(sig);
162
+ candidates.push({ prefix: '•', text: `Tool: ${summary}`, score: 50, messageIndex: mi, lineIndex: Number.MAX_SAFE_INTEGER });
163
+ }
164
+ }
165
+ }
166
+ }
167
+ if (candidates.length === 0) return '';
168
+ candidates.sort((a, b) =>
169
+ (b.score - a.score)
170
+ || (b.messageIndex - a.messageIndex)
171
+ || (b.lineIndex - a.lineIndex)
172
+ );
173
+ let result = '## Preserved Facts\n';
174
+ let kept = 0;
175
+ for (const c of candidates) {
176
+ if (kept >= 25) break;
177
+ let line = `- ${c.prefix} ${c.text}\n`;
178
+ if (result.length + line.length > PRESERVED_FACTS_MAX_CHARS) {
179
+ const room = PRESERVED_FACTS_MAX_CHARS - result.length - 8;
180
+ if (kept === 0 && room > 32) line = `- ${c.prefix} ${c.text.slice(0, room)}…\n`;
181
+ else continue;
182
+ }
183
+ result += line;
184
+ kept += 1;
185
+ }
186
+ return kept > 0 ? result : '';
187
+ }
188
+
189
+ function protectedTailStart(messages, tailTurns = PRUNE_TAIL_TURNS) {
190
+ let seenUsers = 0;
191
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
192
+ if (messages[i]?.role !== 'user') continue;
193
+ seenUsers += 1;
194
+ if (seenUsers >= tailTurns) return i;
195
+ }
196
+ return 0;
197
+ }
198
+
199
+ function pruneToolOutputText(text, maxChars, toolCallId) {
200
+ const value = String(text ?? '').replace(/\r\n/g, '\n');
201
+ if (value.length <= maxChars) return value;
202
+ const hash = createHash('sha256').update(value).digest('hex').slice(0, 16);
203
+ const head = value.slice(0, PRUNE_TOOL_OUTPUT_HEAD_CHARS);
204
+ const tail = value.slice(-PRUNE_TOOL_OUTPUT_TAIL_CHARS);
205
+ return [
206
+ `[mixdog pruned old tool output: ${value.length} chars, sha256:${hash}${toolCallId ? `, tool_use_id=${toolCallId}` : ''}]`,
207
+ head,
208
+ '... [old tool output omitted during worker compaction] ...',
209
+ tail,
210
+ ].join('\n');
211
+ }
212
+
213
+ export function pruneToolOutputs(messages, budgetTokens, opts = {}) {
214
+ const budget = effectiveBudget(budgetTokens, opts);
215
+ let result = reconcileDedupStubs(dedupToolResultBodies(sanitizeToolPairs(messages)));
216
+ if (estimateMessagesTokens(result) <= budget) return result;
217
+
218
+ const maxChars = Math.max(256, Number(opts?.maxToolOutputChars) || PRUNE_TOOL_OUTPUT_MAX_CHARS);
219
+ const protectFrom = protectedTailStart(result, Number(opts?.tailTurns) || PRUNE_TAIL_TURNS);
220
+ const candidates = [];
221
+ for (let i = 0; i < protectFrom; i += 1) {
222
+ const m = result[i];
223
+ if (m?.role !== 'tool' || typeof m.content !== 'string') continue;
224
+ if (m.content.length <= maxChars) continue;
225
+ candidates.push({ index: i, length: m.content.length });
226
+ }
227
+ candidates.sort((a, b) => b.length - a.length);
228
+ for (const c of candidates) {
229
+ const m = result[c.index];
230
+ result[c.index] = {
231
+ ...m,
232
+ content: pruneToolOutputText(m.content, maxChars, m.toolCallId),
233
+ compacted: true,
234
+ compactedKind: 'tool_output_prune',
235
+ };
236
+ if (estimateMessagesTokens(result) <= budget) break;
237
+ }
238
+ return reconcileDedupStubs(result);
239
+ }
240
+
241
+ // Anchor-independent tool-output prune (loop overflow safety net).
242
+ //
243
+ // pruneToolOutputs protects the most-recent tailTurns of USER-anchored history,
244
+ // so a single-turn transcript with no user boundary yields protectFrom=0 and
245
+ // prunes nothing. This variant needs no user anchor: it middle-truncates the
246
+ // OLDEST oversized tool_result bodies first, walking forward, until the
247
+ // transcript fits the budget. The newest tool_result is truncated last (and
248
+ // only if still necessary) so fresh state is preserved as long as possible.
249
+ // Structure/pairing is preserved (only string content shrinks), and the result
250
+ // is re-reconciled so tool pairing stays provider-valid.
251
+ export function pruneToolOutputsUnanchored(messages, budgetTokens, opts = {}) {
252
+ const budget = effectiveBudget(budgetTokens, opts);
253
+ let result = reconcileDedupStubs(dedupToolResultBodies(sanitizeToolPairs(messages)));
254
+ if (estimateMessagesTokens(result) <= budget) return result;
255
+
256
+ const maxChars = Math.max(256, Number(opts?.maxToolOutputChars) || PRUNE_TOOL_OUTPUT_MAX_CHARS);
257
+ // Oldest -> newest so recent tool output survives longest. No user-turn
258
+ // protection: every oversized tool_result is a candidate.
259
+ for (let i = 0; i < result.length; i += 1) {
260
+ const m = result[i];
261
+ if (m?.role !== 'tool' || typeof m.content !== 'string') continue;
262
+ if (m.content.length <= maxChars) continue;
263
+ result[i] = {
264
+ ...m,
265
+ content: pruneToolOutputText(m.content, maxChars, m.toolCallId),
266
+ compacted: true,
267
+ compactedKind: 'tool_output_prune',
268
+ };
269
+ if (estimateMessagesTokens(result) <= budget) break;
270
+ }
271
+ return reconcileDedupStubs(result);
272
+ }
273
+
274
+ export function preserveRecentBudget(budget, opts = {}) {
275
+ const maxForBudget = Math.max(1, Math.floor(Number(budget || 0) * 0.8));
276
+ const explicit = Number(opts.preserveRecentTokens ?? opts.keepTokens);
277
+ if (Number.isFinite(explicit) && explicit > 0) {
278
+ return Math.max(1, Math.min(Math.floor(explicit), maxForBudget));
279
+ }
280
+ return Math.max(
281
+ 1,
282
+ Math.min(
283
+ DEFAULT_COMPACTION_KEEP_TOKENS,
284
+ Math.max(MIN_PRESERVE_RECENT_TOKENS, Math.floor(budget * 0.25)),
285
+ maxForBudget,
286
+ ),
287
+ );
288
+ }
@@ -0,0 +1,85 @@
1
+ // Shared compaction constants and compact-type helpers. Extracted verbatim
2
+ // from compact.mjs (behavior-preserving). compact.mjs re-exports the public
3
+ // members from here so the external surface is unchanged.
4
+ import {
5
+ DEFAULT_COMPACTION_BUFFER_TOKENS,
6
+ DEFAULT_COMPACTION_BUFFER_RATIO,
7
+ MAX_COMPACTION_BUFFER_RATIO,
8
+ DEFAULT_COMPACTION_KEEP_TOKENS,
9
+ normalizeCompactionBufferRatio,
10
+ compactionBufferTokensForBoundary,
11
+ } from '../context-utils.mjs';
12
+
13
+ export {
14
+ DEFAULT_COMPACTION_BUFFER_TOKENS,
15
+ DEFAULT_COMPACTION_BUFFER_RATIO,
16
+ MAX_COMPACTION_BUFFER_RATIO,
17
+ DEFAULT_COMPACTION_KEEP_TOKENS,
18
+ normalizeCompactionBufferRatio,
19
+ compactionBufferTokensForBoundary,
20
+ };
21
+
22
+ export const SUMMARY_PREFIX = 'A previous model worked on this task and produced the compacted handoff summary below. Build on the work already done and avoid duplicating it; treat the summary as authoritative context for continuing the task. You also retain the preserved recent turns that follow.';
23
+ export const SUMMARY_OUTPUT_TOKENS = 4_096;
24
+ // Unified context-share rule: every derived "how much of the model context
25
+ // may this budget take" ratio uses ONE number — 5%. Consumers:
26
+ // - compact target budget (loop/compact-policy.mjs COMPACT_TARGET_RATIO)
27
+ // - recall-fasttrack injection cap (loop.mjs recallTokenCap)
28
+ // Keep them in lockstep; do not fork per-consumer ratios without a decision.
29
+ export const CONTEXT_SHARE_RATIO = 0.05;
30
+ // Floor for the recall-injection cap so small-context models still get a
31
+ // usable recall slice (cap = max(floor, contextWindow * CONTEXT_SHARE_RATIO)).
32
+ export const RECALL_TOKEN_CAP_FLOOR_TOKENS = 2_048;
33
+ // Minimum room the generated summary needs after the mandatory (system +
34
+ // preserved tail) cost is accounted for. When the configured target budget is
35
+ // smaller than the mandatory cost (e.g. the preserved recent turn carries a
36
+ // large tool result), the compaction MUST still proceed: the old head is the
37
+ // part being summarized away, so dropping it already shrinks the transcript.
38
+ // Refusing with "exceeds budget" here is what surfaced as auto-clear / overflow
39
+ // compact failures. Floor the working budget to mandatory + this room instead.
40
+ export const COMPACT_SUMMARY_MIN_ROOM_TOKENS = 4_000;
41
+
42
+ export const COMPACT_TYPE_SEMANTIC = 'semantic';
43
+ export const COMPACT_TYPE_RECALL_FASTTRACK = 'recall-fasttrack';
44
+ export const DEFAULT_COMPACT_TYPE = COMPACT_TYPE_SEMANTIC;
45
+ export const COMPACT_TYPES = Object.freeze([
46
+ COMPACT_TYPE_SEMANTIC,
47
+ COMPACT_TYPE_RECALL_FASTTRACK,
48
+ ]);
49
+
50
+ export function normalizeCompactType(value, fallback = DEFAULT_COMPACT_TYPE) {
51
+ const raw = String(value ?? '').trim().toLowerCase().replace(/_/g, '-');
52
+ if (!raw) return fallback;
53
+ if (raw === '1' || raw === 'type1' || raw === 'type-1' || raw === 'bench1' || raw === 'bench-1' || raw === 'semantic' || raw === 'summary') {
54
+ return COMPACT_TYPE_SEMANTIC;
55
+ }
56
+ // Recall fast-track aliases. `replace(/_/g,'-')` above already folds
57
+ // snake_case (fast_track -> fast-track), but list both dash/no-dash forms
58
+ // explicitly so callers passing either spelling resolve deterministically.
59
+ if (raw === '2' || raw === 'type2' || raw === 'type-2' || raw === 'recall' || raw === 'recall-fast' || raw === 'recall-fasttrack' || raw === 'recall-fast-track' || raw === 'fasttrack' || raw === 'fast-track') {
60
+ return COMPACT_TYPE_RECALL_FASTTRACK;
61
+ }
62
+ // Unknown / unrecognized value: fall back to the caller-provided default
63
+ // (semantic by default). Callers that need to detect an unknown value
64
+ // should compare the input against COMPACT_TYPES before normalizing.
65
+ return fallback;
66
+ }
67
+
68
+ export function compactTypeIsSemantic(value) {
69
+ return normalizeCompactType(value) === COMPACT_TYPE_SEMANTIC;
70
+ }
71
+
72
+ export function compactTypeIsRecallFastTrack(value) {
73
+ return normalizeCompactType(value) === COMPACT_TYPE_RECALL_FASTTRACK;
74
+ }
75
+
76
+ export function compactDebugEnabled() {
77
+ return String(process.env.MIXDOG_COMPACT_DEBUG || '').trim() === '1';
78
+ }
79
+
80
+ export function compactDebugLog(scope, details = {}) {
81
+ if (!compactDebugEnabled()) return;
82
+ try {
83
+ process.stderr.write(`[compact] ${scope} ${JSON.stringify(details)}\n`);
84
+ } catch { /* best-effort diagnostics only */ }
85
+ }