mixdog 1.4.1 → 1.4.3

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 (630) hide show
  1. package/package.json +4 -3
  2. package/scripts/build-shipped-history.mjs +125 -33
  3. package/scripts/gate-local.mjs +220 -63
  4. package/scripts/graph-parity.mjs +8 -3
  5. package/scripts/lib/gate-scheduler.mjs +139 -32
  6. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  7. package/scripts/lib/run-node-tests.mjs +71 -34
  8. package/scripts/lib/scoped-temp.mjs +1 -0
  9. package/scripts/lib/test-failure-records.mjs +14 -1
  10. package/scripts/lib/test-failure-summary.mjs +9 -4
  11. package/scripts/lib/test-spawn-ledger.mjs +43 -3
  12. package/scripts/post-release-check.mjs +26 -16
  13. package/scripts/pptx-kit-index.mjs +5 -6
  14. package/scripts/provider-pingpong-live.mjs +1 -1
  15. package/scripts/prune-embedding-runtime.mjs +3 -1
  16. package/scripts/release-paths.mjs +7 -1
  17. package/scripts/suite-health.mjs +110 -28
  18. package/scripts/test.mjs +9 -14
  19. package/src/agents/advisor/AGENT.md +4 -4
  20. package/src/agents/advisor/agent.json +1 -1
  21. package/src/agents/explore/AGENT.md +22 -0
  22. package/src/agents/explore/agent.json +6 -0
  23. package/src/cli.mjs +7 -0
  24. package/src/defaults/agents.json +7 -0
  25. package/src/defaults/shipped-history.json +38 -2
  26. package/src/defaults/skills/browser-use/SKILL.md +4 -1
  27. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  28. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  29. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  30. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  31. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  32. package/src/defaults/skills/computer-use/SKILL.md +12 -2
  33. package/src/defaults/skills/setup/SKILL.md +2 -2
  34. package/src/defaults/skills/setup/references/actions.md +28 -9
  35. package/src/defaults/skills/setup/references/surfaces.md +5 -2
  36. package/src/defaults/skills/setup-guide/SKILL.md +11 -3
  37. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  38. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  39. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  40. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  41. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  42. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  43. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  44. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  45. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  46. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  47. package/src/defaults/skills/visualize/SKILL.md +6 -2
  48. package/src/lib/instruction-text.cjs +36 -0
  49. package/src/lib/rules-builder.cjs +40 -50
  50. package/src/orchestration/balanced.md +23 -0
  51. package/src/orchestration/focused.md +22 -0
  52. package/src/orchestration/none.md +5 -0
  53. package/src/orchestration/swarm.md +24 -0
  54. package/src/rules/agent-mode/AGENT_MODE.md +3 -1
  55. package/src/rules/agent-mode/MIXY.md +2 -2
  56. package/src/rules/lead/LEAD.md +3 -3
  57. package/src/rules/shared/20-research.md +3 -6
  58. package/src/rules/shared/40-editing.md +1 -0
  59. package/src/rules/shared/60-verification.md +10 -9
  60. package/src/rules/shared/70-delivery.md +4 -3
  61. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  62. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -2
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +132 -21
  65. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -9
  66. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
  69. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  70. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  71. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +7 -7
  72. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -30
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +124 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +204 -49
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +16 -7
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +157 -57
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +54 -23
  79. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
  81. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
  82. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
  84. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +43 -27
  85. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +43 -8
  86. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +30 -28
  87. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
  89. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
  90. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  91. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  92. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  93. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  95. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  96. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  97. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  98. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  99. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
  102. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +48 -31
  103. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  104. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  105. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  106. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  107. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  109. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
  110. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
  111. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
  112. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +26 -28
  113. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +3 -1
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +1 -0
  116. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  118. package/src/runtime/agent/orchestrator/providers/registry.mjs +33 -2
  119. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  120. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  121. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  122. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +576 -0
  123. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
  124. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +32 -0
  125. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
  126. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
  127. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
  129. package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
  130. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
  131. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
  132. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
  133. package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
  134. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
  136. package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
  137. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
  138. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
  139. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
  140. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -47
  141. package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
  142. package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
  143. package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +12 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
  146. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +4 -1
  147. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +15 -6
  148. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  149. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
  150. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
  151. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  152. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  153. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
  154. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +94 -10
  155. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
  156. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
  158. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
  159. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
  160. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
  161. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +4 -4
  162. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +11 -6
  163. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +6 -6
  164. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
  165. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +11 -1
  166. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  167. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +37 -8
  169. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
  170. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
  172. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
  174. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
  175. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
  176. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
  177. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
  178. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
  179. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
  180. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
  181. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
  182. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +27 -3
  183. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +18 -2
  184. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  185. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  186. package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
  187. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
  188. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +103 -50
  189. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
  190. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
  191. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
  192. package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
  193. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
  194. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
  195. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
  196. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
  197. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
  198. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  199. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
  200. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
  201. package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
  202. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
  203. package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
  204. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +20 -0
  205. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -14
  206. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
  207. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +12 -0
  208. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
  210. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +56 -14
  211. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
  212. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +59 -20
  213. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
  214. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  215. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  216. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +18 -4
  217. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
  218. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
  219. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
  220. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
  221. package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
  222. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +48 -30
  223. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  224. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
  225. package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
  226. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +12 -6
  227. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
  228. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +1 -1
  229. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
  230. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
  231. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
  232. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
  233. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
  234. package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
  235. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
  236. package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
  237. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +243 -38
  238. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  239. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +177 -8
  240. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  241. package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
  242. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
  243. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
  244. package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
  245. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
  246. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
  247. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
  248. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
  249. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  250. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
  251. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  252. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
  253. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
  254. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +42 -28
  255. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +30 -14
  256. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
  257. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  258. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
  259. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
  260. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  261. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
  262. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +95 -20
  263. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  264. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
  265. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
  266. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
  267. package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
  268. package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
  269. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
  270. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
  271. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
  272. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  273. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  274. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  275. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +105 -15
  276. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  277. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  278. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  279. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +35 -11
  280. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +2 -0
  281. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  282. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -6
  283. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
  284. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  285. package/src/runtime/attachments/attachment-reference.mjs +1 -1
  286. package/src/runtime/attachments/pdf-extract.mjs +1 -1
  287. package/src/runtime/attachments/store.d.mts +1 -0
  288. package/src/runtime/attachments/store.mjs +2 -2
  289. package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
  290. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
  291. package/src/runtime/channels/lib/scheduler.mjs +33 -0
  292. package/src/runtime/channels/lib/schedules-db.mjs +11 -4
  293. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
  294. package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
  295. package/src/runtime/channels/lib/whisper-port.mjs +19 -10
  296. package/src/runtime/channels/lib/whisper-server.mjs +22 -13
  297. package/src/runtime/channels/lib/worker-main.mjs +9 -31
  298. package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
  299. package/src/runtime/connectors/connector-accounts-mcp.mjs +372 -103
  300. package/src/runtime/connectors/gmail-app-password.mjs +4 -4
  301. package/src/runtime/connectors/google-mcp.mjs +1 -1
  302. package/src/runtime/effort-judge/judge-client.mjs +2 -2
  303. package/src/runtime/effort-judge/judge-input.mjs +4 -4
  304. package/src/runtime/explore/explorer.mjs +88 -0
  305. package/src/runtime/explore/tool-defs.mjs +35 -0
  306. package/src/runtime/explore/tool.mjs +123 -0
  307. package/src/runtime/local-provider/hardware.mjs +1 -1
  308. package/src/runtime/media/adapters/codex-image.mjs +3 -3
  309. package/src/runtime/media/adapters/gemini-video.mjs +1 -1
  310. package/src/runtime/media/media-usage.mjs +1 -1
  311. package/src/runtime/media/store.mjs +1 -1
  312. package/src/runtime/memory/index.mjs +15 -1
  313. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  314. package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
  315. package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
  316. package/src/runtime/memory/lib/memory-embed.mjs +83 -14
  317. package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
  318. package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
  319. package/src/runtime/memory/lib/memory.mjs +17 -0
  320. package/src/runtime/memory/lib/pg/process.mjs +1 -1
  321. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
  322. package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
  323. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  324. package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
  325. package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
  326. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  327. package/src/runtime/office/capabilities.mjs +1 -1
  328. package/src/runtime/office/core/office-core.mjs +1 -1
  329. package/src/runtime/office/design/content-model.mjs +3 -3
  330. package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
  331. package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
  332. package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
  333. package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
  334. package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
  335. package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
  336. package/src/runtime/office/portable/portable-soffice.mjs +8 -6
  337. package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
  338. package/src/runtime/office/quality/assurance-structure-docx.mjs +1 -1
  339. package/src/runtime/shared/agent-route-config.mjs +17 -2
  340. package/src/runtime/shared/atomic-file.d.mts +8 -0
  341. package/src/runtime/shared/atomic-file.mjs +103 -2
  342. package/src/runtime/shared/background-tasks.mjs +37 -10
  343. package/src/runtime/shared/background-wait.mjs +66 -0
  344. package/src/runtime/shared/child-guardian.mjs +17 -7
  345. package/src/runtime/shared/config.mjs +313 -16
  346. package/src/runtime/shared/connector-accounts.mjs +5 -5
  347. package/src/runtime/shared/connector-catalog.d.mts +14 -8
  348. package/src/runtime/shared/connector-catalog.mjs +70 -52
  349. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  350. package/src/runtime/shared/file-has-data.mjs +26 -2
  351. package/src/runtime/shared/file-lock.mjs +21 -18
  352. package/src/runtime/shared/git-launch.d.mts +9 -0
  353. package/src/runtime/shared/git-launch.mjs +213 -0
  354. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  355. package/src/runtime/shared/git-runner.mjs +7 -2
  356. package/src/runtime/shared/idle-gc.mjs +30 -7
  357. package/src/runtime/shared/llm/cost.mjs +6 -1
  358. package/src/runtime/shared/llm/http-agent.mjs +316 -29
  359. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  360. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  361. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  362. package/src/runtime/shared/llm/system-proxy.mjs +453 -0
  363. package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
  364. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
  365. package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
  366. package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
  367. package/src/runtime/shared/maintenance-schedule.mjs +109 -0
  368. package/src/runtime/shared/process-runner.mjs +8 -4
  369. package/src/runtime/shared/provider-accounts.mjs +52 -2
  370. package/src/runtime/shared/routine-trigger.d.mts +13 -0
  371. package/src/runtime/shared/routine-trigger.mjs +34 -0
  372. package/src/runtime/shared/run-child.d.mts +2 -1
  373. package/src/runtime/shared/run-child.mjs +70 -18
  374. package/src/runtime/shared/session-scratch.mjs +14 -2
  375. package/src/runtime/shared/shipped-definitions.mjs +7 -1
  376. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  377. package/src/runtime/shared/staged-update.mjs +47 -2
  378. package/src/runtime/shared/tool-card-model.golden.json +3 -12
  379. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  380. package/src/runtime/shared/tool-surface.mjs +110 -36
  381. package/src/runtime/shared/tool-work-units.mjs +35 -6
  382. package/src/runtime/shared/turn-snapshot.mjs +26 -4
  383. package/src/runtime/shared/turn-worktree-snapshot.mjs +409 -54
  384. package/src/runtime/shared/worker-requests.mjs +17 -3
  385. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  386. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  387. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  388. package/src/runtime/shared/worktrees/branch-management.mjs +5 -3
  389. package/src/runtime/shared/worktrees/contract.mjs +16 -6
  390. package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
  391. package/src/runtime/shared/worktrees/disk.mjs +79 -2
  392. package/src/runtime/shared/worktrees/git.mjs +97 -7
  393. package/src/runtime/shared/worktrees/include.mjs +6 -4
  394. package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
  395. package/src/runtime/shared/worktrees/manager.mjs +962 -197
  396. package/src/runtime/shared/worktrees/settings.mjs +33 -7
  397. package/src/runtime/shared/worktrees/test-support.mjs +5 -1
  398. package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
  399. package/src/runtime/tidy/install.mjs +95 -15
  400. package/src/runtime/tidy/languages.mjs +17 -15
  401. package/src/runtime/tidy/process.mjs +9 -5
  402. package/src/runtime/tidy/resolve.mjs +19 -16
  403. package/src/runtime/tidy/runners/biome.mjs +3 -3
  404. package/src/runtime/tidy/tool.mjs +1 -1
  405. package/src/runtime/tidy/worktree.mjs +10 -6
  406. package/src/runtime/visualize-bridge/client.mjs +4 -1
  407. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  408. package/src/runtime/web-search/index.mjs +4 -23
  409. package/src/runtime/web-search/lib/web-tools.mjs +2 -2
  410. package/src/runtime/web-search/tool-defs.mjs +17 -26
  411. package/src/session-runtime/agent-profile-changes.mjs +55 -4
  412. package/src/session-runtime/agent-task-sessions.mjs +410 -353
  413. package/src/session-runtime/boot/apis.mjs +27 -4
  414. package/src/session-runtime/boot/begin.mjs +2 -0
  415. package/src/session-runtime/boot/config.mjs +10 -1
  416. package/src/session-runtime/boot/facade.mjs +1 -0
  417. package/src/session-runtime/boot/providers.mjs +70 -3
  418. package/src/session-runtime/boot/session.mjs +2 -0
  419. package/src/session-runtime/boot/tools.mjs +71 -25
  420. package/src/session-runtime/bot-status.mjs +103 -103
  421. package/src/session-runtime/channel-config-api.mjs +1 -0
  422. package/src/session-runtime/config-change-effects.mjs +179 -0
  423. package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
  424. package/src/session-runtime/config-lifecycle.mjs +160 -24
  425. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  426. package/src/session-runtime/conversation-import/discovery.mjs +245 -47
  427. package/src/session-runtime/conversation-import/service.mjs +12 -5
  428. package/src/session-runtime/goal-facade-api.mjs +49 -3
  429. package/src/session-runtime/handoff-attachments.mjs +1 -1
  430. package/src/session-runtime/held-requests.mjs +3 -1
  431. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
  432. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
  433. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -11
  434. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  435. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +161 -120
  436. package/src/session-runtime/internal-tool-executor.mjs +12 -19
  437. package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
  438. package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
  439. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
  440. package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
  441. package/src/session-runtime/lifecycle/teardown/session-close.mjs +10 -5
  442. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  443. package/src/session-runtime/lifecycle-api.mjs +1 -0
  444. package/src/session-runtime/mcp-glue/server-catalog.mjs +27 -10
  445. package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
  446. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  447. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  448. package/src/session-runtime/model-route/set-route.mjs +40 -7
  449. package/src/session-runtime/native-web-search/routes.mjs +3 -3
  450. package/src/session-runtime/provider-auth/login.mjs +2 -2
  451. package/src/session-runtime/provider-auth/refresh.mjs +6 -3
  452. package/src/session-runtime/provider-auth/setup.mjs +1 -1
  453. package/src/session-runtime/provider-readiness.mjs +53 -4
  454. package/src/session-runtime/resource-api.mjs +1 -1
  455. package/src/session-runtime/resource-extension-sync.mjs +4 -8
  456. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  457. package/src/session-runtime/resource-mcp-connector-accounts.mjs +49 -32
  458. package/src/session-runtime/resource-mcp-secrets.mjs +27 -10
  459. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  460. package/src/session-runtime/resource-plugins-api.mjs +4 -0
  461. package/src/session-runtime/routine-executor.mjs +152 -0
  462. package/src/session-runtime/routine-home-turn.mjs +46 -5
  463. package/src/session-runtime/runtime-facade.mjs +5 -0
  464. package/src/session-runtime/runtime-feature-gates.mjs +2 -11
  465. package/src/session-runtime/schedule-session-run.mjs +1 -0
  466. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
  467. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
  468. package/src/session-runtime/services/agent-tool/send-flow.mjs +120 -8
  469. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
  470. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
  471. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  472. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +28 -7
  473. package/src/session-runtime/services/agent-tool.mjs +51 -1
  474. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  475. package/src/session-runtime/services/channel-admin.mjs +18 -0
  476. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  477. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  478. package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
  479. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  480. package/src/session-runtime/services/plugin-admin.mjs +14 -6
  481. package/src/session-runtime/services/provider-admin.mjs +281 -10
  482. package/src/session-runtime/services/seeds.mjs +7 -0
  483. package/src/session-runtime/services/usage-stats-model.mjs +32 -11
  484. package/src/session-runtime/session-create/materialize.mjs +4 -2
  485. package/src/session-runtime/session-create/session-options.mjs +2 -0
  486. package/src/session-runtime/session-turn-api.mjs +2 -0
  487. package/src/session-runtime/settings-builtin-tools-api.mjs +5 -10
  488. package/src/session-runtime/settings-system-api.mjs +1 -1
  489. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  490. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  491. package/src/session-runtime/setup-tool/executor.mjs +14 -3
  492. package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
  493. package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
  494. package/src/session-runtime/setup-tool/tool-defs.mjs +28 -2
  495. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
  496. package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
  497. package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
  498. package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
  499. package/src/session-runtime/tool-policy-refresh.mjs +182 -5
  500. package/src/session-runtime/tool-surface.mjs +38 -1
  501. package/src/session-runtime/turn/agent-ops.mjs +33 -2
  502. package/src/session-runtime/turn/session-ops.mjs +2 -0
  503. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
  504. package/src/session-runtime/turn/turn-run.mjs +1 -1
  505. package/src/session-runtime/webhook-session-run.mjs +2 -0
  506. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +46 -11
  507. package/src/session-runtime/workflow-agents-api/agent-route.mjs +2 -0
  508. package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
  509. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
  510. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
  511. package/src/session-runtime/workflow-agents-api.mjs +2 -0
  512. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  513. package/src/session-runtime/workspace/project-branches.mjs +1 -1
  514. package/src/session-runtime/workspace/session-view.mjs +149 -0
  515. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  516. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  517. package/src/session-runtime/workspace/transitions.mjs +128 -1070
  518. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  519. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  520. package/src/session-runtime/workspace/worktree-maintenance.mjs +200 -0
  521. package/src/session-runtime/workspace/worktree-setup.mjs +97 -0
  522. package/src/session-runtime/workspace/worktree-tool.mjs +70 -54
  523. package/src/standalone/daemon-agent-control.mjs +25 -8
  524. package/src/standalone/daemon-desktop-runtime.mjs +8 -2
  525. package/src/standalone/daemon-lag-profiler.mjs +7 -7
  526. package/src/standalone/daemon-session-control.mjs +151 -136
  527. package/src/standalone/daemon.mjs +61 -2
  528. package/src/standalone/desktop-service-registry.mjs +9 -0
  529. package/src/standalone/local-session-runtime.mjs +21 -1
  530. package/src/standalone/session-client/daemon-unavailable.mjs +4 -1
  531. package/src/standalone/session-client.mjs +5 -1
  532. package/src/standalone/session-frame-stream.mjs +83 -11
  533. package/src/standalone/session-protocol.mjs +15 -0
  534. package/src/standalone/session-service/agent-route-follow.mjs +23 -20
  535. package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
  536. package/src/standalone/session-service/agent-tree.mjs +45 -9
  537. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  538. package/src/standalone/session-service/quota-resume.mjs +9 -0
  539. package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
  540. package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
  541. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  542. package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
  543. package/src/standalone/session-service/session-calls.mjs +2 -1
  544. package/src/standalone/session-service/stored-reader.mjs +127 -40
  545. package/src/tui/App.jsx +162 -164
  546. package/src/tui/app/app-view/floating-panel.jsx +3 -0
  547. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  548. package/src/tui/app/clipboard.mjs +22 -11
  549. package/src/tui/app/create-app-pickers.mjs +2 -0
  550. package/src/tui/app/doctor.mjs +2 -2
  551. package/src/tui/app/panel-signature.mjs +1 -1
  552. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  553. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  554. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  555. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  556. package/src/tui/app/provider-setup/oauth-actions.mjs +25 -6
  557. package/src/tui/app/provider-setup-picker.mjs +20 -13
  558. package/src/tui/app/settings-picker.mjs +53 -8
  559. package/src/tui/app/shell-layout/row-budgets.mjs +14 -12
  560. package/src/tui/app/slash-commands.mjs +2 -1
  561. package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
  562. package/src/tui/app/slash-dispatch/session-commands.mjs +47 -1
  563. package/src/tui/app/slash-dispatch.mjs +1 -1
  564. package/src/tui/app/use-btw-panel.mjs +29 -0
  565. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  566. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  567. package/src/tui/app/use-mouse-input.mjs +11 -0
  568. package/src/tui/app/use-onboarding-start.mjs +8 -3
  569. package/src/tui/app/use-panel-transition.mjs +10 -1
  570. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  571. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  572. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  573. package/src/tui/app/use-prompt-queue-history.mjs +5 -4
  574. package/src/tui/app/use-selection-focus.mjs +11 -2
  575. package/src/tui/app/use-selection-geometry.mjs +18 -9
  576. package/src/tui/app/use-selection-paint.mjs +4 -4
  577. package/src/tui/app/use-selection-stitch.mjs +3 -3
  578. package/src/tui/app/use-slash-palette.mjs +62 -0
  579. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  580. package/src/tui/app/use-transcript-activity.mjs +48 -33
  581. package/src/tui/app/use-transcript-follow.mjs +12 -4
  582. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  583. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  584. package/src/tui/app/use-transcript-window/geometry.mjs +20 -6
  585. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  586. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  587. package/src/tui/app/use-transcript-window.mjs +21 -11
  588. package/src/tui/app/use-tui-ready.mjs +18 -0
  589. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  590. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  591. package/src/tui/components/Picker.jsx +27 -3
  592. package/src/tui/components/SignInUrl.jsx +68 -0
  593. package/src/tui/components/TextEntryPanel.jsx +76 -46
  594. package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
  595. package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
  596. package/src/tui/components/question-panel-model.mjs +4 -4
  597. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  598. package/src/tui/components/tool-execution/standard-card.jsx +3 -4
  599. package/src/tui/components/user-message-skills.mjs +1 -1
  600. package/src/tui/dist/index.mjs +2205 -1371
  601. package/src/tui/markdown/render-ansi.mjs +1 -1
  602. package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
  603. package/src/tui/session/agent-job-feed.mjs +2 -0
  604. package/src/tui/session/context-state.mjs +3 -0
  605. package/src/tui/session/goal-turn-state.mjs +6 -2
  606. package/src/tui/session/quota-resume.mjs +1 -1
  607. package/src/tui/session/session-api/controls.mjs +4 -0
  608. package/src/tui/session/session-api/extensions.mjs +8 -0
  609. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  610. package/src/tui/session/session-api/intake.mjs +2 -0
  611. package/src/tui/session/session-api/integrations.mjs +2 -2
  612. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  613. package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
  614. package/src/tui/session/session-api/routes-agent-definitions.mjs +6 -0
  615. package/src/tui/session/session-api/settings.mjs +2 -2
  616. package/src/tui/session/session-api/worktree.mjs +13 -1
  617. package/src/tui/session/session-flow/drain.mjs +11 -14
  618. package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
  619. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  620. package/src/tui/session/transcript-spill.mjs +92 -4
  621. package/src/tui/session/transcript-store.mjs +2 -0
  622. package/src/tui/session/turn-quiet.mjs +1 -1
  623. package/src/tui/session/turn.mjs +3 -0
  624. package/src/tui/session/worktree-setup-notice.mjs +1 -1
  625. package/src/tui/spinner-meta.mjs +3 -3
  626. package/src/tui/spinner-verbs.mjs +2 -2
  627. package/src/defaults/skills/goal-management/SKILL.md +0 -169
  628. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
  629. package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
  630. package/src/session-runtime/goal-tasks.mjs +0 -1
