@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,98 @@
1
+ /**
2
+ * Shared model-facing truncation policy — US-001.
3
+ *
4
+ * The single byte-safe implementation that `after_tool` will apply, replacing
5
+ * the per-tool `truncate(body, ctx.maxBytes)` helpers. Constants, direction
6
+ * lookup, and the core algorithm all live here so a future regression that
7
+ * resuscitates a per-tool slicer sits behind one import rather than 5+.
8
+ *
9
+ * Three independent caps compose as an ORDERED PIPELINE, never as alternatives:
10
+ * 1. Per-line cap: every line longer than MODEL_MAX_LINE_CHARS (UTF-16
11
+ * code units) is shortened.
12
+ * 2. Line-count cap: if still over MODEL_MAX_LINES lines, direction selects
13
+ * which lines to keep — over the WHOLE body, not over a byte window of it.
14
+ * 3. Byte cap LAST: if the result still exceeds MODEL_MAX_BYTES, cut on a
15
+ * codepoint boundary. Running it last is what makes the byte ceiling
16
+ * unconditional — nothing is appended or prepended after the cut. Under
17
+ * `tail-with-first-line` the first line is retained INSIDE that budget,
18
+ * with the trailing slice taken against what remains after the first line
19
+ * and its newline; if the first line alone does not fit, it is itself cut.
20
+ *
21
+ * Line counting: a trailing newline TERMINATES the last line rather than
22
+ * opening an empty one, and an empty body has no lines, matching
23
+ * `readFileSlice`'s `totalLines` and `readTool`'s `[N lines]` header.
24
+ */
25
+ /** Tool-layer I/O bound for ranged file reads. */
26
+ export declare const READ_CEILING = 2000000;
27
+ export declare const MODEL_MAX_BYTES = 40000;
28
+ export declare const MODEL_MAX_LINES = 1000;
29
+ export declare const MODEL_MAX_LINE_CHARS = 2000;
30
+ export type TruncationDirection = "head" | "tail-with-first-line";
31
+ /** Result of `truncateForModel`: the rewritten body and whether any stage changed it. */
32
+ export interface TruncationResult {
33
+ readonly content: string;
34
+ readonly truncated: boolean;
35
+ /** Full UTF-8 byte length of the input, regardless of whether anything was truncated. */
36
+ readonly originalBytes: number;
37
+ }
38
+ /** Options passed to `truncateForModel`. Direction is determined by tool name via `truncationDirectionFor`. */
39
+ export interface TruncateForModelOptions {
40
+ readonly direction: TruncationDirection;
41
+ }
42
+ /** Look up the truncation direction for a tool by name. */
43
+ export declare function truncationDirectionFor(toolName: string): TruncationDirection;
44
+ /**
45
+ * Split a body into lines honouring the trailing-newline convention:
46
+ * "a\nb\n" is two lines, not three. An empty body yields zero lines.
47
+ *
48
+ * Exported because the spill-and-marker composition (src/tools/spill.ts) has
49
+ * to count the same lines this policy counts: a marker line occupies one of
50
+ * `MODEL_MAX_LINES`, and counting it with a raw `split("\n")` would let the
51
+ * delivered content exceed the cap by exactly the phantom empty line.
52
+ */
53
+ export declare function splitModelLines(body: string): string[];
54
+ /**
55
+ * Shorten a single line so its UTF-16 code-unit length is at most
56
+ * `maxCodeUnits`. Backs up one code unit if the cut would land on a high
57
+ * surrogate (the lead of a surrogate pair), so the result never carries
58
+ * a lone surrogate that would re-encode as U+FFFD in a downstream stage.
59
+ *
60
+ * Exported for the same reason `splitModelLines` is: the marker composition
61
+ * keeps lines that the policy has not yet shaped, and it must shorten them
62
+ * the one way the policy shortens them.
63
+ */
64
+ export declare function capModelLine(line: string, maxCodeUnits: number): string;
65
+ /**
66
+ * Cut `body` to at most `maxBytes` bytes on a clean codepoint boundary.
67
+ * Nothing is appended after the cut: the byte cap is unconditional.
68
+ *
69
+ * The one codepoint-boundary slicer in the tool layer — `readFileSlice`
70
+ * needs the same guarantee for its own I/O bound, so it shares this rather
71
+ * than growing a second copy.
72
+ *
73
+ * The boundary is found STRUCTURALLY, by walking back over continuation bytes,
74
+ * not by measuring the decoded candidate's byte length. Measurement is unsound:
75
+ * a slice that ends mid-codepoint decodes into a U+FFFD, and that replacement
76
+ * costs 3 bytes — exactly what a truncated 4-byte codepoint contributed when
77
+ * three of its four bytes were included. The length check then passes while the
78
+ * output carries a replacement character, which is the outcome the codepoint
79
+ * boundary exists to prevent. Walking back stops where a codepoint starts, so
80
+ * the slice is always whole codepoints and re-encodes to exactly `cut` bytes.
81
+ */
82
+ export declare function cutBufferToByteCap(buffer: Buffer, maxBytes: number): Buffer;
83
+ export declare function cutToByteCap(body: string, maxBytes: number): string;
84
+ /**
85
+ * Apply the model-facing truncation policy to a tool result body.
86
+ *
87
+ * Three stages, in order, each independently reachable: per-line cap,
88
+ * line-count cap, byte cap. The pipeline reports `truncated: true` when
89
+ * any stage changed the content. `originalBytes` always reports the
90
+ * input's full UTF-8 byte length, regardless of which stages fired.
91
+ *
92
+ * Direction governs both stages that drop content: the line-count cap keeps
93
+ * the first N lines (`head`) or the first line plus the last N-1 lines
94
+ * (`tail-with-first-line`), and the byte cap keeps the leading bytes or the
95
+ * first line plus the trailing bytes the same way. Within-cap bodies are
96
+ * returned unchanged regardless of direction.
97
+ */
98
+ export declare function truncateForModel(body: string, opts: TruncateForModelOptions): TruncationResult;
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Shared model-facing truncation policy — US-001.
3
+ *
4
+ * The single byte-safe implementation that `after_tool` will apply, replacing
5
+ * the per-tool `truncate(body, ctx.maxBytes)` helpers. Constants, direction
6
+ * lookup, and the core algorithm all live here so a future regression that
7
+ * resuscitates a per-tool slicer sits behind one import rather than 5+.
8
+ *
9
+ * Three independent caps compose as an ORDERED PIPELINE, never as alternatives:
10
+ * 1. Per-line cap: every line longer than MODEL_MAX_LINE_CHARS (UTF-16
11
+ * code units) is shortened.
12
+ * 2. Line-count cap: if still over MODEL_MAX_LINES lines, direction selects
13
+ * which lines to keep — over the WHOLE body, not over a byte window of it.
14
+ * 3. Byte cap LAST: if the result still exceeds MODEL_MAX_BYTES, cut on a
15
+ * codepoint boundary. Running it last is what makes the byte ceiling
16
+ * unconditional — nothing is appended or prepended after the cut. Under
17
+ * `tail-with-first-line` the first line is retained INSIDE that budget,
18
+ * with the trailing slice taken against what remains after the first line
19
+ * and its newline; if the first line alone does not fit, it is itself cut.
20
+ *
21
+ * Line counting: a trailing newline TERMINATES the last line rather than
22
+ * opening an empty one, and an empty body has no lines, matching
23
+ * `readFileSlice`'s `totalLines` and `readTool`'s `[N lines]` header.
24
+ */
25
+ /** Tool-layer I/O bound for ranged file reads. */
26
+ export const READ_CEILING = 2_000_000;
27
+ export const MODEL_MAX_BYTES = 40_000;
28
+ export const MODEL_MAX_LINES = 1_000;
29
+ export const MODEL_MAX_LINE_CHARS = 2_000;
30
+ /** Tools whose output direction is `tail-with-first-line` — keep the start AND the end. */
31
+ const TAIL_DIRECTION_TOOLS = new Set(["Bash", "RunCommand", "Exec"]);
32
+ /** Look up the truncation direction for a tool by name. */
33
+ export function truncationDirectionFor(toolName) {
34
+ if (TAIL_DIRECTION_TOOLS.has(toolName))
35
+ return "tail-with-first-line";
36
+ return "head";
37
+ }
38
+ /**
39
+ * Split a body into lines honouring the trailing-newline convention:
40
+ * "a\nb\n" is two lines, not three. An empty body yields zero lines.
41
+ *
42
+ * Exported because the spill-and-marker composition (src/tools/spill.ts) has
43
+ * to count the same lines this policy counts: a marker line occupies one of
44
+ * `MODEL_MAX_LINES`, and counting it with a raw `split("\n")` would let the
45
+ * delivered content exceed the cap by exactly the phantom empty line.
46
+ */
47
+ export function splitModelLines(body) {
48
+ if (body === "")
49
+ return [];
50
+ const trimmed = body.endsWith("\n") ? body.slice(0, -1) : body;
51
+ return trimmed.split("\n");
52
+ }
53
+ /**
54
+ * Shorten a single line so its UTF-16 code-unit length is at most
55
+ * `maxCodeUnits`. Backs up one code unit if the cut would land on a high
56
+ * surrogate (the lead of a surrogate pair), so the result never carries
57
+ * a lone surrogate that would re-encode as U+FFFD in a downstream stage.
58
+ *
59
+ * Exported for the same reason `splitModelLines` is: the marker composition
60
+ * keeps lines that the policy has not yet shaped, and it must shorten them
61
+ * the one way the policy shortens them.
62
+ */
63
+ export function capModelLine(line, maxCodeUnits) {
64
+ if (line.length <= maxCodeUnits)
65
+ return line;
66
+ let cut = maxCodeUnits;
67
+ // Avoid splitting a surrogate pair: if the last code unit kept is a
68
+ // high surrogate, back up one so the pair stays whole.
69
+ if (cut > 0) {
70
+ const last = line.charCodeAt(cut - 1);
71
+ if (last >= 0xd800 && last <= 0xdbff)
72
+ cut -= 1;
73
+ }
74
+ return line.slice(0, cut);
75
+ }
76
+ /**
77
+ * Apply the per-line cap (stage 1) to every line, preserving the array
78
+ * shape so later stages can count and slice by line.
79
+ */
80
+ function applyLineCharCap(lines, maxLineChars) {
81
+ let changed = false;
82
+ const out = [];
83
+ for (const line of lines) {
84
+ if (line.length <= maxLineChars) {
85
+ out.push(line);
86
+ continue;
87
+ }
88
+ changed = true;
89
+ out.push(capModelLine(line, maxLineChars));
90
+ }
91
+ return { lines: out, changed };
92
+ }
93
+ /**
94
+ * Apply the line-count cap (stage 2) by direction, chosen over the WHOLE
95
+ * body rather than over a byte window of it. `head` keeps the first N
96
+ * lines and drops the rest; `tail-with-first-line` keeps the first line
97
+ * followed by the last N-1 lines, dropping the middle.
98
+ */
99
+ function applyLineCountCap(lines, maxLines, direction) {
100
+ if (lines.length <= maxLines)
101
+ return { lines, changed: false };
102
+ if (direction === "head") {
103
+ return { lines: lines.slice(0, maxLines), changed: true };
104
+ }
105
+ // tail-with-first-line: first line + last (maxLines - 1) lines.
106
+ // When maxLines is 0 or 1 the result is just the first line.
107
+ const tailCount = Math.max(0, maxLines - 1);
108
+ const tail = lines.slice(-tailCount);
109
+ const head = lines.slice(0, 1);
110
+ return { lines: [...head, ...tail], changed: true };
111
+ }
112
+ /**
113
+ * True when `byte` is a UTF-8 continuation byte (`10xxxxxx`) — meaning the
114
+ * position it sits at is INSIDE a multi-byte codepoint, not at its start.
115
+ */
116
+ function isContinuationByte(byte) {
117
+ return byte !== undefined && (byte & 0xc0) === 0x80;
118
+ }
119
+ function utf8SequenceLength(byte) {
120
+ if (byte === undefined || byte < 0x80)
121
+ return 1;
122
+ if ((byte & 0xe0) === 0xc0)
123
+ return 2;
124
+ if ((byte & 0xf0) === 0xe0)
125
+ return 3;
126
+ if ((byte & 0xf8) === 0xf0)
127
+ return 4;
128
+ return 1;
129
+ }
130
+ /**
131
+ * Cut `body` to at most `maxBytes` bytes on a clean codepoint boundary.
132
+ * Nothing is appended after the cut: the byte cap is unconditional.
133
+ *
134
+ * The one codepoint-boundary slicer in the tool layer — `readFileSlice`
135
+ * needs the same guarantee for its own I/O bound, so it shares this rather
136
+ * than growing a second copy.
137
+ *
138
+ * The boundary is found STRUCTURALLY, by walking back over continuation bytes,
139
+ * not by measuring the decoded candidate's byte length. Measurement is unsound:
140
+ * a slice that ends mid-codepoint decodes into a U+FFFD, and that replacement
141
+ * costs 3 bytes — exactly what a truncated 4-byte codepoint contributed when
142
+ * three of its four bytes were included. The length check then passes while the
143
+ * output carries a replacement character, which is the outcome the codepoint
144
+ * boundary exists to prevent. Walking back stops where a codepoint starts, so
145
+ * the slice is always whole codepoints and re-encodes to exactly `cut` bytes.
146
+ */
147
+ export function cutBufferToByteCap(buffer, maxBytes) {
148
+ let cut = Math.min(buffer.length, maxBytes);
149
+ while (cut > 0 && isContinuationByte(buffer[cut]))
150
+ cut -= 1;
151
+ let sequenceStart = cut - 1;
152
+ while (sequenceStart >= 0 && isContinuationByte(buffer[sequenceStart]))
153
+ sequenceStart -= 1;
154
+ if (sequenceStart >= 0 && sequenceStart + utf8SequenceLength(buffer[sequenceStart]) > cut) {
155
+ cut = sequenceStart;
156
+ }
157
+ return buffer.subarray(0, cut);
158
+ }
159
+ export function cutToByteCap(body, maxBytes) {
160
+ const buffer = Buffer.from(body, "utf8");
161
+ return cutBufferToByteCap(buffer, maxBytes).toString("utf8");
162
+ }
163
+ /**
164
+ * Cut `body` down to its LAST `maxBytes` bytes, on a clean codepoint boundary.
165
+ * The mirror of `cutToByteCap`: that one backs up over the continuation bytes a
166
+ * cut-inside-a-codepoint would end on, this one skips forward over the
167
+ * continuation bytes such a start would begin on. Same structural rule, same
168
+ * reason — measuring the decoded candidate would accept a tail that opens with
169
+ * a U+FFFD whenever the replacement costs no more bytes than the partial
170
+ * codepoint it replaced.
171
+ */
172
+ function tailWithinBytes(body, maxBytes) {
173
+ const buf = Buffer.from(body, "utf8");
174
+ if (buf.length <= maxBytes)
175
+ return body;
176
+ let start = buf.length - maxBytes;
177
+ while (start < buf.length && isContinuationByte(buf[start]))
178
+ start += 1;
179
+ return buf.subarray(start).toString("utf8");
180
+ }
181
+ /**
182
+ * Cut a `tail-with-first-line` body to `maxBytes`: the body's first line, then
183
+ * its LAST bytes, dropping the middle. The byte stage's half of the same choice
184
+ * the line-count stage makes — a body cut for length should lose its middle,
185
+ * not its end, when the direction says the end is what matters.
186
+ *
187
+ * The first line is retained INSIDE the budget, not on top of it: the trailing
188
+ * slice is measured against what remains once the first line and its newline
189
+ * are paid for. If the first line alone does not fit, it is itself cut. Either
190
+ * way nothing is appended after the cut — the trailing slice is what fills the
191
+ * remaining budget, never an extra.
192
+ */
193
+ function cutKeepingFirstLine(body, maxBytes) {
194
+ const lineEnd = body.indexOf("\n");
195
+ // No newline: the body is one line, so there is no tail distinct from its head.
196
+ if (lineEnd === -1)
197
+ return cutToByteCap(body, maxBytes);
198
+ const firstLine = body.slice(0, lineEnd);
199
+ const firstLineBytes = Buffer.byteLength(firstLine, "utf8");
200
+ // Paying for the first line and its newline leaves no room for a tail.
201
+ if (firstLineBytes + 1 >= maxBytes)
202
+ return cutToByteCap(firstLine, maxBytes);
203
+ const tail = tailWithinBytes(body.slice(lineEnd + 1), maxBytes - firstLineBytes - 1);
204
+ return `${firstLine}\n${tail}`;
205
+ }
206
+ /**
207
+ * Apply the model-facing truncation policy to a tool result body.
208
+ *
209
+ * Three stages, in order, each independently reachable: per-line cap,
210
+ * line-count cap, byte cap. The pipeline reports `truncated: true` when
211
+ * any stage changed the content. `originalBytes` always reports the
212
+ * input's full UTF-8 byte length, regardless of which stages fired.
213
+ *
214
+ * Direction governs both stages that drop content: the line-count cap keeps
215
+ * the first N lines (`head`) or the first line plus the last N-1 lines
216
+ * (`tail-with-first-line`), and the byte cap keeps the leading bytes or the
217
+ * first line plus the trailing bytes the same way. Within-cap bodies are
218
+ * returned unchanged regardless of direction.
219
+ */
220
+ export function truncateForModel(body, opts) {
221
+ const originalBytes = Buffer.byteLength(body, "utf8");
222
+ // Split the body into lines once; every cap is checked against the
223
+ // line-aware view, so we don't pay for `split("\n")` three times.
224
+ const lines = splitModelLines(body);
225
+ // Per-line cap is measured in UTF-16 code units — the same metric
226
+ // `String#length` reports — so a 2_000-character line of single-unit
227
+ // codepoints is at the cap, not over it. It is independent of the byte
228
+ // ceiling: stage 3 alone decides which otherwise-valid bytes are retained.
229
+ const perLineOver = lines.some((l) => l.length > MODEL_MAX_LINE_CHARS);
230
+ let working = lines;
231
+ let changed = false;
232
+ // Stage 1: per-line cap.
233
+ if (perLineOver) {
234
+ const capped = applyLineCharCap(working, MODEL_MAX_LINE_CHARS);
235
+ working = capped.lines;
236
+ if (capped.changed)
237
+ changed = true;
238
+ }
239
+ // Stage 2: line-count cap. The cap fires only when the body is strictly
240
+ // over MODEL_MAX_LINES — within-cap bodies must be returned unchanged
241
+ // per AC1, so no preview is allowed at smaller scales.
242
+ if (working.length > MODEL_MAX_LINES) {
243
+ const trimmed = applyLineCountCap(working, MODEL_MAX_LINES, opts.direction);
244
+ working = trimmed.lines;
245
+ if (trimmed.changed)
246
+ changed = true;
247
+ }
248
+ // Re-join with newlines. The trailing-newline convention: the "\n" is
249
+ // preserved only when the body ended in one and NO stage modified it —
250
+ // `changed` covers a line merely SHORTENED by stage 1 as well as one
251
+ // DROPPED by stage 2. Gating on "dropped" alone would be wrong: stage 2
252
+ // fires only at strictly more than MODEL_MAX_LINES, so a stage-1-shortened
253
+ // body can sit at exactly MODEL_MAX_LINES lines, and re-emitting "\n" there
254
+ // inflates `split("\n").length` to MODEL_MAX_LINES + 1 — past the cap that
255
+ // AC4 pins via the naive split. The conservative guard is deliberate: a
256
+ // modified body has already lost byte-for-byte fidelity, so dropping its
257
+ // terminator costs nothing the caller can observe, while re-adding it can
258
+ // break the count.
259
+ const joined = working.join("\n");
260
+ let rebuilt = !changed && body.endsWith("\n") && working.length > 0 ? `${joined}\n` : joined;
261
+ // Stage 3: byte cap. Runs last, so the byte ceiling is unconditional —
262
+ // nothing is appended after the cut. The direction decides which bytes
263
+ // survive the cut, exactly as it decided which lines survived stage 2:
264
+ // `head` keeps the leading bytes, `tail-with-first-line` keeps the first
265
+ // line plus the trailing bytes and drops the middle.
266
+ let truncated = changed;
267
+ if (Buffer.byteLength(rebuilt, "utf8") > MODEL_MAX_BYTES) {
268
+ rebuilt =
269
+ opts.direction === "tail-with-first-line"
270
+ ? cutKeepingFirstLine(rebuilt, MODEL_MAX_BYTES)
271
+ : cutToByteCap(rebuilt, MODEL_MAX_BYTES);
272
+ truncated = true;
273
+ }
274
+ // Trim a trailing surrogate so the byte cap (or the per-line cap) cannot
275
+ // leave the body ending on a lone half of a surrogate pair. A cut that
276
+ // lands on a UTF-16 surrogate code unit is a structural artifact of the
277
+ // cap — there is no scenario in which ending the body on a surrogate is
278
+ // intentional, and downstream UTF-8 decoding of such a body produces a
279
+ // U+FFFD replacement character that the model cannot distinguish from
280
+ // a real codepoint. We only strip when something above changed the
281
+ // content; an untruncated body that happens to end on a surrogate is
282
+ // already what the caller gave us and is left alone.
283
+ if (truncated && rebuilt.length > 0) {
284
+ const lastChar = rebuilt.charCodeAt(rebuilt.length - 1);
285
+ if (lastChar >= 0xd800 && lastChar <= 0xdfff) {
286
+ let trimmed = rebuilt.length;
287
+ while (trimmed > 0) {
288
+ const c = rebuilt.charCodeAt(trimmed - 1);
289
+ if (c < 0xd800 || c > 0xdfff)
290
+ break;
291
+ trimmed -= 1;
292
+ }
293
+ rebuilt = rebuilt.slice(0, trimmed);
294
+ }
295
+ }
296
+ return { content: rebuilt, truncated, originalBytes };
297
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Shared vocabulary for nax's own coding tools.
3
+ *
4
+ * Deliberately free of any transport type: this module is imported by the
5
+ * policy, the tools and the runtime, none of which may see `@nathapp/nax-ai`
6
+ * (check:nax-ai-imports confines that package to src/agents/native/).
7
+ */
8
+ /**
9
+ * Policy identity for RunCommand's allowlisted, model-authored argv branch,
10
+ * and the marker an operation's `tools` declaration uses to request it.
11
+ * `Exec` is not a registered tool — nothing in `registerBuiltinCodingTools`
12
+ * carries this name, and it is reserved (`RESERVED_TOOL_NAMES`) precisely so
13
+ * no third party can register a tool that shadows the identity.
14
+ */
15
+ export declare const EXEC_TOOL_NAME = "Exec";
16
+ /**
17
+ * Policy identity of the model-authored shell tool.
18
+ *
19
+ * Unlike `Exec`, `Bash` IS a registered tool — but a session-local one
20
+ * (`createBashTool`), because it needs the project's shell and env-strip list.
21
+ * Reserved (`RESERVED_TOOL_NAMES`) so no third party can register a tool that
22
+ * shadows the identity the policy gates.
23
+ */
24
+ export declare const BASH_TOOL_NAME = "Bash";
25
+ /** The tools nax ships. Third parties register additional names at runtime. */
26
+ export type CodingToolName = "Read" | "Glob" | "Grep" | "Write" | "Edit" | "Delete" | "Git" | "GitCommit" | "RunCommand" | "RequestCapability" | "Exec" | "Bash" | "ScratchpadWrite" | "ScratchpadRead" | "ScratchpadList";
27
+ /**
28
+ * One declarative permission grant, as produced by resolvePermissions.
29
+ *
30
+ * `patterns` is either globs over the tool's path-bearing fields
31
+ * (`["src/**"]`), or the verb list for a verb-gated tool (`["diff","log"]`).
32
+ * `["*"]` means unconditional — but never wider than the root.
33
+ */
34
+ export interface ToolGrant {
35
+ readonly tool: string;
36
+ readonly patterns: readonly string[];
37
+ }
38
+ /**
39
+ * How the policy gates a given tool, declared by the tool itself.
40
+ *
41
+ * Declaring the path-bearing fields is what lets the policy gate a tool it has
42
+ * no special knowledge of, including one registered by a third party. A tool
43
+ * with no path fields is gated at the tool/verb level instead — the honest
44
+ * expression for something whose arguments are not paths.
45
+ *
46
+ * `arrayPathFields` and `refPathFields` extend the same containment seam to
47
+ * array-valued inputs — Git is the only current user.
48
+ *
49
+ * A verb-gated tool's grant list is overloaded: `["diff","log"]` are verbs, so
50
+ * matching them against a path denies everything. The policy separates the two
51
+ * by `allowedVerbs`, a closed set the tool declares — a pattern naming a
52
+ * permitted verb is a verb, anything else is a path glob. So `Git(diff,src/**)`
53
+ * grants the `diff` verb over `src/**`, and `Git(diff)` declares no path glob
54
+ * and is bounded by the root alone.
55
+ */
56
+ export interface ToolScope {
57
+ /**
58
+ * String paths in the input. Dot-separated names address nested object
59
+ * properties (for example `values.files` in RunCommand).
60
+ */
61
+ readonly pathFields: readonly string[];
62
+ /**
63
+ * Root-relative subdirectory the tool's path fields are confined to.
64
+ * Containment runs against `<root>/<confineTo>` instead of `<root>`, so a
65
+ * path-bearing tool can never reach the rest of the repository even under
66
+ * an unconditional grant. Grant globs, deny rules and `naxOwnedWriteRefusal`
67
+ * keep seeing the canonical repo-root-relative spelling: containment is the
68
+ * single seam, only its ROOT shifts. Tool-declared, never config-declared --
69
+ * a mis-set `pathFields` grant must not be able to widen the tool's reach.
70
+ */
71
+ readonly confineTo?: string;
72
+ /** Array-valued fields whose every element is a path. */
73
+ readonly arrayPathFields?: readonly string[];
74
+ /**
75
+ * String fields whose value is a whitespace-separated LIST of paths, every
76
+ * element resolved and grant-checked on its own (`splitPathList`).
77
+ *
78
+ * Distinct from `pathFields`, which checks the value as ONE path. That is
79
+ * sound only while the value also REACHES the shell as one argument: a
80
+ * multi-element value like `"a.test.ts ../../etc/passwd"` resolves, as a
81
+ * single path, to a location inside the root, so `pathFields` approves it.
82
+ * Whole-value quoting is what kept that honest -- and whole-value quoting is
83
+ * exactly the defect #1998 fixes. So per-element quoting and per-element
84
+ * checking have to land together: this field is the checking half. Nothing
85
+ * was exploitable before; splitting without it would have made it so.
86
+ */
87
+ readonly listPathFields?: readonly string[];
88
+ /**
89
+ * Array-valued fields whose elements are refs that MAY carry a path after a
90
+ * `:` (git's `<rev>:<path>` syntax). Only the substring after the first `:`
91
+ * is checked for containment; an element with no `:`, or an empty path
92
+ * after it, is a pure revision and is left unchecked.
93
+ */
94
+ readonly refPathFields?: readonly string[];
95
+ readonly verbField?: string;
96
+ readonly allowedVerbs?: readonly string[];
97
+ /**
98
+ * The input field holding a model-authored argv (RunCommand's `Exec`
99
+ * branch). When a call carries this field, the policy checks it under the
100
+ * `Exec` identity (`EXEC_TOOL_NAME`), matching each grant pattern against
101
+ * the argv token-by-token, rather than under the tool's own name — an
102
+ * `Exec(...)` grant is otherwise never consulted and a `RunCommand(*)`
103
+ * wildcard would silently cover the argv branch too.
104
+ */
105
+ readonly argvField?: string;
106
+ /**
107
+ * The input field holding a model-authored shell COMMAND STRING (`Bash`).
108
+ * A call carrying this field is evaluated per shell segment by
109
+ * `src/tools/policy-bash.ts` rather than by the verb or path branches; a
110
+ * tool declaring it must be granted explicitly, since no profile grants one.
111
+ */
112
+ readonly commandField?: string;
113
+ }
114
+ /**
115
+ * `breach` separates "you may not write there" from "that path is not in this
116
+ * repository at all". Both deny; only the latter is logged at warn, because a
117
+ * path escaping the root can mean prompt injection.
118
+ *
119
+ * `outcome` makes the refusal three-state. "ask" means every OTHER gate passed
120
+ * and an ask rule matched -- an AskResolver may approve it (spec R1). Absent or
121
+ * "denied" is final. The ask shape is still `allowed: false` on purpose: any
122
+ * consumer that only reads `allowed` fails closed. `resolvedPaths` is present
123
+ * only with "ask", describing what an approval would admit.
124
+ */
125
+ export type PolicyVerdict = {
126
+ readonly allowed: true;
127
+ readonly resolvedPaths: readonly string[];
128
+ } | {
129
+ readonly allowed: false;
130
+ readonly reason: string;
131
+ readonly breach: boolean;
132
+ readonly outcome?: "denied" | "ask";
133
+ readonly resolvedPaths?: readonly string[];
134
+ /** Present only with outcome "ask": the matching configured rule expression. */
135
+ readonly rule?: string;
136
+ /** See BashCheck.escalatable in policy-bash.ts. `false` on every
137
+ * non-escalatable denial (the shared deny helper materializes it on
138
+ * every branch), which reads as not-escalatable. */
139
+ readonly escalatable?: boolean;
140
+ };
141
+ export interface ToolPolicy {
142
+ readonly root: string;
143
+ grantedTools(): readonly string[];
144
+ check(tool: string, scope: ToolScope, input: Record<string, unknown>): PolicyVerdict;
145
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared vocabulary for nax's own coding tools.
3
+ *
4
+ * Deliberately free of any transport type: this module is imported by the
5
+ * policy, the tools and the runtime, none of which may see `@nathapp/nax-ai`
6
+ * (check:nax-ai-imports confines that package to src/agents/native/).
7
+ */
8
+ /**
9
+ * Policy identity for RunCommand's allowlisted, model-authored argv branch,
10
+ * and the marker an operation's `tools` declaration uses to request it.
11
+ * `Exec` is not a registered tool — nothing in `registerBuiltinCodingTools`
12
+ * carries this name, and it is reserved (`RESERVED_TOOL_NAMES`) precisely so
13
+ * no third party can register a tool that shadows the identity.
14
+ */
15
+ export const EXEC_TOOL_NAME = "Exec";
16
+ /**
17
+ * Policy identity of the model-authored shell tool.
18
+ *
19
+ * Unlike `Exec`, `Bash` IS a registered tool — but a session-local one
20
+ * (`createBashTool`), because it needs the project's shell and env-strip list.
21
+ * Reserved (`RESERVED_TOOL_NAMES`) so no third party can register a tool that
22
+ * shadows the identity the policy gates.
23
+ */
24
+ export const BASH_TOOL_NAME = "Bash";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The extra clause `policy.ts`'s `verbBranch` appends to a subcommand denial
3
+ * when the tool also offers an argv escape hatch (`scope.argvField`).
4
+ *
5
+ * run-2026-09-14T05-55-54-734Z, Shape B: an agent with argv available still
6
+ * stuffed raw shell into the verb field repeatedly rather than ever trying
7
+ * "argv" -- so the structural fact ("this field is never a shell string") is
8
+ * stated unconditionally. But a bare "use argv instead" would have sent
9
+ * every one of the audit's four real denials straight into a SECOND one:
10
+ * the compiled Exec grant was install-only (`bun install`, `bun add*`, ...)
11
+ * against requests like "bun test ... | head -200" and "wc". So the actual
12
+ * compiled grant is named here too, using the identical text
13
+ * `describeExecAllowlist` already renders into the tool description, which
14
+ * is what lets an agent see in one denial whether argv would help at all
15
+ * before spending a second turn finding out.
16
+ *
17
+ * Extracted from `policy.ts` into its own module so that file (already at
18
+ * its 600-line limit) can carry this explanation without trimming it.
19
+ *
20
+ * The clause names `argvField` explicitly (for "argv", ...) rather than
21
+ * appending `describeExecAllowlist`'s bare "permitted forms: ..." after the
22
+ * structural sentence: two unlabelled "permitted ..." lists back to back --
23
+ * one for `verbField`'s enum, one for argv's grant -- reads as a single
24
+ * list, and an agent can misread the second half as more legal values for
25
+ * `verbField`, denying again on the enum (the very second-denial loop this
26
+ * exists to prevent). `describeExecAllowlist`'s own text is unchanged: the
27
+ * tool description's surrounding sentence already establishes argv as the
28
+ * subject there, so this supplies its own framing around the same list
29
+ * rather than editing shared text to fit one caller. The framing is a bare
30
+ * attribution ("for \"argv\", ...") precisely so it composes with whichever
31
+ * of describeExecAllowlist's three forms is returned -- prefixing a verb
32
+ * such as "accepts:" instead would double the label on the populated form
33
+ * ("accepts: permitted forms: ...") and misread the empty one.
34
+ */
35
+ export declare function argvShellHint(verbField: string, argvField: string | undefined, execPatterns: readonly string[]): string;
@@ -0,0 +1,40 @@
1
+ import { describeExecAllowlist } from "./exec-allowlist-text.js";
2
+ /**
3
+ * The extra clause `policy.ts`'s `verbBranch` appends to a subcommand denial
4
+ * when the tool also offers an argv escape hatch (`scope.argvField`).
5
+ *
6
+ * run-2026-09-14T05-55-54-734Z, Shape B: an agent with argv available still
7
+ * stuffed raw shell into the verb field repeatedly rather than ever trying
8
+ * "argv" -- so the structural fact ("this field is never a shell string") is
9
+ * stated unconditionally. But a bare "use argv instead" would have sent
10
+ * every one of the audit's four real denials straight into a SECOND one:
11
+ * the compiled Exec grant was install-only (`bun install`, `bun add*`, ...)
12
+ * against requests like "bun test ... | head -200" and "wc". So the actual
13
+ * compiled grant is named here too, using the identical text
14
+ * `describeExecAllowlist` already renders into the tool description, which
15
+ * is what lets an agent see in one denial whether argv would help at all
16
+ * before spending a second turn finding out.
17
+ *
18
+ * Extracted from `policy.ts` into its own module so that file (already at
19
+ * its 600-line limit) can carry this explanation without trimming it.
20
+ *
21
+ * The clause names `argvField` explicitly (for "argv", ...) rather than
22
+ * appending `describeExecAllowlist`'s bare "permitted forms: ..." after the
23
+ * structural sentence: two unlabelled "permitted ..." lists back to back --
24
+ * one for `verbField`'s enum, one for argv's grant -- reads as a single
25
+ * list, and an agent can misread the second half as more legal values for
26
+ * `verbField`, denying again on the enum (the very second-denial loop this
27
+ * exists to prevent). `describeExecAllowlist`'s own text is unchanged: the
28
+ * tool description's surrounding sentence already establishes argv as the
29
+ * subject there, so this supplies its own framing around the same list
30
+ * rather than editing shared text to fit one caller. The framing is a bare
31
+ * attribution ("for \"argv\", ...") precisely so it composes with whichever
32
+ * of describeExecAllowlist's three forms is returned -- prefixing a verb
33
+ * such as "accepts:" instead would double the label on the populated form
34
+ * ("accepts: permitted forms: ...") and misread the empty one.
35
+ */
36
+ export function argvShellHint(verbField, argvField, execPatterns) {
37
+ if (argvField === undefined)
38
+ return "";
39
+ return ` -- "${verbField}" never takes a shell string -- for "${argvField}", ${describeExecAllowlist(execPatterns)}`;
40
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Write a file whose path the policy already resolved and approved.
3
+ *
4
+ * No production op declares this tool: everything that writes also needs to run
5
+ * tests, which needs Bash, which C1 excludes. It exists so #374's gate has a
6
+ * concrete subject (design section 3.5) and is exercised by tests alone.
7
+ */
8
+ import type { CodingTool } from "./registry.ts";
9
+ export declare const writeTool: CodingTool;