mixdog 0.9.172 → 0.9.173

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 (623) hide show
  1. package/package.json +3 -3
  2. package/scripts/code-graph-description-contract.mjs +1 -2
  3. package/scripts/context-renewal-live-check.mjs +1 -1
  4. package/scripts/coverage-query.mjs +27 -0
  5. package/scripts/generate-runtime-manifest.mjs +12 -16
  6. package/scripts/graph-parity.mjs +108 -75
  7. package/scripts/lib/coverage.mjs +309 -0
  8. package/scripts/lib/embedding-model-bench-core.mjs +4 -2
  9. package/scripts/lib/recall-bench-eval.mjs +4 -4
  10. package/scripts/lib/run-node-tests.mjs +137 -13
  11. package/scripts/lib/test-failure-records.mjs +46 -0
  12. package/scripts/lib/test-failure-summary.mjs +59 -0
  13. package/scripts/lib/test-summary-reporter.mjs +7 -3
  14. package/scripts/mouse-probe.mjs +1 -3
  15. package/scripts/office/build-pptx-icons.mjs +0 -1
  16. package/scripts/perf/transcript-runtime-probe.mjs +20 -4
  17. package/scripts/pg-memory-diet.mjs +2 -4
  18. package/scripts/routing-corpus.mjs +0 -2
  19. package/scripts/session-diag.mjs +0 -7
  20. package/scripts/session-sweep.mjs +102 -56
  21. package/scripts/test.mjs +113 -37
  22. package/scripts/tui-frame-harness.mjs +1 -1
  23. package/src/app.mjs +79 -74
  24. package/src/cli.mjs +0 -2
  25. package/src/defaults/skills/browser-use/SKILL.md +8 -6
  26. package/src/defaults/skills/code-tidy/SKILL.md +67 -17
  27. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +26 -3
  28. package/src/defaults/skills/computer-use/SKILL.md +12 -5
  29. package/src/defaults/skills/goal-management/SKILL.md +16 -9
  30. package/src/defaults/skills/pdf/SKILL.md +2 -1
  31. package/src/defaults/skills/xlsx/SKILL.md +3 -3
  32. package/src/headless-exec.mjs +1 -2
  33. package/src/help.mjs +2 -2
  34. package/src/lib/rules-builder.cjs +0 -23
  35. package/src/repl/slash-commands.mjs +1 -5
  36. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/turn.mjs +4 -4
  37. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +25 -41
  38. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +25 -24
  39. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +15 -22
  40. package/src/runtime/agent/orchestrator/agent-trace.mjs +0 -22
  41. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +56 -0
  42. package/src/runtime/agent/orchestrator/config.mjs +34 -76
  43. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +40 -39
  45. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +75 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +152 -0
  47. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +168 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +248 -0
  50. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +16 -206
  51. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +11 -63
  52. package/src/runtime/agent/orchestrator/providers/anthropic-recovery-guards.mjs +86 -0
  53. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +88 -0
  54. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +87 -591
  55. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +32 -288
  56. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +101 -0
  57. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +161 -0
  58. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +114 -0
  59. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +266 -0
  60. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +54 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +145 -0
  62. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +56 -39
  63. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +107 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini.mjs +23 -533
  65. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +10 -4
  66. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +83 -32
  67. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +41 -25
  68. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -98
  69. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +114 -0
  70. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +258 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +48 -214
  72. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +80 -60
  73. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +108 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +8 -87
  75. package/src/runtime/agent/orchestrator/providers/openai-ws-send-outcome.mjs +52 -293
  76. package/src/runtime/agent/orchestrator/providers/openai-ws-send-trace.mjs +316 -0
  77. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +76 -253
  78. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -7
  79. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +17 -16
  80. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +6 -4
  81. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +9 -13
  83. package/src/runtime/agent/orchestrator/session/context-utils.mjs +1 -2
  84. package/src/runtime/agent/orchestrator/session/evidence-union.mjs +6 -9
  85. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -3
  86. package/src/runtime/agent/orchestrator/session/loop/diagnostics.mjs +1 -8
  87. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +1 -3
  88. package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +1 -5
  89. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +0 -1
  90. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -55
  91. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +63 -0
  92. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -1
  93. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +80 -65
  94. package/src/runtime/agent/orchestrator/session/manager/pending-claim-ledger.mjs +68 -0
  95. package/src/runtime/agent/orchestrator/session/manager/pending-delivered-ledger.mjs +56 -0
  96. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +84 -0
  97. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +51 -651
  98. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +264 -0
  99. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +131 -0
  100. package/src/runtime/agent/orchestrator/session/manager/pending-spool-sweep.mjs +73 -0
  101. package/src/runtime/agent/orchestrator/session/manager/prepared-resume-cache.mjs +122 -0
  102. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +2 -1
  103. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +5 -4
  104. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +60 -35
  105. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +3 -2
  106. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +22 -646
  107. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +135 -0
  108. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +150 -0
  109. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +154 -0
  110. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +135 -0
  111. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -11
  112. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +0 -6
  113. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +1 -3
  114. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +11 -12
  115. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +23 -32
  116. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +275 -0
  117. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +67 -0
  118. package/src/runtime/agent/orchestrator/session/store/listing.mjs +1 -1
  119. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +72 -0
  120. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +53 -0
  121. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +60 -0
  122. package/src/runtime/agent/orchestrator/session/store/pending-saves.mjs +55 -0
  123. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +7 -15
  124. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +149 -0
  125. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +55 -0
  126. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +108 -0
  127. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +531 -0
  128. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +126 -0
  129. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +24 -0
  130. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +2 -10
  131. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +46 -0
  132. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +11 -791
  133. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +126 -0
  134. package/src/runtime/agent/orchestrator/session/store.mjs +33 -771
  135. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  136. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +13 -7
  137. package/src/runtime/agent/orchestrator/stable-hash-stringify.mjs +10 -0
  138. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +50 -75
  139. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +2 -1
  140. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +22 -21
  141. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +44 -27
  143. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +36 -54
  144. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +8 -5
  145. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +8 -5
  146. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +18 -9
  147. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-request.mjs +2 -3
  148. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-single-file-rescue.mjs +80 -67
  149. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +12 -4
  150. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +1 -4
  151. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +61 -72
  152. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +0 -5
  153. package/src/runtime/agent/orchestrator/tools/builtin/read-office-files.mjs +3 -3
  154. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +11 -14
  155. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +3 -6
  156. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +1 -2
  157. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +124 -94
  158. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +6 -3
  159. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/reach-preflight.mjs +1 -1
  160. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -1
  161. package/src/runtime/agent/orchestrator/tools/builtin/read-windows.mjs +2 -2
  162. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +1 -1
  163. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +6 -6
  164. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +21 -35
  166. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +0 -3
  167. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +1 -3
  168. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +1 -1
  169. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +0 -2
  170. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +4 -3
  171. package/src/runtime/agent/orchestrator/tools/builtin/text-stats.mjs +6 -3
  172. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -4
  173. package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-roots.mjs +7 -7
  174. package/src/runtime/agent/orchestrator/tools/code-graph/ast-calls.mjs +0 -3
  175. package/src/runtime/agent/orchestrator/tools/code-graph/build-file-infos.mjs +182 -0
  176. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +14 -159
  177. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +2 -3
  178. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +21 -8
  179. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +0 -3
  180. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +5 -3
  181. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -3
  182. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +1 -2
  183. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.golden.json +1 -1
  184. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +0 -1
  185. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
  186. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +1 -1
  187. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +1 -1
  188. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +3 -6
  189. package/src/runtime/agent/orchestrator/tools/patch/mutation-output.mjs +1 -1
  190. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +25 -39
  191. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +9 -14
  192. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +11 -6
  193. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +5 -6
  194. package/src/runtime/agent/orchestrator/tools/patch/text-lines.mjs +4 -4
  195. package/src/runtime/agent/orchestrator/tools/patch/v4a-anchors.mjs +10 -7
  196. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +20 -30
  197. package/src/runtime/agent/orchestrator/tools/patch/v4a-windows.mjs +4 -19
  198. package/src/runtime/agent/orchestrator/tools/patch/wave.mjs +13 -2
  199. package/src/runtime/agent/orchestrator/tools/patch.mjs +0 -19
  200. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +60 -36
  201. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +0 -4
  202. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +6 -7
  203. package/src/runtime/attachments/store.mjs +13 -12
  204. package/src/runtime/browser-bridge/client.mjs +19 -9
  205. package/src/runtime/channels/index.mjs +1 -1
  206. package/src/runtime/channels/lib/event-pipeline.mjs +8 -8
  207. package/src/runtime/channels/lib/event-queue.mjs +15 -20
  208. package/src/runtime/channels/lib/executor.mjs +10 -7
  209. package/src/runtime/channels/lib/owner-heartbeat.mjs +0 -4
  210. package/src/runtime/channels/lib/runtime-paths.mjs +30 -33
  211. package/src/runtime/channels/lib/scheduler.mjs +11 -20
  212. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +0 -4
  213. package/src/runtime/channels/lib/voice-transcription.mjs +127 -104
  214. package/src/runtime/channels/lib/webhook/verification.mjs +33 -20
  215. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -2
  216. package/src/runtime/channels/lib/worker-main.mjs +2 -2
  217. package/src/runtime/computer-bridge/action-schema.mjs +20 -0
  218. package/src/runtime/computer-bridge/core-actions.mjs +15 -0
  219. package/src/runtime/github/client.mjs +2 -3
  220. package/src/runtime/github/commands.mjs +9 -3
  221. package/src/runtime/github/contract.mjs +7 -0
  222. package/src/runtime/local-provider/catalog.mjs +4 -3
  223. package/src/runtime/local-provider/hardware.mjs +7 -20
  224. package/src/runtime/local-provider/resumable-installations.mjs +12 -19
  225. package/src/runtime/media/auth.mjs +16 -13
  226. package/src/runtime/media/catalog.mjs +14 -6
  227. package/src/runtime/media/lanes.mjs +2 -1
  228. package/src/runtime/memory/index.mjs +5 -10
  229. package/src/runtime/memory/lib/core-memory-store.mjs +3 -7
  230. package/src/runtime/memory/lib/embedding-provider.mjs +9 -19
  231. package/src/runtime/memory/lib/embedding-worker.mjs +24 -28
  232. package/src/runtime/memory/lib/http-router/admin-routes.mjs +5 -51
  233. package/src/runtime/memory/lib/http-router/admin-trace-routes.mjs +65 -0
  234. package/src/runtime/memory/lib/memory-action-handlers/cycle-actions.mjs +18 -143
  235. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +84 -0
  236. package/src/runtime/memory/lib/memory-action-handlers/cycle-rebuild-action.mjs +94 -0
  237. package/src/runtime/memory/lib/memory-action-handlers/manage-actions.mjs +92 -53
  238. package/src/runtime/memory/lib/memory-categories.mjs +13 -0
  239. package/src/runtime/memory/lib/memory-chunk-quality.mjs +134 -74
  240. package/src/runtime/memory/lib/memory-cycle-requests.mjs +64 -70
  241. package/src/runtime/memory/lib/memory-cycle1.mjs +17 -33
  242. package/src/runtime/memory/lib/memory-embed.mjs +10 -2
  243. package/src/runtime/memory/lib/memory-ops-policy.mjs +3 -2
  244. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
  245. package/src/runtime/memory/lib/memory.mjs +7 -35
  246. package/src/runtime/memory/lib/pg/adapter.mjs +3 -4
  247. package/src/runtime/memory/lib/pg/process.mjs +23 -28
  248. package/src/runtime/memory/lib/pg/supervisor.mjs +10 -11
  249. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -2
  250. package/src/runtime/memory/lib/query-maintenance-handlers.mjs +112 -89
  251. package/src/runtime/memory/lib/query-ranking.mjs +8 -11
  252. package/src/runtime/memory/lib/recall-format.mjs +6 -12
  253. package/src/runtime/memory/lib/recall-hybrid/plan.mjs +1 -1
  254. package/src/runtime/memory/lib/runtime-fetcher.mjs +73 -65
  255. package/src/runtime/memory/lib/trace-store.mjs +10 -19
  256. package/src/runtime/memory/lib/transcript-ingest.mjs +7 -4
  257. package/src/runtime/memory/tool-defs.mjs +5 -8
  258. package/src/runtime/office/authoring/pptx-author-action.mjs +2 -2
  259. package/src/runtime/office/authoring/pptx-author-session.mjs +6 -9
  260. package/src/runtime/office/authoring/pptx-brief.mjs +11 -4
  261. package/src/runtime/office/authoring/pptx-receipt.mjs +79 -60
  262. package/src/runtime/office/bench/benchmark.mjs +102 -82
  263. package/src/runtime/office/capabilities-catalog.mjs +2 -0
  264. package/src/runtime/office/capabilities-signatures.mjs +8 -0
  265. package/src/runtime/office/capabilities.mjs +3 -4
  266. package/src/runtime/office/com/com-adapter.mjs +13 -1
  267. package/src/runtime/office/com/office-com-host.ps1 +63 -1
  268. package/src/runtime/office/core/office-actions-batch.mjs +12 -12
  269. package/src/runtime/office/core/office-actions-open.mjs +2 -7
  270. package/src/runtime/office/core/office-core.mjs +20 -1
  271. package/src/runtime/office/core/office-finalize/session-save.mjs +9 -6
  272. package/src/runtime/office/core/office-session-dispatch.mjs +86 -0
  273. package/src/runtime/office/core/office-sessionless-actions.mjs +115 -0
  274. package/src/runtime/office/core/office-sessions.mjs +2 -2
  275. package/src/runtime/office/core/office-transactions.mjs +2 -4
  276. package/src/runtime/office/core/tabular.mjs +13 -16
  277. package/src/runtime/office/design/composition-system.mjs +32 -28
  278. package/src/runtime/office/design/content-model.mjs +2 -1
  279. package/src/runtime/office/design/design-art-direction.mjs +17 -14
  280. package/src/runtime/office/design/design-creative-director.mjs +1 -1
  281. package/src/runtime/office/design/design-discipline.mjs +8 -0
  282. package/src/runtime/office/design/design-tokens.mjs +9 -5
  283. package/src/runtime/office/design/docx/design-docx-components.mjs +88 -156
  284. package/src/runtime/office/design/library/design-library-pack.mjs +11 -23
  285. package/src/runtime/office/design/library/design-library.mjs +15 -37
  286. package/src/runtime/office/design/library/design-template-index.mjs +1 -9
  287. package/src/runtime/office/design/library/design-template-induct.mjs +2 -1
  288. package/src/runtime/office/design/native-design.mjs +2 -3
  289. package/src/runtime/office/index.mjs +7 -160
  290. package/src/runtime/office/pdf/pdf-adapter.mjs +17 -2
  291. package/src/runtime/office/pdf/pdf-ocr.mjs +2 -1
  292. package/src/runtime/office/pdf/pdf-render.mjs +12 -13
  293. package/src/runtime/office/pdf/pdf-writer.mjs +135 -6
  294. package/src/runtime/office/portable/docx-revisions.mjs +5 -5
  295. package/src/runtime/office/portable/docx-runs.mjs +4 -7
  296. package/src/runtime/office/portable/docx-tracked-edits.mjs +14 -9
  297. package/src/runtime/office/portable/font-provisioner.mjs +2 -2
  298. package/src/runtime/office/portable/ooxml-validator.mjs +4 -1
  299. package/src/runtime/office/portable/portable-docx-edits.mjs +61 -45
  300. package/src/runtime/office/portable/portable-docx-operations.mjs +13 -4
  301. package/src/runtime/office/portable/portable-docx-parts.mjs +59 -70
  302. package/src/runtime/office/portable/portable-docx-xml.mjs +3 -5
  303. package/src/runtime/office/portable/portable-opc.mjs +63 -39
  304. package/src/runtime/office/portable/portable-package.mjs +5 -23
  305. package/src/runtime/office/portable/portable-pptx-chart.mjs +7 -6
  306. package/src/runtime/office/portable/portable-pptx-charts.mjs +27 -20
  307. package/src/runtime/office/portable/portable-pptx-core.mjs +10 -21
  308. package/src/runtime/office/portable/portable-pptx-deck.mjs +5 -8
  309. package/src/runtime/office/portable/portable-pptx-package.mjs +23 -30
  310. package/src/runtime/office/portable/portable-pptx-shapes.mjs +21 -57
  311. package/src/runtime/office/portable/portable-sheet-audits.mjs +14 -36
  312. package/src/runtime/office/portable/portable-sheet-page.mjs +22 -23
  313. package/src/runtime/office/portable/portable-sheet-parts.mjs +4 -5
  314. package/src/runtime/office/portable/portable-sheet-styles.mjs +11 -19
  315. package/src/runtime/office/portable/portable-sheet-xml.mjs +15 -7
  316. package/src/runtime/office/portable/portable-slide-shapes.mjs +4 -0
  317. package/src/runtime/office/portable/portable-snapshot-pptx.mjs +2 -2
  318. package/src/runtime/office/portable/portable-snapshot-shared.mjs +1 -1
  319. package/src/runtime/office/portable/portable-snapshot-xlsx.mjs +16 -17
  320. package/src/runtime/office/portable/portable-soffice.mjs +176 -13
  321. package/src/runtime/office/portable/portable-validation-package.mjs +16 -18
  322. package/src/runtime/office/portable/portable-validation.mjs +57 -31
  323. package/src/runtime/office/portable/portable-xlsx-cell-values.mjs +15 -0
  324. package/src/runtime/office/portable/portable-xlsx-charts.mjs +192 -0
  325. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +323 -0
  326. package/src/runtime/office/portable/portable-xlsx-operations.mjs +29 -627
  327. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +94 -0
  328. package/src/runtime/office/portable/portable-xlsx-range-layout.mjs +236 -0
  329. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +19 -14
  330. package/src/runtime/office/portable/portable-xlsx.mjs +32 -0
  331. package/src/runtime/office/portable/portable-xml.mjs +9 -0
  332. package/src/runtime/office/portable/pptx-relations.mjs +11 -0
  333. package/src/runtime/office/portable/pptx-targets.mjs +2 -1
  334. package/src/runtime/office/portable/review-editability.mjs +4 -11
  335. package/src/runtime/office/portable/text-metrics.mjs +35 -55
  336. package/src/runtime/office/portable/xlsx-assertions.mjs +2 -15
  337. package/src/runtime/office/portable/xlsx-audit-support.mjs +2 -6
  338. package/src/runtime/office/portable/xlsx-contract.mjs +4 -0
  339. package/src/runtime/office/portable/xlsx-formula-audit.mjs +11 -4
  340. package/src/runtime/office/portable/xlsx-recalculate.mjs +1 -1
  341. package/src/runtime/office/portable/xlsx-sheet-hygiene.mjs +5 -1
  342. package/src/runtime/office/quality/assurance-rendered.mjs +3 -2
  343. package/src/runtime/office/quality/assurance-structure-pptx.mjs +16 -15
  344. package/src/runtime/office/quality/assurance-trust.mjs +25 -10
  345. package/src/runtime/office/quality/design-aesthetics.mjs +172 -145
  346. package/src/runtime/office/quality/design-deck-diversity.mjs +2 -5
  347. package/src/runtime/office/quality/design-review-authored.mjs +2 -2
  348. package/src/runtime/office/quality/design-review-critique.mjs +36 -37
  349. package/src/runtime/office/quality/design-review.mjs +113 -96
  350. package/src/runtime/office/quality/document-acceptance.mjs +10 -3
  351. package/src/runtime/office/quality/inline-audit.mjs +7 -2
  352. package/src/runtime/office/quality/pptx-deck-rubric.mjs +14 -6
  353. package/src/runtime/office/quality/pptx-slide-roles.mjs +7 -9
  354. package/src/runtime/office/quality/presentation-acceptance.mjs +2 -6
  355. package/src/runtime/office/quality/quality-pipeline.mjs +21 -14
  356. package/src/runtime/office/quality/quality-score.mjs +2 -2
  357. package/src/runtime/office/quality/render-air.mjs +12 -5
  358. package/src/runtime/shared/atomic-file.mjs +19 -8
  359. package/src/runtime/shared/config.mjs +7 -13
  360. package/src/runtime/shared/err-text.mjs +11 -6
  361. package/src/runtime/shared/file-lock.mjs +10 -6
  362. package/src/runtime/shared/inline-file-kind.mjs +51 -0
  363. package/src/runtime/shared/llm/usage-accounting.mjs +9 -6
  364. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +272 -0
  365. package/src/runtime/shared/llm/usage-ledger.mjs +8 -218
  366. package/src/runtime/shared/pristine-execution.mjs +2 -6
  367. package/src/runtime/shared/process-lifecycle.mjs +34 -52
  368. package/src/runtime/shared/process-shutdown.mjs +1 -1
  369. package/src/runtime/shared/schedule-model-ref.mjs +14 -0
  370. package/src/runtime/shared/schedule-session-run.mjs +3 -13
  371. package/src/runtime/shared/schedules-db.mjs +16 -39
  372. package/src/runtime/shared/staged-child-result.mjs +2 -7
  373. package/src/runtime/shared/staged-update.mjs +1 -5
  374. package/src/runtime/shared/tool-card-model/agent-surface.mjs +8 -3
  375. package/src/runtime/shared/tool-card-model/card-status.mjs +2 -1
  376. package/src/runtime/shared/tool-card-model/displayed-result.mjs +2 -3
  377. package/src/runtime/shared/tool-card-model/generic-detail.mjs +5 -3
  378. package/src/runtime/shared/tool-card-model.golden-cases.mjs +10 -2
  379. package/src/runtime/shared/tool-card-model.golden.json +3 -12
  380. package/src/runtime/shared/tool-execution-contract.mjs +22 -26
  381. package/src/runtime/shared/tool-result-summary.mjs +7 -14
  382. package/src/runtime/shared/tool-surface.mjs +30 -33
  383. package/src/runtime/shared/tool-work-units.mjs +12 -8
  384. package/src/runtime/shared/turn-snapshot-store.mjs +2 -4
  385. package/src/runtime/shared/turn-worktree-snapshot.mjs +13 -16
  386. package/src/runtime/shared/update-checker.mjs +5 -0
  387. package/src/runtime/shared/user-data-guard.mjs +10 -17
  388. package/src/runtime/shared/webhook-session-run.mjs +3 -15
  389. package/src/runtime/tidy/history-comment.mjs +32 -27
  390. package/src/runtime/tidy/process.mjs +18 -21
  391. package/src/runtime/tidy/report.mjs +50 -9
  392. package/src/runtime/tidy/resolve.mjs +13 -14
  393. package/src/runtime/tidy/runners/air.mjs +2 -20
  394. package/src/runtime/tidy/runners/biome.mjs +4 -6
  395. package/src/runtime/tidy/runners/dprint.mjs +2 -23
  396. package/src/runtime/tidy/runners/mago.mjs +2 -20
  397. package/src/runtime/tidy/runners/ruff.mjs +2 -8
  398. package/src/runtime/tidy/runners/shared.mjs +35 -9
  399. package/src/runtime/tidy/runners/stylua.mjs +2 -20
  400. package/src/runtime/tidy/shadow.mjs +138 -0
  401. package/src/runtime/tidy/tool.mjs +33 -0
  402. package/src/runtime/tidy/worktree.mjs +114 -0
  403. package/src/runtime/web-search/index.mjs +64 -68
  404. package/src/runtime/web-search/lib/http-fetch.mjs +39 -54
  405. package/src/runtime/web-search/lib/ssrf-guard.mjs +19 -52
  406. package/src/runtime/web-search/lib/web-tools.mjs +2 -2
  407. package/src/session-runtime/config-helpers.mjs +9 -9
  408. package/src/session-runtime/context-inspection.mjs +64 -23
  409. package/src/session-runtime/context-status-cache.mjs +0 -2
  410. package/src/session-runtime/context-status-inspection.mjs +54 -25
  411. package/src/session-runtime/context-status-shape.mjs +1 -18
  412. package/src/session-runtime/goal-facade-api.mjs +15 -12
  413. package/src/session-runtime/goal-mutations.mjs +4 -3
  414. package/src/session-runtime/goal-runtime.mjs +5 -0
  415. package/src/session-runtime/goal-tasks.mjs +5 -10
  416. package/src/session-runtime/goal-text.mjs +46 -9
  417. package/src/session-runtime/goal-tool-defs.mjs +7 -8
  418. package/src/session-runtime/goal-tool-exec.mjs +1 -1
  419. package/src/session-runtime/goal-turns.mjs +54 -3
  420. package/src/session-runtime/hook-payload.mjs +2 -1
  421. package/src/session-runtime/inheritance-fit.mjs +3 -3
  422. package/src/session-runtime/lifecycle/teardown.mjs +9 -1
  423. package/src/session-runtime/mcp-glue/server-catalog.mjs +4 -6
  424. package/src/session-runtime/model-capabilities.mjs +1 -3
  425. package/src/session-runtime/notification-bus.mjs +30 -7
  426. package/src/session-runtime/plugin-mcp.mjs +0 -2
  427. package/src/session-runtime/provider-auth/accounts.mjs +2 -2
  428. package/src/session-runtime/provider-auth/login.mjs +0 -7
  429. package/src/session-runtime/provider-usage.mjs +1 -1
  430. package/src/session-runtime/runtime-paths.mjs +1 -3
  431. package/src/session-runtime/session-hooks.mjs +37 -96
  432. package/src/session-runtime/statusline-route.mjs +1 -4
  433. package/src/session-runtime/tool-catalog-schema.mjs +0 -6
  434. package/src/session-runtime/tool-catalog.mjs +10 -15
  435. package/src/session-runtime/workflow-ids.mjs +1 -2
  436. package/src/standalone/agent-task-status.mjs +1 -2
  437. package/src/standalone/agent-tool/close-flow/close-all.mjs +67 -0
  438. package/src/standalone/agent-tool/close-flow.mjs +2 -58
  439. package/src/standalone/agent-tool/execute-send.mjs +91 -0
  440. package/src/standalone/agent-tool/execute-spawn.mjs +76 -0
  441. package/src/standalone/agent-tool/execute.mjs +7 -147
  442. package/src/standalone/agent-tool/helpers.mjs +3 -6
  443. package/src/standalone/agent-tool/notify/early-completion.mjs +58 -0
  444. package/src/standalone/agent-tool/notify.mjs +7 -55
  445. package/src/standalone/agent-tool/provider-init.mjs +73 -64
  446. package/src/standalone/agent-tool/render.mjs +123 -115
  447. package/src/standalone/agent-tool/spawn-flow/spawn-plan.mjs +89 -0
  448. package/src/standalone/agent-tool/spawn-flow/spawn-prep.mjs +16 -75
  449. package/src/standalone/agent-tool/spawn-flow/spawn-run.mjs +1 -1
  450. package/src/standalone/agent-tool/worker-rows.mjs +0 -7
  451. package/src/standalone/channel-admin.mjs +13 -18
  452. package/src/standalone/channel-transport/remote-binding.mjs +10 -79
  453. package/src/standalone/channel-transport/remote-intent.mjs +52 -0
  454. package/src/standalone/channel-transport/remote-notify.mjs +15 -107
  455. package/src/standalone/channel-transport/remote-pointer.mjs +124 -0
  456. package/src/standalone/channel-transport/remote-state.mjs +47 -0
  457. package/src/standalone/daemon-agent-control.mjs +65 -0
  458. package/src/standalone/daemon-channels-loader.mjs +60 -0
  459. package/src/standalone/daemon-desktop-runtime.mjs +28 -0
  460. package/src/standalone/daemon-local-session-bridge.mjs +62 -0
  461. package/src/standalone/daemon-stored-session-views.mjs +55 -0
  462. package/src/standalone/daemon.mjs +175 -320
  463. package/src/standalone/desktop-service-registry.mjs +9 -7
  464. package/src/standalone/folder-dialog.mjs +11 -12
  465. package/src/standalone/hook-bus/command-handler.mjs +60 -49
  466. package/src/standalone/hook-bus/event-runner.mjs +7 -74
  467. package/src/standalone/hook-bus/handler-dispatch.mjs +75 -0
  468. package/src/standalone/provider-admin.mjs +14 -35
  469. package/src/standalone/session-client/attachment-calls.mjs +67 -0
  470. package/src/standalone/session-client/stream-link.mjs +85 -0
  471. package/src/standalone/session-client.mjs +31 -106
  472. package/src/standalone/session-frame-stream.mjs +4 -2
  473. package/src/standalone/session-protocol.mjs +0 -1
  474. package/src/standalone/session-runtime-agent-control-client.mjs +11 -27
  475. package/src/standalone/session-runtime-agent-control.mjs +85 -0
  476. package/src/standalone/session-runtime-host.mjs +30 -472
  477. package/src/standalone/session-runtime-inline-host/telemetry.mjs +38 -0
  478. package/src/standalone/session-runtime-inline-host.mjs +6 -37
  479. package/src/standalone/session-runtime-proxy.mjs +164 -0
  480. package/src/standalone/session-runtime-shard-router.mjs +4 -7
  481. package/src/standalone/session-runtime-spawn-leases.mjs +82 -0
  482. package/src/standalone/session-runtime-worker.mjs +27 -7
  483. package/src/standalone/session-service/agent-tree/agent-child.mjs +59 -0
  484. package/src/standalone/session-service/agent-tree/agent-turns.mjs +4 -55
  485. package/src/standalone/session-service/agent-tree.mjs +3 -1
  486. package/src/standalone/session-service/entries/disposal.mjs +72 -0
  487. package/src/standalone/session-service/entries/external-binding.mjs +6 -3
  488. package/src/standalone/session-service/entries/lifecycle.mjs +15 -59
  489. package/src/standalone/session-service/retention/busy.mjs +50 -0
  490. package/src/standalone/session-service/retention.mjs +5 -44
  491. package/src/standalone/session-service/session-calls/goal-recovery.mjs +44 -0
  492. package/src/standalone/session-service/session-calls/session-action.mjs +5 -8
  493. package/src/standalone/session-service/session-calls/session-turns.mjs +12 -42
  494. package/src/standalone/session-service/session-calls/session-views.mjs +8 -8
  495. package/src/standalone/session-transport/stack.mjs +131 -0
  496. package/src/standalone/session-transport.mjs +10 -103
  497. package/src/standalone/usage-dashboard.mjs +167 -165
  498. package/src/standalone/usage-stats-model.mjs +117 -97
  499. package/src/tui/App.jsx +27 -210
  500. package/src/tui/app/app-format.mjs +7 -0
  501. package/src/tui/app/app-view/floating-panel.jsx +229 -0
  502. package/src/tui/app/app-view/text-entry-labels.mjs +81 -0
  503. package/src/tui/app/app-view/transcript-viewport/trailing-bands.jsx +94 -0
  504. package/src/tui/app/app-view/transcript-viewport/transcript-rows.jsx +87 -0
  505. package/src/tui/app/app-view/transcript-viewport.jsx +74 -0
  506. package/src/tui/app/app-view.jsx +12 -450
  507. package/src/tui/app/core-memory-picker/core-rows.mjs +32 -0
  508. package/src/tui/app/core-memory-picker/entry-prompts.mjs +45 -0
  509. package/src/tui/app/core-memory-picker/list-panels.mjs +75 -0
  510. package/src/tui/app/core-memory-picker.mjs +26 -120
  511. package/src/tui/app/doctor.mjs +17 -12
  512. package/src/tui/app/extension-pickers/skills-pickers/project-skills.mjs +38 -0
  513. package/src/tui/app/extension-pickers/skills-pickers/skill-detail.mjs +80 -0
  514. package/src/tui/app/extension-pickers/skills-pickers/skill-items.mjs +44 -0
  515. package/src/tui/app/extension-pickers/skills-pickers.mjs +31 -137
  516. package/src/tui/app/model-picker/route-selection/context-window.mjs +47 -0
  517. package/src/tui/app/model-picker/route-selection/effort.mjs +87 -0
  518. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +84 -0
  519. package/src/tui/app/model-picker/route-selection/model-key.mjs +5 -0
  520. package/src/tui/app/model-picker/route-selection.mjs +34 -200
  521. package/src/tui/app/mouse-input/edge-autoscroll.mjs +53 -0
  522. package/src/tui/app/mouse-input/geometry.mjs +109 -0
  523. package/src/tui/app/mouse-input/sgr-buttons.mjs +14 -0
  524. package/src/tui/app/mouse-input/wheel-router.mjs +96 -0
  525. package/src/tui/app/panel-signature.mjs +58 -0
  526. package/src/tui/app/project-picker/project-service.mjs +3 -7
  527. package/src/tui/app/prompt-submit.mjs +2 -7
  528. package/src/tui/app/provider-setup/api-key-actions.mjs +7 -80
  529. package/src/tui/app/provider-setup/oauth-actions.mjs +2 -10
  530. package/src/tui/app/provider-setup/provider-items.mjs +20 -3
  531. package/src/tui/app/settings-picker/settings-toggles/cycled.mjs +4 -0
  532. package/src/tui/app/settings-picker/settings-toggles/listed-cyclers.mjs +77 -0
  533. package/src/tui/app/settings-picker/settings-toggles/store-toggles.mjs +61 -0
  534. package/src/tui/app/settings-picker/settings-toggles/theme-cycler.mjs +36 -0
  535. package/src/tui/app/settings-picker/settings-toggles.mjs +21 -149
  536. package/src/tui/app/slash-commands.mjs +14 -4
  537. package/src/tui/app/store-service-call.mjs +17 -0
  538. package/src/tui/app/text-entry-policy.mjs +1 -3
  539. package/src/tui/app/transcript-row-estimate.mjs +31 -91
  540. package/src/tui/app/transcript-window.mjs +19 -0
  541. package/src/tui/app/use-disabled-skills.mjs +52 -0
  542. package/src/tui/app/use-global-key-input/copy-first.mjs +43 -0
  543. package/src/tui/app/use-global-key-input/shift-arrow.mjs +30 -0
  544. package/src/tui/app/use-global-key-input.mjs +10 -67
  545. package/src/tui/app/use-model-catalog-cache.mjs +86 -0
  546. package/src/tui/app/use-mouse-input.mjs +64 -218
  547. package/src/tui/app/use-pasted-buffers.mjs +55 -81
  548. package/src/tui/app/use-prompt-draft-flow/draft-change.mjs +66 -0
  549. package/src/tui/app/use-prompt-draft-flow/prompt-cancels.mjs +40 -0
  550. package/src/tui/app/use-prompt-draft-flow.mjs +38 -77
  551. package/src/tui/app/use-prompt-queue-history/prompt-history.mjs +22 -0
  552. package/src/tui/app/use-prompt-queue-history/restore-to-prompt.mjs +147 -0
  553. package/src/tui/app/use-prompt-queue-history.mjs +20 -117
  554. package/src/tui/app/use-selection-focus.mjs +2 -1
  555. package/src/tui/app/use-selection-geometry.mjs +9 -12
  556. package/src/tui/app/use-selection-paint/paint-queue.mjs +90 -0
  557. package/src/tui/app/use-selection-paint/selection-clip.mjs +33 -0
  558. package/src/tui/app/use-selection-paint.mjs +31 -102
  559. package/src/tui/app/use-transcript-activity.mjs +1 -1
  560. package/src/tui/app/use-transcript-window/instance-state.mjs +1 -1
  561. package/src/tui/app/use-welcome-prompt-hint.mjs +1 -1
  562. package/src/tui/app/workflow-cycle.mjs +63 -0
  563. package/src/tui/components/AnsiText.jsx +47 -15
  564. package/src/tui/components/ItemRightHintOverprint.jsx +2 -15
  565. package/src/tui/components/Picker.jsx +9 -23
  566. package/src/tui/components/PromptInput.jsx +89 -335
  567. package/src/tui/components/QueuedCommands.jsx +1 -3
  568. package/src/tui/components/Spinner.jsx +1 -2
  569. package/src/tui/components/StatusLine.jsx +11 -21
  570. package/src/tui/components/TextEntryPanel.jsx +5 -52
  571. package/src/tui/components/ToolExecution.jsx +5 -14
  572. package/src/tui/components/TranscriptItem.jsx +0 -2
  573. package/src/tui/components/TurnDone.jsx +2 -14
  574. package/src/tui/components/prompt-input/cursor-anchor.mjs +65 -0
  575. package/src/tui/components/prompt-input/edit-helpers.mjs +1 -2
  576. package/src/tui/components/prompt-input/key-signals.mjs +122 -0
  577. package/src/tui/components/prompt-input/mouse-selection.mjs +114 -0
  578. package/src/tui/components/prompt-input/restore-policy.d.mts +0 -1
  579. package/src/tui/components/prompt-input/restore-policy.mjs +3 -8
  580. package/src/tui/components/prompt-input/selected-text.jsx +25 -0
  581. package/src/tui/components/prompt-input/undo-stack.mjs +75 -0
  582. package/src/tui/components/tool-output-format.mjs +1 -2
  583. package/src/tui/dist/index.mjs +2841 -2469
  584. package/src/tui/index.jsx +2 -2
  585. package/src/tui/markdown/measure-rendered-rows.mjs +8 -33
  586. package/src/tui/markdown/render-ansi.mjs +5 -5
  587. package/src/tui/markdown/table-layout/box-lines.mjs +46 -0
  588. package/src/tui/markdown/table-layout/cells.mjs +23 -0
  589. package/src/tui/markdown/table-layout/column-widths.mjs +65 -0
  590. package/src/tui/markdown/table-layout/vertical-lines.mjs +46 -0
  591. package/src/tui/markdown/table-layout.mjs +21 -151
  592. package/src/tui/prompt-history-store.mjs +0 -9
  593. package/src/tui/session/active-tool-summary.mjs +19 -27
  594. package/src/tui/session/agent-envelope.mjs +9 -9
  595. package/src/tui/session/live-share/owner.mjs +9 -7
  596. package/src/tui/session/live-share/retry.mjs +15 -0
  597. package/src/tui/session/live-share/viewer/frame-router.mjs +32 -0
  598. package/src/tui/session/live-share/viewer/pipe-client.mjs +108 -0
  599. package/src/tui/session/live-share/viewer.mjs +23 -114
  600. package/src/tui/session/notice-surface.mjs +1 -1
  601. package/src/tui/session/notice-text.mjs +21 -17
  602. package/src/tui/session/queue-helpers.mjs +12 -14
  603. package/src/tui/session/session-api/intake/submission.mjs +3 -8
  604. package/src/tui/session/session-api/integrations.mjs +0 -11
  605. package/src/tui/session/session-api/lifecycle/session-reset.mjs +76 -74
  606. package/src/tui/session/session-flow/drain.mjs +18 -5
  607. package/src/tui/session/session-flow/queue/entry-shape.mjs +53 -0
  608. package/src/tui/session/session-flow/queue/take-entries.mjs +82 -0
  609. package/src/tui/session/session-flow/queue.mjs +10 -112
  610. package/src/tui/session/session-flow/session-reset.mjs +2 -7
  611. package/src/tui/session/tool-approval.mjs +13 -17
  612. package/src/tui/session/tool-result-text.mjs +16 -28
  613. package/src/tui/session/transcript-spill.mjs +3 -7
  614. package/src/tui/session/transcript-store/replace-items.mjs +66 -0
  615. package/src/tui/session/transcript-store/streaming-tail.mjs +49 -0
  616. package/src/tui/session/transcript-store.mjs +16 -95
  617. package/src/tui/statusline-ansi-bridge.mjs +1 -40
  618. package/src/ui/markdown.mjs +3 -2
  619. package/src/ui/statusline-agents.mjs +9 -14
  620. package/src/ui/statusline.mjs +4 -4
  621. package/src/runtime/channels/lib/cli-worker-host.mjs +0 -2
  622. package/src/runtime/memory/lib/llm-worker-host.mjs +0 -11
  623. package/src/standalone/opencode-go-login.mjs +0 -140
