clinkx 0.3.0 → 1.0.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 (555) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +10 -0
  3. package/README.md +82 -26
  4. package/clinkx-workflows/dist/allowed-roots.d.ts +1 -0
  5. package/clinkx-workflows/dist/allowed-roots.js +5 -0
  6. package/clinkx-workflows/dist/allowed-roots.js.map +1 -0
  7. package/clinkx-workflows/dist/artifacts.d.ts +20 -1
  8. package/clinkx-workflows/dist/artifacts.js +38 -9
  9. package/clinkx-workflows/dist/artifacts.js.map +1 -1
  10. package/clinkx-workflows/dist/backend.d.ts +4 -0
  11. package/clinkx-workflows/dist/citation-check.d.ts +40 -0
  12. package/clinkx-workflows/dist/citation-check.js +348 -0
  13. package/clinkx-workflows/dist/citation-check.js.map +1 -0
  14. package/clinkx-workflows/dist/clink-client.d.ts +2 -1
  15. package/clinkx-workflows/dist/clink-client.js +17 -4
  16. package/clinkx-workflows/dist/clink-client.js.map +1 -1
  17. package/clinkx-workflows/dist/config.d.ts +145 -16
  18. package/clinkx-workflows/dist/config.js +384 -26
  19. package/clinkx-workflows/dist/config.js.map +1 -1
  20. package/clinkx-workflows/dist/contained-file.d.ts +172 -0
  21. package/clinkx-workflows/dist/contained-file.js +483 -0
  22. package/clinkx-workflows/dist/contained-file.js.map +1 -0
  23. package/clinkx-workflows/dist/context-builder-lifecycle-contract.d.ts +416 -0
  24. package/clinkx-workflows/dist/context-builder-lifecycle-contract.js +328 -0
  25. package/clinkx-workflows/dist/context-builder-lifecycle-contract.js.map +1 -0
  26. package/clinkx-workflows/dist/context-builder-lifecycle-router.d.ts +24 -0
  27. package/clinkx-workflows/dist/context-builder-lifecycle-router.js +39 -0
  28. package/clinkx-workflows/dist/context-builder-lifecycle-router.js.map +1 -0
  29. package/clinkx-workflows/dist/context-cache-key.d.ts +53 -0
  30. package/clinkx-workflows/dist/context-cache-key.js +91 -0
  31. package/clinkx-workflows/dist/context-cache-key.js.map +1 -0
  32. package/clinkx-workflows/dist/context-mode-resolver.d.ts +27 -0
  33. package/clinkx-workflows/dist/context-mode-resolver.js +126 -0
  34. package/clinkx-workflows/dist/context-mode-resolver.js.map +1 -0
  35. package/clinkx-workflows/dist/context-preflight.d.ts +184 -0
  36. package/clinkx-workflows/dist/context-preflight.js +91 -0
  37. package/clinkx-workflows/dist/context-preflight.js.map +1 -0
  38. package/clinkx-workflows/dist/council-expander.d.ts +1 -1
  39. package/clinkx-workflows/dist/council-expander.js +2 -1
  40. package/clinkx-workflows/dist/council-expander.js.map +1 -1
  41. package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +165 -0
  42. package/clinkx-workflows/dist/dashboard/activity-contract.js +105 -0
  43. package/clinkx-workflows/dist/dashboard/activity-contract.js.map +1 -0
  44. package/clinkx-workflows/dist/dashboard/activity-lease.d.ts +25 -0
  45. package/clinkx-workflows/dist/dashboard/activity-lease.js +34 -0
  46. package/clinkx-workflows/dist/dashboard/activity-lease.js.map +1 -0
  47. package/clinkx-workflows/dist/dashboard/activity-reader.d.ts +86 -0
  48. package/clinkx-workflows/dist/dashboard/activity-reader.js +480 -0
  49. package/clinkx-workflows/dist/dashboard/activity-reader.js.map +1 -0
  50. package/clinkx-workflows/dist/dashboard/api-routes.d.ts +36 -0
  51. package/clinkx-workflows/dist/dashboard/api-routes.js +1047 -0
  52. package/clinkx-workflows/dist/dashboard/api-routes.js.map +1 -0
  53. package/clinkx-workflows/dist/dashboard/artifact-index.d.ts +37 -0
  54. package/clinkx-workflows/dist/dashboard/artifact-index.js +145 -0
  55. package/clinkx-workflows/dist/dashboard/artifact-index.js.map +1 -0
  56. package/clinkx-workflows/dist/dashboard/auth/challenge-store.d.ts +31 -0
  57. package/clinkx-workflows/dist/dashboard/auth/challenge-store.js +65 -0
  58. package/clinkx-workflows/dist/dashboard/auth/challenge-store.js.map +1 -0
  59. package/clinkx-workflows/dist/dashboard/auth/controller.d.ts +26 -0
  60. package/clinkx-workflows/dist/dashboard/auth/controller.js +596 -0
  61. package/clinkx-workflows/dist/dashboard/auth/controller.js.map +1 -0
  62. package/clinkx-workflows/dist/dashboard/auth/credential-store.d.ts +60 -0
  63. package/clinkx-workflows/dist/dashboard/auth/credential-store.js +776 -0
  64. package/clinkx-workflows/dist/dashboard/auth/credential-store.js.map +1 -0
  65. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.d.ts +9 -0
  66. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js +44 -0
  67. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js.map +1 -0
  68. package/clinkx-workflows/dist/dashboard/auth/session-store.d.ts +17 -0
  69. package/clinkx-workflows/dist/dashboard/auth/session-store.js +77 -0
  70. package/clinkx-workflows/dist/dashboard/auth/session-store.js.map +1 -0
  71. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.d.ts +25 -0
  72. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js +71 -0
  73. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js.map +1 -0
  74. package/clinkx-workflows/dist/dashboard/context-run-liveness.d.ts +37 -0
  75. package/clinkx-workflows/dist/dashboard/context-run-liveness.js +49 -0
  76. package/clinkx-workflows/dist/dashboard/context-run-liveness.js.map +1 -0
  77. package/clinkx-workflows/dist/dashboard/context-run-reader.d.ts +222 -0
  78. package/clinkx-workflows/dist/dashboard/context-run-reader.js +1178 -0
  79. package/clinkx-workflows/dist/dashboard/context-run-reader.js.map +1 -0
  80. package/clinkx-workflows/dist/dashboard/contracts.d.ts +8936 -0
  81. package/clinkx-workflows/dist/dashboard/contracts.js +1157 -0
  82. package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -0
  83. package/clinkx-workflows/dist/dashboard/generation.d.ts +14 -0
  84. package/clinkx-workflows/dist/dashboard/generation.js +41 -0
  85. package/clinkx-workflows/dist/dashboard/generation.js.map +1 -0
  86. package/clinkx-workflows/dist/dashboard/index.d.ts +28 -0
  87. package/clinkx-workflows/dist/dashboard/index.js +16 -0
  88. package/clinkx-workflows/dist/dashboard/index.js.map +1 -0
  89. package/clinkx-workflows/dist/dashboard/lazy-content.d.ts +101 -0
  90. package/clinkx-workflows/dist/dashboard/lazy-content.js +748 -0
  91. package/clinkx-workflows/dist/dashboard/lazy-content.js.map +1 -0
  92. package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +303 -0
  93. package/clinkx-workflows/dist/dashboard/progress-report-contract.js +221 -0
  94. package/clinkx-workflows/dist/dashboard/progress-report-contract.js.map +1 -0
  95. package/clinkx-workflows/dist/dashboard/progress-report-reader.d.ts +50 -0
  96. package/clinkx-workflows/dist/dashboard/progress-report-reader.js +581 -0
  97. package/clinkx-workflows/dist/dashboard/progress-report-reader.js.map +1 -0
  98. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.d.ts +89 -0
  99. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js +71 -0
  100. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js.map +1 -0
  101. package/clinkx-workflows/dist/dashboard/progress-report-status-store.d.ts +28 -0
  102. package/clinkx-workflows/dist/dashboard/progress-report-status-store.js +110 -0
  103. package/clinkx-workflows/dist/dashboard/progress-report-status-store.js.map +1 -0
  104. package/clinkx-workflows/dist/dashboard/read-model.d.ts +86 -0
  105. package/clinkx-workflows/dist/dashboard/read-model.js +440 -0
  106. package/clinkx-workflows/dist/dashboard/read-model.js.map +1 -0
  107. package/clinkx-workflows/dist/dashboard/request-admission.d.ts +31 -0
  108. package/clinkx-workflows/dist/dashboard/request-admission.js +126 -0
  109. package/clinkx-workflows/dist/dashboard/request-admission.js.map +1 -0
  110. package/clinkx-workflows/dist/dashboard/scanner.d.ts +75 -0
  111. package/clinkx-workflows/dist/dashboard/scanner.js +1110 -0
  112. package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -0
  113. package/clinkx-workflows/dist/dashboard/server-factory.d.ts +50 -0
  114. package/clinkx-workflows/dist/dashboard/server-factory.js +431 -0
  115. package/clinkx-workflows/dist/dashboard/server-factory.js.map +1 -0
  116. package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +2611 -0
  117. package/clinkx-workflows/dist/dashboard/session-log-contract.js +480 -0
  118. package/clinkx-workflows/dist/dashboard/session-log-contract.js.map +1 -0
  119. package/clinkx-workflows/dist/dashboard/session-log-identity.d.ts +21 -0
  120. package/clinkx-workflows/dist/dashboard/session-log-identity.js +101 -0
  121. package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -0
  122. package/clinkx-workflows/dist/dashboard/session-log-journal.d.ts +43 -0
  123. package/clinkx-workflows/dist/dashboard/session-log-journal.js +522 -0
  124. package/clinkx-workflows/dist/dashboard/session-log-journal.js.map +1 -0
  125. package/clinkx-workflows/dist/dashboard/session-log-reader.d.ts +61 -0
  126. package/clinkx-workflows/dist/dashboard/session-log-reader.js +378 -0
  127. package/clinkx-workflows/dist/dashboard/session-log-reader.js.map +1 -0
  128. package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +6 -0
  129. package/clinkx-workflows/dist/dashboard/session-log-redaction.js +367 -0
  130. package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -0
  131. package/clinkx-workflows/dist/dashboard/session-log-retention.d.ts +23 -0
  132. package/clinkx-workflows/dist/dashboard/session-log-retention.js +212 -0
  133. package/clinkx-workflows/dist/dashboard/session-log-retention.js.map +1 -0
  134. package/clinkx-workflows/dist/dashboard/static-responses.d.ts +10 -0
  135. package/clinkx-workflows/dist/dashboard/static-responses.js +94 -0
  136. package/clinkx-workflows/dist/dashboard/static-responses.js.map +1 -0
  137. package/clinkx-workflows/dist/dashboard/worker-lock-observer.d.ts +28 -0
  138. package/clinkx-workflows/dist/dashboard/worker-lock-observer.js +241 -0
  139. package/clinkx-workflows/dist/dashboard/worker-lock-observer.js.map +1 -0
  140. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  141. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  142. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  143. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  144. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  145. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  146. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  147. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  148. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  149. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  150. package/clinkx-workflows/dist/dashboard-ui/assets/index-DOkMXTfB.js +25 -0
  151. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  152. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  153. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  154. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  155. package/clinkx-workflows/dist/dashboard-ui/assets/style-ChJsn0KD.css +1 -0
  156. package/clinkx-workflows/dist/dashboard-ui/contracts/council-plan-review-input-v1.json +158 -0
  157. package/clinkx-workflows/dist/dashboard-ui/index.html +15 -0
  158. package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Mono-OFL.txt +93 -0
  159. package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Sans-OFL.txt +93 -0
  160. package/clinkx-workflows/dist/dashboard-ui/licenses/JetBrains-Mono-OFL.txt +93 -0
  161. package/clinkx-workflows/dist/dashboard-ui/licenses/NOTICE.md +32 -0
  162. package/clinkx-workflows/dist/dashboard-ui/theme-init.js +8 -0
  163. package/clinkx-workflows/dist/definition-normalizer.d.ts +19 -0
  164. package/clinkx-workflows/dist/definition-normalizer.js.map +1 -1
  165. package/clinkx-workflows/dist/delegate-validate.d.ts +78 -0
  166. package/clinkx-workflows/dist/delegate-validate.js +97 -0
  167. package/clinkx-workflows/dist/delegate-validate.js.map +1 -0
  168. package/clinkx-workflows/dist/engine.d.ts +141 -0
  169. package/clinkx-workflows/dist/engine.js +882 -28
  170. package/clinkx-workflows/dist/engine.js.map +1 -1
  171. package/clinkx-workflows/dist/errors.d.ts +2 -0
  172. package/clinkx-workflows/dist/errors.js.map +1 -1
  173. package/clinkx-workflows/dist/fingerprint.d.ts +1 -1
  174. package/clinkx-workflows/dist/fingerprint.js +1 -1
  175. package/clinkx-workflows/dist/index.d.ts +19 -0
  176. package/clinkx-workflows/dist/index.js +15 -0
  177. package/clinkx-workflows/dist/index.js.map +1 -1
  178. package/clinkx-workflows/dist/loader.d.ts +9 -3
  179. package/clinkx-workflows/dist/loader.js +92 -21
  180. package/clinkx-workflows/dist/loader.js.map +1 -1
  181. package/clinkx-workflows/dist/plan-review/admission.d.ts +160 -0
  182. package/clinkx-workflows/dist/plan-review/admission.js +290 -0
  183. package/clinkx-workflows/dist/plan-review/admission.js.map +1 -0
  184. package/clinkx-workflows/dist/plan-review/context-policy.d.ts +55 -0
  185. package/clinkx-workflows/dist/plan-review/context-policy.js +146 -0
  186. package/clinkx-workflows/dist/plan-review/context-policy.js.map +1 -0
  187. package/clinkx-workflows/dist/plan-review/contract.d.ts +763 -0
  188. package/clinkx-workflows/dist/plan-review/contract.js +289 -0
  189. package/clinkx-workflows/dist/plan-review/contract.js.map +1 -0
  190. package/clinkx-workflows/dist/plan-review/digest.d.ts +68 -0
  191. package/clinkx-workflows/dist/plan-review/digest.js +241 -0
  192. package/clinkx-workflows/dist/plan-review/digest.js.map +1 -0
  193. package/clinkx-workflows/dist/plan-review/discovery.d.ts +45 -0
  194. package/clinkx-workflows/dist/plan-review/discovery.js +36 -0
  195. package/clinkx-workflows/dist/plan-review/discovery.js.map +1 -0
  196. package/clinkx-workflows/dist/plan-review/execution-contract.d.ts +298 -0
  197. package/clinkx-workflows/dist/plan-review/execution-contract.js +194 -0
  198. package/clinkx-workflows/dist/plan-review/execution-contract.js.map +1 -0
  199. package/clinkx-workflows/dist/plan-review/expander.d.ts +48 -0
  200. package/clinkx-workflows/dist/plan-review/expander.js +243 -0
  201. package/clinkx-workflows/dist/plan-review/expander.js.map +1 -0
  202. package/clinkx-workflows/dist/plan-review/projector.d.ts +126 -0
  203. package/clinkx-workflows/dist/plan-review/projector.js +437 -0
  204. package/clinkx-workflows/dist/plan-review/projector.js.map +1 -0
  205. package/clinkx-workflows/dist/plan-review/publication.d.ts +95 -0
  206. package/clinkx-workflows/dist/plan-review/publication.js +182 -0
  207. package/clinkx-workflows/dist/plan-review/publication.js.map +1 -0
  208. package/clinkx-workflows/dist/plan-review/ranking-reducer.d.ts +34 -0
  209. package/clinkx-workflows/dist/plan-review/ranking-reducer.js +139 -0
  210. package/clinkx-workflows/dist/plan-review/ranking-reducer.js.map +1 -0
  211. package/clinkx-workflows/dist/plan-review/resume.d.ts +38 -0
  212. package/clinkx-workflows/dist/plan-review/resume.js +122 -0
  213. package/clinkx-workflows/dist/plan-review/resume.js.map +1 -0
  214. package/clinkx-workflows/dist/plan-review/rubric.d.ts +7 -0
  215. package/clinkx-workflows/dist/plan-review/rubric.js +24 -0
  216. package/clinkx-workflows/dist/plan-review/rubric.js.map +1 -0
  217. package/clinkx-workflows/dist/plan-review/run-state.d.ts +398 -0
  218. package/clinkx-workflows/dist/plan-review/run-state.js +183 -0
  219. package/clinkx-workflows/dist/plan-review/run-state.js.map +1 -0
  220. package/clinkx-workflows/dist/plan-review/runtime-policy.d.ts +74 -0
  221. package/clinkx-workflows/dist/plan-review/runtime-policy.js +186 -0
  222. package/clinkx-workflows/dist/plan-review/runtime-policy.js.map +1 -0
  223. package/clinkx-workflows/dist/plan-review/snapshot.d.ts +81 -0
  224. package/clinkx-workflows/dist/plan-review/snapshot.js +170 -0
  225. package/clinkx-workflows/dist/plan-review/snapshot.js.map +1 -0
  226. package/clinkx-workflows/dist/plan-review/stage-error.d.ts +19 -0
  227. package/clinkx-workflows/dist/plan-review/stage-error.js +20 -0
  228. package/clinkx-workflows/dist/plan-review/stage-error.js.map +1 -0
  229. package/clinkx-workflows/dist/post-processors/index.d.ts +20 -0
  230. package/clinkx-workflows/dist/post-processors/index.js +21 -0
  231. package/clinkx-workflows/dist/post-processors/index.js.map +1 -0
  232. package/clinkx-workflows/dist/prompt-budget.d.ts +7 -0
  233. package/clinkx-workflows/dist/prompt-budget.js +7 -1
  234. package/clinkx-workflows/dist/prompt-budget.js.map +1 -1
  235. package/clinkx-workflows/dist/reducers/index.js +2 -0
  236. package/clinkx-workflows/dist/reducers/index.js.map +1 -1
  237. package/clinkx-workflows/dist/run-lock-heartbeat.d.ts +2 -0
  238. package/clinkx-workflows/dist/run-lock-heartbeat.js +3 -0
  239. package/clinkx-workflows/dist/run-lock-heartbeat.js.map +1 -0
  240. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.d.ts +34 -0
  241. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js +74 -0
  242. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js.map +1 -0
  243. package/clinkx-workflows/dist/run-summary/cards/index.js +5 -0
  244. package/clinkx-workflows/dist/run-summary/cards/index.js.map +1 -1
  245. package/clinkx-workflows/dist/run-summary.d.ts +13 -0
  246. package/clinkx-workflows/dist/run-summary.js +29 -1
  247. package/clinkx-workflows/dist/run-summary.js.map +1 -1
  248. package/clinkx-workflows/dist/schema.d.ts +76 -21
  249. package/clinkx-workflows/dist/schema.js +10 -1
  250. package/clinkx-workflows/dist/schema.js.map +1 -1
  251. package/clinkx-workflows/dist/seed-terms.d.ts +30 -0
  252. package/clinkx-workflows/dist/seed-terms.js +176 -0
  253. package/clinkx-workflows/dist/seed-terms.js.map +1 -0
  254. package/clinkx-workflows/dist/server.d.ts +5 -2
  255. package/clinkx-workflows/dist/server.js +82 -52
  256. package/clinkx-workflows/dist/server.js.map +1 -1
  257. package/clinkx-workflows/dist/shutdown.d.ts +18 -20
  258. package/clinkx-workflows/dist/shutdown.js +43 -61
  259. package/clinkx-workflows/dist/shutdown.js.map +1 -1
  260. package/clinkx-workflows/dist/state-schema.d.ts +38 -0
  261. package/clinkx-workflows/dist/state.d.ts +170 -3
  262. package/clinkx-workflows/dist/state.js +1275 -28
  263. package/clinkx-workflows/dist/state.js.map +1 -1
  264. package/clinkx-workflows/dist/transport.d.ts +15 -0
  265. package/clinkx-workflows/dist/transport.js +19 -10
  266. package/clinkx-workflows/dist/transport.js.map +1 -1
  267. package/clinkx-workflows/dist/types.d.ts +14 -2
  268. package/clinkx-workflows/dist/validators/council.d.ts +318 -85
  269. package/clinkx-workflows/dist/validators/council.js +52 -13
  270. package/clinkx-workflows/dist/validators/council.js.map +1 -1
  271. package/clinkx-workflows/dist/validators/index.d.ts +1 -0
  272. package/clinkx-workflows/dist/validators/index.js +3 -0
  273. package/clinkx-workflows/dist/validators/index.js.map +1 -1
  274. package/clinkx-workflows/dist/validators/plan-review.d.ts +398 -0
  275. package/clinkx-workflows/dist/validators/plan-review.js +316 -0
  276. package/clinkx-workflows/dist/validators/plan-review.js.map +1 -0
  277. package/clinkx-workflows/dist/workflow-receipt.js +188 -4
  278. package/clinkx-workflows/dist/workflow-receipt.js.map +1 -1
  279. package/clinkx-workflows/dist/workflow-tools.d.ts +48 -3
  280. package/clinkx-workflows/dist/workflow-tools.js +1402 -59
  281. package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
  282. package/clinkx-workflows/dist/workspace-signature.d.ts +16 -0
  283. package/clinkx-workflows/dist/workspace-signature.js +388 -0
  284. package/clinkx-workflows/dist/workspace-signature.js.map +1 -0
  285. package/clinkx-workflows/templates/council/answer-specialist.txt +2 -1
  286. package/clinkx-workflows/templates/council/code-review-chairman.txt +13 -0
  287. package/clinkx-workflows/templates/council/code-review-cross-reviewer.txt +4 -1
  288. package/clinkx-workflows/templates/council/code-review-reviewer.txt +19 -3
  289. package/clinkx-workflows/templates/council/debug-debugger.txt +2 -1
  290. package/clinkx-workflows/templates/council/discover-analyst.txt +2 -1
  291. package/clinkx-workflows/templates/council/member.txt +2 -1
  292. package/clinkx-workflows/templates/council/plan-review-chairman.txt +68 -0
  293. package/clinkx-workflows/templates/council/plan-review-cross-reviewer.txt +51 -0
  294. package/clinkx-workflows/templates/council/plan-review-reviewer.txt +60 -0
  295. package/clinkx-workflows/templates/plan-review/chairman.txt +48 -0
  296. package/clinkx-workflows/templates/plan-review/cross-review.txt +52 -0
  297. package/clinkx-workflows/templates/plan-review/ranking.txt +16 -0
  298. package/clinkx-workflows/templates/plan-review/reviewer.txt +57 -0
  299. package/clinkx-workflows/workflows/council-answer.yaml +97 -8
  300. package/clinkx-workflows/workflows/council-code-review.yaml +103 -8
  301. package/clinkx-workflows/workflows/council-debug.yaml +97 -8
  302. package/clinkx-workflows/workflows/council-default.yaml +97 -8
  303. package/clinkx-workflows/workflows/council-discover.yaml +97 -8
  304. package/clinkx-workflows/workflows/council-plan-review.yaml +250 -0
  305. package/clinkx-workflows/workflows/plan-review-receipts.yaml +230 -0
  306. package/conf/adapters/agy.json +49 -0
  307. package/conf/adapters/claude.json +5 -0
  308. package/conf/adapters/codex-app-server.json +44 -0
  309. package/conf/adapters/codex.json +7 -2
  310. package/conf/adapters/glm.json +5 -0
  311. package/conf/adapters/grok.json +60 -0
  312. package/conf/adapters/hapi/agy.json +52 -0
  313. package/conf/adapters/hapi/claude.json +5 -0
  314. package/conf/adapters/hapi/codex.json +7 -2
  315. package/conf/adapters/hapi/glm.json +5 -0
  316. package/dist/activity-journal.d.ts +42 -0
  317. package/dist/activity-journal.js +381 -0
  318. package/dist/activity-journal.js.map +1 -0
  319. package/dist/allowed-roots.d.ts +41 -0
  320. package/dist/allowed-roots.js +125 -3
  321. package/dist/allowed-roots.js.map +1 -1
  322. package/dist/codex/activity-session-log.d.ts +14 -0
  323. package/dist/codex/activity-session-log.js +206 -0
  324. package/dist/codex/activity-session-log.js.map +1 -0
  325. package/dist/codex/agent-tools.d.ts +23 -0
  326. package/dist/codex/agent-tools.js +1270 -0
  327. package/dist/codex/agent-tools.js.map +1 -0
  328. package/dist/codex/app-server-client.d.ts +76 -0
  329. package/dist/codex/app-server-client.js +582 -0
  330. package/dist/codex/app-server-client.js.map +1 -0
  331. package/dist/codex/app-server-registry.d.ts +389 -0
  332. package/dist/codex/app-server-registry.js +1407 -0
  333. package/dist/codex/app-server-registry.js.map +1 -0
  334. package/dist/codex/client-types.d.ts +109 -0
  335. package/dist/codex/client-types.js +2 -0
  336. package/dist/codex/client-types.js.map +1 -0
  337. package/dist/codex/protocol.d.ts +5375 -0
  338. package/dist/codex/protocol.js +614 -0
  339. package/dist/codex/protocol.js.map +1 -0
  340. package/dist/codex/session.d.ts +263 -0
  341. package/dist/codex/session.js +466 -0
  342. package/dist/codex/session.js.map +1 -0
  343. package/dist/config.d.ts +185 -19
  344. package/dist/config.js +82 -16
  345. package/dist/config.js.map +1 -1
  346. package/dist/context-builder/anchors.d.ts +1 -0
  347. package/dist/context-builder/anchors.js +3 -0
  348. package/dist/context-builder/anchors.js.map +1 -1
  349. package/dist/context-builder/bundle.d.ts +35 -2
  350. package/dist/context-builder/bundle.js +342 -2417
  351. package/dist/context-builder/bundle.js.map +1 -1
  352. package/dist/context-builder/codemap.d.ts +5 -0
  353. package/dist/context-builder/codemap.js +387 -13
  354. package/dist/context-builder/codemap.js.map +1 -1
  355. package/dist/context-builder/config.d.ts +1 -0
  356. package/dist/context-builder/config.js +12 -0
  357. package/dist/context-builder/config.js.map +1 -1
  358. package/dist/context-builder/context-run-catalog.d.ts +17 -0
  359. package/dist/context-builder/context-run-catalog.js +99 -0
  360. package/dist/context-builder/context-run-catalog.js.map +1 -0
  361. package/dist/context-builder/delegate-packet.d.ts +37 -0
  362. package/dist/context-builder/delegate-packet.js +359 -67
  363. package/dist/context-builder/delegate-packet.js.map +1 -1
  364. package/dist/context-builder/delegate-validation.d.ts +10 -1
  365. package/dist/context-builder/delegate-validation.js +153 -41
  366. package/dist/context-builder/delegate-validation.js.map +1 -1
  367. package/dist/context-builder/delegate.d.ts +2 -0
  368. package/dist/context-builder/delegate.js +1 -0
  369. package/dist/context-builder/delegate.js.map +1 -1
  370. package/dist/context-builder/diagnostics.d.ts +11 -2
  371. package/dist/context-builder/diagnostics.js +39 -6
  372. package/dist/context-builder/diagnostics.js.map +1 -1
  373. package/dist/context-builder/discovery.d.ts +29 -1
  374. package/dist/context-builder/discovery.js +475 -175
  375. package/dist/context-builder/discovery.js.map +1 -1
  376. package/dist/context-builder/globs.js +10 -10
  377. package/dist/context-builder/globs.js.map +1 -1
  378. package/dist/context-builder/handler.d.ts +26 -2
  379. package/dist/context-builder/handler.js +1158 -2416
  380. package/dist/context-builder/handler.js.map +1 -1
  381. package/dist/context-builder/lifecycle-contract.d.ts +424 -0
  382. package/dist/context-builder/lifecycle-contract.js +523 -0
  383. package/dist/context-builder/lifecycle-contract.js.map +1 -0
  384. package/dist/context-builder/lifecycle-journal.d.ts +62 -0
  385. package/dist/context-builder/lifecycle-journal.js +88 -0
  386. package/dist/context-builder/lifecycle-journal.js.map +1 -0
  387. package/dist/context-builder/lifecycle-observer.d.ts +102 -0
  388. package/dist/context-builder/lifecycle-observer.js +258 -0
  389. package/dist/context-builder/lifecycle-observer.js.map +1 -0
  390. package/dist/context-builder/lifecycle-reconcile.d.ts +32 -0
  391. package/dist/context-builder/lifecycle-reconcile.js +191 -0
  392. package/dist/context-builder/lifecycle-reconcile.js.map +1 -0
  393. package/dist/context-builder/lifecycle-recovery.d.ts +2 -0
  394. package/dist/context-builder/lifecycle-recovery.js +14 -0
  395. package/dist/context-builder/lifecycle-recovery.js.map +1 -0
  396. package/dist/context-builder/path-signals.js +9 -5
  397. package/dist/context-builder/path-signals.js.map +1 -1
  398. package/dist/context-builder/progress.d.ts +3 -1
  399. package/dist/context-builder/progress.js +23 -0
  400. package/dist/context-builder/progress.js.map +1 -1
  401. package/dist/context-builder/receipt.d.ts +6 -4
  402. package/dist/context-builder/receipt.js +12 -1
  403. package/dist/context-builder/receipt.js.map +1 -1
  404. package/dist/context-builder/refine-handler.d.ts +14 -0
  405. package/dist/context-builder/refine-handler.js +790 -0
  406. package/dist/context-builder/refine-handler.js.map +1 -0
  407. package/dist/context-builder/review-coverage.d.ts +84 -0
  408. package/dist/context-builder/review-coverage.js +437 -0
  409. package/dist/context-builder/review-coverage.js.map +1 -0
  410. package/dist/context-builder/schema.d.ts +239 -1
  411. package/dist/context-builder/schema.js +112 -6
  412. package/dist/context-builder/schema.js.map +1 -1
  413. package/dist/context-builder/secrets.d.ts +28 -0
  414. package/dist/context-builder/secrets.js +1473 -44
  415. package/dist/context-builder/secrets.js.map +1 -1
  416. package/dist/context-builder/selection.d.ts +7 -3
  417. package/dist/context-builder/selection.js +200 -738
  418. package/dist/context-builder/selection.js.map +1 -1
  419. package/dist/context-builder/session-log-identity.d.ts +1 -0
  420. package/dist/context-builder/session-log-identity.js +66 -0
  421. package/dist/context-builder/session-log-identity.js.map +1 -0
  422. package/dist/context-builder/slice-range-math.d.ts +16 -0
  423. package/dist/context-builder/slice-range-math.js +137 -0
  424. package/dist/context-builder/slice-range-math.js.map +1 -0
  425. package/dist/context-builder/status.d.ts +22 -1
  426. package/dist/context-builder/status.js +59 -4
  427. package/dist/context-builder/status.js.map +1 -1
  428. package/dist/context-builder/storage.d.ts +35 -2
  429. package/dist/context-builder/storage.js +271 -14
  430. package/dist/context-builder/storage.js.map +1 -1
  431. package/dist/context-builder/token-estimate.d.ts +13 -2
  432. package/dist/context-builder/token-estimate.js +77 -4
  433. package/dist/context-builder/token-estimate.js.map +1 -1
  434. package/dist/context-builder/versions.d.ts +29 -0
  435. package/dist/context-builder/versions.js +30 -0
  436. package/dist/context-builder/versions.js.map +1 -0
  437. package/dist/context-builder/workflow-runner.d.ts +69 -0
  438. package/dist/context-builder/workflow-runner.js +1051 -0
  439. package/dist/context-builder/workflow-runner.js.map +1 -0
  440. package/dist/context-builder/write-watchdog.d.ts +14 -0
  441. package/dist/context-builder/write-watchdog.js +38 -0
  442. package/dist/context-builder/write-watchdog.js.map +1 -0
  443. package/dist/dashboard-adapter-catalog.d.ts +9 -0
  444. package/dist/dashboard-adapter-catalog.js +46 -0
  445. package/dist/dashboard-adapter-catalog.js.map +1 -0
  446. package/dist/dashboard-cli.d.ts +79 -0
  447. package/dist/dashboard-cli.js +585 -0
  448. package/dist/dashboard-cli.js.map +1 -0
  449. package/dist/dashboard-companion/client.d.ts +36 -0
  450. package/dist/dashboard-companion/client.js +210 -0
  451. package/dist/dashboard-companion/client.js.map +1 -0
  452. package/dist/dashboard-companion/config.d.ts +8 -0
  453. package/dist/dashboard-companion/config.js +52 -0
  454. package/dist/dashboard-companion/config.js.map +1 -0
  455. package/dist/dashboard-companion/http-proxy.d.ts +10 -0
  456. package/dist/dashboard-companion/http-proxy.js +234 -0
  457. package/dist/dashboard-companion/http-proxy.js.map +1 -0
  458. package/dist/dashboard-companion/protocol.d.ts +80 -0
  459. package/dist/dashboard-companion/protocol.js +455 -0
  460. package/dist/dashboard-companion/protocol.js.map +1 -0
  461. package/dist/dashboard-companion/standalone-assets.d.ts +12 -0
  462. package/dist/dashboard-companion/standalone-assets.js +72 -0
  463. package/dist/dashboard-companion/standalone-assets.js.map +1 -0
  464. package/dist/dashboard-companion/standalone-entry.d.ts +1 -0
  465. package/dist/dashboard-companion/standalone-entry.js +17 -0
  466. package/dist/dashboard-companion/standalone-entry.js.map +1 -0
  467. package/dist/dashboard-gateway/config.d.ts +9 -0
  468. package/dist/dashboard-gateway/config.js +44 -0
  469. package/dist/dashboard-gateway/config.js.map +1 -0
  470. package/dist/dashboard-gateway/index.d.ts +11 -0
  471. package/dist/dashboard-gateway/index.js +755 -0
  472. package/dist/dashboard-gateway/index.js.map +1 -0
  473. package/dist/dashboard-gateway/main.d.ts +1 -0
  474. package/dist/dashboard-gateway/main.js +25 -0
  475. package/dist/dashboard-gateway/main.js.map +1 -0
  476. package/dist/dashboard-pairing-token.d.ts +1 -0
  477. package/dist/dashboard-pairing-token.js +5 -0
  478. package/dist/dashboard-pairing-token.js.map +1 -0
  479. package/dist/elaris/client.d.ts +73 -0
  480. package/dist/elaris/client.js +825 -0
  481. package/dist/elaris/client.js.map +1 -0
  482. package/dist/elaris/config.d.ts +18 -0
  483. package/dist/elaris/config.js +75 -0
  484. package/dist/elaris/config.js.map +1 -0
  485. package/dist/elaris/errors.d.ts +41 -0
  486. package/dist/elaris/errors.js +205 -0
  487. package/dist/elaris/errors.js.map +1 -0
  488. package/dist/elaris/github-repo.d.ts +18 -0
  489. package/dist/elaris/github-repo.js +89 -0
  490. package/dist/elaris/github-repo.js.map +1 -0
  491. package/dist/elaris/index.d.ts +10 -0
  492. package/dist/elaris/index.js +11 -0
  493. package/dist/elaris/index.js.map +1 -0
  494. package/dist/elaris/redact.d.ts +89 -0
  495. package/dist/elaris/redact.js +423 -0
  496. package/dist/elaris/redact.js.map +1 -0
  497. package/dist/elaris/run-id.d.ts +5 -0
  498. package/dist/elaris/run-id.js +20 -0
  499. package/dist/elaris/run-id.js.map +1 -0
  500. package/dist/elaris/status.d.ts +45 -0
  501. package/dist/elaris/status.js +122 -0
  502. package/dist/elaris/status.js.map +1 -0
  503. package/dist/elaris/tool-schemas.d.ts +314 -0
  504. package/dist/elaris/tool-schemas.js +208 -0
  505. package/dist/elaris/tool-schemas.js.map +1 -0
  506. package/dist/elaris/tools.d.ts +73 -0
  507. package/dist/elaris/tools.js +1065 -0
  508. package/dist/elaris/tools.js.map +1 -0
  509. package/dist/elaris/types.d.ts +1541 -0
  510. package/dist/elaris/types.js +252 -0
  511. package/dist/elaris/types.js.map +1 -0
  512. package/dist/errors.d.ts +16 -0
  513. package/dist/errors.js +19 -0
  514. package/dist/errors.js.map +1 -1
  515. package/dist/handler.d.ts +4 -0
  516. package/dist/handler.js +10 -1
  517. package/dist/handler.js.map +1 -1
  518. package/dist/index.d.ts +2 -1
  519. package/dist/index.js +13 -3
  520. package/dist/index.js.map +1 -1
  521. package/dist/local-clink-backend.d.ts +3 -0
  522. package/dist/local-clink-backend.js +4 -0
  523. package/dist/local-clink-backend.js.map +1 -1
  524. package/dist/package-info.d.ts +2 -0
  525. package/dist/package-info.js +18 -0
  526. package/dist/package-info.js.map +1 -0
  527. package/dist/parsers/codex-jsonl.js +48 -3
  528. package/dist/parsers/codex-jsonl.js.map +1 -1
  529. package/dist/parsers/extract.d.ts +10 -1
  530. package/dist/parsers/extract.js +11 -3
  531. package/dist/parsers/extract.js.map +1 -1
  532. package/dist/pipeline.d.ts +4 -0
  533. package/dist/pipeline.js +129 -8
  534. package/dist/pipeline.js.map +1 -1
  535. package/dist/run-dir.d.ts +24 -0
  536. package/dist/run-dir.js +201 -2
  537. package/dist/run-dir.js.map +1 -1
  538. package/dist/runner.d.ts +13 -0
  539. package/dist/runner.js +184 -15
  540. package/dist/runner.js.map +1 -1
  541. package/dist/safety.d.ts +4 -4
  542. package/dist/safety.js +4 -4
  543. package/dist/schema.d.ts +7 -7
  544. package/dist/server.d.ts +1 -0
  545. package/dist/server.js +359 -17
  546. package/dist/server.js.map +1 -1
  547. package/dist/session-log-stream.d.ts +18 -0
  548. package/dist/session-log-stream.js +319 -0
  549. package/dist/session-log-stream.js.map +1 -0
  550. package/package.json +46 -5
  551. package/conf/adapters/gemini.json +0 -44
  552. package/conf/adapters/hapi/gemini.json +0 -47
  553. package/dist/parsers/gemini-json.d.ts +0 -2
  554. package/dist/parsers/gemini-json.js +0 -72
  555. package/dist/parsers/gemini-json.js.map +0 -1
