mixdog 0.7.1

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 (404) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.gitattributes +34 -0
  4. package/.mcp.json +14 -0
  5. package/ARCHITECTURE.md +77 -0
  6. package/CHANGELOG.md +7 -0
  7. package/CONTRIBUTING.md +45 -0
  8. package/DATA-FLOW.md +79 -0
  9. package/LICENSE +21 -0
  10. package/README.md +389 -0
  11. package/SECURITY.md +138 -0
  12. package/UNINSTALL.md +112 -0
  13. package/agents/maintenance.md +5 -0
  14. package/agents/memory-classification.md +30 -0
  15. package/agents/scheduler-task.md +18 -0
  16. package/agents/webhook-handler.md +27 -0
  17. package/agents/worker.md +24 -0
  18. package/bin/bridge +133 -0
  19. package/bin/statusline-launcher.mjs +78 -0
  20. package/bin/statusline-lib.mjs +550 -0
  21. package/bin/statusline.mjs +607 -0
  22. package/bun.lock +802 -0
  23. package/commands/config.md +16 -0
  24. package/commands/doctor.md +13 -0
  25. package/commands/setup.md +17 -0
  26. package/defaults/cycle3-review-prompt.md +90 -0
  27. package/defaults/hidden-roles.json +65 -0
  28. package/defaults/memory-chunk-prompt.md +63 -0
  29. package/defaults/memory-promote-prompt.md +135 -0
  30. package/defaults/mixdog-config.template.json +27 -0
  31. package/defaults/user-workflow.json +8 -0
  32. package/defaults/user-workflow.md +12 -0
  33. package/hooks/hooks.json +73 -0
  34. package/hooks/lib/active-instance.cjs +77 -0
  35. package/hooks/lib/permission-evaluator.cjs +411 -0
  36. package/hooks/lib/permission-route.cjs +63 -0
  37. package/hooks/lib/permission-rules.cjs +170 -0
  38. package/hooks/lib/settings-loader.cjs +116 -0
  39. package/hooks/post-tool-use.cjs +84 -0
  40. package/hooks/pre-mcp-sandbox.cjs +158 -0
  41. package/hooks/pre-tool-subagent.cjs +253 -0
  42. package/hooks/session-start.cjs +1372 -0
  43. package/hooks/turn-timer.cjs +82 -0
  44. package/lib/claude-md-writer.cjs +386 -0
  45. package/lib/config-cjs.cjs +61 -0
  46. package/lib/hook-pipe-path.cjs +10 -0
  47. package/lib/keychain-cjs.cjs +263 -0
  48. package/lib/plugin-paths.cjs +61 -0
  49. package/lib/rules-builder.cjs +241 -0
  50. package/lib/text-utils.cjs +61 -0
  51. package/native/README.md +117 -0
  52. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  53. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  54. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  55. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  56. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  57. package/package.json +107 -0
  58. package/prompts/code-review.txt +16 -0
  59. package/prompts/security-audit.txt +17 -0
  60. package/rules/bridge/00-common.md +39 -0
  61. package/rules/bridge/20-skip-protocol.md +18 -0
  62. package/rules/bridge/30-explorer.md +33 -0
  63. package/rules/bridge/40-cycle1-agent.md +52 -0
  64. package/rules/bridge/41-cycle2-agent.md +62 -0
  65. package/rules/bridge/42-cycle3-agent.md +44 -0
  66. package/rules/lead/00-tool-lead.md +61 -0
  67. package/rules/lead/01-general.md +23 -0
  68. package/rules/lead/02-channels.md +49 -0
  69. package/rules/lead/03-team.md +27 -0
  70. package/rules/lead/04-workflow.md +20 -0
  71. package/rules/shared/00-language.md +14 -0
  72. package/rules/shared/01-tool.md +138 -0
  73. package/scripts/bootstrap.mjs +184 -0
  74. package/scripts/bridge-unify-smoke.mjs +308 -0
  75. package/scripts/build-runtime-linux.sh +348 -0
  76. package/scripts/build-runtime-macos.sh +217 -0
  77. package/scripts/build-runtime-windows.ps1 +242 -0
  78. package/scripts/builtin-utils-smoke.mjs +392 -0
  79. package/scripts/check-json.mjs +45 -0
  80. package/scripts/check-syntax-changed.mjs +102 -0
  81. package/scripts/check-syntax.mjs +58 -0
  82. package/scripts/code-graph-batch.test.mjs +33 -0
  83. package/scripts/config-preserve-smoke.mjs +180 -0
  84. package/scripts/doctor.mjs +484 -0
  85. package/scripts/edit-normalize-fuzz.mjs +130 -0
  86. package/scripts/edit-normalize-smoke.mjs +401 -0
  87. package/scripts/edit-operation-smoke.mjs +369 -0
  88. package/scripts/edit2-smoke.mjs +63 -0
  89. package/scripts/fuzzy-e2e.mjs +28 -0
  90. package/scripts/fuzzy-smoke.mjs +26 -0
  91. package/scripts/generate-runtime-manifest.mjs +166 -0
  92. package/scripts/guard-smoke.mjs +66 -0
  93. package/scripts/hidden-role-schema-smoke.mjs +162 -0
  94. package/scripts/hook-routing-smoke.mjs +29 -0
  95. package/scripts/inject-input.ps1 +204 -0
  96. package/scripts/io-complex-smoke.mjs +667 -0
  97. package/scripts/io-explore-bench.mjs +424 -0
  98. package/scripts/io-guardrails-smoke.mjs +205 -0
  99. package/scripts/io-mini-bench-baseline.json +11 -0
  100. package/scripts/io-mini-bench.mjs +216 -0
  101. package/scripts/io-route-harness.mjs +933 -0
  102. package/scripts/io-telemetry-report.mjs +691 -0
  103. package/scripts/mutation-bench.mjs +564 -0
  104. package/scripts/mutation-io-smoke.mjs +1081 -0
  105. package/scripts/native-patch-bridge-smoke.mjs +288 -0
  106. package/scripts/native-patch-smoke.mjs +304 -0
  107. package/scripts/patch-interior-context-smoke.mjs +49 -0
  108. package/scripts/patch-newline-utf8-smoke.mjs +157 -0
  109. package/scripts/perf-hook-smoke.mjs +71 -0
  110. package/scripts/permission-eval-smoke.mjs +426 -0
  111. package/scripts/prep-patch.mjs +53 -0
  112. package/scripts/prep-shim.mjs +96 -0
  113. package/scripts/provider-cache-smoke.mjs +687 -0
  114. package/scripts/report-runtime-health.mjs +132 -0
  115. package/scripts/run-mcp.mjs +1547 -0
  116. package/scripts/salvage-v4a-shatter.test.mjs +58 -0
  117. package/scripts/scoped-cache-io-smoke.mjs +103 -0
  118. package/scripts/shell-policy-round3-smoke.mjs +46 -0
  119. package/scripts/smoke-runtime-negative.ps1 +100 -0
  120. package/scripts/smoke-runtime-negative.sh +95 -0
  121. package/scripts/stall-policy-smoke.mjs +50 -0
  122. package/scripts/start-memory-worker.mjs +23 -0
  123. package/scripts/statusline-launcher-smoke.mjs +82 -0
  124. package/scripts/stress-atomic-write.mjs +1028 -0
  125. package/scripts/test-config-rmw-restore.mjs +122 -0
  126. package/scripts/test-fault-inject.mjs +164 -0
  127. package/scripts/test-large-file.mjs +174 -0
  128. package/scripts/tool-edge-smoke.mjs +209 -0
  129. package/scripts/uninstall.mjs +201 -0
  130. package/scripts/webhook-selfheal-smoke.mjs +29 -0
  131. package/scripts/write-overwrite-guard-smoke.mjs +56 -0
  132. package/server-main.mjs +3055 -0
  133. package/server.mjs +468 -0
  134. package/setup/config-merge.mjs +254 -0
  135. package/setup/install.mjs +120 -0
  136. package/setup/launch-core.mjs +507 -0
  137. package/setup/launch.mjs +101 -0
  138. package/setup/setup-server.mjs +3206 -0
  139. package/setup/setup.html +3693 -0
  140. package/skills/retro-skill-proposer/SKILL.md +92 -0
  141. package/skills/schedule-add/SKILL.md +77 -0
  142. package/skills/setup/SKILL.md +346 -0
  143. package/skills/webhook-add/SKILL.md +81 -0
  144. package/src/agent/bridge-stall-watchdog.mjs +337 -0
  145. package/src/agent/index.mjs +2138 -0
  146. package/src/agent/orchestrator/activity-bus.mjs +38 -0
  147. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
  148. package/src/agent/orchestrator/bridge-retry.mjs +220 -0
  149. package/src/agent/orchestrator/bridge-trace.mjs +583 -0
  150. package/src/agent/orchestrator/cache-mtime.mjs +58 -0
  151. package/src/agent/orchestrator/config.mjs +358 -0
  152. package/src/agent/orchestrator/context/collect.mjs +651 -0
  153. package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
  154. package/src/agent/orchestrator/drain-registry.mjs +50 -0
  155. package/src/agent/orchestrator/explore-validator.mjs +8 -0
  156. package/src/agent/orchestrator/internal-roles.mjs +118 -0
  157. package/src/agent/orchestrator/internal-tools.mjs +88 -0
  158. package/src/agent/orchestrator/jobs.mjs +116 -0
  159. package/src/agent/orchestrator/mcp/client.mjs +364 -0
  160. package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
  161. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
  162. package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
  163. package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
  164. package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
  165. package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
  166. package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
  167. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
  168. package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
  169. package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
  170. package/src/agent/orchestrator/providers/registry.mjs +192 -0
  171. package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
  172. package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
  173. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  174. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  175. package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
  176. package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
  177. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
  178. package/src/agent/orchestrator/session/cache/util.mjs +49 -0
  179. package/src/agent/orchestrator/session/loop.mjs +1478 -0
  180. package/src/agent/orchestrator/session/manager.mjs +1975 -0
  181. package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
  182. package/src/agent/orchestrator/session/result-classification.mjs +65 -0
  183. package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
  184. package/src/agent/orchestrator/session/store.mjs +624 -0
  185. package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
  186. package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  187. package/src/agent/orchestrator/session/trim.mjs +491 -0
  188. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
  189. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
  190. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
  191. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
  192. package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
  193. package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
  194. package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
  195. package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
  196. package/src/agent/orchestrator/stall-policy.mjs +195 -0
  197. package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
  198. package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
  199. package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
  200. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  201. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
  202. package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
  203. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
  204. package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
  205. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
  206. package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
  207. package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
  208. package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
  209. package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
  210. package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
  211. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
  212. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
  213. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
  214. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
  215. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
  216. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
  217. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
  218. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
  219. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
  220. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
  221. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
  222. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
  223. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
  224. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
  225. package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
  226. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
  227. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
  228. package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
  229. package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
  230. package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
  231. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
  232. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
  233. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
  234. package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
  235. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
  236. package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
  237. package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
  238. package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
  239. package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
  240. package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
  241. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  242. package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
  243. package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
  244. package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
  245. package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
  246. package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
  247. package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
  248. package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
  249. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
  250. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
  251. package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
  252. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  253. package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
  254. package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
  255. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
  256. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
  257. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
  258. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
  259. package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
  260. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
  261. package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
  262. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
  263. package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
  264. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
  265. package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
  266. package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
  267. package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
  268. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
  269. package/src/agent/orchestrator/tools/builtin.mjs +500 -0
  270. package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
  271. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  272. package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
  273. package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
  274. package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  275. package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
  276. package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
  277. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
  278. package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
  279. package/src/agent/orchestrator/tools/host-input.mjs +204 -0
  280. package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
  281. package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
  282. package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
  283. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
  284. package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
  285. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
  286. package/src/agent/orchestrator/tools/patch.mjs +2754 -0
  287. package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
  288. package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
  289. package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
  290. package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
  291. package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
  292. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
  293. package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
  294. package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
  295. package/src/agent/orchestrator/workflow-store.mjs +93 -0
  296. package/src/agent/tool-defs.mjs +103 -0
  297. package/src/channels/backends/discord.mjs +784 -0
  298. package/src/channels/data/voice-runtime-manifest.json +138 -0
  299. package/src/channels/index.mjs +3229 -0
  300. package/src/channels/lib/cli-worker-host.mjs +12 -0
  301. package/src/channels/lib/config-lock.mjs +13 -0
  302. package/src/channels/lib/config.mjs +292 -0
  303. package/src/channels/lib/drop-trace.mjs +71 -0
  304. package/src/channels/lib/event-pipeline.mjs +81 -0
  305. package/src/channels/lib/event-queue.mjs +345 -0
  306. package/src/channels/lib/executor.mjs +168 -0
  307. package/src/channels/lib/format.mjs +188 -0
  308. package/src/channels/lib/holidays.mjs +138 -0
  309. package/src/channels/lib/hook-pipe-server.mjs +802 -0
  310. package/src/channels/lib/interaction-workflows.mjs +184 -0
  311. package/src/channels/lib/memory-client.mjs +149 -0
  312. package/src/channels/lib/output-forwarder.mjs +765 -0
  313. package/src/channels/lib/runtime-paths.mjs +479 -0
  314. package/src/channels/lib/scheduler.mjs +723 -0
  315. package/src/channels/lib/session-control.mjs +36 -0
  316. package/src/channels/lib/session-discovery.mjs +103 -0
  317. package/src/channels/lib/settings.mjs +11 -0
  318. package/src/channels/lib/state-file.mjs +68 -0
  319. package/src/channels/lib/status-snapshot.mjs +219 -0
  320. package/src/channels/lib/tool-format.mjs +140 -0
  321. package/src/channels/lib/transcript-discovery.mjs +195 -0
  322. package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
  323. package/src/channels/lib/webhook.mjs +1179 -0
  324. package/src/channels/lib/whisper-server.mjs +477 -0
  325. package/src/channels/tool-defs.mjs +170 -0
  326. package/src/daemon/host.mjs +118 -0
  327. package/src/daemon/mcp-transport.mjs +47 -0
  328. package/src/daemon/session.mjs +100 -0
  329. package/src/daemon/thin-client.mjs +71 -0
  330. package/src/daemon/transport.mjs +163 -0
  331. package/src/memory/data/runtime-manifest.json +40 -0
  332. package/src/memory/index.mjs +3305 -0
  333. package/src/memory/lib/agent-ipc.mjs +93 -0
  334. package/src/memory/lib/bridge-trace-queries.mjs +120 -0
  335. package/src/memory/lib/core-memory-store.mjs +330 -0
  336. package/src/memory/lib/embedding-provider.mjs +269 -0
  337. package/src/memory/lib/embedding-worker.mjs +323 -0
  338. package/src/memory/lib/llm-worker-host.mjs +17 -0
  339. package/src/memory/lib/memory-cycle.mjs +11 -0
  340. package/src/memory/lib/memory-cycle1.mjs +641 -0
  341. package/src/memory/lib/memory-cycle2.mjs +1284 -0
  342. package/src/memory/lib/memory-cycle3.mjs +540 -0
  343. package/src/memory/lib/memory-embed.mjs +299 -0
  344. package/src/memory/lib/memory-extraction.mjs +5 -0
  345. package/src/memory/lib/memory-maintenance-store.mjs +32 -0
  346. package/src/memory/lib/memory-ops-policy.mjs +190 -0
  347. package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
  348. package/src/memory/lib/memory-recall-read-query.mjs +7 -0
  349. package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
  350. package/src/memory/lib/memory-recall-store.mjs +621 -0
  351. package/src/memory/lib/memory-retrievers.mjs +112 -0
  352. package/src/memory/lib/memory-score.mjs +71 -0
  353. package/src/memory/lib/memory-text-utils.mjs +58 -0
  354. package/src/memory/lib/memory.mjs +412 -0
  355. package/src/memory/lib/model-profile.mjs +85 -0
  356. package/src/memory/lib/pg/adapter.mjs +308 -0
  357. package/src/memory/lib/pg/process.mjs +360 -0
  358. package/src/memory/lib/pg/supervisor.mjs +396 -0
  359. package/src/memory/lib/project-id-resolver.mjs +86 -0
  360. package/src/memory/lib/runtime-fetcher.mjs +442 -0
  361. package/src/memory/lib/trace-store.mjs +728 -0
  362. package/src/memory/tool-defs.mjs +79 -0
  363. package/src/search/index.mjs +1173 -0
  364. package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
  365. package/src/search/lib/backends/exa.mjs +50 -0
  366. package/src/search/lib/backends/firecrawl.mjs +61 -0
  367. package/src/search/lib/backends/gemini-api.mjs +83 -0
  368. package/src/search/lib/backends/grok-oauth.mjs +86 -0
  369. package/src/search/lib/backends/index.mjs +150 -0
  370. package/src/search/lib/backends/openai-api.mjs +144 -0
  371. package/src/search/lib/backends/openai-oauth.mjs +98 -0
  372. package/src/search/lib/backends/openai-web-search.mjs +76 -0
  373. package/src/search/lib/backends/tavily.mjs +55 -0
  374. package/src/search/lib/backends/xai-api.mjs +113 -0
  375. package/src/search/lib/cache.mjs +131 -0
  376. package/src/search/lib/config.mjs +192 -0
  377. package/src/search/lib/formatter.mjs +115 -0
  378. package/src/search/lib/provider-usage.mjs +67 -0
  379. package/src/search/lib/providers.mjs +47 -0
  380. package/src/search/lib/search-intent.mjs +109 -0
  381. package/src/search/lib/setup-handler.mjs +261 -0
  382. package/src/search/lib/state.mjs +201 -0
  383. package/src/search/lib/web-tools.mjs +1207 -0
  384. package/src/search/tool-defs.mjs +83 -0
  385. package/src/setup/defender-exclusion.mjs +183 -0
  386. package/src/shared/abort-controller.mjs +15 -0
  387. package/src/shared/atomic-file.mjs +420 -0
  388. package/src/shared/config.mjs +350 -0
  389. package/src/shared/daemon-recycle.mjs +108 -0
  390. package/src/shared/disable-claude-builtins.mjs +88 -0
  391. package/src/shared/err-text.mjs +12 -0
  392. package/src/shared/llm/cost.mjs +66 -0
  393. package/src/shared/llm/http-agent.mjs +123 -0
  394. package/src/shared/llm/index.mjs +41 -0
  395. package/src/shared/llm/pid-cleanup.mjs +27 -0
  396. package/src/shared/llm/usage-log.mjs +47 -0
  397. package/src/shared/plugin-paths.mjs +58 -0
  398. package/src/shared/schedules-store.mjs +70 -0
  399. package/src/shared/seed.mjs +119 -0
  400. package/src/shared/user-cwd.mjs +213 -0
  401. package/src/shared/user-data-guard.mjs +238 -0
  402. package/src/status/aggregator.mjs +584 -0
  403. package/src/status/server.mjs +413 -0
  404. package/tools.json +1653 -0
