mixdog 1.2.3 → 1.2.5

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 (469) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +6 -4
  3. package/scripts/lib/gate-scheduler.mjs +29 -11
  4. package/scripts/mobile-release.mjs +5 -4
  5. package/scripts/probe-provider-catalog.mjs +31 -0
  6. package/scripts/provider-pingpong-live.mjs +128 -38
  7. package/scripts/session-sweep.mjs +5 -25
  8. package/src/agents/maintainer/AGENT.md +1 -1
  9. package/src/agents/maintainer/agent.json +1 -1
  10. package/src/agents/reviewer/AGENT.md +2 -4
  11. package/src/agents/security/AGENT.md +1 -2
  12. package/src/agents/writer/AGENT.md +2 -3
  13. package/src/defaults/shipped-history.json +7 -1
  14. package/src/defaults/skills/image/SKILL.md +21 -23
  15. package/src/lib/keychain-cjs.cjs +13 -11
  16. package/src/output-styles/common.md +2 -1
  17. package/src/rules/agent/AGENT.md +7 -5
  18. package/src/rules/lead/LEAD.md +13 -13
  19. package/src/rules/shared/45-destructive.md +4 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  22. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  23. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  24. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  25. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  26. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  31. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  32. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  34. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  35. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  36. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  37. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  38. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  41. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  46. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  48. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  50. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  51. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  52. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  53. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  54. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  56. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  57. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  59. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  60. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  61. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  62. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  64. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  65. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  66. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  67. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  68. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  69. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  70. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  80. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  82. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  86. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  87. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  88. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  89. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  90. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  91. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  92. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  93. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  96. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  97. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  98. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  99. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  100. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  101. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  103. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  109. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  110. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  111. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  112. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  113. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  114. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  115. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  116. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  117. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  118. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  119. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  120. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  121. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  123. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  124. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  125. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  128. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  129. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  131. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  132. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  133. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  134. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  135. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  141. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  146. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  147. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  148. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  149. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  150. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  151. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  155. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  157. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  158. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  161. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  162. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  163. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  164. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  165. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  167. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  169. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  173. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  174. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  178. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  179. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  180. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  181. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +160 -212
  182. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  183. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  184. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  185. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  186. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  187. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  188. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  189. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  190. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  191. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  192. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  193. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  194. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  197. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  199. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  200. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  208. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  209. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  210. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  211. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  212. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  216. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  217. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  218. package/src/runtime/attachments/store.d.mts +9 -0
  219. package/src/runtime/attachments/store.mjs +80 -9
  220. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  221. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  222. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  223. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  224. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  225. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  226. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  227. package/src/runtime/github/tool.mjs +118 -17
  228. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  229. package/src/runtime/media/catalog.mjs +4 -4
  230. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  231. package/src/runtime/media/defaults.mjs +37 -0
  232. package/src/runtime/media/jobs.mjs +12 -2
  233. package/src/runtime/media/lanes.mjs +12 -0
  234. package/src/runtime/media/tool-defs.mjs +15 -4
  235. package/src/runtime/media/tool.mjs +41 -12
  236. package/src/runtime/media/upstream-error.mjs +72 -6
  237. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  238. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  240. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  242. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  243. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  244. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  245. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  246. package/src/runtime/office/index.mjs +2 -3
  247. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  248. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  249. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  250. package/src/runtime/shared/atomic-file.d.mts +1 -1
  251. package/src/runtime/shared/background-tasks.mjs +6 -1
  252. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  253. package/src/runtime/shared/browser-launch.mjs +15 -0
  254. package/src/runtime/shared/err-text.mjs +42 -4
  255. package/src/runtime/shared/file-lock.mjs +6 -5
  256. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  257. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  258. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  259. package/src/runtime/shared/model-route-chain.mjs +48 -0
  260. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  261. package/src/runtime/shared/pid-liveness.mjs +12 -0
  262. package/src/runtime/shared/provider-api-key.mjs +6 -0
  263. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  264. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  265. package/src/runtime/shared/quiet-progress.mjs +101 -0
  266. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  267. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  268. package/src/runtime/shared/remote-intent.mjs +2 -42
  269. package/src/runtime/shared/slow-span.mjs +8 -1
  270. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  271. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  272. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  273. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  274. package/src/runtime/shared/tool-status.mjs +3 -2
  275. package/src/runtime/shared/tool-surface.mjs +2 -3
  276. package/src/runtime/shared/tool-work-units.mjs +22 -15
  277. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  278. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  279. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  280. package/src/runtime/shared/transcript-writer.mjs +14 -4
  281. package/src/runtime/shared/turn-changes.mjs +2 -1
  282. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  283. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  284. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  285. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  286. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  287. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  288. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  289. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  290. package/src/runtime/shared/worktrees/include.mjs +125 -7
  291. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  292. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  293. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  294. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  295. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  296. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  297. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  298. package/src/runtime/terminal-bridge/client.mjs +14 -10
  299. package/src/session-runtime/boot/apis.mjs +7 -1
  300. package/src/session-runtime/boot/config.mjs +2 -0
  301. package/src/session-runtime/boot/facade.mjs +3 -0
  302. package/src/session-runtime/boot/session.mjs +3 -2
  303. package/src/session-runtime/boot/tools.mjs +3 -0
  304. package/src/session-runtime/conversation-import/service.mjs +1 -1
  305. package/src/session-runtime/goal-facade-api.mjs +2 -2
  306. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  307. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  308. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  309. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  310. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  311. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  312. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  313. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  314. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  315. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  316. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  317. package/src/session-runtime/media-api.mjs +8 -0
  318. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  319. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  320. package/src/session-runtime/model-route/set-route.mjs +114 -34
  321. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  322. package/src/session-runtime/model-route-api.mjs +4 -2
  323. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  324. package/src/session-runtime/native-web-search.mjs +9 -8
  325. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  326. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  327. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  328. package/src/session-runtime/provider-models.mjs +1 -0
  329. package/src/session-runtime/provider-usage.mjs +4 -0
  330. package/src/session-runtime/route-resolution.mjs +3 -1
  331. package/src/session-runtime/runtime-core.mjs +4 -0
  332. package/src/session-runtime/runtime-facade.mjs +6 -0
  333. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  334. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  335. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  336. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  337. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  338. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  339. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  340. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  341. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  343. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  344. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  345. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  346. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  347. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  348. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  349. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  350. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  351. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  352. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  353. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  354. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  355. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  356. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  357. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  358. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  359. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  360. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  361. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  362. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  363. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  364. package/src/session-runtime/services/agent-tool.mjs +4 -0
  365. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  366. package/src/session-runtime/services/provider-admin.mjs +77 -0
  367. package/src/session-runtime/session-create/session-options.mjs +7 -5
  368. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  369. package/src/session-runtime/session-transcript.mjs +4 -5
  370. package/src/session-runtime/session-turn-api.mjs +3 -9
  371. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  372. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  373. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  374. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  375. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  376. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  377. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  378. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  379. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  380. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  381. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  382. package/src/session-runtime/turn/turn-run.mjs +1 -36
  383. package/src/session-runtime/usage-stats-api.mjs +4 -1
  384. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  385. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  386. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  387. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  388. package/src/session-runtime/workspace/transitions.mjs +248 -68
  389. package/src/standalone/channel-transport.mjs +3 -4
  390. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  391. package/src/standalone/daemon-pg-stop.mjs +1 -3
  392. package/src/standalone/daemon-session-control.mjs +88 -0
  393. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  394. package/src/standalone/daemon.mjs +2 -0
  395. package/src/standalone/desktop-service-registry.mjs +32 -5
  396. package/src/standalone/local-session-runtime.mjs +11 -12
  397. package/src/standalone/session-protocol.mjs +7 -0
  398. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  399. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  400. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  401. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  402. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  403. package/src/standalone/session-service/agent-tree.mjs +28 -2
  404. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  405. package/src/standalone/session-service/entries.mjs +1 -0
  406. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  407. package/src/standalone/session-service/quota-resume.mjs +28 -13
  408. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  409. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  410. package/src/standalone/session-tool-output.mjs +82 -0
  411. package/src/tui/app/app-view.jsx +2 -1
  412. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  413. package/src/tui/app/provider-setup-picker.mjs +15 -3
  414. package/src/tui/app/slash-commands.mjs +6 -0
  415. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  416. package/src/tui/components/Spinner.jsx +4 -0
  417. package/src/tui/components/SpinnerByline.jsx +21 -2
  418. package/src/tui/components/status-line/local-line.mjs +10 -2
  419. package/src/tui/dist/index.mjs +529 -270
  420. package/src/tui/session/active-tool-summary.mjs +1 -1
  421. package/src/tui/session/context-state.mjs +0 -3
  422. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  423. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  424. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  425. package/src/tui/session/quota-resume.mjs +140 -61
  426. package/src/tui/session/session-api/commands.mjs +4 -10
  427. package/src/tui/session/session-api/controls.mjs +12 -0
  428. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  429. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  430. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  431. package/src/tui/session/session-api/media.mjs +2 -0
  432. package/src/tui/session/session-api/routes.mjs +81 -57
  433. package/src/tui/session/session-api/settings.mjs +3 -3
  434. package/src/tui/session/session-api/worktree.mjs +3 -2
  435. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  436. package/src/tui/session/session-flow/drain.mjs +6 -4
  437. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  438. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  439. package/src/tui/session/session-flow/queue.mjs +1 -1
  440. package/src/tui/session/tool-approval.mjs +17 -8
  441. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  442. package/src/tui/session/tui-steering-persist.mjs +3 -5
  443. package/src/tui/session/turn-quiet.mjs +53 -0
  444. package/src/tui/session/turn-stream.mjs +5 -1
  445. package/src/tui/session/turn.mjs +38 -31
  446. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  447. package/src/tui/spinner-meta.mjs +10 -0
  448. package/src/tui/spinner-verbs.mjs +3 -0
  449. package/src/ui/statusline.mjs +13 -3
  450. package/src/ui/streaming-markdown-heal.mjs +70 -18
  451. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  452. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  453. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  454. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  455. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  456. package/src/session-runtime/turn/share-ops.mjs +0 -77
  457. package/src/tui/session/cross-surface-share.mjs +0 -406
  458. package/src/tui/session/live-share/delta.mjs +0 -153
  459. package/src/tui/session/live-share/mirror.mjs +0 -106
  460. package/src/tui/session/live-share/owner.mjs +0 -254
  461. package/src/tui/session/live-share/retry.mjs +0 -54
  462. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  463. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  464. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  465. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  466. package/src/tui/session/live-share/viewer.mjs +0 -99
  467. package/src/tui/session/live-share/wire.mjs +0 -44
  468. package/src/tui/session/live-share.mjs +0 -154
  469. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -12,9 +12,16 @@
