@kenkaiiii/ggcoder 5.49.5 → 5.49.7

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 (956) hide show
  1. package/dist/app-sidecar.js +34 -9
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/core/settings-manager.d.ts +2 -2
  4. package/dist/hf-pull.d.ts +38 -2
  5. package/dist/hf-pull.d.ts.map +1 -1
  6. package/dist/hf-pull.js +122 -6
  7. package/dist/hf-pull.js.map +1 -1
  8. package/package.json +5 -5
  9. package/dist/app-sidecar-auth-broadcast.test.d.ts +0 -2
  10. package/dist/app-sidecar-auth-broadcast.test.d.ts.map +0 -1
  11. package/dist/app-sidecar-auth-broadcast.test.js +0 -383
  12. package/dist/app-sidecar-auth-broadcast.test.js.map +0 -1
  13. package/dist/app-sidecar-sessions.test.d.ts +0 -2
  14. package/dist/app-sidecar-sessions.test.d.ts.map +0 -1
  15. package/dist/app-sidecar-sessions.test.js +0 -178
  16. package/dist/app-sidecar-sessions.test.js.map +0 -1
  17. package/dist/chat-agents/general.test.d.ts +0 -2
  18. package/dist/chat-agents/general.test.d.ts.map +0 -1
  19. package/dist/chat-agents/general.test.js +0 -53
  20. package/dist/chat-agents/general.test.js.map +0 -1
  21. package/dist/chat-agents/jiwa.test.d.ts +0 -2
  22. package/dist/chat-agents/jiwa.test.d.ts.map +0 -1
  23. package/dist/chat-agents/jiwa.test.js +0 -66
  24. package/dist/chat-agents/jiwa.test.js.map +0 -1
  25. package/dist/chat-agents/memory.test.d.ts +0 -2
  26. package/dist/chat-agents/memory.test.d.ts.map +0 -1
  27. package/dist/chat-agents/memory.test.js +0 -165
  28. package/dist/chat-agents/memory.test.js.map +0 -1
  29. package/dist/chat-agents/specialists.test.d.ts +0 -2
  30. package/dist/chat-agents/specialists.test.d.ts.map +0 -1
  31. package/dist/chat-agents/specialists.test.js +0 -98
  32. package/dist/chat-agents/specialists.test.js.map +0 -1
  33. package/dist/cli/command-routing.test.d.ts +0 -2
  34. package/dist/cli/command-routing.test.d.ts.map +0 -1
  35. package/dist/cli/command-routing.test.js +0 -60
  36. package/dist/cli/command-routing.test.js.map +0 -1
  37. package/dist/config.test.d.ts +0 -2
  38. package/dist/config.test.d.ts.map +0 -1
  39. package/dist/config.test.js +0 -148
  40. package/dist/config.test.js.map +0 -1
  41. package/dist/core/agent-mcp-passthrough.test.d.ts +0 -2
  42. package/dist/core/agent-mcp-passthrough.test.d.ts.map +0 -1
  43. package/dist/core/agent-mcp-passthrough.test.js +0 -61
  44. package/dist/core/agent-mcp-passthrough.test.js.map +0 -1
  45. package/dist/core/agent-notifications.test.d.ts +0 -2
  46. package/dist/core/agent-notifications.test.d.ts.map +0 -1
  47. package/dist/core/agent-notifications.test.js +0 -97
  48. package/dist/core/agent-notifications.test.js.map +0 -1
  49. package/dist/core/agent-session-add-dir.test.d.ts +0 -2
  50. package/dist/core/agent-session-add-dir.test.d.ts.map +0 -1
  51. package/dist/core/agent-session-add-dir.test.js +0 -110
  52. package/dist/core/agent-session-add-dir.test.js.map +0 -1
  53. package/dist/core/agent-session-checkpoint-flush.test.d.ts +0 -2
  54. package/dist/core/agent-session-checkpoint-flush.test.d.ts.map +0 -1
  55. package/dist/core/agent-session-checkpoint-flush.test.js +0 -201
  56. package/dist/core/agent-session-checkpoint-flush.test.js.map +0 -1
  57. package/dist/core/agent-session-compaction.test.d.ts +0 -2
  58. package/dist/core/agent-session-compaction.test.d.ts.map +0 -1
  59. package/dist/core/agent-session-compaction.test.js +0 -1157
  60. package/dist/core/agent-session-compaction.test.js.map +0 -1
  61. package/dist/core/agent-session-crash-durability.test.d.ts +0 -2
  62. package/dist/core/agent-session-crash-durability.test.d.ts.map +0 -1
  63. package/dist/core/agent-session-crash-durability.test.js +0 -102
  64. package/dist/core/agent-session-crash-durability.test.js.map +0 -1
  65. package/dist/core/agent-session-marker-anchors.test.d.ts +0 -2
  66. package/dist/core/agent-session-marker-anchors.test.d.ts.map +0 -1
  67. package/dist/core/agent-session-marker-anchors.test.js +0 -326
  68. package/dist/core/agent-session-marker-anchors.test.js.map +0 -1
  69. package/dist/core/agent-session-memory-tail.test.d.ts +0 -2
  70. package/dist/core/agent-session-memory-tail.test.d.ts.map +0 -1
  71. package/dist/core/agent-session-memory-tail.test.js +0 -119
  72. package/dist/core/agent-session-memory-tail.test.js.map +0 -1
  73. package/dist/core/agent-session-model-switch.test.d.ts +0 -2
  74. package/dist/core/agent-session-model-switch.test.d.ts.map +0 -1
  75. package/dist/core/agent-session-model-switch.test.js +0 -161
  76. package/dist/core/agent-session-model-switch.test.js.map +0 -1
  77. package/dist/core/agent-session-notifications.test.d.ts +0 -2
  78. package/dist/core/agent-session-notifications.test.d.ts.map +0 -1
  79. package/dist/core/agent-session-notifications.test.js +0 -114
  80. package/dist/core/agent-session-notifications.test.js.map +0 -1
  81. package/dist/core/agent-session-pre-init-arming.test.d.ts +0 -2
  82. package/dist/core/agent-session-pre-init-arming.test.d.ts.map +0 -1
  83. package/dist/core/agent-session-pre-init-arming.test.js +0 -60
  84. package/dist/core/agent-session-pre-init-arming.test.js.map +0 -1
  85. package/dist/core/agent-session-process-gate.test.d.ts +0 -2
  86. package/dist/core/agent-session-process-gate.test.d.ts.map +0 -1
  87. package/dist/core/agent-session-process-gate.test.js +0 -98
  88. package/dist/core/agent-session-process-gate.test.js.map +0 -1
  89. package/dist/core/agent-session-queue.test.d.ts +0 -2
  90. package/dist/core/agent-session-queue.test.d.ts.map +0 -1
  91. package/dist/core/agent-session-queue.test.js +0 -184
  92. package/dist/core/agent-session-queue.test.js.map +0 -1
  93. package/dist/core/agent-session-review-coverage.test.d.ts +0 -2
  94. package/dist/core/agent-session-review-coverage.test.d.ts.map +0 -1
  95. package/dist/core/agent-session-review-coverage.test.js +0 -191
  96. package/dist/core/agent-session-review-coverage.test.js.map +0 -1
  97. package/dist/core/agent-session-slash-restore.test.d.ts +0 -2
  98. package/dist/core/agent-session-slash-restore.test.d.ts.map +0 -1
  99. package/dist/core/agent-session-slash-restore.test.js +0 -121
  100. package/dist/core/agent-session-slash-restore.test.js.map +0 -1
  101. package/dist/core/agent-session-subagent-policy.test.d.ts +0 -2
  102. package/dist/core/agent-session-subagent-policy.test.d.ts.map +0 -1
  103. package/dist/core/agent-session-subagent-policy.test.js +0 -40
  104. package/dist/core/agent-session-subagent-policy.test.js.map +0 -1
  105. package/dist/core/agent-session-tool-result-policy.test.d.ts +0 -2
  106. package/dist/core/agent-session-tool-result-policy.test.d.ts.map +0 -1
  107. package/dist/core/agent-session-tool-result-policy.test.js +0 -25
  108. package/dist/core/agent-session-tool-result-policy.test.js.map +0 -1
  109. package/dist/core/agent-session-tool-tiers.test.d.ts +0 -2
  110. package/dist/core/agent-session-tool-tiers.test.d.ts.map +0 -1
  111. package/dist/core/agent-session-tool-tiers.test.js +0 -138
  112. package/dist/core/agent-session-tool-tiers.test.js.map +0 -1
  113. package/dist/core/agent-session-verification-flow.test.d.ts +0 -2
  114. package/dist/core/agent-session-verification-flow.test.d.ts.map +0 -1
  115. package/dist/core/agent-session-verification-flow.test.js +0 -125
  116. package/dist/core/agent-session-verification-flow.test.js.map +0 -1
  117. package/dist/core/agent-session-verification-gate.test.d.ts +0 -2
  118. package/dist/core/agent-session-verification-gate.test.d.ts.map +0 -1
  119. package/dist/core/agent-session-verification-gate.test.js +0 -137
  120. package/dist/core/agent-session-verification-gate.test.js.map +0 -1
  121. package/dist/core/agent-tools-allowlist.test.d.ts +0 -2
  122. package/dist/core/agent-tools-allowlist.test.d.ts.map +0 -1
  123. package/dist/core/agent-tools-allowlist.test.js +0 -74
  124. package/dist/core/agent-tools-allowlist.test.js.map +0 -1
  125. package/dist/core/agents.test.d.ts +0 -2
  126. package/dist/core/agents.test.d.ts.map +0 -1
  127. package/dist/core/agents.test.js +0 -147
  128. package/dist/core/agents.test.js.map +0 -1
  129. package/dist/core/auto-update.test.d.ts +0 -2
  130. package/dist/core/auto-update.test.d.ts.map +0 -1
  131. package/dist/core/auto-update.test.js +0 -204
  132. package/dist/core/auto-update.test.js.map +0 -1
  133. package/dist/core/autopilot-cycle.test.d.ts +0 -2
  134. package/dist/core/autopilot-cycle.test.d.ts.map +0 -1
  135. package/dist/core/autopilot-cycle.test.js +0 -375
  136. package/dist/core/autopilot-cycle.test.js.map +0 -1
  137. package/dist/core/autopilot-gate.test.d.ts +0 -2
  138. package/dist/core/autopilot-gate.test.d.ts.map +0 -1
  139. package/dist/core/autopilot-gate.test.js +0 -298
  140. package/dist/core/autopilot-gate.test.js.map +0 -1
  141. package/dist/core/autopilot-verdict.test.d.ts +0 -2
  142. package/dist/core/autopilot-verdict.test.d.ts.map +0 -1
  143. package/dist/core/autopilot-verdict.test.js +0 -153
  144. package/dist/core/autopilot-verdict.test.js.map +0 -1
  145. package/dist/core/checkpoint-store.test.d.ts +0 -2
  146. package/dist/core/checkpoint-store.test.d.ts.map +0 -1
  147. package/dist/core/checkpoint-store.test.js +0 -111
  148. package/dist/core/checkpoint-store.test.js.map +0 -1
  149. package/dist/core/code-retrieval-chunkers.test.d.ts +0 -2
  150. package/dist/core/code-retrieval-chunkers.test.d.ts.map +0 -1
  151. package/dist/core/code-retrieval-chunkers.test.js +0 -203
  152. package/dist/core/code-retrieval-chunkers.test.js.map +0 -1
  153. package/dist/core/compaction/active-context.test.d.ts +0 -2
  154. package/dist/core/compaction/active-context.test.d.ts.map +0 -1
  155. package/dist/core/compaction/active-context.test.js +0 -57
  156. package/dist/core/compaction/active-context.test.js.map +0 -1
  157. package/dist/core/compaction/compactor.test.d.ts +0 -2
  158. package/dist/core/compaction/compactor.test.d.ts.map +0 -1
  159. package/dist/core/compaction/compactor.test.js +0 -1072
  160. package/dist/core/compaction/compactor.test.js.map +0 -1
  161. package/dist/core/compaction/policy.test.d.ts +0 -2
  162. package/dist/core/compaction/policy.test.d.ts.map +0 -1
  163. package/dist/core/compaction/policy.test.js +0 -35
  164. package/dist/core/compaction/policy.test.js.map +0 -1
  165. package/dist/core/compaction/query-aware-selector.test.d.ts +0 -2
  166. package/dist/core/compaction/query-aware-selector.test.d.ts.map +0 -1
  167. package/dist/core/compaction/query-aware-selector.test.js +0 -96
  168. package/dist/core/compaction/query-aware-selector.test.js.map +0 -1
  169. package/dist/core/compaction/token-estimator.test.d.ts +0 -2
  170. package/dist/core/compaction/token-estimator.test.d.ts.map +0 -1
  171. package/dist/core/compaction/token-estimator.test.js +0 -283
  172. package/dist/core/compaction/token-estimator.test.js.map +0 -1
  173. package/dist/core/compaction/tool-result-pruner.test.d.ts +0 -2
  174. package/dist/core/compaction/tool-result-pruner.test.d.ts.map +0 -1
  175. package/dist/core/compaction/tool-result-pruner.test.js +0 -320
  176. package/dist/core/compaction/tool-result-pruner.test.js.map +0 -1
  177. package/dist/core/context-limits.test.d.ts +0 -2
  178. package/dist/core/context-limits.test.d.ts.map +0 -1
  179. package/dist/core/context-limits.test.js +0 -55
  180. package/dist/core/context-limits.test.js.map +0 -1
  181. package/dist/core/continue-replay-inventory.test.d.ts +0 -2
  182. package/dist/core/continue-replay-inventory.test.d.ts.map +0 -1
  183. package/dist/core/continue-replay-inventory.test.js +0 -42
  184. package/dist/core/continue-replay-inventory.test.js.map +0 -1
  185. package/dist/core/encode-cwd.test.d.ts +0 -2
  186. package/dist/core/encode-cwd.test.d.ts.map +0 -1
  187. package/dist/core/encode-cwd.test.js +0 -80
  188. package/dist/core/encode-cwd.test.js.map +0 -1
  189. package/dist/core/event-bus.test.d.ts +0 -2
  190. package/dist/core/event-bus.test.d.ts.map +0 -1
  191. package/dist/core/event-bus.test.js +0 -33
  192. package/dist/core/event-bus.test.js.map +0 -1
  193. package/dist/core/extensions/plugin-bundles.test.d.ts +0 -2
  194. package/dist/core/extensions/plugin-bundles.test.d.ts.map +0 -1
  195. package/dist/core/extensions/plugin-bundles.test.js +0 -110
  196. package/dist/core/extensions/plugin-bundles.test.js.map +0 -1
  197. package/dist/core/foreign-session-import.test.d.ts +0 -2
  198. package/dist/core/foreign-session-import.test.d.ts.map +0 -1
  199. package/dist/core/foreign-session-import.test.js +0 -220
  200. package/dist/core/foreign-session-import.test.js.map +0 -1
  201. package/dist/core/hashline-edit-benchmark.test.d.ts +0 -2
  202. package/dist/core/hashline-edit-benchmark.test.d.ts.map +0 -1
  203. package/dist/core/hashline-edit-benchmark.test.js +0 -190
  204. package/dist/core/hashline-edit-benchmark.test.js.map +0 -1
  205. package/dist/core/ideal-review.test.d.ts +0 -2
  206. package/dist/core/ideal-review.test.d.ts.map +0 -1
  207. package/dist/core/ideal-review.test.js +0 -242
  208. package/dist/core/ideal-review.test.js.map +0 -1
  209. package/dist/core/input-normalization.test.d.ts +0 -2
  210. package/dist/core/input-normalization.test.d.ts.map +0 -1
  211. package/dist/core/input-normalization.test.js +0 -75
  212. package/dist/core/input-normalization.test.js.map +0 -1
  213. package/dist/core/json-mode-flag-parity.test.d.ts +0 -2
  214. package/dist/core/json-mode-flag-parity.test.d.ts.map +0 -1
  215. package/dist/core/json-mode-flag-parity.test.js +0 -66
  216. package/dist/core/json-mode-flag-parity.test.js.map +0 -1
  217. package/dist/core/ken-context.test.d.ts +0 -2
  218. package/dist/core/ken-context.test.d.ts.map +0 -1
  219. package/dist/core/ken-context.test.js +0 -342
  220. package/dist/core/ken-context.test.js.map +0 -1
  221. package/dist/core/ken-model.test.d.ts +0 -2
  222. package/dist/core/ken-model.test.d.ts.map +0 -1
  223. package/dist/core/ken-model.test.js +0 -51
  224. package/dist/core/ken-model.test.js.map +0 -1
  225. package/dist/core/ken-prompt.test.d.ts +0 -2
  226. package/dist/core/ken-prompt.test.d.ts.map +0 -1
  227. package/dist/core/ken-prompt.test.js +0 -191
  228. package/dist/core/ken-prompt.test.js.map +0 -1
  229. package/dist/core/language-detector.test.d.ts +0 -2
  230. package/dist/core/language-detector.test.d.ts.map +0 -1
  231. package/dist/core/language-detector.test.js +0 -109
  232. package/dist/core/language-detector.test.js.map +0 -1
  233. package/dist/core/local-endpoint-store.test.d.ts +0 -2
  234. package/dist/core/local-endpoint-store.test.d.ts.map +0 -1
  235. package/dist/core/local-endpoint-store.test.js +0 -123
  236. package/dist/core/local-endpoint-store.test.js.map +0 -1
  237. package/dist/core/loop-breaker.test.d.ts +0 -2
  238. package/dist/core/loop-breaker.test.d.ts.map +0 -1
  239. package/dist/core/loop-breaker.test.js +0 -238
  240. package/dist/core/loop-breaker.test.js.map +0 -1
  241. package/dist/core/lsp/client-navigation.test.d.ts +0 -2
  242. package/dist/core/lsp/client-navigation.test.d.ts.map +0 -1
  243. package/dist/core/lsp/client-navigation.test.js +0 -135
  244. package/dist/core/lsp/client-navigation.test.js.map +0 -1
  245. package/dist/core/lsp/format.test.d.ts +0 -2
  246. package/dist/core/lsp/format.test.d.ts.map +0 -1
  247. package/dist/core/lsp/format.test.js +0 -56
  248. package/dist/core/lsp/format.test.js.map +0 -1
  249. package/dist/core/lsp/integration.test.d.ts +0 -2
  250. package/dist/core/lsp/integration.test.d.ts.map +0 -1
  251. package/dist/core/lsp/integration.test.js +0 -60
  252. package/dist/core/lsp/integration.test.js.map +0 -1
  253. package/dist/core/lsp/jsonrpc.test.d.ts +0 -2
  254. package/dist/core/lsp/jsonrpc.test.d.ts.map +0 -1
  255. package/dist/core/lsp/jsonrpc.test.js +0 -127
  256. package/dist/core/lsp/jsonrpc.test.js.map +0 -1
  257. package/dist/core/lsp/manager-navigation.test.d.ts +0 -2
  258. package/dist/core/lsp/manager-navigation.test.d.ts.map +0 -1
  259. package/dist/core/lsp/manager-navigation.test.js +0 -121
  260. package/dist/core/lsp/manager-navigation.test.js.map +0 -1
  261. package/dist/core/lsp/manager.test.d.ts +0 -2
  262. package/dist/core/lsp/manager.test.d.ts.map +0 -1
  263. package/dist/core/lsp/manager.test.js +0 -286
  264. package/dist/core/lsp/manager.test.js.map +0 -1
  265. package/dist/core/lsp/pool.test.d.ts +0 -2
  266. package/dist/core/lsp/pool.test.d.ts.map +0 -1
  267. package/dist/core/lsp/pool.test.js +0 -286
  268. package/dist/core/lsp/pool.test.js.map +0 -1
  269. package/dist/core/lsp/servers.test.d.ts +0 -2
  270. package/dist/core/lsp/servers.test.d.ts.map +0 -1
  271. package/dist/core/lsp/servers.test.js +0 -67
  272. package/dist/core/lsp/servers.test.js.map +0 -1
  273. package/dist/core/lsp/uri.test.d.ts +0 -2
  274. package/dist/core/lsp/uri.test.d.ts.map +0 -1
  275. package/dist/core/lsp/uri.test.js +0 -56
  276. package/dist/core/lsp/uri.test.js.map +0 -1
  277. package/dist/core/lsp/windows.test.d.ts +0 -2
  278. package/dist/core/lsp/windows.test.d.ts.map +0 -1
  279. package/dist/core/lsp/windows.test.js +0 -239
  280. package/dist/core/lsp/windows.test.js.map +0 -1
  281. package/dist/core/mcp/catalog-cache.test.d.ts +0 -2
  282. package/dist/core/mcp/catalog-cache.test.d.ts.map +0 -1
  283. package/dist/core/mcp/catalog-cache.test.js +0 -99
  284. package/dist/core/mcp/catalog-cache.test.js.map +0 -1
  285. package/dist/core/mcp/client-session-recovery.test.d.ts +0 -2
  286. package/dist/core/mcp/client-session-recovery.test.d.ts.map +0 -1
  287. package/dist/core/mcp/client-session-recovery.test.js +0 -231
  288. package/dist/core/mcp/client-session-recovery.test.js.map +0 -1
  289. package/dist/core/mcp/content.test.d.ts +0 -2
  290. package/dist/core/mcp/content.test.d.ts.map +0 -1
  291. package/dist/core/mcp/content.test.js +0 -152
  292. package/dist/core/mcp/content.test.js.map +0 -1
  293. package/dist/core/mcp/defaults.test.d.ts +0 -2
  294. package/dist/core/mcp/defaults.test.d.ts.map +0 -1
  295. package/dist/core/mcp/defaults.test.js +0 -60
  296. package/dist/core/mcp/defaults.test.js.map +0 -1
  297. package/dist/core/mcp/elicitation.test.d.ts +0 -2
  298. package/dist/core/mcp/elicitation.test.d.ts.map +0 -1
  299. package/dist/core/mcp/elicitation.test.js +0 -158
  300. package/dist/core/mcp/elicitation.test.js.map +0 -1
  301. package/dist/core/mcp/legacy-negotiation.test.d.ts +0 -2
  302. package/dist/core/mcp/legacy-negotiation.test.d.ts.map +0 -1
  303. package/dist/core/mcp/legacy-negotiation.test.js +0 -80
  304. package/dist/core/mcp/legacy-negotiation.test.js.map +0 -1
  305. package/dist/core/mcp/loopback.test.d.ts +0 -2
  306. package/dist/core/mcp/loopback.test.d.ts.map +0 -1
  307. package/dist/core/mcp/loopback.test.js +0 -87
  308. package/dist/core/mcp/loopback.test.js.map +0 -1
  309. package/dist/core/mcp/oauth-store.test.d.ts +0 -2
  310. package/dist/core/mcp/oauth-store.test.d.ts.map +0 -1
  311. package/dist/core/mcp/oauth-store.test.js +0 -94
  312. package/dist/core/mcp/oauth-store.test.js.map +0 -1
  313. package/dist/core/mcp/parse-add-command.test.d.ts +0 -2
  314. package/dist/core/mcp/parse-add-command.test.d.ts.map +0 -1
  315. package/dist/core/mcp/parse-add-command.test.js +0 -81
  316. package/dist/core/mcp/parse-add-command.test.js.map +0 -1
  317. package/dist/core/mcp/resolve-stdio.test.d.ts +0 -2
  318. package/dist/core/mcp/resolve-stdio.test.d.ts.map +0 -1
  319. package/dist/core/mcp/resolve-stdio.test.js +0 -239
  320. package/dist/core/mcp/resolve-stdio.test.js.map +0 -1
  321. package/dist/core/mcp/session-elicitation-wiring.test.d.ts +0 -2
  322. package/dist/core/mcp/session-elicitation-wiring.test.d.ts.map +0 -1
  323. package/dist/core/mcp/session-elicitation-wiring.test.js +0 -138
  324. package/dist/core/mcp/session-elicitation-wiring.test.js.map +0 -1
  325. package/dist/core/mcp/shared-pool.test.d.ts +0 -2
  326. package/dist/core/mcp/shared-pool.test.d.ts.map +0 -1
  327. package/dist/core/mcp/shared-pool.test.js +0 -425
  328. package/dist/core/mcp/shared-pool.test.js.map +0 -1
  329. package/dist/core/mcp/store.test.d.ts +0 -2
  330. package/dist/core/mcp/store.test.d.ts.map +0 -1
  331. package/dist/core/mcp/store.test.js +0 -110
  332. package/dist/core/mcp/store.test.js.map +0 -1
  333. package/dist/core/mcp/when-connected.test.d.ts +0 -2
  334. package/dist/core/mcp/when-connected.test.d.ts.map +0 -1
  335. package/dist/core/mcp/when-connected.test.js +0 -46
  336. package/dist/core/mcp/when-connected.test.js.map +0 -1
  337. package/dist/core/message-images.test.d.ts +0 -2
  338. package/dist/core/message-images.test.d.ts.map +0 -1
  339. package/dist/core/message-images.test.js +0 -62
  340. package/dist/core/message-images.test.js.map +0 -1
  341. package/dist/core/network-guard.test.d.ts +0 -2
  342. package/dist/core/network-guard.test.d.ts.map +0 -1
  343. package/dist/core/network-guard.test.js +0 -72
  344. package/dist/core/network-guard.test.js.map +0 -1
  345. package/dist/core/persistent-shell.test.d.ts +0 -2
  346. package/dist/core/persistent-shell.test.d.ts.map +0 -1
  347. package/dist/core/persistent-shell.test.js +0 -84
  348. package/dist/core/persistent-shell.test.js.map +0 -1
  349. package/dist/core/process-gate.test.d.ts +0 -2
  350. package/dist/core/process-gate.test.d.ts.map +0 -1
  351. package/dist/core/process-gate.test.js +0 -87
  352. package/dist/core/process-gate.test.js.map +0 -1
  353. package/dist/core/process-manager-dev-server-repro.test.d.ts +0 -2
  354. package/dist/core/process-manager-dev-server-repro.test.d.ts.map +0 -1
  355. package/dist/core/process-manager-dev-server-repro.test.js +0 -225
  356. package/dist/core/process-manager-dev-server-repro.test.js.map +0 -1
  357. package/dist/core/process-manager-notifications.test.d.ts +0 -2
  358. package/dist/core/process-manager-notifications.test.d.ts.map +0 -1
  359. package/dist/core/process-manager-notifications.test.js +0 -245
  360. package/dist/core/process-manager-notifications.test.js.map +0 -1
  361. package/dist/core/process-manager-wake.test.d.ts +0 -2
  362. package/dist/core/process-manager-wake.test.d.ts.map +0 -1
  363. package/dist/core/process-manager-wake.test.js +0 -100
  364. package/dist/core/process-manager-wake.test.js.map +0 -1
  365. package/dist/core/progress/engine.test.d.ts +0 -2
  366. package/dist/core/progress/engine.test.d.ts.map +0 -1
  367. package/dist/core/progress/engine.test.js +0 -136
  368. package/dist/core/progress/engine.test.js.map +0 -1
  369. package/dist/core/progress/git-xp.test.d.ts +0 -2
  370. package/dist/core/progress/git-xp.test.d.ts.map +0 -1
  371. package/dist/core/progress/git-xp.test.js +0 -88
  372. package/dist/core/progress/git-xp.test.js.map +0 -1
  373. package/dist/core/progress/ranks.test.d.ts +0 -2
  374. package/dist/core/progress/ranks.test.d.ts.map +0 -1
  375. package/dist/core/progress/ranks.test.js +0 -100
  376. package/dist/core/progress/ranks.test.js.map +0 -1
  377. package/dist/core/progress/rebuild.test.d.ts +0 -2
  378. package/dist/core/progress/rebuild.test.d.ts.map +0 -1
  379. package/dist/core/progress/rebuild.test.js +0 -103
  380. package/dist/core/progress/rebuild.test.js.map +0 -1
  381. package/dist/core/progress/store.test.d.ts +0 -2
  382. package/dist/core/progress/store.test.d.ts.map +0 -1
  383. package/dist/core/progress/store.test.js +0 -108
  384. package/dist/core/progress/store.test.js.map +0 -1
  385. package/dist/core/project-discovery.test.d.ts +0 -2
  386. package/dist/core/project-discovery.test.d.ts.map +0 -1
  387. package/dist/core/project-discovery.test.js +0 -519
  388. package/dist/core/project-discovery.test.js.map +0 -1
  389. package/dist/core/prompt-commands.test.d.ts +0 -2
  390. package/dist/core/prompt-commands.test.d.ts.map +0 -1
  391. package/dist/core/prompt-commands.test.js +0 -180
  392. package/dist/core/prompt-commands.test.js.map +0 -1
  393. package/dist/core/provider-cache.e2e.test.d.ts +0 -2
  394. package/dist/core/provider-cache.e2e.test.d.ts.map +0 -1
  395. package/dist/core/provider-cache.e2e.test.js +0 -125
  396. package/dist/core/provider-cache.e2e.test.js.map +0 -1
  397. package/dist/core/radio.test.d.ts +0 -2
  398. package/dist/core/radio.test.d.ts.map +0 -1
  399. package/dist/core/radio.test.js +0 -117
  400. package/dist/core/radio.test.js.map +0 -1
  401. package/dist/core/regrounding.test.d.ts +0 -2
  402. package/dist/core/regrounding.test.d.ts.map +0 -1
  403. package/dist/core/regrounding.test.js +0 -38
  404. package/dist/core/regrounding.test.js.map +0 -1
  405. package/dist/core/resolve-start.test.d.ts +0 -2
  406. package/dist/core/resolve-start.test.d.ts.map +0 -1
  407. package/dist/core/resolve-start.test.js +0 -68
  408. package/dist/core/resolve-start.test.js.map +0 -1
  409. package/dist/core/run-claim.test.d.ts +0 -2
  410. package/dist/core/run-claim.test.d.ts.map +0 -1
  411. package/dist/core/run-claim.test.js +0 -107
  412. package/dist/core/run-claim.test.js.map +0 -1
  413. package/dist/core/run-journal.test.d.ts +0 -2
  414. package/dist/core/run-journal.test.d.ts.map +0 -1
  415. package/dist/core/run-journal.test.js +0 -195
  416. package/dist/core/run-journal.test.js.map +0 -1
  417. package/dist/core/run-lifecycle.test.d.ts +0 -2
  418. package/dist/core/run-lifecycle.test.d.ts.map +0 -1
  419. package/dist/core/run-lifecycle.test.js +0 -64
  420. package/dist/core/run-lifecycle.test.js.map +0 -1
  421. package/dist/core/sandbox-feedback.test.d.ts +0 -2
  422. package/dist/core/sandbox-feedback.test.d.ts.map +0 -1
  423. package/dist/core/sandbox-feedback.test.js +0 -32
  424. package/dist/core/sandbox-feedback.test.js.map +0 -1
  425. package/dist/core/sandbox.test.d.ts +0 -2
  426. package/dist/core/sandbox.test.d.ts.map +0 -1
  427. package/dist/core/sandbox.test.js +0 -288
  428. package/dist/core/sandbox.test.js.map +0 -1
  429. package/dist/core/semantic-search-benchmark.test.d.ts +0 -2
  430. package/dist/core/semantic-search-benchmark.test.d.ts.map +0 -1
  431. package/dist/core/semantic-search-benchmark.test.js +0 -89
  432. package/dist/core/semantic-search-benchmark.test.js.map +0 -1
  433. package/dist/core/session-compaction.test.d.ts +0 -2
  434. package/dist/core/session-compaction.test.d.ts.map +0 -1
  435. package/dist/core/session-compaction.test.js +0 -92
  436. package/dist/core/session-compaction.test.js.map +0 -1
  437. package/dist/core/session-export.test.d.ts +0 -2
  438. package/dist/core/session-export.test.d.ts.map +0 -1
  439. package/dist/core/session-export.test.js +0 -353
  440. package/dist/core/session-export.test.js.map +0 -1
  441. package/dist/core/session-history.test.d.ts +0 -2
  442. package/dist/core/session-history.test.d.ts.map +0 -1
  443. package/dist/core/session-history.test.js +0 -272
  444. package/dist/core/session-history.test.js.map +0 -1
  445. package/dist/core/session-injections.test.d.ts +0 -2
  446. package/dist/core/session-injections.test.d.ts.map +0 -1
  447. package/dist/core/session-injections.test.js +0 -117
  448. package/dist/core/session-injections.test.js.map +0 -1
  449. package/dist/core/session-manager.test.d.ts +0 -2
  450. package/dist/core/session-manager.test.d.ts.map +0 -1
  451. package/dist/core/session-manager.test.js +0 -571
  452. package/dist/core/session-manager.test.js.map +0 -1
  453. package/dist/core/session-notification-visibility.test.d.ts +0 -2
  454. package/dist/core/session-notification-visibility.test.d.ts.map +0 -1
  455. package/dist/core/session-notification-visibility.test.js +0 -113
  456. package/dist/core/session-notification-visibility.test.js.map +0 -1
  457. package/dist/core/session-reconstructability.test.d.ts +0 -2
  458. package/dist/core/session-reconstructability.test.d.ts.map +0 -1
  459. package/dist/core/session-reconstructability.test.js +0 -105
  460. package/dist/core/session-reconstructability.test.js.map +0 -1
  461. package/dist/core/session-restore-display.test.d.ts +0 -2
  462. package/dist/core/session-restore-display.test.d.ts.map +0 -1
  463. package/dist/core/session-restore-display.test.js +0 -103
  464. package/dist/core/session-restore-display.test.js.map +0 -1
  465. package/dist/core/session-storage-fd-leak.test.d.ts +0 -2
  466. package/dist/core/session-storage-fd-leak.test.d.ts.map +0 -1
  467. package/dist/core/session-storage-fd-leak.test.js +0 -116
  468. package/dist/core/session-storage-fd-leak.test.js.map +0 -1
  469. package/dist/core/session-storage.test.d.ts +0 -2
  470. package/dist/core/session-storage.test.d.ts.map +0 -1
  471. package/dist/core/session-storage.test.js +0 -277
  472. package/dist/core/session-storage.test.js.map +0 -1
  473. package/dist/core/setup-history.test.d.ts +0 -2
  474. package/dist/core/setup-history.test.d.ts.map +0 -1
  475. package/dist/core/setup-history.test.js +0 -59
  476. package/dist/core/setup-history.test.js.map +0 -1
  477. package/dist/core/shell.test.d.ts +0 -2
  478. package/dist/core/shell.test.d.ts.map +0 -1
  479. package/dist/core/shell.test.js +0 -87
  480. package/dist/core/shell.test.js.map +0 -1
  481. package/dist/core/shutdown.test.d.ts +0 -2
  482. package/dist/core/shutdown.test.d.ts.map +0 -1
  483. package/dist/core/shutdown.test.js +0 -160
  484. package/dist/core/shutdown.test.js.map +0 -1
  485. package/dist/core/sidecar-error-reporter.test.d.ts +0 -2
  486. package/dist/core/sidecar-error-reporter.test.d.ts.map +0 -1
  487. package/dist/core/sidecar-error-reporter.test.js +0 -61
  488. package/dist/core/sidecar-error-reporter.test.js.map +0 -1
  489. package/dist/core/skills-routing.test.d.ts +0 -2
  490. package/dist/core/skills-routing.test.d.ts.map +0 -1
  491. package/dist/core/skills-routing.test.js +0 -310
  492. package/dist/core/skills-routing.test.js.map +0 -1
  493. package/dist/core/slash-commands.test.d.ts +0 -2
  494. package/dist/core/slash-commands.test.d.ts.map +0 -1
  495. package/dist/core/slash-commands.test.js +0 -59
  496. package/dist/core/slash-commands.test.js.map +0 -1
  497. package/dist/core/source-maps.test.d.ts +0 -2
  498. package/dist/core/source-maps.test.d.ts.map +0 -1
  499. package/dist/core/source-maps.test.js +0 -175
  500. package/dist/core/source-maps.test.js.map +0 -1
  501. package/dist/core/speed-benchmark.test.d.ts +0 -2
  502. package/dist/core/speed-benchmark.test.d.ts.map +0 -1
  503. package/dist/core/speed-benchmark.test.js +0 -102
  504. package/dist/core/speed-benchmark.test.js.map +0 -1
  505. package/dist/core/subagent-manager.test.d.ts +0 -2
  506. package/dist/core/subagent-manager.test.d.ts.map +0 -1
  507. package/dist/core/subagent-manager.test.js +0 -536
  508. package/dist/core/subagent-manager.test.js.map +0 -1
  509. package/dist/core/subagent-store.test.d.ts +0 -2
  510. package/dist/core/subagent-store.test.d.ts.map +0 -1
  511. package/dist/core/subagent-store.test.js +0 -106
  512. package/dist/core/subagent-store.test.js.map +0 -1
  513. package/dist/core/subagent-turn-record.test.d.ts +0 -2
  514. package/dist/core/subagent-turn-record.test.d.ts.map +0 -1
  515. package/dist/core/subagent-turn-record.test.js +0 -70
  516. package/dist/core/subagent-turn-record.test.js.map +0 -1
  517. package/dist/core/verification-evidence.test.d.ts +0 -2
  518. package/dist/core/verification-evidence.test.d.ts.map +0 -1
  519. package/dist/core/verification-evidence.test.js +0 -103
  520. package/dist/core/verification-evidence.test.js.map +0 -1
  521. package/dist/core/verification-gate.test.d.ts +0 -2
  522. package/dist/core/verification-gate.test.d.ts.map +0 -1
  523. package/dist/core/verification-gate.test.js +0 -138
  524. package/dist/core/verification-gate.test.js.map +0 -1
  525. package/dist/core/verify-commands.test.d.ts +0 -2
  526. package/dist/core/verify-commands.test.d.ts.map +0 -1
  527. package/dist/core/verify-commands.test.js +0 -81
  528. package/dist/core/verify-commands.test.js.map +0 -1
  529. package/dist/core/workspace-guard.test.d.ts +0 -2
  530. package/dist/core/workspace-guard.test.d.ts.map +0 -1
  531. package/dist/core/workspace-guard.test.js +0 -107
  532. package/dist/core/workspace-guard.test.js.map +0 -1
  533. package/dist/hf-pull.test.d.ts +0 -2
  534. package/dist/hf-pull.test.d.ts.map +0 -1
  535. package/dist/hf-pull.test.js +0 -112
  536. package/dist/hf-pull.test.js.map +0 -1
  537. package/dist/modes/acp-mode.test.d.ts +0 -2
  538. package/dist/modes/acp-mode.test.d.ts.map +0 -1
  539. package/dist/modes/acp-mode.test.js +0 -1067
  540. package/dist/modes/acp-mode.test.js.map +0 -1
  541. package/dist/modes/json-mode.test.d.ts +0 -2
  542. package/dist/modes/json-mode.test.d.ts.map +0 -1
  543. package/dist/modes/json-mode.test.js +0 -84
  544. package/dist/modes/json-mode.test.js.map +0 -1
  545. package/dist/modes/subagent-worker-mode.test.d.ts +0 -2
  546. package/dist/modes/subagent-worker-mode.test.d.ts.map +0 -1
  547. package/dist/modes/subagent-worker-mode.test.js +0 -52
  548. package/dist/modes/subagent-worker-mode.test.js.map +0 -1
  549. package/dist/session.test.d.ts +0 -2
  550. package/dist/session.test.d.ts.map +0 -1
  551. package/dist/session.test.js +0 -35
  552. package/dist/session.test.js.map +0 -1
  553. package/dist/system-prompt.test.d.ts +0 -2
  554. package/dist/system-prompt.test.d.ts.map +0 -1
  555. package/dist/system-prompt.test.js +0 -684
  556. package/dist/system-prompt.test.js.map +0 -1
  557. package/dist/system-prompt.tiering.test.d.ts +0 -2
  558. package/dist/system-prompt.tiering.test.d.ts.map +0 -1
  559. package/dist/system-prompt.tiering.test.js +0 -112
  560. package/dist/system-prompt.tiering.test.js.map +0 -1
  561. package/dist/tools/bash.test.d.ts +0 -2
  562. package/dist/tools/bash.test.d.ts.map +0 -1
  563. package/dist/tools/bash.test.js +0 -248
  564. package/dist/tools/bash.test.js.map +0 -1
  565. package/dist/tools/checkpoint-hook.test.d.ts +0 -2
  566. package/dist/tools/checkpoint-hook.test.d.ts.map +0 -1
  567. package/dist/tools/checkpoint-hook.test.js +0 -65
  568. package/dist/tools/checkpoint-hook.test.js.map +0 -1
  569. package/dist/tools/code-nav.test.d.ts +0 -2
  570. package/dist/tools/code-nav.test.d.ts.map +0 -1
  571. package/dist/tools/code-nav.test.js +0 -136
  572. package/dist/tools/code-nav.test.js.map +0 -1
  573. package/dist/tools/code-skeleton.test.d.ts +0 -2
  574. package/dist/tools/code-skeleton.test.d.ts.map +0 -1
  575. package/dist/tools/code-skeleton.test.js +0 -115
  576. package/dist/tools/code-skeleton.test.js.map +0 -1
  577. package/dist/tools/compress-integration.test.d.ts +0 -2
  578. package/dist/tools/compress-integration.test.d.ts.map +0 -1
  579. package/dist/tools/compress-integration.test.js +0 -59
  580. package/dist/tools/compress-integration.test.js.map +0 -1
  581. package/dist/tools/compress.test.d.ts +0 -2
  582. package/dist/tools/compress.test.d.ts.map +0 -1
  583. package/dist/tools/compress.test.js +0 -60
  584. package/dist/tools/compress.test.js.map +0 -1
  585. package/dist/tools/edit-diff.test.d.ts +0 -2
  586. package/dist/tools/edit-diff.test.d.ts.map +0 -1
  587. package/dist/tools/edit-diff.test.js +0 -329
  588. package/dist/tools/edit-diff.test.js.map +0 -1
  589. package/dist/tools/edit-robustness.test.d.ts +0 -2
  590. package/dist/tools/edit-robustness.test.d.ts.map +0 -1
  591. package/dist/tools/edit-robustness.test.js +0 -417
  592. package/dist/tools/edit-robustness.test.js.map +0 -1
  593. package/dist/tools/edit.test.d.ts +0 -2
  594. package/dist/tools/edit.test.d.ts.map +0 -1
  595. package/dist/tools/edit.test.js +0 -1037
  596. package/dist/tools/edit.test.js.map +0 -1
  597. package/dist/tools/exit-plan.test.d.ts +0 -2
  598. package/dist/tools/exit-plan.test.d.ts.map +0 -1
  599. package/dist/tools/exit-plan.test.js +0 -72
  600. package/dist/tools/exit-plan.test.js.map +0 -1
  601. package/dist/tools/generate-image.test.d.ts +0 -2
  602. package/dist/tools/generate-image.test.d.ts.map +0 -1
  603. package/dist/tools/generate-image.test.js +0 -240
  604. package/dist/tools/generate-image.test.js.map +0 -1
  605. package/dist/tools/grep-parity.test.d.ts +0 -2
  606. package/dist/tools/grep-parity.test.d.ts.map +0 -1
  607. package/dist/tools/grep-parity.test.js +0 -92
  608. package/dist/tools/grep-parity.test.js.map +0 -1
  609. package/dist/tools/grep-recall.test.d.ts +0 -2
  610. package/dist/tools/grep-recall.test.d.ts.map +0 -1
  611. package/dist/tools/grep-recall.test.js +0 -114
  612. package/dist/tools/grep-recall.test.js.map +0 -1
  613. package/dist/tools/grep-spawn.test.d.ts +0 -2
  614. package/dist/tools/grep-spawn.test.d.ts.map +0 -1
  615. package/dist/tools/grep-spawn.test.js +0 -66
  616. package/dist/tools/grep-spawn.test.js.map +0 -1
  617. package/dist/tools/grep.test.d.ts +0 -2
  618. package/dist/tools/grep.test.d.ts.map +0 -1
  619. package/dist/tools/grep.test.js +0 -82
  620. package/dist/tools/grep.test.js.map +0 -1
  621. package/dist/tools/html-extract.test.d.ts +0 -2
  622. package/dist/tools/html-extract.test.d.ts.map +0 -1
  623. package/dist/tools/html-extract.test.js +0 -60
  624. package/dist/tools/html-extract.test.js.map +0 -1
  625. package/dist/tools/index.test.d.ts +0 -2
  626. package/dist/tools/index.test.d.ts.map +0 -1
  627. package/dist/tools/index.test.js +0 -55
  628. package/dist/tools/index.test.js.map +0 -1
  629. package/dist/tools/overflow.test.d.ts +0 -2
  630. package/dist/tools/overflow.test.d.ts.map +0 -1
  631. package/dist/tools/overflow.test.js +0 -59
  632. package/dist/tools/overflow.test.js.map +0 -1
  633. package/dist/tools/path-utils.test.d.ts +0 -2
  634. package/dist/tools/path-utils.test.d.ts.map +0 -1
  635. package/dist/tools/path-utils.test.js +0 -110
  636. package/dist/tools/path-utils.test.js.map +0 -1
  637. package/dist/tools/pdf-extract.test.d.ts +0 -2
  638. package/dist/tools/pdf-extract.test.d.ts.map +0 -1
  639. package/dist/tools/pdf-extract.test.js +0 -15
  640. package/dist/tools/pdf-extract.test.js.map +0 -1
  641. package/dist/tools/plan-mode.test.d.ts +0 -2
  642. package/dist/tools/plan-mode.test.d.ts.map +0 -1
  643. package/dist/tools/plan-mode.test.js +0 -91
  644. package/dist/tools/plan-mode.test.js.map +0 -1
  645. package/dist/tools/read-only-bash.test.d.ts +0 -2
  646. package/dist/tools/read-only-bash.test.d.ts.map +0 -1
  647. package/dist/tools/read-only-bash.test.js +0 -96
  648. package/dist/tools/read-only-bash.test.js.map +0 -1
  649. package/dist/tools/read.test.d.ts +0 -2
  650. package/dist/tools/read.test.d.ts.map +0 -1
  651. package/dist/tools/read.test.js +0 -146
  652. package/dist/tools/read.test.js.map +0 -1
  653. package/dist/tools/screenshot.test.d.ts +0 -2
  654. package/dist/tools/screenshot.test.d.ts.map +0 -1
  655. package/dist/tools/screenshot.test.js +0 -145
  656. package/dist/tools/screenshot.test.js.map +0 -1
  657. package/dist/tools/search-code.test.d.ts +0 -2
  658. package/dist/tools/search-code.test.d.ts.map +0 -1
  659. package/dist/tools/search-code.test.js +0 -83
  660. package/dist/tools/search-code.test.js.map +0 -1
  661. package/dist/tools/source-path.test.d.ts +0 -2
  662. package/dist/tools/source-path.test.d.ts.map +0 -1
  663. package/dist/tools/source-path.test.js +0 -80
  664. package/dist/tools/source-path.test.js.map +0 -1
  665. package/dist/tools/subagent-control.test.d.ts +0 -2
  666. package/dist/tools/subagent-control.test.d.ts.map +0 -1
  667. package/dist/tools/subagent-control.test.js +0 -73
  668. package/dist/tools/subagent-control.test.js.map +0 -1
  669. package/dist/tools/subagent-shared.test.d.ts +0 -2
  670. package/dist/tools/subagent-shared.test.d.ts.map +0 -1
  671. package/dist/tools/subagent-shared.test.js +0 -83
  672. package/dist/tools/subagent-shared.test.js.map +0 -1
  673. package/dist/tools/subagent.test.d.ts +0 -2
  674. package/dist/tools/subagent.test.d.ts.map +0 -1
  675. package/dist/tools/subagent.test.js +0 -180
  676. package/dist/tools/subagent.test.js.map +0 -1
  677. package/dist/tools/task-send.test.d.ts +0 -2
  678. package/dist/tools/task-send.test.d.ts.map +0 -1
  679. package/dist/tools/task-send.test.js +0 -104
  680. package/dist/tools/task-send.test.js.map +0 -1
  681. package/dist/tools/tool-catalog.test.d.ts +0 -2
  682. package/dist/tools/tool-catalog.test.d.ts.map +0 -1
  683. package/dist/tools/tool-catalog.test.js +0 -124
  684. package/dist/tools/tool-catalog.test.js.map +0 -1
  685. package/dist/tools/tool-search.test.d.ts +0 -2
  686. package/dist/tools/tool-search.test.d.ts.map +0 -1
  687. package/dist/tools/tool-search.test.js +0 -127
  688. package/dist/tools/tool-search.test.js.map +0 -1
  689. package/dist/tools/tool-tiers.test.d.ts +0 -2
  690. package/dist/tools/tool-tiers.test.d.ts.map +0 -1
  691. package/dist/tools/tool-tiers.test.js +0 -48
  692. package/dist/tools/tool-tiers.test.js.map +0 -1
  693. package/dist/tools/truncate-utils.test.d.ts +0 -2
  694. package/dist/tools/truncate-utils.test.d.ts.map +0 -1
  695. package/dist/tools/truncate-utils.test.js +0 -93
  696. package/dist/tools/truncate-utils.test.js.map +0 -1
  697. package/dist/tools/truncate.test.d.ts +0 -2
  698. package/dist/tools/truncate.test.d.ts.map +0 -1
  699. package/dist/tools/truncate.test.js +0 -100
  700. package/dist/tools/truncate.test.js.map +0 -1
  701. package/dist/tools/web-fetch.test.d.ts +0 -2
  702. package/dist/tools/web-fetch.test.d.ts.map +0 -1
  703. package/dist/tools/web-fetch.test.js +0 -692
  704. package/dist/tools/web-fetch.test.js.map +0 -1
  705. package/dist/tools/web-search.test.d.ts +0 -2
  706. package/dist/tools/web-search.test.d.ts.map +0 -1
  707. package/dist/tools/web-search.test.js +0 -333
  708. package/dist/tools/web-search.test.js.map +0 -1
  709. package/dist/tools/write.test.d.ts +0 -2
  710. package/dist/tools/write.test.d.ts.map +0 -1
  711. package/dist/tools/write.test.js +0 -176
  712. package/dist/tools/write.test.js.map +0 -1
  713. package/dist/ui/anchor-pad-reclaim.test.d.ts +0 -2
  714. package/dist/ui/anchor-pad-reclaim.test.d.ts.map +0 -1
  715. package/dist/ui/anchor-pad-reclaim.test.js +0 -146
  716. package/dist/ui/anchor-pad-reclaim.test.js.map +0 -1
  717. package/dist/ui/app-items.test.d.ts +0 -2
  718. package/dist/ui/app-items.test.d.ts.map +0 -1
  719. package/dist/ui/app-items.test.js +0 -54
  720. package/dist/ui/app-items.test.js.map +0 -1
  721. package/dist/ui/app-state-persistence.test.d.ts +0 -2
  722. package/dist/ui/app-state-persistence.test.d.ts.map +0 -1
  723. package/dist/ui/app-state-persistence.test.js +0 -71
  724. package/dist/ui/app-state-persistence.test.js.map +0 -1
  725. package/dist/ui/assistant-stream-flush-parity.test.d.ts +0 -2
  726. package/dist/ui/assistant-stream-flush-parity.test.d.ts.map +0 -1
  727. package/dist/ui/assistant-stream-flush-parity.test.js +0 -85
  728. package/dist/ui/assistant-stream-flush-parity.test.js.map +0 -1
  729. package/dist/ui/atomic-flush-regression.test.d.ts +0 -2
  730. package/dist/ui/atomic-flush-regression.test.d.ts.map +0 -1
  731. package/dist/ui/atomic-flush-regression.test.js +0 -218
  732. package/dist/ui/atomic-flush-regression.test.js.map +0 -1
  733. package/dist/ui/chat-layout-pinning.test.d.ts +0 -2
  734. package/dist/ui/chat-layout-pinning.test.d.ts.map +0 -1
  735. package/dist/ui/chat-layout-pinning.test.js +0 -525
  736. package/dist/ui/chat-layout-pinning.test.js.map +0 -1
  737. package/dist/ui/components/ActivityIndicator.test.d.ts +0 -2
  738. package/dist/ui/components/ActivityIndicator.test.d.ts.map +0 -1
  739. package/dist/ui/components/ActivityIndicator.test.js +0 -40
  740. package/dist/ui/components/ActivityIndicator.test.js.map +0 -1
  741. package/dist/ui/components/AssistantMessage.test.d.ts +0 -2
  742. package/dist/ui/components/AssistantMessage.test.d.ts.map +0 -1
  743. package/dist/ui/components/AssistantMessage.test.js +0 -418
  744. package/dist/ui/components/AssistantMessage.test.js.map +0 -1
  745. package/dist/ui/components/Footer.test.d.ts +0 -2
  746. package/dist/ui/components/Footer.test.d.ts.map +0 -1
  747. package/dist/ui/components/Footer.test.js +0 -16
  748. package/dist/ui/components/Footer.test.js.map +0 -1
  749. package/dist/ui/components/InputArea.test.d.ts +0 -2
  750. package/dist/ui/components/InputArea.test.d.ts.map +0 -1
  751. package/dist/ui/components/InputArea.test.js +0 -95
  752. package/dist/ui/components/InputArea.test.js.map +0 -1
  753. package/dist/ui/components/PlanOverlay.test.d.ts +0 -2
  754. package/dist/ui/components/PlanOverlay.test.d.ts.map +0 -1
  755. package/dist/ui/components/PlanOverlay.test.js +0 -24
  756. package/dist/ui/components/PlanOverlay.test.js.map +0 -1
  757. package/dist/ui/components/StreamingArea.test.d.ts +0 -2
  758. package/dist/ui/components/StreamingArea.test.d.ts.map +0 -1
  759. package/dist/ui/components/StreamingArea.test.js +0 -18
  760. package/dist/ui/components/StreamingArea.test.js.map +0 -1
  761. package/dist/ui/components/UserMessage.test.d.ts +0 -2
  762. package/dist/ui/components/UserMessage.test.d.ts.map +0 -1
  763. package/dist/ui/components/UserMessage.test.js +0 -39
  764. package/dist/ui/components/UserMessage.test.js.map +0 -1
  765. package/dist/ui/components/themed-output.test.d.ts +0 -2
  766. package/dist/ui/components/themed-output.test.d.ts.map +0 -1
  767. package/dist/ui/components/themed-output.test.js +0 -111
  768. package/dist/ui/components/themed-output.test.js.map +0 -1
  769. package/dist/ui/footer-jump-regression.test.d.ts +0 -2
  770. package/dist/ui/footer-jump-regression.test.d.ts.map +0 -1
  771. package/dist/ui/footer-jump-regression.test.js +0 -224
  772. package/dist/ui/footer-jump-regression.test.js.map +0 -1
  773. package/dist/ui/footer-status-layout.test.d.ts +0 -2
  774. package/dist/ui/footer-status-layout.test.d.ts.map +0 -1
  775. package/dist/ui/footer-status-layout.test.js +0 -71
  776. package/dist/ui/footer-status-layout.test.js.map +0 -1
  777. package/dist/ui/frame-clip-regression.test.d.ts +0 -2
  778. package/dist/ui/frame-clip-regression.test.d.ts.map +0 -1
  779. package/dist/ui/frame-clip-regression.test.js +0 -215
  780. package/dist/ui/frame-clip-regression.test.js.map +0 -1
  781. package/dist/ui/hooks/stall-retry-duplicate.test.d.ts +0 -2
  782. package/dist/ui/hooks/stall-retry-duplicate.test.d.ts.map +0 -1
  783. package/dist/ui/hooks/stall-retry-duplicate.test.js +0 -94
  784. package/dist/ui/hooks/stall-retry-duplicate.test.js.map +0 -1
  785. package/dist/ui/hooks/useAgentLoop.test.d.ts +0 -2
  786. package/dist/ui/hooks/useAgentLoop.test.d.ts.map +0 -1
  787. package/dist/ui/hooks/useAgentLoop.test.js +0 -66
  788. package/dist/ui/hooks/useAgentLoop.test.js.map +0 -1
  789. package/dist/ui/hooks/useContextCompaction.test.d.ts +0 -2
  790. package/dist/ui/hooks/useContextCompaction.test.d.ts.map +0 -1
  791. package/dist/ui/hooks/useContextCompaction.test.js +0 -135
  792. package/dist/ui/hooks/useContextCompaction.test.js.map +0 -1
  793. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts +0 -2
  794. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts.map +0 -1
  795. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js +0 -81
  796. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js.map +0 -1
  797. package/dist/ui/hooks/useTranscriptHistory.test.d.ts +0 -2
  798. package/dist/ui/hooks/useTranscriptHistory.test.d.ts.map +0 -1
  799. package/dist/ui/hooks/useTranscriptHistory.test.js +0 -61
  800. package/dist/ui/hooks/useTranscriptHistory.test.js.map +0 -1
  801. package/dist/ui/hooks/useTranscriptScroll.test.d.ts +0 -2
  802. package/dist/ui/hooks/useTranscriptScroll.test.d.ts.map +0 -1
  803. package/dist/ui/hooks/useTranscriptScroll.test.js +0 -119
  804. package/dist/ui/hooks/useTranscriptScroll.test.js.map +0 -1
  805. package/dist/ui/live-area-clamp.test.d.ts +0 -2
  806. package/dist/ui/live-area-clamp.test.d.ts.map +0 -1
  807. package/dist/ui/live-area-clamp.test.js +0 -125
  808. package/dist/ui/live-area-clamp.test.js.map +0 -1
  809. package/dist/ui/live-area-height.test.d.ts +0 -2
  810. package/dist/ui/live-area-height.test.d.ts.map +0 -1
  811. package/dist/ui/live-area-height.test.js +0 -76
  812. package/dist/ui/live-area-height.test.js.map +0 -1
  813. package/dist/ui/live-frame-height.test.d.ts +0 -2
  814. package/dist/ui/live-frame-height.test.d.ts.map +0 -1
  815. package/dist/ui/live-frame-height.test.js +0 -116
  816. package/dist/ui/live-frame-height.test.js.map +0 -1
  817. package/dist/ui/live-item-flush.test.d.ts +0 -2
  818. package/dist/ui/live-item-flush.test.d.ts.map +0 -1
  819. package/dist/ui/live-item-flush.test.js +0 -437
  820. package/dist/ui/live-item-flush.test.js.map +0 -1
  821. package/dist/ui/long-prompt-regression-harness.test.d.ts +0 -2
  822. package/dist/ui/long-prompt-regression-harness.test.d.ts.map +0 -1
  823. package/dist/ui/long-prompt-regression-harness.test.js +0 -195
  824. package/dist/ui/long-prompt-regression-harness.test.js.map +0 -1
  825. package/dist/ui/plan-overlay.test.d.ts +0 -2
  826. package/dist/ui/plan-overlay.test.d.ts.map +0 -1
  827. package/dist/ui/plan-overlay.test.js +0 -305
  828. package/dist/ui/plan-overlay.test.js.map +0 -1
  829. package/dist/ui/queued-message.test.d.ts +0 -2
  830. package/dist/ui/queued-message.test.d.ts.map +0 -1
  831. package/dist/ui/queued-message.test.js +0 -172
  832. package/dist/ui/queued-message.test.js.map +0 -1
  833. package/dist/ui/render.test.d.ts +0 -2
  834. package/dist/ui/render.test.d.ts.map +0 -1
  835. package/dist/ui/render.test.js +0 -42
  836. package/dist/ui/render.test.js.map +0 -1
  837. package/dist/ui/scroll-stabilization.test.d.ts +0 -2
  838. package/dist/ui/scroll-stabilization.test.d.ts.map +0 -1
  839. package/dist/ui/scroll-stabilization.test.js +0 -64
  840. package/dist/ui/scroll-stabilization.test.js.map +0 -1
  841. package/dist/ui/slash-command-images.test.d.ts +0 -2
  842. package/dist/ui/slash-command-images.test.d.ts.map +0 -1
  843. package/dist/ui/slash-command-images.test.js +0 -110
  844. package/dist/ui/slash-command-images.test.js.map +0 -1
  845. package/dist/ui/startup-banner-order.test.d.ts +0 -2
  846. package/dist/ui/startup-banner-order.test.d.ts.map +0 -1
  847. package/dist/ui/startup-banner-order.test.js +0 -105
  848. package/dist/ui/startup-banner-order.test.js.map +0 -1
  849. package/dist/ui/stores/transcript-scroll-store.test.d.ts +0 -2
  850. package/dist/ui/stores/transcript-scroll-store.test.d.ts.map +0 -1
  851. package/dist/ui/stores/transcript-scroll-store.test.js +0 -73
  852. package/dist/ui/stores/transcript-scroll-store.test.js.map +0 -1
  853. package/dist/ui/streaming-flush-bounce.test.d.ts +0 -2
  854. package/dist/ui/streaming-flush-bounce.test.d.ts.map +0 -1
  855. package/dist/ui/streaming-flush-bounce.test.js +0 -177
  856. package/dist/ui/streaming-flush-bounce.test.js.map +0 -1
  857. package/dist/ui/submit-boundary-gap.test.d.ts +0 -2
  858. package/dist/ui/submit-boundary-gap.test.d.ts.map +0 -1
  859. package/dist/ui/submit-boundary-gap.test.js +0 -180
  860. package/dist/ui/submit-boundary-gap.test.js.map +0 -1
  861. package/dist/ui/terminal-history-repaint.test.d.ts +0 -2
  862. package/dist/ui/terminal-history-repaint.test.d.ts.map +0 -1
  863. package/dist/ui/terminal-history-repaint.test.js +0 -73
  864. package/dist/ui/terminal-history-repaint.test.js.map +0 -1
  865. package/dist/ui/terminal-history-retry-dedup.test.d.ts +0 -2
  866. package/dist/ui/terminal-history-retry-dedup.test.d.ts.map +0 -1
  867. package/dist/ui/terminal-history-retry-dedup.test.js +0 -61
  868. package/dist/ui/terminal-history-retry-dedup.test.js.map +0 -1
  869. package/dist/ui/terminal-history.test.d.ts +0 -2
  870. package/dist/ui/terminal-history.test.d.ts.map +0 -1
  871. package/dist/ui/terminal-history.test.js +0 -557
  872. package/dist/ui/terminal-history.test.js.map +0 -1
  873. package/dist/ui/thinking-level-cycle.test.d.ts +0 -2
  874. package/dist/ui/thinking-level-cycle.test.d.ts.map +0 -1
  875. package/dist/ui/thinking-level-cycle.test.js +0 -45
  876. package/dist/ui/thinking-level-cycle.test.js.map +0 -1
  877. package/dist/ui/tool-group-summary.test.d.ts +0 -2
  878. package/dist/ui/tool-group-summary.test.d.ts.map +0 -1
  879. package/dist/ui/tool-group-summary.test.js +0 -79
  880. package/dist/ui/tool-group-summary.test.js.map +0 -1
  881. package/dist/ui/transcript/spacing.test.d.ts +0 -2
  882. package/dist/ui/transcript/spacing.test.d.ts.map +0 -1
  883. package/dist/ui/transcript/spacing.test.js +0 -187
  884. package/dist/ui/transcript/spacing.test.js.map +0 -1
  885. package/dist/ui/transcript/transcript-lines.test.d.ts +0 -2
  886. package/dist/ui/transcript/transcript-lines.test.d.ts.map +0 -1
  887. package/dist/ui/transcript/transcript-lines.test.js +0 -76
  888. package/dist/ui/transcript/transcript-lines.test.js.map +0 -1
  889. package/dist/ui/transcript-viewport-pinning.test.d.ts +0 -2
  890. package/dist/ui/transcript-viewport-pinning.test.d.ts.map +0 -1
  891. package/dist/ui/transcript-viewport-pinning.test.js +0 -148
  892. package/dist/ui/transcript-viewport-pinning.test.js.map +0 -1
  893. package/dist/ui/tui-history-parity.test.d.ts +0 -2
  894. package/dist/ui/tui-history-parity.test.d.ts.map +0 -1
  895. package/dist/ui/tui-history-parity.test.js +0 -369
  896. package/dist/ui/tui-history-parity.test.js.map +0 -1
  897. package/dist/ui/tui-simulation.test.d.ts +0 -2
  898. package/dist/ui/tui-simulation.test.d.ts.map +0 -1
  899. package/dist/ui/tui-simulation.test.js +0 -145
  900. package/dist/ui/tui-simulation.test.js.map +0 -1
  901. package/dist/ui/tui-terminal-recorder.test.d.ts +0 -2
  902. package/dist/ui/tui-terminal-recorder.test.d.ts.map +0 -1
  903. package/dist/ui/tui-terminal-recorder.test.js +0 -134
  904. package/dist/ui/tui-terminal-recorder.test.js.map +0 -1
  905. package/dist/ui/utils/assistant-stream-split.test.d.ts +0 -2
  906. package/dist/ui/utils/assistant-stream-split.test.d.ts.map +0 -1
  907. package/dist/ui/utils/assistant-stream-split.test.js +0 -74
  908. package/dist/ui/utils/assistant-stream-split.test.js.map +0 -1
  909. package/dist/ui/utils/markdown-renderer.test.d.ts +0 -2
  910. package/dist/ui/utils/markdown-renderer.test.d.ts.map +0 -1
  911. package/dist/ui/utils/markdown-renderer.test.js +0 -63
  912. package/dist/ui/utils/markdown-renderer.test.js.map +0 -1
  913. package/dist/ui/utils/table-text.test.d.ts +0 -2
  914. package/dist/ui/utils/table-text.test.d.ts.map +0 -1
  915. package/dist/ui/utils/table-text.test.js +0 -202
  916. package/dist/ui/utils/table-text.test.js.map +0 -1
  917. package/dist/ui/utils/terminal-graphics.test.d.ts +0 -2
  918. package/dist/ui/utils/terminal-graphics.test.d.ts.map +0 -1
  919. package/dist/ui/utils/terminal-graphics.test.js +0 -100
  920. package/dist/ui/utils/terminal-graphics.test.js.map +0 -1
  921. package/dist/ui/utils/terminal-input.test.d.ts +0 -2
  922. package/dist/ui/utils/terminal-input.test.d.ts.map +0 -1
  923. package/dist/ui/utils/terminal-input.test.js +0 -15
  924. package/dist/ui/utils/terminal-input.test.js.map +0 -1
  925. package/dist/ui/utils/text-utils.test.d.ts +0 -2
  926. package/dist/ui/utils/text-utils.test.d.ts.map +0 -1
  927. package/dist/ui/utils/text-utils.test.js +0 -31
  928. package/dist/ui/utils/text-utils.test.js.map +0 -1
  929. package/dist/utils/git.test.d.ts +0 -2
  930. package/dist/utils/git.test.d.ts.map +0 -1
  931. package/dist/utils/git.test.js +0 -36
  932. package/dist/utils/git.test.js.map +0 -1
  933. package/dist/utils/github.test.d.ts +0 -2
  934. package/dist/utils/github.test.d.ts.map +0 -1
  935. package/dist/utils/github.test.js +0 -23
  936. package/dist/utils/github.test.js.map +0 -1
  937. package/dist/utils/http-body.test.d.ts +0 -2
  938. package/dist/utils/http-body.test.d.ts.map +0 -1
  939. package/dist/utils/http-body.test.js +0 -68
  940. package/dist/utils/http-body.test.js.map +0 -1
  941. package/dist/utils/image.test.d.ts +0 -2
  942. package/dist/utils/image.test.d.ts.map +0 -1
  943. package/dist/utils/image.test.js +0 -139
  944. package/dist/utils/image.test.js.map +0 -1
  945. package/dist/utils/plan-steps.test.d.ts +0 -2
  946. package/dist/utils/plan-steps.test.d.ts.map +0 -1
  947. package/dist/utils/plan-steps.test.js +0 -159
  948. package/dist/utils/plan-steps.test.js.map +0 -1
  949. package/dist/utils/process.test.d.ts +0 -2
  950. package/dist/utils/process.test.d.ts.map +0 -1
  951. package/dist/utils/process.test.js +0 -42
  952. package/dist/utils/process.test.js.map +0 -1
  953. package/dist/utils/text.test.d.ts +0 -2
  954. package/dist/utils/text.test.d.ts.map +0 -1
  955. package/dist/utils/text.test.js +0 -91
  956. package/dist/utils/text.test.js.map +0 -1