@@ -0,0 +1,691 @@
1
+ #!/usr/bin/env bun
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ process.env.CLAUDE_PLUGIN_ROOT ||= ROOT;
8
+ const { getPluginData } = await import('../src/agent/orchestrator/config.mjs');
9
+ const { countJsonNextCalls } = await import('../src/agent/orchestrator/tools/next-call-utils.mjs');
10
+
11
+ const argv = process.argv.slice(2);
12
+ const flags = new Set(argv.filter((arg) => !arg.includes('=')));
13
+ const kv = Object.fromEntries(argv.filter((arg) => arg.includes('=')).map((arg) => arg.replace(/^--/, '').split('=')));
14
+
15
+ const JSON_OUT = flags.has('--json');
16
+ const CHECK = flags.has('--check');
17
+ const REQUIRE_ROWS = flags.has('--require-rows') || /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_IO_TELEMETRY_REQUIRE_ROWS || ''));
18
+ const HOURS = Math.max(1, Number.parseInt(kv.hours || '24', 10) || 24);
19
+ const LIMIT = Math.max(1, Number.parseInt(kv.limit || '10', 10) || 10);
20
+ const SINCE_MS = Date.now() - HOURS * 3600_000;
21
+ const TRACE_PATH = kv.trace || join(getPluginData(), 'history', 'bridge-trace.jsonl');
22
+ const TRACE_EXPLICIT = Object.prototype.hasOwnProperty.call(kv, 'trace');
23
+ const TOOL_EVENTS_PATH = kv.events || join(getPluginData(), 'tool-events.log');
24
+ const MCP_DEBUG_PATH = kv.mcpDebug || kv['mcp-debug'] || join(getPluginData(), 'mcp-debug.log');
25
+
26
+ const IO_TOOLS = new Set(['read', 'grep', 'glob', 'list', 'tree', 'find_files', 'apply_patch', 'edit', 'write', 'bash', 'job_wait']);
27
+ const DEFAULT_THRESHOLDS = {
28
+ read: { p90: 250, errorRate: 0.10, wideReadRate: 0.30 },
29
+ grep: { p90: 500, errorRate: 0.10 },
30
+ glob: { p90: 500, errorRate: 0.10 },
31
+ edit: { p90: 500, errorRate: 0.15 },
32
+ apply_patch: { p90: 2000, errorRate: 0.15 },
33
+ write: { p90: 400, errorRate: 0.10 },
34
+ };
35
+
36
+ function loadThresholds() {
37
+ const raw = process.env.MIXDOG_IO_TELEMETRY_THRESHOLDS_JSON;
38
+ if (!raw) return DEFAULT_THRESHOLDS;
39
+ try {
40
+ const parsed = JSON.parse(raw);
41
+ if (!parsed || typeof parsed !== 'object') return DEFAULT_THRESHOLDS;
42
+ return { ...DEFAULT_THRESHOLDS, ...parsed };
43
+ } catch (err) {
44
+ throw new Error(`invalid MIXDOG_IO_TELEMETRY_THRESHOLDS_JSON: ${err?.message || String(err)}`);
45
+ }
46
+ }
47
+
48
+ function rowTime(row) {
49
+ const raw = row?.ts ?? row?.timestamp;
50
+ if (typeof raw === 'number') return raw;
51
+ const parsed = Date.parse(raw || 0);
52
+ return Number.isFinite(parsed) ? parsed : 0;
53
+ }
54
+
55
+ function loadTraceRows(path) {
56
+ if (!existsSync(path)) return [];
57
+ return readFileSync(path, 'utf8')
58
+ .split(/\r?\n/)
59
+ .filter(Boolean)
60
+ .map((line) => {
61
+ try { return JSON.parse(line); } catch { return null; }
62
+ })
63
+ .filter((row) => row && rowTime(row) >= SINCE_MS);
64
+ }
65
+
66
+ function parseToolEventPath(message) {
67
+ const text = String(message || '');
68
+ const inMatch = text.match(/\bin ([A-Za-z]:\\.*?)(?: \(|$)/);
69
+ if (inMatch?.[1]) return inMatch[1].trim();
70
+ const colonMatch = text.match(/: ([A-Za-z]:\\.*)$/);
71
+ if (colonMatch?.[1]) return colonMatch[1].trim();
72
+ return '';
73
+ }
74
+
75
+ function parseNearestLine(message) {
76
+ const match = String(message || '').match(/Nearest match at line (\d+)/);
77
+ return match ? Number(match[1]) : null;
78
+ }
79
+
80
+ function loadToolEventRows(path) {
81
+ if (!existsSync(path)) return [];
82
+ return readFileSync(path, 'utf8')
83
+ .split(/\r?\n/)
84
+ .filter(Boolean)
85
+ .map((line) => {
86
+ const match = line.match(/^\[(?<ts>[^\]]+)\] \[tool=(?<tool>[^\]]+)\] \[code=(?<code>[^\]]+)\] (?<message>.*)$/);
87
+ if (!match?.groups) return null;
88
+ const ts = match.groups.ts;
89
+ if (rowTime({ ts }) < SINCE_MS) return null;
90
+ const message = match.groups.message || '';
91
+ const eventPath = parseToolEventPath(message);
92
+ const nearestLine = parseNearestLine(message);
93
+ const nextCallCount = countJsonNextCalls(message);
94
+ return {
95
+ ts,
96
+ kind: 'tool',
97
+ event_source: 'tool-events',
98
+ tool_name: match.groups.tool,
99
+ tool_kind: ['edit', 'write', 'apply_patch'].includes(match.groups.tool) ? 'mutation' : 'builtin',
100
+ tool_ms: 0,
101
+ tool_args: eventPath ? { path: eventPath } : {},
102
+ result_kind: 'error',
103
+ result_has_next_call: nextCallCount > 0,
104
+ result_next_call_count: nextCallCount,
105
+ result_bytes_est: Buffer.byteLength(message, 'utf8'),
106
+ result_lines_est: message ? message.split('\n').length : 0,
107
+ event_code: match.groups.code,
108
+ event_path: eventPath || null,
109
+ event_nearest_line: nearestLine,
110
+ event_message: message,
111
+ };
112
+ })
113
+ .filter(Boolean);
114
+ }
115
+
116
+ function parseMcpDebugStartRest(rest) {
117
+ const text = String(rest || '');
118
+ const out = {};
119
+ const mode = text.match(/\bmode=([^\s]+)/);
120
+ if (mode?.[1]) out.mode = mode[1];
121
+ const itemCount = text.match(/\bitemCount=(\d+)/);
122
+ if (itemCount?.[1]) out.itemCount = Number(itemCount[1]);
123
+ const inputHash = text.match(/\binputHash=([a-f0-9]+)/i);
124
+ if (inputHash?.[1]) out.inputHash = inputHash[1];
125
+ const cmdPreview = text.match(/\bcmdPreview="([^"]*)"/);
126
+ if (cmdPreview?.[1]) out.command_preview = cmdPreview[1];
127
+ return out;
128
+ }
129
+
130
+ function loadMcpDebugRows(path) {
131
+ if (!existsSync(path)) return [];
132
+ const starts = new Map();
133
+ const rows = [];
134
+ for (const line of readFileSync(path, 'utf8').split(/\r?\n/)) {
135
+ if (!line) continue;
136
+ let match = line.match(/^\[(?<ts>[^\]]+)\].*?\[dispatch\] start id=(?<id>\S+) tool=(?<tool>\S+)(?<rest>.*)$/);
137
+ if (match?.groups) {
138
+ const ts = match.groups.ts;
139
+ if (rowTime({ ts }) >= SINCE_MS) {
140
+ starts.set(match.groups.id, {
141
+ ts,
142
+ id: match.groups.id,
143
+ tool: match.groups.tool,
144
+ rest: match.groups.rest || '',
145
+ });
146
+ }
147
+ continue;
148
+ }
149
+ match = line.match(/^\[(?<ts>[^\]]+)\].*?\[dispatch\] ok id=(?<id>\S+) tool=(?<tool>\S+) elapsed=(?<elapsed>\d+)ms/);
150
+ if (!match?.groups) continue;
151
+ const ts = match.groups.ts;
152
+ if (rowTime({ ts }) < SINCE_MS) continue;
153
+ const start = starts.get(match.groups.id);
154
+ const tool = match.groups.tool || start?.tool || 'unknown';
155
+ rows.push({
156
+ ts,
157
+ kind: 'tool',
158
+ event_source: 'mcp-debug',
159
+ tool_name: tool,
160
+ tool_kind: ['edit', 'write', 'apply_patch'].includes(tool) ? 'mutation' : 'builtin',
161
+ tool_ms: Number(match.groups.elapsed) || 0,
162
+ tool_args: start ? parseMcpDebugStartRest(start.rest) : {},
163
+ result_kind: 'normal',
164
+ result_has_next_call: false,
165
+ result_next_call_count: 0,
166
+ dispatch_id: match.groups.id,
167
+ dispatch_session_id: String(match.groups.id).split('-')[0] || null,
168
+ dispatch_started_at: start?.ts || null,
169
+ });
170
+ }
171
+ return rows;
172
+ }
173
+
174
+ function percentile(values, p) {
175
+ if (!values.length) return 0;
176
+ const sorted = [...values].sort((a, b) => a - b);
177
+ const idx = Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * p));
178
+ return sorted[idx];
179
+ }
180
+
181
+ function round(value) {
182
+ return Number((Number(value) || 0).toFixed(2));
183
+ }
184
+
185
+ function summarizeSamples(samples) {
186
+ return {
187
+ count: samples.length,
188
+ p50: round(percentile(samples, 0.50)),
189
+ p90: round(percentile(samples, 0.90)),
190
+ max: round(samples.length ? Math.max(...samples) : 0),
191
+ total: round(samples.reduce((sum, value) => sum + value, 0)),
192
+ };
193
+ }
194
+
195
+ function groupToolRows(rows) {
196
+ const byTool = new Map();
197
+ for (const row of rows) {
198
+ if (row.kind !== 'tool') continue;
199
+ const tool = row.tool_name || row.toolName || 'unknown';
200
+ if (!IO_TOOLS.has(tool)) continue;
201
+ const ms = Number(row.tool_ms ?? row.toolMs);
202
+ if (!Number.isFinite(ms)) continue;
203
+ if (!byTool.has(tool)) byTool.set(tool, { samples: [], resultKinds: new Map(), wideReads: 0, broadGrepCalls: 0, largeEditCalls: 0, nextCalls: 0, nextCallTotal: 0, nextCallMisses: new Map(), outputBytes: [], outputLines: [] });
204
+ const bucket = byTool.get(tool);
205
+ bucket.samples.push(ms);
206
+ const kind = row.result_kind || 'normal';
207
+ bucket.resultKinds.set(kind, (bucket.resultKinds.get(kind) || 0) + 1);
208
+ const hasNextCall = row.result_has_next_call === true || row.resultHasNextCall === true;
209
+ const args = row.tool_args || row.toolArgs || {};
210
+ const resultBytes = Number(row.result_bytes_est ?? row.resultBytesEst ?? row.result_bytes ?? row.resultBytes);
211
+ const resultLines = Number(row.result_lines_est ?? row.resultLinesEst ?? row.result_lines ?? row.resultLines);
212
+ if (Number.isFinite(resultBytes)) bucket.outputBytes.push(resultBytes);
213
+ if (Number.isFinite(resultLines)) bucket.outputLines.push(resultLines);
214
+ if (hasNextCall) {
215
+ bucket.nextCalls += 1;
216
+ bucket.nextCallTotal += Math.max(1, Number(row.result_next_call_count ?? row.resultNextCallCount) || 1);
217
+ } else {
218
+ const shape = nextCallShape(tool, args);
219
+ bucket.nextCallMisses.set(shape, (bucket.nextCallMisses.get(shape) || 0) + 1);
220
+ }
221
+ if (tool === 'read') {
222
+ const explicitWide = row.wide_read === true || row.wideRead === true;
223
+ const hasRange = args.offset !== undefined || args.limit !== undefined || args.line !== undefined || args.context !== undefined;
224
+ const mode = args.mode === undefined ? undefined : String(args.mode).toLowerCase();
225
+ const compact = args.budget === 'compact';
226
+ const explicitFull = args.full === true || mode === 'full';
227
+ const defaultFull = mode === undefined && args.full !== false;
228
+ const likelyWide = !hasRange && !compact && (explicitFull || (defaultFull && Number.isFinite(resultBytes) && resultBytes >= 8192));
229
+ if (explicitWide || likelyWide) bucket.wideReads += 1;
230
+ }
231
+ if (tool === 'grep') {
232
+ if (row.broad_grep_call === true || row.broadGrepCall === true) bucket.broadGrepCalls += 1;
233
+ }
234
+ if (tool === 'edit' && (row.large_edit_call === true || row.largeEditCall === true)) {
235
+ bucket.largeEditCalls += 1;
236
+ }
237
+ }
238
+ return [...byTool.entries()]
239
+ .map(([tool, value]) => ({
240
+ tool,
241
+ ...summarizeSamples(value.samples),
242
+ resultKinds: Object.fromEntries(value.resultKinds),
243
+ errors: value.resultKinds.get('error') || 0,
244
+ wideReads: value.wideReads || 0,
245
+ broadGrepCalls: value.broadGrepCalls || 0,
246
+ largeEditCalls: value.largeEditCalls || 0,
247
+ nextCalls: value.nextCalls || 0,
248
+ nextCallTotal: value.nextCallTotal || 0,
249
+ nextCallRate: value.samples.length > 0 ? round((value.nextCalls || 0) / value.samples.length) : 0,
250
+ nextCallMisses: Object.fromEntries([...value.nextCallMisses.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 5)),
251
+ outputBytes: summarizeSamples(value.outputBytes),
252
+ outputLines: summarizeSamples(value.outputLines),
253
+ }))
254
+ .sort((a, b) => b.p90 - a.p90 || b.count - a.count);
255
+ }
256
+
257
+ function nextCallShape(tool, args = {}) {
258
+ if (tool === 'grep') return `grep:${args.output_mode || args.mode || 'files_with_matches'}`;
259
+ if (tool === 'glob') return `glob:${args.pattern || args.glob || args.file_pattern ? 'pattern' : 'missing-pattern'}`;
260
+ if (tool === 'read') {
261
+ if (args.mode) return `read:${args.mode}`;
262
+ if (args.line !== undefined) return 'read:line';
263
+ if (args.offset !== undefined || args.limit !== undefined) return 'read:range';
264
+ return 'read:full';
265
+ }
266
+ if (tool === 'apply_patch') return args.dry_run === true ? 'apply_patch:dry_run' : 'apply_patch:apply';
267
+ if (tool === 'edit') return Array.isArray(args.edits) ? 'edit:batch' : 'edit:single';
268
+ return `${tool}:default`;
269
+ }
270
+
271
+ function evaluateTools(tools, thresholds, options = {}) {
272
+ const failures = [];
273
+ const recommendations = [];
274
+ const skipGenericErrorRate = options.skipGenericErrorRate === true;
275
+ for (const item of tools) {
276
+ const threshold = thresholds[item.tool] || {};
277
+ const errorRate = item.count > 0 ? item.errors / item.count : 0;
278
+ const wideReadRate = item.count > 0 ? item.wideReads / item.count : 0;
279
+ if (Number.isFinite(Number(threshold.p90)) && item.p90 > Number(threshold.p90)) {
280
+ failures.push({ tool: item.tool, reason: 'slow-p90', p90: item.p90, threshold: Number(threshold.p90) });
281
+ recommendations.push(`${item.tool}: p90 ${item.p90}ms exceeds ${threshold.p90}ms; inspect cache hit rate and prefer batched/ranged calls.`);
282
+ }
283
+ if (!skipGenericErrorRate && Number.isFinite(Number(threshold.errorRate)) && errorRate > Number(threshold.errorRate)) {
284
+ failures.push({ tool: item.tool, reason: 'error-rate', errorRate: round(errorRate), threshold: Number(threshold.errorRate) });
285
+ recommendations.push(`${item.tool}: error rate ${round(errorRate * 100)}%; improve call-shape hints or add a focused smoke for the dominant error.`);
286
+ }
287
+ if (item.tool === 'read' && Number.isFinite(Number(threshold.wideReadRate)) && wideReadRate > Number(threshold.wideReadRate)) {
288
+ failures.push({ tool: item.tool, reason: 'wide-read-rate', wideReadRate: round(wideReadRate), threshold: Number(threshold.wideReadRate) });
289
+ recommendations.push(`read: wide-read rate ${round(wideReadRate * 100)}%; prefer grep/find_symbol or read({file_path, offset, limit}).`);
290
+ }
291
+ if (item.tool === 'grep' && item.broadGrepCalls > 0) {
292
+ recommendations.push(`grep: ${item.broadGrepCalls} broad root search call(s); prefer glob/list -> narrowed grep -> batched read when the target root is not pinned.`);
293
+ }
294
+ if (item.tool === 'edit' && item.largeEditCalls > 0) {
295
+ recommendations.push(`edit: ${item.largeEditCalls} large old_string call(s); use direct apply_patch for chunks >=30 lines or structural edits.`);
296
+ }
297
+ if (Number.isFinite(Number(threshold.nextCallRateMin)) && item.nextCallRate < Number(threshold.nextCallRateMin)) {
298
+ failures.push({ tool: item.tool, reason: 'next-call-rate', nextCallRate: item.nextCallRate, threshold: Number(threshold.nextCallRateMin) });
299
+ const missing = Object.entries(item.nextCallMisses || {})
300
+ .slice(0, 3)
301
+ .map(([shape, count]) => `${shape}:${count}`)
302
+ .join(', ');
303
+ recommendations.push(`${item.tool}: next_call coverage ${round(item.nextCallRate * 100)}%; add or preserve direct retry/follow-up guidance for result-bearing paths${missing ? ` (top missing: ${missing})` : ''}.`);
304
+ }
305
+ if (Number.isFinite(Number(threshold.outputBytesP90)) && item.outputBytes?.p90 > Number(threshold.outputBytesP90)) {
306
+ failures.push({ tool: item.tool, reason: 'output-bytes-p90', outputBytesP90: item.outputBytes.p90, threshold: Number(threshold.outputBytesP90) });
307
+ recommendations.push(`${item.tool}: output p90 ${item.outputBytes.p90} bytes exceeds ${threshold.outputBytesP90}; prefer bounded read ranges or tighter result limits.`);
308
+ }
309
+ }
310
+ return { failures, recommendations: Array.from(new Set(recommendations)) };
311
+ }
312
+
313
+ function summarizeLoop(rows) {
314
+ const send = [];
315
+ const bodyBytes = [];
316
+ for (const row of rows) {
317
+ if (row.kind !== 'loop') continue;
318
+ const ms = Number(row.send_ms ?? row.sendMs);
319
+ const bytes = Number(row.body_bytes_est ?? row.bodyBytesEst);
320
+ if (Number.isFinite(ms)) send.push(ms);
321
+ if (Number.isFinite(bytes)) bodyBytes.push(bytes);
322
+ }
323
+ return {
324
+ sends: summarizeSamples(send),
325
+ bodyBytes: summarizeSamples(bodyBytes),
326
+ };
327
+ }
328
+
329
+ function summarizeTrim(rows) {
330
+ let count = 0;
331
+ let bytesBefore = 0;
332
+ let bytesAfter = 0;
333
+ let changed = 0;
334
+ for (const row of rows) {
335
+ if (row.kind !== 'trim_meta') continue;
336
+ count += 1;
337
+ if (row.trim_changed) changed += 1;
338
+ const before = Number(row.before_bytes);
339
+ const after = Number(row.after_bytes);
340
+ if (Number.isFinite(before)) bytesBefore += before;
341
+ if (Number.isFinite(after)) bytesAfter += after;
342
+ }
343
+ return { count, changed, bytesBefore, bytesAfter, bytesSaved: Math.max(0, bytesBefore - bytesAfter) };
344
+ }
345
+
346
+ function summarizeCompression(rows) {
347
+ let count = 0;
348
+ let before = 0;
349
+ let after = 0;
350
+ for (const row of rows) {
351
+ if (row.kind !== 'compress') continue;
352
+ const payload = row.payload || {};
353
+ const b = Number(payload.bytes_before);
354
+ const a = Number(payload.bytes_after);
355
+ if (!Number.isFinite(b) || !Number.isFinite(a)) continue;
356
+ count += 1;
357
+ before += b;
358
+ after += a;
359
+ }
360
+ return { count, bytesBefore: before, bytesAfter: after, bytesSaved: Math.max(0, before - after) };
361
+ }
362
+
363
+ function summarizeRoutes(rows) {
364
+ const byScenario = new Map();
365
+ for (const row of rows) {
366
+ if (row.kind !== 'route') continue;
367
+ const scenario = row.route_scenario || row.routeScenario || 'unknown';
368
+ const route = row.route_name || row.routeName || 'unknown';
369
+ const score = Number(row.route_score ?? row.routeScore);
370
+ const item = {
371
+ route,
372
+ success: row.route_success === true || row.routeSuccess === true,
373
+ wallMs: round(Number(row.route_wall_ms ?? row.routeWallMs)),
374
+ toolCalls: Number(row.route_tool_calls ?? row.routeToolCalls) || 0,
375
+ outputBytes: Number(row.route_output_bytes ?? row.routeOutputBytes) || 0,
376
+ nextCalls: Number(row.route_next_calls ?? row.routeNextCalls) || 0,
377
+ wideReads: Number(row.route_wide_reads ?? row.routeWideReads) || 0,
378
+ broadGrepCalls: Number(row.route_broad_grep_calls ?? row.routeBroadGrepCalls) || 0,
379
+ largeEditCalls: Number(row.route_large_edit_calls ?? row.routeLargeEditCalls) || 0,
380
+ errors: Number(row.route_errors ?? row.routeErrors) || 0,
381
+ score: Number.isFinite(score) ? score : null,
382
+ };
383
+ if (!byScenario.has(scenario)) byScenario.set(scenario, []);
384
+ byScenario.get(scenario).push(item);
385
+ }
386
+ return [...byScenario.entries()]
387
+ .map(([scenario, routes]) => {
388
+ const ranked = [...routes].sort((a, b) => {
389
+ if (a.success !== b.success) return a.success ? -1 : 1;
390
+ const aScore = Number.isFinite(a.score) ? a.score : Number.POSITIVE_INFINITY;
391
+ const bScore = Number.isFinite(b.score) ? b.score : Number.POSITIVE_INFINITY;
392
+ return aScore - bScore || a.toolCalls - b.toolCalls || a.outputBytes - b.outputBytes;
393
+ });
394
+ const winner = ranked.find((route) => route.success) || null;
395
+ return {
396
+ scenario,
397
+ winner: winner?.route || null,
398
+ count: routes.length,
399
+ failures: routes.filter((route) => !route.success).length,
400
+ routes: ranked,
401
+ };
402
+ })
403
+ .sort((a, b) => a.scenario.localeCompare(b.scenario));
404
+ }
405
+
406
+ function evaluateRoutes(routes) {
407
+ const failures = [];
408
+ const recommendations = [];
409
+ for (const scenario of routes) {
410
+ const winner = scenario.routes.find((route) => route.route === scenario.winner) || null;
411
+ if (!winner) {
412
+ failures.push({ scenario: scenario.scenario, reason: 'no-successful-route' });
413
+ recommendations.push(`${scenario.scenario}: no successful route; inspect fixture, route policy, or tool failure output.`);
414
+ continue;
415
+ }
416
+ if (winner.wideReads > 0) {
417
+ failures.push({ scenario: scenario.scenario, route: winner.route, reason: 'route-winner-wide-read' });
418
+ recommendations.push(`${scenario.scenario}: winning route ${winner.route} used a wide read; prefer grep, read({file_path, offset, limit}), or apply_patch.`);
419
+ }
420
+ if (winner.broadGrepCalls > 0) {
421
+ failures.push({ scenario: scenario.scenario, route: winner.route, reason: 'route-winner-broad-grep' });
422
+ recommendations.push(`${scenario.scenario}: winning route ${winner.route} used broad grep; prefer glob/list before repo-root grep.`);
423
+ }
424
+ if (winner.largeEditCalls > 0) {
425
+ failures.push({ scenario: scenario.scenario, route: winner.route, reason: 'route-winner-large-edit' });
426
+ recommendations.push(`${scenario.scenario}: winning route ${winner.route} used large edit; prefer direct apply_patch for large chunks.`);
427
+ }
428
+ if (winner.errors > 0) {
429
+ failures.push({ scenario: scenario.scenario, route: winner.route, reason: 'route-winner-errors' });
430
+ recommendations.push(`${scenario.scenario}: winning route ${winner.route} had tool errors; route scoring or fallback handling needs attention.`);
431
+ }
432
+ }
433
+ return { failures, recommendations };
434
+ }
435
+
436
+ function summarizeEventClusters(rows) {
437
+ const eventRows = rows.filter((row) => row.event_source === 'tool-events' && row.kind === 'tool');
438
+ const byKey = new Map();
439
+ for (const row of eventRows) {
440
+ const key = `${row.tool_name || 'unknown'}|${row.event_code || ''}|${row.event_path || '(none)'}`;
441
+ let bucket = byKey.get(key);
442
+ if (!bucket) {
443
+ bucket = {
444
+ tool: row.tool_name || 'unknown',
445
+ code: row.event_code || '',
446
+ path: row.event_path || null,
447
+ count: 0,
448
+ firstAt: row.ts,
449
+ lastAt: row.ts,
450
+ nearestLines: new Map(),
451
+ sample: row.event_message || '',
452
+ };
453
+ byKey.set(key, bucket);
454
+ }
455
+ bucket.count += 1;
456
+ bucket.lastAt = row.ts;
457
+ if (row.event_nearest_line) {
458
+ const line = String(row.event_nearest_line);
459
+ bucket.nearestLines.set(line, (bucket.nearestLines.get(line) || 0) + 1);
460
+ }
461
+ }
462
+ return [...byKey.values()]
463
+ .map((bucket) => ({
464
+ ...bucket,
465
+ nearestLines: Object.fromEntries([...bucket.nearestLines.entries()].sort((a, b) => b[1] - a[1] || Number(a[0]) - Number(b[0]))),
466
+ sample: String(bucket.sample || '').slice(0, 220),
467
+ }))
468
+ .sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool))
469
+ .slice(0, LIMIT);
470
+ }
471
+
472
+ function summarizeRepeatClusters(rows) {
473
+ const debugRows = rows
474
+ .filter((row) => row.event_source === 'mcp-debug' && row.kind === 'tool' && row.dispatch_session_id)
475
+ .sort((a, b) => rowTime(a) - rowTime(b));
476
+ const bySession = new Map();
477
+ for (const row of debugRows) {
478
+ const sid = row.dispatch_session_id;
479
+ if (!bySession.has(sid)) bySession.set(sid, []);
480
+ bySession.get(sid).push(row);
481
+ }
482
+ const clusters = [];
483
+ const maxGapMs = 15_000;
484
+ for (const [session, sessionRows] of bySession.entries()) {
485
+ for (let i = 0; i < sessionRows.length;) {
486
+ let j = i + 1;
487
+ const tool = sessionRows[i].tool_name || 'unknown';
488
+ while (
489
+ j < sessionRows.length
490
+ && (sessionRows[j].tool_name || 'unknown') === tool
491
+ && rowTime(sessionRows[j]) - rowTime(sessionRows[j - 1]) <= maxGapMs
492
+ ) {
493
+ j += 1;
494
+ }
495
+ const count = j - i;
496
+ if (count >= 3) {
497
+ clusters.push({
498
+ session,
499
+ tool,
500
+ count,
501
+ firstAt: sessionRows[i].ts,
502
+ lastAt: sessionRows[j - 1].ts,
503
+ spanMs: rowTime(sessionRows[j - 1]) - rowTime(sessionRows[i]),
504
+ });
505
+ }
506
+ i = j;
507
+ }
508
+ }
509
+ return clusters
510
+ .sort((a, b) => b.count - a.count || b.spanMs - a.spanMs || a.tool.localeCompare(b.tool))
511
+ .slice(0, LIMIT);
512
+ }
513
+
514
+ function evaluateRepeatClusters(clusters) {
515
+ const recommendations = [];
516
+ for (const cluster of clusters) {
517
+ if (cluster.tool === 'read') {
518
+ recommendations.push(`read: ${cluster.count} consecutive reads in session ${cluster.session}; use one wider read({file_path, offset, limit}) window when the ranges are adjacent.`);
519
+ } else if (cluster.tool === 'grep') {
520
+ recommendations.push(`grep: ${cluster.count} consecutive searches in session ${cluster.session}; combine patterns/globs or switch to read/find_symbol after the first useful hit.`);
521
+ } else if (cluster.tool === 'bash') {
522
+ recommendations.push(`bash: ${cluster.count} consecutive shell calls in session ${cluster.session}; use persistent:true/session_id for setup-heavy commands or dedicated read/grep/list tools for file I/O.`);
523
+ } else if (cluster.tool === 'edit') {
524
+ recommendations.push(`edit: ${cluster.count} consecutive edits in session ${cluster.session}; batch independent replacements in edit({edits:[...]}) or route multi-hunk changes through apply_patch.`);
525
+ } else if (cluster.tool === 'apply_patch') {
526
+ recommendations.push(`apply_patch: ${cluster.count} consecutive patches in session ${cluster.session}; merge adjacent hunks into one patch when they touch the same file.`);
527
+ }
528
+ }
529
+ return { recommendations: Array.from(new Set(recommendations)) };
530
+ }
531
+
532
+ function evaluateEventClusters(clusters) {
533
+ const failures = [];
534
+ const recommendations = [];
535
+ for (const cluster of clusters) {
536
+ if (cluster.tool === 'edit' && cluster.code === '8' && cluster.path && cluster.count >= 2) {
537
+ failures.push({ tool: 'edit', code: '8', path: cluster.path, count: cluster.count, reason: 'same-file-edit-miss-loop' });
538
+ const lines = Object.keys(cluster.nearestLines || {}).slice(0, 3).join(', ');
539
+ recommendations.push(`edit: ${cluster.count} old_string misses on ${cluster.path}${lines ? ` near lines ${lines}` : ''}; re-read a narrow Mixdog window and switch to exact visible bytes or apply_patch.`);
540
+ }
541
+ if (cluster.tool === 'edit' && cluster.code === '10' && cluster.count >= 1) {
542
+ recommendations.push(`edit: ${cluster.count} large old_string warning(s); route chunks >=30 lines through apply_patch instead of edit.`);
543
+ }
544
+ if (cluster.tool === 'write' && cluster.code === '10' && cluster.count >= 1) {
545
+ recommendations.push(`write: ${cluster.count} partial-read overwrite block(s); use apply_patch for partial changes or full read before whole-file write.`);
546
+ }
547
+ }
548
+ return { failures, recommendations: Array.from(new Set(recommendations)) };
549
+ }
550
+
551
+ const traceRows = loadTraceRows(TRACE_PATH);
552
+ const toolEventRows = !TRACE_EXPLICIT ? loadToolEventRows(TOOL_EVENTS_PATH) : [];
553
+ const mcpDebugRows = !TRACE_EXPLICIT ? loadMcpDebugRows(MCP_DEBUG_PATH) : [];
554
+ const fallbackRows = [...mcpDebugRows, ...toolEventRows].sort((a, b) => rowTime(a) - rowTime(b));
555
+ const rows = traceRows.length > 0 ? traceRows : fallbackRows;
556
+ const source = traceRows.length > 0
557
+ ? 'bridge-trace'
558
+ : (mcpDebugRows.length > 0 && toolEventRows.length > 0
559
+ ? 'mcp-debug+tool-events'
560
+ : (mcpDebugRows.length > 0 ? 'mcp-debug' : (toolEventRows.length > 0 ? 'tool-events' : 'none')));
561
+ const thresholds = loadThresholds();
562
+ const routeTraceMode = TRACE_EXPLICIT && rows.some((row) => row.kind === 'route' || row.route_scenario || row.routeScenario);
563
+ const tools = groupToolRows(rows).slice(0, LIMIT);
564
+ const evaluation = evaluateTools(tools, thresholds, { skipGenericErrorRate: routeTraceMode });
565
+ const routes = summarizeRoutes(rows);
566
+ const routeEvaluation = evaluateRoutes(routes);
567
+ const eventRows = traceRows.length > 0 ? [...rows, ...toolEventRows] : rows;
568
+ const eventClusters = summarizeEventClusters(eventRows);
569
+ const eventEvaluation = evaluateEventClusters(eventClusters);
570
+ const repeatRows = traceRows.length > 0 ? [...rows, ...mcpDebugRows] : rows;
571
+ const repeatClusters = summarizeRepeatClusters(repeatRows);
572
+ const repeatEvaluation = evaluateRepeatClusters(repeatClusters);
573
+ const toolSampleCount = tools.reduce((sum, row) => sum + row.count, 0);
574
+ const warnings = [];
575
+ if (traceRows.length === 0 && toolEventRows.length > 0) {
576
+ warnings.push(`no bridge trace rows found at ${TRACE_PATH}; using tool-events fallback at ${TOOL_EVENTS_PATH}`);
577
+ }
578
+ if (traceRows.length === 0 && mcpDebugRows.length > 0) {
579
+ warnings.push(`no bridge trace rows found at ${TRACE_PATH}; using mcp-debug fallback at ${MCP_DEBUG_PATH}`);
580
+ }
581
+ if (rows.length === 0) {
582
+ warnings.push(`no trace rows found at ${TRACE_PATH}; run a live session with MIXDOG_BRIDGE_TRACE_VERBOSE=1, pass --trace=<bridge-trace.jsonl>, feed scripts/io-route-harness.mjs --trace-out=<file>, or inspect tool-events at ${TOOL_EVENTS_PATH}`);
583
+ }
584
+ if (rows.length > 0 && toolSampleCount === 0) {
585
+ warnings.push(`trace rows found at ${TRACE_PATH}, but no I/O tool rows matched; check bridge trace verbosity or feed a route harness trace`);
586
+ }
587
+ const rowFailures = rows.length === 0 && REQUIRE_ROWS
588
+ ? [{ reason: 'no-trace-rows', tracePath: TRACE_PATH, windowHours: HOURS }]
589
+ : (toolSampleCount === 0 && REQUIRE_ROWS
590
+ ? [{ reason: 'no-io-tool-rows', tracePath: TRACE_PATH, windowHours: HOURS }]
591
+ : []);
592
+ const output = {
593
+ windowHours: HOURS,
594
+ tracePath: TRACE_PATH,
595
+ toolEventsPath: TOOL_EVENTS_PATH,
596
+ mcpDebugPath: MCP_DEBUG_PATH,
597
+ source,
598
+ traceRows: traceRows.length,
599
+ toolEventRows: toolEventRows.length,
600
+ mcpDebugRows: mcpDebugRows.length,
601
+ rows: rows.length,
602
+ tools,
603
+ routes,
604
+ eventClusters,
605
+ repeatClusters,
606
+ loop: summarizeLoop(rows),
607
+ trim: summarizeTrim(rows),
608
+ compression: summarizeCompression(rows),
609
+ thresholds: CHECK ? thresholds : undefined,
610
+ failures: CHECK ? [...rowFailures, ...evaluation.failures, ...routeEvaluation.failures, ...eventEvaluation.failures] : undefined,
611
+ warnings,
612
+ recommendations: Array.from(new Set([...warnings, ...evaluation.recommendations, ...routeEvaluation.recommendations, ...eventEvaluation.recommendations, ...repeatEvaluation.recommendations])),
613
+ };
614
+
615
+ if (JSON_OUT) {
616
+ console.log(JSON.stringify(output, null, 2));
617
+ } else {
618
+ console.log(`# I/O Telemetry (${HOURS}h)`);
619
+ console.log(`trace: ${TRACE_PATH}`);
620
+ console.log(`events: ${TOOL_EVENTS_PATH}`);
621
+ console.log(`mcp-debug: ${MCP_DEBUG_PATH}`);
622
+ console.log(`source: ${source}`);
623
+ console.log(`rows: ${rows.length}`);
624
+ if (!rows.length) {
625
+ for (const warning of warnings) console.log(`warning: ${warning}`);
626
+ if (CHECK && REQUIRE_ROWS) {
627
+ console.log('');
628
+ console.log('thresholds: fail no-trace-rows');
629
+ process.exit(1);
630
+ }
631
+ process.exit(0);
632
+ }
633
+ console.log('');
634
+ console.log('tool count p50ms p90ms maxms totalms notes');
635
+ for (const item of output.tools) {
636
+ const notes = [];
637
+ if (item.wideReads) notes.push(`wide-read:${item.wideReads}`);
638
+ if (item.broadGrepCalls) notes.push(`broad-call:${item.broadGrepCalls}`);
639
+ if (item.largeEditCalls) notes.push(`large-edit:${item.largeEditCalls}`);
640
+ if (item.nextCalls) notes.push(`next_call:${item.nextCalls}/${item.count}`);
641
+ if (item.nextCallTotal && item.nextCallTotal !== item.nextCalls) notes.push(`next_call_total:${item.nextCallTotal}`);
642
+ if (item.nextCallMisses && Object.keys(item.nextCallMisses).length > 0) notes.push(`missing_next_call:${Object.entries(item.nextCallMisses).map(([k, v]) => `${k}=${v}`).join(',')}`);
643
+ if (item.outputBytes?.p90) notes.push(`out-p90:${item.outputBytes.p90}B`);
644
+ if (item.outputLines?.p90) notes.push(`lines-p90:${item.outputLines.p90}`);
645
+ const errors = item.resultKinds.error || 0;
646
+ if (errors) notes.push(`errors:${errors}`);
647
+ console.log(`${item.tool.padEnd(18)} ${String(item.count).padStart(5)} ${String(item.p50).padStart(8)} ${String(item.p90).padStart(8)} ${String(item.max).padStart(9)} ${String(item.total).padStart(10)} ${notes.join(' ')}`);
648
+ }
649
+ if (eventClusters.length > 0) {
650
+ console.log('');
651
+ console.log('event clusters');
652
+ for (const cluster of eventClusters) {
653
+ const lines = Object.keys(cluster.nearestLines || {}).slice(0, 4).join(',');
654
+ console.log(`${cluster.tool} code=${cluster.code || '-'} count=${cluster.count} path=${cluster.path || '(none)'}${lines ? ` nearest=${lines}` : ''}`);
655
+ }
656
+ }
657
+ if (repeatClusters.length > 0) {
658
+ console.log('');
659
+ console.log('repeat clusters');
660
+ for (const cluster of repeatClusters) {
661
+ console.log(`${cluster.tool} count=${cluster.count} session=${cluster.session} span=${cluster.spanMs}ms`);
662
+ }
663
+ }
664
+ if (output.routes.length > 0) {
665
+ console.log('');
666
+ console.log('route scenario winner routes failed notes');
667
+ for (const item of output.routes) {
668
+ const winner = item.routes.find((route) => route.route === item.winner);
669
+ const notes = winner ? `score:${winner.score} calls:${winner.toolCalls} broad:${winner.broadGrepCalls || 0} largeEdit:${winner.largeEditCalls || 0} out:${winner.outputBytes}B` : '';
670
+ console.log(`${item.scenario.padEnd(31)} ${(item.winner || '(none)').padEnd(22)} ${String(item.count).padStart(6)} ${String(item.failures).padStart(7)} ${notes}`);
671
+ }
672
+ }
673
+ console.log('');
674
+ console.log(`provider send p50/p90/max ms: ${output.loop.sends.p50}/${output.loop.sends.p90}/${output.loop.sends.max}`);
675
+ console.log(`provider body p50/p90/max bytes: ${output.loop.bodyBytes.p50}/${output.loop.bodyBytes.p90}/${output.loop.bodyBytes.max}`);
676
+ console.log(`trim changed: ${output.trim.changed}/${output.trim.count}, bytes saved: ${output.trim.bytesSaved}`);
677
+ console.log(`compression rows: ${output.compression.count}, bytes saved: ${output.compression.bytesSaved}`);
678
+ if (output.recommendations.length > 0) {
679
+ console.log('');
680
+ console.log('recommendations:');
681
+ for (const line of output.recommendations) console.log(`- ${line}`);
682
+ }
683
+ if (CHECK) {
684
+ console.log('');
685
+ console.log(output.failures.length === 0 ? 'thresholds: pass' : `thresholds: fail ${output.failures.length}`);
686
+ }
687
+ }
688
+
689
+ if (CHECK && output.failures?.length > 0) {
690
+ process.exit(1);
691
+ }