12
12
  // adapter expects — the caller (builtin.mjs default: case) falls back to
13
13
  // the existing EXTERNAL_TOOL_REDIRECTS guidance message in that case.
14
14
  import { readFileSync, mkdirSync, existsSync, lstatSync, realpathSync, statSync } from 'node:fs';
15
- import { dirname } from 'node:path';
15
+ import { dirname, relative, resolve } from 'node:path';
16
16
  import { atomicWrite, symlinkWriteTarget } from './atomic-write.mjs';
17
- import { fileVersion } from './file-version.mjs';
17
+ import { fileVersion, sameFileVersion } from './file-version.mjs';
18
+ import { withBuiltinPathLocks } from './path-locks.mjs';
19
+ import { withAdvisoryLocks } from './advisory-lock.mjs';
20
+ import { canonicalCachePath } from './read-range-index.mjs';
21
+ import { readSourceWindows } from './read-source-windows.mjs';
22
+ import { isReadLineClipped } from './read-formatting.mjs';
23
+ import { readSourceWindow } from '../../../../shared/read-row-numbers.mjs';
24
+ import { cursorNativeCall, releaseCursorNativeCall } from '../../providers/cursor-native-context.mjs';
18
25
  import { assertPathsReachable } from './fs-reachability.mjs';
19
26
  import {
20
27
  normalizeInputPath,
@@ -30,12 +37,12 @@ import {
30
37
  isBlockedDevicePath,
31
38
  isSpecialFileStat,
32
39
  } from './device-paths.mjs';
33
- import { recordReadSnapshot } from './read-snapshot-runtime.mjs';
40
+ import { recordReadSnapshot, getReadSnapshot } from './read-snapshot-runtime.mjs';
34
41
  import { executeBashTool } from './bash-tool.mjs';
35
42
  import { nativeEditSessionSatisfiesContract, runServerEdit } from '../patch/native-server.mjs';
36
43
  import { decodeValidUtf8OrNull, detectPatchTargetCodec, nearestPatchLineMatch } from '../patch/matcher.mjs';
37
44
  import { hashText } from './hash-utils.mjs';
38
- import { invalidateBuiltinResultCache } from './cache-layers.mjs';
45
+ import { invalidateBuiltinResultCache, rawContentCacheGet } from './cache-layers.mjs';
39
46
  import { markCodeGraphDirtyPaths } from '../code-graph-state.mjs';
40
47
  import { reviewFileContent } from '../../../../shared/bounded-file-read.mjs';
41
48
 
@@ -515,13 +522,14 @@ function readEditTarget(fullPath) {
515
522
  let statBefore = {};
516
523
  let content;
517
524
  let fileEnc;
525
+ let rawBuf;
518
526
  try {
519
527
  try {
520
528
  statBefore = { ...fileVersion(statSync(fullPath)) };
521
529
  } catch {
522
530
  /* readFileSync below surfaces the real error */
523
531
  }
524
- const rawBuf = readFileSync(fullPath);
532
+ rawBuf = readFileSync(fullPath);
525
533
  fileEnc = detectPatchTargetCodec(rawBuf);
526
534
  content = fileEnc.certain ? decodeEditBuffer(rawBuf, fileEnc) : null;
527
535
  } catch (err) {
@@ -536,7 +544,7 @@ function readEditTarget(fullPath) {
536
544
  ' Convert the encoding first.',
537
545
  };
538
546
  }
539
- return { content, fileEnc, statBefore };
547
+ return { content, fileEnc, statBefore, rawBuf };
540
548
  }
541
549
 
542
550
  /**
@@ -759,7 +767,7 @@ function resolveStrReplaceArgs(args) {
759
767
  return out;
760
768
  }
761
769
 
762
- async function adaptStrReplace(args, workDir, options) {
770
+ async function adaptStrReplace(args, workDir, options, resolvedTarget = null) {
763
771
  const resolved = resolveStrReplaceArgs(args);
764
772
  if (resolved.error) return resolved.error;
765
773
  let oldStr = resolved.old_string;
@@ -769,7 +777,16 @@ async function adaptStrReplace(args, workDir, options) {
769
777
  }
770
778
  if (oldStr === newStr) return SAME_STRINGS_ERROR;
771
779
  const replaceAll = args?.replace_all === true || String(args?.replace_all || '').toLowerCase() === 'true';
772
- const target = await resolveTargetPath(args, workDir);
780
+ if (!resolvedTarget) {
781
+ return withEditTargetLock(
782
+ args,
783
+ workDir,
784
+ options,
785
+ (target) => adaptStrReplace(args, workDir, options, target),
786
+ oldStr.length === 0
787
+ );
788
+ }
789
+ const target = resolvedTarget || (await resolveTargetPath(args, workDir));
773
790
  if (!target) return null;
774
791
  if (target.error) return target.error;
775
792
  const fullPath = target.full;
@@ -822,10 +839,13 @@ async function adaptStrReplace(args, workDir, options) {
822
839
  return applyEditNative(edit, oldStr, newStr, editPlan, replaceAll);
823
840
  }
824
841
 
825
- async function adaptWrite(args, workDir, options) {
842
+ async function adaptWrite(args, workDir, options, resolvedTarget = null) {
826
843
  const contents = args?.contents ?? args?.content ?? args?.file_text;
827
844
  if (typeof contents !== 'string') return null;
828
- const target = await resolveTargetPath(args, workDir);
845
+ if (!resolvedTarget) {
846
+ return withEditTargetLock(args, workDir, options, (target) => adaptWrite(args, workDir, options, target));
847
+ }
848
+ const target = resolvedTarget || (await resolveTargetPath(args, workDir));
829
849
  if (!target) return null;
830
850
  if (target.error) return target.error;
831
851
  const fullPath = target.full;
@@ -849,6 +869,180 @@ async function adaptBash(key, args, workDir, options) {
849
869
  return executeBashTool(shellArgs, workDir, options);
850
870
  }
851
871
 
872
+ async function withEditTargetLock(args, workDir, options, run, allowCreate = true) {
873
+ if (options?.signal?.aborted) throw options.signal.reason || new Error('Edit cancelled');
874
+ const target = await resolveTargetPath(args, workDir);
875
+ if (!target || target.error) return target?.error ?? null;
876
+ if (!allowCreate && !existsSync(target.full)) {
877
+ return `Error: cannot read ${target.full} (target is missing or inaccessible)`;
878
+ }
879
+ const resolveReal = () => {
880
+ const nearest = realpathNearestExisting(target.full);
881
+ return nearest ? resolve(nearest.real, relative(nearest.probe, target.full)) : null;
882
+ };
883
+ const real = resolveReal();
884
+ if (!real) return 'Error: cannot resolve edit target for locking';
885
+ return withBuiltinPathLocks([target.full, real], () =>
886
+ withAdvisoryLocks([real], async () => {
887
+ if (options?.signal?.aborted) throw options.signal.reason || new Error('Edit cancelled');
888
+ if (resolveReal() !== real) return 'Error: edit target changed while acquiring its lock';
889
+ return run(target);
890
+ })
891
+ );
892
+ }
893
+
894
+ const NATIVE_SNAPSHOT_MAX_BYTES = 1024 * 1024;
895
+
896
+ // Structured truncation report for a returned read window, taken from the raw
897
+ // source lines of that window with the read display's own clip predicate. A
898
+ // window that cannot be verified is reported clipped (fail closed).
899
+ async function nativeReadWindowReport(fullPath, source) {
900
+ if (source.startLine == null || source.endLine < source.startLine) return { lineClipped: false };
901
+ try {
902
+ const rows = await readSourceWindows(fullPath, [{ start: source.startLine, end: source.endLine }]);
903
+ return { lineClipped: [...rows.values()].some((row) => isReadLineClipped(row)) };
904
+ } catch {
905
+ return { lineClipped: true };
906
+ }
907
+ }
908
+
909
+ /** Clip report of an ordinary read result, for a Cursor read the daemon runs
910
+ * for a remote/proxied provider: `{ lineClipped: true }`, or null when the
911
+ * result is no source window or none of its lines were clipped. */
912
+ export async function remoteReadWindowReport(args, workDir, result) {
913
+ const source = readSourceWindow(typeof result === 'string' ? result : '');
914
+ if (!source) return null;
915
+ const target = await resolveTargetPath(args, workDir);
916
+ const report = !target || target.error ? { lineClipped: true } : await nativeReadWindowReport(target.full, source);
917
+ return report.lineClipped ? report : null;
918
+ }
919
+
920
+ async function captureNativeRead(context, args, workDir, options, result) {
921
+ const pending = context.pending;
922
+ delete pending.nativeSnapshot;
923
+ delete pending.nativeReadReply;
924
+ delete pending.nativeReadWindow;
925
+ const target = await resolveTargetPath(args, workDir);
926
+ if (!target || target.error) return;
927
+ let stat;
928
+ try {
929
+ stat = statSync(target.full);
930
+ } catch (error) {
931
+ if (error.code === 'ENOENT') pending.nativeReadReply = { fileNotFound: { path: pending.native.args.path } };
932
+ return;
933
+ }
934
+ const source = readSourceWindow(typeof result === 'string' ? result : '');
935
+ if (source) pending.nativeReadWindow = await nativeReadWindowReport(target.full, source);
936
+ if (
937
+ !source ||
938
+ source.truncated ||
939
+ source.startLine !== 1 ||
940
+ args.offset ||
941
+ args.limit ||
942
+ stat.size > NATIVE_SNAPSHOT_MAX_BYTES ||
943
+ !stat.isFile()
944
+ )
945
+ return;
946
+ const observed = getReadSnapshot(target.full, options?.readStateScope ?? options?.sessionId ?? null);
947
+ if (!observed?.bodyDelivered || !sameFileVersion(observed, stat)) return;
948
+ const cached = rawContentCacheGet(target.full, stat);
949
+ if (!cached) return;
950
+ const bytes = Buffer.from(cached);
951
+ const text = bytes.toString('utf8');
952
+ if (text.includes('\0') || !Buffer.from(text, 'utf8').equals(bytes)) return;
953
+ const displayed = text
954
+ .replace(/^\uFEFF/, '')
955
+ .replace(/\r\n/g, '\n')
956
+ .replace(/\n$/, '');
957
+ if (displayed !== source.content) return;
958
+ const realPath = canonicalCachePath(realpathSync(target.full));
959
+ if (!sameFileVersion(stat, statSync(target.full))) return;
960
+ pending.nativeSnapshot = {
961
+ bytes,
962
+ text,
963
+ version: fileVersion(stat),
964
+ realPath,
965
+ path: canonicalCachePath(target.full),
966
+ delivered: false,
967
+ };
968
+ pending.nativeReadReply = {
969
+ success: {
970
+ path: pending.native.args.path,
971
+ content: text,
972
+ totalLines: source.totalLines,
973
+ fileSize: bytes.length,
974
+ truncated: false,
975
+ rangeApplied: false,
976
+ },
977
+ };
978
+ }
979
+
980
+ async function replaceNativeSnapshot(context, args, workDir, options) {
981
+ return withEditTargetLock(args, workDir, options, async (target) => {
982
+ const parent = context.pending.native.args.toolCallId;
983
+ if (typeof parent !== 'string' || parent.length === 0) {
984
+ return adaptStrReplace(args, workDir, options, target);
985
+ }
986
+ const path = canonicalCachePath(target.full);
987
+ const reads = [...context.executions.values()].filter(
988
+ (entry) =>
989
+ entry.native?.resultType === 'readResult' &&
990
+ entry.native.args.toolCallId === parent &&
991
+ entry.nativeSnapshot?.path === path
992
+ );
993
+ // Creating a missing/empty file retains the normal create-only contract.
994
+ if (reads.length === 0) return adaptStrReplace(args, workDir, options, target);
995
+ if (reads.length !== 1 || !reads[0].nativeSnapshot.delivered) {
996
+ return 'Error: native edit has no unambiguous, delivered full-file snapshot; use MCP edit with explicit old_string';
997
+ }
998
+ const snapshot = reads[0].nativeSnapshot;
999
+ const read = readEditTarget(target.full);
1000
+ if (read.error) return read.error;
1001
+ if (
1002
+ canonicalCachePath(realpathSync(target.full)) !== snapshot.realPath ||
1003
+ !sameFileVersion(snapshot.version, read.statBefore) ||
1004
+ !read.rawBuf.equals(snapshot.bytes)
1005
+ ) {
1006
+ return 'Error: native edit target changed since its read; no replacement was written';
1007
+ }
1008
+ let after = args.new_string;
1009
+ if (typeof after !== 'string') return 'Error: native replacement requires text';
1010
+ if (read.fileEnc.bomLen && after.startsWith('\uFEFF')) after = after.slice(1);
1011
+ const lineEndings = new Set(read.content.match(/\r\n|\r|\n/g) || []);
1012
+ if (lineEndings.size > 1) {
1013
+ return 'Error: native full-file replacement cannot preserve mixed line endings; use MCP edit with explicit old_string';
1014
+ }
1015
+ if (lineEndings.size === 1) after = convertTextEol(after, lineEndings.values().next().value);
1016
+ const edit = { fullPath: target.full, workDir, options, ...read };
1017
+ const bytes = encodeEditContent(after, read.fileEnc);
1018
+ const stale = await commitEdit(edit, {
1019
+ bytes,
1020
+ after,
1021
+ staleMessage: STALE_DURING_EDIT,
1022
+ });
1023
+ if (!stale) context.pending.nativeWrittenBytes = bytes.length;
1024
+ return stale ?? `Updated ${editDisplayPath(edit)} (conditional native replacement)`;
1025
+ });
1026
+ }
1027
+
1028
+ // Called only after normal runtime permission checks. The capability is tied to
1029
+ // the streamed execution, not to a flag supplied in model arguments.
1030
+ export async function executeCursorNativeBuiltin(name, args, workDir, options, execute) {
1031
+ const context = cursorNativeCall(options.sessionId, options.toolCallId, name, args);
1032
+ if (!context) return execute(name, args, workDir, options);
1033
+ if (name === 'read') {
1034
+ const result = await execute(name, args, workDir, { ...options, suppressReadUnchangedStub: true });
1035
+ await captureNativeRead(context, args, workDir, options, result);
1036
+ return result;
1037
+ }
1038
+ releaseCursorNativeCall(context.pending);
1039
+ try {
1040
+ return await replaceNativeSnapshot(context, args, workDir, options);
1041
+ } catch (error) {
1042
+ return `Error: native edit failed (${error?.message || error})`;
1043
+ }
1044
+ }
1045
+
852
1046
  export async function tryExecuteExternalToolAdapter(name, args, workDir, options) {
853
1047
  if (typeof name !== 'string' || !name) return null;
854
1048
  if (!args || typeof args !== 'object' || Array.isArray(args)) return null;
@@ -74,7 +74,7 @@ export const GIT_TOOL_DEF = {
74
74
  compressible: true,
75
75
  },
76
76
  description:
77
- '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 selected locations.',
77
+ '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.',
78
78
  inputSchema: {
79
79
  type: 'object',
80
80
  properties: {
@@ -142,47 +142,149 @@ function outputLines(value) {
142
142
  return String(value || '').match(/[^\n]*\n|[^\n]+$/g) || [];
143
143
  }
144
144
 
145
- function capOutput(text, limit) {
145
+ // A line cap alone lets a few very long lines (minified files, lockfiles)
146
+ // through, so each command also gets a character budget, and a command array
147
+ // shares one. A cut always states what was omitted and how to page.
148
+ const COMMAND_CHAR_BUDGET = 12_000;
149
+ const ARRAY_CHAR_BUDGET = 24_000;
150
+ const MIN_COMMAND_CHARS = 1_000;
151
+ const LARGE_DIFF_LINES = 200;
152
+ const LARGE_DIFF_HINT =
153
+ 'hint: large diff - run with --stat first for per-file sizes, add -U1 for less context, and page by file with -- <path>';
154
+
155
+ // Line-ending warnings are noise the model cannot act on; the content is unchanged.
156
+ const CRLF_WARNING_RE = /^warning: in the working copy of .* (?:CRLF|LF) will be replaced[^\n]*(?:\n|$)/gm;
157
+ // `index abc..def` carries abbreviated object ids the model never uses; mode
158
+ // changes keep their own `old mode`/`new mode`/`new file mode` lines.
159
+ const DIFF_INDEX_LINE_RE = /^index [0-9a-f,]+\.\.[0-9a-f]+(?: [0-7]+)?\r?(?:\n|$)/gm;
160
+ const PATCH_OPERATIONS = new Set(['diff', 'diff-files', 'diff-index', 'diff-tree', 'show', 'log']);
161
+
162
+ function visibleStderr(value) {
163
+ return String(value || '').replace(CRLF_WARNING_RE, '');
164
+ }
165
+
166
+ function presentStdout(plan, value) {
167
+ const stdout = String(value || '');
168
+ if (plan.operation === 'merge-tree') return stdout.replace(/^Auto-merging .*(?:\r?\n|$)/gm, '');
169
+ if (PATCH_OPERATIONS.has(plan.operation) && /^diff --git /m.test(stdout)) {
170
+ return stdout.replace(DIFF_INDEX_LINE_RE, '');
171
+ }
172
+ return stdout;
173
+ }
174
+
175
+ function normalizeCap(limit) {
176
+ return typeof limit === 'object' ? limit : { lines: limit, chars: COMMAND_CHAR_BUDGET };
177
+ }
178
+
179
+ function capOutput(text, cap) {
146
180
  const lines = outputLines(text);
147
- if (lines.length <= limit) return text;
148
- return appendText(
149
- lines.slice(0, limit).join(''),
150
- `... [${lines.length - limit} more lines omitted; raise output_limit or narrow the command]`
151
- );
181
+ const lineCut = lines.length > cap.lines;
182
+ const kept = lineCut ? lines.slice(0, cap.lines).join('') : text;
183
+ if (kept.length <= cap.chars) {
184
+ return {
185
+ text: lineCut
186
+ ? appendText(
187
+ kept,
188
+ `... [${lines.length - cap.lines} more lines omitted; raise output_limit or narrow the command]`
189
+ )
190
+ : text,
191
+ truncated: lineCut,
192
+ totalLines: lines.length,
193
+ };
194
+ }
195
+ const newline = kept.lastIndexOf('\n', cap.chars - 1);
196
+ const cut = newline > 0 ? newline + 1 : cap.chars;
197
+ const shown = outputLines(kept.slice(0, cut)).length;
198
+ return {
199
+ text: appendText(
200
+ kept.slice(0, cut),
201
+ `... [${lines.length - shown} more lines (${text.length - cut} chars) omitted; output limited to ${cap.chars} chars - narrow the command (-- <path>, -n/--skip) to page]`
202
+ ),
203
+ truncated: true,
204
+ totalLines: lines.length,
205
+ };
152
206
  }
153
207
 
154
208
  function commandResult(plan, result, limit) {
155
- const output = capOutput(appendText(String(result.stdout || ''), String(result.stderr || '')), limit);
209
+ const stdout = presentStdout(plan, result.stdout);
210
+ const capped = capOutput(appendText(stdout, visibleStderr(result.stderr)), normalizeCap(limit));
211
+ let output = capped.text;
212
+ if ((capped.truncated || capped.totalLines > LARGE_DIFF_LINES) && /^diff --git /m.test(stdout)) {
213
+ output = appendText(output, LARGE_DIFF_HINT);
214
+ }
156
215
  if (succeeded(result)) return { text: output, failed: false };
157
216
  const processError = result.error || result.timedOut || result.aborted || result.overflow || result.exitCode == null;
158
- // An exit signal is still a non-zero exit, so a command array stops on it;
159
- // the outcome line only keeps the card from reporting a failure.
160
- const exitHeader = exitSignal(plan, result)
161
- ? `exit ${result.exitCode}\n[outcome: no-match]`
162
- : `exit ${result.exitCode}`;
217
+ if (processError) return { text: appendText(fail(gitFailureReason(plan, result)), output), failed: true };
218
+ const outcome = exitSignal(plan, result);
219
+ if (outcome === 'conflicts') {
220
+ // A conflicted merge-tree is its answer, not a failed command: the array continues.
221
+ return {
222
+ text: appendText(
223
+ `exit ${result.exitCode}\n[outcome: conflicts]\nmerge has conflicts (see the CONFLICT lines); this is a result, not a command failure`,
224
+ output
225
+ ),
226
+ failed: false,
227
+ };
228
+ }
229
+ // A no-match signal is still a non-zero exit, so a command array stops on
230
+ // it; the outcome line only keeps the card from reporting a failure.
163
231
  return {
164
- text: appendText(processError ? fail(gitFailureReason(plan, result)) : exitHeader, output),
232
+ text: appendText(outcome ? `exit ${result.exitCode}\n[outcome: ${outcome}]` : `exit ${result.exitCode}`, output),
165
233
  failed: true,
166
234
  };
167
235
  }
168
236
 
237
+ // Paths come from git unquoted; only a name that would break the one-line
238
+ // listing (control characters, quotes) is JSON-quoted.
239
+ function listedPath(path) {
240
+ return [...path].some((char) => char.charCodeAt(0) < 0x20 || char === '"') ? JSON.stringify(path) : path;
241
+ }
242
+
169
243
  function stageableDiffResult(plan, result, snapshot, limit) {
170
244
  if (!snapshot.diffId) return commandResult(plan, result, limit);
171
245
  const rendered = commandResult(plan, result, limit);
172
- rendered.text = appendText(rendered.text, `diff_id: ${snapshot.diffId}`);
173
- let previousPath;
246
+ // The hunk headers are already in the body; one line per file names the id range.
247
+ const files = [];
174
248
  for (const change of snapshot.changes) {
175
- if (change.path !== previousPath) {
176
- rendered.text = appendText(rendered.text, `file: ${JSON.stringify(change.path)}`);
177
- previousPath = change.path;
178
- }
179
- const location =
180
- change.kind || `@@ -${change.old_start},${change.deletions} +${change.new_start},${change.additions} @@`;
181
- rendered.text = appendText(rendered.text, `change:${change.id} ${location}`);
249
+ const last = files.at(-1);
250
+ if (last?.path === change.path && !change.kind && !last.kind) last.end = change.id;
251
+ else files.push({ path: change.path, start: change.id, end: change.id, kind: change.kind });
182
252
  }
253
+ const listing = files.map(
254
+ ({ path, start, end, kind }) =>
255
+ `${listedPath(path)}: ${start === end ? start : `${start}-${end}`}${kind ? ' [whole file]' : ''}`
256
+ );
257
+ rendered.text = appendText(
258
+ rendered.text,
259
+ [
260
+ `diff_id: ${snapshot.diffId} (ids number the +/- runs in diff order; [whole file] ids stage the entire file)`,
261
+ ...listing,
262
+ ].join('\n')
263
+ );
183
264
  return rendered;
184
265
  }
185
266
 
267
+ // Per-file staged counts; the paths and ranges the model already holds are not repeated per change.
268
+ function stagedSummary(changes) {
269
+ const files = new Map();
270
+ for (const { path, additions = 0, deletions = 0 } of changes) {
271
+ const entry = files.get(path) || { count: 0, additions: 0, deletions: 0 };
272
+ entry.count += 1;
273
+ entry.additions += additions;
274
+ entry.deletions += deletions;
275
+ files.set(path, entry);
276
+ }
277
+ return {
278
+ changes: changes.length,
279
+ additions: [...files.values()].reduce((sum, entry) => sum + entry.additions, 0),
280
+ deletions: [...files.values()].reduce((sum, entry) => sum + entry.deletions, 0),
281
+ files: [...files].map(
282
+ ([path, entry]) =>
283
+ `${path}: ${entry.count} ${entry.count === 1 ? 'change' : 'changes'} +${entry.additions} -${entry.deletions}`
284
+ ),
285
+ };
286
+ }
287
+
186
288
  // A provider sometimes delivers the whole command as one quoted scalar
187
289
  // (`"git diff"`). That tokenizes to a single argument, so the git prefix check
188
290
  // rejected it and the caller paid a failed call plus a retry for a command it
@@ -351,14 +453,16 @@ function succeeded(result) {
351
453
 
352
454
  // `git grep` exits 1 for no match and `git diff --exit-code/--quiet/--check`
353
455
  // exits 1 to report a difference; the shell tool marks the same exits as
354
- // signals, so the git tool does too.
456
+ // signals, so the git tool does too. `git merge-tree` exits 1 with its report
457
+ // on stdout when the merge has conflicts. Returns the outcome name or null.
355
458
  function exitSignal(plan, result) {
356
- if (result.exitCode !== 1 || String(result.stderr || '').trim()) return false;
357
- if (plan.operation === 'grep') return true;
358
- if (plan.operation !== 'diff') return false;
459
+ if (result.exitCode !== 1 || visibleStderr(result.stderr).trim()) return null;
460
+ if (plan.operation === 'grep') return 'no-match';
461
+ if (plan.operation === 'merge-tree') return String(result.stdout || '').trim() ? 'conflicts' : null;
462
+ if (plan.operation !== 'diff') return null;
359
463
  const separator = plan.args.indexOf('--');
360
464
  const options = separator === -1 ? plan.args : plan.args.slice(0, separator);
361
- return options.some((arg) => GIT_DIFF_EXIT_SIGNAL_FLAGS.has(arg));
465
+ return options.some((arg) => GIT_DIFF_EXIT_SIGNAL_FLAGS.has(arg)) ? 'no-match' : null;
362
466
  }
363
467
 
364
468
  // ENOENT from the spawn itself is a capability fact, not a git error: the
@@ -690,10 +794,7 @@ async function executeGitStage(input, workDir, options = {}) {
690
794
  if (!succeeded(applied)) return commandFailure(applyPlan, applied, limit);
691
795
  deleteDiffSnapshot(request.diffId);
692
796
  invalidateAfterGitMutation();
693
- return ok({
694
- staged: true,
695
- changes: built.changes.map(({ id, preview, ...change }) => change),
696
- });
797
+ return ok({ staged: true, ...stagedSummary(built.changes) });
697
798
  });
698
799
  }),
699
800
  { signal }
@@ -703,7 +804,13 @@ async function executeGitStage(input, workDir, options = {}) {
703
804
  async function executeSingleGitTool(input, workDir, options = {}) {
704
805
  if (!input || typeof input !== 'object' || Array.isArray(input)) return rejected('git requires an arguments object');
705
806
  const hasCommand = typeof input.command === 'string' && Boolean(input.command.trim());
706
- if (!hasCommand) return rejected('git requires command');
807
+ if (!hasCommand) {
808
+ return rejected(
809
+ input.include_stage_ids === true
810
+ ? 'git requires command; staging IDs come from command:"git diff" (or "git diff -- <paths>") with include_stage_ids:true'
811
+ : 'git requires command: a full command starting with git, e.g. command:"git status"'
812
+ );
813
+ }
707
814
  let plan;
708
815
  try {
709
816
  plan = localizeConfigPlan(parseCommand(input.command, workDir));
@@ -717,7 +824,10 @@ async function executeSingleGitTool(input, workDir, options = {}) {
717
824
  return rejected('git archive requires -o/--output; binary stdout is not returned');
718
825
  }
719
826
  const limitDefault = plan.operation === 'log' ? 10 : 50;
720
- const limit = outputLimit(input.output_limit, limitDefault);
827
+ const limit = {
828
+ lines: outputLimit(input.output_limit, limitDefault),
829
+ chars: options.charBudget ?? COMMAND_CHAR_BUDGET,
830
+ };
721
831
  const signal = abortSignalOf(options);
722
832
  if (plan.operation === 'init' || plan.operation === 'clone') {
723
833
  const target = creationTarget(plan);
@@ -870,13 +980,14 @@ export async function executeGitTool(rawInput, workDir, options = {}) {
870
980
  return fail(`git command array requires 1 to ${GIT_COMMAND_ARRAY_LIMIT} commands`);
871
981
  }
872
982
 
983
+ const operations = [];
873
984
  for (let index = 0; index < commands.length; index += 1) {
874
985
  const command = commands[index];
875
986
  if (typeof command !== 'string' || !command.trim()) {
876
987
  return fail(`git command ${index + 1} must be a non-empty string`);
877
988
  }
878
989
  try {
879
- localizeConfigPlan(parseCommand(command, workDir));
990
+ operations.push(localizeConfigPlan(parseCommand(command, workDir)).operation);
880
991
  } catch (error) {
881
992
  return fail(`git command ${index + 1}: ${error.message}`);
882
993
  }
@@ -887,12 +998,22 @@ export async function executeGitTool(rawInput, workDir, options = {}) {
887
998
  // not produce. Every command is parsed above before any runs, so a typo in
888
999
  // command 3 never leaves commands 1-2 half applied. Each command still
889
1000
  // acquires its own repository lock inside executeSingleGitTool.
1001
+ // Headers name the position and subcommand only: echoing the argument list
1002
+ // (a 22-path `add`) cost more than the result it labelled.
890
1003
  const results = [];
891
- for (const command of commands) {
892
- const result = await executeSingleGitTool({ ...input, command }, workDir, options);
893
- results.push(`## ${command}\n${result.text}`);
1004
+ let used = 0;
1005
+ for (let index = 0; index < commands.length; index += 1) {
1006
+ const label = `${index + 1}: git ${operations[index]}`;
1007
+ const charBudget = Math.min(COMMAND_CHAR_BUDGET, Math.max(MIN_COMMAND_CHARS, ARRAY_CHAR_BUDGET - used));
1008
+ const result = await executeSingleGitTool({ ...input, command: commands[index] }, workDir, {
1009
+ ...options,
1010
+ charBudget,
1011
+ });
1012
+ const section = `## ${label}\n${result.text}`;
1013
+ used += section.length;
1014
+ results.push(section);
894
1015
  if (result.failed) {
895
- return appendText(results.join('\n'), `error: command failed: ${command}`);
1016
+ return appendText(results.join('\n'), `error: command failed: ${label}`);
896
1017
  }
897
1018
  }
898
1019
  return results.join('\n');
@@ -104,14 +104,19 @@ function parseFiles(raw) {
104
104
  return files;
105
105
  }
106
106
 
107
+ // Change ids are ordinals (c1, c2, …) in diff order. They are only meaningful
108
+ // together with the diff_id they were listed under: staging first verifies the
109
+ // working diff still hashes to that snapshot, so an ordinal can never address a
110
+ // different change than the one the model saw.
107
111
  function annotateChanges(files) {
108
112
  const changes = [];
113
+ let ordinal = 0;
109
114
  for (const file of files) {
110
115
  if (!file.stageable) continue;
111
116
  if (file.kind) {
112
117
  const entries = file.hunks.flatMap((hunk) => hunk.entries);
113
118
  changes.push({
114
- id: `chg_${digest(file.lines.join('\n')).slice(0, 16)}`,
119
+ id: `c${++ordinal}`,
115
120
  path: file.path,
116
121
  kind: file.kind,
117
122
  additions: entries.filter(({ line }) => line.startsWith('+')).length,
@@ -126,9 +131,7 @@ function annotateChanges(files) {
126
131
  let group = null;
127
132
  const finishGroup = () => {
128
133
  if (!group) return;
129
- const id = `chg_${digest(
130
- [file.path, hunkIndex, group.oldStart, group.newStart, group.lines.join('\n')].join('\0')
131
- ).slice(0, 16)}`;
134
+ const id = `c${++ordinal}`;
132
135
  for (const entry of group.entries) entry.changeId = id;
133
136
  changes.push({
134
137
  id,
@@ -137,7 +140,6 @@ function annotateChanges(files) {
137
140
  new_start: group.newStart,
138
141
  additions: group.additions,
139
142
  deletions: group.deletions,
140
- preview: group.lines.slice(0, 4).map((line) => (line.length <= 180 ? line : `${line.slice(0, 180)}…`)),
141
143
  });
142
144
  group = null;
143
145
  };
@@ -9,7 +9,7 @@ export function splitGrepLineNumberOnlyPrefix(line) {
9
9
  };
10
10
  }
11
11
 
12
- export function splitGrepLinePrefix(line) {
12
+ export function splitGrepLinePrefix(line, matchOnly = false) {
13
13
  const text = String(line || '');
14
14
  if (!text) return null;
15
15
  const searchFrom = /^[A-Za-z]:/.test(text) ? 2 : 0;
@@ -19,7 +19,9 @@ export function splitGrepLinePrefix(line) {
19
19
  // Caller context decides whether line-number-only output is possible. A
20
20
  // relative path may itself start with digits + '-' (e.g. `12-x\path.txt`),
21
21
  // so this parser must not reject `^\d+[:\-]` by shape alone.
22
- const delim = text.slice(searchFrom).match(/([:-])(\d+)\1/);
22
+ // `matchOnly`: the caller ran rg without context lines, so only `:N:` is a
23
+ // delimiter and a `-N-` segment belongs to the filename (`config-12-a.txt`).
24
+ const delim = text.slice(searchFrom).match(matchOnly ? /(:)(\d+):/ : /([:-])(\d+)\1/);
23
25
  if (!delim) return null;
24
26
  const pathEnd = searchFrom + delim.index;
25
27
  if (pathEnd <= 0) return null;
@@ -68,7 +70,13 @@ export function parseGrepContextHeader(line) {
68
70
  };
69
71
  }
70
72
 
71
- export function normalizeGrepLine(line, pathOnly = false, outputMode = 'content', filenameOmitted = false) {
73
+ export function normalizeGrepLine(
74
+ line,
75
+ pathOnly = false,
76
+ outputMode = 'content',
77
+ filenameOmitted = false,
78
+ matchOnly = false
79
+ ) {
72
80
  if (process.platform !== 'win32') return line;
73
81
  // files_with_matches mode: the WHOLE line is a path, so every separator is
74
82
  // a path separator and a full `\`->`/` normalisation is correct.
@@ -79,7 +87,7 @@ export function normalizeGrepLine(line, pathOnly = false, outputMode = 'content'
79
87
  // There is no path prefix to normalise; everything after ^\d+[:\-] is
80
88
  // source content and must remain byte-exact.
81
89
  if (filenameOmitted && splitGrepLineNumberOnlyPrefix(line)) return line;
82
- const split = splitGrepLinePrefix(line);
90
+ const split = splitGrepLinePrefix(line, matchOnly);
83
91
  if (split) {
84
92
  return line.slice(0, split.pathEnd).replace(/\\/g, '/') + line.slice(split.pathEnd);
85
93
  }
@@ -99,7 +107,8 @@ export function normalizeGrepLine(line, pathOnly = false, outputMode = 'content'
99
107
  function parseGrepContentLine(line) {
100
108
  const text = String(line || '');
101
109
  if (!text || text === '--' || text.startsWith('... [')) return null;
102
- const split = splitGrepLinePrefix(text);
110
+ // Grouping only runs on context-free content output.
111
+ const split = splitGrepLinePrefix(text, true);
103
112
  if (split?.delimiter !== ':') return null;
104
113
  const content = text.slice(split.markerEnd);
105
114
  return { path: split.path, lineNo: split.lineNo, content };