@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,208 @@
1
+ /**
2
+ * Native session lifecycle.
3
+ *
4
+ * There is no subprocess and no backend that remembers, so opening a session
5
+ * establishes nothing — it records where the conversation will be kept. ADR-027
6
+ * section 10 predicted exactly this shape: "openSession and closeSession become
7
+ * either no-ops or transcript-file handles".
8
+ */
9
+ import { NaxError } from "#src/infra/index";
10
+ import { createSpinBreaker } from "#src/infra/spin-breaker/index";
11
+ import { NATIVE_AGENT } from "../models.js";
12
+ import { nativeSessionId } from "../session-affinity.js";
13
+ import { deleteTranscript, pruneRetainedTranscripts, retainTranscript } from "./transcript-store.js";
14
+ /**
15
+ * Session name -> transcript directory, so sendTurn and close can find it.
16
+ *
17
+ * Cleared by every close path — `closeNativeSession` (in-process) and the
18
+ * adapter's `closePhysicalSession` (run teardown, which only has the name) —
19
+ * through the shared `clearNativeSessionState`. A caller that opens a session
20
+ * and never closes it (e.g. an early return, or a thrown error between open and
21
+ * close) still leaks an entry for the lifetime of the process. Harmless in
22
+ * practice (it is a small in-memory map keyed by session name, not a handle
23
+ * to a real resource), but worth knowing when debugging a growing map.
24
+ */
25
+ export const nativeTranscriptDirs = new Map();
26
+ /**
27
+ * Session name -> the root whose `.nax/scratchpad/` holds this session's
28
+ * spill files (US-003).
29
+ *
30
+ * Recorded from the session's `workdir` at open — the same root the coding
31
+ * tools are confined to — so a spilled body is reachable through
32
+ * `ScratchpadRead` with the relative path the truncation marker names. Same
33
+ * lifecycle as the maps above: set on open, cleared on close.
34
+ */
35
+ export const nativeSessionScratchpadRoots = new Map();
36
+ /**
37
+ * Session name -> timeoutSeconds, so sendTurn can bound each `complete()`
38
+ * call with a deadline (whole-branch review finding 4). Same lifecycle as
39
+ * `nativeTranscriptDirs` — populated on open, cleared on close only.
40
+ */
41
+ export const nativeSessionTimeouts = new Map();
42
+ /**
43
+ * Session name -> the runtime hooks handed to openSession. SessionManager
44
+ * passes both (manager.ts, openSession) and the native adapter previously
45
+ * ignored them, which is why the idle watchdog never covered native. Same
46
+ * lifecycle as `nativeTranscriptDirs`: set on open, cleared on close only.
47
+ */
48
+ export const nativeSessionStreamHooks = new Map();
49
+ /**
50
+ * Session names whose most recent turn failed.
51
+ *
52
+ * `AgentAdapter.closeSession(handle)` carries no failure signal, so the close
53
+ * cannot tell a finished session from a broken one and passed `failed: false`
54
+ * for both -- deleting the transcript of exactly the session whose history the
55
+ * retry needs and a human would read (nax#1838). Rather than widen the adapter
56
+ * interface for one transport, the turn records what it knows here and the
57
+ * close reads it. Same lifecycle as the maps above: written per turn, cleared
58
+ * on close.
59
+ *
60
+ * A later successful turn clears the mark. "Failed" describes the session's
61
+ * current state, not whether it ever stumbled -- a session that recovered and
62
+ * finished is still cleaned up.
63
+ */
64
+ export const nativeSessionFailed = new Set();
65
+ /**
66
+ * Session name -> the identity that owns this session's transcript (nax#1877).
67
+ *
68
+ * Read by `runNativeTurn` on both the load and the save, so a transcript left
69
+ * behind by an abandoned invocation of the same deterministic session name is
70
+ * recognised as foreign rather than silently resumed. Same lifecycle as the
71
+ * maps above: set on open, cleared on close.
72
+ */
73
+ export const nativeSessionTranscriptOwners = new Map();
74
+ /** Session name -> resolved compaction settings. Same lifecycle as the maps above. */
75
+ export const nativeSessionCompaction = new Map();
76
+ /**
77
+ * Session name -> resolved transport-fault retry settings (nax#1870). Same
78
+ * lifecycle as the maps above: set on open, cleared on close.
79
+ */
80
+ export const nativeSessionTransportRetry = new Map();
81
+ /**
82
+ * Session name -> live `SpinBreaker` instance (nax#2013, lifetime extended in
83
+ * nax#2047). Constructed once at open so the cumulative counter survives
84
+ * across `runNativeTurn` calls — a fix round that opens a new turn on the
85
+ * same `handle.id` must inherit the prior turn's evidence rather than start
86
+ * fresh. Same lifecycle as `nativeSessionTransportRetry`: set on open, cleared
87
+ * on close.
88
+ */
89
+ export const nativeSessionSpinBreaker = new Map();
90
+ export const nativeSessionLastUsage = new Map();
91
+ /**
92
+ * The persisted anchor a turn on `model` may use. Prefix stability is a
93
+ * property of (model, prefix) (P3 spec 3.3): an anchor measured under a
94
+ * different model indexes history the transcript store refused (spec 8.3(c)),
95
+ * so it is dropped here rather than mis-sizing the next compaction decision.
96
+ * An entry or a turn with no model makes no claim — the store's own rule.
97
+ */
98
+ export function sessionAnchorFor(sessionName, model) {
99
+ const entry = nativeSessionLastUsage.get(sessionName);
100
+ if (entry?.model === undefined || model === undefined || entry.model === model)
101
+ return entry;
102
+ nativeSessionLastUsage.delete(sessionName);
103
+ return undefined;
104
+ }
105
+ /** Records how the session's latest turn ended, for `closeNativeSession`. */
106
+ export function markNativeTurnOutcome(sessionName, failed) {
107
+ if (failed)
108
+ nativeSessionFailed.add(sessionName);
109
+ else
110
+ nativeSessionFailed.delete(sessionName);
111
+ }
112
+ export async function openNativeSession(name, opts) {
113
+ // Never defaulted. An adapter that picks its own path writes a transcript
114
+ // somewhere nobody looks, which is #1794's empty-packageDir bug one layer up.
115
+ if (!opts.transcriptDir) {
116
+ throw new NaxError(`native session "${name}" opened without a transcriptDir`, "NATIVE_TRANSCRIPT_DIR_MISSING", {
117
+ stage: "native-session",
118
+ });
119
+ }
120
+ nativeTranscriptDirs.set(name, opts.transcriptDir);
121
+ nativeSessionTimeouts.set(name, opts.timeoutSeconds);
122
+ nativeSessionScratchpadRoots.set(name, opts.workdir);
123
+ if (opts.transcriptOwner !== undefined)
124
+ nativeSessionTranscriptOwners.set(name, opts.transcriptOwner);
125
+ else
126
+ nativeSessionTranscriptOwners.delete(name);
127
+ // `resume` is SessionManager's "this name already has a descriptor in this
128
+ // process" signal, and it had no consumer on this transport (nax#1877) — a
129
+ // native session resumed whatever transcript happened to be on disk. Honouring
130
+ // it here is the flag's documented contract; the owner check in loadTranscript
131
+ // is what covers the cases `resume` cannot see (a same-process re-entry into a
132
+ // stage, and a process that died without closing anything).
133
+ if (opts.resume !== true)
134
+ await deleteTranscript(opts.transcriptDir, name);
135
+ if (opts.compaction !== undefined)
136
+ nativeSessionCompaction.set(name, opts.compaction);
137
+ if (opts.transportRetry !== undefined)
138
+ nativeSessionTransportRetry.set(name, opts.transportRetry);
139
+ if (opts.spinBreaker !== undefined)
140
+ nativeSessionSpinBreaker.set(name, createSpinBreaker(opts.spinBreaker));
141
+ nativeSessionStreamHooks.set(name, {
142
+ ...(opts.onStreamActivity !== undefined ? { onStreamActivity: opts.onStreamActivity } : {}),
143
+ ...(opts.onActiveCall !== undefined ? { onActiveCall: opts.onActiveCall } : {}),
144
+ });
145
+ return {
146
+ id: name,
147
+ agentName: NATIVE_AGENT,
148
+ // Both fields carry the same value on purpose. On ACP they differ because a
149
+ // physical session can be re-established under a stable logical record;
150
+ // native has no reconnect, so its logical and physical identity genuinely
151
+ // coincide. `nativeSessionId` is a pure hash of the name and deliberately
152
+ // not memoised, so this is exactly the id `sendTurn` later puts on the wire.
153
+ protocolIds: { recordId: nativeSessionId(name), sessionId: nativeSessionId(name) },
154
+ ...(opts.modelDef !== undefined ? { modelDef: opts.modelDef } : {}),
155
+ ...(opts.modelTier !== undefined ? { modelTier: opts.modelTier } : {}),
156
+ };
157
+ }
158
+ /**
159
+ * Clear every module-level entry for one session name.
160
+ *
161
+ * Extracted so both close paths share one source of truth. `closeNativeSession`
162
+ * has a `SessionHandle`; the adapter's `closePhysicalSession` gets only the
163
+ * handle string that run teardown carries (`descriptor.handle`). The two are
164
+ * the same string — every map is keyed by the session name, and `SessionHandle.id`
165
+ * is that name — but the signatures are not interchangeable, so this helper is
166
+ * string-keyed rather than reconstructed into a synthetic handle.
167
+ */
168
+ export function clearNativeSessionState(sessionName) {
169
+ nativeTranscriptDirs.delete(sessionName);
170
+ nativeSessionTimeouts.delete(sessionName);
171
+ nativeSessionScratchpadRoots.delete(sessionName);
172
+ nativeSessionTranscriptOwners.delete(sessionName);
173
+ nativeSessionStreamHooks.delete(sessionName);
174
+ nativeSessionFailed.delete(sessionName);
175
+ nativeSessionCompaction.delete(sessionName);
176
+ nativeSessionTransportRetry.delete(sessionName);
177
+ nativeSessionSpinBreaker.delete(sessionName);
178
+ nativeSessionLastUsage.delete(sessionName);
179
+ }
180
+ /**
181
+ * Kept on failure, deleted on success. Every Phase B op is lifetime "fresh", so
182
+ * the transcript survives exactly when it is worth reading. The kept-on-failure
183
+ * set is otherwise unbounded, so a failed close also prunes the feature's
184
+ * `sessions/` directory down to `MAX_RETAINED_TRANSCRIPTS` (ADR-028 section 3).
185
+ */
186
+ export async function closeNativeSession(handle, failed) {
187
+ const dir = nativeTranscriptDirs.get(handle.id);
188
+ // An explicit argument wins; otherwise the last turn's own verdict decides.
189
+ // The adapter passes nothing, because its interface has no failure signal.
190
+ const treatAsFailed = failed ?? nativeSessionFailed.has(handle.id);
191
+ try {
192
+ if (dir !== undefined) {
193
+ if (treatAsFailed) {
194
+ // Retain for a human, out of reach of the next session of this name.
195
+ await retainTranscript(dir, handle.id);
196
+ await pruneRetainedTranscripts(dir);
197
+ }
198
+ else {
199
+ await deleteTranscript(dir, handle.id);
200
+ }
201
+ }
202
+ }
203
+ finally {
204
+ // The deletes must run even when the transcript I/O throws: a failed
205
+ // cleanup step is no reason to strand the session's other state.
206
+ clearNativeSessionState(handle.id);
207
+ }
208
+ }
@@ -0,0 +1,2 @@
1
+ import type { ToolInputViolation } from "./tool-input-validation.ts";
2
+ export declare function exemplarFor(schema: unknown, input: Record<string, unknown>, violation: ToolInputViolation): Record<string, unknown>;
@@ -0,0 +1,66 @@
1
+ export function exemplarFor(schema, input, violation) {
2
+ const exemplar = { ...input };
3
+ // Defensive: validateToolInput never currently emits "" (every violation
4
+ // names the offending property), but ToolInputViolation.property is typed
5
+ // as `string`, not a non-empty literal, so a synthetic or future
6
+ // top-level violation with no single property to correct must still be
7
+ // handled -- return the input verbatim rather than writing to an empty key.
8
+ if (violation.property === "") {
9
+ return exemplar;
10
+ }
11
+ if (!isPlainObject(schema))
12
+ return exemplar;
13
+ const properties = schema.properties;
14
+ if (!isPlainObject(properties))
15
+ return exemplar;
16
+ const propSchema = properties[violation.property];
17
+ if (!isPlainObject(propSchema)) {
18
+ exemplar[violation.property] = "<FILL IN>";
19
+ return exemplar;
20
+ }
21
+ const propType = propSchema.type;
22
+ // Enum first (nax#2200): `{type: "string", enum: [...]}` is the common
23
+ // spelling, and a `<FILL IN>` placeholder there violates the very enum the
24
+ // exemplar is meant to demonstrate. The first member is a valid value.
25
+ if (Array.isArray(propSchema.enum) && propSchema.enum.length > 0 && propSchema.enum.every(isString)) {
26
+ exemplar[violation.property] = propSchema.enum[0];
27
+ }
28
+ else if (propType === "string") {
29
+ exemplar[violation.property] = `<FILL IN: ${violation.property}>`;
30
+ }
31
+ else if (propType === "number" || propType === "integer") {
32
+ exemplar[violation.property] = 0;
33
+ }
34
+ else if (propType === "boolean") {
35
+ exemplar[violation.property] = false;
36
+ }
37
+ else if (propType === "null") {
38
+ exemplar[violation.property] = null;
39
+ }
40
+ else if (propType === "array") {
41
+ exemplar[violation.property] = [`<FILL IN: ${violation.property}>`];
42
+ }
43
+ else if (propType === "object") {
44
+ const subProperties = propSchema.properties;
45
+ if (isPlainObject(subProperties)) {
46
+ const subExemplar = {};
47
+ for (const key of Object.keys(subProperties)) {
48
+ subExemplar[key] = "<FILL IN>";
49
+ }
50
+ exemplar[violation.property] = subExemplar;
51
+ }
52
+ else {
53
+ exemplar[violation.property] = { "<FILL IN>": "<FILL IN>" };
54
+ }
55
+ }
56
+ else {
57
+ exemplar[violation.property] = "<FILL IN>";
58
+ }
59
+ return exemplar;
60
+ }
61
+ function isString(value) {
62
+ return typeof value === "string";
63
+ }
64
+ function isPlainObject(value) {
65
+ return typeof value === "object" && value !== null && !Array.isArray(value);
66
+ }
@@ -0,0 +1,15 @@
1
+ export interface ToolInputViolation {
2
+ readonly property: string;
3
+ readonly expected: string;
4
+ readonly actual: string;
5
+ readonly message: string;
6
+ }
7
+ export declare function validateToolInput(schema: unknown, input: unknown): ToolInputViolation | undefined;
8
+ /**
9
+ * The input with every `null`-valued optional property removed, or `undefined`
10
+ * when there is nothing to remove (including any schema the validator does not
11
+ * understand — the same fail-open contract). A property whose own schema
12
+ * declares `type: "null"` keeps its value: there `null` IS the supplied value.
13
+ * Never mutates `input`.
14
+ */
15
+ export declare function stripNullOptionals(schema: unknown, input: unknown): Record<string, unknown> | undefined;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Fail-open subset validator for tool inputs (nax#2047).
3
+ *
4
+ * The model returned RunCommand calls with `values:""` 69 times and
5
+ * `values:"\t"` 13 times in a row -- the empty string produced no rejected
6
+ * keys, the tab became the key "0", and neither message ever named "values"
7
+ * itself as the wrong type, so the loop could not break. This validator
8
+ * catches that shape at the transport boundary so a malformed input never
9
+ * persists in the transcript.
10
+ *
11
+ * The contract is a strict subset of JSON Schema on purpose: type, enum,
12
+ * required, and a `properties` object. Anything we cannot decide -- unknown
13
+ * keywords such as `anyOf`, a missing or non-object schema, extra properties
14
+ * with no `additionalProperties` -- returns `undefined` (allow), so this
15
+ * validator never breaks a tool that was previously passing.
16
+ *
17
+ * `null` on a property the schema does not list as `required` is treated as
18
+ * absent (nax#2200). The tools already read it that way (`Git` treats a
19
+ * non-array `refs` as `[]`), and models routinely spell "not supplied" as
20
+ * `null`; rejecting it made the gate stricter than the tools it guards, and a
21
+ * verifier that sent `refs: null` three times lost its turn to the budget.
22
+ * `stripNullOptionals` removes those properties so the handler sees the same
23
+ * "absent" the validator judged.
24
+ */
25
+ import { describeValuesType } from "#src/internal/describe-value-type";
26
+ export function validateToolInput(schema, input) {
27
+ if (!isPlainObject(schema))
28
+ return undefined;
29
+ const { properties, required, type } = schema;
30
+ if (!isPlainObject(properties))
31
+ return undefined;
32
+ if (type !== undefined && type !== "object")
33
+ return undefined;
34
+ if (!isPlainObject(input))
35
+ return undefined;
36
+ const requiredKeys = requiredSet(required);
37
+ for (const entry of requiredKeys) {
38
+ if (!(entry in input)) {
39
+ return {
40
+ property: entry,
41
+ expected: "present",
42
+ actual: "absent",
43
+ message: `\`${entry}\` is required`,
44
+ };
45
+ }
46
+ }
47
+ for (const key of Object.keys(input)) {
48
+ const propSchema = properties[key];
49
+ if (!isPlainObject(propSchema))
50
+ continue;
51
+ const value = input[key];
52
+ // nax#2200: `null` on an optional property reads as "not supplied".
53
+ if (value === null && !requiredKeys.has(key))
54
+ continue;
55
+ if (typeof propSchema.type === "string" && PRIMITIVE_TYPES.has(propSchema.type)) {
56
+ if (!typeMatches(propSchema.type, value)) {
57
+ const expected = propSchema.type;
58
+ const actual = describeValuesType(value);
59
+ return {
60
+ property: key,
61
+ expected,
62
+ actual,
63
+ message: `\`${key}\` expected ${expected}, got ${actual}`,
64
+ };
65
+ }
66
+ }
67
+ const enumSchema = propSchema.enum;
68
+ if (Array.isArray(enumSchema) && enumSchema.every((m) => typeof m === "string")) {
69
+ if (!enumSchema.includes(value)) {
70
+ const expected = `one of: ${enumSchema.join(", ")}`;
71
+ const actual = describeValuesType(value);
72
+ return {
73
+ property: key,
74
+ expected,
75
+ actual,
76
+ message: `\`${key}\` must be ${expected} (got ${actual})`,
77
+ };
78
+ }
79
+ }
80
+ }
81
+ return undefined;
82
+ }
83
+ /**
84
+ * The input with every `null`-valued optional property removed, or `undefined`
85
+ * when there is nothing to remove (including any schema the validator does not
86
+ * understand — the same fail-open contract). A property whose own schema
87
+ * declares `type: "null"` keeps its value: there `null` IS the supplied value.
88
+ * Never mutates `input`.
89
+ */
90
+ export function stripNullOptionals(schema, input) {
91
+ if (!isPlainObject(schema) || !isPlainObject(input))
92
+ return undefined;
93
+ const { properties, required, type } = schema;
94
+ if (!isPlainObject(properties))
95
+ return undefined;
96
+ if (type !== undefined && type !== "object")
97
+ return undefined;
98
+ const requiredKeys = requiredSet(required);
99
+ const stripped = Object.keys(input).filter((key) => {
100
+ const propSchema = properties[key];
101
+ if (input[key] !== null || requiredKeys.has(key) || !isPlainObject(propSchema))
102
+ return false;
103
+ return propSchema.type !== "null";
104
+ });
105
+ if (stripped.length === 0)
106
+ return undefined;
107
+ const drop = new Set(stripped);
108
+ return Object.fromEntries(Object.entries(input).filter(([key]) => !drop.has(key)));
109
+ }
110
+ function requiredSet(required) {
111
+ if (!Array.isArray(required))
112
+ return new Set();
113
+ return new Set(required.filter((entry) => typeof entry === "string"));
114
+ }
115
+ const PRIMITIVE_TYPES = new Set([
116
+ "string",
117
+ "number",
118
+ "integer",
119
+ "boolean",
120
+ "array",
121
+ "null",
122
+ "object",
123
+ ]);
124
+ function typeMatches(type, value) {
125
+ switch (type) {
126
+ case "string":
127
+ return typeof value === "string";
128
+ case "number":
129
+ return typeof value === "number";
130
+ case "integer":
131
+ return typeof value === "number" && Number.isInteger(value);
132
+ case "boolean":
133
+ return typeof value === "boolean";
134
+ case "array":
135
+ return Array.isArray(value);
136
+ case "null":
137
+ return value === null;
138
+ case "object":
139
+ return typeof value === "object" && value !== null && !Array.isArray(value);
140
+ default:
141
+ return true;
142
+ }
143
+ }
144
+ function isPlainObject(value) {
145
+ return typeof value === "object" && value !== null && !Array.isArray(value);
146
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * ToolDescriptor -> ToolDefinition.
3
+ *
4
+ * Nearly one-to-one. The budget fields stay behind deliberately: nax executes
5
+ * these tools, so `maxCallsPerSession` and `maxTokensPerCall` are enforced by
6
+ * PullToolBudget on this side and mean nothing to a provider.
7
+ */
8
+ import type { ToolDefinition } from "@nathapp/nax-ai";
9
+ import type { ToolDescriptor } from "#src/session/tool-descriptor";
10
+ import type { CodingTool } from "#src/tools/index";
11
+ export declare function toToolDefinitions(descriptors: readonly ToolDescriptor[]): ToolDefinition[];
12
+ /**
13
+ * CodingTool -> ToolDefinition.
14
+ *
15
+ * `scope` and `run` stay behind for the same reason the pull tools' budget
16
+ * fields do: nax executes these, so they mean nothing to a provider.
17
+ */
18
+ export declare function codingToolsToDefinitions(tools: readonly CodingTool[]): ToolDefinition[];
@@ -0,0 +1,27 @@
1
+ /**
2
+ * ToolDescriptor -> ToolDefinition.
3
+ *
4
+ * Nearly one-to-one. The budget fields stay behind deliberately: nax executes
5
+ * these tools, so `maxCallsPerSession` and `maxTokensPerCall` are enforced by
6
+ * PullToolBudget on this side and mean nothing to a provider.
7
+ */
8
+ export function toToolDefinitions(descriptors) {
9
+ return descriptors.map((d) => ({
10
+ name: d.name,
11
+ description: d.description,
12
+ inputSchema: d.inputSchema,
13
+ }));
14
+ }
15
+ /**
16
+ * CodingTool -> ToolDefinition.
17
+ *
18
+ * `scope` and `run` stay behind for the same reason the pull tools' budget
19
+ * fields do: nax executes these, so they mean nothing to a provider.
20
+ */
21
+ export function codingToolsToDefinitions(tools) {
22
+ return tools.map((t) => ({
23
+ name: t.name,
24
+ description: t.description,
25
+ inputSchema: t.inputSchema,
26
+ }));
27
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The one place a native `tool-result` message is constructed.
3
+ *
4
+ * The result used to be appended at seven separate sites in `turn-loop.ts`, so
5
+ * there was no single point at which a policy could be applied even if one
6
+ * existed, and invariants like "`toolCallId` is always set" were re-asserted by
7
+ * hand at each. Every site now builds its message here.
8
+ *
9
+ * That chokepoint is also what makes `after_tool` safe by construction
10
+ * (nax#2151): the event shapes a result BEFORE it enters the message array, so
11
+ * no handler can rewrite history and re-bill the prefix-cached prompt. The four
12
+ * synthetic sites — where no tool ran — use this builder without firing the
13
+ * event; the three genuine sites use both.
14
+ */
15
+ /**
16
+ * A refusal the model is meant to act on. Structural (ADR-029 s5), never a
17
+ * string convention: `{ answer }` alone cannot distinguish "refused, and here
18
+ * is why" from "here is your file".
19
+ */
20
+ export interface DenialInfo {
21
+ readonly reason: string;
22
+ readonly breach: boolean;
23
+ }
24
+ export interface ToolResultMessage {
25
+ readonly role: "tool-result";
26
+ readonly toolCallId: string;
27
+ readonly content: string;
28
+ readonly isError?: boolean;
29
+ readonly denied?: DenialInfo;
30
+ }
31
+ export interface BuildToolResultArgs {
32
+ readonly toolCallId: string;
33
+ readonly content: string;
34
+ readonly isError?: boolean;
35
+ readonly denied?: DenialInfo;
36
+ }
37
+ export declare function buildToolResult(args: BuildToolResultArgs): ToolResultMessage;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The one place a native `tool-result` message is constructed.
3
+ *
4
+ * The result used to be appended at seven separate sites in `turn-loop.ts`, so
5
+ * there was no single point at which a policy could be applied even if one
6
+ * existed, and invariants like "`toolCallId` is always set" were re-asserted by
7
+ * hand at each. Every site now builds its message here.
8
+ *
9
+ * That chokepoint is also what makes `after_tool` safe by construction
10
+ * (nax#2151): the event shapes a result BEFORE it enters the message array, so
11
+ * no handler can rewrite history and re-bill the prefix-cached prompt. The four
12
+ * synthetic sites — where no tool ran — use this builder without firing the
13
+ * event; the three genuine sites use both.
14
+ */
15
+ export function buildToolResult(args) {
16
+ return {
17
+ role: "tool-result",
18
+ toolCallId: args.toolCallId,
19
+ content: args.content,
20
+ // Absent stays absent: a clean result must not carry an `isError: undefined`
21
+ // key, so the field is spread in only when it has a value.
22
+ ...(args.isError === undefined ? {} : { isError: args.isError }),
23
+ ...(args.denied === undefined ? {} : { denied: args.denied }),
24
+ };
25
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Conversation persistence for the native transport.
3
+ *
4
+ * nax-ai's client is stateless — every call takes the whole message array — so
5
+ * nax keeps the conversation. Under ACP the acpx subprocess remembered it and
6
+ * nax stored nothing; SessionDescriptor still has no message field, and gains
7
+ * none. See ADR-028 sections 2 and 3.
8
+ */
9
+ import type { ConversationMessage } from "@nathapp/nax-ai";
10
+ export declare function transcriptPath(dir: string, sessionName: string): string;
11
+ /**
12
+ * Who may resume a transcript. `owner` is the op invocation (nax#1877); `model`
13
+ * is the native model that wrote it (nax#2150, P3 spec 8.3). A field the caller
14
+ * leaves undefined makes no claim, so non-op callers and tests read whatever is
15
+ * there.
16
+ */
17
+ export interface TranscriptIdentity {
18
+ readonly owner?: string;
19
+ readonly model?: string;
20
+ }
21
+ /**
22
+ * The model half of `TranscriptIdentity`: the native `provider/model` id with
23
+ * the reasoning-effort suffix stripped. A thinking signature binds to the
24
+ * model, not to the effort — pi-ai's own `isSameModel` compares
25
+ * provider/api/model for the same reason. `parseModelSpec`, not
26
+ * `parseNativeModel`: this must never throw.
27
+ */
28
+ export declare function transcriptModelIdentity(rawModel: string | undefined): string | undefined;
29
+ /**
30
+ * Missing file means a new conversation. Anything else is a real failure.
31
+ *
32
+ * Returns `[]` — a new conversation — when the transcript on disk belongs to a
33
+ * different `owner` or a different recorded `model` than the caller (nax#1877,
34
+ * nax#2150). A reader that declares no identity is not making a claim and
35
+ * reads whatever is there, which keeps non-op callers and unit tests working
36
+ * unchanged.
37
+ */
38
+ export declare function loadTranscript(dir: string, sessionName: string, identity?: TranscriptIdentity): Promise<ConversationMessage[]>;
39
+ export declare function saveTranscript(dir: string, sessionName: string, messages: readonly ConversationMessage[], identity?: TranscriptIdentity): Promise<void>;
40
+ /**
41
+ * Move a failed session's transcript off the path the next session reads.
42
+ *
43
+ * The kept-on-failure transcript (nax#1838) used to stay at its live name, which
44
+ * is exactly the name `loadTranscript` opens — so the post-mortem artifact and
45
+ * the resumption source were one file (nax#1877). Renaming keeps every byte of
46
+ * debugging value and removes the resumption entirely. It also stops the NEXT
47
+ * attempt's `saveTranscript` from overwriting the artifact #1838 wanted kept.
48
+ *
49
+ * A missing transcript is not an error: a session can fail before its first save.
50
+ */
51
+ export declare function retainTranscript(dir: string, sessionName: string): Promise<void>;
52
+ export declare function deleteTranscript(dir: string, sessionName: string): Promise<void>;
53
+ /**
54
+ * Cap on transcripts retained per feature's `sessions/` directory. Transcripts
55
+ * are deleted on a clean close (`closeNativeSession`) but kept when a turn
56
+ * failed, so the kept-on-failure set otherwise grows without bound — nothing
57
+ * else prunes it (issue #1445 is the same class of unbounded `~/.nax` growth).
58
+ * Follows the precedent of `MAX_RETAINED_RUNS` in `src/metrics/tracker.ts`.
59
+ */
60
+ export declare const MAX_RETAINED_TRANSCRIPTS = 50;
61
+ /**
62
+ * Reset the one-shot truncation-warning flag (for testing only).
63
+ * @internal
64
+ */
65
+ export declare function _resetTranscriptTruncationWarningForTests(): void;
66
+ /**
67
+ * Prune a feature's `sessions/` directory to the `maxRetained` most recently
68
+ * modified transcript files, deleting the oldest first. Called from
69
+ * `closeNativeSession`'s kept-on-failure branch — the only place transcripts
70
+ * accumulate, since a clean close always deletes its own transcript.
71
+ *
72
+ * A missing directory is not an error (nothing to prune yet).
73
+ *
74
+ * Returns the number of transcript files deleted (0 when there is nothing to
75
+ * delete) so callers can report the sweep's work without re-reading the dir.
76
+ */
77
+ export declare function pruneRetainedTranscripts(dir: string, maxRetained?: number): Promise<number>;