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
@@ -9,11 +9,12 @@ import { withBuiltinPathLocks } from './path-locks.mjs';
9
9
  import { withAdvisoryLocks } from './advisory-lock.mjs';
10
10
  import { gitRepoLockKey, withGitWorktreeWriteLock } from './git-repo-rw-lock.mjs';
11
11
  import { invalidateBuiltinResultCache } from './cache-layers.mjs';
12
- import { tryAdmitWorkspaceActivity } from '../../../../shared/worktrees/activity-gate.mjs';
12
+ import { tryAdmitWorkspaceActivityAsync } from '../../../../shared/worktrees/activity-gate.mjs';
13
13
  import { WORKTREE_SNAPSHOT_KIND } from '../../../../shared/worktrees/contract.mjs';
14
14
  import { isWithin } from '../../../../shared/worktrees/paths.mjs';
15
15
  import { readRegistrySync } from '../../../../shared/worktrees/registry.mjs';
16
16
  import { runAbortable } from '../../../../shared/abort-race.mjs';
17
+ import { startGit } from '../../../../shared/git-launch.mjs';
17
18
  import { sleep } from '../../../../shared/sleep.mjs';
18
19
  import { capMarker } from '../../../../shared/tool-output-marker.mjs';
19
20
  import { drainCodeGraphCache } from '../code-graph-state.mjs';
@@ -83,6 +84,9 @@ const SERVER_OPERATIONS = new Set(['daemon', 'instaweb']);
83
84
  // probe into `git rev-parse exited 128` inside a plain directory.
84
85
  const REPO_FREE_OPERATIONS = new Set(['version', 'help']);
85
86
 
