mixdog 1.4.0 → 1.4.2

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 (493) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-shipped-history.mjs +115 -33
  3. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  4. package/scripts/gate-local.mjs +183 -63
  5. package/scripts/latest-guard.mjs +21 -9
  6. package/scripts/lib/gate-scheduler.mjs +138 -32
  7. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +91 -66
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +124 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/lib/test-failure-records.mjs +14 -1
  14. package/scripts/lib/test-failure-summary.mjs +16 -4
  15. package/scripts/lib/test-spawn-ledger.mjs +12 -3
  16. package/scripts/post-release-check.mjs +1 -6
  17. package/scripts/pptx-kit-index.mjs +17 -5
  18. package/scripts/suite-health.mjs +115 -24
  19. package/scripts/test-data-dir.mjs +10 -7
  20. package/scripts/test.mjs +9 -14
  21. package/src/agents/advisor/AGENT.md +4 -4
  22. package/src/agents/advisor/agent.json +1 -1
  23. package/src/agents/explore/AGENT.md +22 -0
  24. package/src/agents/explore/agent.json +6 -0
  25. package/src/app.mjs +4 -0
  26. package/src/cli.mjs +7 -0
  27. package/src/defaults/agents.json +7 -0
  28. package/src/defaults/shipped-history.json +23 -2
  29. package/src/defaults/skills/browser-use/SKILL.md +10 -5
  30. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  31. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  32. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  33. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  34. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  35. package/src/defaults/skills/computer-use/SKILL.md +16 -4
  36. package/src/defaults/skills/goal-management/SKILL.md +1 -1
  37. package/src/defaults/skills/setup/references/actions.md +12 -6
  38. package/src/defaults/skills/setup/references/surfaces.md +2 -2
  39. package/src/defaults/skills/setup-guide/SKILL.md +5 -2
  40. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  41. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  42. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  43. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  44. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  45. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  46. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  47. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  48. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  49. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  50. package/src/defaults/skills/visualize/SKILL.md +6 -2
  51. package/src/lib/rules-builder.cjs +35 -42
  52. package/src/output-styles/common.md +2 -2
  53. package/src/output-styles/simple.md +1 -1
  54. package/src/rules/agent/AGENT.md +5 -4
  55. package/src/rules/lead/LEAD.md +12 -7
  56. package/src/rules/shared/10-tool-workflow.md +3 -1
  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/45-destructive.md +0 -2
  60. package/src/rules/shared/50-execution.md +2 -0
  61. package/src/rules/shared/60-verification.md +10 -9
  62. package/src/rules/shared/70-delivery.md +8 -9
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
  66. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  69. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
  71. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
  72. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
  79. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
  80. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
  81. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
  82. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
  84. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  85. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  86. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  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-oauth-request/initial-status.mjs +3 -1
  90. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  91. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  92. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  94. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  97. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  100. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  101. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  103. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  105. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  106. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
  108. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  109. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  110. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  113. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  114. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  115. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  117. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  129. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  130. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  131. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  132. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  133. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
  135. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  136. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
  137. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  139. package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
  140. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  141. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  142. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  143. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  145. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
  147. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  148. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
  150. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
  151. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  152. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  153. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  154. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  155. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  156. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
  157. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  158. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  159. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  160. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  161. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  162. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  163. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
  164. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
  165. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  166. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  167. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
  168. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  169. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
  170. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  171. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
  172. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  174. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  175. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  179. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  180. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  181. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  184. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  185. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
  186. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  187. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
  188. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
  189. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
  190. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
  192. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
  193. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  194. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  195. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  196. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  197. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  198. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
  199. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  200. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
  201. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  202. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  203. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  204. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
  209. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  210. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
  211. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  212. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  213. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  214. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  215. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  217. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  218. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  219. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
  221. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  223. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  224. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  225. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  226. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  227. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  228. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  229. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  231. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
  232. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  233. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  234. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  235. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  236. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  237. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  238. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  239. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
  240. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  241. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  243. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
  244. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
  245. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  246. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  248. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  249. package/src/runtime/channels/lib/whisper-port.mjs +29 -11
  250. package/src/runtime/channels/lib/whisper-server.mjs +10 -1
  251. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  252. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  253. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  254. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  255. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  256. package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
  257. package/src/runtime/connectors/gmail-app-password.mjs +3 -3
  258. package/src/runtime/explore/explorer.mjs +93 -0
  259. package/src/runtime/explore/tool-defs.mjs +35 -0
  260. package/src/runtime/explore/tool.mjs +123 -0
  261. package/src/runtime/local-provider/server-process.mjs +2 -1
  262. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  263. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  264. package/src/runtime/memory/index.mjs +4 -0
  265. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  266. package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
  267. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  268. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  269. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  270. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  271. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  272. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  273. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  274. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  275. package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
  276. package/src/runtime/office/quality/design-review.mjs +1 -5
  277. package/src/runtime/office/quality/document-brief.mjs +1 -5
  278. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  279. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  280. package/src/runtime/shared/agent-route-config.mjs +17 -2
  281. package/src/runtime/shared/atomic-file.mjs +103 -2
  282. package/src/runtime/shared/background-tasks.mjs +15 -6
  283. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  284. package/src/runtime/shared/connector-accounts.mjs +5 -5
  285. package/src/runtime/shared/connector-catalog.d.mts +11 -4
  286. package/src/runtime/shared/connector-catalog.mjs +60 -52
  287. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  288. package/src/runtime/shared/defaults-separation.mjs +1 -4
  289. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  290. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  291. package/src/runtime/shared/file-has-data.mjs +26 -2
  292. package/src/runtime/shared/file-lock.mjs +13 -15
  293. package/src/runtime/shared/git-launch.d.mts +9 -0
  294. package/src/runtime/shared/git-launch.mjs +213 -0
  295. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  296. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  297. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  298. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  299. package/src/runtime/shared/llm/cost.mjs +11 -2
  300. package/src/runtime/shared/llm/http-agent.mjs +313 -29
  301. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  302. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  303. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  304. package/src/runtime/shared/llm/system-proxy.mjs +452 -0
  305. package/src/runtime/shared/loopback-listener.mjs +25 -2
  306. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  307. package/src/runtime/shared/model-route-chain.mjs +22 -4
  308. package/src/runtime/shared/open-url.mjs +10 -0
  309. package/src/runtime/shared/provider-accounts.mjs +52 -2
  310. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  311. package/src/runtime/shared/session-scratch.mjs +37 -0
  312. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  313. package/src/runtime/shared/staged-update.mjs +47 -2
  314. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  315. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  316. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  317. package/src/runtime/shared/tool-surface.mjs +113 -36
  318. package/src/runtime/shared/tool-work-units.mjs +6 -0
  319. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  320. package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
  321. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  322. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  323. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  324. package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
  325. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  326. package/src/runtime/shared/worktrees/git.mjs +72 -0
  327. package/src/runtime/shared/worktrees/manager.mjs +285 -168
  328. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  329. package/src/runtime/tidy/install.mjs +95 -15
  330. package/src/runtime/tidy/languages.mjs +17 -15
  331. package/src/runtime/tidy/process.mjs +7 -4
  332. package/src/runtime/tidy/resolve.mjs +19 -16
  333. package/src/runtime/tidy/runners/biome.mjs +3 -3
  334. package/src/runtime/tidy/tool.mjs +1 -1
  335. package/src/runtime/tidy/worktree.mjs +10 -6
  336. package/src/runtime/visualize-bridge/client.mjs +6 -1
  337. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  338. package/src/runtime/web-search/index.mjs +1 -1
  339. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  340. package/src/session-runtime/agent-task-sessions.mjs +458 -312
  341. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  342. package/src/session-runtime/boot/tools.mjs +7 -1
  343. package/src/session-runtime/bot-status.mjs +107 -101
  344. package/src/session-runtime/bot-target.mjs +38 -0
  345. package/src/session-runtime/channel-config-api.mjs +6 -1
  346. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  347. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  348. package/src/session-runtime/conversation-import/discovery.mjs +401 -36
  349. package/src/session-runtime/conversation-import/service.mjs +35 -8
  350. package/src/session-runtime/handoff-attachments.mjs +82 -31
  351. package/src/session-runtime/held-requests.mjs +3 -1
  352. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  353. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  354. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
  355. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  356. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  357. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
  358. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  359. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  360. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
  361. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  362. package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
  363. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  364. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  365. package/src/session-runtime/model-route/set-route.mjs +11 -2
  366. package/src/session-runtime/provider-auth/login.mjs +2 -2
  367. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  368. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  369. package/src/session-runtime/provider-models.mjs +4 -2
  370. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  371. package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
  372. package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
  373. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  374. package/src/session-runtime/resource-recall-api.mjs +6 -1
  375. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  376. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  377. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  378. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  379. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  380. package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
  381. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  382. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  383. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  384. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  385. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  386. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  387. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  388. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  389. package/src/session-runtime/services/plugin-admin.mjs +10 -6
  390. package/src/session-runtime/services/provider-admin.mjs +283 -11
  391. package/src/session-runtime/session-turn-api.mjs +2 -0
  392. package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
  393. package/src/session-runtime/settings-system-api.mjs +1 -1
  394. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  395. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  396. package/src/session-runtime/setup-tool/executor.mjs +6 -2
  397. package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
  398. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  399. package/src/session-runtime/turn/agent-ops.mjs +23 -0
  400. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
  401. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  402. package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
  403. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  404. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  405. package/src/session-runtime/workspace/session-view.mjs +149 -0
  406. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  407. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  408. package/src/session-runtime/workspace/transitions.mjs +108 -1041
  409. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  410. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  411. package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
  412. package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
  413. package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
  414. package/src/standalone/daemon-desktop-runtime.mjs +2 -0
  415. package/src/standalone/daemon-session-control.mjs +169 -138
  416. package/src/standalone/daemon.mjs +7 -0
  417. package/src/standalone/desktop-service-registry.mjs +9 -0
  418. package/src/standalone/local-session-runtime.mjs +5 -1
  419. package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
  420. package/src/standalone/session-client.mjs +5 -1
  421. package/src/standalone/session-frame-stream.mjs +83 -11
  422. package/src/standalone/session-protocol.mjs +4 -0
  423. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  424. package/src/standalone/session-service/agent-tree.mjs +45 -9
  425. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  426. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  427. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  428. package/src/standalone/session-service/stored-reader.mjs +127 -40
  429. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  430. package/src/tui/App.jsx +154 -164
  431. package/src/tui/app/app-view/floating-panel.jsx +10 -4
  432. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  433. package/src/tui/app/clipboard.mjs +21 -10
  434. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  435. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  436. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  437. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  438. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  439. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  440. package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
  441. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  442. package/src/tui/app/provider-setup-picker.mjs +29 -18
  443. package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
  444. package/src/tui/app/slash-commands.mjs +1 -0
  445. package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
  446. package/src/tui/app/slash-dispatch.mjs +1 -1
  447. package/src/tui/app/use-btw-panel.mjs +29 -0
  448. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  449. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  450. package/src/tui/app/use-mouse-input.mjs +11 -0
  451. package/src/tui/app/use-onboarding-start.mjs +8 -3
  452. package/src/tui/app/use-panel-transition.mjs +10 -1
  453. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  454. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  455. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  456. package/src/tui/app/use-prompt-queue-history.mjs +3 -2
  457. package/src/tui/app/use-selection-focus.mjs +11 -2
  458. package/src/tui/app/use-selection-geometry.mjs +18 -9
  459. package/src/tui/app/use-selection-paint.mjs +4 -4
  460. package/src/tui/app/use-selection-stitch.mjs +3 -3
  461. package/src/tui/app/use-slash-palette.mjs +62 -0
  462. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  463. package/src/tui/app/use-transcript-activity.mjs +47 -32
  464. package/src/tui/app/use-transcript-follow.mjs +12 -4
  465. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  466. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  467. package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
  468. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  469. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  470. package/src/tui/app/use-transcript-window.mjs +21 -11
  471. package/src/tui/app/use-tui-ready.mjs +18 -0
  472. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  473. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  474. package/src/tui/components/Picker.jsx +27 -3
  475. package/src/tui/components/QuestionPanel.jsx +13 -4
  476. package/src/tui/components/SignInUrl.jsx +68 -0
  477. package/src/tui/components/TextEntryPanel.jsx +76 -46
  478. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  479. package/src/tui/dist/index.mjs +1963 -1361
  480. package/src/tui/session/goal-turn-state.mjs +6 -2
  481. package/src/tui/session/session-api/controls.mjs +4 -0
  482. package/src/tui/session/session-api/extensions.mjs +8 -0
  483. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  484. package/src/tui/session/session-api/intake.mjs +2 -0
  485. package/src/tui/session/session-api/integrations.mjs +2 -2
  486. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  487. package/src/tui/session/session-api/settings.mjs +2 -2
  488. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  489. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  490. package/src/tui/session/transcript-spill.mjs +92 -4
  491. package/src/tui/session/transcript-store.mjs +2 -0
  492. package/src/ui/statusline-agents.mjs +1 -3
  493. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Open-source coding agent that matches Codex on Terminal-Bench 2.1 with 63% fewer tokens.",
