mixdog 0.8.1 → 0.9.0

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 (280) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +205 -33
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1222 -90
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2212 -446
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +110 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +76 -28
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  114. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  115. package/src/runtime/agent/orchestrator/session/loop.mjs +840 -612
  116. package/src/runtime/agent/orchestrator/session/manager.mjs +1574 -459
  117. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  118. package/src/runtime/agent/orchestrator/session/store.mjs +67 -29
  119. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  120. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  121. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  122. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  123. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  124. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  125. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  126. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  127. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  128. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  129. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  130. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  131. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  132. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  133. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  137. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  138. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  139. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  140. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  141. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  142. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  143. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  144. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  145. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  146. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  147. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  148. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  149. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  150. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  151. package/src/runtime/channels/index.mjs +152 -24
  152. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  153. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  154. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  155. package/src/runtime/channels/lib/webhook.mjs +1 -1
  156. package/src/runtime/channels/tool-defs.mjs +29 -29
  157. package/src/runtime/memory/index.mjs +520 -98
  158. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  159. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  160. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  161. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  162. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  163. package/src/runtime/memory/lib/memory-cycle1.mjs +28 -7
  164. package/src/runtime/memory/lib/memory-cycle2.mjs +4 -5
  165. package/src/runtime/memory/lib/memory-cycle3.mjs +2 -2
  166. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  167. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  168. package/src/runtime/memory/lib/memory.mjs +88 -9
  169. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  170. package/src/runtime/memory/lib/pg/adapter.mjs +1 -1
  171. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  172. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  173. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  174. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  175. package/src/runtime/memory/tool-defs.mjs +46 -37
  176. package/src/runtime/search/index.mjs +102 -466
  177. package/src/runtime/search/lib/web-tools.mjs +45 -25
  178. package/src/runtime/search/tool-defs.mjs +16 -23
  179. package/src/runtime/shared/abort-controller.mjs +1 -1
  180. package/src/runtime/shared/atomic-file.mjs +4 -3
  181. package/src/runtime/shared/background-tasks.mjs +122 -11
  182. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  183. package/src/runtime/shared/config.mjs +7 -4
  184. package/src/runtime/shared/err-text.mjs +131 -4
  185. package/src/runtime/shared/llm/cost.mjs +2 -2
  186. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  187. package/src/runtime/shared/llm/index.mjs +34 -11
  188. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  189. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  190. package/src/runtime/shared/singleton-owner.mjs +104 -0
  191. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  192. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  193. package/src/runtime/shared/tool-surface.mjs +624 -95
  194. package/src/runtime/shared/user-data-guard.mjs +0 -2
  195. package/src/standalone/agent-task-status.mjs +203 -0
  196. package/src/standalone/agent-task-status.test.mjs +76 -0
  197. package/src/standalone/agent-tool.mjs +1913 -0
  198. package/src/standalone/channel-worker.mjs +370 -14
  199. package/src/standalone/explore-tool.mjs +165 -70
  200. package/src/standalone/folder-dialog.mjs +314 -0
  201. package/src/standalone/hook-bus.mjs +898 -22
  202. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  203. package/src/standalone/projects.mjs +226 -0
  204. package/src/standalone/provider-admin.mjs +41 -24
  205. package/src/standalone/seeds.mjs +2 -69
  206. package/src/standalone/usage-dashboard.mjs +96 -8
  207. package/src/tui/App.jsx +4788 -2151
  208. package/src/tui/components/AnsiText.jsx +39 -28
  209. package/src/tui/components/ContextPanel.jsx +14 -4
  210. package/src/tui/components/Markdown.jsx +43 -77
  211. package/src/tui/components/MarkdownTable.jsx +9 -184
  212. package/src/tui/components/Message.jsx +28 -11
  213. package/src/tui/components/Picker.jsx +95 -56
  214. package/src/tui/components/PromptInput.jsx +367 -239
  215. package/src/tui/components/QueuedCommands.jsx +1 -1
  216. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  217. package/src/tui/components/Spinner.jsx +67 -38
  218. package/src/tui/components/StatusLine.jsx +606 -38
  219. package/src/tui/components/TextEntryPanel.jsx +128 -9
  220. package/src/tui/components/ToolExecution.jsx +592 -362
  221. package/src/tui/components/TurnDone.jsx +3 -3
  222. package/src/tui/components/UsagePanel.jsx +3 -5
  223. package/src/tui/components/tool-output-format.mjs +365 -0
  224. package/src/tui/components/tool-output-format.test.mjs +220 -0
  225. package/src/tui/dist/index.mjs +8826 -2390
  226. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  227. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  228. package/src/tui/engine.mjs +1455 -279
  229. package/src/tui/figures.mjs +21 -40
  230. package/src/tui/index.jsx +75 -31
  231. package/src/tui/input-editing.mjs +25 -0
  232. package/src/tui/markdown/format-token.mjs +511 -68
  233. package/src/tui/markdown/format-token.test.mjs +216 -0
  234. package/src/tui/markdown/render-ansi.mjs +94 -0
  235. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  236. package/src/tui/markdown/stream-fence.mjs +34 -0
  237. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  238. package/src/tui/markdown/table-layout.mjs +250 -0
  239. package/src/tui/paste-attachments.mjs +0 -7
  240. package/src/tui/spinner-verbs.mjs +1 -2
  241. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  242. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  243. package/src/tui/theme.mjs +746 -24
  244. package/src/tui/time-format.mjs +1 -1
  245. package/src/tui/transcript-tool-failures.mjs +67 -0
  246. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  247. package/src/ui/ansi.mjs +1 -2
  248. package/src/ui/markdown.mjs +85 -26
  249. package/src/ui/markdown.test.mjs +70 -0
  250. package/src/ui/model-display.mjs +121 -0
  251. package/src/ui/session-stats.mjs +44 -0
  252. package/src/ui/statusline-context-label.test.mjs +15 -0
  253. package/src/ui/statusline.mjs +386 -178
  254. package/src/ui/tool-card.mjs +2 -13
  255. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  256. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  257. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  258. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  259. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  260. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  261. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  262. package/src/workflows/default/WORKFLOW.md +12 -5
  263. package/src/workflows/default/workflow.json +0 -1
  264. package/src/workflows/solo/WORKFLOW.md +15 -0
  265. package/src/workflows/solo/workflow.json +7 -0
  266. package/vendor/ink/build/output.js +6 -1
  267. package/src/agents/scheduler-task.md +0 -3
  268. package/src/agents/web-researcher/AGENT.md +0 -3
  269. package/src/agents/web-researcher/agent.json +0 -6
  270. package/src/agents/webhook-handler.md +0 -3
  271. package/src/rules/bridge/00-common.md +0 -5
  272. package/src/rules/bridge/30-explorer.md +0 -4
  273. package/src/rules/lead/00-tool-lead.md +0 -5
  274. package/src/rules/shared/00-language.md +0 -3
  275. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  276. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  277. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  278. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  279. package/src/standalone/bridge-tool.mjs +0 -1414
  280. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -4,7 +4,6 @@
