mixdog 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/README.md +112 -38
  2. package/package.json +10 -3
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/r5-orchestrated-task.json +7 -0
  7. package/scripts/bench/review-tasks.json +20 -0
  8. package/scripts/bench/round-codex.json +114 -0
  9. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  10. package/scripts/bench/round-mixdog-lead.json +269 -0
  11. package/scripts/bench/round-mixdog.json +126 -0
  12. package/scripts/bench/round-r10-bigsample.json +679 -0
  13. package/scripts/bench/round-r11-codexalign.json +257 -0
  14. package/scripts/bench/round-r13-clientmeta.json +464 -0
  15. package/scripts/bench/round-r14-betafeatures.json +466 -0
  16. package/scripts/bench/round-r15-fulldefault.json +462 -0
  17. package/scripts/bench/round-r16-sessionid.json +466 -0
  18. package/scripts/bench/round-r17-wirebytes.json +456 -0
  19. package/scripts/bench/round-r18-prewarm.json +468 -0
  20. package/scripts/bench/round-r19-clean.json +472 -0
  21. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  22. package/scripts/bench/round-r21-delta-retry.json +473 -0
  23. package/scripts/bench/round-r22-full-probe.json +693 -0
  24. package/scripts/bench/round-r23-itemprobe.json +701 -0
  25. package/scripts/bench/round-r24-shapefix.json +677 -0
  26. package/scripts/bench/round-r25-serial.json +464 -0
  27. package/scripts/bench/round-r26-parallel3.json +671 -0
  28. package/scripts/bench/round-r27-parallel10.json +894 -0
  29. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  30. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  31. package/scripts/bench/round-r30-instid.json +253 -0
  32. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  33. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  34. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  35. package/scripts/bench/round-r34-orchestrated.json +120 -0
  36. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  37. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  38. package/scripts/bench/round-r4-codex.json +114 -0
  39. package/scripts/bench/round-r4-mixed.json +225 -0
  40. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  41. package/scripts/bench/round-r6-codex.json +114 -0
  42. package/scripts/bench/round-r6-solo.json +257 -0
  43. package/scripts/bench/round-r7-full.json +254 -0
  44. package/scripts/bench/round-r8-fulldefault.json +255 -0
  45. package/scripts/bench-run.mjs +251 -32
  46. package/scripts/freevar-smoke.mjs +95 -0
  47. package/scripts/internal-comms-bench.mjs +3 -4
  48. package/scripts/internal-comms-smoke.mjs +10 -9
  49. package/scripts/model-catalog-audit.mjs +209 -0
  50. package/scripts/model-list-sanitize-test.mjs +37 -0
  51. package/scripts/mouse-probe.mjs +45 -0
  52. package/scripts/output-style-bench.mjs +13 -6
  53. package/scripts/output-style-smoke.mjs +4 -4
  54. package/scripts/provider-toolcall-test.mjs +7 -3
  55. package/scripts/recall-bench.mjs +76 -13
  56. package/scripts/recall-quality-cases.json +12 -0
  57. package/scripts/recall-usecase-cases.json +18 -0
  58. package/scripts/session-bench.mjs +152 -6
  59. package/scripts/tool-smoke.mjs +25 -65
  60. package/scripts/tui-render-smoke.mjs +90 -0
  61. package/scripts/webhook-smoke.mjs +208 -0
  62. package/src/agents/debugger/AGENT.md +4 -1
  63. package/src/agents/heavy-worker/AGENT.md +9 -8
  64. package/src/agents/maintainer/AGENT.md +4 -0
  65. package/src/agents/reviewer/AGENT.md +2 -1
  66. package/src/agents/scheduler-task/AGENT.md +2 -3
  67. package/src/agents/webhook-handler/AGENT.md +2 -3
  68. package/src/agents/worker/AGENT.md +10 -7
  69. package/src/app.mjs +12 -1
  70. package/src/headless-role.mjs +7 -1
  71. package/src/lib/rules-builder.cjs +4 -0
  72. package/src/mixdog-session-runtime.mjs +647 -2056
  73. package/src/output-styles/default.md +30 -9
  74. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  75. package/src/output-styles/minimal.md +8 -6
  76. package/src/output-styles/simple.md +21 -7
  77. package/src/rules/agent/00-common.md +6 -3
  78. package/src/rules/agent/30-explorer.md +16 -5
  79. package/src/rules/lead/01-general.md +5 -5
  80. package/src/rules/lead/lead-brief.md +15 -0
  81. package/src/rules/lead/lead-tool.md +6 -15
  82. package/src/rules/shared/01-tool.md +17 -21
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  84. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  85. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  86. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  87. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  88. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  89. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  90. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  91. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  92. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  93. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  95. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  97. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  100. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  101. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  102. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  103. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
  104. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  109. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  110. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
  111. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  112. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
  113. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
  117. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  119. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  120. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  121. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  122. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  123. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  124. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  125. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  126. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  127. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  128. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  129. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
  132. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  133. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  134. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  135. package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
  136. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  137. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  138. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  139. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  140. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  143. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  144. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  145. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  146. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  147. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
  149. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  152. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  155. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  157. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  158. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  159. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  161. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  163. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  164. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  165. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  166. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  167. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  168. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  169. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  170. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  171. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  172. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  173. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  174. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  175. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  176. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  177. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  178. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  179. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  180. package/src/runtime/channels/backends/discord.mjs +27 -318
  181. package/src/runtime/channels/backends/telegram.mjs +8 -12
  182. package/src/runtime/channels/index.mjs +247 -701
  183. package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
  184. package/src/runtime/channels/lib/config.mjs +37 -149
  185. package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
  186. package/src/runtime/channels/lib/event-queue.mjs +78 -13
  187. package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
  188. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  189. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  190. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  191. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  192. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  193. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  194. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  195. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  196. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  197. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  198. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  199. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  200. package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
  201. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  202. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  203. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  204. package/src/runtime/channels/lib/webhook.mjs +43 -616
  205. package/src/runtime/channels/tool-defs.mjs +11 -130
  206. package/src/runtime/memory/index.mjs +210 -1948
  207. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  208. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  209. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  210. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  211. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  212. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  213. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  214. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  215. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  216. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  217. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  218. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  219. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  220. package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
  221. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  222. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  223. package/src/runtime/memory/lib/query-handlers.mjs +802 -0
  224. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  225. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  226. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  227. package/src/runtime/memory/tool-defs.mjs +5 -13
  228. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  229. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  230. package/src/runtime/search/lib/web-tools.mjs +24 -602
  231. package/src/runtime/shared/atomic-file.mjs +26 -1
  232. package/src/runtime/shared/config.mjs +14 -4
  233. package/src/runtime/shared/launcher-control.mjs +2 -2
  234. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  235. package/src/runtime/shared/schedules-store.mjs +13 -3
  236. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  237. package/src/runtime/shared/tool-primitives.mjs +308 -0
  238. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  239. package/src/runtime/shared/tool-surface.mjs +80 -898
  240. package/src/runtime/shared/transcript-writer.mjs +23 -0
  241. package/src/runtime/shared/update-checker.mjs +7 -4
  242. package/src/session-runtime/config-helpers.mjs +119 -2
  243. package/src/session-runtime/config-lifecycle.mjs +232 -0
  244. package/src/session-runtime/cwd-plugins.mjs +226 -0
  245. package/src/session-runtime/mcp-glue.mjs +177 -0
  246. package/src/session-runtime/model-recency.mjs +111 -0
  247. package/src/session-runtime/native-search.mjs +247 -0
  248. package/src/session-runtime/output-styles.mjs +11 -9
  249. package/src/session-runtime/prewarm.mjs +142 -0
  250. package/src/session-runtime/provider-models.mjs +278 -0
  251. package/src/session-runtime/provider-usage.mjs +120 -0
  252. package/src/session-runtime/quick-model-rows.mjs +205 -0
  253. package/src/session-runtime/quick-search-models.mjs +47 -0
  254. package/src/session-runtime/session-hooks.mjs +93 -0
  255. package/src/session-runtime/settings-api.mjs +352 -0
  256. package/src/session-runtime/tool-catalog.mjs +29 -29
  257. package/src/session-runtime/tool-defs.mjs +84 -0
  258. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  259. package/src/session-runtime/workflow.mjs +1 -1
  260. package/src/standalone/agent-tool/helpers.mjs +237 -0
  261. package/src/standalone/agent-tool/notify.mjs +107 -0
  262. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  263. package/src/standalone/agent-tool/render.mjs +152 -0
  264. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  265. package/src/standalone/agent-tool.mjs +138 -669
  266. package/src/standalone/channel-admin.mjs +102 -90
  267. package/src/standalone/channel-worker.mjs +4 -7
  268. package/src/standalone/explore-tool.mjs +64 -14
  269. package/src/standalone/hook-bus/config.mjs +207 -0
  270. package/src/standalone/hook-bus/constants.mjs +90 -0
  271. package/src/standalone/hook-bus/handlers.mjs +481 -0
  272. package/src/standalone/hook-bus/payload.mjs +31 -0
  273. package/src/standalone/hook-bus/rules.mjs +77 -0
  274. package/src/standalone/hook-bus.mjs +77 -870
  275. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  276. package/src/standalone/opencode-go-login.mjs +5 -1
  277. package/src/standalone/provider-admin.mjs +1 -16
  278. package/src/standalone/usage-dashboard.mjs +3 -1
  279. package/src/tui/App.jsx +1059 -8110
  280. package/src/tui/app/app-format.mjs +213 -0
  281. package/src/tui/app/channel-pickers.mjs +508 -0
  282. package/src/tui/app/clipboard.mjs +67 -0
  283. package/src/tui/app/core-memory-picker.mjs +210 -0
  284. package/src/tui/app/extension-pickers.mjs +506 -0
  285. package/src/tui/app/input-parsers.mjs +193 -0
  286. package/src/tui/app/maintenance-pickers.mjs +356 -0
  287. package/src/tui/app/model-options.mjs +334 -0
  288. package/src/tui/app/model-picker.mjs +365 -0
  289. package/src/tui/app/onboarding-steps.mjs +400 -0
  290. package/src/tui/app/project-picker.mjs +247 -0
  291. package/src/tui/app/provider-setup-picker.mjs +580 -0
  292. package/src/tui/app/resume-picker.mjs +55 -0
  293. package/src/tui/app/route-pickers.mjs +419 -0
  294. package/src/tui/app/settings-picker.mjs +489 -0
  295. package/src/tui/app/slash-commands.mjs +101 -0
  296. package/src/tui/app/slash-dispatch.mjs +427 -0
  297. package/src/tui/app/text-layout.mjs +46 -0
  298. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  299. package/src/tui/app/transcript-window.mjs +677 -0
  300. package/src/tui/app/use-mouse-input.mjs +460 -0
  301. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  302. package/src/tui/app/use-transcript-scroll.mjs +512 -0
  303. package/src/tui/app/use-transcript-window.mjs +607 -0
  304. package/src/tui/components/ConfirmBar.jsx +10 -7
  305. package/src/tui/components/Picker.jsx +64 -15
  306. package/src/tui/components/PromptInput.jsx +33 -102
  307. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  308. package/src/tui/components/StatusLine.jsx +69 -15
  309. package/src/tui/components/TextEntryPanel.jsx +11 -0
  310. package/src/tui/components/ToolExecution.jsx +52 -594
  311. package/src/tui/components/TranscriptItem.jsx +105 -0
  312. package/src/tui/components/UsagePanel.jsx +18 -4
  313. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  314. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  315. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  316. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  317. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  318. package/src/tui/display-width.mjs +20 -3
  319. package/src/tui/dist/index.mjs +13553 -12384
  320. package/src/tui/engine/agent-job-feed.mjs +133 -0
  321. package/src/tui/engine/notification-plan.mjs +76 -0
  322. package/src/tui/engine/render-timing.mjs +17 -0
  323. package/src/tui/engine/tool-approval.mjs +94 -0
  324. package/src/tui/engine/tool-card-results.mjs +234 -0
  325. package/src/tui/engine/tool-result-status.mjs +135 -0
  326. package/src/tui/engine.mjs +170 -574
  327. package/src/tui/figures.mjs +5 -0
  328. package/src/tui/index.jsx +65 -1
  329. package/src/tui/input-editing.mjs +2 -2
  330. package/src/tui/markdown/format-token.mjs +4 -1
  331. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  332. package/src/tui/theme.mjs +6 -0
  333. package/src/ui/statusline-agents.mjs +213 -0
  334. package/src/ui/statusline-format.mjs +146 -0
  335. package/src/ui/statusline-segments.mjs +148 -0
  336. package/src/ui/statusline.mjs +77 -501
  337. package/src/ui/tool-card.mjs +0 -1
  338. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  339. package/src/workflows/default/WORKFLOW.md +16 -18
  340. package/src/workflows/sequential/WORKFLOW.md +16 -18
  341. package/vendor/ink/build/display-width.js +19 -3
  342. package/vendor/ink/build/ink.js +112 -7
  343. package/vendor/ink/build/log-update.js +17 -3
  344. package/vendor/ink/build/wrap-text.js +125 -0
  345. package/scripts/_test-folder-dialog.mjs +0 -30
  346. package/scripts/fix-brief-fn.mjs +0 -35
  347. package/scripts/fix-format-tool-surface.mjs +0 -24
  348. package/scripts/fix-tool-exec-visible.mjs +0 -42
  349. package/scripts/patch-agent-brief.mjs +0 -48
  350. package/scripts/patch-app.mjs +0 -21
  351. package/scripts/patch-app2.mjs +0 -18
  352. package/scripts/patch-dist-brief.mjs +0 -96
  353. package/scripts/patch-tool-exec.mjs +0 -70
  354. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  355. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  356. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  357. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  358. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  359. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  360. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  361. package/src/runtime/channels/lib/holidays.mjs +0 -138
  362. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  363. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  364. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  365. package/src/standalone/agent-task-status.test.mjs +0 -76
  366. package/src/tui/components/tool-output-format.test.mjs +0 -399
  367. package/src/tui/display-width.test.mjs +0 -35
  368. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  369. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  370. package/src/tui/input-editing.selection.test.mjs +0 -75
  371. package/src/tui/markdown/format-token.test.mjs +0 -354
  372. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  373. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  374. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  375. package/src/tui/paste-fix.test.mjs +0 -119
  376. package/src/tui/prompt-history-store.test.mjs +0 -52
  377. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  378. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  379. package/src/ui/markdown.test.mjs +0 -70
  380. package/src/ui/statusline-context-label.test.mjs +0 -15
  381. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  382. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/freevar-smoke.mjs — undeclared-identifier gate for runtime source.