@@ -53,6 +53,7 @@
53
53
  "src/",
54
54
  "!src/tui/dev",
55
55
  "!src/**/*.test.mjs",
56
+ "!src/**/*.test-hook.mjs",
56
57
  "!src/**/*.test.jsx"
57
58
  ],
58
59
  "scripts": {
@@ -182,7 +183,7 @@
182
183
  "wrap-ansi": "^10.0.0",
183
184
  "ws": "^8.21.0",
184
185
  "yaml": "^2.9.0",
185
- "zod": "^3.25.76"
186
+ "zod": "^4.6.7"
186
187
  },
187
188
  "overrides": {
188
189
  "onnxruntime-node": {
@@ -13,7 +13,7 @@
13
13
  * node scripts/build-shipped-history.mjs --check (release workflow)
14
14
  * A shallow clone has no history: the committed file is left untouched.
15
15
  */
16
- import { execFileSync } from 'node:child_process';
16
+ import { execFileSync, spawn } from 'node:child_process';
17
17
  import { readFileSync, writeFileSync } from 'node:fs';
18
18
  import { dirname, join } from 'node:path';
19
19
  import { fileURLToPath } from 'node:url';
@@ -39,12 +39,81 @@ if (git('rev-parse', '--is-shallow-repository').toString().trim() === 'true') {
39
39
  process.exit(0);
40
40
  }
41
41
 
42
- const blobs = new Map();
43
- const blob = (sha) => {
44
- if (!blobs.has(sha)) blobs.set(sha, git('cat-file', 'blob', sha));
45
- return blobs.get(sha);
42
+ /**
43
+ * One long-lived `git cat-file --batch` answers every object read, so the whole
44
+ * history costs a single process. `read(spec)` resolves to {sha, type, body}
45
+ * or null when the object is missing.
46
+ */
47
+ const catFile = spawn('git', ['cat-file', '--batch'], { cwd: ROOT, stdio: ['pipe', 'pipe', 'inherit'] });
48
+ const waiting = [];
49
+ let pending = Buffer.alloc(0);
50
+ catFile.stdout.on('data', (chunk) => {
51
+ pending = Buffer.concat([pending, chunk]);
52
+ for (;;) {
53
+ const eol = pending.indexOf(10);
54
+ if (eol < 0) return;
55
+ const header = pending.toString('utf8', 0, eol);
56
+ if (header.endsWith(' missing')) {
57
+ pending = pending.subarray(eol + 1);
58
+ waiting.shift()(null);
59
+ continue;
60
+ }
61
+ const [sha, type, size] = header.split(' ');
62
+ const end = eol + 1 + Number(size);
63
+ if (pending.length < end + 1) return;
64
+ const body = pending.subarray(eol + 1, end);
65
+ pending = pending.subarray(end + 1);
66
+ waiting.shift()({ sha, type, body });
67
+ }
68
+ });
69
+ const read = (spec) =>
70
+ new Promise((resolve) => {
71
+ waiting.push(resolve);
72
+ catFile.stdin.write(`${spec}\n`);
73
+ });
74
+
75
+ /** Entries of a tree object body: [{ name, tree, sha }] in git (ls-tree) order. */
76
+ function parseTree(body) {
77
+ const entries = [];
78
+ for (let at = 0; at < body.length; ) {
79
+ const space = body.indexOf(32, at);
80
+ const nul = body.indexOf(0, space);
81
+ const mode = body.toString('latin1', at, space);
82
+ entries.push({
83
+ name: body.toString('utf8', space + 1, nul),
84
+ tree: mode === '40000',
85
+ sha: body.toString('hex', nul + 1, nul + 21),
86
+ });
87
+ at = nul + 21;
88
+ }
89
+ return entries;
90
+ }
91
+
92
+ const treeCache = new Map();
93
+ const blobCache = new Map();
94
+ const treeOf = async (sha) => {
95
+ if (!treeCache.has(sha)) treeCache.set(sha, read(sha).then(({ body }) => parseTree(body)));
96
+ return treeCache.get(sha);
97
+ };
98
+ const blobOf = async (sha) => {
99
+ if (!blobCache.has(sha)) blobCache.set(sha, read(sha).then(({ body }) => body));
100
+ return blobCache.get(sha);
46
101
  };
47
102
 
103
+ /** Relative path -> blob sha for every file under a tree entry (a lone file keeps its own name). */
104
+ async function filesOf({ name, tree, sha }) {
105
+ if (!tree) return new Map([[name, sha]]);
106
+ const files = new Map();
107
+ const walk = async (treeSha, base) => {
108
+ for (const entry of await treeOf(treeSha)) {
109
+ if (entry.tree) await walk(entry.sha, `${base}${entry.name}/`);
110
+ else files.set(`${base}${entry.name}`, entry.sha);
111
+ }
112
+ };
113
+ await walk(sha, '');
114
+ return files;
115
+ }
116
+
48
117
  /** Names (path segment after `prefix`) that ever existed under prefix. */
49
118
  function everNames(prefix) {
50
119
  const names = new Set();
@@ -54,29 +123,39 @@ function everNames(prefix) {
54
123
  return [...names].sort();
55
124
  }
56
125
 
57
- /** Every version of `path`: Map of relative path -> blob sha, one per commit that touched it. */
58
- function versionsOf(path) {
59
- const versions = [];
60
- for (const commit of lines(git('log', '--format=%H', '--', path))) {
61
- const files = new Map();
62
- for (const row of lines(git('ls-tree', '-r', commit, '--', path))) {
63
- const [meta, file] = row.split('\t');
64
- files.set(file.slice(path.length + 1) || file.split('/').pop(), meta.split(' ')[2]);
126
+ /**
127
+ * Every distinct state `prefix/<name>` had in the commits the path-limited
128
+ * `git log -- path` lists (its history simplification decides which commits
129
+ * count). Only the object reads are batched through `cat-file`.
130
+ */
131
+ async function everVersions(prefix) {
132
+ const out = new Map();
133
+ for (const name of everNames(prefix)) {
134
+ const path = `${prefix}/${name}`;
135
+ const states = new Map();
136
+ for (const commit of lines(git('log', '--format=%H', '--', path))) {
137
+ const object = await read(`${commit}:${path}`);
138
+ if (object) states.set(object.sha, { name, tree: object.type === 'tree', sha: object.sha });
65
139
  }
66
- if (files.size) versions.push(files);
140
+ const versions = [];
141
+ for (const entry of states.values()) {
142
+ const files = await filesOf(entry);
143
+ if (files.size) versions.push(files);
144
+ }
145
+ out.set(name, versions);
67
146
  }
68
- return versions;
147
+ return out;
69
148
  }
70
-
71
149
  const unique = (hashes) => [...new Set(hashes)].sort();
72
150
 
73
- function packHistory(dir, entry) {
151
+ async function packHistory(dir, entry) {
74
152
  const out = {};
75
- for (const id of everNames(dir)) {
153
+ for (const [id, versions] of await everVersions(dir)) {
76
154
  const hashes = [];
77
- for (const files of versionsOf(`${dir}/${id}`)) {
78
- const read = (name) => (files.has(name) ? blob(files.get(name)).toString('utf8') : null);
79
- const definition = packDefinitionFrom(read, entry, id);
155
+ for (const files of versions) {
156
+ const texts = new Map();
157
+ for (const [file, sha] of files) texts.set(file, (await blobOf(sha)).toString('utf8'));
158
+ const definition = packDefinitionFrom((name) => texts.get(name) ?? null, entry, id);
80
159
  if (definition) hashes.push(hashDefinition(definition));
81
160
  }
82
161
  if (hashes.length) out[id] = unique(hashes);
@@ -84,32 +163,35 @@ function packHistory(dir, entry) {
84
163
  return out;
85
164
  }
86
165
 
87
- function outputStyleHistory() {
166
+ async function outputStyleHistory() {
88
167
  const out = {};
89
- for (const name of everNames('src/output-styles')) {
90
- const hashes = versionsOf(`src/output-styles/${name}`).map((files) => hashText(blob([...files.values()][0])));
168
+ for (const [name, versions] of await everVersions('src/output-styles')) {
169
+ const hashes = [];
170
+ for (const files of versions) hashes.push(hashText(await blobOf([...files.values()][0])));
91
171
  if (hashes.length) out[name] = unique(hashes);
92
172
  }
93
173
  return out;
94
174
  }
95
175
 
96
- function skillHistory() {
176
+ async function skillHistory() {
97
177
  const out = {};
98
- for (const name of everNames('src/defaults/skills')) {
99
- const hashes = versionsOf(`src/defaults/skills/${name}`).map((files) =>
100
- hashTree([...files].map(([rel, sha]) => [rel, blob(sha)]))
101
- );
178
+ for (const [name, versions] of await everVersions('src/defaults/skills')) {
179
+ const hashes = [];
180
+ for (const files of versions) {
181
+ hashes.push(hashTree(await Promise.all([...files].map(async ([rel, sha]) => [rel, await blobOf(sha)]))));
182
+ }
102
183
  if (hashes.length) out[name] = unique(hashes);
103
184
  }
104
185
  return out;
105
186
  }
106
187
 
107
188
  const history = {
108
- agents: packHistory('src/agents', 'AGENT.md'),
109
- workflows: packHistory('src/workflows', 'WORKFLOW.md'),
110
- outputStyles: outputStyleHistory(),
111
- skills: skillHistory(),
189
+ agents: await packHistory('src/agents', 'AGENT.md'),
190
+ workflows: await packHistory('src/workflows', 'WORKFLOW.md'),
191
+ outputStyles: await outputStyleHistory(),
192
+ skills: await skillHistory(),
112
193
  };
194
+ catFile.stdin.end();
113
195
  const text = `${JSON.stringify(history, null, 2)}\n`;
114
196
  if (CHECK) {
115
197
  let committed = '';
@@ -0,0 +1,25 @@
1
+ // Release Gate guard for the immutable voice runtime release. Deploy reuses the
2
+ // published runtime only while the build-input hash of the checkout matches it,
3
+ // so a change to any hash input (scripts/voice-runtime-config.json, the build
4
+ // scripts and workflow, generate-voice-runtime-manifest.mjs) without a
5
+ // runtimeVersion bump fails here on push instead of stopping a live Deploy.
6
+ // A formatter pass that only re-indents those files counts as a change.
7
+ // Run it on a Linux checkout: the hash covers the files as checked out there.
8
+ import { readFile } from 'node:fs/promises';
9
+
10
+ import {
11
+ VOICE_RUNTIME_VERSION,
12
+ assertVoiceRuntimeManifestIdentity,
13
+ compareSemver,
14
+ } from './generate-voice-runtime-manifest.mjs';
15
+
16
+ const bundled = JSON.parse(
17
+ await readFile(new URL('../src/runtime/channels/data/voice-runtime-manifest.json', import.meta.url), 'utf8')
18
+ );
19
+
20
+ if (compareSemver(VOICE_RUNTIME_VERSION, bundled.version) > 0) {
21
+ console.log(`voice runtime ${VOICE_RUNTIME_VERSION} is not released yet; Deploy builds it`);
22
+ } else {
23
+ await assertVoiceRuntimeManifestIdentity(bundled);
24
+ console.log(`voice runtime ${bundled.version} build inputs match the published runtime`);
25
+ }
@@ -1,12 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  // Local pre-push check. Mirrors the test legs of .github/workflows/release-gate.yml
3
- // (jobs: runtime, runtime-slow, desktop, desktop-tests fast/slow). It MUST be
4
- // updated together with that workflow whenever those commands change.
5
- // Up front, before any build or leg, it runs two fast source checks and stops
6
- // if either fails: the gate's shipped-history job (build-shipped-history.mjs
7
- // --check; needs a full clone, a shallow one keeps the committed file), and the
8
- // released-section changelog check the desktop fast lane asserts
9
- // (changelog-locales.test.mjs; Deploy's plan adds the Unreleased section).
3
+ // (jobs: runtime, runtime-slow, desktop, desktop-tests fast/slow, shipped-history).
4
+ // It MUST be updated together with that workflow whenever those commands change.
5
+ // Up front it runs the released-section changelog check the desktop fast lane
6
+ // asserts (changelog-locales.test.mjs; Deploy's plan adds the Unreleased
7
+ // section) and stops if it fails. Everything else runs from one shared pool of
8
+ // workers (scripts/lib/gate-scheduler.mjs): every test lane is cut into shards
9
+ // of its own files (each run with the lane's own npm script command), and the
10
+ // desktop build + daemon end-to-end test, the patch smoke and the gate's
11
+ // shipped-history job (build-shipped-history.mjs --check; needs a full clone, a
12
+ // shallow one keeps the committed file) are units of the same pool. A failed
13
+ // shipped-history check stops new starts, so the gate still ends early on it.
14
+ // The whole run is at below-normal priority so the desktop stays responsive.
10
15
  // Not covered locally: runtime-macos, computer-backend (cargo + Linux X11),
11
16
  // desktop-tests `computer` lane (windows-latest, src/main/computer/ only),
12
17
  // relay (apps/relay tests), graph (cargo test), patch (cargo test of the
@@ -15,22 +20,32 @@
15
20
  // unittest). The gate's path
16
21
  // filtering is not applied: every check and leg always runs. Run `npm ci`
17
22
  // (root and apps/desktop) first.
23
+ // Usage: npm run gate:local [-- --test-concurrency=<workers>]
24
+ // (MIXDOG_TEST_CONCURRENCY also sets the pool size; default: half the cores, at most 8).
18
25
  import { spawn, spawnSync } from 'node:child_process';
19
26
  import fs from 'node:fs';
20
27
  import os from 'node:os';
21
28
  import path from 'node:path';
22
29
  import { fileURLToPath } from 'node:url';
23
- import { gateTestBudget, runGateLegs } from './lib/gate-scheduler.mjs';
30
+ import {
31
+ DEFAULT_ESTIMATE_MS,
32
+ gateTestBudget,
33
+ measuredFileTimes,
34
+ planShards,
35
+ readTimingHistory,
36
+ runPool,
37
+ saveTimingHistory,
38
+ } from './lib/gate-scheduler.mjs';
39
+ import { pruneStaleTempDirs, removeTempDirSync } from './lib/stale-temp.mjs';
24
40
 
25
41
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
26
42
  const started = Date.now();
43
+ const budget = gateTestBudget(process.argv.slice(2));
44
+ // Inherited by every process the gate starts (Windows and POSIX alike).
45
+ os.setPriority(os.constants.priority.PRIORITY_BELOW_NORMAL);
27
46
 
28
- const preChecks = ['node scripts/build-shipped-history.mjs --check', 'node scripts/check-changelog-locales.mjs'];
29
- const failedPreChecks = preChecks.filter(
30
- (cmd) => spawnSync(cmd, { cwd: root, stdio: 'inherit', shell: true }).status !== 0
31
- );
32
- if (failedPreChecks.length) {
33
- console.error(`[gate:local] pre-check failed: ${failedPreChecks.join('; ')}`);
47
+ if (spawnSync('node scripts/check-changelog-locales.mjs', { cwd: root, stdio: 'inherit', shell: true }).status !== 0) {
48
+ console.error('[gate:local] pre-check failed: node scripts/check-changelog-locales.mjs');
34
49
  process.exit(1);
35
50
  }
36
51
 
@@ -47,74 +62,179 @@ if (build.status !== 0) {
47
62
  process.exit(1);
48
63
  }
49
64
 
50
- const logDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mixdog-gate-local-'));
51
- const resourceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mixdog-gate-resources-'));
52
- const budget = gateTestBudget();
53
- // weight: relative worker share, roughly each leg's measured duration x its 3 workers
54
- // (runtime ~440s, runtime-slow ~545s, desktop fast ~390s, desktop slow ~280s).
55
- const legs = [
56
- { name: 'runtime', weight: 3, cmds: ['npm test', 'npm run smoke:patch'] },
57
- { name: 'desktop-tests-fast', weight: 3, cmds: ['npm test --prefix apps/desktop'] },
58
- { name: 'runtime-slow', weight: 4, cmds: ['npm run test:slow'] },
59
- { name: 'desktop-tests-slow', weight: 2, cmds: ['npm run test:slow --prefix apps/desktop'] },
60
- { name: 'desktop', cmds: ['npm run build --prefix apps/desktop', 'npm run test:daemon:e2e --prefix apps/desktop'] },
61
- ];
65
+ // Each run owns its log directory, so concurrent runs never touch each other's
66
+ // logs and the paths the summary prints stay valid after any outcome. Log
67
+ // directories are kept until they go stale; a resources directory a killed
68
+ // run left behind goes the same way. Both are swept at start.
69
+ const LOG_PREFIX = 'mixdog-gate-local-';
70
+ const RESOURCE_PREFIX = 'mixdog-gate-resources-';
71
+ pruneStaleTempDirs([LOG_PREFIX, RESOURCE_PREFIX]);
72
+ const logDir = fs.mkdtempSync(path.join(os.tmpdir(), LOG_PREFIX));
73
+ const resourceDir = fs.mkdtempSync(path.join(os.tmpdir(), RESOURCE_PREFIX));
74
+
75
+ // Measured times live in the repository's common git dir, so every worktree
76
+ // and snapshot of the repo plans from the same history. Keys: a test file's
77
+ // repo-relative path, or `cmd:` + a command unit's commands. Timings only
78
+ // plan the run: without them (no git dir, unreadable or invalid history) the
79
+ // gate plans from default estimates.
80
+ const commonDir = spawnSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {
81
+ cwd: root,
82
+ encoding: 'utf8',
83
+ });
84
+ const historyPath =
85
+ commonDir.status === 0 ? path.join(commonDir.stdout.trim(), 'mixdog-gate-timings.json') : null;
86
+ if (!historyPath) console.warn(`[gate:local] warning: no git common dir, timings not kept: ${commonDir.stderr}`);
87
+ const history = historyPath ? readTimingHistory(historyPath) : {};
88
+ const measured = {};
89
+
90
+ // Each test lane runs its package's own npm script command, given shard files
91
+ // as filters (scripts/test.mjs), without npm or a shell between: a shell's
92
+ // command line could not hold a shard's paths.
93
+ const testLanes = [
94
+ { name: 'runtime', pkg: '.', script: 'test', speed: 'fast' },
95
+ { name: 'runtime-slow', pkg: '.', script: 'test:slow', speed: 'slow' },
96
+ { name: 'desktop-tests-fast', pkg: 'apps/desktop', script: 'test', speed: 'fast' },
97
+ { name: 'desktop-tests-slow', pkg: 'apps/desktop', script: 'test:slow', speed: 'slow' },
98
+ ].map((lane) => {
99
+ const dir = path.join(root, lane.pkg);
100
+ const command = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')).scripts[lane.script];
101
+ const [program, ...args] = command.split(/\s+/);
102
+ if (program !== 'node') throw new Error(`${lane.pkg}/package.json "${lane.script}" must be a node command: ${command}`);
103
+ const list = spawnSync(process.execPath, [...args, '--list'], { cwd: dir, encoding: 'utf8' });
104
+ if (list.status !== 0) throw new Error(`${command} --list failed: ${list.stderr}`);
105
+ const files = list.stdout
106
+ .split(/\r?\n/)
107
+ .filter(Boolean)
108
+ .map((line) => line.replace(/^\S+\s+/, ''));
109
+ const key = (file) => path.posix.join(lane.pkg, file);
110
+ return { ...lane, dir, args, files, key, estimateOf: (file) => history[key(file)] ?? DEFAULT_ESTIMATE_MS[lane.speed] };
111
+ });
112
+ const lanesByName = new Map(testLanes.map((lane) => [lane.name, lane]));
113
+ const commandUnits = [
114
+ { lane: 'shipped-history', cmds: ['node scripts/build-shipped-history.mjs --check'], stopOnFail: true },
115
+ { lane: 'runtime', cmds: ['npm run smoke:patch'] },
116
+ // Only the daemon end-to-end test reads the build (apps/desktop/out); no
117
+ // test lane does, so the build shares the pool with them.
118
+ { lane: 'desktop', cmds: ['npm run build --prefix apps/desktop', 'npm run test:daemon:e2e --prefix apps/desktop'] },
119
+ ].map((unit) => {
120
+ const key = `cmd:${unit.cmds.join(' && ')}`;
121
+ return { ...unit, key, estimate: history[key] ?? DEFAULT_ESTIMATE_MS.command };
122
+ });
123
+ const units = [...planShards(testLanes, budget), ...commandUnits];
124
+ const laneCounts = new Map();
125
+ for (const unit of units) {
126
+ laneCounts.set(unit.lane, (laneCounts.get(unit.lane) ?? 0) + 1);
127
+ unit.id = `${unit.lane}.${laneCounts.get(unit.lane)}`;
128
+ }
129
+ const laneNames = [...laneCounts.keys()];
130
+ const laneLog = (name) => path.join(logDir, `${name}.log`);
62
131
 
63
- function runCmd(cmd, logFd, concurrency) {
132
+ // Every unit is one worker: a shard runs its files one at a time.
133
+ const env = { ...process.env, MIXDOG_TEST_CONCURRENCY: '1', MIXDOG_TEST_OOXML_CACHE_DIR: resourceDir };
134
+ function run(command, args, options, fd) {
64
135
  return new Promise((resolve) => {
65
- fs.writeSync(logFd, `\n$ ${cmd}\n`);
66
- const child = spawn(cmd, {
67
- cwd: root,
68
- shell: true,
69
- stdio: ['ignore', logFd, logFd],
70
- env: {
71
- ...process.env,
72
- MIXDOG_TEST_CONCURRENCY: String(concurrency),
73
- MIXDOG_TEST_OOXML_CACHE_DIR: resourceDir,
74
- },
136
+ const child = spawn(command, args, { ...options, env, windowsHide: true, stdio: ['ignore', fd, fd] });
137
+ child.on('error', (error) => {
138
+ fs.writeSync(fd, `${error.stack}\n`);
139
+ resolve(1);
75
140
  });
76
- child.on('error', () => resolve(1));
77
141
  child.on('close', (code) => resolve(code ?? 1));
78
142
  });
79
143
  }
80
144
 
81
- async function runLeg(leg, concurrency) {
82
- const t0 = Date.now();
83
- leg.log = path.join(logDir, `${leg.name}.log`);
84
- const fd = fs.openSync(leg.log, 'w');
85
- leg.ok = true;
86
- for (const cmd of leg.cmds) {
87
- if ((await runCmd(cmd, fd, concurrency)) !== 0) {
88
- leg.ok = false;
145
+ const TIMING_REPORTER = new URL('./lib/gate-timing-reporter.mjs', import.meta.url).href;
146
+ async function runShard(unit, fd) {
147
+ const lane = lanesByName.get(unit.lane);
148
+ const timings = path.join(logDir, `${unit.id}.timings.jsonl`);
149
+ const args = [...lane.args, `--test-reporter=${TIMING_REPORTER}`, `--test-reporter-destination=${timings}`];
150
+ fs.writeSync(fd, `\n$ (${lane.pkg}) node ${[...args, ...unit.files].join(' ')}\n`);
151
+ const code = await run(process.execPath, [...args, ...unit.files], { cwd: lane.dir }, fd);
152
+ const reported = Object.fromEntries(
153
+ (fs.existsSync(timings) ? fs.readFileSync(timings, 'utf8') : '')
154
+ .split('\n')
155
+ .filter(Boolean)
156
+ .map((line) => {
157
+ const { file, ms } = JSON.parse(line);
158
+ return [file, ms];
159
+ })
160
+ );
161
+ fs.rmSync(timings, { force: true });
162
+ for (const [file, ms] of Object.entries(measuredFileTimes(unit.files, reported, Date.now() - unit.start))) {
163
+ measured[lane.key(file)] = Math.round(ms);
164
+ }
165
+ return code === 0;
166
+ }
167
+
168
+ async function runCommands(unit, fd) {
169
+ let ok = true;
170
+ for (const cmd of unit.cmds) {
171
+ fs.writeSync(fd, `\n$ ${cmd}\n`);
172
+ if ((await run(cmd, [], { cwd: root, shell: true }, fd)) !== 0) {
173
+ ok = false;
89
174
  break;
90
175
  }
91
176
  }
92
- fs.closeSync(fd);
93
- leg.ms = Date.now() - t0;
177
+ measured[unit.key] = Date.now() - unit.start;
178
+ return ok;
94
179
  }
95
180
 
96
- console.log(`[gate:local] build separately, then tests sharing ${budget} workers; logs: ${logDir}`);
181
+ let finished = 0;
182
+ async function runUnit(unit) {
183
+ const part = path.join(logDir, `${unit.id}.part.log`);
184
+ const fd = fs.openSync(part, 'w');
185
+ unit.start = Date.now();
186
+ let ok = false;
187
+ try {
188
+ ok = await (unit.files ? runShard(unit, fd) : runCommands(unit, fd));
189
+ } finally {
190
+ fs.closeSync(fd);
191
+ unit.end = Date.now();
192
+ fs.appendFileSync(laneLog(unit.lane), fs.readFileSync(part));
193
+ fs.rmSync(part);
194
+ }
195
+ console.log(
196
+ `[gate:local] ${ok ? 'ok ' : 'FAIL'} ${unit.id} ${((unit.end - unit.start) / 1000).toFixed(1)}s (${++finished}/${units.length})`
197
+ );
198
+ return ok;
199
+ }
200
+
201
+ console.log(
202
+ `[gate:local] ${units.length} units on ${budget} workers at below-normal priority; timings: ${historyPath}; logs: ${logDir}`
203
+ );
204
+ let notStarted = [];
97
205
  try {
98
- // TypeScript/bundling must not compete with four independent test runners.
99
- await runLeg(legs.at(-1), budget);
100
- await runGateLegs(legs.slice(0, -1), budget, runLeg);
206
+ notStarted = await runPool(units, budget, runUnit);
101
207
  } finally {
102
- fs.rmSync(resourceDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
208
+ removeTempDirSync(resourceDir);
209
+ // The next run plans from these; work this run did not reach keeps its old time.
210
+ const keys = [...testLanes.flatMap((lane) => lane.files.map(lane.key)), ...commandUnits.map((unit) => unit.key)];
211
+ const next = Object.fromEntries(
212
+ keys.filter((key) => (measured[key] ?? history[key]) !== undefined).map((key) => [key, measured[key] ?? history[key]])
213
+ );
214
+ const saveError = historyPath && saveTimingHistory(historyPath, next);
215
+ if (saveError) console.warn(`[gate:local] warning: timings not saved (${saveError.message})`);
103
216
  }
104
217
 
105
218
  console.log('\n=== gate:local summary ===');
106
- for (const leg of legs) {
107
- console.log(`${leg.ok ? 'PASS' : 'FAIL'} ${leg.name} ${(leg.ms / 1000).toFixed(1)}s ${leg.log}`);
108
- if (!leg.ok) {
109
- const lines = fs
110
- .readFileSync(leg.log, 'utf8')
111
- .split(/\r?\n/)
112
- .filter((l) => l.includes('[failure-summary]'));
113
- for (const l of lines) console.log(` ${l}`);
114
- }
219
+ for (const name of laneNames) {
220
+ const laneUnits = units.filter((unit) => unit.lane === name);
221
+ const ran = laneUnits.filter((unit) => unit.end);
222
+ const status = ran.some((unit) => !unit.ok) ? 'FAIL' : ran.length < laneUnits.length ? 'NOT RUN' : 'PASS';
223
+ const span = ran.length ? Math.max(...ran.map((u) => u.end)) - Math.min(...ran.map((u) => u.start)) : 0;
224
+ const work = ran.reduce((sum, unit) => sum + unit.end - unit.start, 0);
225
+ console.log(
226
+ `${status} ${name} ${(span / 1000).toFixed(1)}s (${(work / 1000).toFixed(1)}s worker time, ${ran.length}/${laneUnits.length} units) ${laneLog(name)}`
227
+ );
228
+ if (status !== 'FAIL') continue;
229
+ const lines = fs
230
+ .readFileSync(laneLog(name), 'utf8')
231
+ .split(/\r?\n/)
232
+ .filter((l) => /\[failure-summary\] (?:RUN FAILED|HUNG|FAILED|FLAKY)/.test(l));
233
+ for (const l of lines) console.log(` ${l}`);
115
234
  }
235
+ if (notStarted.length) console.log(`Stopped after a failed ${units.find((u) => u.stopOnFail && u.ok === false).lane} check: ${notStarted.length} unit(s) not run`);
116
236
  console.log(
117
237
  'Not covered locally: runtime-macos, computer-backend, desktop-tests computer lane (Windows-only), relay, graph, patch, browser-import, mobile-scripts'
118
238
  );
119
239
  console.log(`Total: ${((Date.now() - started) / 1000).toFixed(1)}s`);
120
- process.exit(legs.every((l) => l.ok) ? 0 : 1);
240
+ process.exit(units.every((unit) => unit.ok) ? 0 : 1);
@@ -17,10 +17,20 @@ function report(text) {
17
17
  }
18
18
 
19
19
  function openIssue(repo, body) {
20
- const existing = JSON.parse(gh([
21
- 'issue', 'list', '--repo', repo, '--state', 'open', '--search', `"${ISSUE_TITLE}" in:title`,
22
- '--json', 'number,title',
23
- ])).find((issue) => issue.title === ISSUE_TITLE);
20
+ const existing = JSON.parse(
21
+ gh([
22
+ 'issue',
23
+ 'list',
24
+ '--repo',
25
+ repo,
26
+ '--state',
27
+ 'open',
28
+ '--search',
29
+ `"${ISSUE_TITLE}" in:title`,
30
+ '--json',
31
+ 'number,title',
32
+ ])
33
+ ).find((issue) => issue.title === ISSUE_TITLE);
24
34
  if (existing) {
25
35
  gh(['issue', 'comment', String(existing.number), '--repo', repo, '--body', body]);
26
36
  return `commented on issue #${existing.number}`;
@@ -44,11 +54,13 @@ export function main(repo = process.env.GITHUB_REPOSITORY) {
44
54
  const problem = `GitHub Latest was \`${actual ?? 'none'}\` but the newest published app release is \`${expected.tag_name}\`.`;
45
55
  gh(['release', 'edit', expected.tag_name, '--repo', repo, '--latest']);
46
56
  const repaired = currentLatestTag(repo);
47
- const outcome = repaired === expected.tag_name
48
- ? `Set Latest back to \`${expected.tag_name}\`.`
49
- : `Could not set Latest to \`${expected.tag_name}\`; it is now \`${repaired ?? 'none'}\`.`;
50
- const detail = `${problem}\n${outcome}\n\nWorkflow run: ${process.env.GITHUB_SERVER_URL ?? ''}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID ?? ''}\n`
51
- + 'Check which release or workflow published a non-app release without `--latest=false` / `make_latest: false`.';
57
+ const outcome =
58
+ repaired === expected.tag_name
59
+ ? `Set Latest back to \`${expected.tag_name}\`.`
60
+ : `Could not set Latest to \`${expected.tag_name}\`; it is now \`${repaired ?? 'none'}\`.`;
61
+ const detail =
62
+ `${problem}\n${outcome}\n\nWorkflow run: ${process.env.GITHUB_SERVER_URL ?? ''}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID ?? ''}\n` +
63
+ 'Check which release or workflow published a non-app release without `--latest=false` / `make_latest: false`.';
52
64
  report(`## Latest release guard\n${detail}`);
53
65
  try {
54
66
  report(`Issue: ${openIssue(repo, detail)}.`);