@@ -0,0 +1,309 @@
1
+ // V8 coverage, folded into the one artifact that answers a single question:
2
+ // does the suite actually execute this function? Grepping test files for a
3
+ // symbol answers it wrongly whenever a helper is reached only through a public
4
+ // entry point, so the evidence comes from Node itself — NODE_V8_COVERAGE makes
5
+ // every test process dump raw V8 coverage JSON at exit, and this module folds
6
+ // those dumps (one per batch, one per test child) into per-function lines.
7
+ //
8
+ // No dependency is involved: V8 emits the raw data, `node:fs` reads it back.
9
+ import { createHash } from 'node:crypto';
10
+ import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
11
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ export const ARTIFACT_PATH = '.runtime/coverage/coverage.json';
15
+
16
+ // The runner's own lane patterns: *.test.mjs and *-test.mjs (plus the jsx/ts
17
+ // spellings used by apps/desktop). A test executing itself proves nothing.
18
+ const TEST_FILE = /[.-]test\.[cm]?[jt]sx?$/;
19
+ // tsx transpiles TypeScript before V8 sees it, so the offsets V8 reports for a
20
+ // .ts file describe the transpiled output instead of the file on disk.
21
+ const TYPESCRIPT_FILE = /\.[cm]?tsx?$/;
22
+
23
+ export const EXCLUSION_REASONS = {
24
+ 'transpiled-by-tsx':
25
+ 'TypeScript executed through tsx: V8 offsets describe the transpiled output, not this file, and this round does not translate them through source maps',
26
+ 'offsets-beyond-source':
27
+ 'recorded offsets run past the file on disk, so the executed source was generated or transformed rather than read from here',
28
+ 'source-unreadable': 'the file could not be read back while folding coverage',
29
+ };
30
+
31
+ // The identity a later query uses to tell whether the file still is what was
32
+ // measured. Content, not mtime or size: a checkout, a formatter rewriting a
33
+ // file byte-identically or a restored backup all move mtime without changing a
34
+ // single line, and an edit that swaps two lines keeps the size — either way the
35
+ // recorded line ranges would be wrong or wrongly distrusted. Folding already
36
+ // reads every source in full to translate offsets, so hashing it costs nothing
37
+ // and answers exactly. (A file absent from the artifact has no recorded
38
+ // content to compare, so its mtime against the collection time is the only
39
+ // evidence there is; `queryCoverage` uses that for the unknown-file case.)
40
+ export function sourceDigest(source) {
41
+ return createHash('sha256').update(source).digest('hex');
42
+ }
43
+
44
+ // Sources are normalized the same way at folding and at query time, so the
45
+ // digest compares like with like.
46
+ async function currentSource(rootPath, path) {
47
+ try {
48
+ return (await readFile(join(rootPath, path), 'utf8')).replace(/^\uFEFF/, '');
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ async function modifiedAfter(rootPath, path, generatedAt) {
55
+ const collected = Date.parse(generatedAt ?? '');
56
+ if (Number.isNaN(collected)) return false;
57
+ try {
58
+ return (await stat(join(rootPath, path))).mtimeMs > collected;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ function lineStarts(source) {
65
+ const starts = [0];
66
+ for (let index = 0; index < source.length; index++) {
67
+ if (source.charCodeAt(index) === 10) starts.push(index + 1);
68
+ }
69
+ return starts;
70
+ }
71
+
72
+ // 1-based line holding a 0-based character offset.
73
+ function lineOf(starts, offset) {
74
+ let low = 0;
75
+ let high = starts.length - 1;
76
+ while (low < high) {
77
+ const mid = (low + high + 1) >> 1;
78
+ if (starts[mid] <= offset) low = mid;
79
+ else high = mid - 1;
80
+ }
81
+ return low + 1;
82
+ }
83
+
84
+ // V8 keeps the script's own top-level range as a nameless function spanning the
85
+ // whole source. It is the module body, not a function anyone can call, so it
86
+ // becomes the file's `loaded` flag instead of an entry every line sits inside.
87
+ const isModuleBody = (entry, length) => entry.name === '' && entry.startOffset === 0 && entry.endOffset >= length;
88
+
89
+ function repoRelative(url, rootPath) {
90
+ if (!url.startsWith('file:')) return '';
91
+ const parsed = new URL(url);
92
+ parsed.search = '';
93
+ parsed.hash = '';
94
+ const absolute = fileURLToPath(parsed);
95
+ const path = relative(rootPath, absolute).replaceAll('\\', '/');
96
+ if (path === '' || path.startsWith('../') || isAbsolute(path)) return '';
97
+ return path;
98
+ }
99
+
100
+ // Raw V8 dumps -> { path -> [{ name, startOffset, endOffset, executed }] }.
101
+ // Every dump contributes: a function executed in any batch is executed.
102
+ function collectFunctions(reports, rootPath, excluded) {
103
+ const byFile = new Map();
104
+ for (const report of reports) {
105
+ for (const script of report?.result ?? []) {
106
+ const path = repoRelative(script.url ?? '', rootPath);
107
+ if (path === '' || path.split('/').includes('node_modules') || TEST_FILE.test(path)) continue;
108
+ if (TYPESCRIPT_FILE.test(path)) {
109
+ excluded.set(path, 'transpiled-by-tsx');
110
+ continue;
111
+ }
112
+ const functions = byFile.get(path) ?? new Map();
113
+ byFile.set(path, functions);
114
+ for (const entry of script.functions ?? []) {
115
+ const range = entry.ranges?.[0];
116
+ if (!range) continue;
117
+ const key = `${entry.functionName}\u0000${range.startOffset}\u0000${range.endOffset}`;
118
+ const merged = functions.get(key);
119
+ if (merged) merged.executed ||= range.count > 0;
120
+ else
121
+ functions.set(key, {
122
+ name: entry.functionName,
123
+ startOffset: range.startOffset,
124
+ endOffset: range.endOffset,
125
+ executed: range.count > 0,
126
+ });
127
+ }
128
+ }
129
+ }
130
+ return byFile;
131
+ }
132
+
133
+ // The artifact: per repository source file, every function V8 covered, with
134
+ // offsets translated to 1-based lines and the single bit that matters.
135
+ export async function foldCoverage(reports, { root = process.cwd() } = {}) {
136
+ const rootPath = resolve(root);
137
+ const excluded = new Map();
138
+ const collected = collectFunctions(reports, rootPath, excluded);
139
+ const files = {};
140
+ for (const path of [...collected.keys()].sort()) {
141
+ const entries = [...collected.get(path).values()].sort(
142
+ (left, right) => left.startOffset - right.startOffset || left.endOffset - right.endOffset
143
+ );
144
+ let source;
145
+ try {
146
+ source = (await readFile(join(rootPath, path), 'utf8')).replace(/^\uFEFF/, '');
147
+ } catch {
148
+ excluded.set(path, 'source-unreadable');
149
+ continue;
150
+ }
151
+ if (entries.some((entry) => entry.endOffset > source.length)) {
152
+ excluded.set(path, 'offsets-beyond-source');
153
+ continue;
154
+ }
155
+ const starts = lineStarts(source);
156
+ const body = entries.find((entry) => isModuleBody(entry, source.length));
157
+ files[path] = {
158
+ loaded: body ? body.executed : entries.some((entry) => entry.executed),
159
+ sha256: sourceDigest(source),
160
+ functions: entries
161
+ .filter((entry) => entry !== body)
162
+ .map((entry) => ({
163
+ name: entry.name || '(anonymous)',
164
+ startLine: lineOf(starts, entry.startOffset),
165
+ endLine: lineOf(starts, Math.max(entry.startOffset, entry.endOffset - 1)),
166
+ executed: entry.executed,
167
+ })),
168
+ };
169
+ }
170
+ const covered = Object.values(files);
171
+ return {
172
+ version: 2,
173
+ generatedAt: new Date().toISOString(),
174
+ root: rootPath.replaceAll('\\', '/'),
175
+ summary: {
176
+ filesWithExecutedFunctions: covered.filter((file) => file.functions.some((fn) => fn.executed)).length,
177
+ filesWithoutExecutedFunctions: covered.filter((file) => !file.functions.some((fn) => fn.executed)).length,
178
+ excludedFiles: excluded.size,
179
+ },
180
+ files,
181
+ exclusionReasons: EXCLUSION_REASONS,
182
+ excluded: [...excluded.keys()].sort().map((path) => ({ path, reason: excluded.get(path) })),
183
+ };
184
+ }
185
+
186
+ export async function readRawCoverage(directory) {
187
+ const names = (await readdir(directory)).filter((name) => name.endsWith('.json')).sort();
188
+ const reports = [];
189
+ for (const name of names) reports.push(JSON.parse(await readFile(join(directory, name), 'utf8')));
190
+ return reports;
191
+ }
192
+
193
+ export async function writeCoverageArtifact(artifact, { root = process.cwd(), artifactPath = ARTIFACT_PATH } = {}) {
194
+ const target = resolve(root, artifactPath);
195
+ await mkdir(dirname(target), { recursive: true });
196
+ await writeFile(target, `${JSON.stringify(artifact, null, 2)}\n`);
197
+ return target;
198
+ }
199
+
200
+ export async function loadCoverageArtifact({ root = process.cwd(), artifactPath = ARTIFACT_PATH } = {}) {
201
+ return JSON.parse(await readFile(resolve(root, artifactPath), 'utf8'));
202
+ }
203
+
204
+ // Is this line inside a function the suite executed? The innermost enclosing
205
+ // function answers; a line the file's functions do not cover is reported as
206
+ // such rather than borrowed from the module body around it.
207
+ //
208
+ // A recorded answer is only worth giving while the file still is the file that
209
+ // was measured, so every query re-reads the source and compares its digest
210
+ // first: a changed file answers `stale`, never EXECUTED or NOT EXECUTED. An
211
+ // artifact predating this check has no digest to match, which is itself a
212
+ // mismatch, so it announces itself too.
213
+ export async function queryCoverage(artifact, file, line, { root = process.cwd() } = {}) {
214
+ const rootPath = resolve(root);
215
+ const path = String(file).replaceAll('\\', '/').replace(/^\.\//, '');
216
+ const generatedAt = artifact.generatedAt;
217
+ const entry = artifact.files?.[path];
218
+ const excluded = artifact.excluded?.find((item) => item.path === path);
219
+ if (!entry) {
220
+ if (excluded) return { path, line, status: 'excluded', reason: excluded.reason, generatedAt };
221
+ // Nothing was recorded for this file, so there is no content to compare
222
+ // against; a file touched after the collection is the case where a test
223
+ // was added since, which is exactly how this answer misled a reader.
224
+ const changed = await modifiedAfter(rootPath, path, generatedAt);
225
+ return { path, line, status: 'unknown-file', generatedAt, changedSinceCollection: changed };
226
+ }
227
+ const source = await currentSource(rootPath, path);
228
+ if (source === null) return { path, line, status: 'stale', change: 'missing', generatedAt };
229
+ if (sourceDigest(source) !== entry.sha256) return { path, line, status: 'stale', change: 'modified', generatedAt };
230
+ const inside = entry.functions.filter((fn) => line >= fn.startLine && line <= fn.endLine);
231
+ const innermost = inside.reduce(
232
+ (best, fn) => (best === null || fn.endLine - fn.startLine <= best.endLine - best.startLine ? fn : best),
233
+ null
234
+ );
235
+ if (!innermost) return { path, line, status: 'outside-functions', loaded: entry.loaded, generatedAt };
236
+ return {
237
+ path,
238
+ line,
239
+ status: innermost.executed ? 'executed' : 'not-executed',
240
+ loaded: entry.loaded,
241
+ generatedAt,
242
+ function: innermost,
243
+ };
244
+ }
245
+
246
+ // Every answer a reader must not act on says what to do instead.
247
+ const RECOLLECT = 're-run `node scripts/test.mjs --coverage`';
248
+
249
+ export function formatCoverageAnswer(result) {
250
+ const at = `${result.path}:${result.line}`;
251
+ const collected = result.generatedAt ? `collected ${result.generatedAt}` : 'collected at an unrecorded time';
252
+ if (result.status === 'stale')
253
+ return result.change === 'missing'
254
+ ? `STALE ${at} — the file is gone since coverage was ${collected}, so nothing here can be answered: ${RECOLLECT}`
255
+ : `STALE ${at} — the file changed since coverage was ${collected}, so its recorded lines no longer describe it: ${RECOLLECT}`;
256
+ if (result.status === 'excluded')
257
+ return `EXCLUDED ${at} — not in the artifact: ${EXCLUSION_REASONS[result.reason] ?? result.reason}`;
258
+ if (result.status === 'unknown-file')
259
+ return result.changedSinceCollection
260
+ ? `STALE ${at} — no coverage was recorded for this file and it changed after coverage was ${collected}, so a test may cover it now: ${RECOLLECT}`
261
+ : `NO COVERAGE ${at} — the run ${collected} recorded no coverage for this file; if a test was added since, ${RECOLLECT}`;
262
+ if (result.status === 'outside-functions')
263
+ return `NO FUNCTION ${at} — outside every covered function (module body ${result.loaded ? 'ran' : 'never ran'})`;
264
+ const where = `${result.function.name} (lines ${result.function.startLine}-${result.function.endLine})`;
265
+ return result.status === 'executed'
266
+ ? `EXECUTED ${at} — inside ${where}, which the suite ran`
267
+ : `NOT EXECUTED ${at} — inside ${where}, which no test ran`;
268
+ }
269
+
270
+ // The whole-artifact signal: how much of what was measured no longer exists as
271
+ // measured, and when it was measured, so a caller can decide to re-collect
272
+ // before asking anything at all.
273
+ export async function coverageStaleness(artifact, { root = process.cwd() } = {}) {
274
+ const rootPath = resolve(root);
275
+ const changed = [];
276
+ const missing = [];
277
+ for (const [path, entry] of Object.entries(artifact.files ?? {})) {
278
+ const source = await currentSource(rootPath, path);
279
+ if (source === null) missing.push(path);
280
+ else if (sourceDigest(source) !== entry.sha256) changed.push(path);
281
+ }
282
+ return {
283
+ generatedAt: artifact.generatedAt,
284
+ files: Object.keys(artifact.files ?? {}).length,
285
+ changed: changed.sort(),
286
+ missing: missing.sort(),
287
+ changedCount: changed.length + missing.length,
288
+ stale: changed.length + missing.length > 0,
289
+ };
290
+ }
291
+
292
+ export function formatStaleness(state) {
293
+ const collected = state.generatedAt ? `collected ${state.generatedAt}` : 'collected at an unrecorded time';
294
+ return state.stale
295
+ ? `STALE coverage ${collected}: ${state.changedCount} of ${state.files} files changed since (${state.missing.length} gone); ${RECOLLECT}`
296
+ : `FRESH coverage ${collected}: all ${state.files} files unchanged since`;
297
+ }
298
+
299
+ // Called once per `--coverage` run, after the last batch has exited.
300
+ export async function reportCoverage(rawDirectory, { root = process.cwd(), log = console.error } = {}) {
301
+ const artifact = await foldCoverage(await readRawCoverage(rawDirectory), { root });
302
+ const target = await writeCoverageArtifact(artifact, { root });
303
+ const { filesWithExecutedFunctions, filesWithoutExecutedFunctions, excludedFiles } = artifact.summary;
304
+ log(
305
+ `Coverage: ${filesWithExecutedFunctions} files with executed functions, ${filesWithoutExecutedFunctions} without, ${excludedFiles} excluded`
306
+ );
307
+ log(`Coverage artifact: ${target}`);
308
+ return target;
309
+ }
@@ -97,6 +97,8 @@ export function buildEvaluation(caseFile, kase) {
97
97
  : [];
98
98
  if (typeof query !== 'string' || !query.trim() || targets.length === 0) return null;
99
99
  const temporal = parsePeriod(String(kase?.args?.period || ''), true);
100
+ const startMs = Number(temporal?.startMs);
101
+ const endMs = Number(temporal?.endMs);
100
102
  return {
101
103
  id: String(kase.id),
102
104
  caseFile,
@@ -114,8 +116,8 @@ export function buildEvaluation(caseFile, kase) {
114
116
  .toLowerCase()
115
117
  )
116
118
  .filter(Boolean),
117
- startMs: Number.isFinite(Number(temporal?.startMs)) ? Number(temporal.startMs) : null,
118
- endMs: Number.isFinite(Number(temporal?.endMs)) ? Number(temporal.endMs) : null,
119
+ startMs: Number.isFinite(startMs) ? startMs : null,
120
+ endMs: Number.isFinite(endMs) ? endMs : null,
119
121
  },
120
122
  };
121
123
  }
@@ -184,12 +184,12 @@ export function scoreWithinPeriod(items, temporal, toleranceMs = 60_000) {
184
184
  const endMs = Number(temporal?.endMs);
185
185
  const hasStart = Number.isFinite(startMs);
186
186
  const hasEnd = Number.isFinite(endMs);
187
- const offenders = items.filter(
187
+ const timestamped = items.filter((item) => item.timestampMs !== null);
188
+ const offenders = timestamped.filter(
188
189
  (item) =>
189
- item.timestampMs !== null &&
190
- ((hasStart && item.timestampMs < startMs - toleranceMs) || (hasEnd && item.timestampMs > endMs + toleranceMs))
190
+ (hasStart && item.timestampMs < startMs - toleranceMs) || (hasEnd && item.timestampMs > endMs + toleranceMs)
191
191
  );
192
- return { checked: items.filter((item) => item.timestampMs !== null).length, offenders, ok: offenders.length === 0 };
192
+ return { checked: timestamped.length, offenders, ok: offenders.length === 0 };
193
193
  }
194
194
 
195
195
  export function evaluateCase(kase, outcome, quality, recency, allContain, withinPeriod, pageOrder) {
@@ -1,28 +1,152 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { mkdtemp } from 'node:fs/promises';
2
+ import { appendFile, mkdtemp, readFile, rm } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
+ import { FAILURE_RECORDS_ENV } from './test-failure-records.mjs';
6
+ import { classifyRerunOutcomes, formatFailureSummary } from './test-failure-summary.mjs';
5
7
 
6
8
  const SUMMARY_REPORTER = new URL('./test-summary-reporter.mjs', import.meta.url).href;
7
9
 
10
+ // A whole-suite run passes ~700 paths to one node process. Windows caps a
11
+ // command line at 32767 characters (CreateProcess) and POSIX caps argv+env at
12
+ // ARG_MAX, so the file list is spawned in batches that stay under the cap
13
+ // instead of dying with spawn ENAMETOOLONG before any test runs.
14
+ export const ARG_BUDGET = process.platform === 'win32' ? 30_000 : 1_000_000;
15
+
16
+ // An argument costs its own characters plus the separator and its quotes.
17
+ const argCost = (arg) => arg.length + 3;
18
+
19
+ // Batches keep the file order Node was given; a single path larger than the
20
+ // budget still gets its own batch rather than being dropped. An empty list
21
+ // stays one batch: Node's own discovery is what an argument-less run means.
22
+ export function chunkFileArgs(fileArgs, budget) {
23
+ const chunks = [[]];
24
+ let cost = 0;
25
+ for (const file of fileArgs) {
26
+ const current = chunks.at(-1);
27
+ if (current.length > 0 && cost + argCost(file) > budget) {
28
+ chunks.push([file]);
29
+ cost = argCost(file);
30
+ continue;
31
+ }
32
+ current.push(file);
33
+ cost += argCost(file);
34
+ }
35
+ return chunks;
36
+ }
37
+
38
+ function spawnBatch(args, env) {
39
+ return new Promise((resolve) => {
40
+ const child = spawn(process.execPath, args, { stdio: 'inherit', env });
41
+ child.on('error', (error) => {
42
+ console.error(error);
43
+ resolve(1);
44
+ });
45
+ child.on('exit', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
46
+ });
47
+ }
48
+
49
+ // The failure reporter writes one JSON line per failed test; a spawn that
50
+ // died before Node opened its destination leaves no file at all.
51
+ async function readFailureRecords(path) {
52
+ const text = await readFile(path, 'utf8').catch(() => '');
53
+ return text
54
+ .split('\n')
55
+ .filter(Boolean)
56
+ .map((line) => JSON.parse(line));
57
+ }
58
+
8
59
  // Both discovery and explicit-file runs use the same output contract. Node
9
- // still owns test selection, diagnostics, skip/todo counts and exit status.
10
- export async function runNodeTests(nodeArgs, fileArgs) {
60
+ // still owns test selection, diagnostics, skip/todo counts and exit status;
61
+ // a batched run keeps one full log and fails on the first failing batch.
62
+ //
63
+ // With `coverage`, every batch inherits one fresh NODE_V8_COVERAGE directory,
64
+ // so V8 writes a dump per test process into it and the batches fold into a
65
+ // single artifact. Without it nothing here changes — not the spawn options,
66
+ // not the environment, not the output.
67
+ //
68
+ // `rerunFailed` (default 0 = off) re-runs the files that failed, up to n
69
+ // times. A test that passes on a re-run is reported as flaky and, because the
70
+ // flag was passed, does not fail the run; a test that fails every re-run
71
+ // still exits 1. Every run ends with the failure summary block, so a filtered
72
+ // log can still name what failed.
73
+ export async function runNodeTests(
74
+ nodeArgs,
75
+ fileArgs,
76
+ { argBudget = ARG_BUDGET, coverage = false, rerunFailed = 0 } = {}
77
+ ) {
11
78
  const logPath = join(await mkdtemp(join(tmpdir(), 'mixdog-test-output-')), 'full.log');
12
79
  console.error(`Full test log: ${logPath}`);
13
- const child = spawn(process.execPath, [
80
+ // Failure records live outside the log directory: the log directory holds
81
+ // exactly one merged log, and these records are an internal artifact.
82
+ const failureDir = await mkdtemp(join(tmpdir(), 'mixdog-test-failures-'));
83
+ const runArgs = [
14
84
  ...nodeArgs,
15
85
  `--test-reporter=${SUMMARY_REPORTER}`,
16
86
  '--test-reporter-destination=stdout',
17
87
  '--test-reporter=spec',
18
- `--test-reporter-destination=${logPath}`,
19
- ...fileArgs,
20
- ], { stdio: 'inherit' });
21
- child.on('error', (error) => {
22
- console.error(error);
23
- process.exitCode = 1;
24
- });
25
- child.on('exit', (code, signal) => {
26
- process.exitCode = code ?? (signal ? 1 : 0);
88
+ ];
89
+ const fixedCost =
90
+ argCost(process.execPath) +
91
+ runArgs.reduce((total, arg) => total + argCost(arg), 0) +
92
+ argCost(`--test-reporter-destination=${logPath}`) +
93
+ argCost('.999'); // the per-batch log suffix
94
+ const batches = chunkFileArgs(fileArgs, Math.max(argBudget - fixedCost, 1));
95
+ const coverageDir = coverage ? await mkdtemp(join(tmpdir(), 'mixdog-v8-coverage-')) : '';
96
+ // Failure records travel by environment, not argv: the command line is
97
+ // already budgeted, and a third reporter would warn about listeners.
98
+ const childEnv = (recordsPath) => ({
99
+ ...process.env,
100
+ [FAILURE_RECORDS_ENV]: recordsPath,
101
+ ...(coverageDir ? { NODE_V8_COVERAGE: coverageDir } : {}),
27
102
  });
103
+ let status = 0;
104
+ const failures = [];
105
+ for (const [index, batch] of batches.entries()) {
106
+ const batchLog = batches.length === 1 ? logPath : `${logPath}.${index + 1}`;
107
+ const batchFailures = join(failureDir, `batch-${index + 1}.jsonl`);
108
+ const code = await spawnBatch(
109
+ [...runArgs, `--test-reporter-destination=${batchLog}`, ...batch],
110
+ childEnv(batchFailures)
111
+ );
112
+ if (batchLog !== logPath) {
113
+ await appendFile(logPath, await readFile(batchLog));
114
+ await rm(batchLog, { force: true });
115
+ }
116
+ failures.push(...(await readFailureRecords(batchFailures)));
117
+ if (status === 0) status = code;
118
+ }
119
+
120
+ const attempts = [];
121
+ let lastAttemptStatus = 0;
122
+ let pending = failures;
123
+ for (let attempt = 1; attempt <= rerunFailed && pending.length > 0; attempt += 1) {
124
+ const files = [...new Set(pending.map((record) => record.file))];
125
+ console.error(`Re-running ${files.length} failed file(s): attempt ${attempt}/${rerunFailed}`);
126
+ const attemptLog = `${logPath}.rerun-${attempt}`;
127
+ const attemptFailures = join(failureDir, `rerun-${attempt}.jsonl`);
128
+ lastAttemptStatus = await spawnBatch(
129
+ [...runArgs, `--test-reporter-destination=${attemptLog}`, ...files],
130
+ childEnv(attemptFailures)
131
+ );
132
+ await appendFile(logPath, await readFile(attemptLog));
133
+ await rm(attemptLog, { force: true });
134
+ attempts.push({ files, failures: await readFailureRecords(attemptFailures) });
135
+ pending = classifyRerunOutcomes(failures, attempts).failed;
136
+ }
137
+ const { failed, flaky } = classifyRerunOutcomes(failures, attempts);
138
+ // Flaky tolerance applies only to an ordinary test-failure exit that the
139
+ // re-runs fully explained: nothing failed twice and the last re-run itself
140
+ // came back clean.
141
+ if (rerunFailed > 0 && status === 1 && failed.length === 0 && flaky.length > 0 && lastAttemptStatus === 0) status = 0;
142
+ process.exitCode = status;
143
+
144
+ await rm(failureDir, { recursive: true, force: true });
145
+ if (coverageDir) {
146
+ const { reportCoverage } = await import('./coverage.mjs');
147
+ await reportCoverage(coverageDir);
148
+ await rm(coverageDir, { recursive: true, force: true });
149
+ }
150
+ // Last line of the run, after every reporter and the coverage report.
151
+ process.stdout.write(`\n${formatFailureSummary({ failed, flaky, rerunFailed })}`);
28
152
  }
@@ -0,0 +1,46 @@
1
+ // Structured failure records for the run-level failure summary.
2
+ //
3
+ // Node keeps ownership of diagnostics, totals and exit status; this only
4
+ // records "which test failed, in which file" as one JSON line each, so the
5
+ // runner can name every failure after all other output even when the run log
6
+ // was filtered (scripts/lib/run-node-tests.mjs).
7
+ //
8
+ // It rides the existing summary reporter instead of adding a third node:test
9
+ // reporter: a third reporter pushes the shared TestsStream past its listener
10
+ // cap and every run would start with a MaxListenersExceededWarning.
11
+ import { appendFileSync } from 'node:fs';
12
+ import { relative } from 'node:path';
13
+
14
+ export const FAILURE_RECORDS_ENV = 'MIXDOG_TEST_FAILURE_RECORDS';
15
+
16
+ // Ancestor names are tracked per file: node:test runs files concurrently, so
17
+ // one flat stack would mix their nestings.
18
+ export function failureRecord(event, stacks) {
19
+ const file = event.data?.file ? relative(process.cwd(), String(event.data.file)).replaceAll('\\', '/') : '';
20
+ if (event.type === 'test:start') {
21
+ const stack = stacks.get(file) ?? [];
22
+ stack.length = event.data.nesting;
23
+ stack[event.data.nesting] = event.data.name;
24
+ stacks.set(file, stack);
25
+ return null;
26
+ }
27
+ if (event.type !== 'test:fail') return null;
28
+ // A failing todo does not fail the run, and a suite reports its children's
29
+ // failures as its own; only the leaves that decide the exit status belong
30
+ // in the summary.
31
+ if (event.data.todo) return null;
32
+ if (event.data.details?.error?.failureType === 'subtestsFailed') return null;
33
+ const ancestors = (stacks.get(file) ?? []).slice(0, event.data.nesting);
34
+ return { file, name: [...ancestors, event.data.name].join(' > ') };
35
+ }
36
+
37
+ // The runner sets FAILURE_RECORDS_ENV to a per-spawn JSONL path. Without it
38
+ // nothing is recorded, so a direct `node --test` run is unaffected.
39
+ export function createFailureRecorder(path = process.env[FAILURE_RECORDS_ENV]) {
40
+ if (!path) return () => {};
41
+ const stacks = new Map();
42
+ return (event) => {
43
+ const record = failureRecord(event, stacks);
44
+ if (record) appendFileSync(path, `${JSON.stringify(record)}\n`);
45
+ };
46
+ }
@@ -0,0 +1,59 @@
1
+ // Run-level failure classification and the final summary block.
2
+ //
3
+ // A filtered run log loses a failure: `ℹ fail 1` says a test failed but never
4
+ // which one. Every line here carries SUMMARY_TAG and names its file and test,
5
+ // so the block survives `grep`/`Select-String` filtering, and the runner
6
+ // prints it after all other output (scripts/lib/run-node-tests.mjs).
7
+ export const SUMMARY_TAG = '[failure-summary]';
8
+
9
+ const keyOf = (record) => `${record.file}\u0000${record.name}`;
10
+ const byLocation = (a, b) => (a.file === b.file ? a.name.localeCompare(b.name) : a.file.localeCompare(b.file));
11
+
12
+ // A test that failed and then passed inside the re-run budget is flaky; one
13
+ // that fails every attempt stays failed. A failure that only appears during a
14
+ // re-run joins the failed set from the attempt that produced it, so a re-run
15
+ // can never hide a test it just broke.
16
+ //
17
+ // attempts: [{ files: [rerun file paths], failures: [{ file, name }] }]
18
+ export function classifyRerunOutcomes(initialFailures, attempts = []) {
19
+ const pending = new Map(initialFailures.map((record) => [keyOf(record), { ...record }]));
20
+ const flaky = new Map();
21
+ for (const [index, attempt] of attempts.entries()) {
22
+ const attemptNumber = index + 1;
23
+ const rerunFiles = new Set(attempt.files);
24
+ const stillFailing = new Set(attempt.failures.map(keyOf));
25
+ for (const [key, record] of [...pending]) {
26
+ if (!rerunFiles.has(record.file) || stillFailing.has(key)) continue;
27
+ pending.delete(key);
28
+ flaky.set(key, { ...record, passedOnAttempt: attemptNumber });
29
+ }
30
+ for (const record of attempt.failures) {
31
+ const key = keyOf(record);
32
+ if (pending.has(key) || flaky.has(key)) continue;
33
+ pending.set(key, { ...record, firstSeenOnRerun: attemptNumber });
34
+ }
35
+ }
36
+ return { failed: [...pending.values()].sort(byLocation), flaky: [...flaky.values()].sort(byLocation) };
37
+ }
38
+
39
+ // Exit-code policy, stated wherever the classification is shown.
40
+ export function policyLine(rerunFailed) {
41
+ return rerunFailed > 0
42
+ ? `policy: --rerun-failed ${rerunFailed} is set, so a flaky test does not fail the run; a test that fails every re-run still exits 1`
43
+ : 'policy: --rerun-failed is off, so every failure fails the run (pass --rerun-failed <n> to classify flaky)';
44
+ }
45
+
46
+ export function formatFailureSummary({ failed = [], flaky = [], rerunFailed = 0 }) {
47
+ const lines = [`${SUMMARY_TAG} ${failed.length} failed, ${flaky.length} flaky (every failed test is named below)`];
48
+ for (const record of failed) {
49
+ const origin = record.firstSeenOnRerun ? ` (first seen on re-run ${record.firstSeenOnRerun})` : '';
50
+ lines.push(`${SUMMARY_TAG} FAILED ${record.file} > ${record.name}${origin}`);
51
+ }
52
+ for (const record of flaky) {
53
+ lines.push(
54
+ `${SUMMARY_TAG} FLAKY ${record.file} > ${record.name} (failed, then passed on re-run ${record.passedOnAttempt}/${rerunFailed})`
55
+ );
56
+ }
57
+ if (failed.length > 0 || flaky.length > 0) lines.push(`${SUMMARY_TAG} ${policyLine(rerunFailed)}`);
58
+ return `${lines.join('\n')}\n`;
59
+ }
@@ -1,14 +1,18 @@
1
1
  import { Readable } from 'node:stream';
2
2
  import { spec } from 'node:test/reporters';
3
+ import { createFailureRecorder } from './test-failure-records.mjs';
3
4
  import timingReporter from './test-timing-reporter.mjs';
4
5
 
5
6
  // Filter structured success events, never arbitrary stdout/stderr text.
6
7
  // Node's own reporter still owns failure diagnostics, warnings and totals.
7
- // The runner writes an unfiltered spec report alongside this summary.
8
+ // The runner writes an unfiltered spec report alongside this summary, and
9
+ // every failure is also recorded for the run's final failure summary.
8
10
  export default async function* summaryReporter(source) {
9
11
  const timings = [];
12
+ const recordFailure = createFailureRecorder();
10
13
  async function* events() {
11
14
  for await (const event of source) {
15
+ recordFailure(event);
12
16
  if ((event.type === 'test:pass' || event.type === 'test:fail') && event.data.nesting === 0) {
13
17
  timings.push({
14
18
  type: event.type,
@@ -17,8 +21,8 @@ export default async function* summaryReporter(source) {
17
21
  }
18
22
  // A start without its matching pass leaves a pending name in spec's
19
23
  // parent stack. Suppress both; fail events carry their own location.
20
- if (event.type !== 'test:start' &&
21
- (event.type !== 'test:pass' || event.data.skip || event.data.todo)) yield event;
24
+ if (event.type !== 'test:start' && (event.type !== 'test:pass' || event.data.skip || event.data.todo))
25
+ yield event;
22
26
  }
23
27
  }
24
28
  yield* Readable.from(events()).pipe(new spec());
@@ -29,10 +29,8 @@ process.on('SIGINT', cleanup);
29
29
  process.stdin.on('data', (buf) => {
30
30
  const s = buf.toString('utf8');
31
31
  if (s === 'q' || s === '\x03') return cleanup();
32
- let m;
33
- MOUSE.lastIndex = 0;
34
32
  let sawMouse = false;
35
- while ((m = MOUSE.exec(s)) !== null) {
33
+ for (const m of s.matchAll(MOUSE)) {
36
34
  sawMouse = true;
37
35
  const b = Number(m[1]);
38
36
  const mods = [b & 4 ? 'shift' : '', b & 8 ? 'alt' : '', b & 16 ? 'ctrl' : ''].filter(Boolean).join('+') || 'none';