4
4
  "description": "Default Mixdog agent workflow.",
5
5
  "entry": "WORKFLOW.md",
6
6
  "agents": [
7
- "web-researcher",
8
7
  "maintainer",
9
8
  "worker",
10
9
  "heavy-worker",
@@ -0,0 +1,15 @@
1
+ # Solo Workflow
2
+
3
+ 1. Plan — Lead discusses the request with the user, forms a plan, and waits for
4
+ approval before execution.
5
+ 2. Execute — Lead performs all implementation, research, debugging, review, and
6
+ verification work directly. Delegation to any agent is forbidden.
7
+ 3. Verify — Lead checks the result directly, fixes issues directly, and repeats
8
+ until the work is clean or a blocker must be reported.
9
+ 4. Report — Lead summarizes the final state, verification result, and any
10
+ remaining risk or requested next step.
11
+
12
+ Delegation rule:
13
+ - Do not delegate, spawn, send, or ask any agent to perform work.
14
+ - Ignore any available-agent section while this workflow is active; all work is
15
+ handled by Lead directly.
@@ -0,0 +1,7 @@
1
+ {
2
+ "id": "solo",
3
+ "name": "Solo",
4
+ "description": "Lead-only workflow; delegation to any agent is forbidden.",
5
+ "entry": "WORKFLOW.md",
6
+ "agents": []
7
+ }
@@ -308,7 +308,12 @@ export default class Output {
308
308
  // [mixdog fork] Snapshot per-row, column-indexed cell values so the App
309
309
  // can compute word boundaries (double-click select) without retaining
310
310
  // this Output instance, which is created fresh per render and discarded.
311
- const plainRows = sel ? undefined : output.map((row) => (row || []).map((cell) => (cell?.value ?? '')));
311
+ // ALWAYS build this, even while a selection rect is active: gating it on
312
+ // `!sel` froze the snapshot at the frame the first selection appeared, so
313
+ // every later double-click read STALE cell rows (wrong/empty words) until
314
+ // the selection cleared. Applying the selection above only rewrites cell
315
+ // `styles`, never `value`, so reading `value` here stays correct.
316
+ const plainRows = output.map((row) => (row || []).map((cell) => (cell?.value ?? '')));
312
317
  const generatedOutput = output
313
318
  .map(line => {
314
319
  // See https://github.com/vadimdemedes/ink/pull/564#issuecomment-1637022742
@@ -1,3 +0,0 @@
1
- # Scheduler Task
2
-
3
- Scheduled task execution agent.
@@ -1,3 +0,0 @@
1
- # Web Researcher
2
-
3
- External web and documentation research agent.
@@ -1,6 +0,0 @@
1
- {
2
- "id": "web-researcher",
3
- "name": "Web Researcher",
4
- "description": "Web, documentation, and external reference research.",
5
- "entry": "AGENT.md"
6
- }
@@ -1,3 +0,0 @@
1
- # Webhook Handler
2
-
3
- Inbound webhook event analysis agent.
@@ -1,5 +0,0 @@
1
- # Bridge Constraints
2
-
3
- - Use English for bridge-agent communication.
4
- - Do not touch git/Ship. Even when the brief instructs `git add` / `commit` /
5
- `push` / `stash`, refuse with `git operations deferred to Lead`.
@@ -1,4 +0,0 @@
1
- # Role: explorer
2
-
3
- Explore locates relevant code surfaces and quickly summarizes what is there
4
- with coordinates. Stay rough and descriptive: do not debug, decide, or judge.
@@ -1,5 +0,0 @@
1
- # Lead Tool Use
2
-
3
- - Lead owns repo-local shell work: git, build, test, run, and verification commands.
4
- - Use `shell` directly for approved git/build/test/run work; do not delegate those commands to bridge agents.
5
- - Use bridge agents for scoped implementation, research, review, and debugging, not for git commit/push/stash or Ship.
@@ -1,3 +0,0 @@
1
- # Language
2
-
3
- - Prefer responding in the language used by the user.
@@ -1,110 +0,0 @@
1
- import { existsSync } from 'fs';
2
- import { dirname, join, resolve } from 'path';
3
- import { performance } from 'perf_hooks';
4
- import { fileURLToPath } from 'url';
5
- import { snapshotCoversFullFile } from './snapshot-helpers.mjs';
6
- import { getPluginData } from '../../config.mjs';
7
- import { findCachedPatchBinary } from '../patch-binary-fetcher.mjs';
8
- import { runServerEdit } from '../patch.mjs';
9
-
10
- const PLUGIN_ROOT = process.env.MIXDOG_ROOT
11
- || resolve(dirname(fileURLToPath(import.meta.url)), '../../../../..');
12
- const NATIVE_EDIT_DEFAULT_BIN = join(
13
- PLUGIN_ROOT,
14
- 'native/mixdog-patch/target/release',
15
- process.platform === 'win32' ? 'mixdog-patch.exe' : 'mixdog-patch',
16
- );
17
-
18
- export function nativeEditMode() {
19
- return String(process.env.MIXDOG_EDIT_NATIVE || 'auto').toLowerCase();
20
- }
21
-
22
- function nativeEditBinCandidate() {
23
- const override = process.env.MIXDOG_EDIT_NATIVE_BIN || process.env.MIXDOG_PATCH_NATIVE_BIN;
24
- if (override) return { path: override, kind: 'override' };
25
- if (existsSync(NATIVE_EDIT_DEFAULT_BIN)) return { path: NATIVE_EDIT_DEFAULT_BIN, kind: 'local' };
26
- const cached = findCachedPatchBinary(getPluginData());
27
- if (cached) return { path: cached, kind: 'cached' };
28
- return { path: NATIVE_EDIT_DEFAULT_BIN, kind: 'missing' };
29
- }
30
-
31
- export function nativeEditBinPath() {
32
- return nativeEditBinCandidate().path;
33
- }
34
-
35
- export function nativeEditShouldAttempt({ editSnapshot, oldStr, newStr, preloadedContent, preloadedRawBuf }) {
36
- const mode = nativeEditMode();
37
- if (/^(0|false|no|off|js|legacy)$/i.test(mode)) return false;
38
- const forcedNative = /^(1|true|yes|on|native)$/i.test(mode);
39
- const candidate = nativeEditBinCandidate();
40
- if (!existsSync(candidate.path)) return false;
41
- // Cached release prebuilds are guaranteed valid for apply_patch, but older
42
- // manifests (currently v0.6.5 in clean CI) predate the EDIT server protocol.
43
- // In auto mode, native edit is only an acceleration, so require either a
44
- // local cargo build or an explicit override. If a user forces native mode,
45
- // still try the cached binary and surface any protocol failure.
46
- if (candidate.kind === 'cached' && !forcedNative) return false;
47
- if (!snapshotCoversFullFile(editSnapshot)) return false;
48
- if (preloadedContent !== null || preloadedRawBuf !== null) return false;
49
- if (typeof oldStr !== 'string' || oldStr.length === 0 || typeof newStr !== 'string') return false;
50
- if (forcedNative) return true;
51
- // auto: the persistent server removed per-call spawn cost, so route edits to
52
- // native edit2 by default (B3). Same-size edits keep the JS in-place partial
53
- // write, which rewrites bytes in place instead of the whole file.
54
- const oldBytes = Buffer.byteLength(oldStr, 'utf-8');
55
- const newBytes = Buffer.byteLength(newStr, 'utf-8');
56
- if (oldBytes === newBytes) return false;
57
- return true;
58
- }
59
-
60
- export async function runNativeExactEdit({ fullPath, oldStr, newStr, replaceAll, signal = null }) {
61
- const forcedNative = /^(1|true|yes|on|native)$/i.test(nativeEditMode());
62
- if (signal?.aborted) {
63
- return { ok: false, fallback: false, error: signal.reason?.message || signal.reason || 'native edit aborted' };
64
- }
65
- const oldBuf = Buffer.from(oldStr, 'utf-8');
66
- const newBuf = Buffer.from(newStr, 'utf-8');
67
- const started = performance.now();
68
- try {
69
- // PARITY GUARD: the native engine MATCHES via the curly-quote fold
70
- // tier but applies new_string verbatim, silently downgrading the
71
- // file's typographic quotes (JS slow path preserves them via
72
- // preserveQuoteTypography). When old_string carries quote-family
73
- // chars — the only inputs that can land on the curly tier — probe
74
- // with a dry run (persistent server, ~ms) and defer curly-tier
75
- // matches to the JS editor.
76
- if (/["'‘’“”]/.test(oldStr)) {
77
- const probe = await runServerEdit({ fullPath, oldBuf, newBuf, replaceAll, dryRun: true, signal });
78
- if (probe?.tier === 'curly') {
79
- return { ok: false, fallback: true, error: 'curly-quote fold match — deferred to JS editor for typography preservation' };
80
- }
81
- }
82
- const res = await runServerEdit({ fullPath, oldBuf, newBuf, replaceAll, signal });
83
- return {
84
- ok: true,
85
- replacements: res.replacements,
86
- readMs: res.readMs,
87
- applyMs: res.applyMs,
88
- writeMs: res.writeMs,
89
- totalMs: res.totalMs,
90
- roundtripMs: res.roundtripMs ?? (performance.now() - started),
91
- stage: res.tier,
92
- contentHash: res.contentHash,
93
- };
94
- } catch (err) {
95
- if (err?.name === 'AbortError') {
96
- return { ok: false, fallback: false, error: err.message };
97
- }
98
- const msg = String(err?.message || err);
99
- // Tier misses and not-found map to a JS fallback; transport/spawn errors
100
- // also fall back so a server hiccup never blocks an edit. Older cached
101
- // mixdog-patch binaries (for example the v0.6.5 release prebuilds used
102
- // by clean CI before a local cargo build exists) support APPLY but not
103
- // the EDIT server protocol, and answer EDIT with the APPLY parser's
104
- // "bad header" error. In auto mode that means "native edit unavailable",
105
- // not "the edit is invalid", so fall through to the JS editor. When the
106
- // user explicitly forces native mode, keep surfacing the native failure.
107
- const fallback = !forcedNative && /old_string (?:not found|found \d+ times)|not valid UTF-8|no exact match|not found|server|bad header|bad edit header/i.test(msg);
108
- return { ok: false, fallback, error: msg };
109
- }
110
- }
@@ -1,67 +0,0 @@
1
- import * as nodeBuffer from 'node:buffer';
2
- import { readFileSync } from 'node:fs';
3
- import { readFile as readFileAsync } from 'node:fs/promises';
4
- import { decodeRawBufferForSnapshotCheck } from './builtin/snapshot-helpers.mjs';
5
-
6
- function keyForPath(fullPath) {
7
- const text = String(fullPath || '');
8
- return process.platform === 'win32' ? text.toLowerCase() : text;
9
- }
10
-
11
- export function isValidUtf8Buffer(buf) {
12
- if (typeof nodeBuffer.isUtf8 === 'function') return nodeBuffer.isUtf8(buf);
13
- if (typeof Buffer.isUtf8 === 'function') return Buffer.isUtf8(buf);
14
- return Buffer.from(buf.toString('utf-8'), 'utf-8').equals(buf);
15
- }
16
-
17
- export function createMutationContentCache() {
18
- const entries = new Map();
19
-
20
- function getOrCreate(fullPath) {
21
- const key = keyForPath(fullPath);
22
- let entry = entries.get(key);
23
- if (!entry) {
24
- entry = { fullPath, rawBuf: null, content: null };
25
- entries.set(key, entry);
26
- }
27
- return entry;
28
- }
29
-
30
- function seedBuffer(fullPath, rawBuf) {
31
- const entry = getOrCreate(fullPath);
32
- entry.rawBuf = rawBuf;
33
- entry.content = null;
34
- return entry;
35
- }
36
-
37
- function readBufferSync(fullPath) {
38
- const entry = getOrCreate(fullPath);
39
- if (!Buffer.isBuffer(entry.rawBuf)) entry.rawBuf = readFileSync(fullPath);
40
- return entry.rawBuf;
41
- }
42
-
43
- async function readBuffer(fullPath) {
44
- const entry = getOrCreate(fullPath);
45
- if (!Buffer.isBuffer(entry.rawBuf)) entry.rawBuf = await readFileAsync(fullPath);
46
- return entry.rawBuf;
47
- }
48
-
49
- function readTextSync(fullPath) {
50
- const entry = getOrCreate(fullPath);
51
- if (typeof entry.content !== 'string') {
52
- entry.content = decodeRawBufferForSnapshotCheck(readBufferSync(fullPath));
53
- }
54
- return entry.content;
55
- }
56
-
57
- function getEntry(fullPath) {
58
- return entries.get(keyForPath(fullPath)) || null;
59
- }
60
-
61
- function clear(fullPath = null) {
62
- if (fullPath) entries.delete(keyForPath(fullPath));
63
- else entries.clear();
64
- }
65
-
66
- return { seedBuffer, readBuffer, readBufferSync, readTextSync, getEntry, clear };
67
- }
@@ -1,120 +0,0 @@
1
- // bridge-trace-queries.mjs — SQL helpers for the bridge analytic tables.
2
- // All functions accept a `db` handle from openTraceDatabase() and return
3
- // plain row arrays. Query parameters are always $N — no interpolation.
4
-
5
- // ---------------------------------------------------------------------------
6
- // topSessionsByIteration(db, hours, limit)
7
- // Sessions with the highest max_iteration seen in the last N hours.
8
- // ---------------------------------------------------------------------------
9
- export async function topSessionsByIteration(db, hours = 24, limit = 20) {
10
- const { rows } = await db.query(`
11
- SELECT s.session_id,
12
- s.role,
13
- s.model,
14
- s.max_iteration,
15
- s.tool_calls,
16
- s.llm_calls,
17
- s.total_input_tokens,
18
- s.total_output_tokens,
19
- s.started_at,
20
- s.last_seen_at
21
- FROM bridge_sessions s
22
- WHERE s.last_seen_at >= now() - ($1 || ' hours')::interval
23
- ORDER BY s.max_iteration DESC, s.tool_calls DESC
24
- LIMIT $2
25
- `, [String(hours), limit])
26
- return rows
27
- }
28
-
29
- // ---------------------------------------------------------------------------
30
- // repeatToolCalls(db, sessionId)
31
- // Tool calls whose (tool_name, md5(tool_args::text)) combo appears ≥3 times
32
- // in a single session — strong signal for a tool-loop.
33
- // ---------------------------------------------------------------------------
34
- export async function repeatToolCalls(db, sessionId) {
35
- const { rows } = await db.query(`
36
- SELECT tool_name,
37
- md5(tool_args::text) AS args_hash,
38
- COUNT(*) AS call_count,
39
- MIN(ts) AS first_at,
40
- MAX(ts) AS last_at,
41
- (array_agg(tool_args ORDER BY ts))[1] AS sample_args
42
- FROM bridge_calls
43
- WHERE session_id = $1
44
- AND tool_name IS NOT NULL
45
- GROUP BY tool_name, md5(tool_args::text)
46
- HAVING COUNT(*) >= 3
47
- ORDER BY call_count DESC, tool_name
48
- `, [sessionId])
49
- return rows
50
- }
51
-
52
- // ---------------------------------------------------------------------------
53
- // mixedToolPattern(db, sessionId)
54
- // Sliding 3-tool window where grep and read alternate AND share a path token.
55
- // Returns the centre-row of each such window.
56
- // ---------------------------------------------------------------------------
57
- export async function mixedToolPattern(db, sessionId) {
58
- const { rows } = await db.query(`
59
- WITH ordered AS (
60
- SELECT id, ts, tool_name,
61
- tool_args->>'path' AS path,
62
- -- basename: last segment after splitting on '/'
63
- regexp_replace(tool_args->>'path', '^.*/', '') AS basename,
64
- LAG (tool_name, 1) OVER (ORDER BY ts, id) AS prev1,
65
- LAG (tool_name, 2) OVER (ORDER BY ts, id) AS prev2,
66
- LEAD(tool_name, 1) OVER (ORDER BY ts, id) AS next1,
67
- LAG (tool_args->>'path', 1) OVER (ORDER BY ts, id) AS prev_path,
68
- LEAD(tool_args->>'path', 1) OVER (ORDER BY ts, id) AS next_path,
69
- -- basenames of neighbours
70
- regexp_replace(LAG (tool_args->>'path', 1) OVER (ORDER BY ts, id), '^.*/', '') AS prev_base,
71
- regexp_replace(LEAD(tool_args->>'path', 1) OVER (ORDER BY ts, id), '^.*/', '') AS next_base
72
- FROM bridge_calls
73
- WHERE session_id = $1
74
- AND tool_name IN ('grep','read')
75
- )
76
- SELECT id, ts, tool_name, path
77
- FROM ordered
78
- WHERE -- true 3-tool alternation: centre plus BOTH flanking neighbours (window size = 3)
79
- (
80
- (tool_name = 'read' AND prev1 = 'grep' AND next1 = 'grep')
81
- OR (tool_name = 'grep' AND prev1 = 'read' AND next1 = 'read')
82
- )
83
- -- path-token sharing: basename or exact path matches across all 3 slots
84
- AND basename IS NOT NULL
85
- AND (
86
- -- exact path shared with both neighbours
87
- (path = prev_path AND path = next_path)
88
- -- or basename shared with both neighbours
89
- OR (basename = prev_base AND basename = next_base)
90
- -- or mixed: exact on one side, basename on other
91
- OR (path = prev_path AND basename = next_base)
92
- OR (path = next_path AND basename = prev_base)
93
- )
94
- ORDER BY ts, id
95
- `, [sessionId])
96
- return rows
97
- }
98
-
99
- // ---------------------------------------------------------------------------
100
- // tokenUsageByRole(db, hours)
101
- // Sum of input/output tokens grouped by role over the last N hours.
102
- // ---------------------------------------------------------------------------
103
- export async function tokenUsageByRole(db, hours = 24) {
104
- const { rows } = await db.query(`
105
- SELECT s.role,
106
- COUNT(DISTINCT l.session_id) AS sessions,
107
- SUM(l.input_tokens) AS total_input,
108
- SUM(l.output_tokens) AS total_output,
109
- SUM(l.cached_tokens) AS total_cached,
110
- SUM(l.cache_write_tokens) AS total_cache_write,
111
- AVG(l.input_tokens)::int AS avg_input_per_call,
112
- AVG(l.output_tokens)::int AS avg_output_per_call
113
- FROM bridge_llm l
114
- JOIN bridge_sessions s USING (session_id)
115
- WHERE l.ts >= now() - ($1 || ' hours')::interval
116
- GROUP BY s.role
117
- ORDER BY total_input DESC NULLS LAST
118
- `, [String(hours)])
119
- return rows
120
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * pid-cleanup.mjs — Orphaned bridge CLI process cleanup.
3
- * Used by server.mjs on startup and shutdown.
4
- */
5
- import fs from 'fs'
6
- import path from 'path'
7
- import os from 'os'
8
-
9
- const PID_DIR = path.join(os.tmpdir(), 'mixdog-bridge')
10
- const PID_FILE = path.join(PID_DIR, 'bridge-pids.json')
11
-
12
- export function cleanupOrphanedPids() {
13
- let killed = 0
14
- try {
15
- const pids = JSON.parse(fs.readFileSync(PID_FILE, 'utf8'))
16
- for (const pid of pids) {
17
- try {
18
- process.kill(pid, 0)
19
- process.kill(pid, 'SIGTERM')
20
- process.stderr.write(`[bridge-cleanup] killed orphaned PID ${pid}\n`)
21
- killed++
22
- } catch {}
23
- }
24
- fs.writeFileSync(PID_FILE, JSON.stringify([]))
25
- } catch {}
26
- return killed
27
- }