package/dist/run-dir.js CHANGED
@@ -1,8 +1,22 @@
1
- import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
2
- import { join } from "node:path";
1
+ import { chmodSync, closeSync, lstatSync, mkdtempSync, mkdirSync, openSync, readdirSync, readSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { logger } from "./logger.js";
6
+ /** Cap on retained (idle-killed) run dirs kept under the runs base dir. */
7
+ const MAX_RETAINED_RUN_DIRS = 20;
8
+ const RETAINED_MARKER = ".retained";
9
+ const STDERR_LOG = "stderr.log";
10
+ /** Payload cap on retained stderr.log bytes (head + tail slices only); the truncation marker line adds ~50 bytes on top. */
11
+ export const RETAINED_STDERR_MAX_BYTES = 1_048_576; // 1 MiB
12
+ export const RETAINED_STDERR_HEAD_BYTES = 524_288; // 512 KiB
13
+ export const RETAINED_STDERR_TAIL_BYTES = 524_288; // 512 KiB
14
+ function isEnoent(err) {
15
+ return err?.code === "ENOENT";
16
+ }
17
+ function errMessage(err) {
18
+ return err instanceof Error ? err.message : String(err);
19
+ }
6
20
  /**
7
21
  * Create a per-run temp directory under ${TMPDIR}/clinkx/runs/<run_id>.
8
22
  *
@@ -29,4 +43,189 @@ export function createRunDir() {
29
43
  },
30
44
  };
31
45
  }
46
+ /**
47
+ * Retain an idle-killed run dir as diagnostic evidence:
48
+ * - keep ONLY stderr.log; delete everything else (PROMPT.txt must not persist
49
+ * without explicit debug_keep_artifacts opt-in, RESULT.md is absent or
50
+ * incomplete on an idle kill, and CLI-created files are unbounded)
51
+ * - cap stderr.log at RETAINED_STDERR_MAX_BYTES via a head+tail slice
52
+ * - drop a `.retained` marker so retained dirs are identifiable
53
+ * - prune the oldest retained dirs beyond MAX_RETAINED_RUN_DIRS
54
+ *
55
+ * Runs post-termination, so there are no live-fd concerns. Fails CLOSED: if
56
+ * sanitization cannot complete for any non-ENOENT reason, the whole dir is
57
+ * removed instead of retained.
58
+ *
59
+ * @returns "retained" when the sanitized dir survives on disk, "cleaned" when
60
+ * the dir is confirmed gone instead (fail-closed removal or a race that
61
+ * deleted it), "cleanup_failed" when fail-closed removal could not be
62
+ * confirmed and the dir may still exist unsanitized.
63
+ */
64
+ export function retainRunDir(path) {
65
+ try {
66
+ for (const entry of readdirSync(path, { withFileTypes: true })) {
67
+ if (entry.name === STDERR_LOG) {
68
+ // Reserved-name type check: keep stderr.log ONLY as a regular,
69
+ // non-symlink file. A directory or symlink under that name would
70
+ // bypass sanitization and the byte cap (lstat: isFile() is false
71
+ // for symlinks), so any other type falls through to deletion.
72
+ const st = lstatSync(join(path, entry.name), { throwIfNoEntry: false });
73
+ if (st?.isFile())
74
+ continue;
75
+ }
76
+ // Everything else is deleted — including a type-confused stderr.log
77
+ // and any pre-existing `.retained` entry; the marker is written fresh
78
+ // below so it is always a regular file created by us.
79
+ rmSync(join(path, entry.name), { recursive: true, force: true });
80
+ }
81
+ // Runs only on the lstat-verified regular file (or a no-op when absent)
82
+ capRetainedStderr(join(path, STDERR_LOG));
83
+ }
84
+ catch (err) {
85
+ if (isEnoent(err)) {
86
+ // Run dir raced away entirely — nothing to retain, nothing left on disk
87
+ logger.debug({ run_dir: path }, "run dir disappeared before retention");
88
+ return "cleaned";
89
+ }
90
+ return failClosed(path, "sanitize", err);
91
+ }
92
+ try {
93
+ writeFileSync(join(path, RETAINED_MARKER), "");
94
+ }
95
+ catch (err) {
96
+ if (isEnoent(err)) {
97
+ // Dir raced away — nothing retained, nothing to prune for
98
+ logger.debug({ run_dir: path }, "run dir disappeared before retention marker write");
99
+ return "cleaned";
100
+ }
101
+ // An unmarked dir would evade the pruner's accounting — fail closed too
102
+ return failClosed(path, "write_marker", err);
103
+ }
104
+ pruneRetainedRunDirs(dirname(path));
105
+ return "retained";
106
+ }
107
+ /**
108
+ * Remove the run dir instead of retaining it. Reports the verified outcome:
109
+ * "cleaned" only when the dir is confirmed gone, else "cleanup_failed".
110
+ */
111
+ function failClosed(path, operation, err) {
112
+ logger.warn({ run_dir: path, operation, err: errMessage(err) }, "retention sanitization failed — cleaning run dir instead of retaining");
113
+ let lastRmErr;
114
+ try {
115
+ rmSync(path, { recursive: true, force: true });
116
+ }
117
+ catch (rmErr) {
118
+ lastRmErr = rmErr;
119
+ // Sanitization failures are often permission damage (e.g. a read-only
120
+ // run dir) — repair the dir mode and retry removal once.
121
+ try {
122
+ chmodSync(path, 0o700);
123
+ rmSync(path, { recursive: true, force: true });
124
+ lastRmErr = undefined;
125
+ }
126
+ catch (retryErr) {
127
+ lastRmErr = retryErr;
128
+ }
129
+ }
130
+ // Verify removal with lstat, not existsSync: existsSync returns false when
131
+ // the path is merely INACCESSIBLE (e.g. parent dir mode 000 → EACCES on
132
+ // traversal), which would misreport a surviving dir as "cleaned". Only a
133
+ // definitive ENOENT confirms the dir is gone; any successful lstat or any
134
+ // other error (EACCES, EPERM, ...) means removal is unconfirmed.
135
+ try {
136
+ lstatSync(path);
137
+ }
138
+ catch (verifyErr) {
139
+ if (isEnoent(verifyErr))
140
+ return "cleaned";
141
+ lastRmErr ??= verifyErr;
142
+ }
143
+ logger.warn({ run_dir: path, err: errMessage(lastRmErr ?? "run dir still present after rmSync") }, "failed to remove run dir after retention sanitization failure");
144
+ return "cleanup_failed";
145
+ }
146
+ /**
147
+ * Rewrite an oversized stderr.log as first RETAINED_STDERR_HEAD_BYTES +
148
+ * truncation marker + last RETAINED_STDERR_TAIL_BYTES. Missing file (ENOENT)
149
+ * is fine — the child may never have written stderr. Other errors propagate
150
+ * to the fail-closed path in retainRunDir.
151
+ */
152
+ function capRetainedStderr(stderrPath) {
153
+ let size;
154
+ try {
155
+ size = statSync(stderrPath).size;
156
+ }
157
+ catch (err) {
158
+ if (isEnoent(err))
159
+ return;
160
+ throw err;
161
+ }
162
+ if (size <= RETAINED_STDERR_MAX_BYTES)
163
+ return;
164
+ const fd = openSync(stderrPath, "r");
165
+ let head;
166
+ let tail;
167
+ try {
168
+ head = readSlice(fd, 0, RETAINED_STDERR_HEAD_BYTES);
169
+ tail = readSlice(fd, size - RETAINED_STDERR_TAIL_BYTES, RETAINED_STDERR_TAIL_BYTES);
170
+ }
171
+ finally {
172
+ closeSync(fd);
173
+ }
174
+ const dropped = size - head.length - tail.length;
175
+ const marker = `\n... [clinkx: truncated ${dropped} bytes for retention] ...\n`;
176
+ writeFileSync(stderrPath, Buffer.concat([head, Buffer.from(marker), tail]));
177
+ }
178
+ function readSlice(fd, position, length) {
179
+ const buf = Buffer.alloc(length);
180
+ let offset = 0;
181
+ while (offset < length) {
182
+ const n = readSync(fd, buf, offset, length - offset, position + offset);
183
+ if (n === 0)
184
+ break;
185
+ offset += n;
186
+ }
187
+ return buf.subarray(0, offset);
188
+ }
189
+ function pruneRetainedRunDirs(base) {
190
+ let entries;
191
+ try {
192
+ entries = readdirSync(base, { withFileTypes: true });
193
+ }
194
+ catch (err) {
195
+ if (!isEnoent(err)) {
196
+ logger.debug({ base, err: errMessage(err) }, "failed to list runs base for retention pruning");
197
+ }
198
+ return;
199
+ }
200
+ const retained = [];
201
+ for (const entry of entries) {
202
+ if (!entry.isDirectory())
203
+ continue;
204
+ const dirPath = join(base, entry.name);
205
+ try {
206
+ // Marker mtime = retention time — the pruning sort key
207
+ retained.push({ path: dirPath, mtimeMs: statSync(join(dirPath, RETAINED_MARKER)).mtimeMs });
208
+ }
209
+ catch (err) {
210
+ // Not a retained dir (ENOENT), or raced away — skip
211
+ if (!isEnoent(err)) {
212
+ logger.debug({ dir: dirPath, err: errMessage(err) }, "failed to stat retention marker during pruning");
213
+ }
214
+ }
215
+ }
216
+ if (retained.length <= MAX_RETAINED_RUN_DIRS)
217
+ return;
218
+ retained.sort((a, b) => b.mtimeMs - a.mtimeMs);
219
+ for (const victim of retained.slice(MAX_RETAINED_RUN_DIRS)) {
220
+ try {
221
+ rmSync(victim.path, { recursive: true, force: true });
222
+ }
223
+ catch (err) {
224
+ // Raced with another prune (ENOENT) — fine
225
+ if (!isEnoent(err)) {
226
+ logger.debug({ dir: victim.path, err: errMessage(err) }, "failed to prune retained run dir");
227
+ }
228
+ }
229
+ }
230
+ }
32
231
  //# sourceMappingURL=run-dir.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-dir.js","sourceRoot":"","sources":["../src/run-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9C,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;IAElD,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAEvD,OAAO;QACL,IAAI;QACJ,KAAK;QACL,OAAO;YACL,IAAI,CAAC;gBACH,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,0BAA0B,CAAC,CAAC;YACtD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,kCAAkC,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"run-dir.js","sourceRoot":"","sources":["../src/run-dir.ts"],"names":[],"mappings":"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"}
package/dist/runner.d.ts CHANGED
@@ -1,7 +1,17 @@
1
1
  import { type ChildProcess } from "node:child_process";
2
2
  import type { CliAdapterConfig } from "./config.js";
3
+ import type { SessionLogOwnerV1 } from "../clinkx-workflows/dist/dashboard/session-log-contract.js";
3
4
  /** Active subprocess registry for graceful shutdown. */
4
5
  export declare const activeProcesses: Set<ChildProcess>;
6
+ /** Synchronously prevent every future generic subprocess admission. */
7
+ export declare function latchSubprocessAdmissionForShutdown(): void;
8
+ /**
9
+ * Test-only reset for the process-wide admission latch and deterministic race
10
+ * hook. Tests that call this own the cleanup and must leave no active child.
11
+ */
12
+ export declare function resetSubprocessAdmissionForTest(): void;
13
+ /** Test-only seam for forcing the post-spawn shutdown race deterministically. */
14
+ export declare function setAfterSpawnRegisteredForTest(hook: ((child: ChildProcess) => void) | undefined): void;
5
15
  /** Kill all subprocesses tracked when shutdown begins. */
6
16
  export declare function killAllActive(): Promise<void>;
7
17
  export interface RunOptions {
@@ -12,6 +22,9 @@ export interface RunOptions {
12
22
  idleTimeoutSeconds?: number | undefined;
13
23
  idleTimeoutStartupSeconds?: number | undefined;
14
24
  signal?: AbortSignal | undefined;
25
+ sessionLogOwner?: SessionLogOwnerV1 | undefined;
26
+ /** Internal storage-root override for focused tests. */
27
+ sessionLogRootDir?: string | undefined;
15
28
  }
16
29
  export interface RunResult {
17
30
  exitCode: number | null;
package/dist/runner.js CHANGED
@@ -3,44 +3,76 @@ import { openSync, closeSync, readSync, statSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { logger } from "./logger.js";
5
5
  import { ExecutionError } from "./errors.js";
6
+ import { getStateDir } from "../clinkx-workflows/dist/config.js";
6
7
  import { buildEnv, redactSecrets } from "./env.js";
7
8
  import { preparePromptDelivery } from "./prompt-mode.js";
8
9
  import { assertNoTtyRequired, resolveArgs } from "./safety.js";
10
+ import { createProviderSessionLogProducer } from "./session-log-stream.js";
9
11
  /** Active subprocess registry for graceful shutdown. */
10
12
  export const activeProcesses = new Set();
13
+ let subprocessAdmissionClosed = false;
14
+ let afterSpawnRegisteredForTest;
15
+ /** Synchronously prevent every future generic subprocess admission. */
16
+ export function latchSubprocessAdmissionForShutdown() {
17
+ subprocessAdmissionClosed = true;
18
+ }
19
+ /**
20
+ * Test-only reset for the process-wide admission latch and deterministic race
21
+ * hook. Tests that call this own the cleanup and must leave no active child.
22
+ */
23
+ export function resetSubprocessAdmissionForTest() {
24
+ subprocessAdmissionClosed = false;
25
+ afterSpawnRegisteredForTest = undefined;
26
+ }
27
+ /** Test-only seam for forcing the post-spawn shutdown race deterministically. */
28
+ export function setAfterSpawnRegisteredForTest(hook) {
29
+ afterSpawnRegisteredForTest = hook;
30
+ }
31
+ function shutdownAdmissionError() {
32
+ return new ExecutionError("Server is shutting down; subprocess admission is closed");
33
+ }
11
34
  const ROOT_SHUTDOWN_GRACE_MS = 3_000;
12
35
  /** Kill all subprocesses tracked when shutdown begins. */
13
36
  export async function killAllActive() {
14
- const snapshot = [...activeProcesses];
15
- if (snapshot.length === 0)
37
+ const deadline = Date.now() + ROOT_SHUTDOWN_GRACE_MS;
38
+ const initialSnapshot = [...activeProcesses];
39
+ if (initialSnapshot.length === 0)
16
40
  return;
17
- for (const child of snapshot) {
41
+ for (const child of initialSnapshot) {
18
42
  killProcessGroup(child, "SIGTERM");
19
43
  }
20
44
  // Wait for grace period, but resolve early if all children have exited
21
45
  await new Promise((resolve) => {
22
- const timer = setTimeout(resolve, ROOT_SHUTDOWN_GRACE_MS);
46
+ const timer = setTimeout(resolve, Math.max(0, deadline - Date.now()));
23
47
  const interval = setInterval(() => {
24
- if (snapshot.every((c) => !activeProcesses.has(c))) {
48
+ if (activeProcesses.size === 0) {
25
49
  clearTimeout(timer);
26
50
  clearInterval(interval);
27
51
  resolve();
28
52
  }
29
53
  }, 50);
30
- setTimeout(() => clearInterval(interval), ROOT_SHUTDOWN_GRACE_MS).unref();
54
+ setTimeout(() => clearInterval(interval), Math.max(0, deadline - Date.now())).unref();
31
55
  });
32
- for (const child of snapshot) {
33
- if (activeProcesses.has(child)) {
34
- killProcessGroup(child, "SIGKILL");
35
- }
56
+ // Drain the live registry, not the initial snapshot: a child registered
57
+ // during shutdown is still owned and must not survive the sweep.
58
+ // Escalate every group observed at shutdown entry as well as children still
59
+ // registered now. The direct child can exit after SIGTERM while a descendant
60
+ // forked at that boundary remains in its process group; dropping the initial
61
+ // handle here would orphan that descendant.
62
+ const escalationSnapshot = new Set([...initialSnapshot, ...activeProcesses]);
63
+ for (const child of escalationSnapshot) {
64
+ killProcessGroup(child, "SIGKILL");
36
65
  }
37
66
  }
38
67
  const DEFAULT_MAX_PROMPT_BYTES = 1_048_576; // 1 MB
39
68
  const DEFAULT_TIMEOUT_SECONDS = 600;
40
69
  const DEFAULT_MAX_CAPTURE_BYTES = 2_097_152; // 2 MB default capture retention
41
70
  const GRACE_PERIOD_MS = 5_000;
71
+ const STDERR_POLL_INTERVAL_MS = 1_000;
42
72
  const DEFAULT_IDLE_TIMEOUT_SECONDS = 0;
43
73
  const DEFAULT_IDLE_TIMEOUT_STARTUP_SECONDS = 180;
74
+ // Per-line cap for the provider session-log stream, in characters.
75
+ const DEFAULT_SESSION_LOG_MAX_LINE_CHARS = 1024 * 1024;
44
76
  function safeParseInt(val, fallback) {
45
77
  if (val == null || val === "")
46
78
  return fallback;
@@ -132,22 +164,28 @@ function killProcessGroup(child, sig) {
132
164
  */
133
165
  export function runSubprocess(options) {
134
166
  const { adapter, prompt, runDirPath, signal } = options;
167
+ if (subprocessAdmissionClosed) {
168
+ return Promise.reject(shutdownAdmissionError());
169
+ }
135
170
  const timeoutSeconds = options.timeoutSeconds ??
136
171
  adapter.timeout_seconds ??
137
172
  safeParseInt(process.env["CLINKX_TIMEOUT_SECONDS"], DEFAULT_TIMEOUT_SECONDS);
138
173
  // Idle timeout resolution: RunOptions > adapter config > env var > default
139
- // Suppress-stdout guard: when stdout is suppressed, CLI produces no stdout — disable idle
140
- const suppressesStdout = adapter.suppress_stdout === true;
141
- const idleTimeoutSeconds = suppressesStdout ? 0 :
174
+ // Suppress-stdout guard: when stdout is suppressed there is no stdout to
175
+ // observe, but stderr_to_file polling still provides a byte-activity
176
+ // channel — only zero the idle timeouts when no observable channel remains.
177
+ const noObservableChannel = adapter.suppress_stdout === true && adapter.stderr_to_file !== true;
178
+ const idleTimeoutSeconds = noObservableChannel ? 0 :
142
179
  (options.idleTimeoutSeconds ??
143
180
  adapter.idle_timeout_seconds ??
144
181
  safeParseIntNonNeg(process.env["CLINKX_IDLE_TIMEOUT_SECONDS"], DEFAULT_IDLE_TIMEOUT_SECONDS));
145
- const idleStartupSeconds = suppressesStdout ? 0 :
182
+ const idleStartupSeconds = noObservableChannel ? 0 :
146
183
  (options.idleTimeoutStartupSeconds ??
147
184
  adapter.idle_timeout_startup_seconds ??
148
185
  safeParseIntNonNeg(process.env["CLINKX_IDLE_TIMEOUT_STARTUP_SECONDS"], DEFAULT_IDLE_TIMEOUT_STARTUP_SECONDS));
149
186
  const maxPromptBytes = safeParseInt(process.env["CLINKX_MAX_PROMPT_BYTES"], DEFAULT_MAX_PROMPT_BYTES);
150
187
  const maxCaptureBytes = safeParseInt(process.env["CLINKX_MAX_CAPTURE_BYTES"] ?? process.env["CLINKX_MAX_OUTPUT_BYTES"], DEFAULT_MAX_CAPTURE_BYTES);
188
+ const sessionLogMaxLineChars = safeParseInt(process.env["CLINKX_SESSION_LOG_MAX_LINE_CHARS"], DEFAULT_SESSION_LOG_MAX_LINE_CHARS);
151
189
  // Bound input size
152
190
  const promptBuf = Buffer.from(prompt, "utf-8");
153
191
  if (promptBuf.length > maxPromptBytes) {
@@ -160,7 +198,6 @@ export function runSubprocess(options) {
160
198
  catch (err) {
161
199
  return Promise.reject(err);
162
200
  }
163
- // Build args
164
201
  const args = resolveArgs(adapter.args, adapter.unsafe_args);
165
202
  if (adapter.native_output_file_flag && adapter.result_contract.enabled) {
166
203
  args.push(adapter.native_output_file_flag, join(runDirPath, adapter.result_contract.filename));
@@ -191,6 +228,7 @@ export function runSubprocess(options) {
191
228
  let timeoutHandle;
192
229
  let graceHandle;
193
230
  let idleHandle;
231
+ let stderrPollHandle;
194
232
  let stderrFd;
195
233
  const stderrFilePath = adapter.stderr_to_file
196
234
  ? join(runDirPath, "stderr.log")
@@ -198,6 +236,14 @@ export function runSubprocess(options) {
198
236
  if (stderrFilePath != null) {
199
237
  stderrFd = openSync(stderrFilePath, "w");
200
238
  }
239
+ // Load-bearing second check: argument/env preparation can race shutdown.
240
+ // This is the final point before the OS process can be created.
241
+ if (subprocessAdmissionClosed) {
242
+ if (stderrFd != null)
243
+ closeSync(stderrFd);
244
+ reject(shutdownAdmissionError());
245
+ return;
246
+ }
201
247
  let child;
202
248
  try {
203
249
  child = spawn(adapter.command, args, {
@@ -224,6 +270,27 @@ export function runSubprocess(options) {
224
270
  stderrFd = undefined;
225
271
  }
226
272
  activeProcesses.add(child);
273
+ // Test-only synchronization point. Production has no callback here.
274
+ afterSpawnRegisteredForTest?.(child);
275
+ // Shutdown can latch in the narrow spawn->registration window. Kill the
276
+ // entire newly-created group, retain registry ownership until close/error,
277
+ // and reject the request instead of admitting work after the drain began.
278
+ if (subprocessAdmissionClosed) {
279
+ const releaseRacingChild = () => {
280
+ // Re-sweep after the direct child exits: a shell can fork a descendant
281
+ // at the same instant the first group signal selects its recipients.
282
+ killProcessGroup(child, "SIGKILL");
283
+ activeProcesses.delete(child);
284
+ };
285
+ child.once("error", releaseRacingChild);
286
+ child.once("close", releaseRacingChild);
287
+ killProcessGroup(child, "SIGKILL");
288
+ child.stdin?.destroy();
289
+ child.stdout?.destroy();
290
+ child.stderr?.destroy();
291
+ reject(shutdownAdmissionError());
292
+ return;
293
+ }
227
294
  // Shared kill guard — prevents double-SIGTERM races across wall/idle/cancel paths
228
295
  function initiateKill(reason) {
229
296
  if (killInitiated)
@@ -234,6 +301,7 @@ export function runSubprocess(options) {
234
301
  clearTimeout(timeoutHandle);
235
302
  clearTimeout(idleHandle);
236
303
  clearTimeout(graceHandle); // clear stale grace handle (killInitiated prevents re-entry)
304
+ clearInterval(stderrPollHandle);
237
305
  if (reason === "wall")
238
306
  timedOut = true;
239
307
  if (reason === "idle")
@@ -247,6 +315,41 @@ export function runSubprocess(options) {
247
315
  killProcessGroup(child, "SIGKILL");
248
316
  }, GRACE_PERIOD_MS);
249
317
  }
318
+ const stderrPollingEnabled = stderrFilePath != null && (idleStartupSeconds > 0 || idleTimeoutSeconds > 0);
319
+ let lastStderrSize = 0;
320
+ // Set when an idle-expiry stat failed once. Cleared ONLY by observed byte
321
+ // activity (any probe) or a successful stat AT expiry time — a quiet
322
+ // interval tick must not clear it, or an alternating expiry-failure /
323
+ // quiet-tick pattern would re-arm the idle timer forever. A second
324
+ // unresolved expiry failure proceeds with the kill so a permanently
325
+ // unreadable stderr file cannot make the child unkillable.
326
+ // Deferred regression coverage: deterministically proving the
327
+ // quiet-tick-must-NOT-clear semantics needs an injectable stat probe or
328
+ // fake timers; a revert of that semantics would pass the current suite.
329
+ let idleExpiryStatFailed = false;
330
+ // Shared by the poll tick and the idle-expiry last-chance check so the two
331
+ // activity detectors cannot drift. Any size change (growth or truncation)
332
+ // counts as byte activity.
333
+ function probeStderrFile() {
334
+ if (stderrFilePath == null)
335
+ return "quiet";
336
+ let size;
337
+ try {
338
+ size = statSync(stderrFilePath).size;
339
+ }
340
+ catch {
341
+ // stat failure this tick is non-fatal — try again next tick
342
+ return "stat_failed";
343
+ }
344
+ if (size !== lastStderrSize) {
345
+ lastStderrSize = size;
346
+ receivedFirstByte = true;
347
+ // Byte activity anywhere is conclusive — safe to disarm fail-open
348
+ idleExpiryStatFailed = false;
349
+ return "active";
350
+ }
351
+ return "quiet";
352
+ }
250
353
  // Idle timer: resets on any data from stdout or stderr
251
354
  function resetIdleTimer() {
252
355
  if (killInitiated)
@@ -260,6 +363,30 @@ export function runSubprocess(options) {
260
363
  idleHandle = setTimeout(() => {
261
364
  if (killInitiated)
262
365
  return;
366
+ // Last-chance re-check: with an idle timeout <= poll interval, the
367
+ // expiry can fire before the next poll tick observes fresh stderr
368
+ // bytes — never kill a child on a stale size reading.
369
+ if (stderrPollingEnabled) {
370
+ const probe = probeStderrFile();
371
+ if (probe === "active") {
372
+ resetIdleTimer();
373
+ return;
374
+ }
375
+ if (probe === "quiet") {
376
+ // A successful stat AT expiry is conclusive (genuinely no
377
+ // activity) — disarm the fail-open flag so it cannot linger and
378
+ // be consumed by a later, unrelated expiry failure.
379
+ idleExpiryStatFailed = false;
380
+ }
381
+ if (probe === "stat_failed" && !idleExpiryStatFailed) {
382
+ // A transient stat failure is inconclusive, not "no activity" —
383
+ // don't kill a possibly-active child on it. Give it exactly one
384
+ // more idle window; a second consecutive failure proceeds.
385
+ idleExpiryStatFailed = true;
386
+ resetIdleTimer();
387
+ return;
388
+ }
389
+ }
263
390
  idlePhase = receivedFirstByte ? "inter_byte" : "startup";
264
391
  idleTimeoutEffective = receivedFirstByte ? idleTimeoutSeconds : idleStartupSeconds;
265
392
  initiateKill("idle");
@@ -269,19 +396,42 @@ export function runSubprocess(options) {
269
396
  ? { capture: { head: Buffer.alloc(0), tail: Buffer.alloc(0), totalBytes: 0, truncated: false }, onData(_chunk) { } }
270
397
  : createStreamCapture(maxCaptureBytes);
271
398
  const stderrCapture = createStreamCapture(maxCaptureBytes);
399
+ let sessionLogProducer;
400
+ if (options.sessionLogOwner !== undefined &&
401
+ (adapter.parser === "codex_jsonl" || adapter.parser === "claude_stream_json")) {
402
+ try {
403
+ sessionLogProducer = createProviderSessionLogProducer({
404
+ parser: adapter.parser,
405
+ owner: options.sessionLogOwner,
406
+ rootDir: options.sessionLogRootDir ?? join(getStateDir(), "session-logs"),
407
+ prompt,
408
+ maxPartialLineChars: sessionLogMaxLineChars,
409
+ });
410
+ }
411
+ catch {
412
+ logger.debug({ producerFamily: options.sessionLogOwner.family }, "provider session-log admission failed open");
413
+ }
414
+ }
272
415
  child.stdout?.on("data", (chunk) => {
273
416
  if (chunk.length === 0)
274
417
  return;
275
418
  if (!receivedFirstByte)
276
419
  receivedFirstByte = true;
420
+ // Pipe bytes are conclusive activity — restore the one-shot
421
+ // expiry-stat-failure grace window, matching probeStderrFile()
422
+ idleExpiryStatFailed = false;
277
423
  resetIdleTimer();
278
424
  stdoutCapture.onData(chunk);
425
+ sessionLogProducer?.onStdout(chunk);
279
426
  });
280
427
  child.stderr?.on("data", (chunk) => {
281
428
  if (chunk.length === 0)
282
429
  return;
283
430
  if (!receivedFirstByte)
284
431
  receivedFirstByte = true;
432
+ // Pipe bytes are conclusive activity — restore the one-shot
433
+ // expiry-stat-failure grace window, matching probeStderrFile()
434
+ idleExpiryStatFailed = false;
285
435
  resetIdleTimer();
286
436
  stderrCapture.onData(chunk);
287
437
  });
@@ -300,6 +450,17 @@ export function runSubprocess(options) {
300
450
  }, timeoutSeconds * 1_000);
301
451
  // Arm idle timer immediately after spawn
302
452
  resetIdleTimer();
453
+ // File-backed stderr produces no 'data' events, so the idle timer would
454
+ // only see stdout — for CLIs that write stdout at the very end, that makes
455
+ // startup-idle detection kill healthy turns. Poll stderr file size growth
456
+ // as the byte-activity signal instead (size only, contents never read).
457
+ if (stderrPollingEnabled) {
458
+ stderrPollHandle = setInterval(() => {
459
+ if (probeStderrFile() === "active")
460
+ resetIdleTimer();
461
+ }, STDERR_POLL_INTERVAL_MS);
462
+ stderrPollHandle.unref();
463
+ }
303
464
  // Cancellation via AbortSignal
304
465
  const onAbort = () => {
305
466
  initiateKill("cancel");
@@ -316,16 +477,24 @@ export function runSubprocess(options) {
316
477
  clearTimeout(timeoutHandle);
317
478
  clearTimeout(idleHandle);
318
479
  clearTimeout(graceHandle);
480
+ clearInterval(stderrPollHandle);
319
481
  signal?.removeEventListener("abort", onAbort);
320
482
  activeProcesses.delete(child);
483
+ sessionLogProducer?.finish("failed");
321
484
  reject(new ExecutionError(`Failed to spawn "${adapter.command}": ${err.message}`));
322
485
  });
323
486
  child.on("close", (code, exitSignal) => {
324
487
  clearTimeout(timeoutHandle);
325
488
  clearTimeout(idleHandle);
326
489
  clearTimeout(graceHandle);
490
+ clearInterval(stderrPollHandle);
327
491
  signal?.removeEventListener("abort", onAbort);
328
492
  activeProcesses.delete(child);
493
+ sessionLogProducer?.finish(terminationReason === "cancel"
494
+ ? "cancelled"
495
+ : terminationReason === "wall" || terminationReason === "idle"
496
+ ? "timeout"
497
+ : code === 0 ? "completed" : "failed");
329
498
  // Post-mortem stderr hydration from file-backed capture
330
499
  if (stderrFilePath != null) {
331
500
  try {