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
@@ -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
  }
@@ -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 { getOpenCodeGoAuthCookie } from '../../../shared/config.mjs';
6
+ import { round, cleanString as clean } from './lib/usage-primitives.mjs';
6
7
 
7
8
  const CACHE_FILE = 'opencode-go-usage-cache.json';
8
9
  const LIVE_TTL_MS = 5 * 60_000;
@@ -18,23 +19,14 @@ const LIMITS_USD = Object.freeze({
18
19
  const DISK_JSON_MEMORY_TTL_MS = 1000;
19
20
  let diskJsonCache = { at: 0, file: '', value: null };
20
21
 
22
+ // Local unguarded `num`: this module intentionally coerces '' to 0 via
23
+ // Number(''), unlike the guarded shared num() in lib/usage-primitives.mjs.
24
+ // Behavior differs on empty-string input, so it stays local.
21
25
  function num(value, fallback = null) {
22
26
  const n = Number(value);
23
27
  return Number.isFinite(n) ? n : fallback;
24
28
  }
25
29
 
26
- function round(value, digits = 4) {
27
- const n = Number(value);
28
- if (!Number.isFinite(n)) return null;
29
- const scale = 10 ** digits;
30
- return Math.round(n * scale) / scale;
31
- }
32
-
33
- function clean(value) {
34
- const text = typeof value === 'string' ? value.trim() : '';
35
- return text || null;
36
- }
37
-
38
30
  function cachePath() {
39
31
  return join(resolvePluginData(), CACHE_FILE);
40
32
  }
@@ -249,7 +241,41 @@ export function openCodeGoUsageConfigStatus(config = {}) {
249
241
  };
250
242
  }
251
243
 
244
+ // Primary discovery: GET /auth with the auth cookie, follow-manual. The
245
+ // console redirects authenticated sessions straight to /workspace/{id};
246
+ // unauthenticated/invalid cookies redirect to /auth/authorize instead.
247
+ // This avoids depending on the hashed server-fn id used by the /_server
248
+ // probe (WORKSPACES_SERVER_ID), which can change across console deploys.
249
+ async function fetchWorkspaceIdFromAuthRedirect(authCookie, { signal } = {}) {
250
+ let res;
251
+ try {
252
+ res = await fetch(`${BASE_URL}/auth`, {
253
+ signal,
254
+ redirect: 'manual',
255
+ headers: requestHeaders(authCookie),
256
+ });
257
+ } catch {
258
+ return null; // network/redirect-mode quirk: let the _server fallback decide
259
+ }
260
+ if (res.status === 401 || res.status === 403) {
261
+ const err = new Error('OpenCode Go console auth failed');
262
+ err.code = 'OPENCODE_GO_USAGE_AUTH_FAILED';
263
+ throw err;
264
+ }
265
+ const location = res.headers.get('location') || '';
266
+ if (!location) return null;
267
+ if (/(?:^|\/|\.\/)auth\/authorize\b/.test(location)) {
268
+ const err = new Error('OpenCode Go console auth failed');
269
+ err.code = 'OPENCODE_GO_USAGE_AUTH_FAILED';
270
+ throw err;
271
+ }
272
+ const workspaceMatch = location.match(/\/workspace\/(wrk_[a-zA-Z0-9]+)/);
273
+ return normalizeWorkspaceId(workspaceMatch ? workspaceMatch[1] : location);
274
+ }
275
+
252
276
  async function fetchWorkspaceId(authCookie, { signal } = {}) {
277
+ const fromRedirect = await fetchWorkspaceIdFromAuthRedirect(authCookie, { signal });
278
+ if (fromRedirect) return fromRedirect;
253
279
  const url = new URL(`${BASE_URL}/_server`);
254
280
  url.searchParams.set('id', WORKSPACES_SERVER_ID);
255
281
  const res = await fetch(url, {
@@ -232,12 +232,11 @@ export function jitterDelayMs(ms, ratio = PROVIDER_RETRY_JITTER_RATIO, mode = 's
232
232
  }
233
233
 
234
234
  // ── Shared network-resilience interface ──────────────────────────────────────
235
- // One home for the logic that used to be copied per provider: mid-stream
236
- // classifier (WS + SSE), transport fallback predicate, stream-safety stamp
237
- // latches, abort-aware sleep, handshake classifier, and the retry-budget table.
238
- // Provider differences are passed as ARGUMENTS (policy objects), never branched
239
- // on a hardcoded provider name. Behavior is preserved EXACTLY — each path below
240
- // is the relocated original, selected by policy.mode.
235
+ // One home for the logic shared across providers: mid-stream classifier
236
+ // (WS + SSE), transport fallback predicate, stream-safety stamp latches,
237
+ // abort-aware sleep, handshake classifier, and the retry-budget table.
238
+ // Provider differences are passed as ARGUMENTS (policy objects), never
239
+ // branched on a hardcoded provider name.
241
240
 
242
241
  // F) Retry-budget profiles as DATA. The numbers live ONLY here now.
243
242
  // ws.transientCloseRetries (4) — ws_1006 / ws_1011 connection-loss buckets.
@@ -260,8 +259,8 @@ function _midstreamLimitFor(classifier, policy) {
260
259
  return policy.defaultRetries
261
260
  }
262
261
 
263
- // WS gates each classifier against its own budget (mirrors the old
264
- // _allowMidstreamRetry). SSE applies a single top-of-function budget gate and
262
+ // WS gates each classifier against its own budget. SSE applies a single
263
+ // top-of-function budget gate and
265
264
  // then returns raw classifier strings, so perClassifierGate:false returns the
266
265
  // classifier unconditionally here.
267
266
  function _allowMidstream(classifier, attemptIndex, policy) {
@@ -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
+ }