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,124 @@
1
+ // Explore quality bench — round-based tuning harness.
2
+ //
3
+ // Runs a fixed query set through runExplore against this repo, then reads
4
+ // agent-trace.jsonl to report per-session tool-call counts, wall time, and
5
+ // anchor quality. Usage:
6
+ // node scripts/explore-bench.mjs [roundLabel]
7
+ // Fresh process per run = no in-memory result cache carryover.
8
+ import { readFileSync, existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ import { runExplore } from '../src/standalone/explore-tool.mjs';
12
+
13
+ const ROUND = process.argv[2] || 'r0';
14
+ const CWD = 'C:/Project/mixdog';
15
+
16
+ // Representative locator queries with ground truth: expect = substrings, at
17
+ // least one must appear in the anchor output (quality hit). expectFail = the
18
+ // correct answer is EXPLORATION_FAILED (miss discipline).
19
+ const QUERIES = [
20
+ {
21
+ q: 'where is the tool-call stall watchdog policy resolved and what defaults does it use',
22
+ expect: ['agent-progress-watchdog', 'stall-policy'],
23
+ },
24
+ {
25
+ q: 'session compact / smart compact trigger threshold logic',
26
+ expect: ['compact'],
27
+ },
28
+ {
29
+ q: 'where are hidden agent tool schema profiles defined (read / read-write-search)',
30
+ expect: ['agent-dispatch', 'agents.json', 'internal-agents'],
31
+ },
32
+ {
33
+ q: 'recall memory chunk importance scoring implementation',
34
+ expect: ['importance', 'memory'],
35
+ },
36
+ {
37
+ q: 'websocket reconnect backoff handling',
38
+ expect: ['openai-oauth-ws', 'retry-classifier'],
39
+ },
40
+ // ── extended set: symbol-exact, filename, error-string, korean, flow, config ──
41
+ {
42
+ q: 'buildExplorerPrompt definition', // exact known symbol
43
+ expect: ['explore-tool'],
44
+ },
45
+ {
46
+ q: 'where does the explore tool cache results and what is the TTL',
47
+ expect: ['explore-tool'],
48
+ },
49
+ {
50
+ q: 'error text "maintenance route unresolved for agent" — where is it thrown',
51
+ expect: ['agent-dispatch'],
52
+ },
53
+ {
54
+ q: '에이전트 세션의 최대 루프 반복 횟수는 어디서 정해져?', // korean concept query
55
+ expect: ['agent-loop-policy'],
56
+ },
57
+ {
58
+ q: 'how does a fixed agent slot map to a workflow slot (explore/explorer)',
59
+ expect: ['mixdog-session-runtime', 'internal-agents'],
60
+ },
61
+ {
62
+ q: 'where is the mixdog config json file path resolved (data dir)',
63
+ expect: ['config', 'plugin-paths'],
64
+ },
65
+ {
66
+ q: 'GraphQL schema stitching resolver implementation', // not in this repo
67
+ expectFail: true,
68
+ },
69
+ ];
70
+
71
+ function tracePath() {
72
+ return process.env.MIXDOG_AGENT_TRACE_PATH
73
+ || join(process.env.MIXDOG_DATA_DIR || join(process.env.MIXDOG_HOME || join(homedir(), '.mixdog'), 'data'), 'history', 'agent-trace.jsonl');
74
+ }
75
+
76
+ function readTraceSince(ts) {
77
+ const p = tracePath();
78
+ if (!existsSync(p)) return [];
79
+ const rows = [];
80
+ for (const line of readFileSync(p, 'utf8').split('\n')) {
81
+ if (!line) continue;
82
+ try {
83
+ const r = JSON.parse(line);
84
+ if (r.ts >= ts && r.agent === 'explorer') rows.push(r);
85
+ } catch { /* skip */ }
86
+ }
87
+ return rows;
88
+ }
89
+
90
+ const t0 = Date.now();
91
+ const results = [];
92
+ for (const { q, expect, expectFail } of QUERIES) {
93
+ const qt0 = Date.now();
94
+ const res = await runExplore({ query: q, cwd: CWD }, { callerCwd: CWD });
95
+ const text = res?.content?.[0]?.text || '';
96
+ const anchors = text.split('\n').filter((l) => /:\d+/.test(l)).length;
97
+ const failed = /EXPLORATION_FAILED/.test(text);
98
+ const lower = text.toLowerCase();
99
+ const hit = expectFail
100
+ ? (failed && anchors === 0)
101
+ : (!failed && (expect || []).some((e) => lower.includes(e.toLowerCase())));
102
+ results.push({ q: q.slice(0, 48), ms: Date.now() - qt0, anchors, failed, hit, bytes: text.length });
103
+ }
104
+
105
+ // Attribute tool calls per explorer session spawned during this run.
106
+ const rows = readTraceSince(t0);
107
+ const bySession = new Map();
108
+ for (const r of rows) {
109
+ if (r.kind !== 'tool') continue;
110
+ const s = bySession.get(r.session_id) || { calls: 0, tools: [] };
111
+ s.calls++; s.tools.push(r.tool_name);
112
+ bySession.set(r.session_id, s);
113
+ }
114
+ const callCounts = [...bySession.values()].map((s) => s.calls).sort((a, b) => a - b);
115
+ const p50 = callCounts[Math.floor(callCounts.length / 2)] ?? 0;
116
+
117
+ console.log(`\n=== explore-bench round=${ROUND} ===`);
118
+ for (const r of results) {
119
+ console.log(` [${String(r.ms).padStart(6)}ms] ${r.hit ? 'HIT ' : 'MISS'} anchors=${r.anchors} failed=${r.failed} bytes=${r.bytes} ${r.q}`);
120
+ }
121
+ console.log(` quality: ${results.filter((r) => r.hit).length}/${results.length} hits`);
122
+ console.log(` sessions=${bySession.size} toolcalls p50=${p50} max=${callCounts[callCounts.length - 1] ?? 0} total=${callCounts.reduce((a, b) => a + b, 0)}`);
123
+ for (const [id, s] of bySession) console.log(` ${id.slice(-8)}: ${s.tools.join(',')}`);
124
+ console.log(` wall total=${((Date.now() - t0) / 1000).toFixed(1)}s`);
@@ -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`);
@@ -328,3 +328,194 @@ console.log(JSON.stringify({ hookSpecificOutput: {
328
328
  else process.env.MIXDOG_HOOKS_FILE = prev;
329
329
  }
330
330
  });
331
+
332
+ test('mcp_tool handler routes output through parseHandlerOutput', async () => {
333
+ const root = tempRoot();
334
+ const hooksFile = join(root, 'hooks.json');
335
+ writeJson(hooksFile, {
336
+ hooks: {
337
+ PreToolUse: [{
338
+ matcher: 'shell',
339
+ hooks: [{ type: 'mcp_tool', server: 'guard', tool: 'check' }],
340
+ }],
341
+ },
342
+ });
343
+ const prev = process.env.MIXDOG_HOOKS_FILE;
344
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
345
+ try {
346
+ let seenName = null;
347
+ const bus = createStandaloneHookBus({
348
+ dataDir: root,
349
+ mcpToolRunner: async ({ name }) => {
350
+ seenName = name;
351
+ return JSON.stringify({ hookSpecificOutput: {
352
+ hookEventName: 'PreToolUse',
353
+ permissionDecision: 'deny',
354
+ permissionDecisionReason: 'mcp denied',
355
+ }});
356
+ },
357
+ });
358
+ const denied = await bus.beforeTool({ sessionId: 's', cwd: root, name: 'shell', args: { command: 'x' } });
359
+ assert.equal(seenName, 'mcp__guard__check');
360
+ assert.equal(denied.action, 'deny');
361
+ assert.match(denied.reason, /mcp denied/);
362
+ } finally {
363
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
364
+ else process.env.MIXDOG_HOOKS_FILE = prev;
365
+ }
366
+ });
367
+
368
+ test('prompt handler ok:false maps to deny', async () => {
369
+ const root = tempRoot();
370
+ const hooksFile = join(root, 'hooks.json');
371
+ writeJson(hooksFile, {
372
+ hooks: {
373
+ PreToolUse: [{
374
+ matcher: 'shell',
375
+ hooks: [{ type: 'prompt', prompt: 'is this safe?' }],
376
+ }],
377
+ },
378
+ });
379
+ const prev = process.env.MIXDOG_HOOKS_FILE;
380
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
381
+ try {
382
+ const bus = createStandaloneHookBus({
383
+ dataDir: root,
384
+ promptRunner: async () => JSON.stringify({ ok: false, reason: 'unsafe' }),
385
+ });
386
+ const denied = await bus.beforeTool({ sessionId: 's', cwd: root, name: 'shell', args: { command: 'x' } });
387
+ assert.equal(denied.action, 'deny');
388
+ assert.match(denied.reason, /unsafe/);
389
+ } finally {
390
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
391
+ else process.env.MIXDOG_HOOKS_FILE = prev;
392
+ }
393
+ });
394
+
395
+ test('unconfigured prompt/mcp_tool and unsupported agent emit hook:error and skip', async () => {
396
+ const root = tempRoot();
397
+ const hooksFile = join(root, 'hooks.json');
398
+ writeJson(hooksFile, {
399
+ hooks: {
400
+ PreToolUse: [{
401
+ matcher: 'shell',
402
+ hooks: [
403
+ { type: 'prompt', prompt: 'x' },
404
+ { type: 'mcp_tool', server: 'g', tool: 't' },
405
+ { type: 'agent', agent: 'a' },
406
+ ],
407
+ }],
408
+ },
409
+ });
410
+ const prev = process.env.MIXDOG_HOOKS_FILE;
411
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
412
+ try {
413
+ const bus = createStandaloneHookBus({ dataDir: root });
414
+ const result = await bus.beforeTool({ sessionId: 's', cwd: root, name: 'shell', args: { command: 'x' } });
415
+ assert.equal(result, null);
416
+ const errs = bus.status().recent.filter((e) => e.name === 'hook:error');
417
+ assert.ok(errs.some((e) => /prompt not configured/.test(e.payload?.error || '')));
418
+ assert.ok(errs.some((e) => /mcp_tool not configured/.test(e.payload?.error || '')));
419
+ assert.ok(errs.some((e) => /unsupported hook type: agent/.test(e.payload?.error || '')));
420
+ } finally {
421
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
422
+ else process.env.MIXDOG_HOOKS_FILE = prev;
423
+ }
424
+ });
425
+
426
+ test('setRewakeHandler fires on async child exit code 2', async () => {
427
+ const root = tempRoot();
428
+ const hookScript = join(root, 'rewake.mjs');
429
+ writeFileSync(hookScript, `process.stderr.write('needs rewake'); process.exit(2);`, 'utf8');
430
+ const hooksFile = join(root, 'hooks.json');
431
+ writeJson(hooksFile, {
432
+ hooks: {
433
+ Stop: [{
434
+ hooks: [{ type: 'command', command: process.execPath, args: [hookScript], asyncRewake: true }],
435
+ }],
436
+ },
437
+ });
438
+ const prev = process.env.MIXDOG_HOOKS_FILE;
439
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
440
+ try {
441
+ const bus = createStandaloneHookBus({ dataDir: root });
442
+ const fired = new Promise((resolve) => bus.setRewakeHandler((info) => resolve(info)));
443
+ await bus.dispatch('Stop', { session_id: 's', cwd: root });
444
+ const info = await fired;
445
+ assert.equal(info.eventName, 'Stop');
446
+ assert.match(info.text, /needs rewake/);
447
+ } finally {
448
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
449
+ else process.env.MIXDOG_HOOKS_FILE = prev;
450
+ }
451
+ });
452
+
453
+ test('prompt handler plain-text "no" denies', async () => {
454
+ const root = tempRoot();
455
+ const hooksFile = join(root, 'hooks.json');
456
+ writeJson(hooksFile, {
457
+ hooks: {
458
+ PreToolUse: [{ matcher: 'shell', hooks: [{ type: 'prompt', prompt: 'safe?' }] }],
459
+ },
460
+ });
461
+ const prev = process.env.MIXDOG_HOOKS_FILE;
462
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
463
+ try {
464
+ const bus = createStandaloneHookBus({ dataDir: root, promptRunner: async () => 'no' });
465
+ const denied = await bus.beforeTool({ sessionId: 's', cwd: root, name: 'shell', args: { command: 'x' } });
466
+ assert.equal(denied.action, 'deny');
467
+ assert.match(denied.reason, /no/i);
468
+ } finally {
469
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
470
+ else process.env.MIXDOG_HOOKS_FILE = prev;
471
+ }
472
+ });
473
+
474
+ test('prompt handler timeout does not hang and reports error', async () => {
475
+ const root = tempRoot();
476
+ const hooksFile = join(root, 'hooks.json');
477
+ writeJson(hooksFile, {
478
+ hooks: {
479
+ PreToolUse: [{ matcher: 'shell', hooks: [{ type: 'prompt', prompt: 'x', timeout: 0.05 }] }],
480
+ },
481
+ });
482
+ const prev = process.env.MIXDOG_HOOKS_FILE;
483
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
484
+ try {
485
+ const bus = createStandaloneHookBus({
486
+ dataDir: root,
487
+ promptRunner: () => new Promise(() => {}),
488
+ });
489
+ const result = await bus.beforeTool({ sessionId: 's', cwd: root, name: 'shell', args: { command: 'x' } });
490
+ assert.equal(result, null);
491
+ const errs = bus.status().recent.filter((e) => e.name === 'hook:error');
492
+ assert.ok(errs.some((e) => /timed out/i.test(e.payload?.error || '')));
493
+ } finally {
494
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
495
+ else process.env.MIXDOG_HOOKS_FILE = prev;
496
+ }
497
+ });
498
+
499
+ test('prompt deny uses universal exit-2 path on Stop event', async () => {
500
+ const root = tempRoot();
501
+ const hooksFile = join(root, 'hooks.json');
502
+ writeJson(hooksFile, {
503
+ hooks: {
504
+ Stop: [{ hooks: [{ type: 'prompt', prompt: 'done?' }] }],
505
+ },
506
+ });
507
+ const prev = process.env.MIXDOG_HOOKS_FILE;
508
+ process.env.MIXDOG_HOOKS_FILE = hooksFile;
509
+ try {
510
+ const bus = createStandaloneHookBus({
511
+ dataDir: root,
512
+ promptRunner: async () => JSON.stringify({ ok: false, reason: 'not done' }),
513
+ });
514
+ const result = await bus.dispatch('Stop', { session_id: 's', cwd: root });
515
+ assert.equal(result.blocked, true);
516
+ assert.match(result.reason, /not done/);
517
+ } finally {
518
+ if (prev == null) delete process.env.MIXDOG_HOOKS_FILE;
519
+ else process.env.MIXDOG_HOOKS_FILE = prev;
520
+ }
521
+ });
@@ -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 =
@@ -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,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`);
@@ -0,0 +1,57 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { findBySuffixStrip } from '../src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs';
7
+
8
+ function makeRepo() {
9
+ const root = mkdtempSync(join(tmpdir(), 'mixdog-suffix-test-'));
10
+ mkdirSync(join(root, 'src', 'tui'), { recursive: true });
11
+ writeFileSync(join(root, 'src', 'tui', 'input-editing.mjs'), '// real file\n');
12
+ mkdirSync(join(root, 'vendor', 'ink', 'build'), { recursive: true });
13
+ writeFileSync(join(root, 'vendor', 'ink', 'build', 'index.js'), '// vendored\n');
14
+ return root;
15
+ }
16
+
17
+ test('resolves a hallucinated absolute prefix to the real repo-relative file', () => {
18
+ const root = makeRepo();
19
+ try {
20
+ const hallucinated = '/Users/danma/Local/Project/ink/src/tui/input-editing.mjs';
21
+ const hit = findBySuffixStrip(root, hallucinated);
22
+ assert.equal(hit, 'src/tui/input-editing.mjs');
23
+ } finally {
24
+ rmSync(root, { recursive: true, force: true });
25
+ }
26
+ });
27
+
28
+ test('resolves a hallucinated prefix onto a vendor/ path (no skip-dir filtering)', () => {
29
+ const root = makeRepo();
30
+ try {
31
+ const hallucinated = '/Users/danma/Local/Project/vendor/ink/build/index.js';
32
+ const hit = findBySuffixStrip(root, hallucinated);
33
+ assert.equal(hit, 'vendor/ink/build/index.js');
34
+ } finally {
35
+ rmSync(root, { recursive: true, force: true });
36
+ }
37
+ });
38
+
39
+ test('returns null when no tail length matches a real file', () => {
40
+ const root = makeRepo();
41
+ try {
42
+ const hit = findBySuffixStrip(root, '/Users/danma/Local/Project/does/not/exist.mjs');
43
+ assert.equal(hit, null);
44
+ } finally {
45
+ rmSync(root, { recursive: true, force: true });
46
+ }
47
+ });
48
+
49
+ test('a bare basename (1 segment) is never matched', () => {
50
+ const root = makeRepo();
51
+ try {
52
+ const hit = findBySuffixStrip(root, 'input-editing.mjs');
53
+ assert.equal(hit, null);
54
+ } finally {
55
+ rmSync(root, { recursive: true, force: true });
56
+ }
57
+ });