clinkx 0.3.0 → 1.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 (669) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/LICENSE +10 -0
  3. package/README.md +154 -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/chairman-knobs.d.ts +24 -0
  12. package/clinkx-workflows/dist/chairman-knobs.js +27 -0
  13. package/clinkx-workflows/dist/chairman-knobs.js.map +1 -0
  14. package/clinkx-workflows/dist/citation-check.d.ts +40 -0
  15. package/clinkx-workflows/dist/citation-check.js +348 -0
  16. package/clinkx-workflows/dist/citation-check.js.map +1 -0
  17. package/clinkx-workflows/dist/clink-client.d.ts +12 -1
  18. package/clinkx-workflows/dist/clink-client.js +56 -5
  19. package/clinkx-workflows/dist/clink-client.js.map +1 -1
  20. package/clinkx-workflows/dist/config.d.ts +173 -16
  21. package/clinkx-workflows/dist/config.js +417 -26
  22. package/clinkx-workflows/dist/config.js.map +1 -1
  23. package/clinkx-workflows/dist/contained-file.d.ts +172 -0
  24. package/clinkx-workflows/dist/contained-file.js +483 -0
  25. package/clinkx-workflows/dist/contained-file.js.map +1 -0
  26. package/clinkx-workflows/dist/context-builder-lifecycle-contract.d.ts +416 -0
  27. package/clinkx-workflows/dist/context-builder-lifecycle-contract.js +328 -0
  28. package/clinkx-workflows/dist/context-builder-lifecycle-contract.js.map +1 -0
  29. package/clinkx-workflows/dist/context-builder-lifecycle-router.d.ts +24 -0
  30. package/clinkx-workflows/dist/context-builder-lifecycle-router.js +39 -0
  31. package/clinkx-workflows/dist/context-builder-lifecycle-router.js.map +1 -0
  32. package/clinkx-workflows/dist/context-cache-key.d.ts +53 -0
  33. package/clinkx-workflows/dist/context-cache-key.js +91 -0
  34. package/clinkx-workflows/dist/context-cache-key.js.map +1 -0
  35. package/clinkx-workflows/dist/context-mode-resolver.d.ts +27 -0
  36. package/clinkx-workflows/dist/context-mode-resolver.js +126 -0
  37. package/clinkx-workflows/dist/context-mode-resolver.js.map +1 -0
  38. package/clinkx-workflows/dist/context-preflight.d.ts +189 -0
  39. package/clinkx-workflows/dist/context-preflight.js +91 -0
  40. package/clinkx-workflows/dist/context-preflight.js.map +1 -0
  41. package/clinkx-workflows/dist/council-execution-policy.d.ts +68 -0
  42. package/clinkx-workflows/dist/council-execution-policy.js +110 -0
  43. package/clinkx-workflows/dist/council-execution-policy.js.map +1 -0
  44. package/clinkx-workflows/dist/council-expander.d.ts +10 -9
  45. package/clinkx-workflows/dist/council-expander.js +20 -10
  46. package/clinkx-workflows/dist/council-expander.js.map +1 -1
  47. package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +5718 -0
  48. package/clinkx-workflows/dist/dashboard/activity-contract.js +157 -0
  49. package/clinkx-workflows/dist/dashboard/activity-contract.js.map +1 -0
  50. package/clinkx-workflows/dist/dashboard/activity-lease.d.ts +25 -0
  51. package/clinkx-workflows/dist/dashboard/activity-lease.js +34 -0
  52. package/clinkx-workflows/dist/dashboard/activity-lease.js.map +1 -0
  53. package/clinkx-workflows/dist/dashboard/activity-reader.d.ts +90 -0
  54. package/clinkx-workflows/dist/dashboard/activity-reader.js +493 -0
  55. package/clinkx-workflows/dist/dashboard/activity-reader.js.map +1 -0
  56. package/clinkx-workflows/dist/dashboard/api-routes.d.ts +40 -0
  57. package/clinkx-workflows/dist/dashboard/api-routes.js +1155 -0
  58. package/clinkx-workflows/dist/dashboard/api-routes.js.map +1 -0
  59. package/clinkx-workflows/dist/dashboard/artifact-index.d.ts +37 -0
  60. package/clinkx-workflows/dist/dashboard/artifact-index.js +145 -0
  61. package/clinkx-workflows/dist/dashboard/artifact-index.js.map +1 -0
  62. package/clinkx-workflows/dist/dashboard/auth/challenge-store.d.ts +31 -0
  63. package/clinkx-workflows/dist/dashboard/auth/challenge-store.js +65 -0
  64. package/clinkx-workflows/dist/dashboard/auth/challenge-store.js.map +1 -0
  65. package/clinkx-workflows/dist/dashboard/auth/controller.d.ts +26 -0
  66. package/clinkx-workflows/dist/dashboard/auth/controller.js +596 -0
  67. package/clinkx-workflows/dist/dashboard/auth/controller.js.map +1 -0
  68. package/clinkx-workflows/dist/dashboard/auth/credential-store.d.ts +60 -0
  69. package/clinkx-workflows/dist/dashboard/auth/credential-store.js +776 -0
  70. package/clinkx-workflows/dist/dashboard/auth/credential-store.js.map +1 -0
  71. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.d.ts +9 -0
  72. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js +44 -0
  73. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js.map +1 -0
  74. package/clinkx-workflows/dist/dashboard/auth/session-store.d.ts +17 -0
  75. package/clinkx-workflows/dist/dashboard/auth/session-store.js +77 -0
  76. package/clinkx-workflows/dist/dashboard/auth/session-store.js.map +1 -0
  77. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.d.ts +25 -0
  78. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js +71 -0
  79. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js.map +1 -0
  80. package/clinkx-workflows/dist/dashboard/context-builder-invocation-contract.d.ts +3865 -0
  81. package/clinkx-workflows/dist/dashboard/context-builder-invocation-contract.js +69 -0
  82. package/clinkx-workflows/dist/dashboard/context-builder-invocation-contract.js.map +1 -0
  83. package/clinkx-workflows/dist/dashboard/context-run-liveness.d.ts +37 -0
  84. package/clinkx-workflows/dist/dashboard/context-run-liveness.js +49 -0
  85. package/clinkx-workflows/dist/dashboard/context-run-liveness.js.map +1 -0
  86. package/clinkx-workflows/dist/dashboard/context-run-reader.d.ts +256 -0
  87. package/clinkx-workflows/dist/dashboard/context-run-reader.js +1301 -0
  88. package/clinkx-workflows/dist/dashboard/context-run-reader.js.map +1 -0
  89. package/clinkx-workflows/dist/dashboard/contracts.d.ts +28483 -0
  90. package/clinkx-workflows/dist/dashboard/contracts.js +1284 -0
  91. package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -0
  92. package/clinkx-workflows/dist/dashboard/generation.d.ts +14 -0
  93. package/clinkx-workflows/dist/dashboard/generation.js +41 -0
  94. package/clinkx-workflows/dist/dashboard/generation.js.map +1 -0
  95. package/clinkx-workflows/dist/dashboard/index.d.ts +32 -0
  96. package/clinkx-workflows/dist/dashboard/index.js +20 -0
  97. package/clinkx-workflows/dist/dashboard/index.js.map +1 -0
  98. package/clinkx-workflows/dist/dashboard/invocation-contract.d.ts +786 -0
  99. package/clinkx-workflows/dist/dashboard/invocation-contract.js +201 -0
  100. package/clinkx-workflows/dist/dashboard/invocation-contract.js.map +1 -0
  101. package/clinkx-workflows/dist/dashboard/lazy-content.d.ts +114 -0
  102. package/clinkx-workflows/dist/dashboard/lazy-content.js +799 -0
  103. package/clinkx-workflows/dist/dashboard/lazy-content.js.map +1 -0
  104. package/clinkx-workflows/dist/dashboard/native-transcript/contract.d.ts +90 -0
  105. package/clinkx-workflows/dist/dashboard/native-transcript/contract.js +31 -0
  106. package/clinkx-workflows/dist/dashboard/native-transcript/contract.js.map +1 -0
  107. package/clinkx-workflows/dist/dashboard/native-transcript/projection.d.ts +12 -0
  108. package/clinkx-workflows/dist/dashboard/native-transcript/projection.js +286 -0
  109. package/clinkx-workflows/dist/dashboard/native-transcript/projection.js.map +1 -0
  110. package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +446 -0
  111. package/clinkx-workflows/dist/dashboard/progress-report-contract.js +221 -0
  112. package/clinkx-workflows/dist/dashboard/progress-report-contract.js.map +1 -0
  113. package/clinkx-workflows/dist/dashboard/progress-report-reader.d.ts +50 -0
  114. package/clinkx-workflows/dist/dashboard/progress-report-reader.js +529 -0
  115. package/clinkx-workflows/dist/dashboard/progress-report-reader.js.map +1 -0
  116. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.d.ts +89 -0
  117. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js +71 -0
  118. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js.map +1 -0
  119. package/clinkx-workflows/dist/dashboard/progress-report-status-store.d.ts +29 -0
  120. package/clinkx-workflows/dist/dashboard/progress-report-status-store.js +111 -0
  121. package/clinkx-workflows/dist/dashboard/progress-report-status-store.js.map +1 -0
  122. package/clinkx-workflows/dist/dashboard/prompt-builder-library-contract.d.ts +194 -0
  123. package/clinkx-workflows/dist/dashboard/prompt-builder-library-contract.js +89 -0
  124. package/clinkx-workflows/dist/dashboard/prompt-builder-library-contract.js.map +1 -0
  125. package/clinkx-workflows/dist/dashboard/prompt-builder-library-store.d.ts +26 -0
  126. package/clinkx-workflows/dist/dashboard/prompt-builder-library-store.js +102 -0
  127. package/clinkx-workflows/dist/dashboard/prompt-builder-library-store.js.map +1 -0
  128. package/clinkx-workflows/dist/dashboard/read-model.d.ts +86 -0
  129. package/clinkx-workflows/dist/dashboard/read-model.js +440 -0
  130. package/clinkx-workflows/dist/dashboard/read-model.js.map +1 -0
  131. package/clinkx-workflows/dist/dashboard/request-admission.d.ts +31 -0
  132. package/clinkx-workflows/dist/dashboard/request-admission.js +130 -0
  133. package/clinkx-workflows/dist/dashboard/request-admission.js.map +1 -0
  134. package/clinkx-workflows/dist/dashboard/scanner.d.ts +78 -0
  135. package/clinkx-workflows/dist/dashboard/scanner.js +1154 -0
  136. package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -0
  137. package/clinkx-workflows/dist/dashboard/server-factory.d.ts +63 -0
  138. package/clinkx-workflows/dist/dashboard/server-factory.js +483 -0
  139. package/clinkx-workflows/dist/dashboard/server-factory.js.map +1 -0
  140. package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +2778 -0
  141. package/clinkx-workflows/dist/dashboard/session-log-contract.js +506 -0
  142. package/clinkx-workflows/dist/dashboard/session-log-contract.js.map +1 -0
  143. package/clinkx-workflows/dist/dashboard/session-log-identity.d.ts +21 -0
  144. package/clinkx-workflows/dist/dashboard/session-log-identity.js +101 -0
  145. package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -0
  146. package/clinkx-workflows/dist/dashboard/session-log-journal.d.ts +43 -0
  147. package/clinkx-workflows/dist/dashboard/session-log-journal.js +529 -0
  148. package/clinkx-workflows/dist/dashboard/session-log-journal.js.map +1 -0
  149. package/clinkx-workflows/dist/dashboard/session-log-reader.d.ts +61 -0
  150. package/clinkx-workflows/dist/dashboard/session-log-reader.js +378 -0
  151. package/clinkx-workflows/dist/dashboard/session-log-reader.js.map +1 -0
  152. package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +14 -0
  153. package/clinkx-workflows/dist/dashboard/session-log-redaction.js +1101 -0
  154. package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -0
  155. package/clinkx-workflows/dist/dashboard/session-log-retention.d.ts +23 -0
  156. package/clinkx-workflows/dist/dashboard/session-log-retention.js +212 -0
  157. package/clinkx-workflows/dist/dashboard/session-log-retention.js.map +1 -0
  158. package/clinkx-workflows/dist/dashboard/static-responses.d.ts +10 -0
  159. package/clinkx-workflows/dist/dashboard/static-responses.js +94 -0
  160. package/clinkx-workflows/dist/dashboard/static-responses.js.map +1 -0
  161. package/clinkx-workflows/dist/dashboard/worker-lock-observer.d.ts +28 -0
  162. package/clinkx-workflows/dist/dashboard/worker-lock-observer.js +241 -0
  163. package/clinkx-workflows/dist/dashboard/worker-lock-observer.js.map +1 -0
  164. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  165. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  166. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  167. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  168. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  169. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  170. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  171. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  172. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  173. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  174. package/clinkx-workflows/dist/dashboard-ui/assets/index-JPHQVtn9.js +106 -0
  175. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  176. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  177. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  178. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  179. package/clinkx-workflows/dist/dashboard-ui/assets/style-DOMHk-j6.css +1 -0
  180. package/clinkx-workflows/dist/dashboard-ui/contracts/council-plan-review-input-v1.json +158 -0
  181. package/clinkx-workflows/dist/dashboard-ui/index.html +15 -0
  182. package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Mono-OFL.txt +93 -0
  183. package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Sans-OFL.txt +93 -0
  184. package/clinkx-workflows/dist/dashboard-ui/licenses/JetBrains-Mono-OFL.txt +93 -0
  185. package/clinkx-workflows/dist/dashboard-ui/licenses/NOTICE.md +32 -0
  186. package/clinkx-workflows/dist/dashboard-ui/theme-init.js +8 -0
  187. package/clinkx-workflows/dist/definition-normalizer.d.ts +19 -0
  188. package/clinkx-workflows/dist/definition-normalizer.js.map +1 -1
  189. package/clinkx-workflows/dist/delegate-finalizer-policy.d.ts +26 -0
  190. package/clinkx-workflows/dist/delegate-finalizer-policy.js +72 -0
  191. package/clinkx-workflows/dist/delegate-finalizer-policy.js.map +1 -0
  192. package/clinkx-workflows/dist/delegate-signature.d.ts +14 -0
  193. package/clinkx-workflows/dist/delegate-signature.js +14 -0
  194. package/clinkx-workflows/dist/delegate-signature.js.map +1 -0
  195. package/clinkx-workflows/dist/delegate-validate.d.ts +108 -0
  196. package/clinkx-workflows/dist/delegate-validate.js +148 -0
  197. package/clinkx-workflows/dist/delegate-validate.js.map +1 -0
  198. package/clinkx-workflows/dist/engine.d.ts +251 -0
  199. package/clinkx-workflows/dist/engine.js +1342 -92
  200. package/clinkx-workflows/dist/engine.js.map +1 -1
  201. package/clinkx-workflows/dist/errors.d.ts +24 -2
  202. package/clinkx-workflows/dist/errors.js +30 -2
  203. package/clinkx-workflows/dist/errors.js.map +1 -1
  204. package/clinkx-workflows/dist/fidelity.d.ts +47 -1
  205. package/clinkx-workflows/dist/fidelity.js +54 -1
  206. package/clinkx-workflows/dist/fidelity.js.map +1 -1
  207. package/clinkx-workflows/dist/fingerprint.d.ts +25 -7
  208. package/clinkx-workflows/dist/fingerprint.js +33 -10
  209. package/clinkx-workflows/dist/fingerprint.js.map +1 -1
  210. package/clinkx-workflows/dist/index.d.ts +19 -0
  211. package/clinkx-workflows/dist/index.js +68 -1
  212. package/clinkx-workflows/dist/index.js.map +1 -1
  213. package/clinkx-workflows/dist/loader.d.ts +9 -3
  214. package/clinkx-workflows/dist/loader.js +92 -21
  215. package/clinkx-workflows/dist/loader.js.map +1 -1
  216. package/clinkx-workflows/dist/logger.d.ts +1 -1
  217. package/clinkx-workflows/dist/logger.js +17 -3
  218. package/clinkx-workflows/dist/logger.js.map +1 -1
  219. package/clinkx-workflows/dist/monotonic-clock.d.ts +19 -0
  220. package/clinkx-workflows/dist/monotonic-clock.js +22 -0
  221. package/clinkx-workflows/dist/monotonic-clock.js.map +1 -0
  222. package/clinkx-workflows/dist/plan-review/admission.d.ts +160 -0
  223. package/clinkx-workflows/dist/plan-review/admission.js +290 -0
  224. package/clinkx-workflows/dist/plan-review/admission.js.map +1 -0
  225. package/clinkx-workflows/dist/plan-review/context-policy.d.ts +55 -0
  226. package/clinkx-workflows/dist/plan-review/context-policy.js +146 -0
  227. package/clinkx-workflows/dist/plan-review/context-policy.js.map +1 -0
  228. package/clinkx-workflows/dist/plan-review/contract.d.ts +763 -0
  229. package/clinkx-workflows/dist/plan-review/contract.js +289 -0
  230. package/clinkx-workflows/dist/plan-review/contract.js.map +1 -0
  231. package/clinkx-workflows/dist/plan-review/digest.d.ts +68 -0
  232. package/clinkx-workflows/dist/plan-review/digest.js +241 -0
  233. package/clinkx-workflows/dist/plan-review/digest.js.map +1 -0
  234. package/clinkx-workflows/dist/plan-review/discovery.d.ts +45 -0
  235. package/clinkx-workflows/dist/plan-review/discovery.js +36 -0
  236. package/clinkx-workflows/dist/plan-review/discovery.js.map +1 -0
  237. package/clinkx-workflows/dist/plan-review/execution-contract.d.ts +298 -0
  238. package/clinkx-workflows/dist/plan-review/execution-contract.js +194 -0
  239. package/clinkx-workflows/dist/plan-review/execution-contract.js.map +1 -0
  240. package/clinkx-workflows/dist/plan-review/expander.d.ts +48 -0
  241. package/clinkx-workflows/dist/plan-review/expander.js +243 -0
  242. package/clinkx-workflows/dist/plan-review/expander.js.map +1 -0
  243. package/clinkx-workflows/dist/plan-review/projector.d.ts +126 -0
  244. package/clinkx-workflows/dist/plan-review/projector.js +437 -0
  245. package/clinkx-workflows/dist/plan-review/projector.js.map +1 -0
  246. package/clinkx-workflows/dist/plan-review/publication.d.ts +95 -0
  247. package/clinkx-workflows/dist/plan-review/publication.js +182 -0
  248. package/clinkx-workflows/dist/plan-review/publication.js.map +1 -0
  249. package/clinkx-workflows/dist/plan-review/ranking-reducer.d.ts +34 -0
  250. package/clinkx-workflows/dist/plan-review/ranking-reducer.js +139 -0
  251. package/clinkx-workflows/dist/plan-review/ranking-reducer.js.map +1 -0
  252. package/clinkx-workflows/dist/plan-review/resume.d.ts +38 -0
  253. package/clinkx-workflows/dist/plan-review/resume.js +122 -0
  254. package/clinkx-workflows/dist/plan-review/resume.js.map +1 -0
  255. package/clinkx-workflows/dist/plan-review/rubric.d.ts +7 -0
  256. package/clinkx-workflows/dist/plan-review/rubric.js +24 -0
  257. package/clinkx-workflows/dist/plan-review/rubric.js.map +1 -0
  258. package/clinkx-workflows/dist/plan-review/run-state.d.ts +398 -0
  259. package/clinkx-workflows/dist/plan-review/run-state.js +183 -0
  260. package/clinkx-workflows/dist/plan-review/run-state.js.map +1 -0
  261. package/clinkx-workflows/dist/plan-review/runtime-policy.d.ts +74 -0
  262. package/clinkx-workflows/dist/plan-review/runtime-policy.js +186 -0
  263. package/clinkx-workflows/dist/plan-review/runtime-policy.js.map +1 -0
  264. package/clinkx-workflows/dist/plan-review/snapshot.d.ts +81 -0
  265. package/clinkx-workflows/dist/plan-review/snapshot.js +170 -0
  266. package/clinkx-workflows/dist/plan-review/snapshot.js.map +1 -0
  267. package/clinkx-workflows/dist/plan-review/stage-error.d.ts +19 -0
  268. package/clinkx-workflows/dist/plan-review/stage-error.js +20 -0
  269. package/clinkx-workflows/dist/plan-review/stage-error.js.map +1 -0
  270. package/clinkx-workflows/dist/post-processors/index.d.ts +20 -0
  271. package/clinkx-workflows/dist/post-processors/index.js +21 -0
  272. package/clinkx-workflows/dist/post-processors/index.js.map +1 -0
  273. package/clinkx-workflows/dist/prompt-budget.d.ts +7 -0
  274. package/clinkx-workflows/dist/prompt-budget.js +7 -1
  275. package/clinkx-workflows/dist/prompt-budget.js.map +1 -1
  276. package/clinkx-workflows/dist/reducers/index.js +2 -0
  277. package/clinkx-workflows/dist/reducers/index.js.map +1 -1
  278. package/clinkx-workflows/dist/roster-contract.d.ts +889 -0
  279. package/clinkx-workflows/dist/roster-contract.js +180 -0
  280. package/clinkx-workflows/dist/roster-contract.js.map +1 -0
  281. package/clinkx-workflows/dist/rosters.d.ts +14 -0
  282. package/clinkx-workflows/dist/rosters.js +169 -0
  283. package/clinkx-workflows/dist/rosters.js.map +1 -0
  284. package/clinkx-workflows/dist/run-health.d.ts +7 -0
  285. package/clinkx-workflows/dist/run-health.js +25 -0
  286. package/clinkx-workflows/dist/run-health.js.map +1 -0
  287. package/clinkx-workflows/dist/run-lock-heartbeat.d.ts +2 -0
  288. package/clinkx-workflows/dist/run-lock-heartbeat.js +3 -0
  289. package/clinkx-workflows/dist/run-lock-heartbeat.js.map +1 -0
  290. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.d.ts +34 -0
  291. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js +74 -0
  292. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js.map +1 -0
  293. package/clinkx-workflows/dist/run-summary/cards/index.js +5 -0
  294. package/clinkx-workflows/dist/run-summary/cards/index.js.map +1 -1
  295. package/clinkx-workflows/dist/run-summary.d.ts +13 -0
  296. package/clinkx-workflows/dist/run-summary.js +29 -1
  297. package/clinkx-workflows/dist/run-summary.js.map +1 -1
  298. package/clinkx-workflows/dist/schema.d.ts +140 -21
  299. package/clinkx-workflows/dist/schema.js +34 -1
  300. package/clinkx-workflows/dist/schema.js.map +1 -1
  301. package/clinkx-workflows/dist/seed-terms.d.ts +30 -0
  302. package/clinkx-workflows/dist/seed-terms.js +176 -0
  303. package/clinkx-workflows/dist/seed-terms.js.map +1 -0
  304. package/clinkx-workflows/dist/server.d.ts +5 -2
  305. package/clinkx-workflows/dist/server.js +84 -52
  306. package/clinkx-workflows/dist/server.js.map +1 -1
  307. package/clinkx-workflows/dist/shutdown.d.ts +29 -18
  308. package/clinkx-workflows/dist/shutdown.js +90 -64
  309. package/clinkx-workflows/dist/shutdown.js.map +1 -1
  310. package/clinkx-workflows/dist/state-schema.d.ts +61 -0
  311. package/clinkx-workflows/dist/state.d.ts +175 -3
  312. package/clinkx-workflows/dist/state.js +1398 -44
  313. package/clinkx-workflows/dist/state.js.map +1 -1
  314. package/clinkx-workflows/dist/stdio-watchdog.d.ts +52 -0
  315. package/clinkx-workflows/dist/stdio-watchdog.js +212 -0
  316. package/clinkx-workflows/dist/stdio-watchdog.js.map +1 -0
  317. package/clinkx-workflows/dist/transport.d.ts +15 -0
  318. package/clinkx-workflows/dist/transport.js +19 -10
  319. package/clinkx-workflows/dist/transport.js.map +1 -1
  320. package/clinkx-workflows/dist/types.d.ts +20 -2
  321. package/clinkx-workflows/dist/validators/council.d.ts +334 -101
  322. package/clinkx-workflows/dist/validators/council.js +52 -13
  323. package/clinkx-workflows/dist/validators/council.js.map +1 -1
  324. package/clinkx-workflows/dist/validators/index.d.ts +1 -0
  325. package/clinkx-workflows/dist/validators/index.js +3 -0
  326. package/clinkx-workflows/dist/validators/index.js.map +1 -1
  327. package/clinkx-workflows/dist/validators/plan-review.d.ts +398 -0
  328. package/clinkx-workflows/dist/validators/plan-review.js +316 -0
  329. package/clinkx-workflows/dist/validators/plan-review.js.map +1 -0
  330. package/clinkx-workflows/dist/workflow-receipt.js +188 -4
  331. package/clinkx-workflows/dist/workflow-receipt.js.map +1 -1
  332. package/clinkx-workflows/dist/workflow-tools.d.ts +118 -3
  333. package/clinkx-workflows/dist/workflow-tools.js +2110 -175
  334. package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
  335. package/clinkx-workflows/dist/workspace-signature.d.ts +16 -0
  336. package/clinkx-workflows/dist/workspace-signature.js +388 -0
  337. package/clinkx-workflows/dist/workspace-signature.js.map +1 -0
  338. package/clinkx-workflows/templates/council/answer-chairman.txt +3 -0
  339. package/clinkx-workflows/templates/council/answer-cross-reviewer.txt +3 -0
  340. package/clinkx-workflows/templates/council/answer-specialist.txt +5 -1
  341. package/clinkx-workflows/templates/council/chairman.txt +3 -0
  342. package/clinkx-workflows/templates/council/code-review-chairman.txt +16 -0
  343. package/clinkx-workflows/templates/council/code-review-cross-reviewer.txt +7 -1
  344. package/clinkx-workflows/templates/council/code-review-reviewer.txt +22 -3
  345. package/clinkx-workflows/templates/council/debug-chairman.txt +3 -0
  346. package/clinkx-workflows/templates/council/debug-cross-reviewer.txt +3 -0
  347. package/clinkx-workflows/templates/council/debug-debugger.txt +5 -1
  348. package/clinkx-workflows/templates/council/discover-analyst.txt +5 -1
  349. package/clinkx-workflows/templates/council/discover-chairman.txt +3 -0
  350. package/clinkx-workflows/templates/council/discover-cross-reviewer.txt +3 -0
  351. package/clinkx-workflows/templates/council/member.txt +5 -1
  352. package/clinkx-workflows/templates/council/plan-review-chairman.txt +71 -0
  353. package/clinkx-workflows/templates/council/plan-review-cross-reviewer.txt +54 -0
  354. package/clinkx-workflows/templates/council/plan-review-reviewer.txt +63 -0
  355. package/clinkx-workflows/templates/council/reviewer.txt +3 -0
  356. package/clinkx-workflows/templates/plan-review/chairman.txt +48 -0
  357. package/clinkx-workflows/templates/plan-review/cross-review.txt +52 -0
  358. package/clinkx-workflows/templates/plan-review/ranking.txt +16 -0
  359. package/clinkx-workflows/templates/plan-review/reviewer.txt +57 -0
  360. package/clinkx-workflows/workflows/council-answer.yaml +100 -8
  361. package/clinkx-workflows/workflows/council-code-review.yaml +106 -8
  362. package/clinkx-workflows/workflows/council-debug.yaml +100 -8
  363. package/clinkx-workflows/workflows/council-default.yaml +104 -8
  364. package/clinkx-workflows/workflows/council-discover.yaml +100 -8
  365. package/clinkx-workflows/workflows/council-plan-review.yaml +253 -0
  366. package/clinkx-workflows/workflows/plan-review-receipts.yaml +230 -0
  367. package/conf/adapters/agy.json +56 -0
  368. package/conf/adapters/claude.json +12 -0
  369. package/conf/adapters/codex-app-server.json +44 -0
  370. package/conf/adapters/codex.json +14 -2
  371. package/conf/adapters/glm.json +24 -3
  372. package/conf/adapters/grok.json +67 -0
  373. package/conf/adapters/hapi/agy.json +59 -0
  374. package/conf/adapters/hapi/claude.json +12 -0
  375. package/conf/adapters/hapi/codex.json +14 -2
  376. package/conf/adapters/hapi/glm.json +25 -4
  377. package/conf/rosters/default-council.json +21 -0
  378. package/dist/activity-journal.d.ts +56 -0
  379. package/dist/activity-journal.js +706 -0
  380. package/dist/activity-journal.js.map +1 -0
  381. package/dist/allowed-roots.d.ts +41 -0
  382. package/dist/allowed-roots.js +125 -3
  383. package/dist/allowed-roots.js.map +1 -1
  384. package/dist/codex/activity-session-log.d.ts +14 -0
  385. package/dist/codex/activity-session-log.js +206 -0
  386. package/dist/codex/activity-session-log.js.map +1 -0
  387. package/dist/codex/agent-tools.d.ts +23 -0
  388. package/dist/codex/agent-tools.js +1306 -0
  389. package/dist/codex/agent-tools.js.map +1 -0
  390. package/dist/codex/app-server-client.d.ts +79 -0
  391. package/dist/codex/app-server-client.js +593 -0
  392. package/dist/codex/app-server-client.js.map +1 -0
  393. package/dist/codex/app-server-registry.d.ts +407 -0
  394. package/dist/codex/app-server-registry.js +1493 -0
  395. package/dist/codex/app-server-registry.js.map +1 -0
  396. package/dist/codex/client-types.d.ts +115 -0
  397. package/dist/codex/client-types.js +2 -0
  398. package/dist/codex/client-types.js.map +1 -0
  399. package/dist/codex/protocol.d.ts +5375 -0
  400. package/dist/codex/protocol.js +614 -0
  401. package/dist/codex/protocol.js.map +1 -0
  402. package/dist/codex/session.d.ts +263 -0
  403. package/dist/codex/session.js +466 -0
  404. package/dist/codex/session.js.map +1 -0
  405. package/dist/config.d.ts +386 -23
  406. package/dist/config.js +184 -15
  407. package/dist/config.js.map +1 -1
  408. package/dist/context-builder/anchors.d.ts +1 -0
  409. package/dist/context-builder/anchors.js +3 -0
  410. package/dist/context-builder/anchors.js.map +1 -1
  411. package/dist/context-builder/bundle.d.ts +48 -2
  412. package/dist/context-builder/bundle.js +388 -2417
  413. package/dist/context-builder/bundle.js.map +1 -1
  414. package/dist/context-builder/codemap.d.ts +5 -0
  415. package/dist/context-builder/codemap.js +387 -13
  416. package/dist/context-builder/codemap.js.map +1 -1
  417. package/dist/context-builder/config.d.ts +1 -0
  418. package/dist/context-builder/config.js +12 -0
  419. package/dist/context-builder/config.js.map +1 -1
  420. package/dist/context-builder/context-run-catalog.d.ts +17 -0
  421. package/dist/context-builder/context-run-catalog.js +99 -0
  422. package/dist/context-builder/context-run-catalog.js.map +1 -0
  423. package/dist/context-builder/delegate-finalizer-effective.d.ts +33 -0
  424. package/dist/context-builder/delegate-finalizer-effective.js +82 -0
  425. package/dist/context-builder/delegate-finalizer-effective.js.map +1 -0
  426. package/dist/context-builder/delegate-packet.d.ts +55 -0
  427. package/dist/context-builder/delegate-packet.js +430 -74
  428. package/dist/context-builder/delegate-packet.js.map +1 -1
  429. package/dist/context-builder/delegate-reconstruction.d.ts +29 -0
  430. package/dist/context-builder/delegate-reconstruction.js +110 -41
  431. package/dist/context-builder/delegate-reconstruction.js.map +1 -1
  432. package/dist/context-builder/delegate-validation.d.ts +41 -1
  433. package/dist/context-builder/delegate-validation.js +237 -48
  434. package/dist/context-builder/delegate-validation.js.map +1 -1
  435. package/dist/context-builder/delegate.d.ts +11 -0
  436. package/dist/context-builder/delegate.js +210 -58
  437. package/dist/context-builder/delegate.js.map +1 -1
  438. package/dist/context-builder/diagnostics.d.ts +11 -2
  439. package/dist/context-builder/diagnostics.js +39 -6
  440. package/dist/context-builder/diagnostics.js.map +1 -1
  441. package/dist/context-builder/discovery-progress.d.ts +25 -0
  442. package/dist/context-builder/discovery-progress.js +64 -0
  443. package/dist/context-builder/discovery-progress.js.map +1 -0
  444. package/dist/context-builder/discovery-watchdog.d.ts +15 -0
  445. package/dist/context-builder/discovery-watchdog.js +39 -0
  446. package/dist/context-builder/discovery-watchdog.js.map +1 -0
  447. package/dist/context-builder/discovery.d.ts +36 -1
  448. package/dist/context-builder/discovery.js +584 -172
  449. package/dist/context-builder/discovery.js.map +1 -1
  450. package/dist/context-builder/external-gaps.d.ts +73 -0
  451. package/dist/context-builder/external-gaps.js +248 -0
  452. package/dist/context-builder/external-gaps.js.map +1 -0
  453. package/dist/context-builder/globs.d.ts +1 -0
  454. package/dist/context-builder/globs.js +25 -10
  455. package/dist/context-builder/globs.js.map +1 -1
  456. package/dist/context-builder/handler.d.ts +67 -2
  457. package/dist/context-builder/handler.js +1810 -2440
  458. package/dist/context-builder/handler.js.map +1 -1
  459. package/dist/context-builder/invocation-sidecar.d.ts +41 -0
  460. package/dist/context-builder/invocation-sidecar.js +189 -0
  461. package/dist/context-builder/invocation-sidecar.js.map +1 -0
  462. package/dist/context-builder/lifecycle-contract.d.ts +424 -0
  463. package/dist/context-builder/lifecycle-contract.js +523 -0
  464. package/dist/context-builder/lifecycle-contract.js.map +1 -0
  465. package/dist/context-builder/lifecycle-journal.d.ts +62 -0
  466. package/dist/context-builder/lifecycle-journal.js +88 -0
  467. package/dist/context-builder/lifecycle-journal.js.map +1 -0
  468. package/dist/context-builder/lifecycle-observer.d.ts +102 -0
  469. package/dist/context-builder/lifecycle-observer.js +258 -0
  470. package/dist/context-builder/lifecycle-observer.js.map +1 -0
  471. package/dist/context-builder/lifecycle-reconcile.d.ts +32 -0
  472. package/dist/context-builder/lifecycle-reconcile.js +191 -0
  473. package/dist/context-builder/lifecycle-reconcile.js.map +1 -0
  474. package/dist/context-builder/lifecycle-recovery.d.ts +2 -0
  475. package/dist/context-builder/lifecycle-recovery.js +14 -0
  476. package/dist/context-builder/lifecycle-recovery.js.map +1 -0
  477. package/dist/context-builder/mutation-lock.d.ts +32 -0
  478. package/dist/context-builder/mutation-lock.js +321 -0
  479. package/dist/context-builder/mutation-lock.js.map +1 -0
  480. package/dist/context-builder/path-signals.js +9 -5
  481. package/dist/context-builder/path-signals.js.map +1 -1
  482. package/dist/context-builder/progress.d.ts +3 -1
  483. package/dist/context-builder/progress.js +23 -0
  484. package/dist/context-builder/progress.js.map +1 -1
  485. package/dist/context-builder/receipt.d.ts +6 -4
  486. package/dist/context-builder/receipt.js +12 -1
  487. package/dist/context-builder/receipt.js.map +1 -1
  488. package/dist/context-builder/refine-handler.d.ts +14 -0
  489. package/dist/context-builder/refine-handler.js +790 -0
  490. package/dist/context-builder/refine-handler.js.map +1 -0
  491. package/dist/context-builder/review-coverage.d.ts +116 -0
  492. package/dist/context-builder/review-coverage.js +544 -0
  493. package/dist/context-builder/review-coverage.js.map +1 -0
  494. package/dist/context-builder/schema.d.ts +315 -2
  495. package/dist/context-builder/schema.js +133 -7
  496. package/dist/context-builder/schema.js.map +1 -1
  497. package/dist/context-builder/secrets.d.ts +59 -0
  498. package/dist/context-builder/secrets.js +2255 -44
  499. package/dist/context-builder/secrets.js.map +1 -1
  500. package/dist/context-builder/selection.d.ts +7 -3
  501. package/dist/context-builder/selection.js +200 -738
  502. package/dist/context-builder/selection.js.map +1 -1
  503. package/dist/context-builder/session-log-identity.d.ts +1 -0
  504. package/dist/context-builder/session-log-identity.js +66 -0
  505. package/dist/context-builder/session-log-identity.js.map +1 -0
  506. package/dist/context-builder/slice-range-math.d.ts +16 -0
  507. package/dist/context-builder/slice-range-math.js +137 -0
  508. package/dist/context-builder/slice-range-math.js.map +1 -0
  509. package/dist/context-builder/status.d.ts +23 -2
  510. package/dist/context-builder/status.js +60 -4
  511. package/dist/context-builder/status.js.map +1 -1
  512. package/dist/context-builder/storage.d.ts +59 -2
  513. package/dist/context-builder/storage.js +500 -168
  514. package/dist/context-builder/storage.js.map +1 -1
  515. package/dist/context-builder/token-estimate.d.ts +13 -2
  516. package/dist/context-builder/token-estimate.js +77 -4
  517. package/dist/context-builder/token-estimate.js.map +1 -1
  518. package/dist/context-builder/versions.d.ts +44 -0
  519. package/dist/context-builder/versions.js +45 -0
  520. package/dist/context-builder/versions.js.map +1 -0
  521. package/dist/context-builder/workflow-runner.d.ts +75 -0
  522. package/dist/context-builder/workflow-runner.js +1241 -0
  523. package/dist/context-builder/workflow-runner.js.map +1 -0
  524. package/dist/context-builder/write-watchdog.d.ts +14 -0
  525. package/dist/context-builder/write-watchdog.js +38 -0
  526. package/dist/context-builder/write-watchdog.js.map +1 -0
  527. package/dist/dashboard-adapter-catalog.d.ts +9 -0
  528. package/dist/dashboard-adapter-catalog.js +53 -0
  529. package/dist/dashboard-adapter-catalog.js.map +1 -0
  530. package/dist/dashboard-cli.d.ts +81 -0
  531. package/dist/dashboard-cli.js +623 -0
  532. package/dist/dashboard-cli.js.map +1 -0
  533. package/dist/dashboard-companion/client.d.ts +37 -0
  534. package/dist/dashboard-companion/client.js +229 -0
  535. package/dist/dashboard-companion/client.js.map +1 -0
  536. package/dist/dashboard-companion/config.d.ts +8 -0
  537. package/dist/dashboard-companion/config.js +52 -0
  538. package/dist/dashboard-companion/config.js.map +1 -0
  539. package/dist/dashboard-companion/http-proxy.d.ts +10 -0
  540. package/dist/dashboard-companion/http-proxy.js +234 -0
  541. package/dist/dashboard-companion/http-proxy.js.map +1 -0
  542. package/dist/dashboard-companion/protocol.d.ts +80 -0
  543. package/dist/dashboard-companion/protocol.js +458 -0
  544. package/dist/dashboard-companion/protocol.js.map +1 -0
  545. package/dist/dashboard-companion/standalone-assets.d.ts +12 -0
  546. package/dist/dashboard-companion/standalone-assets.js +72 -0
  547. package/dist/dashboard-companion/standalone-assets.js.map +1 -0
  548. package/dist/dashboard-companion/standalone-entry.d.ts +1 -0
  549. package/dist/dashboard-companion/standalone-entry.js +17 -0
  550. package/dist/dashboard-companion/standalone-entry.js.map +1 -0
  551. package/dist/dashboard-gateway/config.d.ts +9 -0
  552. package/dist/dashboard-gateway/config.js +44 -0
  553. package/dist/dashboard-gateway/config.js.map +1 -0
  554. package/dist/dashboard-gateway/index.d.ts +12 -0
  555. package/dist/dashboard-gateway/index.js +786 -0
  556. package/dist/dashboard-gateway/index.js.map +1 -0
  557. package/dist/dashboard-gateway/main.d.ts +1 -0
  558. package/dist/dashboard-gateway/main.js +25 -0
  559. package/dist/dashboard-gateway/main.js.map +1 -0
  560. package/dist/dashboard-pairing-token.d.ts +1 -0
  561. package/dist/dashboard-pairing-token.js +5 -0
  562. package/dist/dashboard-pairing-token.js.map +1 -0
  563. package/dist/elaris/client.d.ts +73 -0
  564. package/dist/elaris/client.js +825 -0
  565. package/dist/elaris/client.js.map +1 -0
  566. package/dist/elaris/config.d.ts +18 -0
  567. package/dist/elaris/config.js +75 -0
  568. package/dist/elaris/config.js.map +1 -0
  569. package/dist/elaris/errors.d.ts +41 -0
  570. package/dist/elaris/errors.js +205 -0
  571. package/dist/elaris/errors.js.map +1 -0
  572. package/dist/elaris/github-repo.d.ts +18 -0
  573. package/dist/elaris/github-repo.js +89 -0
  574. package/dist/elaris/github-repo.js.map +1 -0
  575. package/dist/elaris/index.d.ts +10 -0
  576. package/dist/elaris/index.js +11 -0
  577. package/dist/elaris/index.js.map +1 -0
  578. package/dist/elaris/redact.d.ts +89 -0
  579. package/dist/elaris/redact.js +423 -0
  580. package/dist/elaris/redact.js.map +1 -0
  581. package/dist/elaris/run-id.d.ts +5 -0
  582. package/dist/elaris/run-id.js +20 -0
  583. package/dist/elaris/run-id.js.map +1 -0
  584. package/dist/elaris/status.d.ts +45 -0
  585. package/dist/elaris/status.js +122 -0
  586. package/dist/elaris/status.js.map +1 -0
  587. package/dist/elaris/tool-schemas.d.ts +314 -0
  588. package/dist/elaris/tool-schemas.js +208 -0
  589. package/dist/elaris/tool-schemas.js.map +1 -0
  590. package/dist/elaris/tools.d.ts +73 -0
  591. package/dist/elaris/tools.js +1065 -0
  592. package/dist/elaris/tools.js.map +1 -0
  593. package/dist/elaris/types.d.ts +1541 -0
  594. package/dist/elaris/types.js +252 -0
  595. package/dist/elaris/types.js.map +1 -0
  596. package/dist/errors.d.ts +16 -0
  597. package/dist/errors.js +19 -0
  598. package/dist/errors.js.map +1 -1
  599. package/dist/handler.d.ts +4 -0
  600. package/dist/handler.js +15 -2
  601. package/dist/handler.js.map +1 -1
  602. package/dist/index.d.ts +20 -2
  603. package/dist/index.js +154 -6
  604. package/dist/index.js.map +1 -1
  605. package/dist/local-clink-backend.d.ts +3 -0
  606. package/dist/local-clink-backend.js +4 -0
  607. package/dist/local-clink-backend.js.map +1 -1
  608. package/dist/logger.d.ts +1 -1
  609. package/dist/logger.js +18 -3
  610. package/dist/logger.js.map +1 -1
  611. package/dist/package-info.d.ts +2 -0
  612. package/dist/package-info.js +18 -0
  613. package/dist/package-info.js.map +1 -0
  614. package/dist/parsers/agy-stream-json.d.ts +14 -0
  615. package/dist/parsers/agy-stream-json.js +172 -0
  616. package/dist/parsers/agy-stream-json.js.map +1 -0
  617. package/dist/parsers/codex-jsonl.js +48 -3
  618. package/dist/parsers/codex-jsonl.js.map +1 -1
  619. package/dist/parsers/extract.d.ts +10 -1
  620. package/dist/parsers/extract.js +16 -4
  621. package/dist/parsers/extract.js.map +1 -1
  622. package/dist/parsers/observed-invocation.d.ts +20 -0
  623. package/dist/parsers/observed-invocation.js +42 -0
  624. package/dist/parsers/observed-invocation.js.map +1 -0
  625. package/dist/pipeline.d.ts +17 -0
  626. package/dist/pipeline.js +181 -10
  627. package/dist/pipeline.js.map +1 -1
  628. package/dist/progress-report/lock.d.ts +74 -0
  629. package/dist/progress-report/lock.js +343 -0
  630. package/dist/progress-report/lock.js.map +1 -0
  631. package/dist/progress-report/schema.d.ts +808 -0
  632. package/dist/progress-report/schema.js +119 -0
  633. package/dist/progress-report/schema.js.map +1 -0
  634. package/dist/progress-report/store.d.ts +66 -0
  635. package/dist/progress-report/store.js +766 -0
  636. package/dist/progress-report/store.js.map +1 -0
  637. package/dist/progress-report/tools.d.ts +20 -0
  638. package/dist/progress-report/tools.js +141 -0
  639. package/dist/progress-report/tools.js.map +1 -0
  640. package/dist/prompt-mode.d.ts +1 -1
  641. package/dist/prompt-mode.js +7 -2
  642. package/dist/prompt-mode.js.map +1 -1
  643. package/dist/run-dir.d.ts +24 -0
  644. package/dist/run-dir.js +201 -2
  645. package/dist/run-dir.js.map +1 -1
  646. package/dist/runner.d.ts +20 -0
  647. package/dist/runner.js +192 -16
  648. package/dist/runner.js.map +1 -1
  649. package/dist/safety.d.ts +54 -4
  650. package/dist/safety.js +186 -5
  651. package/dist/safety.js.map +1 -1
  652. package/dist/schema.d.ts +15 -7
  653. package/dist/schema.js +20 -0
  654. package/dist/schema.js.map +1 -1
  655. package/dist/server.d.ts +7 -1
  656. package/dist/server.js +451 -23
  657. package/dist/server.js.map +1 -1
  658. package/dist/session-log-stream.d.ts +18 -0
  659. package/dist/session-log-stream.js +566 -0
  660. package/dist/session-log-stream.js.map +1 -0
  661. package/dist/stdio-activity.d.ts +28 -0
  662. package/dist/stdio-activity.js +72 -0
  663. package/dist/stdio-activity.js.map +1 -0
  664. package/package.json +47 -5
  665. package/conf/adapters/gemini.json +0 -44
  666. package/conf/adapters/hapi/gemini.json +0 -47
  667. package/dist/parsers/gemini-json.d.ts +0 -2
  668. package/dist/parsers/gemini-json.js +0 -72
  669. package/dist/parsers/gemini-json.js.map +0 -1