87
+ const GIT_DESCRIPTION_HEAD =
88
+ 'Run Git here, never through shell. An array runs in order and stops on failure; batch read-only commands in one. diff for known changes, status to discover them; history only when needed. git diff or git diff -- <paths> includes untracked files. Set include_stage_ids:true to select changes, then action:stage with diff_id/change_ids. Staging rejects stale or cross-Project diffs and returns per-file staged counts.';
89
+
86
90
  export const GIT_TOOL_DEF = {
87
91
  name: 'git',
88
92
  title: 'Git',
@@ -94,8 +98,7 @@ export const GIT_TOOL_DEF = {
94
98
  openWorldHint: true,
95
99
  compressible: true,
96
100
  },
97
- description:
98
- 'Run Git here, never through shell. An array (max 10) runs in order and stops on failure; batch read-only commands in one. diff for known changes, status to discover them; history only when needed. git diff or git diff -- <paths> includes untracked files. Set include_stage_ids:true to select changes, then action:stage with diff_id/change_ids. Staging rejects stale or cross-Project diffs and returns per-file staged counts. Worktrees are managed only: action:worktree (Lead only) moves a session into/out of its managed worktree, merges, removes, lists, prunes; git worktree add/remove/list/prune map to managed operations (add -b keeps the session in place; add --detach makes a read-only snapshot worktree, removed when its turn ends; list --porcelain stays raw git; move/lock/unlock/repair are refused). Merge or remove only when the user asks; carry and large-size answers come from the user; force only on explicit request.',
101
+ description: `${GIT_DESCRIPTION_HEAD} Worktrees are managed only: action:worktree; git worktree add/remove/list/prune map to managed operations (add -b keeps the session in place; add --detach makes a read-only snapshot worktree, removed when its turn ends; list --porcelain stays raw git; move/lock/unlock/repair are refused). Merge or remove only when the user asks.`,
99
102
  inputSchema: {
100
103
  type: 'object',
101
104
  properties: {
@@ -118,14 +121,14 @@ export const GIT_TOOL_DEF = {
118
121
  minimum: 1,
119
122
  maximum: GIT_OUTPUT_LIMIT_MAX,
120
123
  description:
121
- 'Body line cap, max 1000; staging IDs are not capped. Default 500; git log defaults to 100. A cut ends with [output capped …] naming how to continue.',
124
+ 'Body line cap; staging IDs are not capped. Default 500; git log defaults to 100. A cut ends with [output capped …] naming how to continue.',
122
125
  },
123
126
  include_stage_ids: {
124
127
  type: 'boolean',
125
128
  description:
126
129
  'Include staging IDs outside the body cap for git diff or git diff -- <paths> only. Default false.',
127
130
  },
128
- diff_id: { type: 'string', description: 'stage: exact diff_id from git diff with include_stage_ids:true.' },
131
+ diff_id: { type: 'string', description: 'stage: exact diff_id from git diff.' },
129
132
  change_ids: {
130
133
  anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 50 }],
131
134
  description: 'stage: exact change ID or IDs to stage, including new files.',
@@ -158,6 +161,29 @@ export const GIT_TOOL_DEF = {
158
161
  },
159
162
  };
160
163
 
164
+ // Only the Lead host runs enter/return/merge/remove/prune (git-worktree-tool.mjs
165
+ // leadHost); an agent keeps `list`, so its schema omits every field the other
166
+ // operations take.
167
+ const LEAD_ONLY_WORKTREE_FIELDS = ['session_id', 'name', 'branch', 'create', 'carry', 'confirm_large', 'force'];
168
+
169
+ function buildGitAgentToolDef(base) {
170
+ const properties = { ...base.inputSchema.properties };
171
+ for (const field of LEAD_ONLY_WORKTREE_FIELDS) delete properties[field];
172
+ properties.action = {
173
+ ...properties.action,
174
+ description: 'Default command. stage uses diff_id/change_ids instead of command. worktree uses worktree_action.',
175
+ };
176
+ properties.worktree_action = { type: 'string', enum: ['list'], description: 'list only.' };
177
+ return {
178
+ ...base,
179
+ description: `${GIT_DESCRIPTION_HEAD} Worktrees are Lead-managed: only worktree_action:list and git worktree list work; every other worktree operation is refused.`,
180
+ inputSchema: { ...base.inputSchema, properties },
181
+ };
182
+ }
183
+
184
+ /** The git tool as a delegated agent sees it (see bindAgentOwner). */
185
+ export const GIT_AGENT_TOOL_DEF = buildGitAgentToolDef(GIT_TOOL_DEF);
186
+
161
187
  function cleanText(value) {
162
188
  return String(value || '')
163
189
  .replace(/\r\n/g, '\n')
@@ -412,7 +438,24 @@ function parseCommand(rawCommand, workDir) {
412
438
  return { command: String(command), cwd, globalArgs, operation, args };
413
439
  }
414
440
 
415
- async function runProcess(program, argv, { cwd, signal, maxBytes = MAX_CAPTURE_BYTES, optionalLocks = true } = {}) {
441
+ function gitToolEnvironment(optionalLocks) {
442
+ return {
443
+ ...process.env,
444
+ GIT_TERMINAL_PROMPT: '0',
445
+ GIT_PAGER: 'cat',
446
+ GIT_EDITOR: 'true',
447
+ GIT_SEQUENCE_EDITOR: 'true',
448
+ LC_ALL: 'C',
449
+ // Reads run beside writes without a lock (git-repo-rw-lock.mjs).
450
+ // With optional locks off `git status` never takes index.lock to
451
+ // write back a refreshed index, so it cannot fail a concurrent
452
+ // write on that lock. (`git diff` ignores the setting and may still
453
+ // refresh the index briefly; the write retry absorbs that.)
454
+ ...(optionalLocks ? {} : { GIT_OPTIONAL_LOCKS: '0' }),
455
+ };
456
+ }
457
+
458
+ async function runProcess(program, argv, { cwd, signal, maxBytes = MAX_CAPTURE_BYTES, env } = {}) {
416
459
  let child;
417
460
  try {
418
461
  await ensureNativeSpawnServer();
@@ -421,20 +464,7 @@ async function runProcess(program, argv, { cwd, signal, maxBytes = MAX_CAPTURE_B
421
464
  argv,
422
465
  spawnOptions: {
423
466
  cwd,
424
- env: {
425
- ...process.env,
426
- GIT_TERMINAL_PROMPT: '0',
427
- GIT_PAGER: 'cat',
428
- GIT_EDITOR: 'true',
429
- GIT_SEQUENCE_EDITOR: 'true',
430
- LC_ALL: 'C',
431
- // Reads run beside writes without a lock (git-repo-rw-lock.mjs).
432
- // With optional locks off `git status` never takes index.lock to
433
- // write back a refreshed index, so it cannot fail a concurrent
434
- // write on that lock. (`git diff` ignores the setting and may still
435
- // refresh the index briefly; the write retry absorbs that.)
436
- ...(optionalLocks ? {} : { GIT_OPTIONAL_LOCKS: '0' }),
437
- },
467
+ env,
438
468
  outputLimit: maxBytes,
439
469
  // Bytes as git wrote them, decoded once by the caller: a multibyte
440
470
  // character split across two pipe reads must not become U+FFFD.
@@ -548,11 +578,11 @@ function commandFailure(plan, result, limit = GIT_OUTPUT_LINES_DEFAULT) {
548
578
  }
549
579
 
550
580
  function runGit(plan, argv, options = {}) {
551
- return runProcess('git', [...plan.globalArgs, ...argv], {
552
- cwd: plan.cwd,
553
- signal: options.signal,
554
- optionalLocks: !options.readOnly,
555
- });
581
+ return startGit(
582
+ gitToolEnvironment(!options.readOnly),
583
+ (command, env) => runProcess(command, [...plan.globalArgs, ...argv], { cwd: plan.cwd, signal: options.signal, env }),
584
+ (result) => result.error
585
+ );
556
586
  }
557
587
 
558
588
  // Another process (an editor, a terminal, an IDE) holding one of git's lock
@@ -560,6 +590,10 @@ function runGit(plan, argv, options = {}) {
560
590
  // config.lock. Commands take that lock before changing anything, so a brief
561
591
  // retry with backoff turns the collision into a short wait; a command that had
562
592
  // already done part of its work reports its own state on the retry.
593
+ // Git reports lock contention only as text: lockfile.c's "Unable to create
594
+ // '<path>.lock': File exists" (and config.c's "could not lock config file"),
595
+ // stable English because runProcess pins LC_ALL=C. The delays are a bounded
596
+ // backoff (3.1 s in all), a policy choice rather than anything git reports.
563
597
  const GIT_LOCK_CONTENTION_RE =
564
598
  /Unable to create '[^'\n]*\.lock': File exists|could not lock config file [^\n]*: File exists/;
565
599
  const GIT_LOCK_RETRY_DELAYS_MS = [100, 200, 400, 800, 1600];
@@ -849,7 +883,8 @@ function snapshotContaining(repo) {
849
883
  }
850
884
  return (
851
885
  Object.values(entries).find(
852
- (record) => record?.kind === WORKTREE_SNAPSHOT_KIND && typeof record.path === 'string' && isWithin(record.path, repo)
886
+ (record) =>
887
+ record?.kind === WORKTREE_SNAPSHOT_KIND && typeof record.path === 'string' && isWithin(record.path, repo)
853
888
  ) ?? null
854
889
  );
855
890
  }
@@ -858,12 +893,13 @@ function snapshotContaining(repo) {
858
893
  // lease it holds (activity-gate, the same as a background shell's) keeps the
859
894
  // snapshot from being removed in any process until it ends, and a snapshot
860
895
  // whose removal is under way refuses it. Returns the release.
861
- function admitSnapshotCommand(repo, sessionId) {
896
+ async function admitSnapshotCommand(repo, sessionId) {
862
897
  const snapshot = snapshotContaining(repo);
863
898
  if (!snapshot) return () => {};
864
- const leave = tryAdmitWorkspaceActivity(sessionId || 'git-tool', repo, { kind: 'command' });
899
+ const leave = await tryAdmitWorkspaceActivityAsync(sessionId || 'git-tool', repo, { kind: 'command' });
865
900
  if (!leave) throw new Error(`the snapshot worktree ${snapshot.path} is being removed; nothing ran`);
866
- return leave;
901
+ // Fails only when workspace admission stopped (reported there), which keeps the snapshot held.
902
+ return () => leave().catch(() => {});
867
903
  }
868
904
 
869
905
  function invalidateAfterGitMutation() {
@@ -1031,14 +1067,14 @@ async function executeSingleGitTool(input, workDir, options = {}) {
1031
1067
  // under it, and a snapshot being removed refuses it.
1032
1068
  let leaveSnapshot;
1033
1069
  try {
1034
- leaveSnapshot = admitSnapshotCommand(repo, options.sessionId);
1070
+ leaveSnapshot = await admitSnapshotCommand(repo, options.sessionId);
1035
1071
  } catch (error) {
1036
1072
  return rejected(error.message);
1037
1073
  }
1038
1074
  try {
1039
1075
  return await runInRepository(input, plan, { repo, lockKey, limit, signal, workDir });
1040
1076
  } finally {
1041
- leaveSnapshot();
1077
+ await leaveSnapshot();
1042
1078
  }
1043
1079
  }
1044
1080
 
@@ -101,6 +101,8 @@ function worktreeFailureText(code, message, details) {
101
101
  return typeof details.path === 'string' && details.path
102
102
  ? `This branch is already checked out in ${details.path}.`
103
103
  : 'This branch is already checked out in another worktree.';
104
+ case 'WORKTREE_DEFAULT_BRANCH':
105
+ return "The repository's default branch can't be opened in a worktree: the project folder could not switch to it while the worktree exists. Check it out in the project folder, or create a new branch for the worktree.";
104
106
  case 'WORKTREE_REMOVAL_INCOMPLETE':
105
107
  return 'The worktree could not be removed completely because a file is still in use. Close it and remove the worktree again.';
106
108
  case 'WORKTREE_PATH_UNSAFE':
@@ -162,6 +164,8 @@ function nextStep(code, details, { raw }) {
162
164
  return 'The workspace changed meanwhile; call again (the workspace is read afresh).';
163
165
  case 'WORKTREE_BUSY_JOBS':
164
166
  return 'Nothing was changed; try again once the session and its workers are idle.';
167
+ case 'WORKTREE_DEFAULT_BRANCH':
168
+ return 'Nothing was created. Work on a new branch instead (worktree_action:"enter" with branch and create:true), or ask the user to check the default branch out in the project folder.';
165
169
  case 'MERGE_CONFLICT':
166
170
  return 'Nothing was merged or forced. Resolve the conflict in the worktree (merge the target branch into it there), or ask the user.';
167
171
  case 'WORKTREE_DIRTY':
@@ -316,7 +320,8 @@ async function rawAdd(plan, input, options) {
316
320
  throw new Error(`git worktree add: ${value} is not supported for managed worktrees`);
317
321
  }
318
322
  if (ADD_BARE_FLAGS.has(value)) continue;
319
- if (value.startsWith('-')) throw new Error(`git worktree add: option ${value} is not supported for managed worktrees`);
323
+ if (value.startsWith('-'))
324
+ throw new Error(`git worktree add: option ${value} is not supported for managed worktrees`);
320
325
  positional.push(value);
321
326
  }
322
327
  const [path, commitish = null] = positional;
@@ -329,11 +334,9 @@ async function rawAdd(plan, input, options) {
329
334
  const txId = txIdOf(options, plan.command);
330
335
  const mapped = { mapped: plan.command, requestedPath: resolve(plan.cwd, path) };
331
336
  // A detached checkout, or a commit-ish that names no local branch: a snapshot.
332
- const snapshotCommit = detach
333
- ? (commitish ?? 'HEAD')
334
- : !branch && commitish && !(await localBranchExists(plan.cwd, commitish))
335
- ? commitish
336
- : null;
337
+ let snapshotCommit = null;
338
+ if (detach) snapshotCommit = commitish ?? 'HEAD';
339
+ else if (!branch && commitish && !(await localBranchExists(plan.cwd, commitish))) snapshotCommit = commitish;
337
340
  if (snapshotCommit) {
338
341
  const commit = await commitIn(plan.cwd, snapshotCommit);
339
342
  if (!commit) throw new Error(`git worktree add: ${snapshotCommit} is not a commit`);
@@ -436,8 +439,9 @@ async function rawPrune(plan, options) {
436
439
  const WORKTREE_HELP = Object.freeze({
437
440
  add: 'git worktree add [-b <branch>] [--detach] <path> [<commit-ish>]: a managed worktree for the branch (the session stays put), or a read-only snapshot for a detached commit.',
438
441
  remove: 'git worktree remove [-f] <path>: the managed removal of that worktree.',
439
- list: 'git worktree list [-v]: managed worktrees plus the repository\'s other worktrees. --porcelain stays raw git.',
440
- prune: 'git worktree prune [-n|--dry-run] [-v]: the managed prune of this repository (clean, merged worktrees no session retains); -n only lists what it would remove.',
442
+ list: "git worktree list [-v]: managed worktrees plus the repository's other worktrees. --porcelain stays raw git.",
443
+ prune:
444
+ 'git worktree prune [-n|--dry-run] [-v]: the managed prune of this repository (clean, merged worktrees no session retains); -n only lists what it would remove.',
441
445
  });
442
446
 
443
447
  /** -h/--help before any `--` (the help answers instead of the operation running). */
@@ -450,7 +454,9 @@ function wantsHelp(args) {
450
454
  function checkListArgs(args) {
451
455
  for (const value of args) {
452
456
  if (value === '-v' || value === '--verbose') continue;
453
- throw new Error(`git worktree list: ${value.startsWith('-') ? `option ${value} is not supported` : `unexpected argument ${value}`}`);
457
+ throw new Error(
458
+ `git worktree list: ${value.startsWith('-') ? `option ${value} is not supported` : `unexpected argument ${value}`}`
459
+ );
454
460
  }
455
461
  }
456
462
 
@@ -466,7 +472,8 @@ export async function executeRawWorktree(plan, input, options = {}) {
466
472
  if (sub === 'list' && plan.args.includes('--porcelain')) return null;
467
473
  try {
468
474
  if (sub === 'list') checkListArgs(plan.args.slice(1));
469
- if (sub === 'list') return { text: okJson({ mapped: plan.command, ...(await listRows(options, plan.cwd)) }), failed: false };
475
+ if (sub === 'list')
476
+ return { text: okJson({ mapped: plan.command, ...(await listRows(options, plan.cwd)) }), failed: false };
470
477
  if (sub === 'add') return { text: await rawAdd(plan, input, options), failed: false };
471
478
  if (sub === 'remove') return { text: await rawRemove(plan, options), failed: false };
472
479
  if (sub === 'prune') return { text: await rawPrune(plan, options), failed: false };
@@ -479,7 +486,9 @@ export async function executeRawWorktree(plan, input, options = {}) {
479
486
  };
480
487
  }
481
488
  return {
482
- text: fail(`git worktree ${sub || '(no subcommand)'} is not supported; use add, remove, list, prune or action:"worktree"`),
489
+ text: fail(
490
+ `git worktree ${sub || '(no subcommand)'} is not supported; use add, remove, list, prune or action:"worktree"`
491
+ ),
483
492
  failed: true,
484
493
  };
485
494
  } catch (error) {
@@ -529,7 +538,8 @@ export async function executeWorktreeAction(input, workDir, options = {}) {
529
538
  if (problem) return fail(problem);
530
539
  const action = input.worktree_action;
531
540
  try {
532
- if (action === 'list') return okJson({ worktree_action: action, ...(await listRows(options, resolve(workDir || process.cwd()))) });
541
+ if (action === 'list')
542
+ return okJson({ worktree_action: action, ...(await listRows(options, resolve(workDir || process.cwd()))) });
533
543
  const host = leadHost(options, `git worktree_action ${action}`);
534
544
  const sessionId = input.session_id?.trim() || null;
535
545
  const request = {
@@ -545,7 +555,9 @@ export async function executeWorktreeAction(input, workDir, options = {}) {
545
555
  }
546
556
  : {}),
547
557
  ...(action === 'remove' ? { force: input.force === true } : {}),
548
- ...(action === 'prune' ? { repoCommonDir: (await repositoryAt(resolve(workDir || process.cwd())))?.repoCommonDir ?? null } : {}),
558
+ ...(action === 'prune'
559
+ ? { repoCommonDir: (await repositoryAt(resolve(workDir || process.cwd())))?.repoCommonDir ?? null }
560
+ : {}),
549
561
  txId: txIdOf(options, action),
550
562
  };
551
563
  const result = await runOn(host, request, options);
@@ -8,11 +8,15 @@ import {
8
8
  // A shared scan's outcome belongs to every subscriber, not only the caller
9
9
  // whose computation won admission. Persisted cache hits contain safe text.
10
10
  export async function runScopedSearchInFlight(key, compute, options, outcome) {
11
- const settled = await runResultCacheInFlight(key, async ({ signal }) => {
12
- const scopedCacheOutcome = createScopedCacheOutcome();
13
- const result = await compute({ signal, scopedCacheOutcome });
14
- return { result, scopedCacheOutcome };
15
- }, options);
11
+ const settled = await runResultCacheInFlight(
12
+ key,
13
+ async ({ signal }) => {
14
+ const scopedCacheOutcome = createScopedCacheOutcome();
15
+ const result = await compute({ signal, scopedCacheOutcome });
16
+ return { result, scopedCacheOutcome };
17
+ },
18
+ options
19
+ );
16
20
  if (typeof settled === 'string') return settled;
17
21
  if (settled.scopedCacheOutcome.complete === false) markScopedCacheIncomplete(outcome);
18
22
  if (settled.scopedCacheOutcome.cacheSafe === false) markScopedCacheUnsafe(outcome);
@@ -27,24 +27,40 @@ import { join } from 'node:path';
27
27
  import { holdsNoData, writeJsonAtomicAsync } from '../../../../../shared/atomic-file.mjs';
28
28
  import { isPidAlive } from '../../../../../shared/pid-liveness.mjs';
29
29
  import { displayShellCommand } from '../../../../../shared/shell-display.mjs';
30
- import { tryAdmitWorkspaceActivity } from '../../../../../shared/worktrees/activity-gate.mjs';
30
+ import { tryAdmitWorkspaceActivityAsync } from '../../../../../shared/worktrees/activity-gate.mjs';
31
31
  import { worktreeError } from '../../../../../shared/worktrees/contract.mjs';
32
32
  import { workspaceSees } from '../../../../../shared/worktrees/workspace-visibility.mjs';
33
33
 
34
34
  const workspaceLeases = new Map();
35
35
 
36
+ /**
37
+ * Calls both releases in order; settles once both did. Never goes unhandled
38
+ * (an asynchronous release rejects only when workspace admission stopped).
39
+ */
40
+ export function bothReleased(first, second) {
41
+ const both = Promise.all([first(), second()]);
42
+ both.catch(() => {});
43
+ return both;
44
+ }
45
+
46
+ /**
47
+ * A workspace grant requested now, for a release handle needed before it is
48
+ * answered: the release, whenever called, follows the grant (and releases
49
+ * nothing if it was refused or failed).
50
+ */
51
+ export function pendingWorkspaceAdmission(granting) {
52
+ granting.catch(() => {});
53
+ return () =>
54
+ granting.then(
55
+ (leave) => leave?.(),
56
+ () => {}
57
+ );
58
+ }
59
+
36
60
  /** Transfer a foreground reader to a surviving-descendant task without a lease gap. */
37
61
  export function adoptShellWorkspaceAdmission(jobId, leave) {
38
62
  const prior = workspaceLeases.get(jobId);
39
- workspaceLeases.set(
40
- jobId,
41
- prior
42
- ? () => {
43
- prior();
44
- leave();
45
- }
46
- : leave
47
- );
63
+ workspaceLeases.set(jobId, prior ? () => bothReleased(prior, leave) : leave);
48
64
  }
49
65
 
50
66
  // A record whose owner process is gone is garbage. Age alone never deletes a
@@ -88,8 +104,13 @@ function validJobId(jobId) {
88
104
 
89
105
  function terminalRecord(base, detail) {
90
106
  const finishedAt = detail?.finishedAt || new Date().toISOString();
107
+ // A completed record no longer claims an unconfirmed termination: it is
108
+ // written once the processes are confirmed gone, or explicitly released.
109
+ // A completed record no longer claims an unconfirmed termination: it is
110
+ // written once the processes are confirmed gone, or explicitly released.
111
+ const { terminationUnconfirmed: _held, ...open } = base || {};
91
112
  return {
92
- ...(base || {}),
113
+ ...open,
93
114
  jobId: String(detail?.jobId || base?.jobId || ''),
94
115
  kind: 'bash',
95
116
  status: String(detail?.status || 'failed'),
@@ -172,7 +193,14 @@ async function sweepOrphanRecords(dir) {
172
193
  }
173
194
  }
174
195
 
175
- /** Publish the record for a job that has just started or been promoted. */
196
+ /**
197
+ * Publish the record for a job that has just started or been promoted. Its
198
+ * workspace lease (under the job's pid) is requested at the call and adopted
199
+ * under the job id at once, so a later release of the job — or of a
200
+ * predecessor's lease — is ordered after it. Resolves once the lease is in
201
+ * both ledger copies; rejects (and writes no record) when it is refused or
202
+ * cannot be written. The returned promise never goes unhandled.
203
+ */
176
204
  export function publishShellJobRecord(
177
205
  task,
178
206
  {
@@ -184,16 +212,20 @@ export function publishShellJobRecord(
184
212
  } = {}
185
213
  ) {
186
214
  const jobId = String(task?.jobId || '').trim();
187
- if (!jobId) return;
215
+ if (!jobId) return Promise.resolve();
216
+ let admitted = Promise.resolve();
188
217
  if (task?.cwd) {
189
- const leave = tryAdmitWorkspaceActivity(ownerSessionId || jobId, task.cwd, {
218
+ const granting = tryAdmitWorkspaceActivityAsync(ownerSessionId || jobId, task.cwd, {
190
219
  pid: Number(task.pid) || process.pid,
191
220
  kind: 'command',
192
221
  });
193
- if (!leave) throw worktreeError('WORKTREE_BUSY_JOBS', 'The command folder is being managed.');
222
+ admitted = granting.then((leave) => {
223
+ if (!leave) throw worktreeError('WORKTREE_BUSY_JOBS', 'The command folder is being managed.');
224
+ });
225
+ admitted.catch(() => {});
194
226
  // A descendant handoff can reuse the job id with a different live pid.
195
- // Acquire its reader before releasing any predecessor's admission.
196
- adoptShellWorkspaceAdmission(jobId, leave);
227
+ // Its reader is requested before any predecessor's admission is released.
228
+ adoptShellWorkspaceAdmission(jobId, pendingWorkspaceAdmission(granting));
197
229
  }
198
230
  const ownerPid = Number(clientHostPid ?? task?.clientHostPid) || process.pid;
199
231
  const dir = jobsDir();
@@ -202,6 +234,8 @@ export function publishShellJobRecord(
202
234
  paths.set(jobId, [jsonPath, ownerPath]);
203
235
  void enqueue(jobId, async () => {
204
236
  try {
237
+ // No record for a job whose workspace lease was not granted.
238
+ await admitted;
205
239
  await mkdir(dir, { recursive: true });
206
240
  if (!sweepStarted) {
207
241
  sweepStarted = true;
@@ -244,6 +278,7 @@ export function publishShellJobRecord(
244
278
  /* the record is presentation only; never fail a job for it */
245
279
  }
246
280
  });
281
+ return admitted;
247
282
  }
248
283
 
249
284
  /** Preserve a bounded terminal snapshot for manual recovery after restart. */
@@ -335,8 +370,9 @@ export function releaseShellJobWorkspace(jobId) {
335
370
  const key = String(jobId || '').trim();
336
371
  const leave = workspaceLeases.get(key);
337
372
  if (!leave) return;
338
- leave();
373
+ const released = leave();
339
374
  if (workspaceLeases.get(key) === leave) workspaceLeases.delete(key);
375
+ return released;
340
376
  }
341
377
 
342
378
  /** Flag a still-open job record as "termination unconfirmed" for the readouts. */
@@ -390,8 +426,10 @@ export function completeShellJobRecord(jobId, detail) {
390
426
  // Callers complete a record only once its processes are confirmed gone
391
427
  // (or explicitly released): the workspace admission follows that, not
392
428
  // the record write, so a failed write cannot strand it with no handle.
393
- leaveWorkspace?.();
429
+ const released = leaveWorkspace?.();
394
430
  if (workspaceLeases.get(key) === leaveWorkspace) workspaceLeases.delete(key);
431
+ // Fails only when workspace admission stopped (reported there), which keeps the folder held.
432
+ await Promise.resolve(released).catch(() => {});
395
433
  }
396
434
  await removePaths(ownerPaths);
397
435
  paths.set(key, [jsonPath, donePath]);
@@ -486,6 +524,6 @@ export function retireShellJobRecord(jobId) {
486
524
  records.delete(key);
487
525
  incarnations.delete(key);
488
526
  await removePaths(list);
489
- leaveWorkspace?.();
527
+ await leaveWorkspace?.();
490
528
  });
491
529
  }
@@ -53,7 +53,13 @@ export async function streamRangeResult(ctx, { source, fileHandle, prefixBuffer
53
53
  res.firstEmitted && res.lastEmitted ? [{ startLine: res.firstEmitted, endLine: res.lastEmitted }] : [];
54
54
  const snapshotMeta = { source, ranges, rangeHashes: _rangeHashesFromRenderedReadText(res.text, ranges) };
55
55
  if (!res.readSnapshotStat) return res.text;
56
- return await commitStreamResult({ ...ctx, st: res.readSnapshotStat }, res.text, snapshotMeta, res.prefixHash, helpers);
56
+ return await commitStreamResult(
57
+ { ...ctx, st: res.readSnapshotStat },
58
+ res.text,
59
+ snapshotMeta,
60
+ res.prefixHash,
61
+ helpers
62
+ );
57
63
  } catch (err) {
58
64
  return `Error: ${normalizeErrorMessage(err instanceof Error ? err.message : String(err))}`;
59
65
  }
@@ -137,9 +137,12 @@ async function readRangeFromHandle(fh, fullPath, offset, limit, stHint, hooks) {
137
137
  }
138
138
  if (!stop) lines.finishTail();
139
139
  let readSnapshotStat = null;
140
- if (!(await publishReadRangeIndex(rangeIndex, fh, null, checkDeadline, (version) => {
141
- readSnapshotStat = version;
142
- }))) checkDeadline();
140
+ if (
141
+ !(await publishReadRangeIndex(rangeIndex, fh, null, checkDeadline, (version) => {
142
+ readSnapshotStat = version;
143
+ }))
144
+ )
145
+ checkDeadline();
143
146
 
144
147
  ioTraceDone('read_range_stream', traceStart, {
145
148
  pathHash: hashText(fullPath).slice(0, 12),
@@ -41,7 +41,7 @@ async function estimateTaskBytes(entry, workDir) {
41
41
  const st = await stat(resolvePath(workDir || '.', String(entry?.path || ''))).catch(() => null);
42
42
  if (!st?.isFile()) return Infinity;
43
43
  const lines = Math.max(1, st.size / AVG_LINE_BYTES);
44
- let need = st.size + st.size / MIN_LINE_BYTES * LINE_PREFIX_BYTES;
44
+ let need = st.size + (st.size / MIN_LINE_BYTES) * LINE_PREFIX_BYTES;
45
45
  // A coalesced entry reads its whole union window, not only its own slot.
46
46
  const limit = Number(readEntryCoalescedDiskWindow(entry)?.limit ?? entry?.limit);
47
47
  if (Number.isFinite(limit) && limit > 0) need *= Math.min(1, limit / lines);
@@ -85,10 +85,14 @@ export async function runBatchReadSnapshotPublication(executeChildBuiltinTool, h
85
85
  // A numbered row may end halfway through its content at the byte cap.
86
86
  // Only this slot's producer can certify complete lines; another read of
87
87
  // the same path must not authorize hashing this slot's partial text.
88
- return mergeReadRanges(ranges.flatMap((range) => (observation?.ranges ?? []).map((complete) => ({
89
- startLine: Math.max(range.startLine, complete.startLine),
90
- endLine: Math.min(range.endLine, complete.endLine),
91
- }))));
88
+ return mergeReadRanges(
89
+ ranges.flatMap((range) =>
90
+ (observation?.ranges ?? []).map((complete) => ({
91
+ startLine: Math.max(range.startLine, complete.startLine),
92
+ endLine: Math.min(range.endLine, complete.endLine),
93
+ }))
94
+ )
95
+ );
92
96
  },
93
97
  _recordReadSnapshot: (fullPath, _st, scope, meta) => {
94
98
  corrections.set(fullPath, { scope: scope ?? null, meta });
@@ -104,22 +108,29 @@ export async function runBatchReadSnapshotPublication(executeChildBuiltinTool, h
104
108
  }
105
109
  for (const [fullPath, correction] of corrections) {
106
110
  const delivered = tasks.filter((task) => task.fullPath === fullPath && hasRenderedLines(task.body));
107
- const observations = delivered.map((task) => task.records.findLast(
108
- (record) => record.fullPath === fullPath && record.scope === correction.scope
109
- ));
111
+ const observations = delivered.map((task) =>
112
+ task.records.findLast((record) => record.fullPath === fullPath && record.scope === correction.scope)
113
+ );
110
114
  // Never combine different versions or bless stale/missing observations.
111
115
  // The eventual recorder rechecks the retained lease after all awaits.
112
- if (observations.length && observations.every((record) =>
113
- record?.isCurrent() && sameFileVersion(record.identity, observations[0].identity)
114
- )) {
116
+ if (
117
+ observations.length &&
118
+ observations.every(
119
+ (record) => record?.isCurrent() && sameFileVersion(record.identity, observations[0].identity)
120
+ )
121
+ ) {
115
122
  observations[0].publish(correction.meta);
116
123
  }
117
124
  }
118
125
  // Rich media/binary results have no line slices. Preserve their original
119
126
  // evidence, still under the producer's lease rather than the batch's.
120
127
  for (const task of tasks) {
121
- if (hasRenderedLines(task.body) || task.body?.isError === true ||
122
- (typeof task.body === 'string' && helpers.classifyResultKind(task.body) === 'error')) continue;
128
+ if (
129
+ hasRenderedLines(task.body) ||
130
+ task.body?.isError === true ||
131
+ (typeof task.body === 'string' && helpers.classifyResultKind(task.body) === 'error')
132
+ )
133
+ continue;
123
134
  for (const record of task.records) {
124
135
  if (!corrections.has(record.fullPath)) record.publish();
125
136
  }
@@ -69,7 +69,12 @@ async function rawRangeHashes(fullPath, mergedRanges, { _hashText }) {
69
69
 
70
70
  export async function recordSlicedBatchSnapshots(orderedResults, workDir, readStateScope, helpers) {
71
71
  const { _mergeReadRanges, _recordReadSnapshot } = helpers;
72
- const { exactRangesByPath, rangeHashesByPath } = deliveredRangesByPath(orderedResults, workDir, readStateScope, helpers);
72
+ const { exactRangesByPath, rangeHashesByPath } = deliveredRangesByPath(
73
+ orderedResults,
74
+ workDir,
75
+ readStateScope,
76
+ helpers
77
+ );
73
78
  for (const [fullPath, ranges] of exactRangesByPath) {
74
79
  const mergedRanges = _mergeReadRanges(ranges);
75
80
  let rangeHashes = rangeHashesByPath.get(fullPath) || [];
@@ -112,14 +112,7 @@ export async function executeReadTool(args, ...rest) {
112
112
  return `${out}\n[skipped ${skipped.length} more file(s) over the per-call limit: ${skipped.join(', ')} — read them in another call]`;
113
113
  }
114
114
 
115
- async function executeReadToolBody(
116
- args,
117
- workDir,
118
- readStateScope,
119
- executeChildBuiltinTool,
120
- options = {},
121
- helpers = {}
122
- ) {
115
+ async function executeReadToolBody(args, workDir, readStateScope, executeChildBuiltinTool, options = {}, helpers = {}) {
123
116
  const { normalizeInputPath, normaliseReadLineWindowArgs } = helpers;
124
117
  applyFilePathAlias(args);
125
118
  args.path = coerceReadFamilyPathArg(args.path);