mixdog 0.9.2 → 0.9.4

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 (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,643 @@
1
+ // V4A hunk locator, in-memory apply, rename sections, and V4A -> unified
2
+ // conversion. Moved verbatim from patch.mjs; anchor/context matching, EOF
3
+ // handling, rename atomicity, and conversion output are all unchanged.
4
+
5
+ import { readFileSync, realpathSync, statSync, mkdirSync } from 'node:fs';
6
+ import { unlink } from 'node:fs/promises';
7
+ import { resolve as pathResolve, relative as pathRelative, isAbsolute, dirname as pathDirname } from 'node:path';
8
+ import {
9
+ normalizeInputPath,
10
+ normalizeOutputPath,
11
+ invalidateBuiltinResultCache,
12
+ clearReadSnapshotForPath,
13
+ } from '../builtin.mjs';
14
+ import {
15
+ rawContentCacheGet,
16
+ rawContentCacheSet,
17
+ } from '../builtin/cache-layers.mjs';
18
+ import { atomicWrite } from '../builtin/atomic-write.mjs';
19
+ import { assertPathReachable, assertPathsReachable } from '../builtin/fs-reachability.mjs';
20
+ import { markCodeGraphDirtyPaths } from '../code-graph-state.mjs';
21
+ import { resolveV4AEntryPath, realpathNearestExistingAncestor } from './paths.mjs';
22
+ import {
23
+ isV4AEndOfFileMarker,
24
+ parseUnifiedBareV4APatch,
25
+ parseUnifiedCountedAsV4APatch,
26
+ } from './parsing.mjs';
27
+ import {
28
+ findLineSequence,
29
+ findLineSequenceEscapeEquiv,
30
+ splitTextLinesForPatch,
31
+ firstMeaningfulPatchLine,
32
+ nearestPatchLineHint,
33
+ compactPatchPreviewLine,
34
+ decodeValidUtf8OrNull,
35
+ } from './matcher.mjs';
36
+
37
+ function v4AHunkLineStats(hunk) {
38
+ let oldCount = 0;
39
+ let newCount = 0;
40
+ const oldLines = [];
41
+ const newLines = [];
42
+ for (const raw of hunk.lines || []) {
43
+ if (!raw) continue;
44
+ const tag = raw[0];
45
+ const body = raw.slice(1);
46
+ if (tag === ' ') {
47
+ oldCount++;
48
+ newCount++;
49
+ oldLines.push(body);
50
+ newLines.push(body);
51
+ } else if (tag === '-') {
52
+ oldCount++;
53
+ oldLines.push(body);
54
+ } else if (tag === '+') {
55
+ newCount++;
56
+ newLines.push(body);
57
+ }
58
+ }
59
+ return { oldCount, newCount, oldLines, newLines };
60
+ }
61
+
62
+ function findAnchorLine(lines, anchors, fromLine) {
63
+ let cursor = Math.max(0, fromLine || 0);
64
+ for (const anchorRaw of anchors || []) {
65
+ const anchor = String(anchorRaw || '').trim();
66
+ if (!anchor) continue;
67
+ const found = lines.findIndex((line, idx) => idx >= cursor && line.includes(anchor));
68
+ if (found === -1) return -1;
69
+ cursor = found + 1;
70
+ }
71
+ return cursor;
72
+ }
73
+
74
+ function formatV4AHunkLocator(hunk) {
75
+ return (hunk.anchors || []).filter(Boolean).join(' > ') || '(no anchor)';
76
+ }
77
+
78
+ function formatV4AAnchorMissHint(sourceLines, hunk) {
79
+ const anchors = (hunk?.anchors || []).filter(Boolean);
80
+ const nearest = anchors.length > 0
81
+ ? anchors.map((anchor) => nearestPatchLineHint(sourceLines, anchor, 0)).find(Boolean)
82
+ : null;
83
+ return anchors.length === 0
84
+ ? ' use an existing @@ anchor from the current file or add exact context lines.'
85
+ : ` use an existing @@ anchor from the current file or add exact context lines; no stubs.${nearest ? ` nearest anchor candidate: ${nearest}.` : ''}`;
86
+ }
87
+
88
+ function formatV4AContextMissHint(sourceLines, stats, anchorLine) {
89
+ const expected = firstMeaningfulPatchLine(stats.oldLines);
90
+ const parts = [];
91
+ if (expected) {
92
+ const nearest = nearestPatchLineHint(sourceLines, expected, anchorLine);
93
+ parts.push(`expected first old line: ${JSON.stringify(compactPatchPreviewLine(expected))}`);
94
+ if (nearest) parts.push(nearest);
95
+ const divergence = firstV4ADivergenceHint(sourceLines, stats.oldLines, anchorLine);
96
+ if (divergence) parts.push(divergence);
97
+ }
98
+ parts.push('use exact current context or a broader @@ anchor; no stubs.');
99
+ return ` ${parts.join('; ')}`;
100
+ }
101
+
102
+ // When the FIRST old line does exist verbatim in the source, the real
103
+ // mismatch is some later line of the block — name it, with both sides
104
+ // JSON-escaped so invisible differences (real char vs literal \uXXXX
105
+ // escape, tabs, trailing spaces) become visible in the error.
106
+ function firstV4ADivergenceHint(sourceLines, oldLines, anchorLine) {
107
+ const lines = oldLines || [];
108
+ const firstIdx = lines.findIndex((l) => String(l ?? '').trim().length > 0);
109
+ if (firstIdx < 0) return '';
110
+ const first = lines[firstIdx];
111
+ const starts = [];
112
+ for (let i = 0; i < sourceLines.length; i++) {
113
+ if (sourceLines[i] === first) starts.push(i - firstIdx);
114
+ }
115
+ const pref = Number.isFinite(anchorLine) && anchorLine >= 0 ? anchorLine : 0;
116
+ const start = starts.filter((s) => s >= 0)
117
+ .sort((a, b) => Math.abs(a - pref) - Math.abs(b - pref) || a - b)[0];
118
+ if (start === undefined) return '';
119
+ for (let k = 0; k < lines.length; k++) {
120
+ const exp = lines[k];
121
+ const act = sourceLines[start + k];
122
+ if (act !== exp) {
123
+ const actText = act === undefined ? '(past EOF)' : JSON.stringify(compactPatchPreviewLine(act));
124
+ return `first divergent line: old[${k + 1}] expected ${JSON.stringify(compactPatchPreviewLine(exp))} vs file line ${start + k + 1} actual ${actText}`;
125
+ }
126
+ }
127
+ return '';
128
+ }
129
+
130
+ function joinTextLinesForPatch(lines) {
131
+ const body = (lines || []).join('\n');
132
+ return lines?.hasFinalNewline !== false ? `${body}\n` : body;
133
+ }
134
+
135
+ function cloneTextLinesForPatch(sourceLines) {
136
+ const lines = [...(sourceLines || [])];
137
+ lines.hasFinalNewline = sourceLines?.hasFinalNewline !== false;
138
+ return lines;
139
+ }
140
+
141
+ function resolveV4AHunkPosition(sourceLines, hunk, nextSearchLine, options = {}) {
142
+ const stats = v4AHunkLineStats(hunk);
143
+ if (stats.oldCount === 0 && stats.newCount === 0) return { skip: true };
144
+ const fuzzy = options.fuzzy !== false;
145
+ const eof = hunk?.isEndOfFile === true;
146
+ const anchorLine = findAnchorLine(sourceLines, hunk.anchors, nextSearchLine);
147
+ if (anchorLine < 0) {
148
+ const msg = `V4A hunk anchor not found: ${formatV4AHunkLocator(hunk)};${formatV4AAnchorMissHint(sourceLines, hunk)}`;
149
+ return { error: msg };
150
+ }
151
+ let oldLinesPattern = stats.oldLines;
152
+ let newLinesPattern = stats.newLines;
153
+ let oldStartIdx;
154
+ let trimmedTrailing = 0;
155
+ let trimmedTrailingNew = 0;
156
+ if (stats.oldCount === 0) {
157
+ oldStartIdx = eof ? sourceLines.length : anchorLine;
158
+ } else {
159
+ const searchFrom = Math.max(0, anchorLine - 1);
160
+ oldStartIdx = findLineSequence(
161
+ sourceLines,
162
+ oldLinesPattern,
163
+ searchFrom,
164
+ searchFrom,
165
+ { fuzzy, eof },
166
+ );
167
+ if (eof && oldStartIdx < 0 && oldLinesPattern.length > 0 && oldLinesPattern[oldLinesPattern.length - 1] === '') {
168
+ oldLinesPattern = oldLinesPattern.slice(0, -1);
169
+ trimmedTrailing = 1;
170
+ if (newLinesPattern.length > 0 && newLinesPattern[newLinesPattern.length - 1] === '') {
171
+ newLinesPattern = newLinesPattern.slice(0, -1);
172
+ trimmedTrailingNew = 1;
173
+ }
174
+ oldStartIdx = findLineSequence(
175
+ sourceLines,
176
+ oldLinesPattern,
177
+ searchFrom,
178
+ searchFrom,
179
+ { fuzzy, eof },
180
+ );
181
+ }
182
+ }
183
+ // Escape-equivalence fallback (fuzzy, non-EOF only): accept a window where each old
184
+ // line matches the source verbatim OR as the file's literal `\uXXXX` escape
185
+ // of the patch's real character. On match, remap old/context lines to the
186
+ // file's on-disk form so untouched context stays byte-identical and the
187
+ // escape representation survives the edit.
188
+ if (oldStartIdx < 0 && fuzzy && !eof && oldLinesPattern.length > 0) {
189
+ const from = Math.max(0, anchorLine - 1);
190
+ const alt = findLineSequenceEscapeEquiv(sourceLines, oldLinesPattern, from, from);
191
+ if (alt >= 0) {
192
+ const remapped = new Map();
193
+ let ambiguous = false;
194
+ for (let k = 0; k < oldLinesPattern.length; k++) {
195
+ const pat = oldLinesPattern[k];
196
+ const src = sourceLines[alt + k];
197
+ if (remapped.has(pat) && remapped.get(pat) !== src) { ambiguous = true; break; }
198
+ remapped.set(pat, src);
199
+ }
200
+ if (!ambiguous) {
201
+ newLinesPattern = newLinesPattern.map((l) => remapped.get(l) ?? l);
202
+ oldLinesPattern = oldLinesPattern.map((_, k) => sourceLines[alt + k]);
203
+ oldStartIdx = alt;
204
+ }
205
+ }
206
+ }
207
+ if (oldStartIdx < 0) {
208
+ const msg = `V4A hunk context not found: ${formatV4AHunkLocator(hunk)};${formatV4AContextMissHint(sourceLines, stats, anchorLine)} Copy context lines verbatim from the latest read output — do not retype them from memory.`;
209
+ return { error: msg };
210
+ }
211
+ const matchLen = stats.oldCount === 0 ? 0 : oldLinesPattern.length;
212
+ return {
213
+ oldStartIdx,
214
+ matchLen,
215
+ newLines: newLinesPattern,
216
+ nextSearchLine: oldStartIdx + Math.max(1, matchLen),
217
+ trimmedTrailing,
218
+ trimmedTrailingNew,
219
+ };
220
+ }
221
+
222
+ function applyV4AHunksToLines(sourceLines, hunks, options = {}) {
223
+ const lines = cloneTextLinesForPatch(sourceLines);
224
+ const orderedHunks = orderV4AHunksByFilePosition(lines, hunks, options.fuzzy !== false);
225
+ let nextSearchLine = 0;
226
+ const replacements = [];
227
+ for (const hunk of orderedHunks) {
228
+ const loc = resolveV4AHunkPosition(lines, hunk, nextSearchLine, options);
229
+ if (loc.skip) continue;
230
+ if (loc.error) throw new Error(loc.error);
231
+ replacements.push({
232
+ oldStartIdx: loc.oldStartIdx,
233
+ oldLen: loc.matchLen,
234
+ newLines: loc.newLines,
235
+ });
236
+ nextSearchLine = loc.nextSearchLine;
237
+ }
238
+ for (const rep of replacements.reverse()) {
239
+ lines.splice(rep.oldStartIdx, rep.oldLen, ...rep.newLines);
240
+ }
241
+ return lines;
242
+ }
243
+
244
+ // Order-independent hunk ordering for the V4A apply / V4A->unified conversion.
245
+ // Two-phase, semantics-preserving; see the original patch.mjs commentary.
246
+ function orderV4AHunksByFilePosition(sourceLines, hunks, fuzzy) {
247
+ const list = hunks || [];
248
+ if (list.length <= 1) return list;
249
+ let nextSearchLine = 0;
250
+ let inputOrderValid = true;
251
+ for (const hunk of list) {
252
+ const stats = v4AHunkLineStats(hunk);
253
+ if (stats.oldCount === 0 && stats.newCount === 0) continue;
254
+ let loc;
255
+ try { loc = resolveV4AHunkPosition(sourceLines, hunk, nextSearchLine, { fuzzy }); }
256
+ catch { loc = { error: true }; }
257
+ if (!loc || loc.error || loc.skip || typeof loc.nextSearchLine !== 'number') {
258
+ inputOrderValid = false;
259
+ break;
260
+ }
261
+ nextSearchLine = loc.nextSearchLine;
262
+ }
263
+ if (inputOrderValid) return list;
264
+ const keyed = [];
265
+ for (let idx = 0; idx < list.length; idx++) {
266
+ const hunk = list[idx];
267
+ const stats = v4AHunkLineStats(hunk);
268
+ if (stats.oldCount === 0 && stats.newCount === 0) {
269
+ keyed.push({ hunk, key: Number.MAX_SAFE_INTEGER, idx });
270
+ continue;
271
+ }
272
+ const seq = [];
273
+ for (const ln of hunk.lines || []) {
274
+ if (isV4AEndOfFileMarker(ln)) continue;
275
+ const p = ln[0];
276
+ if (p === ' ' || p === '-') seq.push(ln.slice(1));
277
+ }
278
+ if (seq.length === 0) return list;
279
+ let pos = -1;
280
+ let count = 0;
281
+ for (let i = 0; i + seq.length <= sourceLines.length; i++) {
282
+ let match = true;
283
+ for (let j = 0; j < seq.length; j++) {
284
+ if (sourceLines[i + j] !== seq[j]) { match = false; break; }
285
+ }
286
+ if (match) {
287
+ if (pos < 0) pos = i;
288
+ count++;
289
+ if (count >= 2) break;
290
+ }
291
+ }
292
+ if (count !== 1) return list;
293
+ keyed.push({ hunk, key: pos, idx });
294
+ }
295
+ keyed.sort((a, b) => (a.key - b.key) || (a.idx - b.idx));
296
+ return keyed.map((e) => e.hunk);
297
+ }
298
+
299
+ export function isV4ARenameSection(section) {
300
+ return section?.kind === 'update' && !!section?.movePath;
301
+ }
302
+
303
+ function v4aRenamePathKey(absPath) {
304
+ return process.platform === 'win32' ? String(absPath || '').toLowerCase() : String(absPath || '');
305
+ }
306
+
307
+ function v4aRenamePathInsideRealBase(absPath, realBase) {
308
+ const checkReal = realpathNearestExistingAncestor(absPath);
309
+ const checkRel = pathRelative(realBase, checkReal);
310
+ if (!checkRel || checkRel.startsWith('..') || isAbsolute(checkRel)) return false;
311
+ return !checkRel.split(/[\\/]+/).some((part) => part === '..');
312
+ }
313
+
314
+ function validateV4ARenameSection(section, basePath, seenDestKeys, realBase) {
315
+ const escapeErr = v4aSectionPathEscapeError(section, basePath);
316
+ if (escapeErr) return escapeErr;
317
+ const escapeDest = v4aSectionPathEscapeError({ path: section.movePath }, basePath);
318
+ if (escapeDest) return escapeDest;
319
+ const srcFull = resolveV4AEntryPath(basePath, section.path);
320
+ const destFull = resolveV4AEntryPath(basePath, section.movePath);
321
+ if (realBase) {
322
+ if (!v4aRenamePathInsideRealBase(srcFull, realBase)) {
323
+ return `apply_patch: ${normalizeOutputPath(section.path)} resolves outside base_path via symlink/junction; refusing V4A rename.`;
324
+ }
325
+ if (!v4aRenamePathInsideRealBase(destFull, realBase)) {
326
+ return `apply_patch: ${normalizeOutputPath(section.movePath)} resolves outside base_path via symlink/junction; refusing V4A rename.`;
327
+ }
328
+ }
329
+ if (v4aRenamePathKey(srcFull) === v4aRenamePathKey(destFull)) {
330
+ return `apply_patch: V4A rename source and destination are the same path (${normalizeOutputPath(section.path)})`;
331
+ }
332
+ const destKey = v4aRenamePathKey(destFull);
333
+ if (seenDestKeys.has(destKey)) {
334
+ return `apply_patch: duplicate V4A rename destination ${normalizeOutputPath(section.movePath)}`;
335
+ }
336
+ seenDestKeys.add(destKey);
337
+ try {
338
+ const st = statSync(srcFull);
339
+ if (!st.isFile()) {
340
+ return `apply_patch: V4A rename source is not a regular file: ${normalizeOutputPath(section.path)}`;
341
+ }
342
+ } catch (err) {
343
+ return `apply_patch: V4A rename source missing or unreadable: ${normalizeOutputPath(section.path)} (${err?.code || err?.message || String(err)})`;
344
+ }
345
+ try {
346
+ const destSt = statSync(destFull);
347
+ if (destSt.isDirectory()) {
348
+ return `apply_patch: V4A rename destination is a directory: ${normalizeOutputPath(section.movePath)}`;
349
+ }
350
+ if (!destSt.isFile()) {
351
+ return `apply_patch: V4A rename destination is not a regular file: ${normalizeOutputPath(section.movePath)}`;
352
+ }
353
+ } catch (err) {
354
+ if (err?.code !== 'ENOENT') {
355
+ return `apply_patch: V4A rename destination unreadable: ${normalizeOutputPath(section.movePath)} (${err?.code || err?.message || String(err)})`;
356
+ }
357
+ }
358
+ if (!section.hunks?.length) {
359
+ return `apply_patch: V4A rename for ${normalizeOutputPath(section.path)} has no update hunks`;
360
+ }
361
+ return null;
362
+ }
363
+
364
+ async function applyV4ARenameSection(section, basePath, options = {}) {
365
+ const srcFull = resolveV4AEntryPath(basePath, section.path);
366
+ const destFull = resolveV4AEntryPath(basePath, section.movePath);
367
+ const displaySrc = normalizeOutputPath(section.path);
368
+ const displayDest = normalizeOutputPath(section.movePath);
369
+ let sourceLines;
370
+ try {
371
+ sourceLines = v4aConversionSourceLines(srcFull, options.linesCache || new Map());
372
+ } catch (err) {
373
+ throw new Error(`apply_patch: V4A rename source unreadable: ${displaySrc} (${err?.code || err?.message || String(err)})`);
374
+ }
375
+ let updatedLines;
376
+ try {
377
+ updatedLines = applyV4AHunksToLines(sourceLines, section.hunks, options);
378
+ } catch (err) {
379
+ throw err;
380
+ }
381
+ const newContent = joinTextLinesForPatch(updatedLines);
382
+ if (options.dryRun) {
383
+ return {
384
+ ok: true,
385
+ dryRun: true,
386
+ displayPath: displayDest,
387
+ linesChanged: section.hunks.reduce((n, h) => n + (h.lines?.length || 0), 0),
388
+ srcFull,
389
+ destFull,
390
+ };
391
+ }
392
+ const originalContent = readFileSync(srcFull);
393
+ let destBefore = null;
394
+ try {
395
+ destBefore = readFileSync(destFull);
396
+ } catch (err) {
397
+ if (err?.code !== 'ENOENT') throw err;
398
+ }
399
+ mkdirSync(pathDirname(destFull), { recursive: true });
400
+ try {
401
+ await atomicWrite(destFull, newContent, { sessionId: options.readStateScope });
402
+ await unlink(srcFull);
403
+ } catch (err) {
404
+ try {
405
+ if (destBefore === null) {
406
+ try { await unlink(destFull); } catch {}
407
+ } else {
408
+ await atomicWrite(destFull, destBefore, { sessionId: options.readStateScope });
409
+ }
410
+ } catch {}
411
+ try {
412
+ await atomicWrite(srcFull, originalContent, { sessionId: options.readStateScope });
413
+ } catch {}
414
+ throw new Error(`apply_patch: V4A rename failed for ${displaySrc} → ${displayDest} (${err?.message || String(err)})`);
415
+ }
416
+ invalidateBuiltinResultCache([srcFull, destFull]);
417
+ markCodeGraphDirtyPaths([srcFull, destFull]);
418
+ clearReadSnapshotForPath(srcFull, options.readStateScope);
419
+ clearReadSnapshotForPath(destFull, options.readStateScope);
420
+ return {
421
+ ok: true,
422
+ displayPath: displayDest,
423
+ fromPath: displaySrc,
424
+ linesChanged: section.hunks.reduce((n, h) => n + (h.lines?.length || 0), 0),
425
+ srcFull,
426
+ destFull,
427
+ };
428
+ }
429
+
430
+ export function formatV4ARenameSuccessLines(results) {
431
+ return (results || [])
432
+ .filter((r) => r?.ok && !r.skipped)
433
+ .map((r) => `OK ${r.displayPath} (renamed from ${r.fromPath}, ~${r.linesChanged} lines touched, engine=v4a-rename)`);
434
+ }
435
+
436
+ export async function planV4ARenameSections(sections, basePath) {
437
+ const renameSections = (sections || []).filter(isV4ARenameSection);
438
+ const remainingSections = (sections || []).filter((s) => !isV4ARenameSection(s));
439
+ if (renameSections.length === 0) {
440
+ return { renameSections: [], remainingSections };
441
+ }
442
+ if (renameSections.length > 1) {
443
+ throw new Error('apply_patch: only one V4A rename (*** Move to:) per patch is supported; split into separate patches.');
444
+ }
445
+ if (remainingSections.length > 0) {
446
+ throw new Error('apply_patch: V4A rename cannot be combined with other add/update/delete sections in the same patch; apply file edits in a separate patch first.');
447
+ }
448
+ await assertPathReachable(basePath);
449
+ const renameReachPaths = renameSections.flatMap((section) => [
450
+ resolveV4AEntryPath(basePath, section.path),
451
+ resolveV4AEntryPath(basePath, section.movePath),
452
+ ]);
453
+ await assertPathsReachable(renameReachPaths);
454
+ let realBase;
455
+ try {
456
+ realBase = realpathSync(pathResolve(basePath));
457
+ } catch (err) {
458
+ throw new Error(`apply_patch: base_path unreadable (${err?.code || err?.message || String(err)}): ${basePath}`);
459
+ }
460
+ const seenDestKeys = new Set();
461
+ for (const section of renameSections) {
462
+ const errText = validateV4ARenameSection(section, basePath, seenDestKeys, realBase);
463
+ if (errText) throw new Error(errText);
464
+ }
465
+ return {
466
+ renameSections,
467
+ remainingSections,
468
+ };
469
+ }
470
+
471
+ export async function applyV4ARenameSections(renameSections, basePath, options = {}) {
472
+ const linesCache = new Map();
473
+ const results = [];
474
+ for (const section of renameSections || []) {
475
+ results.push(await applyV4ARenameSection(section, basePath, { ...options, linesCache }));
476
+ }
477
+ return results;
478
+ }
479
+
480
+ export function convertUnifiedBareV4AToUnifiedPatch(patchStr, basePath, options = {}) {
481
+ return convertV4ASectionsToUnifiedPatch(parseUnifiedBareV4APatch(patchStr), basePath, options);
482
+ }
483
+
484
+ export function convertUnifiedCountedToUnifiedPatchViaV4A(patchStr, basePath, options = {}) {
485
+ return convertV4ASectionsToUnifiedPatch(parseUnifiedCountedAsV4APatch(patchStr), basePath, options);
486
+ }
487
+
488
+ // Lexical path-escape guard for V4A section paths.
489
+ function v4aSectionPathEscapeError(section, basePath) {
490
+ const raw = section?.path;
491
+ if (!raw) return null;
492
+ const norm = normalizeInputPath(raw);
493
+ const segs = norm.split(/[\\/]+/);
494
+ if (segs.some((part) => part === '..')) {
495
+ return `apply_patch: header ${normalizeOutputPath(raw)} is unsupported (path escapes base_path or contains \`..\`).`;
496
+ }
497
+ if (isAbsolute(norm) || /^[A-Za-z]:[\\/]/.test(norm)) {
498
+ if (!basePath) return `apply_patch: header ${normalizeOutputPath(raw)} is unsupported (path escapes base_path or contains \`..\`).`;
499
+ const absHeader = pathResolve(norm);
500
+ const absBase = pathResolve(basePath);
501
+ const rel = pathRelative(absBase, absHeader);
502
+ if (!rel || rel.startsWith('..') || isAbsolute(rel) || rel.split(/[\\/]+/).some((part) => part === '..')) {
503
+ return `apply_patch: header ${normalizeOutputPath(raw)} is unsupported (path escapes base_path or contains \`..\`).`;
504
+ }
505
+ }
506
+ return null;
507
+ }
508
+
509
+ function readRawBufForV4AConversion(fullPath) {
510
+ const st = statSync(fullPath);
511
+ const cached = rawContentCacheGet(fullPath, st);
512
+ if (cached) return cached;
513
+ const rawBuf = readFileSync(fullPath);
514
+ const buf = Buffer.isBuffer(rawBuf) ? rawBuf : Buffer.from(rawBuf);
515
+ rawContentCacheSet(fullPath, st, buf);
516
+ return buf;
517
+ }
518
+
519
+ function v4aConversionSourceLines(fullPath, linesCache) {
520
+ if (linesCache.has(fullPath)) return linesCache.get(fullPath);
521
+ const lines = splitTextLinesForPatch(readRawBufForV4AConversion(fullPath).toString('utf-8'));
522
+ linesCache.set(fullPath, lines);
523
+ return lines;
524
+ }
525
+
526
+ // options.rejectPartial (default true) — see original patch.mjs commentary.
527
+ export async function convertV4ASectionsToUnifiedPatch(sections, basePath, options = {}) {
528
+ {
529
+ const reachPaths = [];
530
+ const _seenReach = new Set();
531
+ for (const s of (sections || [])) {
532
+ if (!s || s.kind === 'add' || typeof s.path !== 'string' || !s.path) continue;
533
+ const fp = resolveV4AEntryPath(basePath, s.path);
534
+ if (_seenReach.has(fp)) continue;
535
+ _seenReach.add(fp);
536
+ reachPaths.push(fp);
537
+ }
538
+ await assertPathsReachable(reachPaths);
539
+ }
540
+ const rejectPartial = options.rejectPartial !== false;
541
+ const rejectedHunks = Array.isArray(options.rejectedHunks) ? options.rejectedHunks : null;
542
+ const fuzzy = options.fuzzy !== false;
543
+ const out = [];
544
+ const v4aLinesCache = new Map();
545
+ for (const section of sections) {
546
+ const escapeErr = v4aSectionPathEscapeError(section, basePath);
547
+ if (escapeErr) throw new Error(escapeErr);
548
+ const displayPath = section.path.replace(/\\/g, '/');
549
+ if (section.kind === 'add') {
550
+ out.push('--- /dev/null');
551
+ out.push(`+++ b/${displayPath}`);
552
+ out.push(`@@ -0,0 +1,${section.lines.length} @@`);
553
+ for (const line of section.lines) out.push(`+${line}`);
554
+ continue;
555
+ }
556
+ if (section.kind === 'delete') {
557
+ const fullPath = resolveV4AEntryPath(basePath, section.path);
558
+ let fileLines = [];
559
+ try {
560
+ const _delRaw = readFileSync(fullPath);
561
+ if (decodeValidUtf8OrNull(_delRaw) !== null) {
562
+ fileLines = v4aConversionSourceLines(fullPath, v4aLinesCache);
563
+ }
564
+ } catch {
565
+ fileLines = [];
566
+ }
567
+ out.push(`--- a/${displayPath}`);
568
+ out.push('+++ /dev/null');
569
+ if (fileLines.length > 0) {
570
+ out.push(`@@ -1,${fileLines.length} +0,0 @@`);
571
+ for (const line of fileLines) out.push(`-${line}`);
572
+ }
573
+ continue;
574
+ }
575
+
576
+ const fullPath = resolveV4AEntryPath(basePath, section.path);
577
+ let sourceLines;
578
+ try {
579
+ sourceLines = v4aConversionSourceLines(fullPath, v4aLinesCache);
580
+ } catch (err) {
581
+ throw new Error(`V4A update target unreadable: ${section.path} (${err?.code || err?.message || String(err)}).`);
582
+ }
583
+ const sectionHunks = [];
584
+ const orderedHunks = orderV4AHunksByFilePosition(sourceLines, section.hunks, fuzzy);
585
+ let nextSearchLine = 0;
586
+ for (const hunk of orderedHunks) {
587
+ const stats = v4AHunkLineStats(hunk);
588
+ if (stats.oldCount === 0 && stats.newCount === 0) continue;
589
+ const loc = resolveV4AHunkPosition(sourceLines, hunk, nextSearchLine, { fuzzy });
590
+ if (loc.skip) continue;
591
+ if (loc.error) {
592
+ const msg = `${loc.error.replace(/^V4A hunk /, `V4A hunk ${section.path}: `)}`;
593
+ if (rejectPartial) throw new Error(msg);
594
+ if (rejectedHunks) rejectedHunks.push({ file: section.path, hunk, reason: msg });
595
+ continue;
596
+ }
597
+ const oldStart = stats.oldCount === 0 ? loc.oldStartIdx : loc.oldStartIdx + 1;
598
+ const newStart = oldStart;
599
+ const tail = (hunk.anchors || []).filter(Boolean).join(' ');
600
+ const oldCount = stats.oldCount === 0 ? 0 : loc.matchLen;
601
+ const newCount = stats.newCount - (loc.trimmedTrailingNew || 0);
602
+ sectionHunks.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@${tail ? ` ${tail}` : ''}`);
603
+ let dropOldAt = -1;
604
+ let dropNewAt = -1;
605
+ if (loc.trimmedTrailing) {
606
+ for (let i = hunk.lines.length - 1; i >= 0; i--) {
607
+ const ln = hunk.lines[i];
608
+ if (isV4AEndOfFileMarker(ln)) continue;
609
+ const p = ln[0];
610
+ if (dropOldAt < 0 && (p === ' ' || p === '-')) dropOldAt = i;
611
+ if (dropNewAt < 0 && loc.trimmedTrailingNew && (p === ' ' || p === '+')) dropNewAt = i;
612
+ if (dropOldAt >= 0 && (!loc.trimmedTrailingNew || dropNewAt >= 0)) break;
613
+ }
614
+ }
615
+ let srcIdx = loc.oldStartIdx;
616
+ const srcEnd = loc.oldStartIdx + loc.matchLen;
617
+ for (let i = 0; i < hunk.lines.length; i++) {
618
+ const line = hunk.lines[i];
619
+ if (isV4AEndOfFileMarker(line)) continue;
620
+ const prefix = line[0];
621
+ if (prefix === ' ' || prefix === '-') {
622
+ if (i === dropOldAt || i === dropNewAt) continue;
623
+ if (srcIdx < srcEnd && srcIdx < sourceLines.length) {
624
+ sectionHunks.push(prefix + sourceLines[srcIdx]);
625
+ } else {
626
+ sectionHunks.push(line);
627
+ }
628
+ srcIdx++;
629
+ } else {
630
+ if (i === dropNewAt) continue;
631
+ sectionHunks.push(line);
632
+ }
633
+ }
634
+ nextSearchLine = loc.nextSearchLine;
635
+ }
636
+ if (sectionHunks.length > 0) {
637
+ out.push(`--- a/${displayPath}`);
638
+ out.push(`+++ b/${displayPath}`);
639
+ for (const line of sectionHunks) out.push(line);
640
+ }
641
+ }
642
+ return out.join('\n') + '\n';
643
+ }