4
+ *
5
+ * Same engine as tui-render-smoke.mjs (acorn + eslint-scope) but pointed at
6
+ * the plain-ESM runtime sources instead of the TUI bundle. Any reference that
7
+ * resolves to no binding and is not a known Node global is a guaranteed
8
+ * ReferenceError on that code path — exactly the class of bug that section
9
+ * extractions leave behind on cold paths (missed import, orphaned helper).
10
+ *
11
+ * Run: node scripts/freevar-smoke.mjs (or `npm run smoke:freevars`)
12
+ * Exit: 0 = clean, 1 = undeclared identifiers found / parse failure.
13
+ */
14
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
15
+ import { dirname, join, relative } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { parse } from 'acorn';
18
+ import { analyze } from 'eslint-scope';
19
+
20
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
21
+ // Plain .mjs source roots. src/tui is covered by smoke:tui on the BUILT
22
+ // bundle (App.jsx is JSX — acorn can't parse it raw); vendor is third-party.
23
+ const SCAN_ROOTS = ['src/runtime', 'src/standalone', 'src/shared', 'scripts'].map(p => join(ROOT, p));
24
+ const SKIP_DIRS = new Set(['node_modules', 'dist', '.git']);
25
+
26
+ const KNOWN_GLOBALS = new Set([
27
+ 'Array', 'ArrayBuffer', 'Atomics', 'BigInt', 'BigInt64Array', 'BigUint64Array', 'Boolean',
28
+ 'DataView', 'Date', 'Error', 'EvalError', 'FinalizationRegistry', 'Float32Array',
29
+ 'Float64Array', 'Function', 'Infinity', 'Int8Array', 'Int16Array', 'Int32Array',
30
+ 'Intl', 'JSON', 'Map', 'Math', 'NaN', 'Number', 'Object', 'Promise', 'Proxy',
31
+ 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', 'SharedArrayBuffer',
32
+ 'String', 'Symbol', 'SyntaxError', 'TypeError', 'URIError', 'Uint8Array',
33
+ 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', 'WeakRef', 'WeakSet',
34
+ 'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'eval',
35
+ 'globalThis', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'undefined',
36
+ 'AbortController', 'AbortSignal', 'Blob', 'Buffer', 'ByteLengthQueuingStrategy',
37
+ 'CompressionStream', 'CountQueuingStrategy', 'Crypto', 'CryptoKey', 'CustomEvent',
38
+ 'DecompressionStream', 'Event', 'EventTarget', 'File', 'FormData', 'Headers',
39
+ 'MessageChannel', 'MessageEvent', 'MessagePort', 'Navigator', 'PerformanceEntry',
40
+ 'PerformanceObserver', 'ReadableStream', 'ReadableStreamDefaultReader', 'Request',
41
+ 'Response', 'SubtleCrypto', 'TextDecoder', 'TextDecoderStream', 'TextEncoder',
42
+ 'TextEncoderStream', 'TransformStream', 'URL', 'URLSearchParams', 'WebAssembly',
43
+ 'WebSocket', 'WritableStream', 'atob', 'btoa', 'clearImmediate', 'clearInterval',
44
+ 'clearTimeout', 'console', 'crypto', 'fetch', 'navigator', 'performance',
45
+ 'process', 'queueMicrotask', 'require', 'setImmediate', 'setInterval',
46
+ 'setTimeout', 'structuredClone', 'reportError',
47
+ // Browser globals: legal inside puppeteer page.evaluate() callbacks, which
48
+ // execute in the BROWSER context (web-tools.mjs scrapers).
49
+ 'document', 'window',
50
+ ]);
51
+
52
+ function* walk(dir) {
53
+ for (const name of readdirSync(dir)) {
54
+ if (SKIP_DIRS.has(name)) continue;
55
+ const p = join(dir, name);
56
+ const st = statSync(p);
57
+ if (st.isDirectory()) yield* walk(p);
58
+ else if (name.endsWith('.mjs')) yield p;
59
+ }
60
+ }
61
+
62
+ let failures = 0;
63
+ let scanned = 0;
64
+ for (const root of SCAN_ROOTS) {
65
+ let files;
66
+ try { files = [...walk(root)]; } catch { continue; }
67
+ for (const file of files) {
68
+ scanned++;
69
+ const source = readFileSync(file, 'utf8');
70
+ let ast;
71
+ try {
72
+ ast = parse(source, { ecmaVersion: 'latest', sourceType: 'module', locations: true, ranges: true });
73
+ } catch (err) {
74
+ failures++;
75
+ console.error(`freevar-smoke: PARSE FAIL ${relative(ROOT, file)}: ${err.message}`);
76
+ continue;
77
+ }
78
+ const scopeManager = analyze(ast, { ecmaVersion: 2022, sourceType: 'module' });
79
+ const seen = new Set();
80
+ for (const ref of scopeManager.globalScope.through) {
81
+ const { name } = ref.identifier;
82
+ if (KNOWN_GLOBALS.has(name) || seen.has(name)) continue;
83
+ seen.add(name);
84
+ failures++;
85
+ const loc = ref.identifier.loc?.start;
86
+ console.error(`freevar-smoke: FAIL ${relative(ROOT, file)}:${loc ? `${loc.line}:${loc.column}` : '?'} — undeclared identifier \`${name}\``);
87
+ }
88
+ }
89
+ }
90
+
91
+ if (failures > 0) {
92
+ console.error(`freevar-smoke: ${failures} problem(s) across ${scanned} files — these are ReferenceErrors waiting on their code path.`);
93
+ process.exit(1);
94
+ }
95
+ console.log(`freevar-smoke: ok — ${scanned} files, no undeclared identifiers`);
@@ -36,6 +36,7 @@ const RULE_FILES = [
36
36
  'agents/debugger/AGENT.md',
37
37
  'workflows/default/WORKFLOW.md',
38
38
  'rules/lead/lead-tool.md',
39
+ 'rules/lead/lead-brief.md',
39
40
  ];
