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
@@ -1,752 +1,88 @@
1
1
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
3
- import { spawn } from 'node:child_process';
4
- import { listRegisteredPlugins } from './plugin-admin.mjs';
5
-
6
- const DEFAULT_EVENTS = Object.freeze([
7
- 'runtime:start',
8
- 'session:create',
9
- 'turn:start',
10
- 'turn:end',
11
- 'turn:error',
12
- 'tool:planned',
13
- 'tool:before',
14
- 'tool:ask',
15
- 'tool:deny',
16
- 'tool:modify',
17
- 'hook:error',
18
- 'SessionStart',
19
- 'UserPromptSubmit',
20
- 'PreToolUse',
21
- 'PostToolUse',
22
- 'Stop',
23
- ]);
24
-
25
- const DEFAULT_COMMAND_TIMEOUT_S = 600;
26
- const USER_PROMPT_TIMEOUT_S = 30;
27
- const MESSAGE_DISPLAY_TIMEOUT_S = 10;
28
- const DEFAULT_PROMPT_TIMEOUT_S = 30;
29
- const DEFAULT_AGENT_TIMEOUT_S = 60;
30
- const MAX_OUTPUT_CHARS = 10_000;
31
- const MAX_BUFFER_BYTES = 10 * 1024 * 1024;
32
-
33
- const TOOL_IF_EVENTS = new Set([
34
- 'PreToolUse',
35
- 'PostToolUse',
36
- 'PostToolUseFailure',
37
- 'PermissionRequest',
38
- 'PermissionDenied',
39
- ]);
40
-
41
- const NO_MATCHER_EVENTS = new Set([
42
- 'UserPromptSubmit',
43
- 'PostToolBatch',
44
- 'Stop',
45
- 'TeammateIdle',
46
- 'TaskCreated',
47
- 'TaskCompleted',
48
- 'WorktreeCreate',
49
- 'WorktreeRemove',
50
- 'CwdChanged',
51
- 'MessageDisplay',
52
- ]);
53
-
54
- const EXIT2_BLOCK_EVENTS = new Set([
55
- 'PreToolUse',
56
- 'PermissionRequest',
57
- 'UserPromptSubmit',
58
- 'UserPromptExpansion',
59
- 'Stop',
60
- 'SubagentStop',
61
- 'TeammateIdle',
62
- 'TaskCreated',
63
- 'TaskCompleted',
64
- 'ConfigChange',
65
- 'PostToolBatch',
66
- 'PreCompact',
67
- 'Elicitation',
68
- 'ElicitationResult',
69
- ]);
70
-
71
- const TOP_LEVEL_DECISION_EVENTS = new Set([
72
- 'UserPromptSubmit',
73
- 'UserPromptExpansion',
74
- 'PostToolUse',
75
- 'PostToolUseFailure',
76
- 'PostToolBatch',
77
- 'Stop',
78
- 'SubagentStop',
79
- 'ConfigChange',
80
- 'PreCompact',
81
- ]);
82
-
83
- const PLAIN_STDOUT_CONTEXT_EVENTS = new Set([
84
- 'SessionStart',
85
- 'UserPromptSubmit',
86
- 'UserPromptExpansion',
87
- ]);
88
-
89
- const SUPPORTED_HANDLER_TYPES = new Set(['command', 'http']);
90
-
91
- function compactValue(value) {
92
- if (value == null) return value;
93
- if (typeof value === 'string') return value.length > 180 ? `${value.slice(0, 180)}...` : value;
94
- if (typeof value === 'number' || typeof value === 'boolean') return value;
95
- if (Array.isArray(value)) return value.slice(0, 8).map(compactValue);
96
- if (typeof value === 'object') {
97
- const out = {};
98
- for (const [key, val] of Object.entries(value).slice(0, 12)) {
99
- if (/token|secret|key|password/i.test(key)) {
100
- out[key] = '<redacted>';
101
- } else {
102
- out[key] = compactValue(val);
103
- }
104
- }
105
- return out;
106
- }
107
- return String(value);
108
- }
109
-
110
- function summarizePayload(payload = {}) {
111
- if (!payload || typeof payload !== 'object') return String(payload ?? '');
112
- const parts = [];
113
- if (payload.sessionId || payload.session_id) parts.push(`session=${payload.sessionId || payload.session_id}`);
114
- if (payload.name || payload.tool_name) parts.push(`name=${payload.name || payload.tool_name}`);
115
- if (payload.provider || payload.model) parts.push([payload.provider, payload.model].filter(Boolean).join('/'));
116
- if (payload.prompt) parts.push(`prompt=${String(payload.prompt).slice(0, 60).replace(/\s+/g, ' ')}`);
117
- if (payload.reason) parts.push(`reason=${String(payload.reason).slice(0, 120)}`);
118
- if (payload.error) parts.push(`error=${String(payload.error).slice(0, 120)}`);
119
- if (payload.elapsedMs != null) parts.push(`${payload.elapsedMs}ms`);
120
- return parts.join(' · ');
121
- }
122
-
123
- function hookRulesPath(dataDir) {
124
- if (process.env.MIXDOG_HOOKS_FILE) return resolve(process.env.MIXDOG_HOOKS_FILE);
125
- return dataDir ? join(dataDir, 'hooks.json') : null;
126
- }
127
-
128
- function normalizeRules(raw) {
129
- if (Array.isArray(raw)) return raw;
130
- if (Array.isArray(raw?.toolBefore)) return raw.toolBefore;
131
- if (Array.isArray(raw?.beforeTool)) return raw.beforeTool;
132
- if (Array.isArray(raw?.hooks?.toolBefore)) return raw.hooks.toolBefore;
133
- return [];
134
- }
135
-
136
- function uniqueHookEntries(entries) {
137
- const seen = new Set();
138
- const out = [];
139
- for (const entry of entries) {
140
- if (!entry?.path) continue;
141
- const resolved = resolve(entry.path);
142
- const key = process.platform === 'win32' ? resolved.toLowerCase() : resolved;
143
- if (seen.has(key)) continue;
144
- seen.add(key);
145
- out.push({ ...entry, path: resolved });
146
- }
147
- return out;
148
- }
149
-
150
- function cleanHookId(value) {
151
- return String(value ?? '').trim()
152
- .toLowerCase()
153
- .replace(/[^a-z0-9_.-]+/g, '-')
154
- .replace(/^-+|-+$/g, '');
155
- }
156
-
157
- function pluginHookConfigEntries(dataDir) {
158
- if (!dataDir) return [];
159
- let plugins = [];
160
- try {
161
- plugins = listRegisteredPlugins({ dataDir });
162
- } catch {
163
- return [];
164
- }
165
- const entries = [];
166
- for (const plugin of plugins || []) {
167
- const root = String(plugin?.root || '').trim();
168
- if (!root || !existsSync(root)) continue;
169
- const id = cleanHookId(plugin.id || plugin.name || plugin.title || root.split(/[\\/]/).pop());
170
- const pluginData = join(dataDir, 'plugins', 'data', id || 'plugin');
171
- entries.push({
172
- path: join(root, 'hooks', 'hooks.json'),
173
- pluginRoot: root,
174
- pluginData,
175
- sourceType: 'plugin',
176
- });
177
- entries.push({
178
- path: join(root, '.mixdog', 'hooks.json'),
179
- pluginRoot: root,
180
- pluginData,
181
- sourceType: 'plugin',
182
- });
183
- }
184
- return entries;
185
- }
186
-
187
- function hookConfigEntries(dataDir, cwd) {
188
- if (process.env.MIXDOG_HOOKS_FILE) {
189
- return uniqueHookEntries([{ path: process.env.MIXDOG_HOOKS_FILE, sourceType: 'env' }]);
190
- }
191
- const projectDir = cwd ? resolve(cwd) : process.cwd();
192
- return uniqueHookEntries([
193
- projectDir ? { path: join(projectDir, '.mixdog', 'hooks.json'), sourceType: 'project' } : null,
194
- projectDir ? { path: join(projectDir, '.mixdog', 'hooks', 'hooks.json'), sourceType: 'project' } : null,
195
- dataDir ? { path: join(dataDir, 'hooks.json'), sourceType: 'data' } : null,
196
- dataDir ? { path: join(dataDir, 'hooks', 'hooks.json'), sourceType: 'data' } : null,
197
- ...pluginHookConfigEntries(dataDir),
198
- ]);
199
- }
200
-
201
- function isStandardConfig(parsed) {
202
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return false;
203
- const hooks = parsed.hooks;
204
- if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) return false;
205
- const values = Object.values(hooks);
206
- if (values.length === 0) return false;
207
- return values.every((groups) => (
208
- Array.isArray(groups)
209
- && groups.every((g) => g && typeof g === 'object' && Array.isArray(g.hooks))
210
- ));
211
- }
212
-
213
- const SIMPLE_MATCHER_RE = /^[A-Za-z0-9_ ,|]*$/;
214
- function matcherFires(matcher, field) {
215
- if (matcher == null) return true;
216
- const text = String(matcher).trim();
217
- if (text === '' || text === '*') return true;
218
- const value = String(field ?? '');
219
- if (SIMPLE_MATCHER_RE.test(text)) {
220
- const items = text.split(/[|,]/).map((s) => s.trim()).filter(Boolean);
221
- if (items.length === 0) return true;
222
- return items.includes(value);
223
- }
224
- try {
225
- return new RegExp(text).test(value);
226
- } catch {
227
- return true;
228
- }
229
- }
230
-
231
- function globToRegExp(glob) {
232
- let out = '^';
233
- for (const ch of String(glob || '')) {
234
- if (ch === '*') out += '.*';
235
- else if (ch === '?') out += '.';
236
- else out += ch.replace(/[.+^${}()|[\]\\]/g, '\\$&');
237
- }
238
- return new RegExp(`${out}$`);
239
- }
240
-
241
- function primaryArgFor(toolName, toolInput) {
242
- const input = toolInput && typeof toolInput === 'object' ? toolInput : {};
243
- if (toolName === 'Bash' || toolName === 'bash' || toolName === 'shell') return input.command ?? '';
244
- if (input.file_path != null) return input.file_path;
245
- if (input.path != null) return input.path;
246
- if (input.command != null) return input.command;
247
- if (input.file != null) return input.file;
248
- return '';
249
- }
250
-
251
- function ifConditionPasses(ifExpr, eventName, toolName, toolInput) {
252
- if (ifExpr == null || String(ifExpr).trim() === '') return true;
253
- if (!TOOL_IF_EVENTS.has(eventName)) return false;
254
- const m = /^\s*([A-Za-z0-9_*]+)\s*\(([^)]*)\)\s*$/.exec(String(ifExpr));
255
- if (!m) return true;
256
- const [, tool, pattern] = m;
257
- if (tool !== '*' && tool !== toolName) return false;
258
- try {
259
- return globToRegExp(pattern.trim()).test(String(primaryArgFor(toolName, toolInput)));
260
- } catch {
261
- return true;
262
- }
263
- }
264
-
265
- function resolvePlaceholders(str, projectDir, pluginData, pluginRoot = null) {
266
- if (typeof str !== 'string') return str;
267
- const resolvedProject = projectDir || process.cwd();
268
- const resolvedPluginRoot = pluginRoot || resolvedProject;
269
- const resolvedPluginData = pluginData || resolvedProject;
270
- return str
271
- .replace(/\$\{CLAUDE_PROJECT_DIR\}/g, resolvedProject)
272
- .replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, resolvedPluginRoot)
273
- .replace(/\$\{CLAUDE_PLUGIN_DATA\}/g, resolvedPluginData)
274
- .replace(/\$\{MIXDOG_PROJECT_DIR\}/g, resolvedProject)
275
- .replace(/\$\{MIXDOG_PLUGIN_ROOT\}/g, resolvedPluginRoot)
276
- .replace(/\$\{MIXDOG_PLUGIN_DATA\}/g, resolvedPluginData);
277
- }
278
-
279
- function parseStandardConfig(parsed, source, meta = {}) {
280
- const events = {};
281
- const hooks = parsed.hooks || {};
282
- for (const [eventName, groups] of Object.entries(hooks)) {
283
- if (!Array.isArray(groups)) continue;
284
- const cleanGroups = [];
285
- for (const group of groups) {
286
- if (!group || typeof group !== 'object') continue;
287
- const handlers = Array.isArray(group.hooks)
288
- ? group.hooks
289
- .filter((h) => h && typeof h === 'object' && h.type)
290
- .map((h) => ({
291
- ...h,
292
- _source: source,
293
- ...(meta.pluginRoot ? { _pluginRoot: meta.pluginRoot } : {}),
294
- ...(meta.pluginData ? { _pluginData: meta.pluginData } : {}),
295
- }))
296
- : [];
297
- if (handlers.length === 0) continue;
298
- cleanGroups.push({ matcher: group.matcher, hooks: handlers, _source: source });
299
- }
300
- if (cleanGroups.length > 0) events[eventName] = cleanGroups;
301
- }
302
- return events;
303
- }
304
-
305
- function mergeEvents(target, source) {
306
- for (const [eventName, groups] of Object.entries(source || {})) {
307
- if (!Array.isArray(target[eventName])) target[eventName] = [];
308
- target[eventName].push(...groups);
309
- }
310
- }
311
-
312
- function buildEventPayload(eventName, input = {}) {
313
- const payload = {
314
- session_id: input.sessionId ?? input.session_id ?? null,
315
- cwd: input.cwd ?? null,
316
- hook_event_name: eventName,
317
- };
318
- if (input.transcriptPath != null || input.transcript_path != null) {
319
- payload.transcript_path = input.transcriptPath ?? input.transcript_path;
320
- }
321
- if (input.permissionMode != null || input.permission_mode != null) {
322
- payload.permission_mode = input.permissionMode ?? input.permission_mode;
323
- }
324
- if (input.effort != null) payload.effort = input.effort;
325
- const toolName = input.name ?? input.tool_name;
326
- if (toolName != null) payload.tool_name = toolName;
327
- const toolInput = input.args ?? input.tool_input;
328
- if (toolInput != null) payload.tool_input = toolInput;
329
- if (input.toolCallId != null || input.tool_use_id != null) {
330
- payload.tool_use_id = input.toolCallId ?? input.tool_use_id;
331
- }
332
- if (input.toolResponse != null || input.tool_response != null || input.result != null) {
333
- payload.tool_response = input.toolResponse ?? input.tool_response ?? input.result;
334
- }
335
- for (const key of [
336
- 'prompt',
337
- 'source',
338
- 'model',
339
- 'session_title',
340
- 'message',
341
- 'notification_type',
342
- 'trigger',
343
- 'load_reason',
344
- 'file_path',
345
- 'memory_type',
346
- 'command_name',
347
- 'command_args',
348
- 'command_source',
349
- 'expansion_type',
350
- 'stop_reason',
351
- 'error_type',
352
- ]) {
353
- if (input[key] != null) payload[key] = input[key];
354
- }
355
- return payload;
356
- }
357
-
358
- function matchFieldFor(eventName, payload) {
359
- if (NO_MATCHER_EVENTS.has(eventName)) return null;
360
- if (eventName === 'SessionStart') return payload.source ?? '';
361
- if (eventName === 'Setup') return payload.trigger ?? '';
362
- if (eventName === 'SessionEnd') return payload.reason ?? '';
363
- if (eventName === 'Notification') return payload.notification_type ?? payload.type ?? '';
364
- if (eventName === 'SubagentStart' || eventName === 'SubagentStop') return payload.agent_type ?? '';
365
- if (eventName === 'PreCompact' || eventName === 'PostCompact') return payload.trigger ?? '';
366
- if (eventName === 'ConfigChange') return payload.source ?? '';
367
- if (eventName === 'InstructionsLoaded') return payload.load_reason ?? '';
368
- if (eventName === 'UserPromptExpansion') return payload.command_name ?? '';
369
- if (eventName === 'StopFailure') return payload.error_type ?? '';
370
- return payload.tool_name ?? '';
371
- }
372
-
373
- function handlerTimeoutS(handler, eventName) {
374
- if (Number.isFinite(handler.timeout) && handler.timeout > 0) return handler.timeout;
375
- if (handler.type === 'prompt') return DEFAULT_PROMPT_TIMEOUT_S;
376
- if (handler.type === 'agent') return DEFAULT_AGENT_TIMEOUT_S;
377
- if (eventName === 'UserPromptSubmit') return USER_PROMPT_TIMEOUT_S;
378
- if (eventName === 'MessageDisplay') return MESSAGE_DISPLAY_TIMEOUT_S;
379
- return DEFAULT_COMMAND_TIMEOUT_S;
380
- }
381
-
382
- function defaultShellKind() {
383
- return process.platform === 'win32' ? 'powershell' : 'bash';
384
- }
385
-
386
- function commandSpawnSpec(handler, projectDir, pluginData) {
387
- const command = resolvePlaceholders(handler.command, projectDir, pluginData, handler._pluginRoot || null);
388
- if (Array.isArray(handler.args)) {
389
- return {
390
- command,
391
- args: handler.args.map((a) => resolvePlaceholders(String(a), projectDir, pluginData, handler._pluginRoot || null)),
392
- shellKind: 'exec',
393
- };
394
- }
395
- const shellKind = handler.shell === 'powershell' || handler.shell === 'bash'
396
- ? handler.shell
397
- : defaultShellKind();
398
- if (shellKind === 'powershell') {
399
- return {
400
- command: process.platform === 'win32' ? 'powershell.exe' : 'pwsh',
401
- args: ['-NoProfile', '-NonInteractive', '-Command', command],
402
- shellKind,
403
- };
404
- }
405
- return {
406
- command: process.platform === 'win32' ? 'bash.exe' : 'bash',
407
- args: ['-lc', command],
408
- shellKind,
409
- };
410
- }
411
-
412
- function hookEnv(projectDir, pluginData, payload, pluginRoot = null) {
413
- const resolvedProject = projectDir || process.cwd();
414
- const resolvedPluginRoot = pluginRoot || resolvedProject;
415
- const resolvedPluginData = pluginData || resolvedProject;
416
- const env = {
417
- ...process.env,
418
- MIXDOG_PROJECT_DIR: resolvedProject,
419
- MIXDOG_PLUGIN_ROOT: resolvedPluginRoot,
420
- MIXDOG_PLUGIN_DATA: resolvedPluginData,
421
- CLAUDE_PROJECT_DIR: resolvedProject,
422
- CLAUDE_PLUGIN_ROOT: resolvedPluginRoot,
423
- CLAUDE_PLUGIN_DATA: resolvedPluginData,
424
- };
425
- const effortLevel = payload?.effort?.level || payload?.effort;
426
- if (effortLevel) env.CLAUDE_EFFORT = String(effortLevel);
427
- return env;
428
- }
429
-
430
- function runCommandHandler(handler, payload, eventName, pluginData, onSpawnError = null) {
431
- const projectDir = payload.cwd || process.cwd();
432
- const effectivePluginData = handler._pluginData || pluginData || null;
433
- const stdin = JSON.stringify(payload);
434
- const timeoutMs = Math.round(handlerTimeoutS(handler, eventName) * 1000);
435
- const spec = commandSpawnSpec(handler, projectDir, effectivePluginData);
436
- const baseOpts = {
437
- cwd: existsSync(projectDir) ? projectDir : undefined,
438
- env: hookEnv(projectDir, effectivePluginData, payload, handler._pluginRoot || null),
439
- windowsHide: true,
440
- stdio: ['pipe', 'pipe', 'pipe'],
441
- };
442
-
443
- if (handler.async === true || handler.asyncRewake === true) {
444
- try {
445
- const child = spawn(spec.command, spec.args, {
446
- ...baseOpts,
447
- detached: true,
448
- stdio: ['pipe', 'ignore', 'ignore'],
449
- });
450
- child.on('error', (error) => {
451
- if (typeof onSpawnError === 'function') onSpawnError(error);
452
- });
453
- child.stdin?.end(stdin);
454
- child.unref?.();
455
- return Promise.resolve({ exitCode: 0, stdout: '', stderr: '', async: true });
456
- } catch (error) {
457
- return Promise.resolve({
458
- exitCode: -1,
459
- stdout: '',
460
- stderr: error?.message || String(error),
461
- timedOut: false,
462
- spawnError: error,
463
- });
464
- }
465
- }
466
-
467
- return new Promise((resolveRun) => {
468
- let child;
469
- let settled = false;
470
- let stdout = '';
471
- let stderr = '';
472
- let stdoutBytes = 0;
473
- let stderrBytes = 0;
474
- let timedOut = false;
475
- let timer = null;
476
- const finish = (result) => {
477
- if (settled) return;
478
- settled = true;
479
- if (timer) clearTimeout(timer);
480
- resolveRun(result);
481
- };
482
- try {
483
- child = spawn(spec.command, spec.args, baseOpts);
484
- } catch (error) {
485
- finish({
486
- exitCode: -1,
487
- stdout: '',
488
- stderr: error?.message || String(error),
489
- timedOut: false,
490
- spawnError: error,
491
- });
492
- return;
493
- }
494
- timer = setTimeout(() => {
495
- timedOut = true;
496
- try { child.kill('SIGTERM'); } catch {}
497
- finish({
498
- exitCode: -1,
499
- stdout,
500
- stderr: stderr || `hook command timed out after ${timeoutMs}ms`,
501
- timedOut,
502
- spawnError: null,
503
- });
504
- }, timeoutMs);
505
- timer.unref?.();
506
- child.stdout?.on('data', (chunk) => {
507
- stdoutBytes += chunk.length;
508
- if (stdoutBytes <= MAX_BUFFER_BYTES) stdout += chunk.toString('utf8');
509
- });
510
- child.stderr?.on('data', (chunk) => {
511
- stderrBytes += chunk.length;
512
- if (stderrBytes <= MAX_BUFFER_BYTES) stderr += chunk.toString('utf8');
513
- });
514
- child.on('error', (error) => {
515
- finish({
516
- exitCode: -1,
517
- stdout,
518
- stderr: stderr || error?.message || String(error),
519
- timedOut: false,
520
- spawnError: error,
521
- });
522
- });
523
- child.on('close', (code) => {
524
- finish({
525
- exitCode: timedOut ? -1 : (typeof code === 'number' ? code : 0),
526
- stdout,
527
- stderr,
528
- timedOut,
529
- spawnError: null,
530
- });
531
- });
532
- try {
533
- child.stdin?.end(stdin);
534
- } catch {}
535
- });
536
- }
537
-
538
- function resolveHeaderValue(value, allowed) {
539
- if (typeof value !== 'string') return String(value ?? '');
540
- return value.replace(/\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g, (_m, braced, bare) => {
541
- const name = braced || bare;
542
- return allowed.has(name) ? (process.env[name] || '') : '';
543
- });
544
- }
545
-
546
- async function runHttpHandler(handler, payload, eventName) {
547
- if (typeof fetch !== 'function') {
548
- return { exitCode: -1, stdout: '', stderr: 'fetch is not available', timedOut: false, spawnError: new Error('fetch is not available') };
549
- }
550
- const timeoutMs = Math.round(handlerTimeoutS(handler, eventName) * 1000);
551
- const controller = new AbortController();
552
- const timer = setTimeout(() => controller.abort(), timeoutMs);
553
- timer.unref?.();
554
- try {
555
- const allowed = new Set(Array.isArray(handler.allowedEnvVars) ? handler.allowedEnvVars.map(String) : []);
556
- const headers = { 'Content-Type': 'application/json' };
557
- if (handler.headers && typeof handler.headers === 'object' && !Array.isArray(handler.headers)) {
558
- for (const [key, value] of Object.entries(handler.headers)) {
559
- headers[key] = resolveHeaderValue(value, allowed);
560
- }
561
- }
562
- const response = await fetch(handler.url, {
563
- method: 'POST',
564
- headers,
565
- body: JSON.stringify(payload),
566
- signal: controller.signal,
567
- });
568
- const text = await response.text();
569
- if (!response.ok) {
570
- return { exitCode: 1, stdout: text, stderr: `HTTP ${response.status} ${response.statusText}`.trim(), timedOut: false, spawnError: null };
571
- }
572
- return { exitCode: 0, stdout: text, stderr: '', timedOut: false, spawnError: null };
573
- } catch (error) {
574
- const aborted = error?.name === 'AbortError';
575
- return {
576
- exitCode: -1,
577
- stdout: '',
578
- stderr: aborted ? `HTTP hook timed out: ${handler.url}` : (error?.message || String(error)),
579
- timedOut: aborted,
580
- spawnError: aborted ? null : error,
581
- };
582
- } finally {
583
- clearTimeout(timer);
584
- }
585
- }
586
-
587
- function limitText(text) {
588
- const value = String(text || '');
589
- if (value.length <= MAX_OUTPUT_CHARS) return value;
590
- return `${value.slice(0, MAX_OUTPUT_CHARS)}\n... [hook output truncated; original ${value.length} chars]`;
591
- }
592
-
593
- function parseHandlerOutput(run, eventName) {
594
- const out = {
595
- block: false,
596
- reason: null,
597
- permissionDecision: null,
598
- updatedInput: null,
599
- updatedToolOutput: null,
600
- additionalContext: null,
601
- systemMessage: null,
602
- suppressOutput: false,
603
- continueFlag: undefined,
604
- askReason: null,
605
- };
606
- if (run.timedOut || run.spawnError || run.async) return out;
607
- if (run.exitCode === 2) {
608
- if (EXIT2_BLOCK_EVENTS.has(eventName)) {
609
- out.block = true;
610
- out.reason = limitText((run.stderr || '').trim()) || `blocked by ${eventName} hook`;
611
- }
612
- return out;
613
- }
614
- if (run.exitCode !== 0) return out;
615
- const rawText = (run.stdout || '').trim();
616
- if (!rawText) return out;
617
- if (!(rawText.startsWith('{') || rawText.startsWith('['))) {
618
- if (PLAIN_STDOUT_CONTEXT_EVENTS.has(eventName)) out.additionalContext = limitText(rawText);
619
- return out;
620
- }
621
- try {
622
- const json = JSON.parse(rawText);
623
- if (!json || typeof json !== 'object' || Array.isArray(json)) return out;
624
- if (json.continue === false) {
625
- out.continueFlag = false;
626
- out.block = true;
627
- out.reason = limitText(json.stopReason || json.reason || `stopped by ${eventName} hook`);
628
- }
629
- if (json.decision === 'block' && TOP_LEVEL_DECISION_EVENTS.has(eventName)) {
630
- out.block = true;
631
- out.reason = limitText(json.reason || out.reason || `blocked by ${eventName} hook`);
632
- }
633
- if (json.suppressOutput) out.suppressOutput = true;
634
- if (typeof json.systemMessage === 'string') out.systemMessage = limitText(json.systemMessage);
635
- if (typeof json.additionalContext === 'string') out.additionalContext = limitText(json.additionalContext);
636
-
637
- const hso = json.hookSpecificOutput;
638
- const hsoMatches = hso && typeof hso === 'object'
639
- && (!hso.hookEventName || hso.hookEventName === eventName);
640
- if (hsoMatches) {
641
- if (typeof hso.additionalContext === 'string') out.additionalContext = limitText(hso.additionalContext);
642
- if (hso.updatedInput && typeof hso.updatedInput === 'object' && !Array.isArray(hso.updatedInput)) {
643
- out.updatedInput = hso.updatedInput;
644
- }
645
- if (hso.updatedToolOutput != null) out.updatedToolOutput = hso.updatedToolOutput;
646
- if (hso.permissionDecision) out.permissionDecision = String(hso.permissionDecision).toLowerCase();
647
- if (hso.permissionDecisionReason) out.reason = out.reason || limitText(hso.permissionDecisionReason);
648
- if (eventName === 'PermissionRequest' && hso.decision && typeof hso.decision === 'object') {
649
- const behavior = String(hso.decision.behavior || '').toLowerCase();
650
- if (behavior === 'deny') {
651
- out.block = true;
652
- out.reason = out.reason || limitText(hso.decision.reason || `denied by ${eventName} hook`);
653
- }
654
- if (hso.decision.updatedInput && typeof hso.decision.updatedInput === 'object' && !Array.isArray(hso.decision.updatedInput)) {
655
- out.updatedInput = hso.decision.updatedInput;
656
- }
657
- }
658
- }
659
- if (eventName === 'PreToolUse') {
660
- if (out.permissionDecision === 'deny') {
661
- out.block = true;
662
- out.reason = out.reason || `denied by ${eventName} hook`;
663
- } else if (out.permissionDecision === 'ask') {
664
- out.askReason = out.reason || `ask requested by ${eventName} hook`;
665
- }
666
- }
667
- } catch {
668
- if (PLAIN_STDOUT_CONTEXT_EVENTS.has(eventName)) out.additionalContext = limitText(rawText);
669
- }
670
- return out;
671
- }
672
-
673
- function matchesList(pattern, value) {
674
- if (pattern == null || pattern === '*' || pattern === 'all') return true;
675
- const items = Array.isArray(pattern) ? pattern : [pattern];
676
- return items.some((item) => {
677
- const text = String(item || '').trim();
678
- if (!text || text === '*' || text === 'all') return true;
679
- if (text.endsWith('*')) return String(value || '').startsWith(text.slice(0, -1));
680
- return text === String(value || '');
681
- });
682
- }
683
-
684
- function ruleMatches(rule, input) {
685
- if (!rule || typeof rule !== 'object' || rule.enabled === false) return false;
686
- const toolPattern = rule.tool ?? rule.name ?? rule.tools;
687
- if (!matchesList(toolPattern, input.name)) return false;
688
- if (rule.cwd && !String(input.cwd || '').includes(String(rule.cwd))) return false;
689
- const match = rule.match ?? rule.contains;
690
- if (match != null) {
691
- const haystack = JSON.stringify({ args: input.args ?? null, cwd: input.cwd ?? null });
692
- const needles = Array.isArray(match) ? match : [match];
693
- if (!needles.some((needle) => haystack.includes(String(needle)))) return false;
694
- }
695
- return true;
696
- }
697
-
698
- function decisionFromRule(rule, input) {
699
- const action = String(rule.action || rule.decision || 'allow').toLowerCase();
700
- if (action === 'deny' || action === 'block') {
701
- return { action: 'deny', reason: rule.reason || rule.message || `blocked by hook rule for ${input.name}` };
702
- }
703
- if (action === 'modify' || action === 'rewrite') {
704
- const nextArgs = rule.args && typeof rule.args === 'object'
705
- ? rule.args
706
- : { ...(input.args || {}), ...(rule.patch || {}) };
707
- return { action: 'modify', args: nextArgs, reason: rule.reason || rule.message || `modified by hook rule for ${input.name}` };
708
- }
709
- if (action === 'ask') {
710
- return { action: 'ask', reason: rule.reason || rule.message || `approval requested by hook rule for ${input.name}` };
711
- }
712
- return { action: 'allow', reason: rule.reason || rule.message || null };
713
- }
714
-
715
- function summarizeRule(rule, index) {
716
- return {
717
- index,
718
- enabled: rule.enabled !== false,
719
- tool: rule.tool ?? rule.name ?? rule.tools ?? '*',
720
- action: rule.action || rule.decision || 'allow',
721
- match: rule.match ?? rule.contains ?? null,
722
- cwd: rule.cwd || null,
723
- reason: rule.reason || rule.message || null,
724
- patch: compactValue(rule.patch || rule.args || null),
725
- };
726
- }
727
-
728
- function handlerDedupeKey(handler) {
729
- if (!handler || typeof handler !== 'object') return '';
730
- if (handler.type === 'command') {
731
- return `command:${handler._pluginRoot || ''}:${handler.command || ''}:${JSON.stringify(handler.args || null)}`;
732
- }
733
- if (handler.type === 'http') return `http:${handler.url || ''}`;
734
- return `${handler.type || 'unknown'}:${JSON.stringify(handler)}`;
735
- }
736
-
737
- function shellCountFor(handler) {
738
- if (handler?.type === 'command') {
739
- if (handler.args) return 'exec';
740
- return handler.shell === 'powershell' || handler.shell === 'bash' ? handler.shell : defaultShellKind();
741
- }
742
- return handler?.type || 'unknown';
743
- }
744
-
745
- export function createStandaloneHookBus({ maxEvents = 80, dataDir = null } = {}) {
2
+ import { dirname } from 'node:path';
3
+ import {
4
+ DEFAULT_EVENTS,
5
+ NO_MATCHER_EVENTS,
6
+ SUPPORTED_HANDLER_TYPES,
7
+ limitText,
8
+ } from './hook-bus/constants.mjs';
9
+ import {
10
+ buildEventPayload,
11
+ hookConfigEntries,
12
+ hookRulesPath,
13
+ isStandardConfig,
14
+ matchFieldFor,
15
+ matcherFires,
16
+ mergeEvents,
17
+ normalizeRules,
18
+ parseStandardConfig,
19
+ } from './hook-bus/config.mjs';
20
+ import {
21
+ handlerTimeoutS,
22
+ ifConditionPasses,
23
+ parseHandlerOutput,
24
+ runCommandHandler,
25
+ runHttpHandler,
26
+ runMcpToolHandler,
27
+ runPromptHandler,
28
+ defaultShellKind,
29
+ } from './hook-bus/handlers.mjs';
30
+ import {
31
+ compactValue,
32
+ summarizePayload,
33
+ } from './hook-bus/payload.mjs';
34
+ import {
35
+ decisionFromRule,
36
+ handlerDedupeKey,
37
+ ruleMatches,
38
+ shellCountFor,
39
+ summarizeRule,
40
+ } from './hook-bus/rules.mjs';
41
+
42
+ // Re-export extracted helpers so existing deep importers keep resolving.
43
+ export {
44
+ DEFAULT_EVENTS,
45
+ SUPPORTED_HANDLER_TYPES,
46
+ limitText,
47
+ } from './hook-bus/constants.mjs';
48
+ export {
49
+ buildEventPayload,
50
+ hookConfigEntries,
51
+ hookRulesPath,
52
+ isStandardConfig,
53
+ matchFieldFor,
54
+ matcherFires,
55
+ mergeEvents,
56
+ normalizeRules,
57
+ parseStandardConfig,
58
+ } from './hook-bus/config.mjs';
59
+ export {
60
+ handlerTimeoutS,
61
+ ifConditionPasses,
62
+ parseHandlerOutput,
63
+ runCommandHandler,
64
+ runHttpHandler,
65
+ runMcpToolHandler,
66
+ runPromptHandler,
67
+ defaultShellKind,
68
+ } from './hook-bus/handlers.mjs';
69
+ export {
70
+ compactValue,
71
+ summarizePayload,
72
+ } from './hook-bus/payload.mjs';
73
+ export {
74
+ decisionFromRule,
75
+ handlerDedupeKey,
76
+ ruleMatches,
77
+ summarizeRule,
78
+ } from './hook-bus/rules.mjs';
79
+
80
+ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null, promptRunner = null, mcpToolRunner = null } = {}) {
746
81
  const recent = [];
747
82
  const counts = new Map(DEFAULT_EVENTS.map((name) => [name, 0]));
748
83
  const rulesPath = hookRulesPath(dataDir);
749
84
  const pluginData = dataDir || null;
85
+ let rewakeHandler = null;
750
86
  let rulesCache = { mtimeMs: -1, rules: [] };
751
87
  let configCache = {
752
88
  key: '',
@@ -883,12 +219,35 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null } = {})
883
219
  error: `hook spawn failed: ${error?.message || error}`,
