@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,224 @@
1
+ /**
2
+ * Run an argv with no shell, a deadline, and a process-group kill.
3
+ *
4
+ * Extracted from worktree/dependencies.ts so its two callers cannot drift.
5
+ * The three behaviours here were each a defect once and must not be rewritten
6
+ * from scratch: MEM-4 (a postinstall grandchild survived proc.kill() and kept
7
+ * running against a deleted worktree), BUG-13 (a hung install had no deadline),
8
+ * and the concurrent drain (a child that fills a pipe buffer never reaches
9
+ * `exited`, defeating the timeout).
10
+ *
11
+ * US-001 adds three more invariants on the same shape:
12
+ * - `signal?: AbortSignal` aborts the process group and reports `aborted`.
13
+ * - An already-aborted signal never spawns and resolves with exitCode -1.
14
+ * - After `exited`, both readers get `DRAIN_GRACE_MS` to close; if either
15
+ * stays open, the group is SIGKILLed and `orphansKilled: true` is set with
16
+ * whatever the readers captured so far.
17
+ */
18
+ import { runtimeSpawn } from "#src/runtime/index";
19
+ import { killProcessGroup } from "./process-kill.js";
20
+ /**
21
+ * Time, in milliseconds, the readers are allowed to drain after `exited`
22
+ * before runArgv SIGKILLs the whole group and reports `orphansKilled`. US-001.
23
+ * Injectable for tests; not exposed as user configuration.
24
+ */
25
+ export const DRAIN_GRACE_MS = 500;
26
+ /** Injectable seam, mirroring _worktreeDependencyDeps. */
27
+ export const _argvExecDeps = {
28
+ spawn: runtimeSpawn,
29
+ killProcessGroup,
30
+ /**
31
+ * The post-exit drain grace (US-001). A real test can shrink it to keep the
32
+ * "background process holding the pipe" case under a second; production
33
+ * stays at the DRAIN_GRACE_MS constant.
34
+ */
35
+ drainGraceMs: DRAIN_GRACE_MS,
36
+ /**
37
+ * Injectable timer pair — lets the BUG-13 "hung install" test drive the
38
+ * timeout off a virtual clock instead of waiting the full 1s schema minimum.
39
+ * Mirrors `_heartbeatDeps` / `_idleWatchdogDeps` / `_authDeps`.
40
+ *
41
+ * @internal
42
+ */
43
+ setTimeout: ((fn, ms) => setTimeout(fn, ms)),
44
+ clearTimeout: ((id) => clearTimeout(id)),
45
+ };
46
+ /**
47
+ * Build the child's env only when the caller actually asked for stripping or
48
+ * an overlay. Leaving it `undefined` otherwise means `Bun.spawn` inherits
49
+ * `process.env` on its own, which is both the cheaper path and what every
50
+ * caller without those options already relied on before this seam existed.
51
+ */
52
+ function buildEnv(options) {
53
+ const hasStrip = (options.stripEnvVars?.length ?? 0) > 0;
54
+ const hasOverlay = options.env !== undefined && Object.keys(options.env).length > 0;
55
+ if (!hasStrip && !hasOverlay)
56
+ return undefined;
57
+ const env = { ...process.env };
58
+ for (const name of options.stripEnvVars ?? [])
59
+ delete env[name];
60
+ Object.assign(env, options.env ?? {});
61
+ return env;
62
+ }
63
+ /**
64
+ * Read a ReadableStream<Uint8Array> to EOF, accumulating text. Resolves when
65
+ * the stream closes (done). The caller races this against a wall-clock
66
+ * deadline to detect an open-after-exit pipe (the `&`ed background case).
67
+ *
68
+ * `Response.text()` doesn't honour a deadline, so the reader is its own
69
+ * function (US-001 "Read stdout and stderr incrementally/concurrently").
70
+ *
71
+ * When `signal` aborts, the reader is cancelled so the pending `read()`
72
+ * resolves with `{done: true}`. The caller uses this to bound settlement
73
+ * after a process-group kill: a child that ignores the kill cannot pin
74
+ * runArgv forever on its still-open pipe.
75
+ */
76
+ async function drainToEof(stream, signal) {
77
+ const reader = stream.getReader();
78
+ const decoder = new TextDecoder();
79
+ let text = "";
80
+ const onAbort = () => {
81
+ // Closing the stream forces the in-flight `read()` below to resolve with
82
+ // `{done: true}`; any bytes already captured stay in `text`.
83
+ reader.cancel().catch(() => {
84
+ // Cancelling after the pipe already closed is a no-op race; ignore.
85
+ });
86
+ };
87
+ signal?.addEventListener("abort", onAbort, { once: true });
88
+ try {
89
+ while (true) {
90
+ const { done, value } = await reader.read();
91
+ if (done === true)
92
+ break;
93
+ if (value !== undefined)
94
+ text += decoder.decode(value, { stream: true });
95
+ }
96
+ }
97
+ catch {
98
+ // Stream cancelled by SIGKILL on the process group; the partial text
99
+ // is still in `text`, so resolve with closed=false and let the caller
100
+ // decide.
101
+ }
102
+ text += decoder.decode();
103
+ if (signal !== undefined)
104
+ signal.removeEventListener("abort", onAbort);
105
+ try {
106
+ reader.releaseLock();
107
+ }
108
+ catch {
109
+ // Already released; nothing to do.
110
+ }
111
+ return { text, closed: true };
112
+ }
113
+ export async function runArgv(options) {
114
+ const signal = options.signal;
115
+ // Already-aborted signal: never spawn. The caller gets `aborted: true` with
116
+ // a sentinel exitCode so a misread of the field can't masquerade as a real
117
+ // process completing (US-001 AC7).
118
+ if (signal?.aborted === true) {
119
+ return { exitCode: -1, stdout: "", stderr: "", timedOut: false, aborted: true, orphansKilled: false };
120
+ }
121
+ const env = buildEnv(options);
122
+ // nax-git-env-allow: generic argv executor; caller-supplied argv, not routed through gitSpawnEnv
123
+ const proc = _argvExecDeps.spawn([...options.argv], {
124
+ cwd: options.cwd,
125
+ stdout: "pipe",
126
+ stderr: "pipe",
127
+ ...(env !== undefined ? { env } : {}),
128
+ // MEM-4: setsid() makes this pid the process-group id, so a group kill
129
+ // reaches grandchildren (a package manager's postinstall) rather than only
130
+ // the direct child.
131
+ detached: true,
132
+ });
133
+ let timedOut = false;
134
+ let aborted = false;
135
+ let orphansKilled = false;
136
+ const killGroup = () => {
137
+ _argvExecDeps.killProcessGroup(proc.pid, "SIGKILL");
138
+ };
139
+ const stdoutController = new AbortController();
140
+ const stderrController = new AbortController();
141
+ const stopReaders = () => {
142
+ stdoutController.abort();
143
+ stderrController.abort();
144
+ };
145
+ // BUG-13: unlike every git call (routed through gitWithTimeout), a spawn
146
+ // with no deadline can block its caller forever on a hung install
147
+ // (registry/NFS stall).
148
+ const timerId = _argvExecDeps.setTimeout(() => {
149
+ timedOut = true;
150
+ // MEM-4: proc.kill() reaches only the direct child, orphaning postinstall
151
+ // grandchildren. killProcessGroup(pid, "SIGKILL") kills the whole group
152
+ // (negative pid), falling back to the single process on ESRCH.
153
+ killGroup();
154
+ stopReaders();
155
+ }, options.timeoutMs);
156
+ // US-001: an abort races the timeout. One listener, removed on every settle
157
+ // path so a never-aborted signal never leaks a registration (AC10).
158
+ const onAbort = () => {
159
+ aborted = true;
160
+ killGroup();
161
+ stopReaders();
162
+ };
163
+ signal?.addEventListener("abort", onAbort, { once: true });
164
+ // Read stdout/stderr concurrently with the exit wait — a process that
165
+ // fills a pipe's OS buffer before being read would otherwise block on the
166
+ // write and never reach `exited`, defeating the timeout's own SIGKILL.
167
+ // Each reader has its own controller so a still-pending one can be forced
168
+ // to settle (via reader.cancel()) after we kill the process group. US-001
169
+ // requires that settlement stay bounded even when a background process
170
+ // inherits the pipe and ignores the SIGKILL.
171
+ const stdoutPromise = drainToEof(proc.stdout, stdoutController.signal);
172
+ const stderrPromise = drainToEof(proc.stderr, stderrController.signal);
173
+ const exitCode = await proc.exited;
174
+ // Post-exit drain grace. After exit, the streams MUST close — they were
175
+ // owned by the shell, which is now dead. Background processes that
176
+ // inherited the pipe (the `&` case) keep it open: that's the orphan.
177
+ const graceMs = _argvExecDeps.drainGraceMs;
178
+ // Cleared on every settle path: a successful command whose readers close
179
+ // before the grace elapses would otherwise leave a pending timer whose
180
+ // callback fires 500ms later and resolves an orphaned promise. Under load
181
+ // the leaked registrations accumulate in the timer wheel for nothing.
182
+ let graceTimerId;
183
+ const gracePromise = new Promise((resolve) => {
184
+ graceTimerId = _argvExecDeps.setTimeout(() => resolve("expired"), graceMs);
185
+ });
186
+ const stdoutSettled = await Promise.race([
187
+ stdoutPromise,
188
+ gracePromise.then(() => "expired"),
189
+ ]);
190
+ const stderrSettled = await Promise.race([
191
+ stderrPromise,
192
+ gracePromise.then(() => "expired"),
193
+ ]);
194
+ if (graceTimerId !== undefined)
195
+ _argvExecDeps.clearTimeout(graceTimerId);
196
+ const stdoutClosed = stdoutSettled !== "expired";
197
+ const stderrClosed = stderrSettled !== "expired";
198
+ if (!stdoutClosed || !stderrClosed) {
199
+ orphansKilled = true;
200
+ killGroup();
201
+ // The OS should close the pipes once the group dies; race that against
202
+ // a small post-kill budget. If a holdout somehow ignores SIGKILL or is
203
+ // not yet reaped, abort the matching reader controller — that drives
204
+ // `reader.cancel()` in drainToEof, which forces the pending `read()` to
205
+ // resolve with `done: true` and bounds settlement.
206
+ if (!stdoutClosed)
207
+ stdoutController.abort();
208
+ if (!stderrClosed)
209
+ stderrController.abort();
210
+ }
211
+ // Always await the readers to harvest whatever they captured.
212
+ const stdoutFinal = stdoutClosed ? stdoutSettled : await stdoutPromise;
213
+ const stderrFinal = stderrClosed ? stderrSettled : await stderrPromise;
214
+ _argvExecDeps.clearTimeout(timerId);
215
+ signal?.removeEventListener("abort", onAbort);
216
+ return {
217
+ exitCode,
218
+ stdout: stdoutFinal.text,
219
+ stderr: stderrFinal.text,
220
+ timedOut,
221
+ aborted,
222
+ orphansKilled,
223
+ };
224
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Ceilings that bound the WORK, not just the answer.
3
+ *
4
+ * Every tool honoured `maxBytes` on the way out, but produced its output by
5
+ * reading a whole file or draining a whole stream first. The ceiling described
6
+ * what the model was told while the memory it took to get there was unbounded,
7
+ * so a large but entirely permitted in-root file was enough to exhaust it --
8
+ * no traversal, no escape, nothing the permission policy could see.
9
+ *
10
+ * Both helpers deliberately return up to `maxBytes + 1` bytes. That extra byte
11
+ * is what lets a caller distinguish "this is the whole thing" from "there was
12
+ * more", without a second stat or read.
13
+ */
14
+ /** Read at most `maxBytes + 1` bytes of a UTF-8 file, never the whole file. */
15
+ export declare function readPrefix(path: string, maxBytes: number): Promise<string>;
16
+ /**
17
+ * Consume a stream until `maxBytes + 1` bytes have arrived, then stop.
18
+ *
19
+ * Cancelling the reader is the point: a subprocess whose output is no longer
20
+ * being consumed blocks on its own pipe and is then reaped by its caller's
21
+ * timeout, instead of being allowed to fill memory for as long as that timeout
22
+ * permits. `git log -p` on a large repository is the case in mind.
23
+ */
24
+ export declare function drainBounded(stream: ReadableStream<Uint8Array>, maxBytes: number): Promise<string>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Ceilings that bound the WORK, not just the answer.
3
+ *
4
+ * Every tool honoured `maxBytes` on the way out, but produced its output by
5
+ * reading a whole file or draining a whole stream first. The ceiling described
6
+ * what the model was told while the memory it took to get there was unbounded,
7
+ * so a large but entirely permitted in-root file was enough to exhaust it --
8
+ * no traversal, no escape, nothing the permission policy could see.
9
+ *
10
+ * Both helpers deliberately return up to `maxBytes + 1` bytes. That extra byte
11
+ * is what lets a caller distinguish "this is the whole thing" from "there was
12
+ * more", without a second stat or read.
13
+ */
14
+ import { open } from "node:fs/promises";
15
+ /** Read at most `maxBytes + 1` bytes of a UTF-8 file, never the whole file. */
16
+ export async function readPrefix(path, maxBytes) {
17
+ const handle = await open(path, "r");
18
+ try {
19
+ const buffer = Buffer.alloc(maxBytes + 1);
20
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
21
+ return buffer.subarray(0, bytesRead).toString("utf8");
22
+ }
23
+ finally {
24
+ await handle.close();
25
+ }
26
+ }
27
+ /**
28
+ * Consume a stream until `maxBytes + 1` bytes have arrived, then stop.
29
+ *
30
+ * Cancelling the reader is the point: a subprocess whose output is no longer
31
+ * being consumed blocks on its own pipe and is then reaped by its caller's
32
+ * timeout, instead of being allowed to fill memory for as long as that timeout
33
+ * permits. `git log -p` on a large repository is the case in mind.
34
+ */
35
+ export async function drainBounded(stream, maxBytes) {
36
+ const reader = stream.getReader();
37
+ const decoder = new TextDecoder();
38
+ let out = "";
39
+ try {
40
+ while (Buffer.byteLength(out, "utf8") <= maxBytes) {
41
+ const { done, value } = await reader.read();
42
+ if (done)
43
+ break;
44
+ out += decoder.decode(value, { stream: true });
45
+ }
46
+ return out;
47
+ }
48
+ finally {
49
+ await reader.cancel().catch(() => { });
50
+ }
51
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A declared quality command. A plain string is one shell command (the
3
+ * historical shape). A list means "run every entry, report every failure" —
4
+ * the form that exists because `a && b` short-circuits, hiding each failure
5
+ * after the first from an agent that pays a full round trip per invocation
6
+ * (nax#1990).
7
+ */
8
+ export type QualityCommandSpec = string | string[];
9
+ /**
10
+ * Flatten a spec to the list of commands to actually run. Blank entries are
11
+ * dropped so a stray "" in a list cannot spawn an empty shell; a spec that is
12
+ * entirely blank returns [], which callers treat as "not declared".
13
+ */
14
+ export declare function normalizeCommandSpec(spec: QualityCommandSpec | undefined): string[];
15
+ /**
16
+ * True when any entry chains with `&&`. Used only to warn: a chain still runs
17
+ * exactly as it always did, it just hides later failures.
18
+ */
19
+ export declare function containsShellChain(spec: QualityCommandSpec | undefined): boolean;
20
+ /** True when any command entry includes the supplied literal fragment. */
21
+ export declare function commandSpecIncludes(spec: QualityCommandSpec | undefined, fragment: string): boolean;
22
+ /** Apply a literal replacement to every command entry without changing its shape. */
23
+ export declare function replaceInCommandSpec(spec: QualityCommandSpec, searchValue: string, replacement: string): QualityCommandSpec;
24
+ /**
25
+ * Render a spec as the single string that prompt and context surfaces show a
26
+ * reader. `" && "` is what a human recognises and would type, even though a
27
+ * list does not actually short-circuit when it runs.
28
+ */
29
+ export declare function renderCommandSpec(spec: QualityCommandSpec | undefined): string | undefined;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Flatten a spec to the list of commands to actually run. Blank entries are
3
+ * dropped so a stray "" in a list cannot spawn an empty shell; a spec that is
4
+ * entirely blank returns [], which callers treat as "not declared".
5
+ */
6
+ export function normalizeCommandSpec(spec) {
7
+ if (spec === undefined)
8
+ return [];
9
+ const entries = typeof spec === "string" ? [spec] : spec;
10
+ return entries.map((entry) => entry.trim()).filter((entry) => entry !== "");
11
+ }
12
+ /**
13
+ * True when any entry chains with `&&`. Used only to warn: a chain still runs
14
+ * exactly as it always did, it just hides later failures.
15
+ */
16
+ export function containsShellChain(spec) {
17
+ return normalizeCommandSpec(spec).some((entry) => entry.includes("&&"));
18
+ }
19
+ /** True when any command entry includes the supplied literal fragment. */
20
+ export function commandSpecIncludes(spec, fragment) {
21
+ return normalizeCommandSpec(spec).some((entry) => entry.includes(fragment));
22
+ }
23
+ /** Apply a literal replacement to every command entry without changing its shape. */
24
+ export function replaceInCommandSpec(spec, searchValue, replacement) {
25
+ const replace = (command) => command.replaceAll(searchValue, replacement);
26
+ return typeof spec === "string" ? replace(spec) : spec.map(replace);
27
+ }
28
+ /**
29
+ * Render a spec as the single string that prompt and context surfaces show a
30
+ * reader. `" && "` is what a human recognises and would type, even though a
31
+ * list does not actually short-circuit when it runs.
32
+ */
33
+ export function renderCommandSpec(spec) {
34
+ const steps = normalizeCommandSpec(spec);
35
+ return steps.length === 0 ? undefined : steps.join(" && ");
36
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Vocabulary shared by the tool-input validator (`src/agents/native/session/
3
+ * tool-input-validation.ts`, nax#2047) and `run-command.ts`'s own `values`
4
+ * shape guard (#1924): the "actual" half of a violation reads exactly the
5
+ * way run-command.ts itself would have phrased the same shape, so the gate
6
+ * and the tool produce the same wording.
7
+ */
8
+ export declare function describeValuesType(value: unknown): string;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Vocabulary shared by the tool-input validator (`src/agents/native/session/
3
+ * tool-input-validation.ts`, nax#2047) and `run-command.ts`'s own `values`
4
+ * shape guard (#1924): the "actual" half of a violation reads exactly the
5
+ * way run-command.ts itself would have phrased the same shape, so the gate
6
+ * and the tool produce the same wording.
7
+ */
8
+ export function describeValuesType(value) {
9
+ if (value === null)
10
+ return "null";
11
+ if (Array.isArray(value))
12
+ return "an array";
13
+ return `a ${typeof value}`;
14
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Frame a finished spawn result the way the model expects to see it: an
3
+ * aborted call opens with the cancellation line, an orphansKilled call appends
4
+ * the `[nax]` footer, a timed-out call reports the deadline, and everything
5
+ * else gets `exit N\nstdout\nstderr` (US-001 AC14/AC15).
6
+ *
7
+ * One function shared by the `Bash` tool and `RunCommand` (Exec) so the two
8
+ * ladders a model reads interchangeably cannot drift — the deadline label is
9
+ * the only difference between the call sites, so it is a parameter.
10
+ */
11
+ export declare function formatExecBody(launched: {
12
+ aborted?: boolean;
13
+ orphansKilled?: boolean;
14
+ timedOut: boolean;
15
+ exitCode: number;
16
+ stdout: string;
17
+ stderr: string;
18
+ }, timeoutMs: number): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Frame a finished spawn result the way the model expects to see it: an
3
+ * aborted call opens with the cancellation line, an orphansKilled call appends
4
+ * the `[nax]` footer, a timed-out call reports the deadline, and everything
5
+ * else gets `exit N\nstdout\nstderr` (US-001 AC14/AC15).
6
+ *
7
+ * One function shared by the `Bash` tool and `RunCommand` (Exec) so the two
8
+ * ladders a model reads interchangeably cannot drift — the deadline label is
9
+ * the only difference between the call sites, so it is a parameter.
10
+ */
11
+ export function formatExecBody(launched, timeoutMs) {
12
+ if (launched.aborted === true) {
13
+ return `Cancelled: the turn ended while this command was running.\nexit ${launched.exitCode}\n${launched.stdout}\n${launched.stderr}`;
14
+ }
15
+ if (launched.orphansKilled === true) {
16
+ return `exit ${launched.exitCode}\n${launched.stdout}\n${launched.stderr}\n[nax] background processes still holding the output were killed`;
17
+ }
18
+ if (launched.timedOut) {
19
+ return `timed out after ${timeoutMs}ms`;
20
+ }
21
+ return `exit ${launched.exitCode}\n${launched.stdout}\n${launched.stderr}`;
22
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Shared single-file lock used by `withPathFileLock` and `withQueueFileLock`.
3
+ *
4
+ * Mutual exclusion derives from the atomicity of an exclusive create: the
5
+ * lock is ONE file at `<target>.lock`, and the writer whose
6
+ * `writeFile(path, pid, { flag: "wx" })` succeeds holds the lock. There is
7
+ * no candidate ordering, no filesystem birthtime, and no polling race.
8
+ *
9
+ * #1731: the previous design created timestamped candidate files and polled
10
+ * until it saw itself as the "oldest live" candidate. That protocol is only
11
+ * sound if candidate order matches creation order, which it did not: the
12
+ * sort key was `(stat().birthtimeMs, name.localeCompare)` — on Linux CI
13
+ * filesystems birthtimeMs is degenerate so order collapsed to the random
14
+ * uuid tail of the name, and a poll whose readdir completed before a
15
+ * competitor's create landed missed that competitor entirely. A
16
+ * later-created candidate could therefore sort before the holder, also
17
+ * conclude it was oldest, and enter — two writers inside the critical
18
+ * section, one write silently lost. An exclusive create cannot exhibit any
19
+ * of those failure modes: exactly one create ever succeeds.
20
+ *
21
+ * Staleness (BUG-10 / BUG-25, carried over from the candidate design):
22
+ * - The holder's pid is stored in the lock file. A waiter that loses the
23
+ * create race reads the pid; a PROVEN-dead holder (ESRCH) means the lock
24
+ * is abandoned and may be reclaimed.
25
+ * - Content that cannot be parsed (e.g. the creator crashed between the
26
+ * create and the pid write) is respected until it ages past
27
+ * `EMPTY_LOCK_EVICT_AGE_MS` — a live holder writes its pid within the
28
+ * same tick as the create, so old unparseable content proves abandonment.
29
+ * Inconclusive liveness fails closed: the waiter times out and throws
30
+ * rather than entering over a possible holder.
31
+ *
32
+ * Reclamation is serialized by a claim file (`<target>.lock.claim`), whose
33
+ * exclusive create elects a single "gravedigger" at a time. This closes the
34
+ * steal race: without it, two waiters that both observed the dead holder
35
+ * could interleave (stealer A's create landing between stealer B's
36
+ * re-verification and its unlink), letting B unlink A's fresh live lock and
37
+ * enter over it. Under the claim:
38
+ * - Only the claim holder may unlink the lock file, so no unlink can make
39
+ * the path absent during another gravedigger's verify→unlink window.
40
+ * - The gravedigger RE-VERIFIES abandonment after winning the claim (a
41
+ * stale observation must not kill a lock that a new live holder has
42
+ * since created) and only then unlinks.
43
+ * - If two gravediggers ever do overlap (only possible via the claim's own
44
+ * age-based steal), both re-verify the same dead file, both unlink (the
45
+ * second gets ENOENT), and the exclusive create on the fixed lock path
46
+ * still admits exactly one holder.
47
+ * The claim itself is held for microseconds, so it is stolen purely on age
48
+ * (`CLAIM_MAX_AGE_MS`): a claim older than that proves its gravedigger died
49
+ * mid-reclaim. A crashed gravedigger otherwise pauses reclamation until
50
+ * then — fail closed, never fail into someone else's live lock.
51
+ *
52
+ * Release unlinks the lock file in the `finally` block. The unlink is
53
+ * swallowed because release runs from the same process that created the
54
+ * file — a double-cleanup on retry paths must not throw. A crashed holder
55
+ * leaks the file, which the dead-pid reclamation above reclaims.
56
+ *
57
+ * Mixed versions: the pre-#1731 design wrote `<target>.lock.<time>.<pid>.<uuid>`
58
+ * candidate files, which this design deliberately does NOT scan for — they
59
+ * are inert leftovers here (and legacy candidate files are never GC'd). An
60
+ * old-version process and a new-version process must therefore not run
61
+ * concurrently against the same target during a one-release transition
62
+ * window: each would see only its own lock format and could enter
63
+ * concurrently. This self-heals once old-version processes exit.
64
+ */
65
+ import { readFile, stat, unlink, writeFile } from "node:fs/promises";
66
+ import { isProcessAlive } from "./process-alive.ts";
67
+ export interface FileLockOptions {
68
+ /** Milliseconds between acquisition retries. Default 10. */
69
+ retryMs?: number;
70
+ /** Maximum time to wait for the lock before throwing. Default 5000. */
71
+ timeoutMs?: number;
72
+ /** Lock family used in the timeout error, e.g. "path" or "queue". */
73
+ lockName: string;
74
+ /** Log/error prefix, e.g. "[path-lock]". */
75
+ errorPrefix: string;
76
+ }
77
+ export declare const _fileLockDeps: {
78
+ writeFile: typeof writeFile;
79
+ readFile: typeof readFile;
80
+ stat: typeof stat;
81
+ unlink: typeof unlink;
82
+ now: () => number;
83
+ sleep: (ms: number) => Promise<void>;
84
+ isPidAlive: typeof isProcessAlive;
85
+ };
86
+ /**
87
+ * Serialize `operation` against any other process holding the lock at
88
+ * `lockPath`. See the module doc for the acquisition, staleness, and
89
+ * release semantics.
90
+ */
91
+ export declare function withFileLock<T>(lockPath: string, operation: () => Promise<T>, options: FileLockOptions): Promise<T>;