40
41
 
41
42
  const DEFAULT_WORKER_PROMPT =
@@ -211,6 +212,8 @@ function runHeadlessWorker({ pluginRoot, dataDir, taskCwd, prompt, provider, mod
211
212
  ` message: ${JSON.stringify(prompt)},`,
212
213
  ` provider: ${JSON.stringify(provider || null)},`,
213
214
  ` model: ${JSON.stringify(model || null)},`,
215
+ ` effort: ${JSON.stringify(effort || null)},`,
216
+ ` fast: ${JSON.stringify(fast === true)},`,
214
217
  ` cwd: ${JSON.stringify(taskCwd)},`,
215
218
  ` write: (t) => out.push(t),`,
216
219
  ` writeErr: (t) => process.stderr.write(t),`,
@@ -229,8 +232,6 @@ function runHeadlessWorker({ pluginRoot, dataDir, taskCwd, prompt, provider, mod
229
232
  ...process.env,
230
233
  MIXDOG_ROOT: pluginRoot,
231
234
  MIXDOG_DATA_DIR: dataDir,
232
- ...(effort ? { MIXDOG_AGENT_EFFORT: effort } : {}),
233
- ...(fast ? { MIXDOG_AGENT_FAST: '1' } : {}),
234
235
  },
235
236
  });
236
237
  ok = true;
@@ -477,8 +478,6 @@ function runLiveLeadDelegation({ pluginRoot, dataDir, taskCwd, prompt, provider,
477
478
  ...process.env,
478
479
  MIXDOG_ROOT: pluginRoot,
479
480
  MIXDOG_DATA_DIR: dataDir,
480
- ...(effort ? { MIXDOG_AGENT_EFFORT: effort } : {}),
481
- ...(fast ? { MIXDOG_AGENT_FAST: '1' } : {}),
482
481
  },
483
482
  });
