mixdog 0.9.63 → 0.9.64

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 (403) hide show
  1. package/README.md +3 -0
  2. package/package.json +9 -2
  3. package/scripts/code-graph-description-contract.mjs +3 -3
  4. package/scripts/verify-release-assets.mjs +2 -2
  5. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
  6. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
  7. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  8. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  9. package/src/runtime/agent/orchestrator/config.mjs +19 -124
  10. package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
  11. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
  12. package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
  13. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
  14. package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
  15. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
  16. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
  17. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
  18. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
  19. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
  20. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
  24. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
  25. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
  27. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
  30. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
  32. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
  37. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
  39. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
  41. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
  46. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
  49. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
  50. package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
  51. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
  54. package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
  57. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
  59. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
  60. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
  61. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
  62. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
  64. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
  66. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
  68. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
  69. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
  70. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
  73. package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
  74. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
  75. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
  77. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  79. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  81. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
  83. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
  91. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
  92. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
  93. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
  96. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
  97. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
  99. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
  102. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
  105. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
  106. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
  107. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
  108. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
  109. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
  110. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
  111. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
  112. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
  115. package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
  116. package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
  119. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
  120. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
  121. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  123. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
  124. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
  127. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
  129. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
  130. package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
  131. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  132. package/src/runtime/channels/lib/telegram-format.mjs +1 -1
  133. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
  134. package/src/runtime/channels/tool-defs.mjs +2 -3
  135. package/src/runtime/memory/index.mjs +1 -5
  136. package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
  137. package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
  138. package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
  139. package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
  140. package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
  141. package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
  142. package/src/runtime/memory/lib/ko-morph.mjs +2 -2
  143. package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
  144. package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
  145. package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
  146. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
  147. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
  148. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
  149. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
  150. package/src/runtime/memory/lib/memory-embed.mjs +2 -6
  151. package/src/runtime/memory/lib/memory-log.mjs +8 -0
  152. package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
  153. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
  154. package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
  155. package/src/runtime/memory/lib/memory-score.mjs +1 -1
  156. package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
  157. package/src/runtime/memory/lib/memory.mjs +2 -6
  158. package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
  159. package/src/runtime/memory/lib/pg/process.mjs +1 -5
  160. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
  161. package/src/runtime/memory/lib/recall-format.mjs +2 -2
  162. package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
  163. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
  164. package/src/runtime/memory/lib/trace-store.mjs +2 -6
  165. package/src/runtime/search/lib/http-fetch.mjs +6 -6
  166. package/src/runtime/search/lib/web-tools.mjs +2 -2
  167. package/src/runtime/shared/buffered-appender.mjs +1 -1
  168. package/src/runtime/shared/child-guardian.mjs +1 -1
  169. package/src/runtime/shared/child-spawn-gate.mjs +1 -1
  170. package/src/runtime/shared/config.mjs +4 -4
  171. package/src/runtime/shared/launcher-control.mjs +9 -10
  172. package/src/runtime/shared/llm/index.mjs +5 -20
  173. package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
  174. package/src/runtime/shared/memory-snapshot.mjs +4 -4
  175. package/src/runtime/shared/pristine-execution.mjs +1 -1
  176. package/src/runtime/shared/process-lifecycle.mjs +0 -1
  177. package/src/runtime/shared/process-shutdown.mjs +1 -1
  178. package/src/runtime/shared/resource-admission.mjs +2 -2
  179. package/src/runtime/shared/schedule-session-run.mjs +1 -1
  180. package/src/runtime/shared/schedules-db.mjs +1 -1
  181. package/src/runtime/shared/service-discovery.mjs +2 -2
  182. package/src/runtime/shared/staged-update.mjs +6 -6
  183. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  184. package/src/runtime/shared/tool-primitives.mjs +1 -1
  185. package/src/runtime/shared/tool-surface.mjs +2 -2
  186. package/src/runtime/shared/update-checker.mjs +1 -1
  187. package/src/runtime/shared/user-cwd.mjs +3 -3
  188. package/src/runtime/shared/user-data-guard.mjs +4 -4
  189. package/src/runtime/shared/workspace-router.mjs +5 -5
  190. package/src/session-runtime/config-helpers.mjs +2 -3
  191. package/src/session-runtime/config-lifecycle.mjs +1 -1
  192. package/src/session-runtime/hitch-profile.mjs +1 -1
  193. package/src/session-runtime/lifecycle-api.mjs +6 -4
  194. package/src/session-runtime/model-recency.mjs +5 -5
  195. package/src/session-runtime/plugin-mcp.mjs +1 -1
  196. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  197. package/src/session-runtime/session-text.mjs +4 -4
  198. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  199. package/src/session-runtime/tool-catalog.mjs +6 -483
  200. package/src/session-runtime/tool-defs.mjs +1 -1
  201. package/src/standalone/agent-task-status.mjs +1 -1
  202. package/src/standalone/agent-tool/helpers.mjs +2 -2
  203. package/src/standalone/channel-admin.mjs +4 -4
  204. package/src/standalone/explore-tool.mjs +3 -3
  205. package/src/standalone/hook-bus/config.mjs +1 -1
  206. package/src/standalone/hook-bus/constants.mjs +1 -1
  207. package/src/standalone/hook-bus/rules.mjs +1 -1
  208. package/src/standalone/projects.mjs +1 -1
  209. package/src/standalone/provider-admin.mjs +2 -2
  210. package/src/tui/app/app-format.mjs +2 -2
  211. package/src/tui/app/clipboard.mjs +3 -3
  212. package/src/tui/app/input-parsers.mjs +1 -1
  213. package/src/tui/app/text-layout.mjs +1 -1
  214. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  215. package/src/tui/app/transcript-window.mjs +5 -347
  216. package/src/tui/app/use-mouse-input.mjs +1 -1
  217. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  218. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  219. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  220. package/src/tui/components/tool-output-format.mjs +7 -7
  221. package/src/tui/display-width.mjs +4 -4
  222. package/src/tui/dist/index.mjs +63 -97
  223. package/src/tui/engine/agent-envelope.mjs +5 -5
  224. package/src/tui/engine/notification-plan.mjs +1 -1
  225. package/src/tui/engine/queue-helpers.mjs +3 -3
  226. package/src/tui/engine/render-timing.mjs +4 -5
  227. package/src/tui/engine/session-api-ext.mjs +3 -3
  228. package/src/tui/engine/tool-result-status.mjs +5 -5
  229. package/src/tui/engine/tool-result-text.mjs +1 -1
  230. package/src/tui/engine/transcript-spill.mjs +585 -0
  231. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  232. package/src/tui/engine.mjs +2 -474
  233. package/src/tui/lib/voice-setup.mjs +1 -1
  234. package/src/tui/markdown/format-token.mjs +1 -1
  235. package/src/tui/markdown/render-ansi.mjs +1 -1
  236. package/src/tui/markdown/table-layout.mjs +4 -4
  237. package/src/tui/paste-attachments.mjs +1 -1
  238. package/src/tui/prompt-history-store.mjs +3 -3
  239. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  240. package/src/tui/theme.mjs +1 -1
  241. package/src/tui/transcript-tool-failures.mjs +2 -2
  242. package/src/ui/ansi.mjs +0 -9
  243. package/src/ui/statusline-format.mjs +2 -4
  244. package/scripts/_bench-cwc.json +0 -20
  245. package/scripts/_jitter-fuzz.jsx +0 -42
  246. package/scripts/_jitter-fuzz2.jsx +0 -30
  247. package/scripts/_jitter-probe.jsx +0 -35
  248. package/scripts/_jp2.jsx +0 -16
  249. package/scripts/_smoke_wd.mjs +0 -7
  250. package/scripts/abort-recovery-test.mjs +0 -175
  251. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  252. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  253. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  254. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  255. package/scripts/agent-loop-policy-test.mjs +0 -37
  256. package/scripts/agent-model-liveness-test.mjs +0 -763
  257. package/scripts/agent-parallel-smoke.mjs +0 -435
  258. package/scripts/agent-route-batch-test.mjs +0 -40
  259. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  260. package/scripts/agent-terminal-reap-test.mjs +0 -252
  261. package/scripts/agent-trace-io-test.mjs +0 -133
  262. package/scripts/ansi-color-capability-test.mjs +0 -90
  263. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  264. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  265. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  266. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  267. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  268. package/scripts/arg-guard-test.mjs +0 -93
  269. package/scripts/async-notify-settlement-test.mjs +0 -99
  270. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  271. package/scripts/background-task-meta-smoke.mjs +0 -38
  272. package/scripts/bench-run.mjs +0 -508
  273. package/scripts/boot-smoke.mjs +0 -137
  274. package/scripts/build-runtime-windows.ps1 +0 -242
  275. package/scripts/channel-daemon-smoke.mjs +0 -1103
  276. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  277. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  278. package/scripts/code-graph-dispatch-test.mjs +0 -96
  279. package/scripts/code-graph-root-federation-test.mjs +0 -273
  280. package/scripts/compact-active-turn-test.mjs +0 -68
  281. package/scripts/compact-file-reattach-test.mjs +0 -88
  282. package/scripts/compact-pressure-test.mjs +0 -788
  283. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  284. package/scripts/compact-recall-digest-test.mjs +0 -57
  285. package/scripts/compact-smoke.mjs +0 -1017
  286. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  287. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  288. package/scripts/context-mcp-metering-test.mjs +0 -1406
  289. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  290. package/scripts/deferred-tool-loading-test.mjs +0 -250
  291. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  292. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  293. package/scripts/embedding-worker-exit-test.mjs +0 -76
  294. package/scripts/execution-completion-dedup-test.mjs +0 -205
  295. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  296. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  297. package/scripts/explore-bench-tmp.mjs +0 -36
  298. package/scripts/explore-bench.mjs +0 -248
  299. package/scripts/explore-prompt-policy-test.mjs +0 -256
  300. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  301. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  302. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  303. package/scripts/freevar-smoke.mjs +0 -98
  304. package/scripts/gemini-provider-test.mjs +0 -1448
  305. package/scripts/generate-oc-icons.mjs +0 -11
  306. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  307. package/scripts/headless-pristine-execution-test.mjs +0 -614
  308. package/scripts/hook-bus-test.mjs +0 -552
  309. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  310. package/scripts/internal-comms-bench-test.mjs +0 -226
  311. package/scripts/internal-comms-bench.mjs +0 -853
  312. package/scripts/internal-comms-smoke.mjs +0 -277
  313. package/scripts/internal-tools-normalization-test.mjs +0 -62
  314. package/scripts/interrupted-turn-history-test.mjs +0 -399
  315. package/scripts/lead-workflow-smoke.mjs +0 -592
  316. package/scripts/lifecycle-api-test.mjs +0 -137
  317. package/scripts/live-share-test.mjs +0 -148
  318. package/scripts/live-worker-smoke.mjs +0 -333
  319. package/scripts/log-writer-guard-smoke.mjs +0 -131
  320. package/scripts/maintenance-default-routes-test.mjs +0 -164
  321. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  322. package/scripts/max-output-recovery-test.mjs +0 -368
  323. package/scripts/mcp-client-normalization-test.mjs +0 -45
  324. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  325. package/scripts/memory-core-input-test.mjs +0 -167
  326. package/scripts/memory-cycle-routing-test.mjs +0 -111
  327. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  328. package/scripts/memory-pg-recovery-test.mjs +0 -59
  329. package/scripts/memory-rule-contract-test.mjs +0 -93
  330. package/scripts/model-list-sanitize-test.mjs +0 -37
  331. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  332. package/scripts/native-edit-wire-test.mjs +0 -152
  333. package/scripts/notify-completion-mirror-test.mjs +0 -73
  334. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  335. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  336. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  337. package/scripts/output-style-bench.mjs +0 -292
  338. package/scripts/output-style-smoke.mjs +0 -145
  339. package/scripts/parent-abort-link-test.mjs +0 -90
  340. package/scripts/patch-binary-cache-test.mjs +0 -181
  341. package/scripts/path-suffix-test.mjs +0 -57
  342. package/scripts/pending-completion-drop-test.mjs +0 -239
  343. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  344. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  345. package/scripts/process-lifecycle-test.mjs +0 -461
  346. package/scripts/prompt-immediate-render-test.mjs +0 -89
  347. package/scripts/prompt-input-parity-test.mjs +0 -145
  348. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  349. package/scripts/provider-contract-test.mjs +0 -632
  350. package/scripts/provider-stream-stall-test.mjs +0 -276
  351. package/scripts/provider-toolcall-test.mjs +0 -3063
  352. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  353. package/scripts/recall-bench.mjs +0 -359
  354. package/scripts/resource-admission-test.mjs +0 -792
  355. package/scripts/result-classification-test.mjs +0 -75
  356. package/scripts/rg-runner-test.mjs +0 -240
  357. package/scripts/routing-corpus-test.mjs +0 -349
  358. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  359. package/scripts/session-bench-cache-break-test.mjs +0 -102
  360. package/scripts/session-bench.mjs +0 -1708
  361. package/scripts/session-context-bench.mjs +0 -344
  362. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  363. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  364. package/scripts/session-ingest-smoke.mjs +0 -177
  365. package/scripts/session-orphan-sweep-test.mjs +0 -109
  366. package/scripts/set-effort-config-test.mjs +0 -41
  367. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  368. package/scripts/shell-hardening-test.mjs +0 -209
  369. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  370. package/scripts/ship-mode-test.mjs +0 -98
  371. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  372. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  373. package/scripts/smoke-loop-report.mjs +0 -221
  374. package/scripts/smoke-loop.mjs +0 -202
  375. package/scripts/smoke-runtime-negative.ps1 +0 -106
  376. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  377. package/scripts/steering-drain-buckets-test.mjs +0 -467
  378. package/scripts/steering-fold-provenance-test.mjs +0 -71
  379. package/scripts/streaming-tail-window-test.mjs +0 -175
  380. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  381. package/scripts/task-bench.mjs +0 -205
  382. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  383. package/scripts/termio-input-smoke.mjs +0 -199
  384. package/scripts/tool-result-hook-test.mjs +0 -48
  385. package/scripts/tool-smoke.mjs +0 -2453
  386. package/scripts/tool-tui-presentation-test.mjs +0 -286
  387. package/scripts/toolcall-args-test.mjs +0 -239
  388. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  389. package/scripts/tui-background-failure-smoke.mjs +0 -73
  390. package/scripts/tui-perf-run.ps1 +0 -26
  391. package/scripts/tui-render-smoke.mjs +0 -90
  392. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  393. package/scripts/tui-runtime-load-bench.mjs +0 -50
  394. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  395. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  396. package/scripts/tui-transcript-perf-test.mjs +0 -934
  397. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  398. package/scripts/verify-release-assets-test.mjs +0 -281
  399. package/scripts/web-fetch-routing-test.mjs +0 -158
  400. package/scripts/webhook-smoke.mjs +0 -201
  401. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  402. package/scripts/worker-notify-rejection-test.mjs +0 -51
  403. package/scripts/write-backpressure-test.mjs +0 -147
