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
@@ -0,0 +1,82 @@
1
+ // Message classification, protected-context splitting, and summary-message
2
+ // construction helpers. Extracted verbatim from compact.mjs
3
+ // (behavior-preserving).
4
+ import { SUMMARY_PREFIX } from './constants.mjs';
5
+ import { sha16, roleCounts } from './text-utils.mjs';
6
+
7
+ export function compactHeader(oldHistory) {
8
+ const encoded = JSON.stringify(oldHistory ?? []);
9
+ return [
10
+ SUMMARY_PREFIX,
11
+ `messages=${oldHistory.length} sha256=${sha16(encoded)} roles=${roleCounts(oldHistory) || 'none'}`,
12
+ ];
13
+ }
14
+
15
+ export function makeSummaryMessage(content) {
16
+ return { role: 'user', content };
17
+ }
18
+
19
+ // A compact summary message is a synthetic role:'user' message carrying the
20
+ // SUMMARY_PREFIX anchor. It is NOT a real user turn: it must be excluded from
21
+ // real user-turn boundary calculations and treated as merge input, otherwise
22
+ // an old summary can sit in the preserved tail as a live user message,
23
+ // duplicate, or fail to merge across repeated compaction.
24
+ export function isSummaryMessage(m) {
25
+ return m?.role === 'user'
26
+ && typeof m.content === 'string'
27
+ && m.content.startsWith(SUMMARY_PREFIX);
28
+ }
29
+
30
+ export function isProtectedContextUserMessage(m) {
31
+ if (m?.role !== 'user' || typeof m.content !== 'string') return false;
32
+ return m.content.trimStart().startsWith('<system-reminder>');
33
+ }
34
+
35
+ // An injected Skill-body user message (the general newMessages channel carries
36
+ // the full SKILL.md body as a role:'user' message after the Skill tool_result).
37
+ // Like isSummaryMessage / isProtectedContextUserMessage, it is detected by
38
+ // content prefix (the `<skill>` envelope from buildSkillResultEnvelope) so the
39
+ // check survives even if the synthetic `meta` field is dropped during a tail
40
+ // rebuild. It is NOT the human's latest prompt and must be excluded from
41
+ // "latest human request" selection (deriveCurrentRequest /
42
+ // buildRecallFastTrackQuery). The `meta:'skill'` marker is also honoured.
43
+ export function isInjectedSkillBodyMessage(m) {
44
+ if (m?.role !== 'user') return false;
45
+ if (m.meta === 'skill') return true;
46
+ return typeof m.content === 'string' && m.content.trimStart().startsWith('<skill>');
47
+ }
48
+
49
+ export function isProtectedContextAckMessage(m) {
50
+ return m?.role === 'assistant'
51
+ && typeof m.content === 'string'
52
+ && m.content.trim() === '.'
53
+ && !Array.isArray(m.toolCalls);
54
+ }
55
+
56
+ export function splitProtectedContext(messages) {
57
+ const protectedPrefix = [];
58
+ const conversation = [];
59
+ let prefixMode = true;
60
+ let previousWasProtectedContext = false;
61
+ for (const m of messages || []) {
62
+ if (m?.role === 'system') {
63
+ protectedPrefix.push(m);
64
+ previousWasProtectedContext = false;
65
+ continue;
66
+ }
67
+ if (prefixMode && isProtectedContextUserMessage(m)) {
68
+ protectedPrefix.push(m);
69
+ previousWasProtectedContext = true;
70
+ continue;
71
+ }
72
+ if (prefixMode && previousWasProtectedContext && isProtectedContextAckMessage(m)) {
73
+ protectedPrefix.push(m);
74
+ previousWasProtectedContext = false;
75
+ continue;
76
+ }
77
+ prefixMode = false;
78
+ previousWasProtectedContext = false;
79
+ conversation.push(m);
80
+ }
81
+ return { protectedPrefix, conversation };
82
+ }
@@ -0,0 +1,315 @@
1
+ // Semantic-summary schema machinery: required-section anchors, heading
2
+ // matching, schema validation, deterministic repair/backfill, minimal
3
+ // schema-valid scaffold, and section-aware truncation. Extracted verbatim
4
+ // from summary.mjs (behavior-preserving) — summary.mjs re-exports the
5
+ // public entry points so the compact/ facade surface is unchanged.
6
+ import {
7
+ extractText,
8
+ truncateMiddle,
9
+ } from './text-utils.mjs';
10
+ import {
11
+ isProtectedContextUserMessage,
12
+ isInjectedSkillBodyMessage,
13
+ } from './messages.mjs';
14
+
15
+ // Canonical section anchors the semantic summary template (SUMMARY_TEMPLATE)
16
+ // must contain. Used for lightweight schema validation of provider output.
17
+ const REQUIRED_SUMMARY_SECTIONS = Object.freeze([
18
+ '## Goal',
19
+ '## Constraints',
20
+ '## Progress',
21
+ '## Key Decisions',
22
+ '## Next Steps',
23
+ '## Critical Context',
24
+ '## Relevant Files',
25
+ ]);
26
+
27
+ // Collect actual top-level (`## `, not `### `) heading lines from a summary.
28
+ // Validation is heading-anchor based (not substring includes) so prose or code
29
+ // that merely mentions "## Relevant Files" inside a bullet body cannot satisfy
30
+ // a section anchor.
31
+ function summaryHeadingLines(summary) {
32
+ const out = [];
33
+ let inFence = false;
34
+ for (const rawLine of String(summary || '').split('\n')) {
35
+ const line = rawLine.trim();
36
+ if (/^(?:```|~~~)/.test(line)) { inFence = !inFence; continue; }
37
+ if (inFence) continue;
38
+ if (/^##\s+\S/.test(line) && !/^###\s+/.test(line)) out.push(line);
39
+ }
40
+ return out;
41
+ }
42
+
43
+ // An anchor matches a heading when the heading title equals the anchor title or
44
+ // extends it at a word/punctuation boundary. This lets `## Constraints &
45
+ // Preferences` satisfy the `## Constraints` anchor while NOT letting an
46
+ // unrelated `## Goalkeeper` heading satisfy `## Goal`. Requires a real `## `
47
+ // heading line (not a substring buried in prose).
48
+ export function headingMatchesAnchor(heading, anchor) {
49
+ const anchorTitle = anchor.replace(/^##\s+/, '').trim().toLowerCase();
50
+ const headingTitle = heading.replace(/^##\s+/, '').trim().toLowerCase();
51
+ if (headingTitle === anchorTitle) return true;
52
+ if (!headingTitle.startsWith(anchorTitle)) return false;
53
+ // Next char after the anchor title must be a boundary (space or &/punct),
54
+ // not a continuation letter/digit.
55
+ const nextChar = headingTitle.charAt(anchorTitle.length);
56
+ return /[\s&:(-]/.test(nextChar);
57
+ }
58
+
59
+ function summarySchemaScore(summary) {
60
+ const headings = summaryHeadingLines(summary);
61
+ let hits = 0;
62
+ for (const anchor of REQUIRED_SUMMARY_SECTIONS) {
63
+ if (headings.some((h) => headingMatchesAnchor(h, anchor))) hits += 1;
64
+ }
65
+ return hits;
66
+ }
67
+
68
+ // A summary is schema-valid only when EVERY required section anchor is present
69
+ // as a real heading. A partial summary (e.g. missing Critical Context /
70
+ // Relevant Files) must be repaired rather than injected unchanged.
71
+ export function summaryIsSchemaValid(summary) {
72
+ if (summarySchemaScore(summary) !== REQUIRED_SUMMARY_SECTIONS.length) return false;
73
+ return !summaryHasUnrecognizedHeadings(summary);
74
+ }
75
+
76
+ function deriveRelevantFilesBullets(head) {
77
+ const seen = new Set();
78
+ const out = [];
79
+ const fileRe = /(?:[A-Za-z]:[\\/]|\.{1,2}[\\/]|[\w$.-]+[\\/])?[\w$.-]+\.(?: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/gi;
80
+ for (const m of Array.isArray(head) ? head : []) {
81
+ const text = extractText(m);
82
+ if (!text) continue;
83
+ let match;
84
+ while ((match = fileRe.exec(text)) && out.length < 8) {
85
+ const file = match[0];
86
+ const key = file.toLowerCase();
87
+ if (seen.has(key)) continue;
88
+ seen.add(key);
89
+ out.push(`- ${file}`);
90
+ }
91
+ if (out.length >= 8) break;
92
+ }
93
+ return out;
94
+ }
95
+
96
+ function deriveCurrentRequest(messages) {
97
+ for (let i = (Array.isArray(messages) ? messages.length : 0) - 1; i >= 0; i -= 1) {
98
+ const m = messages[i];
99
+ if (m?.role === 'user' && !isProtectedContextUserMessage(m) && !isInjectedSkillBodyMessage(m)) {
100
+ const text = truncateMiddle(extractText(m).trim(), 400);
101
+ if (text) return text;
102
+ }
103
+ }
104
+ return '';
105
+ }
106
+
107
+ // Canonical ordered section headings the structured summary scaffold emits.
108
+ // Each `## ` heading maps to one REQUIRED_SUMMARY_SECTIONS anchor; Progress
109
+ // additionally carries its three `### ` sub-headings.
110
+ const SUMMARY_SECTION_LAYOUT = Object.freeze([
111
+ { heading: '## Goal', anchor: '## Goal' },
112
+ { heading: '## Constraints & Preferences', anchor: '## Constraints' },
113
+ { heading: '## Progress', anchor: '## Progress', sub: ['### Done', '### In Progress', '### Blocked'] },
114
+ { heading: '## Key Decisions', anchor: '## Key Decisions' },
115
+ { heading: '## Next Steps', anchor: '## Next Steps' },
116
+ { heading: '## Critical Context', anchor: '## Critical Context' },
117
+ { heading: '## Relevant Files', anchor: '## Relevant Files' },
118
+ ]);
119
+
120
+ // Split a markdown summary into a map of top-level `## ` heading -> body lines.
121
+ function parseSummarySections(text) {
122
+ const map = new Map();
123
+ let current = null;
124
+ let inFence = false;
125
+ for (const rawLine of String(text || '').split('\n')) {
126
+ const trimmed = rawLine.trim();
127
+ const line = rawLine.replace(/\s+$/, '');
128
+ if (/^(?:```|~~~)/.test(trimmed)) {
129
+ inFence = !inFence;
130
+ if (current) map.get(current).push(line);
131
+ continue;
132
+ }
133
+ if (!inFence && /^##\s+/.test(trimmed) && !/^###\s+/.test(trimmed)) {
134
+ current = trimmed;
135
+ if (!map.has(current)) map.set(current, []);
136
+ continue;
137
+ }
138
+ if (current) map.get(current).push(line);
139
+ }
140
+ return map;
141
+ }
142
+
143
+ function summarySectionIsRecognized(heading) {
144
+ for (const section of SUMMARY_SECTION_LAYOUT) {
145
+ if (headingMatchesAnchor(heading, section.anchor)) return true;
146
+ }
147
+ return false;
148
+ }
149
+
150
+ export function summaryHasUnrecognizedHeadings(summary) {
151
+ for (const heading of parseSummarySections(summary).keys()) {
152
+ if (!summarySectionIsRecognized(heading)) return true;
153
+ }
154
+ return false;
155
+ }
156
+
157
+ function summaryLinesToBullets(text) {
158
+ return String(text || '')
159
+ .split('\n')
160
+ .map((l) => l.trim())
161
+ .filter(Boolean)
162
+ .map((l) => (l.startsWith('-') ? l : `- ${l}`));
163
+ }
164
+
165
+ function unrecognizedSummarySectionText(present) {
166
+ const chunks = [];
167
+ for (const [heading, body] of present) {
168
+ if (summarySectionIsRecognized(heading)) continue;
169
+ const lines = [heading];
170
+ for (const line of body || []) {
171
+ const trimmed = String(line).trim();
172
+ if (trimmed) lines.push(line);
173
+ }
174
+ chunks.push(lines.join('\n'));
175
+ }
176
+ return chunks.join('\n\n').trim();
177
+ }
178
+
179
+ // Deterministic schema repair for a non-empty but malformed/partial semantic
180
+ // summary. Preserve every section the provider DID supply (matched by anchor),
181
+ // and scaffold the missing required sections so downstream consumers always
182
+ // receive the full structured anchored shape. Content that lives outside any
183
+ // recognized section is routed into Critical Context so nothing is dropped.
184
+ // Lightly backfill Goal / Relevant Files from the transcript when empty.
185
+ export function repairSemanticSummary(summary, { head = [], tail = [] } = {}) {
186
+ const raw = String(summary || '').trim();
187
+ const present = parseSummarySections(raw);
188
+ // Capture any leading content before the first recognized `## ` heading so
189
+ // an entirely unstructured blob is preserved rather than silently dropped.
190
+ let preamble = '';
191
+ if (raw) {
192
+ const firstHeading = raw.search(/(^|\n)\s*##\s+/);
193
+ preamble = firstHeading === -1 ? raw : raw.slice(0, firstHeading);
194
+ preamble = preamble.trim();
195
+ }
196
+ const orphanText = unrecognizedSummarySectionText(present);
197
+ const extraContextParts = [];
198
+ if (preamble) extraContextParts.push(preamble);
199
+ if (orphanText) extraContextParts.push(orphanText);
200
+ const extraContext = extraContextParts.join('\n\n').trim();
201
+ const bulletize = (lines) => {
202
+ const cleaned = (Array.isArray(lines) ? lines : [])
203
+ .map((l) => String(l).trim())
204
+ .filter(Boolean);
205
+ return cleaned.length ? cleaned : null;
206
+ };
207
+ const findPresent = (anchor) => {
208
+ let found = null;
209
+ for (const [heading, body] of present) {
210
+ if (headingMatchesAnchor(heading, anchor)) {
211
+ if (!found) found = [];
212
+ found.push(...(body || []));
213
+ }
214
+ }
215
+ return found;
216
+ };
217
+ const goal = deriveCurrentRequest(tail) || deriveCurrentRequest(head);
218
+ const files = deriveRelevantFilesBullets(head);
219
+ const out = [];
220
+ for (const section of SUMMARY_SECTION_LAYOUT) {
221
+ if (out.length) out.push('');
222
+ out.push(section.heading);
223
+ const body = bulletize(findPresent(section.anchor));
224
+ if (section.sub) {
225
+ // Progress: keep provider sub-bodies when present, else scaffold.
226
+ if (body) {
227
+ out.push(...body);
228
+ } else {
229
+ for (const sub of section.sub) {
230
+ out.push(sub, '- (none)');
231
+ }
232
+ }
233
+ continue;
234
+ }
235
+ if (section.anchor === '## Critical Context') {
236
+ const ccLines = [];
237
+ if (body) ccLines.push(...body);
238
+ for (const line of summaryLinesToBullets(extraContext)) {
239
+ if (!ccLines.some((existing) => existing.trim() === line.trim())) ccLines.push(line);
240
+ }
241
+ if (ccLines.some((line) => line.trim() !== '- (none)')) {
242
+ const withoutPlaceholder = ccLines.filter((line) => line.trim() !== '- (none)');
243
+ out.push(...(withoutPlaceholder.length ? withoutPlaceholder : ccLines));
244
+ } else {
245
+ out.push(...(ccLines.length ? ccLines : ['- (none)']));
246
+ }
247
+ continue;
248
+ }
249
+ if (body) {
250
+ out.push(...body);
251
+ continue;
252
+ }
253
+ if (section.anchor === '## Goal') {
254
+ out.push(goal ? `- ${goal}` : '- (none)');
255
+ } else if (section.anchor === '## Relevant Files') {
256
+ out.push(...(files.length ? files : ['- (none)']));
257
+ } else {
258
+ out.push('- (none)');
259
+ }
260
+ }
261
+ return out.join('\n');
262
+ }
263
+
264
+ // A headings-only structured summary: every required `## ` (and Progress `### `)
265
+ // anchor present with `- (none)` bodies. This is the minimal schema-valid shape
266
+ // the fitter can fall back to when token pressure cannot hold real section
267
+ // bodies — it still passes summaryIsSchemaValid so the injected message is
268
+ // never partial.
269
+ export function minimalSchemaSummary() {
270
+ const out = [];
271
+ for (const section of SUMMARY_SECTION_LAYOUT) {
272
+ if (out.length) out.push('');
273
+ out.push(section.heading);
274
+ if (section.sub) {
275
+ for (const sub of section.sub) out.push(sub, '- (none)');
276
+ } else {
277
+ out.push('- (none)');
278
+ }
279
+ }
280
+ return out.join('\n');
281
+ }
282
+
283
+ // Section-aware truncation: keep EVERY `## ` heading and Progress `### `
284
+ // sub-heading intact, trimming only section bodies to `perSectionChars`. Unlike
285
+ // a raw text.slice(0, n) this never drops a trailing required section, so the
286
+ // result stays schema-valid (all anchors present) at any budget.
287
+ export function truncateSummaryBySections(summary, perSectionChars) {
288
+ const sections = parseSummarySections(summary);
289
+ const out = [];
290
+ for (const section of SUMMARY_SECTION_LAYOUT) {
291
+ if (out.length) out.push('');
292
+ out.push(section.heading);
293
+ let body = null;
294
+ for (const [heading, lines] of sections) {
295
+ if (headingMatchesAnchor(heading, section.anchor)) {
296
+ if (!body) body = [];
297
+ body.push(...(lines || []));
298
+ }
299
+ }
300
+ const bodyText = (Array.isArray(body) ? body : [])
301
+ .map((l) => String(l).trim())
302
+ .filter(Boolean)
303
+ .join('\n');
304
+ if (!bodyText) {
305
+ if (section.sub) for (const sub of section.sub) out.push(sub, '- (none)');
306
+ else out.push('- (none)');
307
+ continue;
308
+ }
309
+ const trimmed = perSectionChars > 0 ? truncateMiddle(bodyText, perSectionChars) : '';
310
+ if (trimmed) out.push(trimmed);
311
+ else if (section.sub) for (const sub of section.sub) out.push(sub, '- (none)');
312
+ else out.push('- (none)');
313
+ }
314
+ return out.join('\n');
315
+ }