@@ -1,42 +1,149 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
1
3
  import { availableParallelism } from 'node:os';
2
- import { testConcurrencyArg } from './run-node-tests.mjs';
3
4
 
4
- // A local gate has one budget, not a full machine's worth of workers per lane.
5
- // Six is the interactive-desktop default; an explicit budget still wins.
6
- export function gateTestBudget(env = process.env, parallelism = availableParallelism()) {
7
- const argument = testConcurrencyArg([], env);
8
- return argument ? Number(argument.split('=')[1]) : Math.min(6, parallelism);
5
+ // The local gate runs every lane from one shared pool of workers: each lane's
6
+ // files are cut into shards, and whichever worker is free takes the longest
7
+ // shard left, whatever its lane. Shard sizes come from the previous run's
8
+ // measured per-file times, so the split stays balanced without hand-kept
9
+ // weights, and no worker idles while another lane still has work.
10
+
11
+ // The gate runs its whole process tree at below-normal priority, so a
12
+ // foreground app always wins the CPU. Priority does not bound what the
13
+ // desktop shell shares with every process (handles, desktop heap, work
14
+ // queues; explorer.exe has failed with "no work queue available" under many
15
+ // concurrent test processes), so the default also caps the worker count:
16
+ // half the cores, leaving the other half for what the budget does not count
17
+ // (each shard's runner, the processes tests spawn, the desktop build's own
18
+ // threads), and never more than DEFAULT_MAX_WORKERS. Each worker is three
19
+ // node processes (shard runner, its node --test batch, the test file) plus
20
+ // whatever the test spawns. Measured on a 20-core Windows PC with other work
21
+ // running: at 10 workers, test-file processes began dying within ~20 ms with
22
+ // no output (every remaining file of a shard, in 2 of 2 runs); at 8 and 6
23
+ // they did not.
24
+ // An explicit budget still wins: --test-concurrency (gate argument), then
25
+ // MIXDOG_TEST_CONCURRENCY.
26
+ const DEFAULT_MAX_WORKERS = 8;
27
+ export function gateTestBudget(argv = [], env = process.env, parallelism = availableParallelism()) {
28
+ const index = argv.findIndex((arg) => arg === '--test-concurrency' || arg.startsWith('--test-concurrency='));
29
+ let explicit = env.MIXDOG_TEST_CONCURRENCY || undefined;
30
+ // A flag without a value is an error, not "unspecified".
31
+ if (index >= 0) explicit = argv[index].includes('=') ? argv[index].split('=')[1] : (argv[index + 1] ?? '');
32
+ if (explicit === undefined) return Math.max(1, Math.min(DEFAULT_MAX_WORKERS, Math.floor(parallelism / 2)));
33
+ const budget = Number(explicit);
34
+ if (!Number.isInteger(budget) || budget < 1)
35
+ throw new Error(`gate test budget must be a positive integer (got "${explicit}")`);
36
+ return budget;
37
+ }
38
+
39
+ // Estimates for work with no recorded time yet: the next run records it.
40
+ export const DEFAULT_ESTIMATE_MS = { fast: 2_000, slow: 25_000, command: 60_000 };
41
+
42
+ // Previous runs' measured times ({ version, ms: { key: ms } }). A missing,
43
+ // unreadable or malformed file counts as no history, and an entry that is
44
+ // not a finite positive number is dropped (its work gets the default).
45
+ export function readTimingHistory(file) {
46
+ let ms;
47
+ try {
48
+ ms = JSON.parse(readFileSync(file, 'utf8'))?.ms;
49
+ } catch {
50
+ return {};
51
+ }
52
+ if (!ms || typeof ms !== 'object' || Array.isArray(ms)) return {};
53
+ return Object.fromEntries(
54
+ Object.entries(ms).filter(([, value]) => typeof value === 'number' && Number.isFinite(value) && value > 0)
55
+ );
9
56
  }
10
57
 
11
- // Splits the budget across legs in proportion to leg.weight (default 1), at least
12
- // one worker each. Spare workers go one at a time to the leg with the highest
13
- // weight per worker, so longer legs get more. Needs budget >= legs.length.
14
- export function allocateWorkers(legs, budget) {
15
- const shares = legs.map(() => 1);
16
- for (let spare = budget - legs.length; spare > 0; spare--) {
17
- let best = 0;
18
- for (let i = 1; i < legs.length; i++) {
19
- if ((legs[i].weight ?? 1) / shares[i] > (legs[best].weight ?? 1) / shares[best]) best = i;
58
+ // Writes the history through a temp file of its own (concurrent gates in
59
+ // other worktrees share the file) and renames it into place. Never throws:
60
+ // returns the error, or null once saved; the timings only plan the next run.
61
+ export function saveTimingHistory(file, ms) {
62
+ const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
63
+ try {
64
+ writeFileSync(temp, `${JSON.stringify({ version: 1, ms })}\n`);
65
+ renameSync(temp, file);
66
+ return null;
67
+ } catch (error) {
68
+ try {
69
+ rmSync(temp, { force: true });
70
+ } catch {
71
+ // The save error is the one reported.
20
72
  }
21
- shares[best]++;
73
+ return error;
22
74
  }
23
- return shares;
24
75
  }
76
+ // Enough shards per worker that the pool's tail (the shards still running
77
+ // when the queue is empty) stays short; the floor bounds the per-shard runner
78
+ // start-up and process sweep (~1-2 s each).
79
+ const SHARDS_PER_WORKER = 8;
80
+ const MIN_SHARD_MS = 15_000;
25
81
 
26
- // The test runners read their worker count when spawned, so it cannot grow
27
- // mid-run: shares are static. With fewer workers than legs, each worker runs
28
- // legs one after another.
29
- export async function runGateLegs(legs, budget, runLeg) {
30
- if (!Number.isInteger(budget) || budget < 1) throw new Error('gate test budget must be a positive integer');
31
- if (budget < legs.length) {
32
- let next = 0;
33
- await Promise.all(
34
- Array.from({ length: budget }, async () => {
35
- while (next < legs.length) await runLeg(legs[next++], 1);
36
- })
37
- );
38
- return;
82
+ // test.mjs selects every lane file whose path contains a given filter, so a
83
+ // file whose path contains another file's path has to share that file's shard
84
+ // or it would run twice. Returns the indivisible groups.
85
+ export function filterGroups(files) {
86
+ const parent = new Map(files.map((file) => [file, file]));
87
+ const find = (file) => (parent.get(file) === file ? file : find(parent.get(file)));
88
+ for (const inner of files) {
89
+ for (const outer of files) if (outer !== inner && outer.includes(inner)) parent.set(find(outer), find(inner));
39
90
  }
40
- const shares = allocateWorkers(legs, budget);
41
- await Promise.all(legs.map((leg, index) => runLeg(leg, shares[index])));
91
+ const groups = new Map();
92
+ for (const file of files) groups.set(find(file), [...(groups.get(find(file)) ?? []), file]);
93
+ return [...groups.values()];
94
+ }
95
+
96
+ // Cuts each lane ({ name, files, estimateOf }) into shards of about one
97
+ // target size, longest-first into the least-loaded shard, so a file longer
98
+ // than the target runs alone. Returns [{ lane, files, estimate }].
99
+ export function planShards(lanes, budget) {
100
+ const total = lanes.reduce((sum, lane) => sum + lane.files.reduce((s, file) => s + lane.estimateOf(file), 0), 0);
101
+ const target = Math.max(MIN_SHARD_MS, total / (budget * SHARDS_PER_WORKER));
102
+ const shards = [];
103
+ for (const lane of lanes) {
104
+ const items = filterGroups(lane.files)
105
+ .map((files) => ({ files, estimate: files.reduce((s, file) => s + lane.estimateOf(file), 0) }))
106
+ .sort((a, b) => b.estimate - a.estimate);
107
+ const laneTotal = items.reduce((sum, item) => sum + item.estimate, 0);
108
+ const bins = Array.from({ length: Math.min(items.length, Math.max(1, Math.round(laneTotal / target))) }, () => ({
109
+ lane: lane.name,
110
+ files: [],
111
+ estimate: 0,
112
+ }));
113
+ for (const item of items) {
114
+ const bin = bins.reduce((least, candidate) => (candidate.estimate < least.estimate ? candidate : least));
115
+ bin.files.push(...item.files);
116
+ bin.estimate += item.estimate;
117
+ }
118
+ shards.push(...bins);
119
+ }
120
+ return shards;
121
+ }
122
+
123
+ // Per-file time of a finished shard: what its own tests reported plus an
124
+ // equal share of the rest of the shard's wall time (process start-up, module
125
+ // load, the runner's own work). Returns { file: ms }.
126
+ export function measuredFileTimes(files, reportedMs, wallMs) {
127
+ const reported = files.reduce((sum, file) => sum + (reportedMs[file] ?? 0), 0);
128
+ const overhead = Math.max(0, wallMs - reported) / files.length;
129
+ return Object.fromEntries(files.map((file) => [file, (reportedMs[file] ?? 0) + overhead]));
130
+ }
131
+
132
+ // Runs every unit on `budget` workers, longest estimate first. A failed unit
133
+ // marked stopOnFail stops new starts (running units finish). Returns the
134
+ // units never started.
135
+ export async function runPool(units, budget, run) {
136
+ if (!Number.isInteger(budget) || budget < 1) throw new Error('gate test budget must be a positive integer');
137
+ const queue = [...units].sort((a, b) => b.estimate - a.estimate);
138
+ let stopped = false;
139
+ await Promise.all(
140
+ Array.from({ length: Math.min(budget, queue.length) }, async () => {
141
+ while (!stopped && queue.length > 0) {
142
+ const unit = queue.shift();
143
+ unit.ok = await run(unit);
144
+ if (!unit.ok && unit.stopOnFail) stopped = true;
145
+ }
146
+ })
147
+ );
148
+ return queue;
42
149
  }
@@ -0,0 +1,16 @@
1
+ // node:test reporter the local gate adds to each shard it runs: one JSON line
2
+ // per test file, { file, ms }, the file's top-level test time (relative to the
3
+ // package the run started in, like `scripts/test.mjs --list`). The gate turns
4
+ // these into the per-file estimates its next run plans shards from.
5
+ import { relative } from 'node:path';
6
+
7
+ export default async function* gateTimingReporter(source) {
8
+ const byFile = new Map();
9
+ for await (const event of source) {
10
+ if (event.type !== 'test:pass' && event.type !== 'test:fail') continue;
11
+ if (event.data.nesting !== 0 || !event.data.file) continue;
12
+ const file = relative(process.cwd(), String(event.data.file)).replaceAll('\\', '/');
13
+ byFile.set(file, (byFile.get(file) ?? 0) + (Number(event.data.details?.duration_ms) || 0));
14
+ }
15
+ for (const [file, ms] of byFile) yield `${JSON.stringify({ file, ms })}\n`;
16
+ }
@@ -29,6 +29,8 @@ const TEST_LOG_PREFIX = 'mixdog-test-output-';
29
29
  const TEST_FAILURES_PREFIX = 'mixdog-test-failures-';
30
30
  const TEST_SCRATCH_PREFIX = 'mixdog-test-scratch-';
31
31
  export const RUNNER_TEMP_PREFIXES = [TEST_LOG_PREFIX, TEST_FAILURES_PREFIX, TEST_SCRATCH_PREFIX];
32
+ // Printed first by every run; the local gate finds a dead unit's log by it.
33
+ export const FULL_LOG_LABEL = 'Full test log: ';
32
34
 
33
35
  // Batches keep the file order Node was given; a single path larger than the
34
36
  // budget still gets its own batch rather than being dropped. An empty list
@@ -82,7 +84,8 @@ const concurrencyFlag = (arg) => arg === '--test-concurrency' || arg.startsWith(
82
84
  function concurrencyOf(runArgs, fileArgs) {
83
85
  const args = [...runArgs, ...fileArgs];
84
86
  const index = args.findIndex(concurrencyFlag);
85
- const value = index < 0 ? null : positiveInt(args[index].includes('=') ? args[index].split('=')[1] : args[index + 1]);
87
+ let value = null;
88
+ if (index >= 0) value = positiveInt(args[index].includes('=') ? args[index].split('=')[1] : args[index + 1]);
86
89
  return value ?? Math.max(1, availableParallelism() - 1);
87
90
  }
88
91
 
@@ -141,11 +144,19 @@ function capture(command, args) {
141
144
  });
142
145
  }
143
146
 
144
- // Kills a process and every descendant still linked to it. On POSIX a batch
145
- // leads its own process group (spawned detached), so the group goes with it.
147
+ // Kills a live batch and every descendant still linked to it (on Windows the
148
+ // shared creation-checked tree kill). On POSIX a batch leads its own process
149
+ // group (spawned detached), so the group goes with it.
150
+ // Loaded only when a Windows kill runs, so importers that need just this
151
+ // module's constants (the local gate) do not pull in the runtime.
152
+ async function loadKillProcessTrees() {
153
+ const { killProcessTrees } = await import('../../src/runtime/agent/orchestrator/mcp/child-tree.mjs');
154
+ return killProcessTrees;
155
+ }
156
+
146
157
  async function killTree(pid) {
147
158
  if (process.platform === 'win32') {
148
- await capture('taskkill', ['/T', '/F', '/PID', String(pid)]);
159
+ await (await loadKillProcessTrees())([pid]);
149
160
  return;
150
161
  }
151
162
  for (const target of [-pid, pid]) {
@@ -189,17 +200,29 @@ async function processSnapshot() {
189
200
  });
190
201
  }
191
202
 
192
- const CLOCK_SLACK_MS = 2000;
203
+ // How far a snapshot's creation time may sit from Date.now() read around the
204
+ // spawn. Windows reports creation to the microsecond from the same system
205
+ // clock, so only a clock tick separates them; POSIX ps reports whole elapsed
206
+ // seconds. No wider: Windows hands a freed pid to the next process within
207
+ // milliseconds, and a 2 s window made a process another run had just started
208
+ // pass for one of this run's exited spawns, so it was killed with them.
209
+ const clockSlackMs = (platform) => (platform === 'win32' ? 50 : 2000);
193
210
 
194
211
  // The processes this run owns, out of a snapshot: an anchor (the batch, or a
195
212
  // child a test process recorded in its spawn ledger) still alive under its
196
- // pid, the orphans an exited anchor left (they still name it as their parent;
197
- // a later process that reuses its pid only has younger children), every
198
- // descendant of those, and anything executing from the run's scratch root
199
- // (e.g. the postgres the memory runtime downloads into MIXDOG_DATA_DIR).
213
+ // pid, the orphans an exited anchor left (they still name it as their parent),
214
+ // every descendant of those, and anything executing from the run's scratch
215
+ // root (e.g. the postgres the memory runtime downloads into MIXDOG_DATA_DIR).
216
+ // An anchor is { pid, before, since, exited? }: the clock just before and just
217
+ // after its spawn, and when its exit was seen. A pid alone never identifies a
218
+ // process: an anchor seen to exit no longer holds its pid, so whatever holds
219
+ // it now is another process; a live holder was created within its spawn; an
220
+ // orphan was created while its anchor lived (a later process that reuses the
221
+ // pid only has younger children); a descendant is younger than its parent.
200
222
  // Command-line text never makes a process the run's: an editor or terminal
201
223
  // opened on the scratch root is not.
202
- export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, platform = process.platform }) {
224
+ export function ownedProcesses(snapshot, anchors, { scratchDir, platform = process.platform }) {
225
+ const slack = clockSlackMs(platform);
203
226
  const byPid = new Map(snapshot.map((entry) => [entry.pid, entry]));
204
227
  const childrenOf = new Map();
205
228
  for (const entry of snapshot) childrenOf.set(entry.ppid, [...(childrenOf.get(entry.ppid) ?? []), entry]);
@@ -210,14 +233,19 @@ export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, plat
210
233
  owned.set(entry.pid, entry);
211
234
  queue.push(entry);
212
235
  };
213
- for (const { pid, since } of anchors) {
236
+ for (const { pid, before, since, exited } of anchors) {
214
237
  const holder = byPid.get(pid);
215
- if (holder && holder.created >= batchStart - CLOCK_SLACK_MS && holder.created <= since + CLOCK_SLACK_MS) {
238
+ if (holder && exited === undefined && holder.created >= before - slack && holder.created <= since + slack) {
216
239
  claim(holder);
217
240
  continue;
218
241
  }
219
242
  for (const orphan of childrenOf.get(pid) ?? []) {
220
- if (orphan.created >= since - CLOCK_SLACK_MS && (!holder || orphan.created < holder.created)) claim(orphan);
243
+ if (
244
+ orphan.created >= before - slack &&
245
+ orphan.created <= (exited ?? Number.POSITIVE_INFINITY) + slack &&
246
+ (!holder || orphan.created < holder.created)
247
+ )
248
+ claim(orphan);
221
249
  }
222
250
  }
223
251
  const fold = (path) => (platform === 'win32' ? path.toLowerCase() : path);
@@ -229,26 +257,30 @@ export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, plat
229
257
  while (queue.length > 0) {
230
258
  const parent = queue.pop();
231
259
  for (const entry of childrenOf.get(parent.pid) ?? []) {
232
- if (entry.created >= parent.created - CLOCK_SLACK_MS) claim(entry);
260
+ if (entry.created >= parent.created - slack) claim(entry);
233
261
  }
234
262
  }
235
263
  return [...owned.values()];
236
264
  }
237
265
 
266
+ // One anchor per recorded spawn (scripts/lib/test-spawn-ledger.mjs): its
267
+ // "<pid> <before> <since>" line, with "<exited>" from the line that repeats
268
+ // it once the exit was seen.
238
269
  async function readSpawnLedger(path) {
239
270
  const text = await readFile(path, 'utf8').catch(() => '');
240
- return text
241
- .split('\n')
242
- .filter(Boolean)
243
- .map((line) => {
244
- const [pid, since] = line.split(' ').map(Number);
245
- return { pid, since };
246
- });
271
+ const spawns = new Map();
272
+ for (const line of text.split('\n').filter(Boolean)) {
273
+ const [pid, before, since, exited] = line.split(' ').map(Number);
274
+ const key = `${pid} ${before} ${since}`;
275
+ spawns.set(key, { ...(spawns.get(key) ?? { pid, before, since }), ...(exited ? { exited } : {}) });
276
+ }
277
+ return [...spawns.values()];
247
278
  }
248
279
 
249
280
  // A process a test leaves running must not outlive its batch: it can hold a
250
281
  // port, a database, or files in the scratch root the run removes at the end.
251
- async function reapLeftovers(child, { batchStart, ledger, scratchDir }) {
282
+ // `batch` is the batch's own anchor, its exit already seen.
283
+ async function reapLeftovers(child, { batch, ledger, scratchDir }) {
252
284
  // The batch itself has exited: only its process group can remain.
253
285
  if (process.platform !== 'win32' && child.pid) {
254
286
  try {
@@ -257,17 +289,19 @@ async function reapLeftovers(child, { batchStart, ledger, scratchDir }) {
257
289
  /* the group is empty */
258
290
  }
259
291
  }
260
- const anchors = [...(child.pid ? [{ pid: child.pid, since: batchStart }] : []), ...(await readSpawnLedger(ledger))];
261
- await killOwned(anchors, { batchStart, scratchDir });
292
+ const anchors = [...(child.pid ? [batch] : []), ...(await readSpawnLedger(ledger))];
293
+ await killOwned(anchors, { scratchDir });
262
294
  }
263
295
 
264
- async function killOwned(anchors, { batchStart, scratchDir }) {
265
- const leftovers = ownedProcesses(await processSnapshot(), anchors, { batchStart, scratchDir });
296
+ async function killOwned(anchors, { scratchDir }) {
297
+ // Every owned process ran when the snapshot was taken.
298
+ const snapshotAt = Date.now();
299
+ const leftovers = ownedProcesses(await processSnapshot(), anchors, { scratchDir });
266
300
  if (leftovers.length === 0) return;
267
301
  const pids = leftovers.map((entry) => entry.pid);
268
302
  console.error(`Killing ${pids.length} process(es) a test left running: ${pids.join(' ')}`);
269
303
  if (process.platform === 'win32') {
270
- await capture('taskkill', ['/T', '/F', ...pids.flatMap((pid) => ['/PID', String(pid)])]);
304
+ await (await loadKillProcessTrees())(pids, { aliveAt: snapshotAt });
271
305
  return;
272
306
  }
273
307
  for (const pid of pids) {
@@ -279,12 +313,13 @@ async function killOwned(anchors, { batchStart, scratchDir }) {
279
313
  }
280
314
  }
281
315
 
282
- // Resolves { code, hung } once the batch exited and its leftovers are gone.
316
+ // Resolves { code, hung, pid } once the batch exited and its leftovers are gone.
283
317
  // `scratchDir` '' = leave the scratch-root sweep to the caller (side-by-side batches).
284
318
  function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
285
319
  return new Promise((resolve) => {
286
- const batchStart = Date.now();
320
+ const before = Date.now();
287
321
  const child = spawn(process.execPath, args, { stdio: 'inherit', env, detached: process.platform !== 'win32' });
322
+ const since = Date.now();
288
323
  let hung = '';
289
324
  const watchdog = setTimeout(() => {
290
325
  hung = `hung after ${minutes(timeoutMs)} min: ${files.join(' ') || '(discovered files)'}`;
@@ -302,10 +337,11 @@ function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
302
337
  const signals = process.platform === 'win32' ? [] : ['SIGINT', 'SIGTERM'];
303
338
  for (const signal of signals) process.on(signal, forward);
304
339
  const settle = async (code) => {
340
+ const batch = { pid: child.pid, before, since, exited: Date.now() };
305
341
  clearTimeout(watchdog);
306
342
  for (const signal of signals) process.off(signal, forward);
307
- await reapLeftovers(child, { batchStart, ledger, scratchDir });
308
- resolve({ code: hung ? 1 : code, hung });
343
+ await reapLeftovers(child, { batch, ledger, scratchDir });
344
+ resolve({ code: hung ? 1 : code, hung, pid: child.pid });
309
345
  };
310
346
  child.on('error', (error) => {
311
347
  console.error(error);
@@ -363,7 +399,7 @@ export async function runNodeTests(nodeArgs, fileArgs, options = {}) {
363
399
  pruneStaleTempDirs(RUNNER_TEMP_PREFIXES);
364
400
  const outputDir = await mkdtemp(join(tmpdir(), TEST_LOG_PREFIX));
365
401
  const logPath = join(outputDir, 'full.log');
366
- console.error(`Full test log: ${logPath}`);
402
+ console.error(`${FULL_LOG_LABEL}${logPath}`);
367
403
  // Failure records live outside the log directory: the log directory holds
368
404
  // exactly one merged log, and these records are an internal artifact.
369
405
  const failureDir = await mkdtemp(join(tmpdir(), TEST_FAILURES_PREFIX));
@@ -461,7 +497,7 @@ async function executeRun(
461
497
  );
462
498
  // Peers were still running while each batch exited, so the scratch root's
463
499
  // own processes (e.g. a postgres a test started) are swept once, here.
464
- if (overlapped) await killOwned([], { batchStart: Date.now(), scratchDir });
500
+ if (overlapped) await killOwned([], { scratchDir });
465
501
  // Logs and records merge in batch order, whatever order the batches finished.
466
502
  for (const [index, outcome] of outcomes.entries()) {
467
503
  const batchLog = batchLogOf(index);
@@ -518,6 +554,7 @@ async function executeRun(
518
554
  await rm(coverageDir, { recursive: true, force: true });
519
555
  }
520
556
  // Last line of the run, after every reporter and the coverage report.
521
- process.stdout.write(`\n${formatFailureSummary({ failed, flaky, rerunFailed, hung, exitCode: status })}`);
557
+ const pids = outcomes.filter((outcome) => outcome.code !== 0 && outcome.pid).map((outcome) => outcome.pid);
558
+ process.stdout.write(`\n${formatFailureSummary({ failed, flaky, rerunFailed, hung, exitCode: status, pids })}`);
522
559
  return status;
523
560
  }
@@ -71,6 +71,7 @@ export function registerOwnedTempDir(dir) {
71
71
  installExitHook();
72
72
  return resolved;
73
73
  }
74
+
74
75
  /**
75
76
  * For code under test that makes its own mixdog-* directories in the system
76
77
  * temp dir: points TEMP/TMP/TMPDIR at one scoped directory for the rest of the
@@ -36,7 +36,20 @@ export function failureRecord(event, stacks) {
36
36
  if (event.data.todo) return null;
37
37
  if (event.data.details?.error?.failureType === 'subtestsFailed') return null;
38
38
  const ancestors = (stacks.get(file) ?? []).slice(0, event.data.nesting);
39
- return { file, name: [...ancestors, event.data.name].join(' > ') };
39
+ const exit = processExit(event.data.details?.error);
40
+ return { file, name: [...ancestors, event.data.name].join(' > '), ...(exit ? { exit } : {}) };
41
+ }
42
+
43
+ // A test file whose process ended badly fails with only "test failed"; node
44
+ // keeps how it ended on the error. A Windows NTSTATUS code (e.g. 0xC0000142,
45
+ // the process could not initialise) reads better in hex.
46
+ export function processExit(error) {
47
+ if (error?.failureType !== 'testCodeFailure') return '';
48
+ if (error.signal) return `test process killed by ${error.signal}`;
49
+ const code = error.exitCode;
50
+ if (!Number.isInteger(code) || code === 0) return '';
51
+ const hex = code > 255 || code < 0 ? ` (0x${(code >>> 0).toString(16).toUpperCase().padStart(8, '0')})` : '';
52
+ return `test process exited with code ${code}${hex}`;
40
53
  }
41
54
 
42
55
  // The runner sets FAILURE_RECORDS_ENV to a per-spawn JSONL path. Without it
@@ -46,19 +46,24 @@ export function policyLine(rerunFailed) {
46
46
  // `hung` holds one "hung after N min: <files>" message per batch the runner's
47
47
  // watchdog killed; such a batch fails the run whatever its tests reported.
48
48
  // A non-zero `exitCode` with no failed test, flaky test or hung batch behind it
49
- // (a bad node flag, a crashed runner) is its own failure, never "0 failed".
50
- export function formatFailureSummary({ failed = [], flaky = [], rerunFailed = 0, hung = [], exitCode = 0 }) {
49
+ // (a bad node flag, a crashed runner, a kill from outside) is its own failure,
50
+ // never "0 failed". `pids` names the node --test processes that exited
51
+ // non-zero, so a kill can be matched to the "Killing ... <pid>" line of
52
+ // whatever killed them.
53
+ export function formatFailureSummary({ failed = [], flaky = [], rerunFailed = 0, hung = [], exitCode = 0, pids = [] }) {
51
54
  const hungCount = hung.length > 0 ? `, ${hung.length} hung batch(es)` : '';
52
55
  const unexplained = exitCode !== 0 && failed.length === 0 && hung.length === 0;
56
+ const runner = pids.length > 0 ? `node --test pid ${pids.join(' ')}: ` : '';
53
57
  const lines = [
54
58
  unexplained
55
- ? `${SUMMARY_TAG} RUN FAILED: exit code ${exitCode} with no failed test recorded (bad argument or runner error; see output above)`
59
+ ? `${SUMMARY_TAG} RUN FAILED: exit code ${exitCode} with no failed test recorded (${runner}bad argument, runner error or killed from outside; see output above)`
56
60
  : `${SUMMARY_TAG} ${failed.length} failed, ${flaky.length} flaky${hungCount} (every failed test is named below)`,
57
61
  ];
58
62
  for (const message of hung) lines.push(`${SUMMARY_TAG} HUNG ${message}`);
59
63
  for (const record of failed) {
60
64
  const origin = record.firstSeenOnRerun ? ` (first seen on re-run ${record.firstSeenOnRerun})` : '';
61
- lines.push(`${SUMMARY_TAG} FAILED ${record.file} > ${record.name}${origin}`);
65
+ const exit = record.exit ? ` (${record.exit})` : '';
66
+ lines.push(`${SUMMARY_TAG} FAILED ${record.file} > ${record.name}${exit}${origin}`);
62
67
  }
63
68
  for (const record of flaky) {
64
69
  lines.push(
@@ -1,12 +1,14 @@
1
1
  // Preloaded (--import) into every test process the runner starts: records
2
- // "<pid> <spawn time ms>" for each child process, so a process a test leaves
2
+ // each child process's pid, spawn time and exit, so a process a test leaves
3
3
  // running can be traced back to the run after its parent exited
4
4
  // (scripts/lib/run-node-tests.mjs). Without the runner's environment it does
5
5
  // nothing.
6
6
  import { ChildProcess } from 'node:child_process';
7
7
  import { appendFileSync, readFileSync } from 'node:fs';
8
+ import { constants } from 'node:os';
8
9
  import { dirname, join } from 'node:path';
9
10
  import { pathToFileURL } from 'node:url';
11
+ import { getSystemErrorMap } from 'node:util';
10
12
  import { FAILURE_RECORDS_ENV, spawnLedgerPath } from './test-failure-records.mjs';
11
13
 
12
14
  // --test-force-exit ends a test file's process once every test registered so
@@ -54,11 +56,49 @@ if (
54
56
 
55
57
  const ledger = spawnLedgerPath(process.env[FAILURE_RECORDS_ENV]);
56
58
  if (ledger) {
57
- // spawn, exec, execFile and fork all start their process here.
59
+ // spawn, exec, execFile and fork all start their process here. A spawn is
60
+ // "<pid> <before> <since>", the clock just before and just after it; the
61
+ // same line plus "<exited>" follows once its exit is seen. The 'exit' event
62
+ // comes before the child's handle is closed, so the pid is still this
63
+ // child's when the exit is written: from then on another process may hold it.
58
64
  const spawn = ChildProcess.prototype.spawn;
65
+ const exited = new Set();
59
66
  ChildProcess.prototype.spawn = function recordedSpawn(...args) {
67
+ const before = Date.now();
60
68
  const result = spawn.apply(this, args);
61
- if (this.pid) appendFileSync(ledger, `${this.pid} ${Date.now()}\n`);
69
+ if (this.pid) {
70
+ const { pid } = this;
71
+ exited.delete(pid);
72
+ const spawned = `${pid} ${before} ${Date.now()}`;
73
+ appendFileSync(ledger, `${spawned}\n`);
74
+ this.once('exit', () => {
75
+ exited.add(pid);
76
+ appendFileSync(ledger, `${spawned} ${Date.now()}\n`);
77
+ });
78
+ }
62
79
  return result;
63
80
  };
81
+ // Once a child's exit is seen its pid is free, and Windows hands it to the
82
+ // next process started within moments — on a loaded gate often a parallel
83
+ // unit's test runner, node --test batch or test file process. A cleanup that
84
+ // signals every pid it recorded (e.g. `after(() => process.kill(pid))`, or a
85
+ // kill(pid, 0) liveness poll) then terminates that process instead, and the
86
+ // whole unit dies without output. Such a signal fails ESRCH here, as for a
87
+ // gone process, and never reaches whatever holds the pid now. Arguments Node
88
+ // rejects (a non-integer pid, an unknown signal) still go to Node, which
89
+ // throws before any signal is sent; this rejects only what it would send.
90
+ const kill = process.kill;
91
+ const [ESRCH] = [...getSystemErrorMap()].find(([, [name]]) => name === 'ESRCH');
92
+ const sendable = (signal) => signal === (signal | 0) || Boolean(constants.signals[signal || 'SIGTERM']);
93
+ process.kill = function killLiveChildOnly(pid, signal) {
94
+ // Only a primitive pid is checked: coercing an object would run its
95
+ // valueOf before Node's own coercion does. Number(pid) === (pid | 0) is
96
+ // Node's `pid == (pid | 0)` integer check.
97
+ const primitive = typeof pid === 'number' || typeof pid === 'string';
98
+ if (primitive && Number(pid) === (pid | 0) && exited.has(pid | 0) && sendable(signal)) {
99
+ // The shape of Node's own failed kill (an ErrnoException).
100
+ throw Object.assign(new Error('kill ESRCH'), { errno: ESRCH, code: 'ESRCH', syscall: 'kill' });
101
+ }
102
+ return kill.call(process, pid, signal);
103
+ };
64
104
  }
@@ -87,9 +87,12 @@ export async function checkFeed({ text, label, version, assets, hashAsset, requi
87
87
  for (const name of requiredFiles) {
88
88
  if (!byUrl.has(name)) problems.push(`${label}: files[] does not list ${name}`);
89
89
  }
90
+ // Every listed asset downloads at once; the problems keep files[] order.
91
+ const fileAssets = feed.files.map((file) => assets.find((candidate) => candidate.name === file.url));
92
+ const digests = await Promise.all(fileAssets.map((asset) => (asset ? hashAsset(asset) : null)));
90
93
  const hashes = new Map();
91
- for (const file of feed.files) {
92
- const asset = assets.find((candidate) => candidate.name === file.url);
94
+ for (const [index, file] of feed.files.entries()) {
95
+ const asset = fileAssets[index];
93
96
  if (!asset) {
94
97
  problems.push(`${label}: ${file.url} is not an uploaded asset`);
95
98
  continue;
@@ -99,7 +102,7 @@ export async function checkFeed({ text, label, version, assets, hashAsset, requi
99
102
  problems.push(`${label}: ${file.url} size ${size} != uploaded ${asset.size}`);
100
103
  }
101
104
  const sha512 = field(file, 'sha512');
102
- const actual = await hashAsset(asset);
105
+ const actual = digests[index];
103
106
  hashes.set(file.url, actual);
104
107
  if (sha512 !== actual) problems.push(`${label}: ${file.url} sha512 does not match the uploaded asset`);
105
108
  }
@@ -233,6 +236,25 @@ export async function detectPublished({ fetchRelease, sleep = defaultSleep, atte
233
236
  }
234
237
  }
235
238
 
239
+ /**
240
+ * Run independent `[name, run]` sections concurrently. Results keep the
241
+ * section order; a thrown section becomes a "check failed" problem.
242
+ */
243
+ export function runSections(sections) {
244
+ return Promise.all(
245
+ sections.map(async ([name, run]) => {
246
+ try {
247
+ const outcome = await run();
248
+ return Array.isArray(outcome)
249
+ ? { name, problems: outcome, warnings: [] }
250
+ : { name, problems: outcome.problems, warnings: outcome.warnings };
251
+ } catch (error) {
252
+ return { name, problems: [`check failed: ${error.message}`], warnings: [] };
253
+ }
254
+ })
255
+ );
256
+ }
257
+
236
258
  async function sha512Base64(url) {
237
259
  const response = await fetch(url, { redirect: 'follow' });
238
260
  if (!response.ok) throw new Error(`download ${url} answered ${response.status}`);
@@ -290,19 +312,7 @@ async function main() {
290
312
  ],
291
313
  ['npm', () => waitForNpm({ name: 'mixdog', version })],
292
314
  ];
293
- const results = [];
294
- for (const [name, run] of sections) {
295
- try {
296
- const outcome = await run();
297
- results.push(
298
- Array.isArray(outcome)
299
- ? { name, problems: outcome, warnings: [] }
300
- : { name, problems: outcome.problems, warnings: outcome.warnings }
301
- );
302
- } catch (error) {
303
- results.push({ name, problems: [`check failed: ${error.message}`], warnings: [] });
304
- }
305
- }
315
+ const results = await runSections(sections);
306
316
 
307
317
  const lines = [`## Post-release check for ${tag}`, ''];
308
318
  for (const { name, problems, warnings } of results) {
@@ -21,12 +21,11 @@ const SOURCES = [
21
21
  // A statement this short says its whole meaning (a constant, a one-line arrow); a longer one gives its first line.
22
22
  const WHOLE_STATEMENT = 240;
23
23
 
24
- const propertyName = (property) =>
25
- property.type === 'Property'
26
- ? String(property.key.name ?? property.key.value ?? '')
27
- : property.type === 'SpreadElement'
28
- ? '…'
29
- : '';
24
+ function propertyName(property) {
25
+ if (property.type === 'Property') return String(property.key.name ?? property.key.value ?? '');
26
+ if (property.type === 'SpreadElement') return '…';
27
+ return '';
28
+ }
30
29
 
31
30
  // The keys an object literal offers a script, one level into object-valued keys ("body: { x, y, w, h }"). A table of
32
31
  // like entries (STYLES: an id per style, each with the same fields) reads as its ids and the fields each carries.