484
483
  const j = raw.lastIndexOf('{');
@@ -24,17 +24,18 @@ function flat(text) {
24
24
  return String(text || '').replace(/\s+/g, ' ');
25
25
  }
26
26
 
27
- // --- Lead brief contract: canonical in lead-tool.md, referenced from WORKFLOW -
27
+ // --- Lead brief contract: canonical in lead-brief.md, referenced from WORKFLOW -
28
28
  const workflow = readSrc('workflows', 'default', 'WORKFLOW.md');
29
- const leadTool = readSrc('rules', 'lead', 'lead-tool.md');
29
+ const leadBrief = readSrc('rules', 'lead', 'lead-brief.md');
30
30
  const BRIEF_FIELDS = ['Goal:', 'Anchors:', 'Allow/Forbid:', 'Deliver:', 'Verify:'];
31
- // Canonical brief contract lives in lead-tool.md (Lead tool-use rules).
32
- assert(/minimum characters, maximum information/i.test(flat(leadTool)), 'lead-tool.md: brief must state min-char/max-info principle');
33
- for (const field of BRIEF_FIELDS) assert(leadTool.includes(field), `lead-tool.md: brief missing labeled field ${field}`);
34
- assert(leadTool.includes('Stop:'), 'lead-tool.md: brief must add Stop: for heavy-worker bound');
35
- assert(/role-known|already (?:owns|knows)|wasted cost|wasted/i.test(flat(leadTool)), 'lead-tool.md: brief must ban restating known rules/background as cost');
36
- // WORKFLOW.md must not duplicate the field list; it defers to the lead-tool contract.
37
- assert(/lead-tool brief contract/i.test(flat(workflow)), 'WORKFLOW.md: must defer to the lead-tool brief contract');
31
+ // Canonical brief contract lives in lead-brief.md (Lead brief contract).
32
+ assert(/minimum characters, maximum information/i.test(flat(leadBrief)), 'lead-brief.md: brief must state min-char/max-info principle');
33
+ for (const field of BRIEF_FIELDS) assert(leadBrief.includes(field), `lead-brief.md: brief missing labeled field ${field}`);
34
+ assert(leadBrief.includes('Stop:'), 'lead-brief.md: brief must add Stop: for heavy-worker bound');
35
+ assert(/role-known|already (?:owns|knows)|wasted cost|wasted/i.test(flat(leadBrief)), 'lead-brief.md: brief must ban restating known rules/background as cost');
36
+ assert(/authoritative/i.test(flat(leadBrief)), 'lead-brief.md: brief must state spec-file precedence over summary');
37
+ // WORKFLOW.md must not duplicate the field list; it defers to the lead brief contract.
38
+ assert(/lead brief contract/i.test(flat(workflow)), 'WORKFLOW.md: must defer to the lead brief contract');
38
39
  assert(!BRIEF_FIELDS.every((field) => workflow.includes(field)), 'WORKFLOW.md: must not duplicate the full brief field list');