@@ -1,1037 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { prettifyError } from "zod";
3
- import fs from "node:fs/promises";
4
- import path from "node:path";
5
- import os from "node:os";
6
- import { createEditTool } from "./edit.js";
7
- import { lineHash } from "../core/hashline.js";
8
- import { recordRead } from "./read-tracker.js";
9
- function resultToString(result) {
10
- if (typeof result === "string")
11
- return result;
12
- if (result && typeof result === "object" && "details" in result) {
13
- const details = result.details;
14
- return details?.diff ?? "";
15
- }
16
- return "";
17
- }
18
- async function markRead(tracker, filePath) {
19
- const stat = await fs.stat(filePath);
20
- const content = await fs.readFile(filePath, "utf-8");
21
- recordRead(tracker, filePath, content, stat.mtimeMs);
22
- }
23
- describe("createEditTool", () => {
24
- let tmpDir;
25
- beforeEach(async () => {
26
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "edit-test-"));
27
- });
28
- afterEach(async () => {
29
- await fs.rm(tmpDir, { recursive: true, force: true });
30
- });
31
- it("opts into sequential agent-loop execution", () => {
32
- const tool = createEditTool(tmpDir);
33
- expect(tool.executionMode).toBe("sequential");
34
- });
35
- it("replaces exact text and returns a diff", async () => {
36
- const filePath = path.join(tmpDir, "hello.txt");
37
- await fs.writeFile(filePath, "hello world\n");
38
- const tool = createEditTool(tmpDir);
39
- const result = await tool.execute({ file_path: "hello.txt", edits: [{ old_text: "hello", new_text: "goodbye" }] }, { signal: new AbortController().signal, toolCallId: "test-1" });
40
- const diff = resultToString(result);
41
- expect(diff).toContain("-hello world");
42
- expect(diff).toContain("+goodbye world");
43
- const written = await fs.readFile(filePath, "utf-8");
44
- expect(written).toBe("goodbye world\n");
45
- });
46
- it("applies multiple edits sequentially", async () => {
47
- const filePath = path.join(tmpDir, "multi.txt");
48
- await fs.writeFile(filePath, "alpha\nbeta\ngamma\n");
49
- const tool = createEditTool(tmpDir);
50
- const result = await tool.execute({
51
- file_path: "multi.txt",
52
- edits: [
53
- { old_text: "alpha", new_text: "ALPHA" },
54
- { old_text: "gamma", new_text: "GAMMA" },
55
- ],
56
- }, { signal: new AbortController().signal, toolCallId: "test-multi" });
57
- const summary = typeof result === "string" ? result : result.content;
58
- expect(summary).toContain("2 edits");
59
- const written = await fs.readFile(filePath, "utf-8");
60
- expect(written).toBe("ALPHA\nbeta\nGAMMA\n");
61
- });
62
- it("supports chained edits where later edits depend on earlier ones", async () => {
63
- const filePath = path.join(tmpDir, "chain.txt");
64
- await fs.writeFile(filePath, "foo\n");
65
- const tool = createEditTool(tmpDir);
66
- await tool.execute({
67
- file_path: "chain.txt",
68
- edits: [
69
- { old_text: "foo", new_text: "bar" },
70
- { old_text: "bar", new_text: "baz" },
71
- ],
72
- }, { signal: new AbortController().signal, toolCallId: "test-chain" });
73
- const written = await fs.readFile(filePath, "utf-8");
74
- expect(written).toBe("baz\n");
75
- });
76
- it("reports edit index on failure within a multi-edit batch (atomic mode)", async () => {
77
- const filePath = path.join(tmpDir, "batch.txt");
78
- await fs.writeFile(filePath, "one two three\n");
79
- const tool = createEditTool(tmpDir);
80
- await expect(tool.execute({
81
- file_path: "batch.txt",
82
- edits: [
83
- { old_text: "one", new_text: "1" },
84
- { old_text: "missing", new_text: "x" },
85
- ],
86
- atomic: true,
87
- }, { signal: new AbortController().signal, toolCallId: "test-batch" })).rejects.toThrow(/edit 2\/2/);
88
- // Nothing should have been written — atomic
89
- const written = await fs.readFile(filePath, "utf-8");
90
- expect(written).toBe("one two three\n");
91
- });
92
- it("throws when file hasn't been read with readFiles tracking", async () => {
93
- const filePath = path.join(tmpDir, "unread.txt");
94
- await fs.writeFile(filePath, "content\n");
95
- const readFiles = new Map();
96
- const tool = createEditTool(tmpDir, readFiles);
97
- await expect(tool.execute({ file_path: "unread.txt", edits: [{ old_text: "content", new_text: "new" }] }, { signal: new AbortController().signal, toolCallId: "test-3" })).rejects.toThrow("File must be read first");
98
- });
99
- it("allows edit when file is in readFiles tracker", async () => {
100
- const filePath = path.join(tmpDir, "tracked.txt");
101
- await fs.writeFile(filePath, "alpha beta\n");
102
- const readFiles = new Map();
103
- await markRead(readFiles, filePath);
104
- const tool = createEditTool(tmpDir, readFiles);
105
- const result = await tool.execute({ file_path: "tracked.txt", edits: [{ old_text: "alpha", new_text: "gamma" }] }, { signal: new AbortController().signal, toolCallId: "test-4" });
106
- const diff = resultToString(result);
107
- expect(diff).toContain("-alpha beta");
108
- expect(diff).toContain("+gamma beta");
109
- const written = await fs.readFile(filePath, "utf-8");
110
- expect(written).toBe("gamma beta\n");
111
- });
112
- it("rejects edit when the file changed since it was read", async () => {
113
- const filePath = path.join(tmpDir, "stale.txt");
114
- await fs.writeFile(filePath, "alpha\n");
115
- const readFiles = new Map();
116
- await markRead(readFiles, filePath);
117
- // Simulate an external formatter rewriting the file (different bytes,
118
- // forced bumped mtime so the fast-path mtime check trips).
119
- await fs.writeFile(filePath, "ALPHA\n");
120
- const future = new Date(Date.now() + 5_000);
121
- await fs.utimes(filePath, future, future);
122
- const tool = createEditTool(tmpDir, readFiles);
123
- await expect(tool.execute({ file_path: "stale.txt", edits: [{ old_text: "ALPHA", new_text: "beta" }] }, { signal: new AbortController().signal, toolCallId: "test-stale" })).rejects.toThrow(/modified since/);
124
- });
125
- it("allows consecutive edits without re-reading (recordWrite refreshes)", async () => {
126
- const filePath = path.join(tmpDir, "consec.txt");
127
- await fs.writeFile(filePath, "one\ntwo\n");
128
- const readFiles = new Map();
129
- await markRead(readFiles, filePath);
130
- const tool = createEditTool(tmpDir, readFiles);
131
- await tool.execute({ file_path: "consec.txt", edits: [{ old_text: "one", new_text: "ONE" }] }, { signal: new AbortController().signal, toolCallId: "test-consec-1" });
132
- // Second edit must succeed without an explicit re-read.
133
- await tool.execute({ file_path: "consec.txt", edits: [{ old_text: "two", new_text: "TWO" }] }, { signal: new AbortController().signal, toolCallId: "test-consec-2" });
134
- const written = await fs.readFile(filePath, "utf-8");
135
- expect(written).toBe("ONE\nTWO\n");
136
- });
137
- it("appends a 'Closest match' snippet when old_text is not found", async () => {
138
- const filePath = path.join(tmpDir, "snippet.txt");
139
- await fs.writeFile(filePath, "import { useState } from 'react';\n\nexport function Counter() {\n const [count, setCount] = useState(0);\n return <div>{count}</div>;\n}\n");
140
- const tool = createEditTool(tmpDir);
141
- await expect(tool.execute({
142
- file_path: "snippet.txt",
143
- edits: [
144
- {
145
- old_text: "const [count, setCount] = useState(1);",
146
- new_text: "const [count, setCount] = useState(2);",
147
- },
148
- ],
149
- }, { signal: new AbortController().signal, toolCallId: "test-snippet" })).rejects.toThrow(/Closest match in file:[\s\S]*useState\(0\)/);
150
- });
151
- it("suggests a bounded re-read around the closest match when not_found", async () => {
152
- const filePath = path.join(tmpDir, "rehint.txt");
153
- await fs.writeFile(filePath, "import { useState } from 'react';\n\nexport function Counter() {\n const [count, setCount] = useState(0);\n return <div>{count}</div>;\n}\n");
154
- const tool = createEditTool(tmpDir);
155
- // The closest match is on line 4. With ±25 lines, offset clamps to 1 and
156
- // limit stays at 50. The hint uses the same file_path the model passed.
157
- await expect(tool.execute({
158
- file_path: "rehint.txt",
159
- edits: [
160
- {
161
- old_text: "const [count, setCount] = useState(1);",
162
- new_text: "const [count, setCount] = useState(2);",
163
- },
164
- ],
165
- }, { signal: new AbortController().signal, toolCallId: "rehint-1" })).rejects.toThrow(/Suggested re-read: `read file_path="rehint\.txt" offset=1 limit=50`/);
166
- });
167
- it("aggregates multiple edit failures into one error (atomic mode)", async () => {
168
- const filePath = path.join(tmpDir, "agg.txt");
169
- await fs.writeFile(filePath, "alpha\nbeta\ngamma\n");
170
- const tool = createEditTool(tmpDir);
171
- await expect(tool.execute({
172
- file_path: "agg.txt",
173
- edits: [
174
- { old_text: "alpha", new_text: "ALPHA" },
175
- { old_text: "MISSING", new_text: "X" },
176
- { old_text: "ALSO_MISSING", new_text: "Y" },
177
- ],
178
- atomic: true,
179
- }, { signal: new AbortController().signal, toolCallId: "test-agg" })).rejects.toThrow(/2 of 3 edits failed[\s\S]*edit 2\/3[\s\S]*edit 3\/3/);
180
- // Atomic — nothing written.
181
- const written = await fs.readFile(filePath, "utf-8");
182
- expect(written).toBe("alpha\nbeta\ngamma\n");
183
- });
184
- it("partial-apply (default): keeps successful edits and reports failures for retry", async () => {
185
- const filePath = path.join(tmpDir, "partial.txt");
186
- await fs.writeFile(filePath, "alpha\nbeta\ngamma\n");
187
- const tool = createEditTool(tmpDir);
188
- const result = await tool.execute({
189
- file_path: "partial.txt",
190
- edits: [
191
- { old_text: "alpha", new_text: "ALPHA" },
192
- { old_text: "MISSING", new_text: "X" },
193
- { old_text: "gamma", new_text: "GAMMA" },
194
- ],
195
- }, { signal: new AbortController().signal, toolCallId: "test-partial" });
196
- const summary = typeof result === "string" ? result : result.content;
197
- expect(summary).toMatch(/Applied 2 of 3 edits/);
198
- expect(summary).toMatch(/re-issue ONLY these/);
199
- expect(summary).toMatch(/edit 2\/3/);
200
- expect(summary).not.toMatch(/edit 1\/3/);
201
- expect(summary).not.toMatch(/edit 3\/3/);
202
- // Successful edits landed; failed one didn't.
203
- const written = await fs.readFile(filePath, "utf-8");
204
- expect(written).toBe("ALPHA\nbeta\nGAMMA\n");
205
- });
206
- it("partial-apply on a 19-edit batch with 2 failures lands the other 17", async () => {
207
- const filePath = path.join(tmpDir, "big.css");
208
- const lines = Array.from({ length: 19 }, (_, i) => `.cls${i} { color: red; }`);
209
- await fs.writeFile(filePath, lines.join("\n") + "\n");
210
- const edits = lines.map((line, i) => ({
211
- old_text: line,
212
- // Two of them deliberately drift so the batch sees real failures.
213
- new_text: i === 7 || i === 13
214
- ? line.replace("color: red", "color: blue")
215
- : line.replace("red", "green"),
216
- }));
217
- // Corrupt edits 8 (index 7) and 14 (index 13) by paraphrasing old_text.
218
- edits[7] = { old_text: ".cls7 { colour: red; }", new_text: ".cls7 { color: blue; }" };
219
- edits[13] = { old_text: ".cls13 { colur: red; }", new_text: ".cls13 { color: blue; }" };
220
- const tool = createEditTool(tmpDir);
221
- const result = await tool.execute({ file_path: "big.css", edits }, { signal: new AbortController().signal, toolCallId: "test-19" });
222
- const summary = typeof result === "string" ? result : result.content;
223
- expect(summary).toMatch(/Applied 17 of 19 edits/);
224
- expect(summary).toMatch(/edit 8\/19/);
225
- expect(summary).toMatch(/edit 14\/19/);
226
- const written = await fs.readFile(filePath, "utf-8");
227
- // 17 lines should have green, the two failed ones still red.
228
- expect((written.match(/color: green/g) ?? []).length).toBe(17);
229
- expect(written).toContain(".cls7 { color: red; }");
230
- expect(written).toContain(".cls13 { color: red; }");
231
- });
232
- it("throws when every edit fails even in partial-apply mode", async () => {
233
- const filePath = path.join(tmpDir, "all-fail.txt");
234
- await fs.writeFile(filePath, "untouched\n");
235
- const tool = createEditTool(tmpDir);
236
- await expect(tool.execute({
237
- file_path: "all-fail.txt",
238
- edits: [
239
- { old_text: "MISSING1", new_text: "X" },
240
- { old_text: "MISSING2", new_text: "Y" },
241
- ],
242
- }, { signal: new AbortController().signal, toolCallId: "test-all-fail" })).rejects.toThrow(/2 of 2 edits failed/);
243
- const written = await fs.readFile(filePath, "utf-8");
244
- expect(written).toBe("untouched\n");
245
- });
246
- it("suppresses Closest-match snippet in partial-apply when other edits succeeded", async () => {
247
- // Mirrors the StartingYourAgency.tsx scenario: token-heavy lines where
248
- // findClosestSnippet returns noisy top-of-file regions. When 17 of 19
249
- // edits succeed, those diffs already give the model context — the
250
- // snippet would just be noise.
251
- const filePath = path.join(tmpDir, "noisy.tsx");
252
- const lines = Array.from({ length: 6 }, (_, i) => ` <div className="card-${i}">Section ${i}</div>`);
253
- await fs.writeFile(filePath, lines.join("\n") + "\n");
254
- const edits = lines.map((line) => ({
255
- old_text: line,
256
- new_text: line.replace("card-", "glass-card-"),
257
- }));
258
- // Drift edit 4 — paraphrase the case so it doesn't match.
259
- edits[3] = {
260
- old_text: ` <div className="Card-3">Section 3</div>`,
261
- new_text: ` <div className="glass-card-3">Section 3</div>`,
262
- };
263
- const tool = createEditTool(tmpDir);
264
- const result = await tool.execute({ file_path: "noisy.tsx", edits }, { signal: new AbortController().signal, toolCallId: "test-suppress" });
265
- const summary = typeof result === "string" ? result : result.content;
266
- expect(summary).toMatch(/Applied 5 of 6/);
267
- expect(summary).toMatch(/edit 4\/6/);
268
- // The snippet would be ~3-7 lines starting with "Closest match in file:".
269
- // In partial-apply with successes, we suppress it.
270
- expect(summary).not.toMatch(/Closest match in file:/);
271
- });
272
- it("keeps Closest-match snippet when no other edit succeeded", async () => {
273
- // Single-edit call — no surrounding context for the model, so the snippet
274
- // is genuinely useful and must remain. Use overlapping tokens so the
275
- // closest-snippet heuristic actually fires.
276
- const filePath = path.join(tmpDir, "lonely.tsx");
277
- await fs.writeFile(filePath, "function Counter() {\n const [count, setCount] = useState(0);\n return count;\n}\n");
278
- const tool = createEditTool(tmpDir);
279
- await expect(tool.execute({
280
- file_path: "lonely.tsx",
281
- edits: [
282
- {
283
- old_text: "const [count, setCount] = useState(1);",
284
- new_text: "const [count, setCount] = useState(2);",
285
- },
286
- ],
287
- }, { signal: new AbortController().signal, toolCallId: "test-keep" })).rejects.toThrow(/Closest match in file:[\s\S]*useState\(0\)/);
288
- });
289
- it("atomic mode keeps Closest-match snippet (model retries against unchanged file)", async () => {
290
- const filePath = path.join(tmpDir, "atomic-snippet.tsx");
291
- const lines = Array.from({ length: 4 }, (_, i) => ` <div className="card-${i}">Section ${i}</div>`);
292
- await fs.writeFile(filePath, lines.join("\n") + "\n");
293
- const edits = lines.map((line) => ({
294
- old_text: line,
295
- new_text: line.replace("card-", "glass-card-"),
296
- }));
297
- edits[2] = {
298
- old_text: ` <div className="Card-2">Section 2</div>`,
299
- new_text: ` <div className="glass-card-2">Section 2</div>`,
300
- };
301
- const tool = createEditTool(tmpDir);
302
- await expect(tool.execute({ file_path: "atomic-snippet.tsx", edits, atomic: true }, { signal: new AbortController().signal, toolCallId: "test-atomic-snippet" })).rejects.toThrow(/Closest match in file:/);
303
- });
304
- it("indent-flex: model omits indentation entirely; file has 4-space prefix — applies it", async () => {
305
- const filePath = path.join(tmpDir, "indent.ts");
306
- await fs.writeFile(filePath, " const x = 1;\n const y = 2;\n const z = 3;\n");
307
- const tool = createEditTool(tmpDir);
308
- const result = await tool.execute({
309
- file_path: "indent.ts",
310
- edits: [
311
- {
312
- old_text: "const x = 1;\nconst y = 2;\nconst z = 3;",
313
- new_text: "const x = 10;\nconst y = 20;\nconst z = 30;",
314
- },
315
- ],
316
- }, { signal: new AbortController().signal, toolCallId: "test-indent-flex" });
317
- const summary = typeof result === "string" ? result : result.content;
318
- expect(summary).toMatch(/Successfully/);
319
- const written = await fs.readFile(filePath, "utf-8");
320
- expect(written).toBe(" const x = 10;\n const y = 20;\n const z = 30;\n");
321
- });
322
- it("indent-flex: model used 2-space but file uses 4 — outdents both, re-indents new", async () => {
323
- const filePath = path.join(tmpDir, "mixed-indent.ts");
324
- await fs.writeFile(filePath, " if (x) {\n return y;\n }\n");
325
- const tool = createEditTool(tmpDir);
326
- const result = await tool.execute({
327
- file_path: "mixed-indent.ts",
328
- edits: [
329
- {
330
- old_text: " if (x) {\n return y;\n }",
331
- new_text: " if (x) {\n return z;\n }",
332
- },
333
- ],
334
- }, { signal: new AbortController().signal, toolCallId: "test-mixed-indent" });
335
- const summary = typeof result === "string" ? result : result.content;
336
- expect(summary).toMatch(/Successfully/);
337
- const written = await fs.readFile(filePath, "utf-8");
338
- expect(written).toBe(" if (x) {\n return z;\n }\n");
339
- });
340
- it("dotdotdots: model elides middle with `...`, edit lands and middle preserved", async () => {
341
- const filePath = path.join(tmpDir, "elide.ts");
342
- await fs.writeFile(filePath, [
343
- "function pomodoro() {",
344
- " const timer = startTimer();",
345
- " trackPomodoro(timer);",
346
- " scheduleBreak();",
347
- " return timer;",
348
- "}",
349
- ].join("\n") + "\n");
350
- const tool = createEditTool(tmpDir);
351
- const result = await tool.execute({
352
- file_path: "elide.ts",
353
- edits: [
354
- {
355
- old_text: "function pomodoro() {\n ...\n return timer;\n}",
356
- new_text: "function pomodoro(): Timer {\n ...\n return timer;\n}",
357
- },
358
- ],
359
- }, { signal: new AbortController().signal, toolCallId: "test-elide" });
360
- const summary = typeof result === "string" ? result : result.content;
361
- expect(summary).toMatch(/Successfully/);
362
- const written = await fs.readFile(filePath, "utf-8");
363
- expect(written).toContain("function pomodoro(): Timer {");
364
- // Middle preserved verbatim.
365
- expect(written).toContain("trackPomodoro(timer);");
366
- expect(written).toContain("scheduleBreak();");
367
- });
368
- it("dotdotdots: model can omit common indentation from elided bookends", async () => {
369
- const filePath = path.join(tmpDir, "elide-indent.ts");
370
- await fs.writeFile(filePath, " function pomodoro() {\n trackPomodoro(timer);\n return timer;\n }\n");
371
- const tool = createEditTool(tmpDir);
372
- await tool.execute({
373
- file_path: "elide-indent.ts",
374
- edits: [
375
- {
376
- old_text: "function pomodoro() {\n ...\n return timer;\n}",
377
- new_text: "function pomodoro(): Timer {\n ...\n return timer;\n}",
378
- },
379
- ],
380
- }, { signal: new AbortController().signal, toolCallId: "test-elide-indent" });
381
- const written = await fs.readFile(filePath, "utf-8");
382
- expect(written).toBe(" function pomodoro(): Timer {\n trackPomodoro(timer);\n return timer;\n }\n");
383
- });
384
- it("dotdotdots: failed elision falls through to standard not_found error", async () => {
385
- const filePath = path.join(tmpDir, "elide-fail.ts");
386
- await fs.writeFile(filePath, "function actuallyExists() { return 1; }\n");
387
- const tool = createEditTool(tmpDir);
388
- await expect(tool.execute({
389
- file_path: "elide-fail.ts",
390
- edits: [
391
- {
392
- // Bookends don't exist in the file.
393
- old_text: "function nonexistent() {\n ...\n return x;\n}",
394
- new_text: "function nonexistent() {\n ...\n return y;\n}",
395
- },
396
- ],
397
- }, { signal: new AbortController().signal, toolCallId: "test-elide-fail" })).rejects.toThrow(/old_text not found/);
398
- });
399
- it("treats no-op edits where old_text equals new_text as successful no-ops", async () => {
400
- const filePath = path.join(tmpDir, "noop.txt");
401
- await fs.writeFile(filePath, "hello world\n");
402
- const mutated = [];
403
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, (mutatedPath) => {
404
- mutated.push(mutatedPath);
405
- });
406
- const result = await tool.execute({
407
- file_path: "noop.txt",
408
- edits: [{ old_text: "hello", new_text: "hello" }],
409
- }, { signal: new AbortController().signal, toolCallId: "test-noop" });
410
- const summary = typeof result === "string" ? result : result.content;
411
- expect(summary).toMatch(/No changes needed[\s\S]*no-op/);
412
- expect(mutated).toEqual([]);
413
- // File untouched — confirms we didn't write a no-op.
414
- const written = await fs.readFile(filePath, "utf-8");
415
- expect(written).toBe("hello world\n");
416
- });
417
- it("applies real edits atomically when the batch also contains no-ops", async () => {
418
- const filePath = path.join(tmpDir, "noop-atomic.txt");
419
- await fs.writeFile(filePath, "alpha\nbeta\ngamma\n");
420
- const tool = createEditTool(tmpDir);
421
- const result = await tool.execute({
422
- file_path: "noop-atomic.txt",
423
- edits: [
424
- { old_text: "alpha", new_text: "ALPHA" },
425
- { old_text: "beta", new_text: "beta" },
426
- { old_text: "gamma", new_text: "GAMMA" },
427
- ],
428
- atomic: true,
429
- }, { signal: new AbortController().signal, toolCallId: "test-noop-atomic" });
430
- const summary = typeof result === "string" ? result : result.content;
431
- expect(summary).toContain("3 edits");
432
- const written = await fs.readFile(filePath, "utf-8");
433
- expect(written).toBe("ALPHA\nbeta\nGAMMA\n");
434
- });
435
- it("no-op edit in a partial-apply batch still lets the other edits land", async () => {
436
- const filePath = path.join(tmpDir, "noop-batch.txt");
437
- await fs.writeFile(filePath, "alpha\nbeta\n");
438
- const tool = createEditTool(tmpDir);
439
- const result = await tool.execute({
440
- file_path: "noop-batch.txt",
441
- edits: [
442
- { old_text: "alpha", new_text: "ALPHA" },
443
- { old_text: "beta", new_text: "beta" }, // no-op
444
- ],
445
- }, { signal: new AbortController().signal, toolCallId: "test-noop-batch" });
446
- const summary = typeof result === "string" ? result : result.content;
447
- expect(summary).toMatch(/Successfully applied 2 edits/);
448
- const written = await fs.readFile(filePath, "utf-8");
449
- expect(written).toBe("ALPHA\nbeta\n");
450
- });
451
- it("strips a spurious leading blank line in old_text and still matches", async () => {
452
- const filePath = path.join(tmpDir, "blank.ts");
453
- await fs.writeFile(filePath, "function foo() {\n return 42;\n}\n");
454
- const tool = createEditTool(tmpDir);
455
- const result = await tool.execute({
456
- file_path: "blank.ts",
457
- edits: [
458
- {
459
- // Note the leading blank line — the model often pastes one in.
460
- old_text: "\n return 42;",
461
- new_text: "\n return 100;",
462
- },
463
- ],
464
- }, { signal: new AbortController().signal, toolCallId: "test-blank" });
465
- const summary = typeof result === "string" ? result : result.content;
466
- expect(summary).toMatch(/Successfully/);
467
- const written = await fs.readFile(filePath, "utf-8");
468
- expect(written).toBe("function foo() {\n return 100;\n}\n");
469
- });
470
- it("throws when old_text is not found", async () => {
471
- const filePath = path.join(tmpDir, "missing.txt");
472
- await fs.writeFile(filePath, "some content here\n");
473
- const tool = createEditTool(tmpDir);
474
- await expect(tool.execute({
475
- file_path: "missing.txt",
476
- edits: [{ old_text: "nonexistent text", new_text: "replacement" }],
477
- }, { signal: new AbortController().signal, toolCallId: "test-5" })).rejects.toThrow("old_text not found");
478
- });
479
- it("keeps the read tracker valid after a not_found failure so the model can retry immediately", async () => {
480
- const filePath = path.join(tmpDir, "guardrail.txt");
481
- await fs.writeFile(filePath, "the actual content\n");
482
- const tracker = new Map();
483
- await markRead(tracker, filePath);
484
- const tool = createEditTool(tmpDir, tracker);
485
- // First edit fails with not_found. Nothing was written, so the file is
486
- // unchanged and the tracker must stay valid — invalidating it here used to
487
- // surface a misleading "File must be read first" on the next edit.
488
- await expect(tool.execute({
489
- file_path: "guardrail.txt",
490
- edits: [{ old_text: "the wrong content", new_text: "anything" }],
491
- }, { signal: new AbortController().signal, toolCallId: "guardrail-1" })).rejects.toThrow(/old_text not found/);
492
- expect(tracker.has(filePath)).toBe(true);
493
- // Second edit with corrected old_text succeeds without a forced re-read.
494
- const result = await tool.execute({
495
- file_path: "guardrail.txt",
496
- edits: [{ old_text: "the actual content", new_text: "replaced" }],
497
- }, { signal: new AbortController().signal, toolCallId: "guardrail-2" });
498
- const summary = typeof result === "string" ? result : result.content;
499
- expect(summary).toMatch(/Successfully replaced/);
500
- expect(await fs.readFile(filePath, "utf-8")).toBe("replaced\n");
501
- });
502
- it("refreshes (not clears) the tracker on partial-apply so retries validate against the written content", async () => {
503
- const filePath = path.join(tmpDir, "partial.txt");
504
- await fs.writeFile(filePath, "alpha\nbeta\ngamma\n");
505
- const tracker = new Map();
506
- await markRead(tracker, filePath);
507
- const tool = createEditTool(tmpDir, tracker);
508
- // One edit succeeds (alpha → ALPHA), one fails with not_found (missing).
509
- // The success is written and the tracker is refreshed to the new content.
510
- await tool.execute({
511
- file_path: "partial.txt",
512
- edits: [
513
- { old_text: "alpha", new_text: "ALPHA" },
514
- { old_text: "missing", new_text: "replacement" },
515
- ],
516
- }, { signal: new AbortController().signal, toolCallId: "partial-1" });
517
- expect(await fs.readFile(filePath, "utf-8")).toBe("ALPHA\nbeta\ngamma\n");
518
- expect(tracker.has(filePath)).toBe(true);
519
- // A follow-up edit against the just-written content applies without a re-read.
520
- const result = await tool.execute({
521
- file_path: "partial.txt",
522
- edits: [{ old_text: "beta", new_text: "BETA" }],
523
- }, { signal: new AbortController().signal, toolCallId: "partial-2" });
524
- const summary = typeof result === "string" ? result : result.content;
525
- expect(summary).toMatch(/Successfully replaced/);
526
- expect(await fs.readFile(filePath, "utf-8")).toBe("ALPHA\nBETA\ngamma\n");
527
- });
528
- it("throws when old_text matches multiple times", async () => {
529
- const filePath = path.join(tmpDir, "dupes.txt");
530
- await fs.writeFile(filePath, "foo bar foo baz foo\n");
531
- const tool = createEditTool(tmpDir);
532
- await expect(tool.execute({ file_path: "dupes.txt", edits: [{ old_text: "foo", new_text: "qux" }] }, { signal: new AbortController().signal, toolCallId: "test-6" })).rejects.toThrow(/found 3 times/);
533
- });
534
- it("includes line numbers of every duplicate match in the error", async () => {
535
- const filePath = path.join(tmpDir, "pomodoro.css");
536
- await fs.writeFile(filePath, [
537
- ".timer { color: white; }",
538
- ".button { color: black; }",
539
- ".label { color: white; }",
540
- ".footer { color: white; }",
541
- "",
542
- ].join("\n"));
543
- const tool = createEditTool(tmpDir);
544
- await expect(tool.execute({
545
- file_path: "pomodoro.css",
546
- edits: [{ old_text: "color: white;", new_text: "color: red;" }],
547
- }, { signal: new AbortController().signal, toolCallId: "test-dup-lines" })).rejects.toThrow(/Matches at:[\s\S]*line 1[\s\S]*line 3[\s\S]*line 4/);
548
- });
549
- it("hints at replace_all in the duplicate-match error", async () => {
550
- const filePath = path.join(tmpDir, "hint.txt");
551
- await fs.writeFile(filePath, "foo\nfoo\nfoo\n");
552
- const tool = createEditTool(tmpDir);
553
- await expect(tool.execute({ file_path: "hint.txt", edits: [{ old_text: "foo", new_text: "bar" }] }, { signal: new AbortController().signal, toolCallId: "test-dup-hint" })).rejects.toThrow(/replace_all: true/);
554
- });
555
- it("replaces every occurrence when replace_all: true is set", async () => {
556
- const filePath = path.join(tmpDir, "rename.css");
557
- await fs.writeFile(filePath, [".timer { color: white; }", ".label { color: white; }", ".footer { color: white; }"].join("\n") + "\n");
558
- const tool = createEditTool(tmpDir);
559
- const result = await tool.execute({
560
- file_path: "rename.css",
561
- edits: [{ old_text: "color: white;", new_text: "color: red;", replace_all: true }],
562
- }, { signal: new AbortController().signal, toolCallId: "test-replace-all" });
563
- const summary = typeof result === "string" ? result : result.content;
564
- expect(summary).toMatch(/Successfully/);
565
- const written = await fs.readFile(filePath, "utf-8");
566
- expect(written).toBe([".timer { color: red; }", ".label { color: red; }", ".footer { color: red; }"].join("\n") +
567
- "\n");
568
- });
569
- it("replace_all still errors when no occurrences exist (with closest-match hint)", async () => {
570
- const filePath = path.join(tmpDir, "missing-all.txt");
571
- await fs.writeFile(filePath, "alpha beta\n");
572
- const tool = createEditTool(tmpDir);
573
- await expect(tool.execute({
574
- file_path: "missing-all.txt",
575
- edits: [{ old_text: "gamma", new_text: "delta", replace_all: true }],
576
- }, { signal: new AbortController().signal, toolCallId: "test-replace-all-missing" })).rejects.toThrow(/old_text not found/);
577
- });
578
- it("replace_all applies fuzzy matches instead of requiring exact split", async () => {
579
- const filePath = path.join(tmpDir, "replace-all-fuzzy.txt");
580
- await fs.writeFile(filePath, "say “hi”\nsay “hi”\n");
581
- const tool = createEditTool(tmpDir);
582
- await tool.execute({
583
- file_path: "replace-all-fuzzy.txt",
584
- edits: [{ old_text: 'say "hi"', new_text: "say hello", replace_all: true }],
585
- }, { signal: new AbortController().signal, toolCallId: "test-replace-all-fuzzy" });
586
- const written = await fs.readFile(filePath, "utf-8");
587
- expect(written).toBe("say hello\nsay hello\n");
588
- });
589
- it("replace_all coexists with sequential edits in one batch", async () => {
590
- const filePath = path.join(tmpDir, "mixed.css");
591
- await fs.writeFile(filePath, [".a { color: white; }", ".b { color: white; }", ".header { font-size: 12px; }"].join("\n") +
592
- "\n");
593
- const tool = createEditTool(tmpDir);
594
- const result = await tool.execute({
595
- file_path: "mixed.css",
596
- edits: [
597
- { old_text: "color: white;", new_text: "color: red;", replace_all: true },
598
- { old_text: "font-size: 12px;", new_text: "font-size: 14px;" },
599
- ],
600
- }, { signal: new AbortController().signal, toolCallId: "test-mixed" });
601
- const summary = typeof result === "string" ? result : result.content;
602
- expect(summary).toContain("2 edits");
603
- const written = await fs.readFile(filePath, "utf-8");
604
- expect(written).toBe([".a { color: red; }", ".b { color: red; }", ".header { font-size: 14px; }"].join("\n") +
605
- "\n");
606
- });
607
- it("handles fuzzy matching with trailing whitespace and smart quotes", async () => {
608
- const filePath = path.join(tmpDir, "fuzzy.txt");
609
- await fs.writeFile(filePath, "const msg = 'hello'; \nend\n");
610
- const tool = createEditTool(tmpDir);
611
- const result = await tool.execute({
612
- file_path: "fuzzy.txt",
613
- edits: [
614
- {
615
- old_text: "const msg = \u2018hello\u2019;",
616
- new_text: "const msg = 'goodbye';",
617
- },
618
- ],
619
- }, { signal: new AbortController().signal, toolCallId: "test-7" });
620
- const diff = resultToString(result);
621
- expect(diff).toContain("+const msg = 'goodbye';");
622
- const written = await fs.readFile(filePath, "utf-8");
623
- expect(written).toContain("goodbye");
624
- });
625
- it("preserves CRLF line endings", async () => {
626
- const filePath = path.join(tmpDir, "crlf.txt");
627
- await fs.writeFile(filePath, "line one\r\nline two\r\nline three\r\n");
628
- const tool = createEditTool(tmpDir);
629
- const result = await tool.execute({ file_path: "crlf.txt", edits: [{ old_text: "line two", new_text: "line TWO" }] }, { signal: new AbortController().signal, toolCallId: "test-8" });
630
- const diff = resultToString(result);
631
- expect(diff).toContain("+line TWO");
632
- const written = await fs.readFile(filePath, "utf-8");
633
- expect(written).toBe("line one\r\nline TWO\r\nline three\r\n");
634
- });
635
- it("calls mutation callback after successful edits", async () => {
636
- const filePath = path.join(tmpDir, "mutated.txt");
637
- await fs.writeFile(filePath, "alpha\n");
638
- const mutated = [];
639
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, (mutatedPath) => {
640
- mutated.push(mutatedPath);
641
- });
642
- await tool.execute({ file_path: "mutated.txt", edits: [{ old_text: "alpha", new_text: "beta" }] }, { signal: new AbortController().signal, toolCallId: "test-mutated" });
643
- expect(mutated).toEqual([filePath]);
644
- });
645
- it("does not call mutation callback when no edits are written", async () => {
646
- const filePath = path.join(tmpDir, "not-mutated.txt");
647
- await fs.writeFile(filePath, "alpha\n");
648
- const mutated = [];
649
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, (mutatedPath) => {
650
- mutated.push(mutatedPath);
651
- });
652
- await expect(tool.execute({ file_path: "not-mutated.txt", edits: [{ old_text: "missing", new_text: "beta" }] }, { signal: new AbortController().signal, toolCallId: "test-not-mutated" })).rejects.toThrow("old_text not found");
653
- expect(mutated).toEqual([]);
654
- });
655
- describe("LSP diagnostics", () => {
656
- function contentOf(result) {
657
- if (result && typeof result === "object" && "content" in result) {
658
- const c = result.content;
659
- if (typeof c === "string")
660
- return c;
661
- }
662
- return String(result);
663
- }
664
- it("appends a non-empty diagnostics string to a successful edit", async () => {
665
- const filePath = path.join(tmpDir, "diag.ts");
666
- await fs.writeFile(filePath, "const x = 1;\n");
667
- const seen = [];
668
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async (fp, content) => {
669
- seen.push({ filePath: fp, content });
670
- return "\n\nDiagnostics in diag.ts (informational — may resolve after related edits):\nL1:7 boom (typescript)";
671
- });
672
- const result = await tool.execute({
673
- file_path: "diag.ts",
674
- edits: [{ old_text: "const x = 1;", new_text: 'const x: number = "a";' }],
675
- }, { signal: new AbortController().signal, toolCallId: "test-diag-1" });
676
- const content = contentOf(result);
677
- expect(content).toContain("Successfully replaced text in diag.ts.");
678
- expect(content).toContain("L1:7 boom (typescript)");
679
- expect(seen).toEqual([{ filePath, content: 'const x: number = "a";\n' }]);
680
- });
681
- it("leaves the result unchanged when the provider returns empty", async () => {
682
- const filePath = path.join(tmpDir, "clean.ts");
683
- await fs.writeFile(filePath, "alpha\n");
684
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async () => "");
685
- const result = await tool.execute({ file_path: "clean.ts", edits: [{ old_text: "alpha", new_text: "beta" }] }, { signal: new AbortController().signal, toolCallId: "test-diag-2" });
686
- expect(contentOf(result)).toBe("Successfully replaced text in clean.ts.");
687
- });
688
- it("tells the provider which matching strategy placed the edit", async () => {
689
- // Attribution is the point of the telemetry: a regression traced to `...`
690
- // elision means something very different from one traced to an exact
691
- // match, and only this tool knows which ladder rung fired.
692
- const cases = [
693
- { name: "exact.ts", file: "alpha\n", old: "alpha", next: "beta", expect: "text" },
694
- {
695
- name: "indent.ts",
696
- file: " const x = 1;\n const y = 2;\n",
697
- old: "const x = 1;\nconst y = 2;",
698
- next: "const x = 10;\nconst y = 20;",
699
- expect: "indent_flex",
700
- },
701
- {
702
- name: "elide.ts",
703
- file: "function f() {\n keep();\n return 1;\n}\n",
704
- old: "function f() {\n ...\n return 1;\n}",
705
- next: "function g() {\n ...\n return 1;\n}",
706
- expect: "dotdotdot",
707
- },
708
- ];
709
- for (const c of cases) {
710
- await fs.writeFile(path.join(tmpDir, c.name), c.file);
711
- let seen;
712
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async (_p, _c, source) => {
713
- seen = source;
714
- return "";
715
- });
716
- await tool.execute({ file_path: c.name, edits: [{ old_text: c.old, new_text: c.next }] }, { signal: new AbortController().signal, toolCallId: `test-source-${c.expect}` });
717
- expect(seen).toBe(c.expect);
718
- }
719
- });
720
- it("blames the riskiest strategy when one batch mixes them", async () => {
721
- // A batch that needed an elision is a batch whose breakage should be
722
- // attributed to the elision, not to the exact match beside it.
723
- await fs.writeFile(path.join(tmpDir, "mixed.ts"), "const a = 1;\nfunction f() {\n keep();\n return 1;\n}\n");
724
- let seen;
725
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async (_p, _c, source) => {
726
- seen = source;
727
- return "";
728
- });
729
- await tool.execute({
730
- file_path: "mixed.ts",
731
- edits: [
732
- { old_text: "const a = 1;", new_text: "const a = 2;" },
733
- {
734
- old_text: "function f() {\n ...\n return 1;\n}",
735
- new_text: "function g() {\n ...\n return 1;\n}",
736
- },
737
- ],
738
- }, { signal: new AbortController().signal, toolCallId: "test-source-mixed" });
739
- expect(seen).toBe("dotdotdot");
740
- });
741
- it("leaves the result unchanged when the provider throws", async () => {
742
- const filePath = path.join(tmpDir, "throws.ts");
743
- await fs.writeFile(filePath, "alpha\n");
744
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async () => {
745
- throw new Error("lsp exploded");
746
- });
747
- const result = await tool.execute({ file_path: "throws.ts", edits: [{ old_text: "alpha", new_text: "beta" }] }, { signal: new AbortController().signal, toolCallId: "test-diag-3" });
748
- expect(contentOf(result)).toBe("Successfully replaced text in throws.ts.");
749
- expect(await fs.readFile(filePath, "utf-8")).toBe("beta\n");
750
- });
751
- it("does not call the provider when nothing was written", async () => {
752
- const filePath = path.join(tmpDir, "untouched.ts");
753
- await fs.writeFile(filePath, "alpha\n");
754
- let calls = 0;
755
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async () => {
756
- calls++;
757
- return "should not appear";
758
- });
759
- await expect(tool.execute({ file_path: "untouched.ts", edits: [{ old_text: "missing", new_text: "beta" }] }, { signal: new AbortController().signal, toolCallId: "test-diag-4" })).rejects.toThrow("old_text not found");
760
- expect(calls).toBe(0);
761
- });
762
- it("appends diagnostics to partial-success results", async () => {
763
- const filePath = path.join(tmpDir, "partial.ts");
764
- await fs.writeFile(filePath, "alpha\ngamma\n");
765
- const tool = createEditTool(tmpDir, undefined, undefined, undefined, undefined, undefined, async () => "\n\nDiagnostics in partial.ts (informational — may resolve after related edits):\nL1:1 oops (typescript)");
766
- const result = await tool.execute({
767
- file_path: "partial.ts",
768
- edits: [
769
- { old_text: "alpha", new_text: "beta" },
770
- { old_text: "missing", new_text: "x" },
771
- ],
772
- }, { signal: new AbortController().signal, toolCallId: "test-diag-5" });
773
- const content = contentOf(result);
774
- expect(content).toContain("Applied 1 of 2 edits");
775
- expect(content).toContain("L1:1 oops (typescript)");
776
- });
777
- });
778
- });
779
- describe("edit anchor guard", () => {
780
- let tmpDir;
781
- beforeEach(async () => {
782
- tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "edit-anchor-test-"));
783
- });
784
- afterEach(async () => {
785
- await fs.rm(tmpDir, { recursive: true, force: true });
786
- });
787
- function diffOf(result) {
788
- if (typeof result === "string")
789
- return result;
790
- if (result && typeof result === "object" && "details" in result) {
791
- return result.details?.diff ?? "";
792
- }
793
- return "";
794
- }
795
- /** Anchor for a 1-based line in `content`. */
796
- function anchorFor(content, line1) {
797
- const idx = line1 - 1;
798
- const text = content.split("\n")[idx];
799
- return { line: line1, hash: lineHash(text, idx) };
800
- }
801
- it("applies normally when the anchor matches", async () => {
802
- const filePath = path.join(tmpDir, "a.ts");
803
- const content = "const a = 1;\nconst b = 2;\nconst c = 3;\n";
804
- await fs.writeFile(filePath, content);
805
- const tracker = new Map();
806
- await markRead(tracker, filePath);
807
- const a = anchorFor(content, 2);
808
- const tool = createEditTool(tmpDir, tracker);
809
- const result = await tool.execute({
810
- file_path: "a.ts",
811
- edits: [
812
- {
813
- old_text: "const b = 2;",
814
- new_text: "const b = 20;",
815
- anchor: { start_line: a.line, start_hash: a.hash, end_line: a.line, end_hash: a.hash },
816
- },
817
- ],
818
- }, { signal: new AbortController().signal, toolCallId: "anc-1" });
819
- expect(diffOf(result)).toContain("+const b = 20;");
820
- expect(await fs.readFile(filePath, "utf-8")).toBe("const a = 1;\nconst b = 20;\nconst c = 3;\n");
821
- });
822
- it("rejects a wrong start_hash with stale_anchor and writes nothing", async () => {
823
- const filePath = path.join(tmpDir, "b.ts");
824
- const content = "const a = 1;\nconst b = 2;\n";
825
- await fs.writeFile(filePath, content);
826
- const tracker = new Map();
827
- await markRead(tracker, filePath);
828
- const tool = createEditTool(tmpDir, tracker);
829
- await expect(tool.execute({
830
- file_path: "b.ts",
831
- edits: [
832
- {
833
- old_text: "const b = 2;",
834
- new_text: "const b = 20;",
835
- anchor: { start_line: 2, start_hash: "dead", end_line: 2, end_hash: "dead" },
836
- },
837
- ],
838
- }, { signal: new AbortController().signal, toolCallId: "anc-2" })).rejects.toThrow(/changed since you read it/);
839
- // File untouched.
840
- expect(await fs.readFile(filePath, "utf-8")).toBe(content);
841
- });
842
- it("partial-applies a multi-edit batch where one anchor is stale", async () => {
843
- const filePath = path.join(tmpDir, "c.ts");
844
- const content = "const a = 1;\nconst b = 2;\nconst c = 3;\n";
845
- await fs.writeFile(filePath, content);
846
- const tracker = new Map();
847
- await markRead(tracker, filePath);
848
- const a = anchorFor(content, 1);
849
- const tool = createEditTool(tmpDir, tracker);
850
- const result = await tool.execute({
851
- file_path: "c.ts",
852
- edits: [
853
- {
854
- old_text: "const a = 1;",
855
- new_text: "const a = 10;",
856
- anchor: { start_line: a.line, start_hash: a.hash, end_line: a.line, end_hash: a.hash },
857
- },
858
- {
859
- old_text: "const c = 3;",
860
- new_text: "const c = 30;",
861
- anchor: { start_line: 3, start_hash: "dead", end_line: 3, end_hash: "dead" },
862
- },
863
- ],
864
- }, { signal: new AbortController().signal, toolCallId: "anc-3" });
865
- const content2 = contentOf(result);
866
- expect(content2).toContain("Applied 1 of 2 edits");
867
- expect(content2).toContain("changed since you read it");
868
- // First edit persisted; the stale one did not.
869
- expect(await fs.readFile(filePath, "utf-8")).toBe("const a = 10;\nconst b = 2;\nconst c = 3;\n");
870
- });
871
- it("absent anchor behaves exactly like today (fuzzy path)", async () => {
872
- const filePath = path.join(tmpDir, "d.ts");
873
- await fs.writeFile(filePath, "const a = 1;\n");
874
- const tracker = new Map();
875
- await markRead(tracker, filePath);
876
- const tool = createEditTool(tmpDir, tracker);
877
- const result = await tool.execute({ file_path: "d.ts", edits: [{ old_text: "const a = 1;", new_text: "const a = 2;" }] }, { signal: new AbortController().signal, toolCallId: "anc-4" });
878
- expect(diffOf(result)).toContain("+const a = 2;");
879
- expect(await fs.readFile(filePath, "utf-8")).toBe("const a = 2;\n");
880
- });
881
- // ── Span form { span, lines } ──
882
- function spanFor(content, startLine, endLine) {
883
- const lines = content.split("\n");
884
- return {
885
- start_line: startLine,
886
- start_hash: lineHash(lines[startLine - 1], startLine - 1),
887
- end_line: endLine,
888
- end_hash: lineHash(lines[endLine - 1], endLine - 1),
889
- };
890
- }
891
- it("span form replaces the pinned line range without old_text", async () => {
892
- const content = "const a = 1;\nconst b = 2;\nconst c = 3;\n";
893
- const filePath = path.join(tmpDir, "span1.ts");
894
- await fs.writeFile(filePath, content);
895
- const tool = createEditTool(tmpDir);
896
- const result = await tool.execute({
897
- file_path: "span1.ts",
898
- edits: [{ span: spanFor(content, 2, 2), lines: ["const b = 20;"] }],
899
- }, { signal: new AbortController().signal, toolCallId: "span-1" });
900
- expect(diffOf(result)).toContain("+const b = 20;");
901
- expect(await fs.readFile(filePath, "utf-8")).toBe("const a = 1;\nconst b = 20;\nconst c = 3;\n");
902
- });
903
- it("span form: multi-line replace, insert-by-expansion, and delete via empty lines", async () => {
904
- const content = "one\ntwo\nthree\nfour\nfive\n";
905
- const filePath = path.join(tmpDir, "span2.txt");
906
- await fs.writeFile(filePath, content);
907
- const tool = createEditTool(tmpDir);
908
- await tool.execute({
909
- file_path: "span2.txt",
910
- edits: [
911
- // Replace lines 2-3 with three lines (expansion = insertion).
912
- { span: spanFor(content, 2, 3), lines: ["TWO", "TWO.5", "THREE"] },
913
- // Delete line 5 ("five"). Anchors verify against the file AS READ,
914
- // and spans apply bottom-up, so the earlier expansion doesn't shift this.
915
- { span: spanFor(content, 5, 5), lines: [] },
916
- ],
917
- }, { signal: new AbortController().signal, toolCallId: "span-2" });
918
- // Deleting line 5 ("five") leaves the trailing newline's empty segment as EOF.
919
- expect(await fs.readFile(filePath, "utf-8")).toBe("one\nTWO\nTWO.5\nTHREE\nfour\n");
920
- });
921
- it("span form rejects stale hashes without touching the file", async () => {
922
- const content = "alpha\nbeta\n";
923
- const filePath = path.join(tmpDir, "span3.txt");
924
- await fs.writeFile(filePath, content);
925
- const tool = createEditTool(tmpDir);
926
- await expect(tool.execute({
927
- file_path: "span3.txt",
928
- edits: [
929
- {
930
- span: { start_line: 1, start_hash: "dead", end_line: 1, end_hash: "dead" },
931
- lines: ["hijacked"],
932
- },
933
- ],
934
- }, { signal: new AbortController().signal, toolCallId: "span-3" })).rejects.toThrow(/changed since you read it/);
935
- expect(await fs.readFile(filePath, "utf-8")).toBe(content);
936
- });
937
- it("span form rejects overlapping spans (first wins, overlap reported)", async () => {
938
- const content = "l1\nl2\nl3\nl4\n";
939
- const filePath = path.join(tmpDir, "span4.txt");
940
- await fs.writeFile(filePath, content);
941
- const tool = createEditTool(tmpDir);
942
- const result = await tool.execute({
943
- file_path: "span4.txt",
944
- edits: [
945
- { span: spanFor(content, 1, 2), lines: ["A"] },
946
- { span: spanFor(content, 2, 3), lines: ["B"] },
947
- ],
948
- }, { signal: new AbortController().signal, toolCallId: "span-4" });
949
- expect(contentOf(result)).toContain("overlaps");
950
- expect(await fs.readFile(filePath, "utf-8")).toBe("A\nl3\nl4\n");
951
- });
952
- it("span and text forms mix in one batch: spans first, then text on the result", async () => {
953
- const content = "const x = 1;\nconst y = 2;\n";
954
- const filePath = path.join(tmpDir, "span5.ts");
955
- await fs.writeFile(filePath, content);
956
- const tool = createEditTool(tmpDir);
957
- await tool.execute({
958
- file_path: "span5.ts",
959
- edits: [
960
- { span: spanFor(content, 1, 1), lines: ["const x = 10;"] },
961
- { old_text: "const y = 2;", new_text: "const y = 20;" },
962
- ],
963
- }, { signal: new AbortController().signal, toolCallId: "span-5" });
964
- expect(await fs.readFile(filePath, "utf-8")).toBe("const x = 10;\nconst y = 20;\n");
965
- });
966
- it("rejects an edit that mixes span with old_text, and one with neither form", async () => {
967
- const content = "a\nb\n";
968
- const filePath = path.join(tmpDir, "span6.txt");
969
- await fs.writeFile(filePath, content);
970
- const tool = createEditTool(tmpDir);
971
- await expect(tool.execute({
972
- file_path: "span6.txt",
973
- edits: [{ span: spanFor(content, 1, 1), lines: ["z"], old_text: "a", new_text: "z" }],
974
- }, { signal: new AbortController().signal, toolCallId: "span-6" })).rejects.toThrow(/must not mix/);
975
- await expect(tool.execute({ file_path: "span6.txt", edits: [{}] }, { signal: new AbortController().signal, toolCallId: "span-7" })).rejects.toThrow(/has neither/);
976
- expect(await fs.readFile(filePath, "utf-8")).toBe(content);
977
- });
978
- it("blocks edits outside the workspace with the guard error", async () => {
979
- const tool = createEditTool(tmpDir);
980
- const outside = path.join(os.homedir(), "Documents", "gg-guard-test-outside.txt");
981
- const raw = await tool.execute({ file_path: outside, edits: [{ old_text: "a", new_text: "b" }] }, { signal: new AbortController().signal, toolCallId: "guard-1" });
982
- expect(contentOf(raw)).toContain("outside the workspace");
983
- expect(contentOf(raw)).toContain("allowOutsideWorkspaceWrites");
984
- });
985
- });
986
- /**
987
- * Models intermittently hand-serialize `edits` into a JSON string instead of
988
- * emitting a real array (~1% of edit calls across opus-5/sonnet-5/glm-5.x).
989
- * Well-formed strings are coerced; malformed ones must be rejected with a
990
- * message that names the mistake, because the stock "expected array, received
991
- * string" made the model re-send the identical payload until the agent loop's
992
- * repeat counter killed the turn.
993
- */
994
- describe("edit stringified `edits` handling", () => {
995
- const parse = (edits) => createEditTool(os.tmpdir()).parameters.safeParse({ file_path: "a.ts", edits });
996
- const errorFor = (edits) => {
997
- const result = parse(edits);
998
- expect(result.success).toBe(false);
999
- return result.success ? "" : prettifyError(result.error);
1000
- };
1001
- it("coerces a well-formed stringified array back into edits", () => {
1002
- const result = parse(JSON.stringify([{ old_text: "a", new_text: "b" }]));
1003
- expect(result.success).toBe(true);
1004
- expect(result.success && result.data.edits).toEqual([{ old_text: "a", new_text: "b" }]);
1005
- });
1006
- // Verbatim payloads recovered from ~/.gg session logs. Each broke a real
1007
- // turn: unescaped control characters, a dropped `new_text` key, a `":`
1008
- // corrupted into `>`, and a stream truncated mid-string.
1009
- it.each([
1010
- ["raw control character", '[{"old_text": "a\\nb", "new_text": "c\nd"}]'],
1011
- ["missing new_text key", '[{"old_text": "a", " * Egress limits"}]'],
1012
- ["corrupted key delimiter", '[{"old_text">function stopServer() {'],
1013
- ["truncated mid-payload", '[{"old_text": "a", "new_text": "bb'],
1014
- ])("rejects %s with actionable guidance", (_label, payload) => {
1015
- const message = errorFor(payload);
1016
- expect(message).toContain("JSON-encoded string");
1017
- expect(message).toContain("real JSON array");
1018
- expect(message).toContain("split the work");
1019
- // The unactionable stock message is what caused the retry loop.
1020
- expect(message).not.toContain("expected array, received string");
1021
- });
1022
- it("leaves non-string type errors on their default message", () => {
1023
- expect(errorFor(42)).toContain("expected array, received number");
1024
- });
1025
- it("still reports per-item errors inside a real array", () => {
1026
- expect(errorFor([{ old_text: 5 }])).toContain("expected string, received number");
1027
- });
1028
- });
1029
- function contentOf(result) {
1030
- if (typeof result === "string")
1031
- return result;
1032
- if (result && typeof result === "object" && "content" in result) {
1033
- return result.content ?? "";
1034
- }
1035
- return "";
1036
- }
1037
- //# sourceMappingURL=edit.test.js.map