@@ -0,0 +1,1493 @@
1
+ import { ExecutionError, InvalidParamsError } from "../errors.js";
2
+ import { Semaphore } from "../concurrency.js";
3
+ import { logger } from "../logger.js";
4
+ import { CodexAppServerClient as RealCodexAppServerClient, sandboxPolicyFromMode, } from "./app-server-client.js";
5
+ import { CODEX_EVENT_AGENT_MESSAGE_DELTA, CODEX_EVENT_ITEM_COMPLETED, CODEX_EVENT_ITEM_PLAN_DELTA, CODEX_EVENT_ITEM_STARTED, CODEX_EVENT_THREAD_TOKEN_USAGE_UPDATED, CODEX_EVENT_TURN_COMPLETED, CODEX_EVENT_TURN_DIFF_UPDATED, CODEX_EVENT_TURN_PLAN_UPDATED, CODEX_EVENT_TURN_STARTED, CODEX_REQUEST_COMMAND_EXECUTION_APPROVAL, CODEX_REQUEST_FILE_CHANGE_APPROVAL, CODEX_REQUEST_PERMISSIONS_APPROVAL, } from "./protocol.js";
6
+ import { SessionRecord, } from "./session.js";
7
+ import { buildObservedInvocationV1 } from "../../clinkx-workflows/dist/dashboard/invocation-contract.js";
8
+ /** Typed sentinel for a withBudget timeout so callers can gate on the type,
9
+ * not a fragile message substring (review fix). */
10
+ class BudgetExceededError extends ExecutionError {
11
+ constructor(message = "app-server RPC budget exceeded") {
12
+ super(message);
13
+ this.name = "BudgetExceededError";
14
+ }
15
+ }
16
+ const DEFAULTS = {
17
+ maxActiveTurns: 5,
18
+ maxRetainedSessions: 20,
19
+ sessionTtlSeconds: 1800,
20
+ eventBuffer: 500,
21
+ turnTimeoutSeconds: 600,
22
+ approvalTimeoutSeconds: 120,
23
+ maxAgentDepth: 2,
24
+ maxResponseChars: 20_000,
25
+ maxEventTextChars: 2_000,
26
+ interruptBudgetSeconds: 10,
27
+ terminalWaitBudgetSeconds: 10,
28
+ startBudgetSeconds: 60,
29
+ };
30
+ function safeParseInt(val, fallback) {
31
+ if (val == null || val === "")
32
+ return fallback;
33
+ const n = Number(val);
34
+ return Number.isFinite(n) && n > 0 ? n : fallback;
35
+ }
36
+ // Allow 0 (disables a timeout) but reject negatives / non-numbers.
37
+ function safeParseNonNegInt(val, fallback) {
38
+ if (val == null || val === "")
39
+ return fallback;
40
+ const n = Number(val);
41
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
42
+ }
43
+ let appServerActivityObserver;
44
+ let appServerAdmissionClosed = false;
45
+ /** Internal root-entrypoint activity sink; library callers leave it unset. */
46
+ export function setAppServerActivityObserver(observer) {
47
+ appServerActivityObserver = observer;
48
+ }
49
+ /**
50
+ * Synchronously close process-wide app-server admission before root cleanup
51
+ * starts. This also latches the currently live registry, if any; the global
52
+ * bit is retained when no registry exists so a later get cannot create one
53
+ * during shutdown.
54
+ */
55
+ export function latchAppServerAdmissionForShutdown(reason = "shutdown") {
56
+ appServerAdmissionClosed = true;
57
+ appServerRegistrySingleton?.beginShutdown(reason);
58
+ }
59
+ function notifyAppServerActivity() {
60
+ try {
61
+ appServerActivityObserver?.();
62
+ }
63
+ catch (error) {
64
+ logger.warn({ err: error instanceof Error ? error.message : String(error) }, "app-server activity observer failed");
65
+ }
66
+ }
67
+ function createDeferred() {
68
+ let resolve;
69
+ const promise = new Promise((res) => {
70
+ resolve = res;
71
+ });
72
+ return { promise, resolve };
73
+ }
74
+ function extractThreadId(result) {
75
+ // The result schemas are .passthrough() (additive vendor fields), whose index
76
+ // signature defeats union narrowing — read the id fields via a typed view.
77
+ const r = result;
78
+ if (typeof r.thread?.id === "string")
79
+ return r.thread.id;
80
+ if (typeof r.threadId === "string")
81
+ return r.threadId;
82
+ throw new ExecutionError("Codex app-server returned a thread result without a thread id.");
83
+ }
84
+ function extractTurnId(result) {
85
+ const r = result;
86
+ if (typeof r.turn?.id === "string")
87
+ return r.turn.id;
88
+ if (typeof r.turnId === "string")
89
+ return r.turnId;
90
+ throw new ExecutionError("Codex app-server returned a turn result without a turn id.");
91
+ }
92
+ function approvalTypeFromMethod(method) {
93
+ switch (method) {
94
+ case CODEX_REQUEST_COMMAND_EXECUTION_APPROVAL:
95
+ return "commandExecution";
96
+ case CODEX_REQUEST_FILE_CHANGE_APPROVAL:
97
+ return "fileChange";
98
+ case CODEX_REQUEST_PERMISSIONS_APPROVAL:
99
+ return "permissions";
100
+ default:
101
+ return null;
102
+ }
103
+ }
104
+ function registryHealthFromClient(activeLeases, retainedSessionCount, clientHealth) {
105
+ return {
106
+ activeLeases,
107
+ retainedSessionCount,
108
+ appServerProtocol: {
109
+ acceptedVersion: clientHealth.acceptedVersion,
110
+ minVersion: clientHealth.minVersion,
111
+ maxVersion: clientHealth.maxVersion,
112
+ strictVersion: clientHealth.strictVersion,
113
+ },
114
+ childRespawnCount: clientHealth.childRespawnCount,
115
+ recentFailures: {
116
+ count: clientHealth.recentFailures.length,
117
+ samples: [...clientHealth.recentFailures],
118
+ },
119
+ };
120
+ }
121
+ function pendingApprovalFromEvent(event, type, requestedAtMs) {
122
+ const base = {
123
+ id: event.id,
124
+ type,
125
+ requestedAtMs,
126
+ };
127
+ switch (event.method) {
128
+ case CODEX_REQUEST_COMMAND_EXECUTION_APPROVAL: {
129
+ const params = event.params;
130
+ return {
131
+ ...base,
132
+ reason: params.reason ?? null,
133
+ ...(hasOwn(params, "command") ? { command: params.command ?? null } : {}),
134
+ ...(hasOwn(params, "cwd") ? { cwd: params.cwd ?? null } : {}),
135
+ ...(hasOwn(params, "networkApprovalContext")
136
+ ? { networkApprovalContext: params.networkApprovalContext ?? null }
137
+ : {}),
138
+ };
139
+ }
140
+ case CODEX_REQUEST_FILE_CHANGE_APPROVAL: {
141
+ const params = event.params;
142
+ return {
143
+ ...base,
144
+ reason: params.reason ?? null,
145
+ ...(hasOwn(params, "grantRoot")
146
+ ? { grantRoot: params.grantRoot ?? null }
147
+ : {}),
148
+ };
149
+ }
150
+ case CODEX_REQUEST_PERMISSIONS_APPROVAL: {
151
+ const params = event.params;
152
+ return {
153
+ ...base,
154
+ reason: params.reason ?? null,
155
+ cwd: params.cwd,
156
+ };
157
+ }
158
+ default:
159
+ return {
160
+ ...base,
161
+ reason: null,
162
+ };
163
+ }
164
+ }
165
+ function hasOwn(value, key) {
166
+ return Object.prototype.hasOwnProperty.call(value, key);
167
+ }
168
+ function asRecord(value) {
169
+ return typeof value === "object" && value !== null && !Array.isArray(value)
170
+ ? value
171
+ : null;
172
+ }
173
+ function stringField(value, key) {
174
+ return typeof value[key] === "string" ? value[key] : null;
175
+ }
176
+ function joinedStringField(value, key) {
177
+ const field = value[key];
178
+ if (typeof field === "string")
179
+ return field;
180
+ if (!Array.isArray(field))
181
+ return null;
182
+ const strings = field.filter((entry) => typeof entry === "string");
183
+ return strings.length > 0 ? strings.join("\n") : null;
184
+ }
185
+ function turnInputText(input) {
186
+ const parts = [];
187
+ for (const entry of input) {
188
+ const item = asRecord(entry);
189
+ if (item != null &&
190
+ item["type"] === "text" &&
191
+ typeof item["text"] === "string") {
192
+ parts.push(item["text"]);
193
+ }
194
+ }
195
+ return parts.length > 0 ? parts.join("\n") : null;
196
+ }
197
+ function toolIdentity(item) {
198
+ const type = stringField(item, "type");
199
+ switch (type) {
200
+ case "commandExecution":
201
+ return {
202
+ toolName: type,
203
+ summary: stringField(item, "command") ?? type,
204
+ };
205
+ case "fileChange": {
206
+ const count = Array.isArray(item["changes"]) ? item["changes"].length : 0;
207
+ return {
208
+ toolName: type,
209
+ summary: count > 0
210
+ ? `${count} file change${count === 1 ? "" : "s"}`
211
+ : type,
212
+ };
213
+ }
214
+ case "mcpToolCall": {
215
+ const server = stringField(item, "server");
216
+ const tool = stringField(item, "tool");
217
+ const toolName = server != null && tool != null ? `${server}/${tool}` : tool ?? type;
218
+ return { toolName, summary: toolName };
219
+ }
220
+ case "dynamicToolCall": {
221
+ const namespace = stringField(item, "namespace");
222
+ const tool = stringField(item, "tool");
223
+ const toolName = namespace != null && tool != null
224
+ ? `${namespace}/${tool}`
225
+ : tool ?? type;
226
+ return { toolName, summary: toolName };
227
+ }
228
+ case "collabAgentToolCall": {
229
+ const toolName = stringField(item, "tool") ?? type;
230
+ return { toolName, summary: stringField(item, "prompt") ?? toolName };
231
+ }
232
+ case "webSearch":
233
+ return { toolName: type, summary: stringField(item, "query") ?? type };
234
+ case "imageView":
235
+ return { toolName: type, summary: stringField(item, "path") ?? type };
236
+ default:
237
+ return null;
238
+ }
239
+ }
240
+ function toolOutcome(item) {
241
+ const status = stringField(item, "status");
242
+ if (status === "failed")
243
+ return "failed";
244
+ if (status === "declined" ||
245
+ status === "cancelled" ||
246
+ status === "interrupted") {
247
+ return "cancelled";
248
+ }
249
+ const exitCode = item["exitCode"];
250
+ if (typeof exitCode === "number" && exitCode !== 0)
251
+ return "failed";
252
+ return "succeeded";
253
+ }
254
+ function itemObservation(itemValue, phase) {
255
+ const item = asRecord(itemValue);
256
+ if (item == null)
257
+ return null;
258
+ const itemId = stringField(item, "id");
259
+ const type = stringField(item, "type");
260
+ if (itemId == null || type == null)
261
+ return null;
262
+ if (phase === "completed" && type === "agentMessage") {
263
+ const content = stringField(item, "text");
264
+ if (content == null)
265
+ return null;
266
+ return {
267
+ kind: "assistant",
268
+ itemId,
269
+ content,
270
+ phase: stringField(item, "phase"),
271
+ };
272
+ }
273
+ if (phase === "completed" && type === "reasoning") {
274
+ const summary = joinedStringField(item, "summary");
275
+ if (summary == null)
276
+ return null;
277
+ return {
278
+ kind: "reasoning",
279
+ itemId,
280
+ summary,
281
+ content: joinedStringField(item, "content"),
282
+ };
283
+ }
284
+ const tool = toolIdentity(item);
285
+ if (tool == null)
286
+ return null;
287
+ if (phase === "started") {
288
+ return {
289
+ kind: "tool_call",
290
+ toolName: tool.toolName,
291
+ callId: itemId,
292
+ summary: tool.summary,
293
+ };
294
+ }
295
+ const resultSummary = stringField(item, "aggregatedOutput") ??
296
+ stringField(asRecord(item["error"]) ?? {}, "message") ??
297
+ tool.summary;
298
+ return {
299
+ kind: "tool_result",
300
+ toolName: tool.toolName,
301
+ callId: itemId,
302
+ outcome: toolOutcome(item),
303
+ summary: resultSummary,
304
+ };
305
+ }
306
+ function approvalAction(approval) {
307
+ return (approval.command ?? approval.grantRoot ?? approval.reason ?? approval.type);
308
+ }
309
+ function optionalTokenCount(value) {
310
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
311
+ }
312
+ function mapTurnStatus(status) {
313
+ switch (status) {
314
+ case "completed":
315
+ return "completed";
316
+ case "interrupted":
317
+ case "cancelled":
318
+ return "interrupted";
319
+ case "failed":
320
+ return "failed";
321
+ default:
322
+ return "failed";
323
+ }
324
+ }
325
+ export function resolveRegistryConfig(override) {
326
+ const env = process.env;
327
+ const base = {
328
+ maxActiveTurns: safeParseInt(env["CLINKX_MAX_ACTIVE_TURNS"], DEFAULTS.maxActiveTurns),
329
+ maxRetainedSessions: safeParseInt(env["CLINKX_MAX_RETAINED_SESSIONS"], DEFAULTS.maxRetainedSessions),
330
+ sessionTtlMs: safeParseNonNegInt(env["CLINKX_SESSION_TTL_SECONDS"], DEFAULTS.sessionTtlSeconds) * 1000,
331
+ eventBuffer: safeParseInt(env["CLINKX_SESSION_EVENT_BUFFER"], DEFAULTS.eventBuffer),
332
+ turnTimeoutMs: safeParseNonNegInt(env["CLINKX_TURN_TIMEOUT_SECONDS"], DEFAULTS.turnTimeoutSeconds) * 1000,
333
+ approvalTimeoutMs: safeParseNonNegInt(env["CLINKX_APPROVAL_TIMEOUT_SECONDS"], DEFAULTS.approvalTimeoutSeconds) * 1000,
334
+ maxAgentDepth: safeParseInt(env["CLINKX_MAX_AGENT_DEPTH"], DEFAULTS.maxAgentDepth),
335
+ currentDepth: safeParseNonNegInt(env["CLINKX_AGENT_DEPTH"], 0),
336
+ maxResponseChars: safeParseInt(env["CLINKX_MAX_RESPONSE_CHARS"], DEFAULTS.maxResponseChars),
337
+ maxEventTextChars: safeParseInt(env["CLINKX_SESSION_EVENT_TEXT_MAX"], DEFAULTS.maxEventTextChars),
338
+ interruptBudgetMs: safeParseInt(env["CLINKX_TURN_INTERRUPT_BUDGET_SECONDS"], DEFAULTS.interruptBudgetSeconds) * 1000,
339
+ terminalWaitBudgetMs: safeParseInt(env["CLINKX_TERMINAL_WAIT_BUDGET_SECONDS"], DEFAULTS.terminalWaitBudgetSeconds) * 1000,
340
+ startBudgetMs: safeParseInt(env["CLINKX_APP_SERVER_START_BUDGET_SECONDS"], DEFAULTS.startBudgetSeconds) * 1000,
341
+ };
342
+ return { ...base, ...override };
343
+ }
344
+ /**
345
+ * Env marker to ADD to the spawned app-server child so the recursion guard
346
+ * survives the env sanitizer (2-6). Single source for both the registry's
347
+ * {@link AppServerRegistry.childEnvMarker} and the default-singleton spawn.
348
+ */
349
+ function childEnvMarkerFor(currentDepth) {
350
+ return { CLINKX_AGENT_DEPTH: String(currentDepth + 1) };
351
+ }
352
+ const defaultSetTimer = (cb, ms) => {
353
+ const handle = setTimeout(cb, ms);
354
+ // Don't keep the STDIO server alive on dangling turn timers.
355
+ handle.unref?.();
356
+ return handle;
357
+ };
358
+ const defaultClearTimer = (handle) => {
359
+ clearTimeout(handle);
360
+ };
361
+ const DEFAULT_APP_SERVER_SPAWN_CONFIG = {
362
+ command: "codex",
363
+ args: ["app-server"],
364
+ };
365
+ /**
366
+ * Registry & lifecycle for long-lived Codex app-server sessions.
367
+ *
368
+ * CORE responsibilities (Phase 2): the active-turn lease (the SOLE concurrency
369
+ * gate — the request {@link Semaphore} is never acquired here), session record
370
+ * accounting, the bounded event ring, retained-cap + TTL reaping, turn and
371
+ * pending-approval timeouts with the shared interrupt → terminal-wait →
372
+ * process-group-kill + respawn escalation path, crash semantics, the recursion
373
+ * depth guard, and a standalone {@link shutdown}.
374
+ *
375
+ * Respawn ownership belongs to the client: the registry only calls
376
+ * {@link CodexAppServerClient.ensureReady} and the client's guarded
377
+ * {@link CodexAppServerClient.killProcessGroup}.
378
+ */
379
+ export class AppServerRegistry {
380
+ client;
381
+ observer;
382
+ clock;
383
+ setTimer;
384
+ clearTimer;
385
+ config;
386
+ lease;
387
+ sessions = new Map();
388
+ /**
389
+ * Thread ids with an in-flight resume reservation. Held synchronously across
390
+ * startSession's awaited RPCs so two concurrent resumes of the same thread
391
+ * cannot both pass the in-flight check before either becomes visible as a
392
+ * running session (review fix A — closes the deferred-insertion race).
393
+ */
394
+ reservedResumeThreads = new Set();
395
+ unsubscribeProcessDied;
396
+ shuttingDown = false;
397
+ shutdownReason = "shutdown";
398
+ constructor(deps) {
399
+ this.client = deps.client;
400
+ this.observer = deps.observer;
401
+ this.clock = deps.clock ?? { now: () => Date.now() };
402
+ this.setTimer = deps.setTimer ?? defaultSetTimer;
403
+ this.clearTimer = deps.clearTimer ?? defaultClearTimer;
404
+ this.config = resolveRegistryConfig(deps.config);
405
+ this.lease = new Semaphore(this.config.maxActiveTurns);
406
+ this.unsubscribeProcessDied = this.client.onProcessDied((event) => this.handleProcessDied(event));
407
+ }
408
+ now() {
409
+ return this.clock.now();
410
+ }
411
+ get activeLeases() {
412
+ return this.lease.active;
413
+ }
414
+ get sessionCount() {
415
+ return this.sessions.size;
416
+ }
417
+ /**
418
+ * Synchronously stop new work on this registry. The asynchronous
419
+ * {@link shutdown} drain remains separate so callers can latch admission
420
+ * before any cleanup await begins.
421
+ */
422
+ beginShutdown(reason = "shutdown") {
423
+ if (this.shuttingDown)
424
+ return;
425
+ this.shuttingDown = true;
426
+ this.shutdownReason = reason;
427
+ this.client.beginShutdown(reason);
428
+ }
429
+ assertAdmissionOpen(action = "start an app-server session") {
430
+ if (this.shuttingDown || appServerAdmissionClosed) {
431
+ throw new InvalidParamsError(`Cannot ${action} after shutdown has started (${this.shutdownReason}).`);
432
+ }
433
+ }
434
+ health() {
435
+ return registryHealthFromClient(this.activeLeases, this.sessionCount, this.client.getHealth());
436
+ }
437
+ /**
438
+ * Env marker to ADD to the spawned app-server child so the recursion guard
439
+ * survives the env sanitizer (2-6). The client owns actual env injection
440
+ * into the AppServerSpawnConfig; the registry only supplies the marker.
441
+ */
442
+ childEnvMarker() {
443
+ return childEnvMarkerFor(this.config.currentDepth);
444
+ }
445
+ require(sessionId) {
446
+ const runtime = this.sessions.get(sessionId);
447
+ if (runtime == null) {
448
+ throw new InvalidParamsError(`Unknown session: ${sessionId}`);
449
+ }
450
+ return runtime;
451
+ }
452
+ /**
453
+ * Start a new (or resumed) session and its first turn. Returns once the turn
454
+ * is RUNNING (lease held); the turn proceeds asynchronously and the lease is
455
+ * released only on provable terminality — held even if the MCP call detaches
456
+ * (2-3). Use {@link waitForTurn} to await completion without consuming a lease.
457
+ */
458
+ async startSession(opts) {
459
+ this.assertAdmissionOpen();
460
+ // Opportunistic reap before admitting new work.
461
+ this.reapIdle();
462
+ // Recursion guard (2-6): refuse beyond the depth ceiling.
463
+ if (this.config.currentDepth >= this.config.maxAgentDepth) {
464
+ throw new ExecutionError(`Agent recursion depth limit reached (${this.config.currentDepth}/${this.config.maxAgentDepth}). ` +
465
+ "Refusing to start a nested Codex app-server session.");
466
+ }
467
+ if (this.sessions.has(opts.sessionId)) {
468
+ throw new InvalidParamsError(`Session already exists: ${opts.sessionId}`);
469
+ }
470
+ // Resume reservation (review fix A): claim the resumed thread SYNCHRONOUSLY
471
+ // — before any await — so two concurrent resumes of the same thread cannot
472
+ // both pass this check during startSession's awaited RPC sequence. The
473
+ // authoritative guard lives here, not in the caller's preflight.
474
+ const resumeThreadId = opts.resumeThreadId;
475
+ if (resumeThreadId != null) {
476
+ const heldByRunning = [...this.sessions.values()].some((rt) => rt.record.threadId === resumeThreadId && !rt.record.isIdle());
477
+ if (this.reservedResumeThreads.has(resumeThreadId) || heldByRunning) {
478
+ throw new InvalidParamsError(`Cannot resume thread ${resumeThreadId}: a turn is already in flight on it. ` +
479
+ "Wait for that turn to complete before resuming.");
480
+ }
481
+ this.reservedResumeThreads.add(resumeThreadId);
482
+ }
483
+ let leaseHeld = false;
484
+ let runtime;
485
+ try {
486
+ // Active-turn lease: the SOLE gate, fail-fast when full (2-3). Acquired
487
+ // INSIDE the try — and after the synchronous reservation-add with no await
488
+ // between — so a pool-full throw here is caught below and releases the
489
+ // reservation (otherwise a saturated resume would leak it permanently).
490
+ this.lease.acquire();
491
+ leaseHeld = true;
492
+ // A silent child must not hang startSession forever holding the lease
493
+ // (review fix 3): bound the whole pre-turn RPC sequence with a budget.
494
+ const established = await this.withBudget((async () => {
495
+ await this.client.ensureReady();
496
+ this.assertAdmissionOpen();
497
+ let threadId;
498
+ if (opts.resumeThreadId != null) {
499
+ const resumed = await this.client.threadResume({
500
+ threadId: opts.resumeThreadId,
501
+ });
502
+ this.assertAdmissionOpen();
503
+ threadId = extractThreadId(resumed);
504
+ }
505
+ else {
506
+ const started = await this.client.threadStart(this.buildThreadStartParams(opts));
507
+ this.assertAdmissionOpen();
508
+ threadId = extractThreadId(started);
509
+ try {
510
+ const raw = started;
511
+ const model = typeof raw.model === "string" && raw.model.length > 0 ? raw.model : undefined;
512
+ const effort = typeof raw.reasoningEffort === "string" && raw.reasoningEffort.length > 0 ? raw.reasoningEffort : undefined;
513
+ const observed = buildObservedInvocationV1("codex_app_server_thread_start", {
514
+ ...(model === undefined ? {} : { model }),
515
+ ...(effort === undefined ? {} : { effort }),
516
+ });
517
+ if (observed !== undefined && opts.activityObservation?.observe != null) {
518
+ void opts.activityObservation.observe(observed).catch((err) => {
519
+ logger.debug({ err: err instanceof Error ? err.message : String(err) }, "app-server observed invocation persistence failed open");
520
+ });
521
+ }
522
+ }
523
+ catch (err) {
524
+ logger.debug({ err: err instanceof Error ? err.message : String(err) }, "app-server observed invocation failed open");
525
+ }
526
+ }
527
+ const origin = this.config.currentDepth === 0 ? "root" : "nested";
528
+ const record = new SessionRecord({
529
+ sessionId: opts.sessionId,
530
+ threadId,
531
+ sandbox: opts.sandbox,
532
+ approval: opts.approval,
533
+ origin,
534
+ depth: this.config.currentDepth,
535
+ createdAtMs: this.now(),
536
+ eventBuffer: this.config.eventBuffer,
537
+ maxEventTextChars: this.config.maxEventTextChars,
538
+ maxFinalTextChars: this.config.maxResponseChars,
539
+ cwd: opts.cwd,
540
+ model: opts.model,
541
+ });
542
+ const rt = {
543
+ record,
544
+ hasLease: true,
545
+ finalized: false,
546
+ unsubscribe: undefined,
547
+ turnTimer: undefined,
548
+ approvalTimer: undefined,
549
+ terminal: createDeferred(),
550
+ attention: createDeferred(),
551
+ turnTimeoutMs: opts.turnTimeoutMs,
552
+ activityObservation: opts.activityObservation,
553
+ sessionObserver: opts.sessionObserver,
554
+ observerTail: Promise.resolve(),
555
+ nextObservationSequence: 0,
556
+ };
557
+ runtime = rt;
558
+ this.sessions.set(opts.sessionId, rt);
559
+ this.observeUserInput(rt, opts.input, this.now());
560
+ // Subscribe before turn/start so early deltas are not lost.
561
+ rt.unsubscribe = this.client.on({ threadId }, (event) => {
562
+ this.handleEvent(rt, event);
563
+ });
564
+ const turnResult = await this.client.turnStart(this.buildTurnStartParams(threadId, opts));
565
+ this.assertAdmissionOpen();
566
+ return { runtime: rt, turnId: extractTurnId(turnResult) };
567
+ })(), this.config.startBudgetMs);
568
+ const { runtime: rt, turnId } = established;
569
+ this.bindSessionObserverTurn(rt, turnId);
570
+ // turnStart flushes buffered provisional events before returning; a fast
571
+ // turn can fully finalize during that flush. Only establish the running
572
+ // turn if it has not already gone terminal (review fix 2).
573
+ if (!rt.finalized && rt.record.activeTurnId == null) {
574
+ rt.record.beginTurn(turnId, this.now());
575
+ this.scheduleTurnTimeout(rt);
576
+ this.observeRunning(rt);
577
+ this.observeLifecycle(rt, "running");
578
+ // Lease intentionally retained — the turn is now running.
579
+ leaseHeld = false;
580
+ }
581
+ else {
582
+ // Already finalized via turn/completed during the flush: the lease was
583
+ // released by finalizeTurn. Do not release it again.
584
+ leaseHeld = false;
585
+ }
586
+ // The session is now in the sessions map (running, or terminal if it
587
+ // finished during the flush). Subsequent resumes are gated by the
588
+ // in-flight session check, so the synchronous reservation can be released.
589
+ if (resumeThreadId != null) {
590
+ this.reservedResumeThreads.delete(resumeThreadId);
591
+ }
592
+ return rt.record.snapshot();
593
+ }
594
+ catch (err) {
595
+ // Release the resume reservation on every failure path so it never leaks.
596
+ if (resumeThreadId != null) {
597
+ this.reservedResumeThreads.delete(resumeThreadId);
598
+ }
599
+ // Failed before the turn was established: tear down, unregister, and
600
+ // release the lease (the session never reached a running turn). Guard the
601
+ // rare race where finalizeTurn already ran during the turnStart
602
+ // provisional flush (a fast turn went terminal) and the IIFE then threw
603
+ // afterwards (e.g. extractTurnId on a malformed turn result): that path
604
+ // already completed the turn and released the lease via finalizeTurn, so
605
+ // neither relabel the completed turn as "failed" nor double-release the
606
+ // lease. Once the runtime exists, runtime.hasLease/finalized are the
607
+ // source of truth (finalizeTurn flips them together); the local leaseHeld
608
+ // only covers a throw before the runtime was created.
609
+ if (runtime != null) {
610
+ if (!runtime.finalized) {
611
+ runtime.unsubscribe?.();
612
+ this.clearTimers(runtime);
613
+ runtime.finalized = true;
614
+ const failureReason = err instanceof Error ? err.message : String(err);
615
+ const now = this.now();
616
+ runtime.record.completeTurn("failed", { failureReason }, now);
617
+ this.observeTerminal(runtime, "failed");
618
+ this.observeError(runtime, failureReason, now, null);
619
+ this.observeLifecycle(runtime, "failed", now, null);
620
+ runtime.terminal.resolve();
621
+ this.sessions.delete(opts.sessionId);
622
+ if (runtime.hasLease) {
623
+ this.lease.release();
624
+ runtime.hasLease = false;
625
+ notifyAppServerActivity();
626
+ }
627
+ }
628
+ }
629
+ else if (leaseHeld) {
630
+ // Acquired the lease but threw before the runtime existed
631
+ // (ensureReady/threadResume/threadStart, or a pre-runtime budget
632
+ // timeout): release the lease taken at the top of the try.
633
+ this.lease.release();
634
+ leaseHeld = false;
635
+ notifyAppServerActivity();
636
+ }
637
+ // On a startup-budget timeout the child is presumed silent/hung: recycle
638
+ // it so the leaked pending request + provisional buffer in the client get
639
+ // cleared by handleChildClose (review fix 3).
640
+ if (err instanceof BudgetExceededError) {
641
+ await this.client
642
+ .killProcessGroup("startSession budget exceeded")
643
+ .catch(() => { });
644
+ // The kill terminated the SHARED child, so every OTHER running session
645
+ // is dead too. The client suppresses onProcessDied for intentional
646
+ // kills, so fan out here or their leases leak (review fix). The
647
+ // triggering session was already torn down + removed above.
648
+ this.finalizeCoActiveAfterSharedKill(undefined, "startSession budget exceeded");
649
+ }
650
+ throw err;
651
+ }
652
+ }
653
+ buildThreadStartParams(opts) {
654
+ const params = { sandbox: opts.sandbox };
655
+ if (opts.model != null)
656
+ params.model = opts.model;
657
+ if (opts.cwd != null)
658
+ params.cwd = opts.cwd;
659
+ if (opts.approvalPolicy != null)
660
+ params.approvalPolicy = opts.approvalPolicy;
661
+ return params;
662
+ }
663
+ buildTurnStartParams(threadId, opts) {
664
+ const params = { threadId, input: opts.input };
665
+ if (opts.cwd != null)
666
+ params.cwd = opts.cwd;
667
+ if (opts.model != null)
668
+ params.model = opts.model;
669
+ if (opts.approvalPolicy != null)
670
+ params.approvalPolicy = opts.approvalPolicy;
671
+ // turn/start requires the TAGGED-OBJECT sandboxPolicy, never the string.
672
+ // When the caller omits it, derive from the thread's sandbox mode so the
673
+ // client does not silently downgrade to read-only (review fix 8).
674
+ params.sandboxPolicy =
675
+ opts.sandboxPolicy ??
676
+ sandboxPolicyFromMode(opts.sandbox, opts.cwd != null ? { writableRoots: [opts.cwd] } : undefined);
677
+ if (opts.outputSchema != null)
678
+ params.outputSchema = opts.outputSchema;
679
+ return params;
680
+ }
681
+ /** Snapshot without consuming an active-turn lease (2-4). */
682
+ poll(sessionId) {
683
+ return this.require(sessionId).record.snapshot();
684
+ }
685
+ list(filter) {
686
+ const all = [...this.sessions.values()].map((rt) => rt.record.snapshot());
687
+ if (filter?.status != null) {
688
+ return all.filter((s) => s.status === filter.status);
689
+ }
690
+ return all;
691
+ }
692
+ /** Bounded event-log read with a monotonic cursor; never consumes a lease (2-4,2-5). */
693
+ getLog(sessionId, cursor, limit) {
694
+ return this.require(sessionId).record.events.read(cursor, limit);
695
+ }
696
+ /**
697
+ * Discovery passthrough for `model/list`. Spawns/initializes the app-server
698
+ * child if needed (via the client) but never starts a session or consumes a
699
+ * lease. Bounded by the start budget like other pre-turn RPCs.
700
+ */
701
+ async listModels(params) {
702
+ this.assertAdmissionOpen("list app-server models");
703
+ return this.withBudget(this.client.modelList(params), this.config.startBudgetMs, "model/list RPC budget exceeded");
704
+ }
705
+ /**
706
+ * Await the active turn's terminality WITHOUT consuming a lease. An optional
707
+ * `timeoutMs` bounds the wait only — the underlying turn keeps running and
708
+ * retains its lease on wait-timeout.
709
+ */
710
+ async waitForTurn(sessionId, opts) {
711
+ const runtime = this.require(sessionId);
712
+ if (runtime.finalized || runtime.record.status !== "running") {
713
+ return runtime.record.snapshot();
714
+ }
715
+ if (runtime.record.pendingApprovals.size > 0) {
716
+ return runtime.record.snapshot();
717
+ }
718
+ await this.raceTurnWait(runtime, opts?.timeoutMs, opts?.signal);
719
+ return runtime.record.snapshot();
720
+ }
721
+ /** Reply to a pending approval. The turn continues; the lease is NOT released. */
722
+ async respondApproval(sessionId, approvalId, decision) {
723
+ if (decision === "accept" || decision === "acceptForSession") {
724
+ this.assertAdmissionOpen("accept an app-server approval");
725
+ }
726
+ const runtime = this.require(sessionId);
727
+ const pending = runtime.record.getPendingApproval(approvalId);
728
+ if (pending == null) {
729
+ throw new InvalidParamsError(`stale or unknown approval: ${String(approvalId)}`);
730
+ }
731
+ this.clearApprovalTimer(runtime);
732
+ try {
733
+ // Defensive: permissions approvals are auto-handled on arrival and never
734
+ // surfaced, so this path normally only sees command/file. If a permissions
735
+ // pending somehow reaches here, use the empty-grant shape (not {decision}).
736
+ await this.withBudget(pending.type === "permissions"
737
+ ? this.client.respondPermissionsApproval(approvalId)
738
+ : this.client.respondApproval({ id: approvalId, decision }), this.config.interruptBudgetMs);
739
+ }
740
+ catch (err) {
741
+ // Restore the auto-decline/escalation backstop the timer provides before
742
+ // surfacing the failure (review fix 4).
743
+ if (runtime.record.pendingApprovals.size > 0) {
744
+ this.scheduleApprovalTimeout(runtime);
745
+ }
746
+ throw err;
747
+ }
748
+ const now = this.now();
749
+ runtime.record.clearPendingApproval(approvalId, now);
750
+ this.observeApprovalResolution(runtime, approvalId, decision, now);
751
+ if (runtime.record.pendingApprovals.size > 0) {
752
+ this.scheduleApprovalTimeout(runtime);
753
+ }
754
+ return runtime.record.snapshot();
755
+ }
756
+ /**
757
+ * Steer the currently active turn. This never starts a new turn and never
758
+ * acquires a new lease; it targets the active turn id captured in the record.
759
+ */
760
+ async steer(sessionId, input) {
761
+ this.assertAdmissionOpen("steer an app-server turn");
762
+ const runtime = this.require(sessionId);
763
+ if (runtime.finalized || runtime.record.status !== "running") {
764
+ throw new InvalidParamsError(`No active turn for session: ${sessionId}`);
765
+ }
766
+ const turnId = runtime.record.activeTurnId;
767
+ if (turnId == null) {
768
+ throw new InvalidParamsError(`No active turn for session: ${sessionId}`);
769
+ }
770
+ await this.withBudget(this.client.turnSteer({
771
+ threadId: runtime.record.threadId,
772
+ expectedTurnId: turnId,
773
+ input,
774
+ }), this.config.interruptBudgetMs, "turnSteer RPC budget exceeded");
775
+ this.observeUserInput(runtime, input, this.now());
776
+ return runtime.record.snapshot();
777
+ }
778
+ /** Explicit cancel → interrupted snapshot (decision 4). */
779
+ async interrupt(sessionId) {
780
+ const runtime = this.require(sessionId);
781
+ if (runtime.finalized || runtime.record.status !== "running") {
782
+ return runtime.record.snapshot();
783
+ }
784
+ await this.terminateTurn(runtime, "interrupted", "explicit interrupt");
785
+ return runtime.record.snapshot();
786
+ }
787
+ /** Remove a session. Running sessions are skipped unless force:true is supplied. */
788
+ async cleanup(sessionId, opts = {}) {
789
+ const runtime = this.require(sessionId);
790
+ if (!runtime.finalized && runtime.record.status === "running") {
791
+ if (opts.force !== true) {
792
+ return { sessionId, cleanedUp: false, skippedActive: true };
793
+ }
794
+ await this.terminateTurn(runtime, "interrupted", "session cleanup");
795
+ }
796
+ this.evict(sessionId, runtime, "cleanup");
797
+ return { sessionId, cleanedUp: true, skippedActive: false };
798
+ }
799
+ /**
800
+ * Standalone shutdown: interrupt every active turn, then kill the app-server
801
+ * child by process group via the client's guarded kill.
802
+ */
803
+ async shutdown(reason = "shutdown") {
804
+ this.beginShutdown(reason);
805
+ const running = [...this.sessions.values()].filter((rt) => !rt.finalized && rt.record.status === "running");
806
+ for (const runtime of running) {
807
+ await this.terminateTurn(runtime, "interrupted", reason);
808
+ }
809
+ try {
810
+ await this.client.killProcessGroup(reason);
811
+ }
812
+ catch (err) {
813
+ logger.error({ err, reason }, "app-server-registry: killProcessGroup during shutdown failed");
814
+ }
815
+ for (const [id, runtime] of [...this.sessions]) {
816
+ this.clearTimers(runtime);
817
+ runtime.unsubscribe?.();
818
+ runtime.unsubscribe = undefined;
819
+ this.sessions.delete(id);
820
+ }
821
+ this.unsubscribeProcessDied();
822
+ }
823
+ /** TTL + retained-cap reaping of idle sessions (2-4). Running sessions are never reaped. */
824
+ reapIdle() {
825
+ const now = this.now();
826
+ if (this.config.sessionTtlMs > 0) {
827
+ for (const [id, runtime] of [...this.sessions]) {
828
+ if (runtime.record.status === "running")
829
+ continue;
830
+ if (now - runtime.record.lastActivityAtMs >= this.config.sessionTtlMs) {
831
+ this.evict(id, runtime, "ttl");
832
+ }
833
+ }
834
+ }
835
+ const idle = [...this.sessions.entries()].filter(([, rt]) => rt.record.status !== "running");
836
+ const overflow = idle.length - this.config.maxRetainedSessions;
837
+ if (overflow > 0) {
838
+ idle.sort((a, b) => a[1].record.lastActivityAtMs - b[1].record.lastActivityAtMs);
839
+ for (let i = 0; i < overflow; i++) {
840
+ const entry = idle[i];
841
+ if (entry == null)
842
+ break;
843
+ this.evict(entry[0], entry[1], "retained-cap");
844
+ }
845
+ }
846
+ }
847
+ // --- internal lifecycle --------------------------------------------------
848
+ handleEvent(runtime, event) {
849
+ if (runtime.finalized)
850
+ return;
851
+ const now = this.now();
852
+ runtime.record.appendEvent(event, now);
853
+ switch (event.method) {
854
+ case CODEX_EVENT_TURN_STARTED: {
855
+ const turnId = event.params.turn.id;
856
+ if (runtime.record.activeTurnId == null) {
857
+ runtime.record.beginTurn(turnId, now);
858
+ this.scheduleTurnTimeout(runtime);
859
+ this.observeRunning(runtime);
860
+ this.observeLifecycle(runtime, "running", now);
861
+ }
862
+ break;
863
+ }
864
+ case CODEX_EVENT_AGENT_MESSAGE_DELTA: {
865
+ // Streaming assistant text. Accumulate into the bounded running preview
866
+ // so a poller can watch a turn mid-stream without a second get_log; the
867
+ // delta is also retained verbatim (capped) in the ring via appendEvent.
868
+ runtime.record.appendAssistantDelta(event.params.delta, event.params.turnId, now);
869
+ break;
870
+ }
871
+ case CODEX_EVENT_ITEM_STARTED: {
872
+ const observation = itemObservation(event.params.item, "started");
873
+ if (observation != null) {
874
+ this.runSessionObservation(runtime, observation, now);
875
+ }
876
+ break;
877
+ }
878
+ case CODEX_EVENT_ITEM_COMPLETED: {
879
+ const item = event.params.item;
880
+ if (item.type === "agentMessage" && typeof item.text === "string") {
881
+ // Cache the latest final-answer text; capped at assignment so any
882
+ // terminal path exposes a bounded value (review fix 7).
883
+ runtime.record.setFinalText(item.text);
884
+ }
885
+ const observation = itemObservation(event.params.item, "completed");
886
+ if (observation != null) {
887
+ this.runSessionObservation(runtime, observation, now);
888
+ }
889
+ break;
890
+ }
891
+ case CODEX_EVENT_THREAD_TOKEN_USAGE_UPDATED: {
892
+ runtime.record.recordTokenUsage(event.params.tokenUsage, now);
893
+ const last = event.params.tokenUsage.last;
894
+ this.runSessionObservation(runtime, {
895
+ kind: "usage",
896
+ inputTokens: optionalTokenCount(last.inputTokens),
897
+ cachedInputTokens: optionalTokenCount(last.cachedInputTokens),
898
+ outputTokens: optionalTokenCount(last.outputTokens),
899
+ reasoningTokens: optionalTokenCount(last.reasoningOutputTokens),
900
+ incomplete: runtime.record.tokenUsage.incomplete,
901
+ }, now);
902
+ break;
903
+ }
904
+ case CODEX_EVENT_TURN_COMPLETED: {
905
+ const status = mapTurnStatus(event.params.turn.status);
906
+ // finalText already cached from item/completed (if any).
907
+ const finalText = runtime.record.finalText;
908
+ this.finalizeTurn(runtime, status, { finalText });
909
+ break;
910
+ }
911
+ case CODEX_EVENT_TURN_DIFF_UPDATED: {
912
+ runtime.record.setLatestDiff(event.params.diff, now);
913
+ break;
914
+ }
915
+ case CODEX_EVENT_TURN_PLAN_UPDATED: {
916
+ runtime.record.setLatestPlan(event.params.plan, event.params.explanation ?? null, now);
917
+ break;
918
+ }
919
+ case CODEX_EVENT_ITEM_PLAN_DELTA:
920
+ // Streaming plan text; no snapshot field — it is already retained in the
921
+ // event ring via appendEvent above (get_log surfaces it).
922
+ break;
923
+ case CODEX_REQUEST_PERMISSIONS_APPROVAL: {
924
+ // Permissions (sandbox-escalation) approvals are never surfaced: under
925
+ // the read-only / approval:never posture we auto-decline on arrival with
926
+ // a protocol-valid empty grant (grants nothing). Best-effort and bounded;
927
+ // a failure here cannot stall the turn beyond the wall-clock timeout.
928
+ const pending = pendingApprovalFromEvent(event, "permissions", now);
929
+ this.observeApprovalRequest(runtime, pending, now);
930
+ void this.autoDeclinePermissionsApproval(runtime, event.id);
931
+ break;
932
+ }
933
+ case CODEX_REQUEST_COMMAND_EXECUTION_APPROVAL:
934
+ case CODEX_REQUEST_FILE_CHANGE_APPROVAL: {
935
+ const type = approvalTypeFromMethod(event.method);
936
+ if (type == null)
937
+ break;
938
+ const pending = pendingApprovalFromEvent(event, type, now);
939
+ runtime.record.setPendingApproval(pending, now);
940
+ this.notifyAttention(runtime);
941
+ this.scheduleApprovalTimeout(runtime);
942
+ this.observeApprovalRequest(runtime, pending, now);
943
+ break;
944
+ }
945
+ default:
946
+ break;
947
+ }
948
+ }
949
+ /**
950
+ * Finalize a turn on PROVABLE terminality. Idempotent. Clears timers,
951
+ * unsubscribes, releases the lease, and resolves the terminal deferred.
952
+ */
953
+ finalizeTurn(runtime, status, opts) {
954
+ if (runtime.finalized)
955
+ return;
956
+ runtime.finalized = true;
957
+ this.clearTimers(runtime);
958
+ runtime.unsubscribe?.();
959
+ runtime.unsubscribe = undefined;
960
+ const turnId = runtime.record.activeTurnId ?? null;
961
+ const now = this.now();
962
+ runtime.record.completeTurn(status, opts, now);
963
+ this.observeTerminal(runtime, status);
964
+ if (status === "failed" || status === "timeout") {
965
+ this.observeError(runtime, runtime.record.failureReason ??
966
+ (status === "timeout"
967
+ ? "Codex app-server turn timed out."
968
+ : "Codex app-server turn failed."), now, turnId);
969
+ }
970
+ this.observeLifecycle(runtime, status, now, turnId);
971
+ if (runtime.hasLease) {
972
+ this.lease.release();
973
+ runtime.hasLease = false;
974
+ notifyAppServerActivity();
975
+ }
976
+ runtime.terminal.resolve();
977
+ }
978
+ runSessionObservation(runtime, semantic, atMs = this.now(), turnId = runtime.record.activeTurnId ?? null) {
979
+ const observer = runtime.sessionObserver ?? this.observer;
980
+ if (observer == null)
981
+ return;
982
+ // Capture sequence and identity synchronously at the semantic reduction
983
+ // point. The async continuation receives only this immutable snapshot.
984
+ const observation = {
985
+ ...semantic,
986
+ sequence: runtime.nextObservationSequence++,
987
+ sessionId: runtime.record.sessionId,
988
+ threadId: runtime.record.threadId,
989
+ turnId,
990
+ atMs,
991
+ };
992
+ const deliver = async () => {
993
+ try {
994
+ await observer(observation);
995
+ }
996
+ catch (err) {
997
+ logger.debug({
998
+ err: err instanceof Error ? err.message : String(err),
999
+ sessionId: observation.sessionId,
1000
+ observationKind: observation.kind,
1001
+ sequence: observation.sequence,
1002
+ }, "app-server-registry: session observation failed open");
1003
+ }
1004
+ };
1005
+ runtime.observerTail = runtime.observerTail.then(deliver, deliver);
1006
+ }
1007
+ bindSessionObserverTurn(runtime, turnId) {
1008
+ const observer = runtime.sessionObserver ?? this.observer;
1009
+ if (observer?.bindTurn == null)
1010
+ return;
1011
+ const binding = Object.freeze({
1012
+ sessionId: runtime.record.sessionId,
1013
+ threadId: runtime.record.threadId,
1014
+ turnId,
1015
+ });
1016
+ const deliver = async () => {
1017
+ try {
1018
+ await observer.bindTurn(binding);
1019
+ }
1020
+ catch {
1021
+ logger.debug({ sessionId: binding.sessionId }, "app-server-registry: session turn binding failed open");
1022
+ }
1023
+ };
1024
+ runtime.observerTail = runtime.observerTail.then(deliver, deliver);
1025
+ }
1026
+ observeUserInput(runtime, input, atMs = this.now()) {
1027
+ const content = turnInputText(input);
1028
+ if (content == null)
1029
+ return;
1030
+ this.runSessionObservation(runtime, { kind: "user", content }, atMs, runtime.record.activeTurnId ?? null);
1031
+ }
1032
+ observeApprovalRequest(runtime, approval, atMs = this.now()) {
1033
+ this.runSessionObservation(runtime, {
1034
+ kind: "approval_request",
1035
+ approvalId: approval.id,
1036
+ approvalType: approval.type,
1037
+ action: approvalAction(approval),
1038
+ }, atMs);
1039
+ }
1040
+ observeApprovalResolution(runtime, approvalId, decision, atMs = this.now()) {
1041
+ this.runSessionObservation(runtime, {
1042
+ kind: "approval_resolution",
1043
+ approvalId,
1044
+ decision,
1045
+ }, atMs);
1046
+ }
1047
+ observeError(runtime, message, atMs = this.now(), turnId = runtime.record.activeTurnId ?? null) {
1048
+ this.runSessionObservation(runtime, {
1049
+ kind: "error",
1050
+ code: "app_server_session_failed",
1051
+ message,
1052
+ retryable: false,
1053
+ }, atMs, turnId);
1054
+ }
1055
+ observeLifecycle(runtime, status, atMs = this.now(), turnId = runtime.record.activeTurnId ?? null) {
1056
+ this.runSessionObservation(runtime, {
1057
+ kind: "lifecycle",
1058
+ status,
1059
+ failureReason: runtime.record.failureReason ?? null,
1060
+ }, atMs, turnId);
1061
+ }
1062
+ observeRunning(runtime) {
1063
+ const observation = runtime.activityObservation;
1064
+ if (observation == null)
1065
+ return;
1066
+ this.runActivityObservation(runtime, "running", () => observation.transition("running"));
1067
+ }
1068
+ observeTerminal(runtime, status) {
1069
+ const observation = runtime.activityObservation;
1070
+ if (observation == null)
1071
+ return;
1072
+ const completion = status === "completed"
1073
+ ? { status: "succeeded", outcomeKind: "success" }
1074
+ : status === "interrupted"
1075
+ ? { status: "cancelled", outcomeKind: "cancelled" }
1076
+ : { status: "failed", outcomeKind: "error" };
1077
+ this.runActivityObservation(runtime, "terminal", () => observation.complete(completion));
1078
+ }
1079
+ runActivityObservation(runtime, boundary, operation) {
1080
+ const report = (err) => {
1081
+ logger.debug({
1082
+ err: err instanceof Error ? err.message : String(err),
1083
+ sessionId: runtime.record.sessionId,
1084
+ }, `app-server-registry: dashboard ${boundary} observation failed open`);
1085
+ };
1086
+ try {
1087
+ void operation().catch(report);
1088
+ }
1089
+ catch (err) {
1090
+ report(err);
1091
+ }
1092
+ }
1093
+ /**
1094
+ * Shared terminality/escalation path (2-8 / 2-9):
1095
+ * 1. bounded interrupt RPC
1096
+ * 2. bounded wait for terminal turn/completed
1097
+ * 3. on hang/no-terminal → process-group kill + respawn + mark terminal
1098
+ *
1099
+ * The lease is released only via {@link finalizeTurn}, i.e. only once the
1100
+ * turn is provably terminal.
1101
+ */
1102
+ async terminateTurn(runtime, statusOnKill, reason) {
1103
+ if (runtime.finalized)
1104
+ return;
1105
+ const threadId = runtime.record.threadId;
1106
+ const turnId = runtime.record.activeTurnId;
1107
+ if (turnId != null) {
1108
+ try {
1109
+ await this.withBudget(this.client.turnInterrupt({ threadId, turnId }), this.config.interruptBudgetMs);
1110
+ const reachedTerminal = await this.waitTerminalWithin(runtime, this.config.terminalWaitBudgetMs);
1111
+ if (reachedTerminal)
1112
+ return; // finalized via turn/completed event
1113
+ }
1114
+ catch (err) {
1115
+ logger.warn({ err, reason, sessionId: runtime.record.sessionId }, "app-server-registry: interrupt RPC failed or timed out — escalating");
1116
+ }
1117
+ }
1118
+ if (runtime.finalized)
1119
+ return;
1120
+ await this.forceKill(runtime, statusOnKill, reason);
1121
+ }
1122
+ async forceKill(runtime, status, reason) {
1123
+ if (runtime.finalized)
1124
+ return;
1125
+ try {
1126
+ await this.client.killProcessGroup(reason);
1127
+ }
1128
+ catch (err) {
1129
+ logger.error({ err, reason, sessionId: runtime.record.sessionId }, "app-server-registry: killProcessGroup failed during escalation");
1130
+ }
1131
+ // Release leases FIRST — the kill terminated the SHARED child, so every
1132
+ // in-flight turn is dead. The client suppresses onProcessDied for
1133
+ // intentional kills, so we must finalize here or leases leak forever
1134
+ // (review fix). This must NOT depend on the respawn: a silent respawned
1135
+ // child would otherwise block escalation before any lease is released.
1136
+ // Future startSession calls re-ensureReady themselves.
1137
+ this.finalizeTurn(runtime, status, { failureReason: reason });
1138
+ this.finalizeCoActiveAfterSharedKill(runtime, reason);
1139
+ if (this.shuttingDown || appServerAdmissionClosed)
1140
+ return;
1141
+ try {
1142
+ // Best-effort respawn ownership is the client's — registry only nudges
1143
+ // it, and bounds it so a silent child cannot hang escalation.
1144
+ await this.withBudget(this.client.ensureReady(), this.config.startBudgetMs);
1145
+ }
1146
+ catch (err) {
1147
+ logger.error({ err, sessionId: runtime.record.sessionId }, "app-server-registry: ensureReady (respawn) failed during escalation");
1148
+ }
1149
+ }
1150
+ /**
1151
+ * Finalize every co-active running session after the SHARED child was killed.
1152
+ * The client suppresses onProcessDied for intentional kills, so without this
1153
+ * their leases leak (review fix). finalizeTurn is idempotent; `except` is the
1154
+ * triggering runtime, handled by the caller.
1155
+ */
1156
+ finalizeCoActiveAfterSharedKill(except, reason) {
1157
+ for (const other of this.sessions.values()) {
1158
+ if (other === except)
1159
+ continue;
1160
+ if (other.finalized || other.record.status !== "running")
1161
+ continue;
1162
+ this.finalizeTurn(other, "failed", {
1163
+ failureReason: `shared app-server killed: ${reason}`,
1164
+ });
1165
+ }
1166
+ }
1167
+ async onTurnTimeout(runtime) {
1168
+ if (runtime.finalized)
1169
+ return;
1170
+ logger.warn({ sessionId: runtime.record.sessionId }, "app-server-registry: turn wall-clock timeout — attempting interrupt");
1171
+ await this.terminateTurn(runtime, "timeout", "turn wall-clock timeout");
1172
+ }
1173
+ async onApprovalTimeout(runtime) {
1174
+ if (runtime.finalized)
1175
+ return;
1176
+ const pendingApprovals = [...runtime.record.pendingApprovals.values()];
1177
+ if (pendingApprovals.length === 0)
1178
+ return;
1179
+ // Auto-decline is NOT terminal (2-9): the turn may continue, and the
1180
+ // decline RPC can hang. Bound it, then await terminality, then escalate.
1181
+ try {
1182
+ for (const pending of pendingApprovals) {
1183
+ if (runtime.finalized)
1184
+ return;
1185
+ // Defensive: permissions approvals are auto-handled on arrival, so this
1186
+ // path normally only sees command/file. Route any stray permissions
1187
+ // pending to the empty-grant shape rather than {decision}.
1188
+ await this.withBudget(pending.type === "permissions"
1189
+ ? this.client.respondPermissionsApproval(pending.id)
1190
+ : this.client.respondApproval({
1191
+ id: pending.id,
1192
+ decision: "decline",
1193
+ }), this.config.interruptBudgetMs);
1194
+ const now = this.now();
1195
+ runtime.record.clearPendingApproval(pending.id, now);
1196
+ this.observeApprovalResolution(runtime, pending.id, "decline", now);
1197
+ }
1198
+ const reachedTerminal = await this.waitTerminalWithin(runtime, this.config.terminalWaitBudgetMs);
1199
+ if (reachedTerminal)
1200
+ return; // finalized via turn/completed event
1201
+ }
1202
+ catch (err) {
1203
+ logger.warn({ err, sessionId: runtime.record.sessionId }, "app-server-registry: auto-decline RPC failed or timed out — escalating");
1204
+ }
1205
+ if (runtime.finalized)
1206
+ return;
1207
+ await this.forceKill(runtime, "failed", "pending-approval timeout");
1208
+ }
1209
+ /**
1210
+ * Auto-decline a permissions (sandbox-escalation) approval the moment it
1211
+ * arrives, with a protocol-valid empty grant. Never surfaced to the user
1212
+ * (read-only / approval:never posture). Bounded: the decline RPC is capped by
1213
+ * `interruptBudgetMs`.
1214
+ *
1215
+ * On decline failure/timeout we do NOT merely log: Codex's child is blocked on
1216
+ * a response that never lands, and if the operator disabled the wall-clock
1217
+ * turn timeout (`CLINKX_TURN_TIMEOUT_SECONDS=0`) there is no backstop until
1218
+ * idle reap. So we escalate the turn directly under a bounded budget,
1219
+ * independent of the global turn timeout, mirroring the command/file
1220
+ * decline-failure recovery in {@link onApprovalTimeout}. terminateTurn is
1221
+ * idempotent and no-ops if the turn already finalized, so this never
1222
+ * double-finalizes.
1223
+ */
1224
+ async autoDeclinePermissionsApproval(runtime, id) {
1225
+ try {
1226
+ await this.withBudget(this.client.respondPermissionsApproval(id), this.config.interruptBudgetMs);
1227
+ this.observeApprovalResolution(runtime, id, "decline", this.now());
1228
+ logger.debug({ sessionId: runtime.record.sessionId, approvalId: id }, "app-server-registry: auto-declined permissions approval (empty grant)");
1229
+ }
1230
+ catch (err) {
1231
+ logger.warn({ err, sessionId: runtime.record.sessionId, approvalId: id }, "app-server-registry: permissions auto-decline failed — escalating turn");
1232
+ // Failure-safe backstop: the child is wedged awaiting a response we could
1233
+ // not deliver. Escalate independently of the (possibly disabled) turn
1234
+ // wall-clock timeout. Idempotent; bounded by interrupt/terminal budgets.
1235
+ await this.terminateTurn(runtime, "failed", "permissions auto-decline failed").catch((escalationErr) => {
1236
+ logger.error({ err: escalationErr, sessionId: runtime.record.sessionId, approvalId: id }, "app-server-registry: escalation after permissions auto-decline failure errored");
1237
+ });
1238
+ }
1239
+ }
1240
+ handleProcessDied(event) {
1241
+ const reason = "app-server process died" +
1242
+ (event.signal != null ? ` (signal ${event.signal})` : "") +
1243
+ (event.exitCode != null ? ` (exit ${event.exitCode})` : "");
1244
+ for (const runtime of this.sessions.values()) {
1245
+ // Crash split (2-10): only active/in-flight turns are unrecoverable.
1246
+ // Idle/completed sessions keep their resumable threadId untouched.
1247
+ if (!runtime.finalized && runtime.record.status === "running") {
1248
+ this.finalizeTurn(runtime, "failed", { failureReason: reason });
1249
+ }
1250
+ }
1251
+ }
1252
+ // --- timers --------------------------------------------------------------
1253
+ scheduleTurnTimeout(runtime) {
1254
+ // Honor an explicit per-session override (review fix B); fall back to the
1255
+ // global default/backstop. A value of 0 disables the timeout, preserving
1256
+ // the global's disabled semantics exactly.
1257
+ const turnTimeoutMs = runtime.turnTimeoutMs ?? this.config.turnTimeoutMs;
1258
+ if (turnTimeoutMs <= 0)
1259
+ return;
1260
+ if (runtime.turnTimer != null)
1261
+ this.clearTimer(runtime.turnTimer);
1262
+ runtime.turnTimer = this.setTimer(() => {
1263
+ void this.onTurnTimeout(runtime);
1264
+ }, turnTimeoutMs);
1265
+ }
1266
+ scheduleApprovalTimeout(runtime) {
1267
+ if (this.config.approvalTimeoutMs <= 0)
1268
+ return;
1269
+ this.clearApprovalTimer(runtime);
1270
+ runtime.approvalTimer = this.setTimer(() => {
1271
+ void this.onApprovalTimeout(runtime);
1272
+ }, this.config.approvalTimeoutMs);
1273
+ }
1274
+ clearApprovalTimer(runtime) {
1275
+ if (runtime.approvalTimer != null) {
1276
+ this.clearTimer(runtime.approvalTimer);
1277
+ runtime.approvalTimer = undefined;
1278
+ }
1279
+ }
1280
+ clearTimers(runtime) {
1281
+ if (runtime.turnTimer != null) {
1282
+ this.clearTimer(runtime.turnTimer);
1283
+ runtime.turnTimer = undefined;
1284
+ }
1285
+ this.clearApprovalTimer(runtime);
1286
+ }
1287
+ evict(sessionId, runtime, why) {
1288
+ this.clearTimers(runtime);
1289
+ runtime.unsubscribe?.();
1290
+ runtime.unsubscribe = undefined;
1291
+ this.sessions.delete(sessionId);
1292
+ logger.debug({ sessionId, why }, "app-server-registry: evicted session");
1293
+ }
1294
+ // --- bounded-wait helpers ------------------------------------------------
1295
+ /** Reject if `promise` does not settle within `ms`. */
1296
+ withBudget(promise, ms, message) {
1297
+ if (ms <= 0)
1298
+ return promise;
1299
+ return new Promise((resolve, reject) => {
1300
+ const handle = this.setTimer(() => {
1301
+ reject(new BudgetExceededError(message));
1302
+ }, ms);
1303
+ promise.then((value) => {
1304
+ this.clearTimer(handle);
1305
+ resolve(value);
1306
+ }, (err) => {
1307
+ this.clearTimer(handle);
1308
+ reject(err instanceof Error ? err : new Error(String(err)));
1309
+ });
1310
+ });
1311
+ }
1312
+ /** Resolve `true` if the terminal deferred settles within `ms`, else `false`. */
1313
+ waitTerminalWithin(runtime, ms) {
1314
+ if (runtime.finalized)
1315
+ return Promise.resolve(true);
1316
+ if (ms <= 0)
1317
+ return Promise.resolve(false);
1318
+ return new Promise((resolve) => {
1319
+ let settled = false;
1320
+ const handle = this.setTimer(() => {
1321
+ if (settled)
1322
+ return;
1323
+ settled = true;
1324
+ resolve(false);
1325
+ }, ms);
1326
+ void runtime.terminal.promise.then(() => {
1327
+ if (settled)
1328
+ return;
1329
+ settled = true;
1330
+ this.clearTimer(handle);
1331
+ resolve(true);
1332
+ });
1333
+ });
1334
+ }
1335
+ notifyAttention(runtime) {
1336
+ const attention = runtime.attention;
1337
+ runtime.attention = createDeferred();
1338
+ attention.resolve();
1339
+ }
1340
+ /**
1341
+ * Await terminality, a wait-local timeout, or newly requested human attention.
1342
+ * All outcomes are lease-free: a pending approval returns a running snapshot.
1343
+ */
1344
+ raceTurnWait(runtime, timeoutMs, signal) {
1345
+ return new Promise((resolve) => {
1346
+ let settled = false;
1347
+ let handle;
1348
+ let onAbort;
1349
+ const done = () => {
1350
+ if (settled)
1351
+ return;
1352
+ settled = true;
1353
+ if (handle != null)
1354
+ this.clearTimer(handle);
1355
+ if (onAbort != null && signal != null) {
1356
+ signal.removeEventListener("abort", onAbort);
1357
+ }
1358
+ resolve();
1359
+ };
1360
+ if (signal?.aborted) {
1361
+ // Already aborted: resolve immediately without arming a timer.
1362
+ resolve();
1363
+ return;
1364
+ }
1365
+ if (timeoutMs != null && timeoutMs > 0) {
1366
+ handle = this.setTimer(done, timeoutMs);
1367
+ }
1368
+ if (signal != null) {
1369
+ onAbort = done;
1370
+ signal.addEventListener("abort", onAbort, { once: true });
1371
+ }
1372
+ void runtime.terminal.promise.then(done);
1373
+ void runtime.attention.promise.then(done);
1374
+ });
1375
+ }
1376
+ }
1377
+ let appServerRegistrySingleton;
1378
+ let appServerRegistrySingletonSpawn;
1379
+ let appServerRegistryTestOverrides;
1380
+ /**
1381
+ * Derive the registry spawn request from a resolved app_server adapter:
1382
+ * `app_server.command` (falling back to the adapter's top-level `command`) and
1383
+ * `app_server.args`.
1384
+ */
1385
+ export function spawnRequestFor(adapter) {
1386
+ return {
1387
+ command: adapter.app_server?.command ?? adapter.command,
1388
+ args: adapter.app_server?.args,
1389
+ };
1390
+ }
1391
+ function resolveSpawnRequest(request) {
1392
+ return {
1393
+ command: request?.command ?? DEFAULT_APP_SERVER_SPAWN_CONFIG.command,
1394
+ args: request?.args ?? [...DEFAULT_APP_SERVER_SPAWN_CONFIG.args],
1395
+ };
1396
+ }
1397
+ function defaultAppServerSpawnConfig(spawn, env) {
1398
+ return {
1399
+ command: spawn.command,
1400
+ args: [...spawn.args],
1401
+ env,
1402
+ };
1403
+ }
1404
+ function createDefaultAppServerRegistry(spawn) {
1405
+ // Resolve config first so the spawned child and the registry share the same
1406
+ // depth: the child must receive CLINKX_AGENT_DEPTH=currentDepth+1 so the
1407
+ // recursion guard survives the env sanitizer (review fix 5).
1408
+ const config = resolveRegistryConfig();
1409
+ const childEnv = childEnvMarkerFor(config.currentDepth);
1410
+ const client = appServerRegistryTestOverrides?.client ??
1411
+ new RealCodexAppServerClient(defaultAppServerSpawnConfig(spawn, childEnv));
1412
+ const registryFactory = appServerRegistryTestOverrides?.registryFactory ??
1413
+ ((deps) => new AppServerRegistry(deps));
1414
+ return registryFactory({ client, config });
1415
+ }
1416
+ /**
1417
+ * Get the process-wide app-server registry, creating it on first call.
1418
+ *
1419
+ * The registry is a singleton: one long-lived `codex app-server` process
1420
+ * multiplexes every session. The adapter's `app_server.command/args` therefore
1421
+ * take effect only at FIRST creation. A later adapter requesting a spawn config
1422
+ * that conflicts with the live one is rejected with {@link InvalidParamsError}
1423
+ * (-32602) rather than silently ignored — building a registry-per-config map
1424
+ * would be over-engineering for a singleton-by-design subsystem.
1425
+ */
1426
+ export function getAppServerRegistry(spawn) {
1427
+ if (appServerAdmissionClosed) {
1428
+ throw new InvalidParamsError("Cannot access the app-server registry after shutdown has started.");
1429
+ }
1430
+ const resolved = resolveSpawnRequest(spawn);
1431
+ if (appServerRegistrySingleton == null) {
1432
+ appServerRegistrySingleton = createDefaultAppServerRegistry(resolved);
1433
+ appServerRegistrySingletonSpawn = resolved;
1434
+ return appServerRegistrySingleton;
1435
+ }
1436
+ // A no-arg call (e.g. clink_agent_manage) is a pure accessor — it never
1437
+ // imposes a spawn config, so it cannot conflict with the live one.
1438
+ if (spawn == null) {
1439
+ return appServerRegistrySingleton;
1440
+ }
1441
+ const live = appServerRegistrySingletonSpawn;
1442
+ if (live != null &&
1443
+ (live.command !== resolved.command ||
1444
+ live.args.length !== resolved.args.length ||
1445
+ live.args.some((arg, i) => arg !== resolved.args[i]))) {
1446
+ throw new InvalidParamsError(`Codex app-server spawn config conflict: the app-server process is already ` +
1447
+ `running as "${live.command} ${live.args.join(" ")}" but this adapter requests ` +
1448
+ `"${resolved.command} ${resolved.args.join(" ")}". The app-server registry is a ` +
1449
+ `single shared process; all app_server adapters must agree on command/args.`);
1450
+ }
1451
+ return appServerRegistrySingleton;
1452
+ }
1453
+ /**
1454
+ * Return the live process-wide registry WITHOUT creating it. Read-only manage
1455
+ * ops (clink_agent_manage) must not lock in a default spawn config before a run
1456
+ * has chosen one — otherwise a later run with a custom app_server.command would
1457
+ * spuriously hit the spawn-config conflict (-32602). See {@link getAppServerRegistry}.
1458
+ */
1459
+ export function peekAppServerRegistry() {
1460
+ return appServerRegistrySingleton;
1461
+ }
1462
+ export async function shutdownAppServerRegistry(reason) {
1463
+ const registry = appServerRegistrySingleton;
1464
+ if (registry == null)
1465
+ return;
1466
+ await registry.shutdown(reason);
1467
+ appServerRegistrySingleton = undefined;
1468
+ appServerRegistrySingletonSpawn = undefined;
1469
+ }
1470
+ /** The spawn command/args the live singleton was created with (test inspection). */
1471
+ export function appServerRegistrySpawnForTest() {
1472
+ return appServerRegistrySingletonSpawn;
1473
+ }
1474
+ export function configureAppServerRegistryForTest(overrides) {
1475
+ appServerRegistryTestOverrides = overrides;
1476
+ appServerRegistrySingleton = undefined;
1477
+ appServerRegistrySingletonSpawn = undefined;
1478
+ }
1479
+ export async function resetAppServerRegistryForTest(reason = "test reset") {
1480
+ const registry = appServerRegistrySingleton;
1481
+ appServerRegistrySingleton = undefined;
1482
+ appServerRegistrySingletonSpawn = undefined;
1483
+ appServerRegistryTestOverrides = undefined;
1484
+ try {
1485
+ if (registry != null) {
1486
+ await registry.shutdown(reason);
1487
+ }
1488
+ }
1489
+ finally {
1490
+ appServerAdmissionClosed = false;
1491
+ }
1492
+ }
1493
+ //# sourceMappingURL=app-server-registry.js.map