@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,200 @@
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 { mkdir, readdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
10
+ import { join } from "node:path";
11
+ import { getLogger, NaxError } from "#src/infra/index";
12
+ import { parseModelSpec } from "../models.js";
13
+ export function transcriptPath(dir, sessionName) {
14
+ return join(dir, `${sessionName}.transcript.json`);
15
+ }
16
+ /**
17
+ * The model half of `TranscriptIdentity`: the native `provider/model` id with
18
+ * the reasoning-effort suffix stripped. A thinking signature binds to the
19
+ * model, not to the effort — pi-ai's own `isSameModel` compares
20
+ * provider/api/model for the same reason. `parseModelSpec`, not
21
+ * `parseNativeModel`: this must never throw.
22
+ */
23
+ export function transcriptModelIdentity(rawModel) {
24
+ return rawModel === undefined ? undefined : parseModelSpec(rawModel).model;
25
+ }
26
+ /** A bare array is a pre-#1877 transcript: real messages, no recorded owner. */
27
+ function isLegacyTranscript(parsed) {
28
+ return Array.isArray(parsed);
29
+ }
30
+ /**
31
+ * Missing file means a new conversation. Anything else is a real failure.
32
+ *
33
+ * Returns `[]` — a new conversation — when the transcript on disk belongs to a
34
+ * different `owner` or a different recorded `model` than the caller (nax#1877,
35
+ * nax#2150). A reader that declares no identity is not making a claim and
36
+ * reads whatever is there, which keeps non-op callers and unit tests working
37
+ * unchanged.
38
+ */
39
+ export async function loadTranscript(dir, sessionName, identity = {}) {
40
+ let raw;
41
+ try {
42
+ raw = await readFile(transcriptPath(dir, sessionName), "utf8");
43
+ }
44
+ catch (err) {
45
+ if (err.code === "ENOENT")
46
+ return [];
47
+ throw err;
48
+ }
49
+ let parsed;
50
+ try {
51
+ parsed = JSON.parse(raw);
52
+ }
53
+ catch (err) {
54
+ // Deliberately not [] — silently restarting a conversation would drop the
55
+ // history the model is mid-way through and look like a fresh session.
56
+ throw new NaxError(`transcript for session "${sessionName}" is unreadable: ${err instanceof Error ? err.message : String(err)}`, "TRANSCRIPT_CORRUPT", {
57
+ stage: "native-session",
58
+ });
59
+ }
60
+ if (isLegacyTranscript(parsed)) {
61
+ // Unowned history is foreign history to a reader that has an identity.
62
+ // Dropping it is the safe direction: the cost is one re-exploration, where
63
+ // inheriting it silently bills a conversation this session never had.
64
+ return identity.owner === undefined ? parsed : [];
65
+ }
66
+ const file = parsed;
67
+ if (isForeignTranscript(file, identity, sessionName))
68
+ return [];
69
+ return file.messages ?? [];
70
+ }
71
+ /**
72
+ * Another invocation's history (nax#1877) or another model's (nax#2150, P3
73
+ * spec 8.3(c)) reads as a new conversation. An ABSENT file model reads, unlike
74
+ * an absent owner: every native production turn records one (the adapter parses
75
+ * the model before the loop runs), so an absent field is a pre-upgrade file —
76
+ * which the owner check already keeps out of new processes.
77
+ */
78
+ function isForeignTranscript(file, identity, sessionName) {
79
+ const { owner, model } = identity;
80
+ if (owner !== undefined && file.owner !== owner) {
81
+ getLogger().debug("native-session", "Ignoring a transcript owned by another invocation", {
82
+ sessionName,
83
+ storedOwner: file.owner,
84
+ owner,
85
+ });
86
+ return true;
87
+ }
88
+ if (model !== undefined && file.model !== undefined && file.model !== model) {
89
+ getLogger().debug("native-session", "Ignoring a transcript written by another model", {
90
+ sessionName,
91
+ storedModel: file.model,
92
+ model,
93
+ });
94
+ return true;
95
+ }
96
+ return false;
97
+ }
98
+ export async function saveTranscript(dir, sessionName, messages, identity = {}) {
99
+ await mkdir(dir, { recursive: true });
100
+ const file = {
101
+ ...(identity.owner !== undefined ? { owner: identity.owner } : {}),
102
+ ...(identity.model !== undefined ? { model: identity.model } : {}),
103
+ savedAt: new Date().toISOString(),
104
+ messages: [...messages],
105
+ };
106
+ await writeFile(transcriptPath(dir, sessionName), JSON.stringify(file, null, 2), "utf8");
107
+ }
108
+ /**
109
+ * Move a failed session's transcript off the path the next session reads.
110
+ *
111
+ * The kept-on-failure transcript (nax#1838) used to stay at its live name, which
112
+ * is exactly the name `loadTranscript` opens — so the post-mortem artifact and
113
+ * the resumption source were one file (nax#1877). Renaming keeps every byte of
114
+ * debugging value and removes the resumption entirely. It also stops the NEXT
115
+ * attempt's `saveTranscript` from overwriting the artifact #1838 wanted kept.
116
+ *
117
+ * A missing transcript is not an error: a session can fail before its first save.
118
+ */
119
+ export async function retainTranscript(dir, sessionName) {
120
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
121
+ try {
122
+ await rename(transcriptPath(dir, sessionName), join(dir, `${sessionName}.transcript.failed-${stamp}.json`));
123
+ }
124
+ catch (err) {
125
+ if (err.code === "ENOENT")
126
+ return;
127
+ throw err;
128
+ }
129
+ }
130
+ export async function deleteTranscript(dir, sessionName) {
131
+ await rm(transcriptPath(dir, sessionName), { force: true });
132
+ }
133
+ /**
134
+ * Cap on transcripts retained per feature's `sessions/` directory. Transcripts
135
+ * are deleted on a clean close (`closeNativeSession`) but kept when a turn
136
+ * failed, so the kept-on-failure set otherwise grows without bound — nothing
137
+ * else prunes it (issue #1445 is the same class of unbounded `~/.nax` growth).
138
+ * Follows the precedent of `MAX_RETAINED_RUNS` in `src/metrics/tracker.ts`.
139
+ */
140
+ export const MAX_RETAINED_TRANSCRIPTS = 50;
141
+ /**
142
+ * One-shot dedupe flag, mirroring `hasWarnedAboutRunTruncation` in
143
+ * `src/metrics/tracker.ts`: pruning runs on every failed close, so once a
144
+ * feature is past the cap this would otherwise warn on every subsequent
145
+ * close. Log it once per process lifetime instead.
146
+ */
147
+ let hasWarnedAboutTranscriptTruncation = false;
148
+ /**
149
+ * Reset the one-shot truncation-warning flag (for testing only).
150
+ * @internal
151
+ */
152
+ export function _resetTranscriptTruncationWarningForTests() {
153
+ hasWarnedAboutTranscriptTruncation = false;
154
+ }
155
+ /**
156
+ * Prune a feature's `sessions/` directory to the `maxRetained` most recently
157
+ * modified transcript files, deleting the oldest first. Called from
158
+ * `closeNativeSession`'s kept-on-failure branch — the only place transcripts
159
+ * accumulate, since a clean close always deletes its own transcript.
160
+ *
161
+ * A missing directory is not an error (nothing to prune yet).
162
+ *
163
+ * Returns the number of transcript files deleted (0 when there is nothing to
164
+ * delete) so callers can report the sweep's work without re-reading the dir.
165
+ */
166
+ export async function pruneRetainedTranscripts(dir, maxRetained = MAX_RETAINED_TRANSCRIPTS) {
167
+ let entries;
168
+ try {
169
+ entries = await readdir(dir);
170
+ }
171
+ catch (err) {
172
+ if (err.code === "ENOENT")
173
+ return 0;
174
+ throw err;
175
+ }
176
+ // Matches both the live name and the `failed-<stamp>` names `retainTranscript`
177
+ // produces — the retained set is the one that accumulates, so a filter that
178
+ // missed it would leave the cap enforcing nothing.
179
+ const transcriptFiles = entries.filter((name) => name.includes(".transcript.") && name.endsWith(".json"));
180
+ if (transcriptFiles.length <= maxRetained)
181
+ return 0;
182
+ const withMtimes = await Promise.all(transcriptFiles.map(async (name) => {
183
+ const fullPath = join(dir, name);
184
+ const stats = await stat(fullPath);
185
+ return { path: fullPath, mtimeMs: stats.mtimeMs };
186
+ }));
187
+ // Oldest first, so the slice below drops exactly the excess over the cap.
188
+ withMtimes.sort((a, b) => a.mtimeMs - b.mtimeMs);
189
+ const toDelete = withMtimes.slice(0, withMtimes.length - maxRetained);
190
+ await Promise.all(toDelete.map((f) => rm(f.path, { force: true })));
191
+ if (!hasWarnedAboutTranscriptTruncation) {
192
+ hasWarnedAboutTranscriptTruncation = true;
193
+ getLogger().warn("native-session", "Transcript retention cap reached — oldest kept-on-failure transcripts pruned", {
194
+ droppedCount: toDelete.length,
195
+ maxRetained,
196
+ dir,
197
+ });
198
+ }
199
+ return toDelete.length;
200
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * US-003 — the native session's model-facing truncation, applied to every
3
+ * genuine tool result before it enters the message array.
4
+ *
5
+ * This IS a registered `after_tool` handler now: `createTruncationHandler`
6
+ * builds it and `registerBuiltinLoopHandlers` registers it LAST, so it shapes
7
+ * whatever earlier handlers produced — the same position the loop's hardcoded
8
+ * call held before the seam carried it. It could not be registered while the
9
+ * dispatcher was synchronous: the spill write has to be awaited, because AC8's
10
+ * fail-open contract means the marker may only name the spill file when the
11
+ * write actually succeeded, so the write cannot be fired and forgotten and its
12
+ * outcome discovered afterwards. Dispatch is async now, so that documented
13
+ * exception is deleted rather than restated. The policy itself remains
14
+ * `applyModelTruncationPolicy` — the one implementation the coding-tool
15
+ * runtime applies too.
16
+ *
17
+ * The spill root is the session's scratchpad root, recorded at open from the
18
+ * session's workdir so the marker's relative path resolves through
19
+ * `ScratchpadRead`. A session driven without one (unit tests calling
20
+ * `runNativeTurn` directly) falls back to its transcript directory, the one path
21
+ * the session is known to own.
22
+ */
23
+ import type { HandlerOf } from "./loop-events/types.ts";
24
+ /**
25
+ * Where this session's spills belong, or `undefined` when the session is not
26
+ * known to the runtime at all — in which case nothing is spilled and the marker
27
+ * names no path, rather than naming a file written somewhere unreachable.
28
+ */
29
+ export declare function spillRootFor(sessionId: string): string | undefined;
30
+ /**
31
+ * Shape one tool result for the model: within every cap it is returned
32
+ * untouched (and nothing is spilled); over a cap it is cut, the untruncated
33
+ * body is written under the scratchpad, and the content ends with the marker
34
+ * naming the spill path and both byte counts.
35
+ */
36
+ export declare function truncateNativeToolResult(sessionId: string, body: string, opts: {
37
+ readonly toolName?: string;
38
+ readonly callId?: string;
39
+ readonly reserveBytes?: number;
40
+ }): Promise<string>;
41
+ /**
42
+ * The `after_tool` handler the loop registers: the same policy, at the same
43
+ * chokepoint, reached through the seam instead of a hardcoded call. A nudge
44
+ * riding on the payload reserves its own bytes out of the result's budget, so
45
+ * the ceiling holds after `withNudge` prepends it.
46
+ */
47
+ export declare function createTruncationHandler(sessionName: string): HandlerOf<"after_tool">;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * US-003 — the native session's model-facing truncation, applied to every
3
+ * genuine tool result before it enters the message array.
4
+ *
5
+ * This IS a registered `after_tool` handler now: `createTruncationHandler`
6
+ * builds it and `registerBuiltinLoopHandlers` registers it LAST, so it shapes
7
+ * whatever earlier handlers produced — the same position the loop's hardcoded
8
+ * call held before the seam carried it. It could not be registered while the
9
+ * dispatcher was synchronous: the spill write has to be awaited, because AC8's
10
+ * fail-open contract means the marker may only name the spill file when the
11
+ * write actually succeeded, so the write cannot be fired and forgotten and its
12
+ * outcome discovered afterwards. Dispatch is async now, so that documented
13
+ * exception is deleted rather than restated. The policy itself remains
14
+ * `applyModelTruncationPolicy` — the one implementation the coding-tool
15
+ * runtime applies too.
16
+ *
17
+ * The spill root is the session's scratchpad root, recorded at open from the
18
+ * session's workdir so the marker's relative path resolves through
19
+ * `ScratchpadRead`. A session driven without one (unit tests calling
20
+ * `runNativeTurn` directly) falls back to its transcript directory, the one path
21
+ * the session is known to own.
22
+ */
23
+ import { applyModelTruncationPolicy, MODEL_MAX_BYTES } from "#src/tools/index";
24
+ import { nudgeOverheadBytes } from "./nudge.js";
25
+ import { nativeSessionScratchpadRoots, nativeTranscriptDirs } from "./session.js";
26
+ /**
27
+ * Where this session's spills belong, or `undefined` when the session is not
28
+ * known to the runtime at all — in which case nothing is spilled and the marker
29
+ * names no path, rather than naming a file written somewhere unreachable.
30
+ */
31
+ export function spillRootFor(sessionId) {
32
+ return nativeSessionScratchpadRoots.get(sessionId) ?? nativeTranscriptDirs.get(sessionId);
33
+ }
34
+ /**
35
+ * Shape one tool result for the model: within every cap it is returned
36
+ * untouched (and nothing is spilled); over a cap it is cut, the untruncated
37
+ * body is written under the scratchpad, and the content ends with the marker
38
+ * naming the spill path and both byte counts.
39
+ */
40
+ export async function truncateNativeToolResult(sessionId, body, opts) {
41
+ const root = spillRootFor(sessionId);
42
+ // `reserveBytes` is what the CALLER will add to this result after the policy
43
+ // has run -- today only the spin-breaker nudge, which the turn loop prepends.
44
+ // Anything appended or prepended downstream has to be charged against the
45
+ // same ceiling here, or the guarantee this chokepoint exists to give (the
46
+ // model-facing content is at most MODEL_MAX_BYTES) is broken by exactly the
47
+ // bytes the caller adds. Reserving is what keeps the ceiling unconditional.
48
+ const reserved = Math.max(0, opts.reserveBytes ?? 0);
49
+ // A session opened through the runtime has a scratchpad root recorded from its
50
+ // workdir, which IS the directory its tools and shell start in, so a
51
+ // root-relative marker resolves there. The transcript-directory fallback is
52
+ // not that directory, so its marker must name the absolute path or the model
53
+ // would look for the spill somewhere it is not.
54
+ return applyModelTruncationPolicy(body, {
55
+ toolName: opts.toolName ?? "",
56
+ callId: opts.callId ?? "",
57
+ ...(root !== undefined
58
+ ? {
59
+ root,
60
+ spillPathStyle: nativeSessionScratchpadRoots.has(sessionId)
61
+ ? "root-relative"
62
+ : "absolute",
63
+ }
64
+ : {}),
65
+ maxBytes: Math.max(0, MODEL_MAX_BYTES - reserved),
66
+ });
67
+ }
68
+ /**
69
+ * The `after_tool` handler the loop registers: the same policy, at the same
70
+ * chokepoint, reached through the seam instead of a hardcoded call. A nudge
71
+ * riding on the payload reserves its own bytes out of the result's budget, so
72
+ * the ceiling holds after `withNudge` prepends it.
73
+ */
74
+ export function createTruncationHandler(sessionName) {
75
+ return async (payload) => {
76
+ const shaped = await truncateNativeToolResult(sessionName, payload.content, {
77
+ toolName: payload.toolName,
78
+ callId: payload.callId,
79
+ ...(payload.nudgeText !== undefined ? { reserveBytes: nudgeOverheadBytes(payload.nudgeText) } : {}),
80
+ });
81
+ return { content: shaped };
82
+ };
83
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The six usage/cost/rate counters `runNativeTurn` accumulates.
3
+ *
4
+ * Extracted because the same ten lines appeared three times — after a
5
+ * proactive compaction summary, after a reactive overflow summary, and after
6
+ * each round trip — and a fourth copy was one loop event away. A factory
7
+ * returning an object, matching `createRateTotals` and `createInvalidCallBudget`.
8
+ *
9
+ * `add` and `usageBeat` are deliberately separate: the round-trip beat carries
10
+ * `roundTrip` and the two summary beats do not, so folding the beat into `add`
11
+ * would either drop that field or fabricate it for the summaries.
12
+ */
13
+ import type { PricingRates, TokenUsage } from "#src/cost/standard-types";
14
+ import type { NativeTurnActivity } from "./turn-events.ts";
15
+ /**
16
+ * Token counts ONLY. `costUsd` is deliberately NOT a field here: both readers
17
+ * (`recordNativeTurnFailureUsage` at turn-loop.ts:542 and the TurnResult at
18
+ * :580) take `tokenUsage` and the cost as SEPARATE arguments, so folding cost
19
+ * in would force every call site to destructure it back out.
20
+ */
21
+ export interface TurnTokenTotals {
22
+ readonly inputTokens: number;
23
+ readonly outputTokens: number;
24
+ /**
25
+ * Undefined until something reports cache data, then a running sum. Staying
26
+ * undefined when nothing ever reports it preserves the absent/zero
27
+ * distinction nax-ai's `toTokenUsage` establishes: "no cache data" and "zero
28
+ * cache tokens" must stay distinguishable downstream (nax#2045).
29
+ */
30
+ readonly cacheReadTokens?: number;
31
+ readonly cacheWriteTokens?: number;
32
+ }
33
+ export interface TurnAccumulator {
34
+ add(usage: TokenUsage, costUsd: number, rates?: PricingRates): void;
35
+ tokens(): TurnTokenTotals;
36
+ costUsd(): number;
37
+ /** Aggregated rate provenance, or undefined when nothing priced. */
38
+ rates(): PricingRates | undefined;
39
+ }
40
+ export declare function createTurnAccumulator(): TurnAccumulator;
41
+ /**
42
+ * The `onActivity` usage beat. `roundTrip` is 1-based and omitted for the two
43
+ * compaction-summary beats, which are not round trips.
44
+ */
45
+ export declare function usageBeat(usage: TokenUsage, costUsd: number, roundTrip?: number): NativeTurnActivity;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The six usage/cost/rate counters `runNativeTurn` accumulates.
3
+ *
4
+ * Extracted because the same ten lines appeared three times — after a
5
+ * proactive compaction summary, after a reactive overflow summary, and after
6
+ * each round trip — and a fourth copy was one loop event away. A factory
7
+ * returning an object, matching `createRateTotals` and `createInvalidCallBudget`.
8
+ *
9
+ * `add` and `usageBeat` are deliberately separate: the round-trip beat carries
10
+ * `roundTrip` and the two summary beats do not, so folding the beat into `add`
11
+ * would either drop that field or fabricate it for the summaries.
12
+ */
13
+ import { addRateTotals, aggregateRates, createRateTotals } from "./rate-provenance.js";
14
+ import { cacheUsageFields } from "./turn-types.js";
15
+ export function createTurnAccumulator() {
16
+ let inputTokens = 0;
17
+ let outputTokens = 0;
18
+ let cacheReadTokens;
19
+ let cacheWriteTokens;
20
+ let costUsd = 0;
21
+ const rateTotals = createRateTotals();
22
+ return {
23
+ add(usage, addedCostUsd, rates) {
24
+ inputTokens += usage.inputTokens;
25
+ outputTokens += usage.outputTokens;
26
+ if (usage.cacheReadTokens !== undefined) {
27
+ cacheReadTokens = (cacheReadTokens ?? 0) + usage.cacheReadTokens;
28
+ }
29
+ if (usage.cacheWriteTokens !== undefined) {
30
+ cacheWriteTokens = (cacheWriteTokens ?? 0) + usage.cacheWriteTokens;
31
+ }
32
+ costUsd += addedCostUsd;
33
+ addRateTotals(rateTotals, usage, rates);
34
+ },
35
+ tokens() {
36
+ return {
37
+ inputTokens,
38
+ outputTokens,
39
+ ...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),
40
+ ...(cacheWriteTokens !== undefined ? { cacheWriteTokens } : {}),
41
+ };
42
+ },
43
+ costUsd() {
44
+ return costUsd;
45
+ },
46
+ rates() {
47
+ return aggregateRates(rateTotals);
48
+ },
49
+ };
50
+ }
51
+ /**
52
+ * The `onActivity` usage beat. `roundTrip` is 1-based and omitted for the two
53
+ * compaction-summary beats, which are not round trips.
54
+ */
55
+ export function usageBeat(usage, costUsd, roundTrip) {
56
+ return {
57
+ kind: "usage",
58
+ inputTokens: usage.inputTokens,
59
+ outputTokens: usage.outputTokens,
60
+ costUsd,
61
+ // Absent stays absent (never 0): `cacheReadTokens` stays
62
+ // `number | undefined` so "no cache data" and "zero cache tokens"
63
+ // remain distinguishable downstream (nax#2045).
64
+ ...cacheUsageFields(usage),
65
+ ...(roundTrip !== undefined ? { roundTrip } : {}),
66
+ };
67
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The `ask_human` branch of the turn loop's tool-call batch.
3
+ *
4
+ * Extracted unchanged. It is not an ordinary tool call: no tool produced it,
5
+ * so it deliberately fires no `before_tool`/`after_tool` event — a policy that
6
+ * shapes tool output has nothing to shape here.
7
+ */
8
+ import type { InteractionExchange, SendTurnOpts } from "#src/session/session-types";
9
+ import { type ToolResultMessage } from "./tool-result.ts";
10
+ export interface AskHumanOutcome {
11
+ /** Appended to the message array by the caller. */
12
+ readonly result: ToolResultMessage;
13
+ /** Present only when a real exchange happened; the caller pushes it. */
14
+ readonly exchange?: InteractionExchange;
15
+ }
16
+ export declare function handleAskHumanCall(args: {
17
+ readonly toolCallId: string;
18
+ readonly question: string;
19
+ readonly interactionsSoFar: number;
20
+ readonly maxInteractions: number;
21
+ readonly roundTrips: number;
22
+ readonly interactionHandler: SendTurnOpts["interactionHandler"];
23
+ }): Promise<AskHumanOutcome>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The `ask_human` branch of the turn loop's tool-call batch.
3
+ *
4
+ * Extracted unchanged. It is not an ordinary tool call: no tool produced it,
5
+ * so it deliberately fires no `before_tool`/`after_tool` event — a policy that
6
+ * shapes tool output has nothing to shape here.
7
+ */
8
+ import { buildToolResult } from "./tool-result.js";
9
+ export async function handleAskHumanCall(args) {
10
+ const { toolCallId, question, interactionsSoFar, maxInteractions, roundTrips, interactionHandler } = args;
11
+ // An unset budget (maxInteractions undefined -> 0) keeps the tool unadvertised
12
+ // above AND refuses a call made anyway. "No budget configured" must not
13
+ // read as "unlimited" — that inverts the property this budget provides.
14
+ if (interactionsSoFar >= maxInteractions) {
15
+ return {
16
+ result: buildToolResult({
17
+ toolCallId,
18
+ content: "The human Q&A budget for this turn is spent. Proceed on your best judgement.",
19
+ isError: true,
20
+ }),
21
+ };
22
+ }
23
+ const answer = await interactionHandler.onInteraction({ kind: "question", text: question });
24
+ // A null answer means no operator is reachable — run-interaction-handler
25
+ // returns null for kind:"question" when no interactionBridge is
26
+ // configured. That is not an exchange: it must not consume budget and
27
+ // must not be recorded as a question the operator answered with "".
28
+ if (answer === null) {
29
+ return {
30
+ result: buildToolResult({
31
+ toolCallId,
32
+ content: "No human operator is available for this run. Proceed on your best judgement.",
33
+ isError: true,
34
+ }),
35
+ };
36
+ }
37
+ return {
38
+ result: buildToolResult({ toolCallId, content: answer.answer }),
39
+ exchange: { turnIndex: roundTrips, question, reply: answer.answer },
40
+ };
41
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The two compaction steps of the native turn loop.
3
+ *
4
+ * `runProactiveCompaction` runs before a round trip when the estimate crosses
5
+ * the threshold; `runOverflowCompaction` is the backstop when a round trip
6
+ * throws a context overflow anyway. They are deliberately TWO functions, not one
7
+ * with a `reactive` flag, because they differ in exactly three ways a boolean
8
+ * would hide:
9
+ *
10
+ * 1. the backstop halves the keep budget (`keepBudget(..., true)`);
11
+ * 2. a summarizer throw is swallowed by the proactive step (the request may
12
+ * still fit) and propagates out of the backstop;
13
+ * 3. only the proactive step logs its progress (Finding 2, whole-branch
14
+ * review 2026-09-04);
15
+ * 4. a `before_compaction` decline is HONOURED proactively — the branch runs
16
+ * before any request, so sending uncompacted is a legitimate outcome — and
17
+ * IGNORED + logged at overflow, where the request has already failed with
18
+ * a context overflow and declining would leave no recovery (spec 6.2).
19
+ *
20
+ * They share only the summarize-and-apply core below. The failure semantics
21
+ * stay with the callers on purpose — see each function's catch.
22
+ */
23
+ import { type TranscriptMessage as NativeTranscriptMessage } from "./compaction.ts";
24
+ import type { LoopEventRegistry } from "./loop-events/index.ts";
25
+ import { type TurnAccumulator } from "./turn-accumulator.ts";
26
+ import type { TurnDeps } from "./turn-types.ts";
27
+ /**
28
+ * The compaction dependencies, with the three the step cannot run without
29
+ * required. `TurnDeps` has them optional (absent disables compaction); the call
30
+ * site narrows them to defined with the guard before calling, and requiring them
31
+ * here is what carries that narrowing into this module.
32
+ */
33
+ export interface CompactionStepDeps {
34
+ readonly summarize: NonNullable<TurnDeps["summarize"]>;
35
+ readonly contextWindow: NonNullable<TurnDeps["contextWindow"]>;
36
+ readonly compaction: NonNullable<TurnDeps["compaction"]>;
37
+ readonly onActivity?: TurnDeps["onActivity"];
38
+ readonly deadline?: TurnDeps["deadline"];
39
+ }
40
+ export interface CompactionStepArgs {
41
+ readonly messages: readonly NativeTranscriptMessage[];
42
+ readonly usage: TurnAccumulator;
43
+ readonly sessionName: string;
44
+ readonly lastUsage: {
45
+ readonly promptTokens: number;
46
+ } | undefined;
47
+ readonly anchorIndex: number | undefined;
48
+ /**
49
+ * The registry actually in use — the loop's local (`deps.loopEvents ??
50
+ * createLoopEventRegistry()`), not `deps.loopEvents` itself, which is
51
+ * usually absent and would dispatch to nothing. Threaded from `turn-loop.ts`
52
+ * and from `completeWithRecovery`'s own arg, the same as `CompleteStepArgs`.
53
+ */
54
+ readonly loopEvents: LoopEventRegistry;
55
+ readonly deps: CompactionStepDeps;
56
+ readonly signal?: AbortSignal;
57
+ }
58
+ export interface CompactionStepResult {
59
+ readonly messages: readonly NativeTranscriptMessage[];
60
+ /** True when the array was rebound, so the caller clears lastUsage/anchorIndex. */
61
+ readonly compacted: boolean;
62
+ /** Proactive only: the summarizer threw, so the overflow retry must not try again. */
63
+ readonly summarizeFailed: boolean;
64
+ }
65
+ export interface OverflowCompactionArgs extends CompactionStepArgs {
66
+ /**
67
+ * The original context-overflow error. `prepareCompaction` returning undefined
68
+ * means there is nothing safe left to drop, so it is rethrown verbatim —
69
+ * tests and the failure-usage ledger key on the error's own identity.
70
+ */
71
+ readonly error: unknown;
72
+ }
73
+ export declare function runProactiveCompaction(args: CompactionStepArgs): Promise<CompactionStepResult>;
74
+ export declare function runOverflowCompaction(args: OverflowCompactionArgs): Promise<CompactionStepResult>;