@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,57 @@
1
+ /**
2
+ * The one-line call description an AskResolver receives, and the reason a
3
+ * `denied:ask` ledger row carries.
4
+ *
5
+ * Summaries are outbound to a human, so they are masked through
6
+ * `maskForPrompt` before they leave the process; a secret that cannot be
7
+ * shown safely withholds the arguments entirely (`unshowable`, review #9).
8
+ */
9
+ import { ASK_CANCELLED_REASON, ASK_DENIED_REASON, ASK_NO_CHANNEL_REASON, ASK_TIMEOUT_REASON, ASK_UNSHOWABLE_REASON, maskForPrompt, } from "#src/permissions/index";
10
+ /** Ceiling for the one-line call description an AskResolver receives. */
11
+ export const MAX_ASK_SUMMARY_CHARS = 200;
12
+ /**
13
+ * One human-readable line describing the call an `ask` rule matched.
14
+ *
15
+ * Built from the scope's DECLARED fields rather than from `JSON.stringify` of
16
+ * the whole input: the input carries a tool's full payload -- file contents on
17
+ * a Write, a commit message, whatever a provider tool takes -- and an
18
+ * AskResolver is by definition an outbound channel to a human. A summary is
19
+ * what approval needs; the payload is what the audit sink already holds.
20
+ */
21
+ export function askSummary(tool, scope, input) {
22
+ const fields = [scope.commandField, scope.argvField, scope.verbField, ...scope.pathFields];
23
+ const parts = fields.flatMap((field) => {
24
+ if (field === undefined)
25
+ return [];
26
+ const value = input[field];
27
+ if (typeof value === "string")
28
+ return [`${field}=${value}`];
29
+ if (Array.isArray(value))
30
+ return [`${field}=${value.filter((v) => typeof v === "string").join(" ")}`];
31
+ return [];
32
+ });
33
+ // Mask the FULL line, then cut: cutting first can split a secret so no
34
+ // pattern matches what is left, and the partial secret would go out.
35
+ const masked = maskForPrompt(`${tool} ${parts.join(" ")}`.trim());
36
+ if (!masked.ok)
37
+ return { summary: `${tool} [arguments withheld: contains a secret]`, unshowable: true };
38
+ return { summary: masked.masked.slice(0, MAX_ASK_SUMMARY_CHARS), unshowable: false };
39
+ }
40
+ /**
41
+ * The reason a `denied:ask` ledger row carries, chosen by WHO refused.
42
+ *
43
+ * One constant for every ask denial asserted "no approval channel is
44
+ * configured" even when a human answered (deny) or nobody answered in time
45
+ * (timeout). Those are materially different facts; see ASK_*_REASON.
46
+ */
47
+ export function askDenyReason(decidedBy) {
48
+ if (decidedBy === "timeout")
49
+ return ASK_TIMEOUT_REASON;
50
+ if (decidedBy === "human")
51
+ return ASK_DENIED_REASON;
52
+ if (decidedBy === "cancelled")
53
+ return ASK_CANCELLED_REASON;
54
+ if (decidedBy === "unshowable")
55
+ return ASK_UNSHOWABLE_REASON;
56
+ return ASK_NO_CHANNEL_REASON;
57
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Per-segment shell working-directory tracking, shared by the two Bash
3
+ * payload screens (`policy-bash.ts`'s `checkBashCommand` and
4
+ * `policy-bash-raw.ts`'s `screenRawBashCommand`).
5
+ *
6
+ * A `cd` in one segment moves the frame of reference for every LATER segment
7
+ * in the same command (`cd child && cat ../secret` reads `<root>/secret`, not
8
+ * `<root>/child/secret`), so both screens must track it identically to avoid
9
+ * being fooled by it. This module is the ONE definition of "what does a `cd`
10
+ * segment resolve to, and how do separators fold that into the running set
11
+ * of candidate working directories" -- mirroring `isNaxOwnedWritePath` in
12
+ * `nax-owned-writes.ts`, which is the same one-definition-two-callers shape
13
+ * for a different seam.
14
+ *
15
+ * What this module does NOT decide: what happens when a `cd` cannot be
16
+ * modelled (an option-shaped target, an opaque one, one that fails to
17
+ * resolve). The two callers have OPPOSITE answers to that question --
18
+ * `checkBashCommand` denies (containment is the whole point of gated mode),
19
+ * `screenRawBashCommand` must fail open (raw enforces no containment by
20
+ * definition; see the caller's own docblock for why). `cdTargetsFor` reports
21
+ * WHY a `cd` could not be modelled and leaves the verdict to the caller.
22
+ */
23
+ import type { BashSegment } from "#src/permissions/index";
24
+ /**
25
+ * The outcome of inspecting one segment for a `cd`:
26
+ *
27
+ * - `not-cd`: the segment does not start with `cd`; nothing to track.
28
+ * - `resolved`: a `cd` whose target resolved against EVERY current frame.
29
+ * - `no-target`: `cd` with no argument at all.
30
+ * - `option-shaped`: the target starts with `-` (`cd -`, `cd -P dir`) and
31
+ * puts the path in a slot this reader does not parse.
32
+ * - `opaque`: the target contains `$`-expansion; its runtime value is
33
+ * unknown here.
34
+ * - `unresolved`: the target failed to resolve from at least one current
35
+ * frame (root escape, `.git/`, or whatever `resolvePath` itself refuses).
36
+ */
37
+ export type CdTargetResult = {
38
+ readonly kind: "not-cd";
39
+ } | {
40
+ readonly kind: "resolved";
41
+ readonly targets: readonly string[];
42
+ } | {
43
+ readonly kind: "no-target";
44
+ } | {
45
+ readonly kind: "option-shaped";
46
+ readonly text: string;
47
+ } | {
48
+ readonly kind: "opaque";
49
+ readonly text: string;
50
+ } | {
51
+ readonly kind: "unresolved";
52
+ readonly text: string;
53
+ };
54
+ /**
55
+ * Reads a segment as a possible `cd`, resolving its target against every
56
+ * frame in `cwd` via the caller-supplied `resolvePath` (the same containment
57
+ * callback both policy modules already receive from `policy.ts`).
58
+ *
59
+ * Resolves against ALL frames, not just the first, for the same reason
60
+ * `checkPayload`'s old `resolveAll` did: after a `;`-joined `cd`, more than
61
+ * one frame can be live at once (see `nextWorkingDirectories` below), and a
62
+ * target that is safe from one frame but escapes from another must not be
63
+ * silently narrowed to the safe one.
64
+ */
65
+ export declare function cdTargetsFor(segment: BashSegment, cwd: readonly string[], resolvePath: (candidate: string, cwd: string) => string | null): CdTargetResult;
66
+ /**
67
+ * Folds a segment's `cd` targets (if any) into the running set of candidate
68
+ * working directories, per the separator that FOLLOWS it:
69
+ *
70
+ * - `&&`: the shell only reaches the next segment if this one succeeded, and
71
+ * a successful `cd` REPLACES the working directory outright.
72
+ * - `;`: the next segment runs regardless of whether the `cd` succeeded, so
73
+ * both the old and new frames stay live -- the command might still be
74
+ * sitting in the OLD directory when the next segment runs.
75
+ * - anything else (`||`, `|`, `&`, or no separator / the last segment): a
76
+ * `cd`'s effect on a LATER segment is not modelled, so the frame set is
77
+ * left unchanged.
78
+ *
79
+ * `cdTargets` is `undefined` for a non-`cd` segment (or one this reader could
80
+ * not resolve, per `cdTargetsFor`), in which case the frame set never changes.
81
+ */
82
+ export declare function nextWorkingDirectories(segment: BashSegment, current: readonly string[], cdTargets: readonly string[] | undefined): readonly string[];
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Reads a segment as a possible `cd`, resolving its target against every
3
+ * frame in `cwd` via the caller-supplied `resolvePath` (the same containment
4
+ * callback both policy modules already receive from `policy.ts`).
5
+ *
6
+ * Resolves against ALL frames, not just the first, for the same reason
7
+ * `checkPayload`'s old `resolveAll` did: after a `;`-joined `cd`, more than
8
+ * one frame can be live at once (see `nextWorkingDirectories` below), and a
9
+ * target that is safe from one frame but escapes from another must not be
10
+ * silently narrowed to the safe one.
11
+ */
12
+ export function cdTargetsFor(segment, cwd, resolvePath) {
13
+ const words = segment.tokens.map((token) => token.text);
14
+ if (words[0] !== "cd")
15
+ return { kind: "not-cd" };
16
+ const target = segment.tokens[1];
17
+ if (target === undefined)
18
+ return { kind: "no-target" };
19
+ // `cd -` returns to $OLDPWD and `cd -P x` puts the path in a later slot:
20
+ // both leave a naive reader tracking `<root>/-` as the new frame of
21
+ // reference while the shell is somewhere else. Option-shaped, not modelled.
22
+ if (target.text.startsWith("-"))
23
+ return { kind: "option-shaped", text: target.text };
24
+ if (target.opaque)
25
+ return { kind: "opaque", text: target.text };
26
+ const resolved = cwd.map((directory) => resolvePath(target.text, directory));
27
+ if (!resolved.every((path) => path !== null))
28
+ return { kind: "unresolved", text: target.text };
29
+ return { kind: "resolved", targets: resolved };
30
+ }
31
+ /**
32
+ * Folds a segment's `cd` targets (if any) into the running set of candidate
33
+ * working directories, per the separator that FOLLOWS it:
34
+ *
35
+ * - `&&`: the shell only reaches the next segment if this one succeeded, and
36
+ * a successful `cd` REPLACES the working directory outright.
37
+ * - `;`: the next segment runs regardless of whether the `cd` succeeded, so
38
+ * both the old and new frames stay live -- the command might still be
39
+ * sitting in the OLD directory when the next segment runs.
40
+ * - anything else (`||`, `|`, `&`, or no separator / the last segment): a
41
+ * `cd`'s effect on a LATER segment is not modelled, so the frame set is
42
+ * left unchanged.
43
+ *
44
+ * `cdTargets` is `undefined` for a non-`cd` segment (or one this reader could
45
+ * not resolve, per `cdTargetsFor`), in which case the frame set never changes.
46
+ */
47
+ export function nextWorkingDirectories(segment, current, cdTargets) {
48
+ if (cdTargets === undefined)
49
+ return current;
50
+ if (segment.separator === "&&")
51
+ return cdTargets;
52
+ if (segment.separator === ";")
53
+ return [...new Set([...current, ...cdTargets])];
54
+ return current;
55
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The model-authored shell tool (spec §4 US-004).
3
+ *
4
+ * Session-local like `RunCommand`, not a global registry entry: it needs the
5
+ * project's shell and secret-strip list, which are per-config. Created only
6
+ * when the operation DECLARED it (see coding-tool-support.ts) — a tool the
7
+ * global registry held would be callable by an op that never declared it,
8
+ * because `callTool` looks a name up before it consults advertisement.
9
+ *
10
+ * WHERE IT RUNS: `ctx.root`, the hop's permitted root and the same root the
11
+ * policy resolved every path against. Never the runtime's workdir — under `-d`
12
+ * those differ, and that difference is the #1794 defect.
13
+ *
14
+ * WHAT GATES IT: nothing here. The command string reached this function only
15
+ * because `policy.check` already lexed it, matched every segment against the
16
+ * stage's `Bash(...)` rules and containment-checked its paths and redirects
17
+ * (src/tools/policy-bash.ts). This module must never be given a "safe enough"
18
+ * check of its own: two gates in two places drift, and the second one is the
19
+ * one nobody tests. The launcher (P4) changes HOW the command runs -- inside
20
+ * an OS sandbox when enabled -- never WHETHER.
21
+ */
22
+ import { runArgv } from "#src/internal/argv-exec";
23
+ import type { BashApprovalMode } from "../config/bash-approval.ts";
24
+ import { type CommandLauncher } from "../sandbox/index.ts";
25
+ import type { CodingTool } from "./registry.ts";
26
+ /**
27
+ * Deadline for a Bash spawn — the same ceiling the Exec branch uses
28
+ * (`EXEC_TIMEOUT_MS`, src/tools/run-command-exec.ts). A model-authored command
29
+ * may legitimately be a build or a full test run, and a shorter default would
30
+ * be worked around rather than respected.
31
+ */
32
+ export declare const BASH_TIMEOUT_MS = 300000;
33
+ export declare const DEFAULT_BASH_SHELL = "/bin/sh";
34
+ export interface BashToolOptions {
35
+ /** `quality.shell`. */
36
+ readonly shell?: string;
37
+ /** `quality.stripEnvVars` — secrets removed before the spawn. */
38
+ readonly stripEnvVars?: readonly string[];
39
+ /** The stage's granted patterns, for the DESCRIPTION only. The policy is the
40
+ * gate; naming the granted forms here is what stops the model spending a
41
+ * turn discovering them by denial. Ignored under `raw` (see
42
+ * `rawDescription` below) -- there is no per-pattern grant to name. */
43
+ readonly patterns?: readonly string[];
44
+ /**
45
+ * ADR-030 mode this description should reflect. Defaults to `gated`, the
46
+ * conservative posture, matching `compileToolPolicy`'s own default so an
47
+ * omitted mode never advertises a capability the policy would not actually
48
+ * grant.
49
+ */
50
+ readonly bashApproval?: BashApprovalMode;
51
+ /**
52
+ * Whether a human can answer an escalated command (ADR-030, amended for P4).
53
+ * Read only under `escalate`; absent or false keeps `gated`'s wording, since
54
+ * a run with no interaction channel denies every ask as `unavailable`.
55
+ */
56
+ readonly humanApproval?: boolean;
57
+ /**
58
+ * P4: how the command runs. Absent = today's direct spawn through
59
+ * `_bashToolDeps.runArgv` (unit tests). Production always passes one --
60
+ * disabled, available or unavailable -- and its state also shapes the
61
+ * description, so the agent always knows whether it is sandboxed.
62
+ */
63
+ readonly launcher?: CommandLauncher;
64
+ }
65
+ /** Injectable spawn seam, mirroring `_argvExecDeps`. */
66
+ export declare const _bashToolDeps: {
67
+ runArgv: typeof runArgv;
68
+ };
69
+ export declare function createBashTool(opts?: BashToolOptions): CodingTool;
@@ -0,0 +1,283 @@
1
+ /**
2
+ * The model-authored shell tool (spec §4 US-004).
3
+ *
4
+ * Session-local like `RunCommand`, not a global registry entry: it needs the
5
+ * project's shell and secret-strip list, which are per-config. Created only
6
+ * when the operation DECLARED it (see coding-tool-support.ts) — a tool the
7
+ * global registry held would be callable by an op that never declared it,
8
+ * because `callTool` looks a name up before it consults advertisement.
9
+ *
10
+ * WHERE IT RUNS: `ctx.root`, the hop's permitted root and the same root the
11
+ * policy resolved every path against. Never the runtime's workdir — under `-d`
12
+ * those differ, and that difference is the #1794 defect.
13
+ *
14
+ * WHAT GATES IT: nothing here. The command string reached this function only
15
+ * because `policy.check` already lexed it, matched every segment against the
16
+ * stage's `Bash(...)` rules and containment-checked its paths and redirects
17
+ * (src/tools/policy-bash.ts). This module must never be given a "safe enough"
18
+ * check of its own: two gates in two places drift, and the second one is the
19
+ * one nobody tests. The launcher (P4) changes HOW the command runs -- inside
20
+ * an OS sandbox when enabled -- never WHETHER.
21
+ */
22
+ import { interceptShell } from "#src/command-interceptor/index";
23
+ import { agentOutputOverlay } from "#src/internal/agent-output-env";
24
+ import { runArgv } from "#src/internal/argv-exec";
25
+ import { formatExecBody } from "#src/internal/exec-framing";
26
+ import { rawBashRefusalReason, sandboxSentence, unsandboxedSentence } from "../sandbox/index.js";
27
+ import { cutToByteCap, READ_CEILING } from "./truncate.js";
28
+ import { BASH_TOOL_NAME } from "./types.js";
29
+ /**
30
+ * Deadline for a Bash spawn — the same ceiling the Exec branch uses
31
+ * (`EXEC_TIMEOUT_MS`, src/tools/run-command-exec.ts). A model-authored command
32
+ * may legitimately be a build or a full test run, and a shorter default would
33
+ * be worked around rather than respected.
34
+ */
35
+ export const BASH_TIMEOUT_MS = 300_000;
36
+ /** Floor for a caller-requested deadline: below this, a real command cannot
37
+ * even start, and a 0 would disable the deadline entirely. */
38
+ const MIN_BASH_TIMEOUT_MS = 1_000;
39
+ export const DEFAULT_BASH_SHELL = "/bin/sh";
40
+ /** Injectable spawn seam, mirroring `_argvExecDeps`. */
41
+ export const _bashToolDeps = {
42
+ runArgv,
43
+ };
44
+ function describeGrants(patterns) {
45
+ const named = (patterns ?? []).filter((pattern) => pattern !== "*");
46
+ if (patterns?.includes("*") === true)
47
+ return "every command form is granted for this stage";
48
+ if (named.length === 0)
49
+ return "no command forms are granted for this stage";
50
+ return `granted command forms: ${named.join(", ")}`;
51
+ }
52
+ const PREFER_STRUCTURED_TOOLS_SENTENCE = "PREFER the structured tools when they express the task -- Read, Glob, Grep, Git and RunCommand return " +
53
+ "bounded, parseable output, and Bash exists for what they cannot express. ";
54
+ /**
55
+ * US-001: a background process holding a pipe keeps the shell from exiting
56
+ * cleanly, so the runtime SIGKILLs the whole process group after a short
57
+ * drain grace. Every description variant states this so the model knows its
58
+ * `&`ed processes will not outlive the call.
59
+ */
60
+ const BACKGROUND_PROCESSES_KILLED_SENTENCE = "background processes still holding the command's output are killed when the command exits. ";
61
+ /**
62
+ * `gated`'s description, also used verbatim for `escalate` when no human is
63
+ * reachable -- see the `escalate` branch of `bashToolDescription`.
64
+ */
65
+ function gatedDescription(shell, patterns) {
66
+ return (`Run one shell command string under ${shell}. ${PREFER_STRUCTURED_TOOLS_SENTENCE}` +
67
+ `${describeGrants(patterns)}; anything else is refused. ` +
68
+ "Each segment of a `&&`/`||`/`;`/`|` chain is checked separately, and command substitution ($(...), backticks), " +
69
+ "process substitution, here-documents and `2>&1` are refused outright because they cannot be analysed. " +
70
+ "Paths and redirect targets must stay inside the repository root. " +
71
+ BACKGROUND_PROCESSES_KILLED_SENTENCE);
72
+ }
73
+ function capitalize(text) {
74
+ return text.charAt(0).toUpperCase() + text.slice(1);
75
+ }
76
+ /**
77
+ * `escalate`'s description when a human is reachable (ADR-030, amended
78
+ * 2026-09-23 and again for US-001). It states checkBashCommand's ACTUAL order:
79
+ * the out-of-bounds payload checks and the deny matcher run BEFORE the
80
+ * escalatable sites -- a lexer refusal, a grant miss, and the one payload
81
+ * refusal the gate cannot adjudicate, an option-shaped `cd` -- so an
82
+ * out-of-bounds command never reaches the human; on a refused command those
83
+ * checks cover the lexable prefix -- the part before the unreadable construct
84
+ * -- and an approved command runs as written. Pinned against the policy in
85
+ * coding-tool-bash-escalate-truth.test.ts.
86
+ */
87
+ function escalateDescription(shell, patterns) {
88
+ return (`Run one shell command string under ${shell}. ${PREFER_STRUCTURED_TOOLS_SENTENCE}` +
89
+ `${capitalize(describeGrants(patterns))}. Every command, granted or not, is checked first: paths ` +
90
+ "and redirect targets must stay inside the repository root, and `.git/` access, denied flags, unexpanded " +
91
+ "`$VAR`, glob or brace characters, `~`, a bare `cd`, and a command matching a deny rule are " +
92
+ "refused without asking. For a command using a construct that cannot be analysed (e.g. command or process " +
93
+ "substitution, backticks, here-documents, subshells, `2>&1`, `#` comments, an option-shaped `cd`), these " +
94
+ "checks cover the part before that construct. A command outside the granted forms, or one using a construct " +
95
+ "that cannot be analysed, is not refused: it is sent to a human for approval (unless an identical command " +
96
+ "was already approved and remembered) and, if they allow it, runs exactly as written; it is refused if they " +
97
+ "deny it or do not answer in time, so prefer the granted forms. Each segment of a `&&`/`||`/`;`/`|` chain is " +
98
+ "checked separately. " +
99
+ BACKGROUND_PROCESSES_KILLED_SENTENCE);
100
+ }
101
+ /**
102
+ * `raw`'s description (ADR-030 / F3). Under `raw`, `checkBashCommand` is
103
+ * never consulted at all -- `commandBranch` in policy-command-branch.ts
104
+ * dispatches to `screenRawBashCommand` and returns before it ever reads
105
+ * `denyBy`/`askBy` -- so every clause of `gatedDescription` above is false
106
+ * under this mode, and advertising it would push the model toward the exact
107
+ * workaround (reading whole files instead of piping/grepping them) this ADR
108
+ * exists to stop paying for.
109
+ */
110
+ const RAW_UNCONTAINED = "The command runs from the repository root, but paths are NOT contained to it: a command may read or write anywhere the nax process " +
111
+ "itself can reach, inside the repository or outside it. ";
112
+ function rawDescription(shell, containment = RAW_UNCONTAINED) {
113
+ return (`Run one shell command string under ${shell}. ${PREFER_STRUCTURED_TOOLS_SENTENCE}` +
114
+ "This stage runs under raw mode (ADR-030): pipes, redirects, command substitution ($(...), backticks), " +
115
+ "process substitution, here-documents and subshells all work here -- nothing is refused for being unparseable, " +
116
+ "and Bash allow/deny/ask rules configured for this stage are NOT consulted. " +
117
+ containment +
118
+ "One refusal is a command the lexer CAN parse that names or redirects into one of the exact file " +
119
+ "paths nax owns -- .nax/config.json, .nax/mono/*/config.json, " +
120
+ ".nax/features/**/prd.json, or the root queue-control files -- change those through nax rather than by " + // nax-feature-dir-allow: prose naming the raw-mode protected-path screen, not a path construction
121
+ "writing them directly. That screen is advisory, not a boundary: it matches exact file paths only, so a " +
122
+ "command using command substitution, a directory target (cp x .nax/), a glob, a nested shell (sh -c '...'), " +
123
+ "tar -C or dd of=, or a symlink alias all skip it; use the sandbox for a boundary. " +
124
+ "A `find` whose start path is `/`, `~` or `$HOME` is refused -- search within the repository instead. " +
125
+ BACKGROUND_PROCESSES_KILLED_SENTENCE);
126
+ }
127
+ function rawUnavailableDescription(shell, reason) {
128
+ return (`Run one shell command string under ${shell} -- but on this machine ${rawBashRefusalReason(reason)} ` +
129
+ "Under raw mode every call is refused; use the structured tools (Read, Glob, Grep, Git, RunCommand) instead. " +
130
+ BACKGROUND_PROCESSES_KILLED_SENTENCE);
131
+ }
132
+ function bashToolDescription(shell, opts) {
133
+ const state = opts.launcher?.state ?? { kind: "disabled" };
134
+ if (opts.bashApproval === "raw") {
135
+ if (state.kind === "available")
136
+ return rawDescription(shell, `The command runs from the repository root ${sandboxSentence(state.network, state.sharedTmp !== false)} `);
137
+ if (state.kind === "unavailable")
138
+ return rawUnavailableDescription(shell, state.reason);
139
+ return rawDescription(shell);
140
+ }
141
+ // `escalate` describes escalation ONLY when a human is reachable (ADR-030,
142
+ // amended for P4). Reachability is a separate config axis -- is an
143
+ // interaction channel configured? -- resolved at the execution stage and
144
+ // passed in as data. Without one every ask resolves `unavailable` and
145
+ // denies, so the wording stays byte-identical to `gated`'s: promising a
146
+ // human there would be the D13a fail-open shape stated in prose.
147
+ const policyDescription = opts.bashApproval === "escalate" && opts.humanApproval === true
148
+ ? escalateDescription(shell, opts.patterns)
149
+ : gatedDescription(shell, opts.patterns);
150
+ if (state.kind === "available") {
151
+ return `${policyDescription} Commands that pass run ${sandboxSentence(state.network, state.sharedTmp !== false)}`;
152
+ }
153
+ if (state.kind === "unavailable")
154
+ return `${policyDescription} ${unsandboxedSentence(state.reason)}`;
155
+ return policyDescription;
156
+ }
157
+ /**
158
+ * Intercept, launch, post-process — everything that decides WHICH command ran.
159
+ * `run` keeps only the framing.
160
+ *
161
+ * The interception belongs HERE and not above the tool: `callTool` has already
162
+ * lexed and gated the model's ORIGINAL string against the stage's `Bash(...)`
163
+ * rules, and this is the first point after that verdict. The rewrite is
164
+ * therefore never the string the policy judged, which is exactly why
165
+ * `interceptShell` re-validates the provider's answer rather than trusting it.
166
+ */
167
+ async function launchIntercepted({ command, shell, opts, ctx, timeoutMs, }) {
168
+ // The run's interceptor arrives on the context (S1 spec port 7), immutable
169
+ // for this call, so the launch below and `postProcess` see the same object.
170
+ const interceptor = ctx.interceptor;
171
+ const intercepted = await interceptShell(command, ctx.root, interceptor);
172
+ const executed = [shell, "-c", intercepted.command];
173
+ // US-004: opt the child into agent-friendly output. The same rule the
174
+ // quality and verification runners apply, just at the agent-facing spawn
175
+ // site. Marker presence is read from the nax process environment (via
176
+ // `_agentOutputEnvDeps.processEnv`), so an inherited CLAUDECODE/REPL_ID is
177
+ // left to speak for itself, and a stripped AGENT stays stripped. The overlay
178
+ // is a small record layered on top of `process.env` — undefined when there
179
+ // is nothing to add, so the child inherits its parent untouched.
180
+ const agentOverlay = agentOutputOverlay(opts.stripEnvVars ?? []);
181
+ const common = {
182
+ cwd: ctx.root,
183
+ timeoutMs,
184
+ stripEnvVars: [...(opts.stripEnvVars ?? [])],
185
+ ...(agentOverlay !== undefined ? { env: agentOverlay } : {}),
186
+ ...(ctx.signal !== undefined ? { signal: ctx.signal } : {}),
187
+ };
188
+ const launched = opts.launcher !== undefined
189
+ ? await opts.launcher.run({
190
+ spec: { kind: "shell", shell, command: intercepted.command },
191
+ root: ctx.root,
192
+ ...common,
193
+ })
194
+ : { ...(await _bashToolDeps.runArgv({ argv: executed, ...common })), executed, sandbox: undefined };
195
+ if (!intercepted.rewritten)
196
+ return { launched, stdout: launched.stdout };
197
+ // The request describes the command the MODEL wrote — the same shape
198
+ // `interceptShell` sent — so post-processing stays keyed to the original,
199
+ // mirroring the Git site, which passes the original argv.
200
+ const req = { kind: "shell", command, cwd: ctx.root, site: "bash" };
201
+ try {
202
+ const processed = interceptor?.postProcess?.(launched.stdout, req)?.output;
203
+ return { launched, stdout: processed ?? launched.stdout };
204
+ }
205
+ catch {
206
+ // Advisory, like the rewrite-time fail-open: a sick post-processor must not
207
+ // discard the output of a command that actually ran.
208
+ return { launched, stdout: launched.stdout };
209
+ }
210
+ }
211
+ export function createBashTool(opts = {}) {
212
+ const shell = opts.shell ?? DEFAULT_BASH_SHELL;
213
+ return {
214
+ name: BASH_TOOL_NAME,
215
+ // A non-zero exit from a command the model wrote is its own red/green loop,
216
+ // not a fault worth an operator's attention — the same reason RunCommand
217
+ // sets this.
218
+ routineErrors: true,
219
+ description: bashToolDescription(shell, opts),
220
+ inputSchema: {
221
+ type: "object",
222
+ properties: {
223
+ command: { type: "string", description: 'The shell command to run, e.g. "bun test src/a.test.ts"' },
224
+ timeoutMs: {
225
+ type: "number",
226
+ description: `Deadline in milliseconds (default and maximum ${BASH_TIMEOUT_MS}).`,
227
+ },
228
+ description: { type: "string", description: "One short line on what this command is for." },
229
+ },
230
+ required: ["command"],
231
+ },
232
+ // `commandField` is what routes this call to the Bash branch of the policy
233
+ // (src/tools/policy-bash.ts). No pathFields: the paths are inside the
234
+ // command string, where only that branch can see them.
235
+ scope: { pathFields: [], commandField: "command" },
236
+ async run(input, ctx) {
237
+ const command = input.command;
238
+ if (typeof command !== "string" || command.trim() === "") {
239
+ return { content: '"command" must be a non-empty string', isError: true };
240
+ }
241
+ const requested = typeof input.timeoutMs === "number" && Number.isFinite(input.timeoutMs) ? input.timeoutMs : BASH_TIMEOUT_MS;
242
+ const timeoutMs = Math.min(Math.max(Math.trunc(requested), MIN_BASH_TIMEOUT_MS), BASH_TIMEOUT_MS);
243
+ try {
244
+ const { launched, stdout } = await launchIntercepted({ command, shell, opts, ctx, timeoutMs });
245
+ // Same framing ladder as RunCommand (Exec) via the shared
246
+ // `formatExecBody`: an aborted call opens with the cancellation line
247
+ // and surfaces whatever partial output the readers captured before the
248
+ // process group was SIGKILLed (AC14); an orphansKilled call appends the
249
+ // "[nax] background processes ..." footer so the model can tell that a
250
+ // `&`ed process held the pipe and was reaped (AC15). Both are
251
+ // independent of the regular `timed out` / `exit N` framing.
252
+ const body = formatExecBody({ ...launched, stdout }, timeoutMs);
253
+ // The tool's own bound is the I/O ceiling, not the model-facing cap:
254
+ // `maxBytes` shapes what the model is told and belongs to the session's
255
+ // truncation policy (which also spills what it cuts), while this one
256
+ // only keeps a runaway command from being buffered without limit. The
257
+ // full size still rides out on `resultBytesPreTruncation`.
258
+ return {
259
+ content: cutToByteCap(body, ctx.readCeiling ?? READ_CEILING),
260
+ isError: launched.timedOut || launched.exitCode !== 0 || launched.aborted === true,
261
+ // The ledger records what actually ran, not what was requested.
262
+ // `exitCode` is recorded only when nax did not kill the process
263
+ // group: under `timedOut` or `aborted` the code is nax's own kill
264
+ // (128+signal) or the -1 never-spawned sentinel, not the command's.
265
+ // `orphansKilled` still records it -- the shell exited by itself and
266
+ // only background processes holding the pipe were reaped afterward.
267
+ audit: {
268
+ executed: launched.executed,
269
+ ...(launched.sandbox !== undefined ? { sandbox: launched.sandbox } : {}),
270
+ ...(!launched.timedOut && launched.aborted !== true ? { exitCode: launched.exitCode } : {}),
271
+ },
272
+ resultBytesPreTruncation: Buffer.byteLength(body, "utf8"),
273
+ };
274
+ }
275
+ catch (err) {
276
+ // A spawn-time failure (an unresolvable cwd, a missing shell) rejects
277
+ // rather than resolving with an exit code; surfaced as an ordinary tool
278
+ // error so it is indistinguishable from any other refusal above.
279
+ return { content: err instanceof Error ? err.message : String(err), isError: true };
280
+ }
281
+ },
282
+ };
283
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Delete a file whose path the policy already resolved and approved.
3
+ *
4
+ * nax#1972: this used to be tracked-only, on the theory that a tracked
5
+ * file's content is in git history (an undo away) while an untracked file
6
+ * exists only on disk and is therefore unrecoverable. That rationale is
7
+ * retired -- it made the guard unsatisfiable for a file the agent itself
8
+ * wrote this session, which is untracked by construction. Paired with the
9
+ * #1937 denial redirect (Exec `rm` bounces back to Delete), the agent had no
10
+ * legal way to remove its own scratch output, and its fallback was to blank
11
+ * files to 0 bytes with Write -- which the auto-commit sweep then carried
12
+ * into history anyway. Refusing the deletion did not make anything more
13
+ * recoverable; it just moved the damage to a different tool.
14
+ *
15
+ * The new rule follows the three states git actually puts a path in:
16
+ *
17
+ * - tracked -> allow (unchanged)
18
+ * - untracked AND gitignored -> refuse (unchanged verdict, new reason)
19
+ * - untracked and NOT gitignored -> allow (the fix)
20
+ *
21
+ * Both untracked classes are equally unrecoverable -- neither is in git
22
+ * history -- so recoverability alone no longer explains the split. What
23
+ * distinguishes them is declared intent. A gitignored path has a rule
24
+ * somebody wrote on purpose, saying "this lives only on this machine"
25
+ * (`.env`, `.nax-pids`, `.claude/settings.local.json`, the fragments and
26
+ * plan-log directories under `.nax` are real examples in this repo) --
27
+ * deleting it is
28
+ * unrecoverable AND was never meant to be sent anywhere else, so the refusal
29
+ * stays, but the message now says why instead of telling the agent to commit
30
+ * a file its own author chose to keep out of git. An untracked-and-not-
31
+ * ignored path has no such rule attached to it at all: nobody has said
32
+ * anything about it one way or the other. In practice that is agent scratch
33
+ * or work in progress, so it is allowed.
34
+ *
35
+ * Accepted cost: a genuine new source file the agent wrote and has not yet
36
+ * committed is untracked-and-not-ignored too, so this permission also makes
37
+ * IT deletable -- there is no signal in git alone that separates "my own
38
+ * throwaway probe" from "a real file I just haven't committed yet". That is
39
+ * deliberate, not an oversight: it is the same permission that lets an agent
40
+ * clean up after itself, and the completion-phase auto-commit sweep
41
+ * (`autoCommitIfDirty`, `src/utils/git.ts`) is the backstop if something
42
+ * real gets removed before it was staged.
43
+ *
44
+ * `.git/` never reaches this point at all -- `resolveWithin`
45
+ * (`src/tools/policy.ts`) excludes it for every path-bearing tool, so
46
+ * `ctx.resolvedPaths` cannot contain a `.git/` path in the first place
47
+ * (nax#1943). Re-checking it here would be exactly the per-tool
48
+ * re-remembering that issue exists to prevent.
49
+ *
50
+ * Not `git rm`: gitTool documents a deliberate read/write split, and `git rm`
51
+ * both deletes and stages, folding two capabilities into one call. Plain
52
+ * unlink plus the existing GitCommit keeps each seam doing one thing --
53
+ * `git add -- <deleted path>` stages a deletion, so no change to GitCommit is
54
+ * needed.
55
+ */
56
+ import type { CodingTool } from "./registry.ts";
57
+ export declare const deleteTool: CodingTool;