39
40
 
40
41
  // --- Agent handoff contract (00-common.md) ---------------------------------
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig } from '../src/runtime/agent/orchestrator/config.mjs';
3
+ import { initProviders, getAllProviders } from '../src/runtime/agent/orchestrator/providers/registry.mjs';
4
+ import {
5
+ loadModelsDevCatalog,
6
+ warmModelMetadataCatalogs,
7
+ } from '../src/runtime/agent/orchestrator/providers/model-catalog.mjs';
8
+ import { QUICK_SEARCH_MODELS } from '../src/session-runtime/quick-search-models.mjs';
9
+
10
+ const args = new Set(process.argv.slice(2));
11
+ const json = args.has('--json');
12
+ const allRows = args.has('--all');
13
+ const refresh = !args.has('--no-refresh');
14
+
15
+ const PROVIDER_ALIAS = Object.freeze({
16
+ 'openai-oauth': 'openai',
17
+ openai: 'openai',
18
+ 'grok-oauth': 'xai',
19
+ xai: 'xai',
20
+ gemini: 'google',
21
+ google: 'google',
22
+ 'anthropic-oauth': 'anthropic',
23
+ anthropic: 'anthropic',
24
+ });
25
+
26
+ const LIT_PREFIXES = Object.freeze({
27
+ openai: ['openai/'],
28
+ xai: ['xai/'],
29
+ google: ['gemini/', 'google/'],
30
+ anthropic: ['anthropic/'],
31
+ deepseek: ['deepseek/'],
32
+ });
33
+
34
+ function n(value) {
35
+ const num = Number(value);
36
+ return Number.isFinite(num) && num > 0 ? Math.floor(num) : null;
37
+ }
38
+
39
+ function contextLabel(value) {
40
+ const v = n(value);
41
+ if (!v) return '-';
42
+ if (v >= 1_000_000) {
43
+ const m = v / 1_000_000;
44
+ const label = Number.isInteger(m)
45
+ ? m.toFixed(0)
46
+ : m.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
47
+ return `${label}M`;
48
+ }
49
+ return `${Math.round(v / 1000)}k`;
50
+ }
51
+
52
+ function isInteresting(id) {
53
+ const s = String(id || '').toLowerCase();
54
+ return /^(gpt-5|gpt-4\.1|claude-|gemini-|grok-|deepseek-v4|glm-|qwen|minimax|kimi)/.test(s);
55
+ }
56
+
57
+ function litellmRow(litellm, provider, id) {
58
+ const p = PROVIDER_ALIAS[provider] || provider;
59
+ const keys = [id, ...(LIT_PREFIXES[p] || []).map(prefix => prefix + id)];
60
+ if (p === 'anthropic') keys.push(`anthropic.${id}-v1:0`, `bedrock/anthropic.${id}-v1:0`);
61
+ for (const key of keys) {
62
+ if (litellm?.[key]) return { key, row: litellm[key] };
63
+ }
64
+ return null;
65
+ }
66
+
67
+ function externalMeta(modelsDev, litellm, provider, id) {
68
+ const p = PROVIDER_ALIAS[provider] || provider;
69
+ const md = modelsDev?.[p]?.models?.[id] || null;
70
+ const lit = litellmRow(litellm, provider, id);
71
+ return {
72
+ modelsDevContext: n(md?.limit?.context),
73
+ modelsDevOutput: n(md?.limit?.output),
74
+ litellmContext: n(lit?.row?.max_input_tokens ?? lit?.row?.max_tokens),
75
+ litellmOutput: n(lit?.row?.max_output_tokens),
76
+ externalName: md?.name || null,
77
+ litellmKey: lit?.key || null,
78
+ };
79
+ }
80
+
81
+ function classifyOneMillion(value) {
82
+ const v = n(value);
83
+ if (!v) return 'unknown';
84
+ if (v === 1_000_000) return 'exact-1M';
85
+ if (v > 900_000 && v < 1_100_000) return 'near-1M-not-exact';
86
+ return 'not-1M';
87
+ }
88
+
89
+ async function listProviderModels(name, provider) {
90
+ let models = null;
91
+ let refreshed = false;
92
+ let error = null;
93
+ try {
94
+ if (refresh && typeof provider?._refreshModelCache === 'function') {
95
+ models = await provider._refreshModelCache();
96
+ refreshed = true;
97
+ }
98
+ if (!Array.isArray(models) && typeof provider?.listModels === 'function') {
99
+ models = await provider.listModels();
100
+ }
101
+ } catch (err) {
102
+ error = err?.message || String(err);
103
+ }
104
+ return { provider: name, refreshed, error, models: Array.isArray(models) ? models : [] };
105
+ }
106
+
107
+ const cfg = loadConfig();
108
+ try {
109
+ await initProviders(cfg.providers || {});
110
+ } catch (err) {
111
+ if (!json) console.error(`[model-audit] init failed: ${err?.message || err}`);
112
+ }
113
+
114
+ const [modelsDev, litellm] = await Promise.all([
115
+ loadModelsDevCatalog().catch(() => ({})),
116
+ warmModelMetadataCatalogs().catch(() => ({})),
117
+ ]);
118
+
119
+ const providerResults = [];
120
+ for (const [name, provider] of getAllProviders()) {
121
+ if (typeof provider?.listModels !== 'function') continue;
122
+ const listed = await listProviderModels(name, provider);
123
+ const rows = listed.models
124
+ .filter(row => row?.id && (allRows || isInteresting(row.id)))
125
+ .map(row => {
126
+ const liveContext = n(row.contextWindow ?? row.maxContextWindow);
127
+ const liveOutput = n(row.outputTokens);
128
+ const external = externalMeta(modelsDev, litellm, name, row.id);
129
+ const externalContext = external.modelsDevContext ?? external.litellmContext;
130
+ const externalOutput = external.modelsDevOutput ?? external.litellmOutput;
131
+ return {
132
+ provider: name,
133
+ id: row.id,
134
+ display: row.display || row.name || null,
135
+ liveContext,
136
+ liveContextLabel: contextLabel(liveContext),
137
+ liveOutput,
138
+ externalContext,
139
+ externalContextLabel: contextLabel(externalContext),
140
+ externalOutput,
141
+ contextVerdict: classifyOneMillion(liveContext),
142
+ contextMismatch: liveContext != null && externalContext != null && liveContext !== externalContext,
143
+ outputMismatch: liveOutput != null && externalOutput != null && liveOutput !== externalOutput,
144
+ source: listed.refreshed ? 'live-refresh' : 'live-list',
145
+ ...external,
146
+ };
147
+ });
148
+ providerResults.push({
149
+ provider: name,
150
+ refreshed: listed.refreshed,
151
+ error: listed.error,
152
+ count: listed.models.length,
153
+ rows,
154
+ });
155
+ }
156
+
157
+ const quickRows = [];
158
+ for (const [provider, models] of Object.entries(QUICK_SEARCH_MODELS)) {
159
+ for (const model of models) {
160
+ if (model?.contextWindow == null) continue;
161
+ const external = externalMeta(modelsDev, litellm, provider, model.id);
162
+ const externalContext = external.modelsDevContext ?? external.litellmContext;
163
+ quickRows.push({
164
+ provider,
165
+ id: model.id,
166
+ staticContext: n(model.contextWindow),
167
+ externalContext,
168
+ mismatch: n(model.contextWindow) != null && externalContext != null && n(model.contextWindow) !== externalContext,
169
+ });
170
+ }
171
+ }
172
+
173
+ const mismatches = providerResults.flatMap(p => p.rows.filter(r => r.contextMismatch || r.outputMismatch));
174
+ const exactOneM = providerResults.flatMap(p => p.rows.filter(r => r.liveContext === 1_000_000));
175
+ const nearOneM = providerResults.flatMap(p => p.rows.filter(r => r.contextVerdict === 'near-1M-not-exact'));
176
+
177
+ const report = {
178
+ generatedAt: new Date().toISOString(),
179
+ enabledProviders: Object.entries(cfg.providers || {}).filter(([, v]) => v?.enabled).map(([k]) => k),
180
+ providerResults,
181
+ mismatches,
182
+ exactOneM,
183
+ nearOneM,
184
+ quickStaticContextRows: quickRows,
185
+ };
186
+
187
+ if (json) {
188
+ console.log(JSON.stringify(report, null, 2));
189
+ } else {
190
+ console.log(`model catalog audit @ ${report.generatedAt}`);
191
+ console.log(`enabled providers: ${report.enabledProviders.join(', ') || '-'}`);
192
+ for (const p of providerResults) {
193
+ console.log(`\n[${p.provider}] count=${p.count} refreshed=${p.refreshed}${p.error ? ` error=${p.error}` : ''}`);
194
+ for (const r of p.rows) {
195
+ const flags = [
196
+ r.contextMismatch ? `context ${r.liveContextLabel} != external ${r.externalContextLabel}` : '',
197
+ r.outputMismatch ? `output ${r.liveOutput} != external ${r.externalOutput}` : '',
198
+ ].filter(Boolean).join('; ');
199
+ console.log(` ${r.id}: ${r.liveContextLabel} ctx, out=${r.liveOutput ?? '-'}${flags ? ` [${flags}]` : ''}`);
200
+ }
201
+ }
202
+ console.log(`\nsummary: mismatches=${mismatches.length}, exact1M=${exactOneM.length}, near1MNotExact=${nearOneM.length}, staticContextRows=${quickRows.length}`);
203
+ if (mismatches.length) {
204
+ console.log('mismatches:');
205
+ for (const r of mismatches.slice(0, 50)) {
206
+ console.log(` ${r.provider}/${r.id}: live=${r.liveContextLabel}/${r.liveOutput ?? '-'} external=${r.externalContextLabel}/${r.externalOutput ?? '-'}`);
207
+ }
208
+ }
209
+ }
@@ -0,0 +1,37 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { sanitizeModelList } from '../src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs';
4
+
5
+ test('Anthropic staleness keeps Claude subfamilies separate', () => {
6
+ const prev = process.env.MIXDOG_MODEL_STALE_MONTHS;
7
+ process.env.MIXDOG_MODEL_STALE_MONTHS = '0';
8
+ try {
9
+ const models = [
10
+ { id: 'claude-opus-4-8', mode: 'chat' },
11
+ { id: 'claude-sonnet-4-6', mode: 'chat' },
12
+ { id: 'claude-haiku-4-5-20251001', mode: 'chat' },
13
+ ];
14
+ const catalogRow = (release_date) => ({
15
+ family: 'claude',
16
+ release_date,
17
+ tool_call: true,
18
+ modalities: { output: ['text'] },
19
+ });
20
+ const out = sanitizeModelList(models, {
21
+ provider: 'anthropic',
22
+ _testCatalog: {
23
+ anthropic: {
24
+ models: {
25
+ 'claude-opus-4-8': catalogRow('2025-12-01'),
26
+ 'claude-sonnet-4-6': catalogRow('2025-11-01'),
27
+ 'claude-haiku-4-5-20251001': catalogRow('2025-10-15'),
28
+ },
29
+ },
30
+ },
31
+ });
32
+ assert.deepEqual(out.map((m) => m.id), models.map((m) => m.id));
33
+ } finally {
34
+ if (prev == null) delete process.env.MIXDOG_MODEL_STALE_MONTHS;
35
+ else process.env.MIXDOG_MODEL_STALE_MONTHS = prev;
36
+ }
37
+ });
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/mouse-probe.mjs — raw SGR mouse event probe.
4
+ *
5
+ * Enables the same SGR mouse tracking the TUI uses and prints every mouse
6
+ * escape sequence it receives, decoded (button id + modifier bits + coords).
7
+ * Use it to see EXACTLY what the terminal sends for ctrl+wheel — if the ctrl
8
+ * bit (16) never appears, the terminal is not forwarding the modifier and the
9
+ * app-side zoom passthrough can never trigger.
10
+ *
11
+ * Run: node scripts/mouse-probe.mjs (q or Ctrl+C to quit)
12
+ */
13
+ const ON = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
14
+ const OFF = '\x1b[?1006l\x1b[?1002l\x1b[?1000l';
15
+ const MOUSE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
16
+
17
+ process.stdout.write(ON);
18
+ process.stdout.write('mouse-probe: wheel / ctrl+wheel / click here. q to quit.\n');
19
+ process.stdin.setRawMode(true);
20
+ process.stdin.resume();
21
+
22
+ const cleanup = () => {
23
+ process.stdout.write(OFF);
24
+ process.stdin.setRawMode(false);
25
+ process.exit(0);
26
+ };
27
+ process.on('SIGINT', cleanup);
28
+
29
+ process.stdin.on('data', (buf) => {
30
+ const s = buf.toString('utf8');
31
+ if (s === 'q' || s === '\x03') return cleanup();
32
+ let m; MOUSE.lastIndex = 0;
33
+ let sawMouse = false;
34
+ while ((m = MOUSE.exec(s)) !== null) {
35
+ sawMouse = true;
36
+ const b = Number(m[1]);
37
+ const mods = [b & 4 ? 'shift' : '', b & 8 ? 'alt' : '', b & 16 ? 'ctrl' : ''].filter(Boolean).join('+') || 'none';
38
+ const base = b & ~(4 | 8 | 16);
39
+ const kind = base === 64 ? 'wheel-up' : base === 65 ? 'wheel-down' : base === 0 ? 'left' : base === 2 ? 'right' : (b & 32) ? 'motion' : `btn${base}`;
40
+ process.stdout.write(`button=${b} base=${base} kind=${kind} mods=${mods} x=${m[2]} y=${m[3]} ${m[4] === 'M' ? 'press' : 'release'}\n`);
41
+ }
42
+ if (!sawMouse && s.startsWith('\x1b')) {
43
+ process.stdout.write(`non-mouse escape: ${JSON.stringify(s)}\n`);
44
+ }
45
+ });
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // Lead output-style verbosity bench (default >= simple >= minimal >= oneline).
2
+ // Lead output-style verbosity bench (default >= simple >= minimal >= extreme-minimal).
3
3
  import { createRequire } from 'node:module';