@@ -1,853 +0,0 @@
1
- #!/usr/bin/env node
2
- // internal-comms-bench.mjs — live A/B token measurement for internal-comms rules.
3
- //
4
- // Variant A (verbose): prior committed blobs via `git show HEAD:src/<rule>`.
5
- // Variant B (optimized): current on-disk src/ copied into a temp PLUGIN_ROOT.
6
- // Only the listed rule files differ between variants; everything else matches.
7
- //
8
- // node scripts/internal-comms-bench.mjs
9
- // node scripts/internal-comms-bench.mjs --run [--model grok] [--provider P] [--json]
10
- import { execFileSync, spawnSync } from 'node:child_process';
11
- import {
12
- copyFileSync,
13
- cpSync,
14
- existsSync,
15
- mkdirSync,
16
- mkdtempSync,
17
- readFileSync,
18
- readdirSync,
19
- rmSync,
20
- writeFileSync,
21
- } from 'node:fs';
22
- import { homedir } from 'node:os';
23
- import { dirname, join, resolve } from 'node:path';
24
- import { fileURLToPath, pathToFileURL } from 'node:url';
25
-
26
- const __dir = dirname(fileURLToPath(import.meta.url));
27
- const REPO_ROOT = resolve(__dir, '..');
28
- const PLUGIN_ROOT = join(REPO_ROOT, 'src');
29
- const HEADLESS = pathToFileURL(resolve(__dir, '../src/headless-role.mjs')).href;
30
-
31
- const RULE_FILES = [
32
- 'rules/agent/00-core.md',
33
- 'rules/agent/00-common.md',
34
- 'rules/agent/20-skip-protocol.md',
35
- 'agents/worker/AGENT.md',
36
- 'agents/heavy-worker/AGENT.md',
37
- 'agents/reviewer/AGENT.md',
38
- 'agents/debugger/AGENT.md',
39
- 'workflows/default/WORKFLOW.md',
40
- 'workflows/solo/WORKFLOW.md',
41
- 'rules/lead/01-general.md',
42
- 'rules/lead/02-channels.md',
43
- 'rules/lead/lead-tool.md',
44
- 'rules/lead/lead-brief.md',
45
- ];
46
-
47
- const DEFAULT_WORKER_PROMPT =
48
- 'In math.js add an exported function add(a, b) that returns a + b. Keep the existing mul export unchanged. Use apply_patch only. Stop when the function exists; hand off with outcome and file:line.';
49
-
50
- // Lead-mode task: FORCES delegation so the real internal-comms flow runs
51
- // (Lead writes a brief -> worker returns a handoff -> reviewer verifies).
52
- // Identical across variants A/B; only the on-disk rule files differ.
53
- const DEFAULT_LEAD_PROMPT = [
54
- 'You are the Lead in an automation benchmark. A file named math.js already exists in your working directory.',
55
- 'This is the planning turn: give a concise plan only, then wait for a later explicit approval. Do not edit, mutate state, or delegate in this turn.',
56
- 'After approval, do NOT edit any file yourself or call apply_patch yourself. Delegate every implementation step: first a worker adds exported add(a, b) returning a + b to math.js while preserving mul, then a reviewer verifies both exports. Finally reply with a one-line outcome plus math.js:line.',
57
- ].join(' ');
58
- export const DEFAULT_LEAD_APPROVAL_PROMPT = 'Proceed with the latest plan.';
59
- export const CHILD_RESULT_START = '__INTERNAL_COMMS_BENCH_RESULT_START__';
60
- export const CHILD_RESULT_END = '__INTERNAL_COMMS_BENCH_RESULT_END__';
61
-
62
- const MODEL_ALIASES = {
63
- opus: { provider: 'anthropic-oauth', model: 'claude-opus-4-8' },
64
- sonnet: { provider: 'anthropic-oauth', model: 'claude-sonnet-5' },
65
- gpt: { provider: 'openai-oauth', model: 'gpt-5.5' },
66
- 'gpt-5.5': { provider: 'openai-oauth', model: 'gpt-5.5' },
67
- grok: { provider: 'grok-oauth', model: 'grok-composer-2.5-fast' },
68
- };
69
-
70
- const AUTH_ARTIFACT_BY_PROVIDER = {
71
- 'grok-oauth': ['grok-oauth.json', 'grok-oauth-models.json'],
72
- 'anthropic-oauth': ['anthropic-oauth-credentials.json', 'anthropic-oauth-models.json'],
73
- 'openai-oauth': ['openai-oauth.json', 'openai-oauth-models.json'],
74
- };
75
-
76
- const INITIAL_MATH_JS = `export function mul(a, b) {
77
- return a * b;
78
- }
79
- `;
80
-
81
- function argValue(name, fallback = null) {
82
- const idx = process.argv.indexOf(name);
83
- if (idx >= 0 && idx + 1 < process.argv.length) return process.argv[idx + 1];
84
- const pref = `${name}=`;
85
- const hit = process.argv.find((a) => a.startsWith(pref));
86
- return hit ? hit.slice(pref.length) : fallback;
87
- }
88
- function hasFlag(name) { return process.argv.includes(name); }
89
-
90
- function resolveModelOpts(modelArg, providerArg) {
91
- const key = String(modelArg || '').trim().toLowerCase();
92
- if (MODEL_ALIASES[key] && !providerArg) return { ...MODEL_ALIASES[key] };
93
- return { provider: providerArg || null, model: modelArg || null };
94
- }
95
-
96
- function defaultUserDataDir() {
97
- return process.env.MIXDOG_DATA_DIR || join(process.env.MIXDOG_HOME || join(homedir(), '.mixdog'), 'data');
98
- }
99
-
100
- function readUnifiedConfig(dataDir) {
101
- try {
102
- const unified = JSON.parse(readFileSync(join(dataDir, 'mixdog-config.json'), 'utf8'));
103
- return unified && typeof unified === 'object' ? unified : {};
104
- } catch { return {}; }
105
- }
106
-
107
- function gitPathForRule(relFromSrc) {
108
- return `src/${String(relFromSrc).replace(/\\/g, '/')}`;
109
- }
110
-
111
- function readPriorRuleBlob(relFromSrc) {
112
- const gitPath = gitPathForRule(relFromSrc);
113
- try {
114
- return execFileSync('git', ['show', `HEAD:${gitPath}`], {
115
- cwd: REPO_ROOT,
116
- encoding: 'utf8',
117
- maxBuffer: 8 * 1024 * 1024,
118
- });
119
- } catch {
120
- return null;
121
- }
122
- }
123
-
124
- function readCurrentRule(relFromSrc) {
125
- return readFileSync(join(PLUGIN_ROOT, relFromSrc), 'utf8');
126
- }
127
-
128
- function ruleVariantBytes() {
129
- const rows = [];
130
- for (const rel of RULE_FILES) {
131
- const prior = readPriorRuleBlob(rel);
132
- const current = readCurrentRule(rel);
133
- const aText = prior != null ? prior : current;
134
- rows.push({
135
- file: rel,
136
- a_chars: aText.length,
137
- b_chars: current.length,
138
- prior_from_git: prior != null,
139
- });
140
- }
141
- return rows;
142
- }
143
-
144
- function authArtifactNamesForSandbox(realDataDir, provider) {
145
- const names = new Set();
146
- for (const file of AUTH_ARTIFACT_BY_PROVIDER[provider] || []) names.add(file);
147
- try {
148
- for (const entry of readdirSync(realDataDir, { withFileTypes: true })) {
149
- if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
150
- if (/oauth/i.test(entry.name) || /credentials/i.test(entry.name)) names.add(entry.name);
151
- }
152
- } catch { /* missing real data dir */ }
153
- return [...names];
154
- }
155
-
156
- function copyAuthArtifacts(realDataDir, sandboxDataDir, provider) {
157
- const copied = [];
158
- const skipped = [];
159
- for (const name of authArtifactNamesForSandbox(realDataDir, provider)) {
160
- const src = join(realDataDir, name);
161
- const dest = join(sandboxDataDir, name);
162
- if (!existsSync(src)) {
163
- skipped.push(name);
164
- continue;
165
- }
166
- try {
167
- copyFileSync(src, dest);
168
- copied.push(name);
169
- } catch {
170
- skipped.push(name);
171
- }
172
- }
173
- return { copied, skipped };
174
- }
175
-
176
- function materializePluginRoot(variant, sandboxRoot) {
177
- const pluginRoot = join(sandboxRoot, `plugin-${variant}`);
178
- cpSync(PLUGIN_ROOT, pluginRoot, { recursive: true });
179
- if (variant === 'A') {
180
- for (const rel of RULE_FILES) {
181
- const prior = readPriorRuleBlob(rel);
182
- if (prior != null) writeFileSync(join(pluginRoot, rel), prior, 'utf8');
183
- }
184
- }
185
- return pluginRoot;
186
- }
187
-
188
- function prepareDataDir(sandboxRoot, variant, realDataDir, userUnified, provider) {
189
- const dataDir = join(sandboxRoot, `data-${variant}`);
190
- mkdirSync(join(dataDir, 'history'), { recursive: true });
191
- writeFileSync(join(dataDir, 'mixdog-config.json'), JSON.stringify(userUnified, null, 2));
192
- copyAuthArtifacts(realDataDir, dataDir, provider);
193
- return dataDir;
194
- }
195
-
196
- function resetTaskCwd(taskCwd) {
197
- writeFileSync(join(taskCwd, 'math.js'), INITIAL_MATH_JS, 'utf8');
198
- }
199
-
200
- function prepareTaskCwd(parentDir) {
201
- const taskCwd = mkdtempSync(join(parentDir, 'task-'));
202
- resetTaskCwd(taskCwd);
203
- return taskCwd;
204
- }
205
-
206
- function extractSessionId(text) {
207
- const s = String(text || '');
208
- const m = s.match(/sessionId:\s*(sess_[A-Za-z0-9_]+)/) || s.match(/\b(sess_[A-Za-z0-9_]+)/);
209
- return m ? m[1] : null;
210
- }
211
-
212
- function runHeadlessWorker({ pluginRoot, dataDir, taskCwd, prompt, provider, model, effort, fast }) {
213
- const driver = [
214
- `import { runHeadlessRole } from ${JSON.stringify(HEADLESS)};`,
215
- `const out = [];`,
216
- `const code = await runHeadlessRole({`,
217
- ` agent: 'worker',`,
218
- ` message: ${JSON.stringify(prompt)},`,
219
- ` provider: ${JSON.stringify(provider || null)},`,
220
- ` model: ${JSON.stringify(model || null)},`,
221
- ` effort: ${JSON.stringify(effort || null)},`,
222
- ` fast: ${JSON.stringify(fast === true)},`,
223
- ` cwd: ${JSON.stringify(taskCwd)},`,
224
- ` write: (t) => out.push(t),`,
225
- ` writeErr: (t) => process.stderr.write(t),`,
226
- `});`,
227
- `process.stdout.write(out.join(''));`,
228
- `process.exit(code);`,
229
- ].join('\n');
230
- const started = Date.now();
231
- let raw = '';
232
- let ok = false;
233
- try {
234
- raw = execFileSync('node', ['--input-type=module', '-e', driver], {
235
- encoding: 'utf8',
236
- maxBuffer: 64 * 1024 * 1024,
237
- env: {
238
- ...process.env,
239
- MIXDOG_ROOT: pluginRoot,
240
- MIXDOG_DATA_DIR: dataDir,
241
- },
242
- });
243
- ok = true;
244
- } catch (e) {
245
- raw = String(e.stdout || '') + String(e.stderr || '');
246
- ok = false;
247
- }
248
- return { sessionId: extractSessionId(raw), ok, ms: Date.now() - started, raw };
249
- }
250
-
251
- function readRows(path) {
252
- if (!existsSync(path)) return [];
253
- const rows = [];
254
- for (const line of readFileSync(path, 'utf8').split(/\r?\n/)) {
255
- if (!line) continue;
256
- try {
257
- rows.push(JSON.parse(line));
258
- } catch { /* tail */ }
259
- }
260
- return rows;
261
- }
262
-
263
- function payload(row) {
264
- return row?.payload && typeof row.payload === 'object' ? row.payload : {};
265
- }
266
-
267
- function field(row, name) {
268
- if (row && row[name] != null) return row[name];
269
- const p = payload(row);
270
- return p[name] != null ? p[name] : null;
271
- }
272
-
273
- function num(row, name) {
274
- const n = Number(field(row, name));
275
- return Number.isFinite(n) ? n : null;
276
- }
277
-
278
- function sessionId(row) {
279
- return String(row?.session_id || row?.sessionId || field(row, 'session_id') || '');
280
- }
281
-
282
- function sum(values) {
283
- return values.reduce((s, v) => s + (Number.isFinite(Number(v)) ? Number(v) : 0), 0);
284
- }
285
-
286
- function groupBy(rows, keyFn) {
287
- const map = new Map();
288
- for (const row of rows) {
289
- const key = keyFn(row);
290
- if (!key) continue;
291
- if (!map.has(key)) map.set(key, []);
292
- map.get(key).push(row);
293
- }
294
- return map;
295
- }
296
-
297
- function inferSessionMeta(rows) {
298
- const sorted = [...rows].sort((a, b) => Number(a.ts || 0) - Number(b.ts || 0));
299
- const preset = sorted.find((r) => r.kind === 'preset_assign');
300
- const usage = [...sorted].reverse().find((r) => r.kind === 'usage_raw' || r.kind === 'usage');
301
- const tool = sorted.find((r) => r.kind === 'tool');
302
- const last = sorted[sorted.length - 1] || {};
303
- const tsValues = sorted.map((r) => Number(r.ts || 0)).filter((n) => n > 0);
304
- return {
305
- session_id: sessionId(last),
306
- parent_session_id: field(preset, 'parent_session_id') || field(preset, 'parentSessionId') || null,
307
- agent: field(preset, 'agent') || field(tool, 'agent') || field(usage, 'sourceName') || field(last, 'sourceName') || null,
308
- min_ts: tsValues.length ? Math.min(...tsValues) : null,
309
- max_ts: tsValues.length ? Math.max(...tsValues) : null,
310
- };
311
- }
312
-
313
- function selectSessionFamily(sessionMetas, query) {
314
- const selected = sessionMetas.find((m) => m.session_id === query)
315
- || sessionMetas.find((m) => m.session_id.startsWith(query));
316
- if (!selected) return [];
317
- const root = selected.parent_session_id
318
- ? sessionMetas.find((m) => m.session_id === selected.parent_session_id) || selected
319
- : selected;
320
- const ids = new Set([root.session_id, selected.session_id]);
321
- for (const meta of sessionMetas) {
322
- if (meta.parent_session_id === root.session_id) ids.add(meta.session_id);
323
- }
324
- return [...ids].filter(Boolean);
325
- }
326
-
327
- function sumUsageForSession(dataDir, rootSessionId) {
328
- const tracePath = join(dataDir, 'history', 'agent-trace.jsonl');
329
- const rows = readRows(tracePath);
330
- const bySid = groupBy(rows, sessionId);
331
- const sessionMetas = [...bySid.entries()].map(([, srows]) => inferSessionMeta(srows));
332
- const family = selectSessionFamily(sessionMetas, rootSessionId);
333
- const usageRows = rows.filter((r) => r.kind === 'usage_raw' && family.includes(sessionId(r)));
334
- return {
335
- tracePath,
336
- session_ids: family,
337
- prompt_tokens: sum(usageRows.map((r) => num(r, 'prompt_tokens'))),
338
- output_tokens: sum(usageRows.map((r) => num(r, 'output_tokens'))),
339
- cached_tokens: sum(usageRows.map((r) => num(r, 'cached_tokens'))),
340
- usage_rows: usageRows.length,
341
- };
342
- }
343
-
344
- // ---- lead mode: multi-agent role-split token attribution -------------------
345
-
346
- const ROLES = ['lead', 'worker', 'reviewer', 'other'];
347
-
348
- function roleOf(agent) {
349
- const a = String(agent || '').toLowerCase();
350
- if (!a) return 'other';
351
- if (a === 'lead' || a === 'main') return 'lead';
352
- if (a.includes('review')) return 'reviewer'; // reviewer before worker (heavy-worker matches 'worker')
353
- if (a.includes('worker')) return 'worker';
354
- return 'other';
355
- }
356
-
357
- function emptyRoleBucket() {
358
- return { prompt_tokens: 0, output_tokens: 0, cached_tokens: 0, usage_rows: 0, total_tokens: 0 };
359
- }
360
-
361
- // Transitive closure over parent_session_id from the Lead root: collects the
362
- // whole Lead+children session tree (worker/reviewer sessions link back via
363
- // parent_session_id recorded on their preset_assign rows).
364
- function collectSessionTree(sessionMetas, rootSessionId) {
365
- const byParent = new Map();
366
- for (const meta of sessionMetas) {
367
- const p = meta.parent_session_id || null;
368
- if (!byParent.has(p)) byParent.set(p, []);
369
- byParent.get(p).push(meta);
370
- }
371
- const ids = new Set();
372
- const queue = [rootSessionId];
373
- while (queue.length) {
374
- const id = queue.shift();
375
- if (!id || ids.has(id)) continue;
376
- ids.add(id);
377
- for (const child of byParent.get(id) || []) queue.push(child.session_id);
378
- }
379
- return ids;
380
- }
381
-
382
- function emptyRoleSplit(dataDir) {
383
- return {
384
- tracePath: join(dataDir, 'history', 'agent-trace.jsonl'),
385
- session_ids: [],
386
- byRole: { lead: emptyRoleBucket(), worker: emptyRoleBucket(), reviewer: emptyRoleBucket(), other: emptyRoleBucket() },
387
- total: { prompt_tokens: 0, output_tokens: 0, total_tokens: 0, usage_rows: 0 },
388
- };
389
- }
390
-
391
- function mathTaskCompleted(taskCwd) {
392
- let source = '';
393
- try {
394
- source = readFileSync(join(taskCwd, 'math.js'), 'utf8');
395
- } catch {
396
- return false;
397
- }
398
- return source !== INITIAL_MATH_JS
399
- && /export\s+function\s+add\s*\(\s*a\s*,\s*b\s*\)\s*\{\s*return\s+a\s*\+\s*b\s*;?\s*\}/.test(source)
400
- && /export\s+function\s+mul\s*\(\s*a\s*,\s*b\s*\)\s*\{\s*return\s+a\s*\*\s*b\s*;?\s*\}/.test(source);
401
- }
402
-
403
- function splitTokensByRole(dataDir, rootSessionId) {
404
- const tracePath = join(dataDir, 'history', 'agent-trace.jsonl');
405
- const rows = readRows(tracePath);
406
- const bySid = groupBy(rows, sessionId);
407
- const sessionMetas = [...bySid.entries()].map(([sid, srows]) => ({ ...inferSessionMeta(srows), session_id: sid }));
408
- const treeIds = collectSessionTree(sessionMetas, rootSessionId);
409
- const roleBySession = new Map();
410
- for (const meta of sessionMetas) {
411
- if (treeIds.has(meta.session_id)) roleBySession.set(meta.session_id, roleOf(meta.agent));
412
- }
413
- // The Lead root session is created via createSession() directly (no
414
- // session-builder), so it emits no preset_assign row and inferSessionMeta
415
- // falls back to sourceName ('internal-comms-bench-lead') which roleOf() maps
416
- // to 'other'. Pin the root to 'lead' explicitly. Worker/reviewer children go
417
- // through session-builder -> traceAgentPreset, so their agent is recorded.
418
- if (treeIds.has(rootSessionId)) roleBySession.set(rootSessionId, 'lead');
419
- const byRole = { lead: emptyRoleBucket(), worker: emptyRoleBucket(), reviewer: emptyRoleBucket(), other: emptyRoleBucket() };
420
- const usageRows = rows.filter((r) => r.kind === 'usage_raw' && treeIds.has(sessionId(r)));
421
- for (const r of usageRows) {
422
- const role = roleBySession.get(sessionId(r)) || 'other';
423
- const bucket = byRole[role] || byRole.other;
424
- bucket.prompt_tokens += num(r, 'prompt_tokens') || 0;
425
- bucket.output_tokens += num(r, 'output_tokens') || 0;
426
- bucket.cached_tokens += num(r, 'cached_tokens') || 0;
427
- bucket.usage_rows += 1;
428
- }
429
- const total = { prompt_tokens: 0, output_tokens: 0, total_tokens: 0, usage_rows: 0 };
430
- for (const role of ROLES) {
431
- byRole[role].total_tokens = byRole[role].prompt_tokens + byRole[role].output_tokens;
432
- total.prompt_tokens += byRole[role].prompt_tokens;
433
- total.output_tokens += byRole[role].output_tokens;
434
- total.total_tokens += byRole[role].total_tokens;
435
- total.usage_rows += byRole[role].usage_rows;
436
- }
437
- return { tracePath, session_ids: [...treeIds], byRole, total };
438
- }
439
-
440
- export function validateLeadRun({ taskCwd, run, split }) {
441
- const reasons = [];
442
- if (!run.ok) reasons.push('child did not return a successful benchmark result');
443
- if (!mathTaskCompleted(taskCwd)) reasons.push('math.js does not contain the approved add and preserved mul implementation');
444
- if ((split?.byRole?.worker?.usage_rows || 0) < 1) reasons.push('worker participation/usage is missing');
445
- if ((split?.byRole?.reviewer?.usage_rows || 0) < 1) reasons.push('reviewer participation/usage is missing');
446
- return { valid: reasons.length === 0, reasons };
447
- }
448
-
449
- export function leadModeExitCode(runsMeta, perVariant) {
450
- const allValid = ['A', 'B'].every((variant) => runsMeta[variant].length > 0 && runsMeta[variant].every((run) => run.valid));
451
- const anyUsage = ['A', 'B'].some((variant) => perVariant[variant].some((split) => split.total.usage_rows > 0));
452
- return allValid && anyUsage ? 0 : 1;
453
- }
454
-
455
- function median(values) {
456
- const arr = values.filter((v) => Number.isFinite(v)).sort((a, b) => a - b);
457
- if (!arr.length) return 0;
458
- const mid = Math.floor(arr.length / 2);
459
- return arr.length % 2 ? arr[mid] : (arr[mid - 1] + arr[mid]) / 2;
460
- }
461
-
462
- function mean(values) {
463
- const arr = values.filter((v) => Number.isFinite(v));
464
- return arr.length ? sum(arr) / arr.length : 0;
465
- }
466
-
467
- function aggregateVariant(splits) {
468
- const out = { byRole: {}, total: {} };
469
- for (const role of ROLES) {
470
- const totals = splits.map((s) => s.byRole[role].total_tokens);
471
- out.byRole[role] = { median: median(totals), mean: mean(totals), runs: totals };
472
- }
473
- const grand = splits.map((s) => s.total.total_tokens);
474
- out.total = { median: median(grand), mean: mean(grand), runs: grand };
475
- return out;
476
- }
477
-
478
- // Drives a REAL Lead session in the variant sandbox through the public session
479
- // runtime, including its deferred standalone tool surface.
480
- // The variant rule files and public runtime modules take effect through the
481
- // sandboxed MIXDOG_ROOT.
482
- export function buildLiveLeadDriver({
483
- runtimeUrl,
484
- traceUrl = pathToFileURL(join(PLUGIN_ROOT, 'runtime/agent/orchestrator/agent-trace-io.mjs')).href,
485
- taskCwd,
486
- prompt,
487
- approvalPrompt = DEFAULT_LEAD_APPROVAL_PROMPT,
488
- provider,
489
- model,
490
- effort,
491
- fast,
492
- }) {
493
- return [
494
- `import { createMixdogSessionRuntime } from ${JSON.stringify(runtimeUrl)};`,
495
- `import { drainAgentTrace } from ${JSON.stringify(traceUrl)};`,
496
- `const rt = await createMixdogSessionRuntime({ provider: ${JSON.stringify(provider)}, model: ${JSON.stringify(model)},`,
497
- ` cwd: ${JSON.stringify(taskCwd)}, toolMode: 'full' });`,
498
- effort ? `await rt.setEffort(${JSON.stringify(effort)});` : '',
499
- fast !== undefined ? `await rt.setFast(${JSON.stringify(fast)});` : '',
500
- `const prePromptTools = rt.toolsStatus?.()?.activeTools || [];`,
501
- `if (!prePromptTools.includes('agent')) throw new Error('Lead runtime agent tool is not active');`,
502
- `let busy = false; let kickDeferred = false; let wake = null;`,
503
- `rt.onNotification(() => { queueMicrotask(() => { kickDeferred = true; if (!busy && wake) wake(); }); return false; });`,
504
- `const askOnce = async (msg) => { busy = true; try { const { result } = await rt.ask(msg); return result; } finally { busy = false; } };`,
505
- `const agentsBusy = () => { const jobs = rt.agentStatus?.()?.agentJobs || []; return jobs.some((j) => /running|pending|spawn|queued/i.test(String(j?.status || j?.state || ''))); };`,
506
- `let result; let sessionId = '';`,
507
- `try {`,
508
- ` await askOnce(${JSON.stringify(prompt)});`,
509
- ` result = await askOnce(${JSON.stringify(approvalPrompt)});`,
510
- ` const deadline = Date.now() + 30 * 60_000;`,
511
- ` while (Date.now() < deadline) {`,
512
- ` if (!kickDeferred) {`,
513
- ` if (!agentsBusy()) break;`,
514
- ` const kicked = await new Promise((resolve) => { wake = () => resolve(true); setTimeout(() => resolve(false), 10_000); });`,
515
- ` wake = null;`,
516
- ` if (!kicked && !kickDeferred && !agentsBusy()) break;`,
517
- ` }`,
518
- ` if (!kickDeferred && !agentsBusy()) break;`,
519
- ` kickDeferred = false; result = (await askOnce('')) || result;`,
520
- ` }`,
521
- ` sessionId = rt.sessionId || rt.id || rt.session?.id || '';`,
522
- `}`,
523
- `finally { try { await rt.close('internal-comms-bench-lead'); } catch {} }`,
524
- `await drainAgentTrace();`,
525
- `const benchResult = JSON.stringify({ text: String(result?.text || result?.content || '').trim(), sessionId });`,
526
- `process.stdout.write(${JSON.stringify(CHILD_RESULT_START)} + benchResult + ${JSON.stringify(CHILD_RESULT_END)} + '\\n', () => process.exit(0));`,
527
- ].filter(Boolean).join('\n');
528
- }
529
-
530
- export function parseLiveLeadResult(stdout) {
531
- const source = String(stdout || '');
532
- const start = source.lastIndexOf(CHILD_RESULT_START);
533
- if (start < 0) return null;
534
- const jsonStart = start + CHILD_RESULT_START.length;
535
- const end = source.indexOf(CHILD_RESULT_END, jsonStart);
536
- if (end < 0) return null;
537
- try {
538
- const result = JSON.parse(source.slice(jsonStart, end));
539
- return typeof result?.sessionId === 'string' && result.sessionId ? result : null;
540
- } catch {
541
- return null;
542
- }
543
- }
544
-
545
- export function runLiveLeadDelegation({
546
- pluginRoot,
547
- dataDir,
548
- taskCwd,
549
- prompt,
550
- provider,
551
- model,
552
- effort,
553
- fast,
554
- runtimeUrls = null,
555
- }) {
556
- const runtimeUrl = runtimeUrls?.runtimeUrl || pathToFileURL(join(pluginRoot, 'mixdog-session-runtime.mjs')).href;
557
- const traceUrl = runtimeUrls?.traceUrl || pathToFileURL(join(pluginRoot, 'runtime/agent/orchestrator/agent-trace-io.mjs')).href;
558
- const driver = buildLiveLeadDriver({
559
- runtimeUrl,
560
- traceUrl,
561
- taskCwd,
562
- prompt,
563
- provider,
564
- model,
565
- effort,
566
- fast,
567
- });
568
- const started = Date.now();
569
- const child = spawnSync(process.execPath, ['--input-type=module', '-e', driver], {
570
- encoding: 'utf8',
571
- maxBuffer: 64 * 1024 * 1024,
572
- env: {
573
- ...process.env,
574
- MIXDOG_ROOT: pluginRoot,
575
- MIXDOG_DATA_DIR: dataDir,
576
- },
577
- });
578
- const stdout = String(child.stdout || '');
579
- const stderr = String(child.stderr || '');
580
- const result = parseLiveLeadResult(stdout);
581
- return {
582
- sessionId: result?.sessionId || null,
583
- ms: Date.now() - started,
584
- ok: child.status === 0 && !child.error && !!result,
585
- raw: stdout + stderr,
586
- stdout,
587
- stderr,
588
- exitCode: child.status,
589
- signal: child.signal || null,
590
- error: child.error ? String(child.error.message || child.error) : null,
591
- };
592
- }
593
-
594
- function fmtDeltaEntry(entry) {
595
- const sign = entry.delta >= 0 ? '+' : '';
596
- const pct = entry.pct == null ? '' : ` (${entry.pct >= 0 ? '+' : ''}${entry.pct.toFixed(1)}%)`;
597
- return `${sign}${Math.round(entry.delta)}${pct}`;
598
- }
599
-
600
- function runLeadMode({ route, effort, fast, repeat, jsonMode, leadPrompt }) {
601
- const realDataDir = defaultUserDataDir();
602
- const userUnified = readUnifiedConfig(realDataDir);
603
- const sandboxRoot = mkdtempSync(join(REPO_ROOT, '.tmp-internal-comms-bench-lead-'));
604
- const perVariant = { A: [], B: [] };
605
- const runsMeta = { A: [], B: [] };
606
- let invalidRun = null;
607
- try {
608
- const pluginRoots = {
609
- A: materializePluginRoot('A', sandboxRoot),
610
- B: materializePluginRoot('B', sandboxRoot),
611
- };
612
- runLoop: for (let i = 0; i < repeat; i += 1) {
613
- for (const variant of ['A', 'B']) {
614
- const taskCwd = prepareTaskCwd(sandboxRoot);
615
- const dataDir = prepareDataDir(sandboxRoot, `${variant}-r${i}`, realDataDir, userUnified, route.provider);
616
- process.stderr.write(`[internal-comms-bench] lead run ${i + 1}/${repeat} variant=${variant} ${route.provider}/${route.model}\n`);
617
- const run = runLiveLeadDelegation({
618
- pluginRoot: pluginRoots[variant],
619
- dataDir,
620
- taskCwd,
621
- prompt: leadPrompt,
622
- provider: route.provider,
623
- model: route.model,
624
- effort,
625
- fast,
626
- });
627
- const split = run.sessionId ? splitTokensByRole(dataDir, run.sessionId) : emptyRoleSplit(dataDir);
628
- const validation = validateLeadRun({ taskCwd, run, split });
629
- if (validation.valid) perVariant[variant].push(split);
630
- const meta = {
631
- ok: run.ok,
632
- valid: validation.valid,
633
- reasons: validation.reasons,
634
- ms: run.ms,
635
- sessionId: run.sessionId,
636
- total: split.total.total_tokens,
637
- diagnostics: {
638
- stdout: run.stdout,
639
- stderr: run.stderr,
640
- exitCode: run.exitCode,
641
- signal: run.signal,
642
- error: run.error,
643
- },
644
- };
645
- runsMeta[variant].push(meta);
646
- process.stderr.write(`[internal-comms-bench] -> ${validation.valid ? 'ok' : 'FAIL'} ${Math.round(run.ms / 1000)}s session=${run.sessionId || '(none)'} total=${split.total.total_tokens} lead=${split.byRole.lead.total_tokens} worker=${split.byRole.worker.total_tokens} reviewer=${split.byRole.reviewer.total_tokens} other=${split.byRole.other.total_tokens}${validation.valid ? '' : ` reasons=${validation.reasons.join('; ')}`}\n`);
647
- if (!validation.valid && (run.stdout || run.stderr || run.error)) {
648
- process.stderr.write(`[internal-comms-bench] child diagnostics variant=${variant} stdout=${JSON.stringify(run.stdout)} stderr=${JSON.stringify(run.stderr)} error=${JSON.stringify(run.error)}\n`);
649
- }
650
- if (!validation.valid) {
651
- invalidRun = { variant, index: i, reasons: validation.reasons };
652
- break runLoop;
653
- }
654
- }
655
- }
656
- } finally {
657
- rmSync(sandboxRoot, { recursive: true, force: true });
658
- }
659
-
660
- if (invalidRun) {
661
- process.stderr.write(`[internal-comms-bench] aborting before aggregation: invalid variant=${invalidRun.variant} run=${invalidRun.index + 1} reasons=${invalidRun.reasons.join('; ')}\n`);
662
- process.exit(1);
663
- return;
664
- }
665
-
666
- const aggA = aggregateVariant(perVariant.A);
667
- const aggB = aggregateVariant(perVariant.B);
668
- const deltaMedian = {};
669
- const deltaMean = {};
670
- for (const role of [...ROLES, 'total']) {
671
- const aM = role === 'total' ? aggA.total.median : aggA.byRole[role].median;
672
- const bM = role === 'total' ? aggB.total.median : aggB.byRole[role].median;
673
- const aAvg = role === 'total' ? aggA.total.mean : aggA.byRole[role].mean;
674
- const bAvg = role === 'total' ? aggB.total.mean : aggB.byRole[role].mean;
675
- deltaMedian[role] = { delta: bM - aM, pct: pctChange(bM, aM) };
676
- deltaMean[role] = { delta: bAvg - aAvg, pct: pctChange(bAvg, aAvg) };
677
- }
678
-
679
- if (jsonMode) {
680
- console.log(JSON.stringify({
681
- mode: 'lead',
682
- route,
683
- repeat,
684
- prompt: leadPrompt,
685
- runs: runsMeta,
686
- variants: { A: { label: 'prior_verbose@HEAD', ...aggA }, B: { label: 'optimized_on_disk', ...aggB } },
687
- delta_B_vs_A: { median: deltaMedian, mean: deltaMean },
688
- note: 'single runs are noise-dominated; median+mean over --repeat N reduce run-to-run noise.',
689
- }, null, 2));
690
- } else {
691
- console.log(`lead-mode multi-agent ${route.provider}/${route.model} repeat=${repeat}`);
692
- console.log('NOTE: single runs are noise-dominated; use --repeat N to stabilize (median+mean shown).');
693
- for (const [id, agg] of [['A', aggA], ['B', aggB]]) {
694
- console.log(`variant ${id} (${id === 'A' ? 'prior_verbose@HEAD' : 'optimized_on_disk'}) per-role tokens median/mean over ${repeat} run(s):`);
695
- for (const role of ROLES) {
696
- const r = agg.byRole[role];
697
- console.log(` ${role.padEnd(9)} med=${Math.round(r.median)} mean=${Math.round(r.mean)}`);
698
- }
699
- console.log(` ${'total'.padEnd(9)} med=${Math.round(agg.total.median)} mean=${Math.round(agg.total.mean)}`);
700
- }
701
- console.log('delta B-vs-A (median): ' + [...ROLES, 'total'].map((r) => `${r}=${fmtDeltaEntry(deltaMedian[r])}`).join(' '));
702
- console.log('delta B-vs-A (mean): ' + [...ROLES, 'total'].map((r) => `${r}=${fmtDeltaEntry(deltaMean[r])}`).join(' '));
703
- }
704
-
705
- process.exit(leadModeExitCode(runsMeta, perVariant));
706
- }
707
-
708
- function pctChange(b, a) {
709
- if (!Number.isFinite(a) || a === 0) return null;
710
- return ((b - a) / a) * 100;
711
- }
712
-
713
- function printUsage() {
714
- process.stdout.write(`internal-comms-bench — live A/B token effect (internal-comms rules).
715
-
716
- Variant A = prior verbose rules from git: git show HEAD:src/<rule> (fallback: current file).
717
- Variant B = optimized rules: current on-disk src/ copied to temp PLUGIN_ROOT.
718
-
719
- Modes:
720
- --mode worker (default) — single worker task via runHeadlessRole (bench-run child pattern).
721
- --mode lead — REAL internal-comms flow: a live Lead session is forced to delegate
722
- one small task to a worker and then a reviewer (via the agent tool).
723
- Tokens are split BY ROLE (lead/worker/reviewer/other) across the whole
724
- Lead+children session tree, parsed from the sandbox agent-trace.jsonl.
725
-
726
- Usage:
727
- node scripts/internal-comms-bench.mjs [--json]
728
- node scripts/internal-comms-bench.mjs --run [--model grok] [--provider P] [--effort E] [--fast] [--prompt "..."] [--json]
729
- node scripts/internal-comms-bench.mjs --run --mode lead [--repeat N] [--model grok] [--provider P] [--effort E] [--fast] [--prompt "..."] [--json]
730
-
731
- --run required for live model calls. Sandboxes temp PLUGIN_ROOT + MIXDOG_DATA_DIR; copies OAuth JSON read-only from your real data dir.
732
- --mode lead measures the optimization the worker mode cannot (Lead brief + agent handoff tokens).
733
- --repeat N (default 1) runs the A/B N times and reports per-role median AND mean; single runs are noise-dominated.
734
- `);
735
- }
736
-
737
- function main() {
738
- const jsonMode = hasFlag('--json');
739
- const doRun = hasFlag('--run');
740
- const mode = String(argValue('--mode', 'worker') || 'worker').trim().toLowerCase();
741
- const repeat = Math.max(1, Number.parseInt(argValue('--repeat', '1'), 10) || 1);
742
- const prompt = argValue('--prompt', mode === 'lead' ? DEFAULT_LEAD_PROMPT : DEFAULT_WORKER_PROMPT);
743
- const cli = resolveModelOpts(argValue('--model', doRun ? 'grok' : null), argValue('--provider', null));
744
- const effort = argValue('--effort', null);
745
- const fast = hasFlag('--fast');
746
-
747
- if (!doRun) {
748
- printUsage();
749
- const ruleStats = ruleVariantBytes();
750
- const aSum = sum(ruleStats.map((r) => r.a_chars));
751
- const bSum = sum(ruleStats.map((r) => r.b_chars));
752
- if (jsonMode) {
753
- console.log(JSON.stringify({
754
- mode: 'usage',
755
- run_mode: mode,
756
- repeat,
757
- rule_files: RULE_FILES,
758
- rule_stats: ruleStats,
759
- rule_chars: { A: aSum, B: bSum },
760
- liveCommand: 'node scripts/internal-comms-bench.mjs --run --model grok',
761
- liveLeadCommand: 'node scripts/internal-comms-bench.mjs --run --mode lead --repeat 3 --model grok',
762
- }, null, 2));
763
- } else {
764
- process.stdout.write(`[internal-comms-bench] rule chars A(prior@HEAD)=${aSum} B(on-disk)=${bSum} (${RULE_FILES.length} files); mode=${mode} repeat=${repeat}\n`);
765
- }
766
- process.exit(0);
767
- }
768
-
769
- const realDataDir = defaultUserDataDir();
770
- const userUnified = readUnifiedConfig(realDataDir);
771
- const route = {
772
- provider: cli.provider || MODEL_ALIASES.grok.provider,
773
- model: cli.model || MODEL_ALIASES.grok.model,
774
- };
775
-
776
- if (mode === 'lead') {
777
- runLeadMode({ route, effort, fast, repeat, jsonMode, leadPrompt: prompt });
778
- return;
779
- }
780
-
781
- const sandboxRoot = mkdtempSync(join(REPO_ROOT, '.tmp-internal-comms-bench-'));
782
- const results = {};
783
- try {
784
- const taskCwd = prepareTaskCwd(sandboxRoot);
785
- for (const variant of ['A', 'B']) {
786
- resetTaskCwd(taskCwd);
787
- const pluginRoot = materializePluginRoot(variant, sandboxRoot);
788
- const dataDir = prepareDataDir(sandboxRoot, variant, realDataDir, userUnified, route.provider);
789
- process.stderr.write(`[internal-comms-bench] variant=${variant} ${route.provider}/${route.model}\n`);
790
- const run = runHeadlessWorker({
791
- pluginRoot,
792
- dataDir,
793
- taskCwd,
794
- prompt,
795
- provider: route.provider,
796
- model: route.model,
797
- effort,
798
- fast,
799
- });
800
- const usage = run.sessionId
801
- ? sumUsageForSession(dataDir, run.sessionId)
802
- : {
803
- session_ids: [],
804
- prompt_tokens: 0,
805
- output_tokens: 0,
806
- cached_tokens: 0,
807
- usage_rows: 0,
808
- tracePath: join(dataDir, 'history', 'agent-trace.jsonl'),
809
- };
810
- usage.total_tokens = usage.prompt_tokens + usage.output_tokens;
811
- results[variant] = { ...run, usage };
812
- process.stderr.write(`[internal-comms-bench] -> ${run.ok ? 'ok' : 'FAIL'} ${Math.round(run.ms / 1000)}s session=${run.sessionId || '(none)'} tokens=${usage.total_tokens}\n`);
813
- }
814
- } finally {
815
- rmSync(sandboxRoot, { recursive: true, force: true });
816
- }
817
-
818
- const a = results.A?.usage || {};
819
- const b = results.B?.usage || {};
820
- const delta = {
821
- prompt_tokens: (b.prompt_tokens || 0) - (a.prompt_tokens || 0),
822
- output_tokens: (b.output_tokens || 0) - (a.output_tokens || 0),
823
- total_tokens: (b.total_tokens || 0) - (a.total_tokens || 0),
824
- pct_total_B_vs_A: pctChange(b.total_tokens || 0, a.total_tokens || 0),
825
- };
826
-
827
- if (jsonMode) {
828
- console.log(JSON.stringify({
829
- mode: 'live',
830
- route,
831
- prompt,
832
- variants: {
833
- A: { label: 'prior_verbose@HEAD', run: { ok: results.A.ok, ms: results.A.ms, sessionId: results.A.sessionId }, usage: a },
834
- B: { label: 'optimized_on_disk', run: { ok: results.B.ok, ms: results.B.ms, sessionId: results.B.sessionId }, usage: b },
835
- },
836
- delta_B_vs_A: delta,
837
- }, null, 2));
838
- } else {
839
- console.log(`live worker ${route.provider}/${route.model}`);
840
- for (const id of ['A', 'B']) {
841
- const u = results[id].usage;
842
- console.log(`variant ${id}: ${results[id].ok ? 'ok' : 'FAIL'} prompt=${u.prompt_tokens} output=${u.output_tokens} total=${u.total_tokens} sessions=${(u.session_ids || []).length}`);
843
- }
844
- const pct = delta.pct_total_B_vs_A;
845
- const pctStr = pct == null ? 'n/a' : `${pct >= 0 ? '+' : ''}${pct.toFixed(1)}%`;
846
- console.log(`delta B-vs-A: prompt=${delta.prompt_tokens >= 0 ? '+' : ''}${delta.prompt_tokens} output=${delta.output_tokens >= 0 ? '+' : ''}${delta.output_tokens} total=${delta.total_tokens >= 0 ? '+' : ''}${delta.total_tokens} (${pctStr})`);
847
- }
848
-
849
- const ok = results.A?.ok && results.B?.ok && (a.usage_rows > 0 || b.usage_rows > 0);
850
- process.exit(ok ? 0 : 1);
851
- }
852
-
853
- if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();