884
220
  });
885
221
  };
886
- return await runCommandHandler(handler, payload, eventName, pluginData, reportSpawnError);
222
+ const reportRewake = ({ eventName: en, payload: pl, text }) => {
223
+ try {
224
+ if (typeof rewakeHandler === 'function') {
225
+ rewakeHandler({ eventName: en, payload: pl, text });
226
+ } else {
227
+ emit('hook:rewake', { name: pl?.tool_name || en, text: text || null });
228
+ }
229
+ } catch {}
230
+ };
231
+ return await runCommandHandler(handler, payload, eventName, pluginData, reportSpawnError, reportRewake);
887
232
  }
888
233
  if (type === 'http') {
889
234
  if (!handler.url) return null;
890
235
  return await runHttpHandler(handler, payload, eventName);
891
236
  }
237
+ if (type === 'mcp_tool') {
238
+ if (typeof mcpToolRunner !== 'function') {
239
+ emit('hook:error', { name: payload.tool_name || eventName, error: 'handler type mcp_tool not configured' });
240
+ return null;
241
+ }
242
+ return await runMcpToolHandler(handler, payload, eventName, mcpToolRunner);
243
+ }
244
+ if (type === 'prompt') {
245
+ if (typeof promptRunner !== 'function') {
246
+ emit('hook:error', { name: payload.tool_name || eventName, error: 'handler type prompt not configured' });
247
+ return null;
248
+ }
249
+ return await runPromptHandler(handler, payload, eventName, promptRunner);
250
+ }
892
251
  return null;
893
252
  }
894
253
 
@@ -1118,5 +477,10 @@ export function createStandaloneHookBus({ maxEvents = 80, dataDir = null } = {})
1118
477
  };
1119
478
  }
1120
479
 
1121
- return { addRule, beforeTool, deleteRule, dispatch, emit, listRules, setRuleEnabled, status };
480
+ function setRewakeHandler(fn) {
481
+ rewakeHandler = typeof fn === 'function' ? fn : null;
482
+ return rewakeHandler;
483
+ }
484
+
485
+ return { addRule, beforeTool, deleteRule, dispatch, emit, listRules, setRewakeHandler, setRuleEnabled, status };
1122
486
  }