4
4
  import { execFileSync } from 'node:child_process';
5
5
  import {
@@ -19,7 +19,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
19
19
  const __dir = dirname(fileURLToPath(import.meta.url));
20
20
  const REPO_ROOT = resolve(__dir, '..');
21
21
  const PLUGIN_ROOT = join(REPO_ROOT, 'src');
22
- const STYLES = ['default', 'simple', 'minimal', 'oneline'];
22
+ const STYLES = ['default', 'simple', 'minimal', 'extreme-minimal'];
23
23
  const DEFAULT_PROMPT =
24
24
  'Reply in plain English only. What is 2+2? Give a short summary suitable for a status report (no tools, no file reads).';
25
25
  const MODEL_ALIASES = {
@@ -85,10 +85,10 @@ function runInjectionScaffold() {
85
85
  ? JSON.parse(readFileSync(templatePath, 'utf8'))
86
86
  : { outputStyle: 'default' };
87
87
  const markers = {
88
- default: 'most detailed of the three',
88
+ default: 'most detailed style',
89
89
  simple: 'Practical concise',
90
90
  minimal: 'a very short summary',
91
- oneline: 'exactly one sentence',
91
+ 'extreme-minimal': 'exactly one sentence',
92
92
  };
93
93
  const snippets = {};
94
94
  try {
@@ -179,11 +179,18 @@ function runLiveLeadTurn({ dataDir, prompt, provider, model, cwd, effort, fast }
179
179
  `let result;`,
180
180
  `try { result = await askSession(session.id, ${JSON.stringify(prompt)}, null, null, ${JSON.stringify(cwd)}); }`,
181
181
  `finally { try { closeSession(session.id, 'output-style-bench'); } catch {} }`,
182
- `process.stdout.write(JSON.stringify({ text: String(result?.text || result?.content || '').trim(), sessionId: session.id }));`,
182
+ // Pooled provider sockets (e.g. openai-oauth WS, 20-min idle TTL) keep the
183
+ // child alive after the turn; flush JSON then force-exit so execFileSync
184
+ // returns as soon as the turn completes.
185
+ `const payload = JSON.stringify({ text: String(result?.text || result?.content || '').trim(), sessionId: session.id });`,
186
+ `await new Promise((r) => process.stdout.write(payload, r));`,
187
+ `process.exit(0);`,
183
188
  ].filter(Boolean).join('\n');
184
189
  const raw = execFileSync('node', ['--input-type=module', '-e', driver], {
185
190
  encoding: 'utf8',
186
191
  maxBuffer: 64 * 1024 * 1024,
192
+ timeout: Number(process.env.OUTPUT_STYLE_BENCH_TURN_TIMEOUT_MS || 10 * 60_000),
193
+ killSignal: 'SIGKILL',
187
194
  env: { ...process.env, MIXDOG_ROOT: PLUGIN_ROOT, MIXDOG_DATA_DIR: dataDir },
188
195
  });
189
196
  const jsonStart = raw.lastIndexOf('{');
@@ -209,7 +216,7 @@ function evaluateVerbosityOrdering(results) {
209
216
  return { orderingOk, verdict, counts: styleCharCountsFromResults(results) };
210
217
  }
211
218
  function printUsage() {
212
- process.stdout.write(`output-style-bench — Lead verbosity ladder (default >= simple >= minimal >= oneline).
219
+ process.stdout.write(`output-style-bench — Lead verbosity ladder (default >= simple >= minimal >= extreme-minimal).
213
220
 
214
221
  Output style is Lead-only (buildLeadMetaContent when owner is not agent).
215
222
  runHeadlessRole worker paths (owner=agent) do NOT inject outputStyle.
@@ -49,8 +49,8 @@ const defaultStyle = readFileSync(join(root, 'src', 'output-styles', 'default.md
49
49
  for (const required of [
50
50
  'name: default',
51
51
  'Concise engineering summaries',
52
- 'Mixdog default — the most detailed of the three styles',
53
- 'match length to the work',
52
+ 'Mixdog default — the most detailed style',
53
+ 'State conclusions, not reasoning',
54
54
  'Use labels such as',
55
55
  '`바뀐 점`, `확인한 것`,',
56
56
  'Synthesize agent or retrieval results',
@@ -72,7 +72,7 @@ for (const required of [
72
72
  'controlled detail',
73
73
  'Synthesize agent or retrieval results',
74
74
  'Do not hide blockers',
75
- 'if verification was',
75
+ 'verification was not run, say so once',
76
76
  ]) {
77
77
  assert(simpleStyle.includes(required), `simple.md missing: ${required}`);
78
78
  }
@@ -81,7 +81,7 @@ for (const [name, style] of Object.entries({
81
81
  default: defaultStyle,
82
82
  simple: simpleStyle,
83
83
  minimal: readFileSync(join(root, 'src', 'output-styles', 'minimal.md'), 'utf8'),
84
- oneline: readFileSync(join(root, 'src', 'output-styles', 'oneline.md'), 'utf8'),
84
+ 'extreme-minimal': readFileSync(join(root, 'src', 'output-styles', 'extreme-minimal.md'), 'utf8'),
85
85
  })) {
86
86
  assert(!style.includes('pre-tool preamble'), `${name} output style must not own language preamble rules`);
87
87
  assert(!style.includes('selected/default user language'), `${name} output style must not duplicate profile language rules`);
@@ -946,17 +946,21 @@ test('anthropic effort: legacy sonnet-4-5 maps effort to thinking budget', () =>
946
946
  assert.equal(modelSupportsEffort(model), false);
947
947
  });
948
948
 
949
- test('anthropic effort: xhigh on opus-4-8 normalizes to max in output_config', () => {
949
+ test('anthropic effort: xhigh on opus-4-8 is a first-class level (not downgraded to max)', () => {
950
+ // Opus 4.8 supports xhigh (modelSupportsXhighEffort), so xhigh is kept
951
+ // verbatim as a first-class effort level — matching codex's ReasoningEffort
952
+ // enum which lists xhigh between high and max. Only models WITHOUT xhigh
953
+ // support clamp it down to high.
950
954
  const model = 'claude-opus-4-8';
951
955
  assert.equal(modelSupportsMaxEffort(model), true);
952
- assert.equal(normalizeAnthropicEffortInput('xhigh', model), 'max');
956
+ assert.equal(normalizeAnthropicEffortInput('xhigh', model), 'xhigh');
953
957
  const body = _buildRequestBodyForCacheSmoke(
954
958
  [{ role: 'user', content: 'hi' }],
955
959
  model,
956
960
  [],
957
961
  { effort: 'xhigh' },
958
962
  );
959
- assert.deepEqual(body.output_config, { effort: 'max' });
963
+ assert.deepEqual(body.output_config, { effort: 'xhigh' });
960
964
  assert.equal(body.thinking, undefined);
961
965
  });
962
966