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
@@ -1,5 +1,6 @@
1
1
  import { InvalidParamsError } from "../errors.js";
2
- const PRIVATE_KEY_BLOCK_PATTERN = /-----BEGIN [A-Z0-9][A-Z0-9 ]{0,48}PRIVATE KEY-----/;
2
+ import { isAlias, isMap, isScalar, isSeq, parseAllDocuments, } from "yaml";
3
+ const PRIVATE_KEY_BLOCK_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/;
3
4
  const WELL_KNOWN_TOKEN_PATTERNS = [
4
5
  { provenance: "aws_access_key_id", pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/ },
5
6
  { provenance: "github_token", pattern: /\bgh[opusr]_[A-Za-z0-9_]{30,}\b/ },
@@ -10,7 +11,668 @@ const WELL_KNOWN_TOKEN_PATTERNS = [
10
11
  { provenance: "npm_token", pattern: /\bnpm_[A-Za-z0-9]{30,}\b/ },
11
12
  { provenance: "slack_token", pattern: /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/ },
12
13
  ];
13
- const ASSIGNMENT_PATTERN = /\b(password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)\b\s*[:=]\s*(['"]?)([^'"\s;,#]{8,})\2/i;
14
+ const ASSIGNMENT_KEY_PATTERN = /(?<![A-Za-z0-9_$-])(?:(["'])([A-Za-z_][A-Za-z0-9_-]*)\1|((?:[A-Za-z_$][A-Za-z0-9_$]*(?:[ \t]*(?:\.|::)[ \t]*[A-Za-z_$][A-Za-z0-9_$]*)*[ \t]*)?\[[ \t]*(["'])([A-Za-z_][A-Za-z0-9_-]*)\4[ \t]*\])|([A-Za-z_$][A-Za-z0-9_$-]*(?:[ \t]*(?:\.|::)[ \t]*[A-Za-z_$][A-Za-z0-9_$-]*)*))(?=[ \t]*[:=])/gi;
15
+ const SENSITIVE_ASSIGNMENT_KEY_PATTERN = /(?:^|[_-])(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)$/i;
16
+ const REDACTION_MARKER = "[redacted-sensitive]";
17
+ const YAML_BLOCK_HEADER_PATTERN_SOURCE = String.raw `(?:(?:&[A-Za-z_][A-Za-z0-9_.-]*|!<[^>\r\n]+>|!!?[A-Za-z_][A-Za-z0-9_.:/-]*)[ \t]+)*[|>](?:[+-][1-9]?|[1-9][+-]?)?`;
18
+ const MAX_YAML_DOCUMENTS = 32;
19
+ const MAX_YAML_NODES = 20_000;
20
+ const MAX_YAML_DEPTH = 128;
21
+ const MAX_YAML_SOURCE_CHARS = 5_000_000;
22
+ const MAX_JS_KEY_LOOKBACK = 32_768;
23
+ const MAX_TS_TYPE_CHARS = 4_096;
24
+ const MAX_TS_TYPE_DEPTH = 32;
25
+ /**
26
+ * Fail-closed provenance for a body whose deterministic scan work exceeded
27
+ * its budget. The budget is a backstop, not the performance mechanism: the
28
+ * type-body header scan is a deterministic delimiter-aware linear scan, and
29
+ * the other index-backed detectors are linear and memoized per text, so
30
+ * realistic and generated corpora never trip it. Known residuals: the
31
+ * dense_fields shape stays super-linear but bounded (tracked follow-up), and
32
+ * the value-span/redaction regexes still have catastrophic-backtracking
33
+ * shapes (member-call-chain, backslash runs; see the scan-wedge issue doc)
34
+ * that can wedge inside a single regex match before the budget trips. Work
35
+ * is charged at real interrupt points (per header candidate, mid index
36
+ * build), so exhaustion unwinds and fails closed. Revisit the calibration
37
+ * if a legitimate body ever reports this reason.
38
+ */
39
+ export const SANITIZE_BUDGET_EXHAUSTED_PROVENANCE = "sanitize_budget_exhausted";
40
+ // Calibrated at >= 20x the linearized realistic-shape cost, with headroom
41
+ // for the dense_fields adversarial shape (measured ~4_000 work units per
42
+ // char at 256 KiB; see tests/context-builder/secrets-perf.test.ts). Larger
43
+ // or denser pathological bodies exhaust it by design and fail closed.
44
+ const SECRET_SCAN_WORK_PER_CHAR = 6_000;
45
+ const SECRET_SCAN_WORK_FLOOR = 4_000_000;
46
+ class SecretScanBudgetExhausted extends Error {
47
+ }
48
+ let activeSecretScanSession;
49
+ let secretScanWorkTotal = 0;
50
+ let secretScanBudgetOverride;
51
+ function chargeWork(units) {
52
+ secretScanWorkTotal += units;
53
+ const session = activeSecretScanSession;
54
+ if (session != null) {
55
+ session.work += units;
56
+ if (session.work > session.limit) {
57
+ throw new SecretScanBudgetExhausted();
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * Runs one public detector entry point inside a per-body session. Nested
63
+ * entry points (sanitize -> scan/redact/residual) share the outer budget and
64
+ * memo; exhaustion unwinds to the outermost entry, which fails closed.
65
+ */
66
+ function withSecretScanSession(text, work, onExhausted) {
67
+ if (activeSecretScanSession != null) {
68
+ return work();
69
+ }
70
+ activeSecretScanSession = {
71
+ limit: secretScanBudgetOverride ??
72
+ SECRET_SCAN_WORK_FLOOR + SECRET_SCAN_WORK_PER_CHAR * text.length,
73
+ work: 0,
74
+ texts: new Map(),
75
+ };
76
+ try {
77
+ return work();
78
+ }
79
+ catch (error) {
80
+ if (error instanceof SecretScanBudgetExhausted) {
81
+ return onExhausted();
82
+ }
83
+ throw error;
84
+ }
85
+ finally {
86
+ activeSecretScanSession = undefined;
87
+ }
88
+ }
89
+ function analysisFor(text) {
90
+ const session = activeSecretScanSession;
91
+ if (session == null) {
92
+ return undefined;
93
+ }
94
+ let analysis = session.texts.get(text);
95
+ if (analysis == null) {
96
+ analysis = {};
97
+ session.texts.set(text, analysis);
98
+ }
99
+ return analysis;
100
+ }
101
+ /** Test-only: deterministic work units consumed by `work`. */
102
+ export function measureSecretScanWorkForTests(work) {
103
+ const before = secretScanWorkTotal;
104
+ const result = work();
105
+ return { result, work: secretScanWorkTotal - before };
106
+ }
107
+ /** Test-only: run `work` with a fixed per-body work budget. */
108
+ export function withSecretScanBudgetForTests(limit, work) {
109
+ const previous = secretScanBudgetOverride;
110
+ secretScanBudgetOverride = limit;
111
+ try {
112
+ return work();
113
+ }
114
+ finally {
115
+ secretScanBudgetOverride = previous;
116
+ }
117
+ }
118
+ /** Test-only: (start, brace) offset of every type-body header match. */
119
+ export function typeBodyHeaderMatchesForTests(text) {
120
+ const headers = collectTypeBodyHeaders(text);
121
+ const pairs = [];
122
+ for (let slot = 0; slot < headers.starts.length; slot++) {
123
+ const start = headers.starts[slot];
124
+ const brace = headers.braces[slot];
125
+ if (start != null && brace != null) {
126
+ pairs.push({ start, brace });
127
+ }
128
+ }
129
+ return pairs;
130
+ }
131
+ function budgetExhaustedFinding() {
132
+ return contentFinding(SANITIZE_BUDGET_EXHAUSTED_PROVENANCE, 1);
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Linear per-text indexes.
136
+ //
137
+ // The detectors below used to rescan a fresh 32 KiB lookback slice per '='
138
+ // (regex matchAll over the window plus a brace scan, per query). Each index is
139
+ // built once per distinct body-level string per scan session, memoized in the
140
+ // SecretTextAnalysis slots, and queried with binary searches / O(1) lookups.
141
+ // Query semantics replicate the legacy slice behavior exactly, including the
142
+ // slice-relative `^`/`\b` window edge (a header or typed field starting
143
+ // exactly at the window start counts) — see the per-query comments.
144
+ // ---------------------------------------------------------------------------
145
+ const TYPED_FIELD_PATTERN = /(?:^|[;{}\r\n])[ \t]*(?:(?:public|private|protected|static|readonly|declare|abstract|override)[ \t]+)*([A-Za-z_$][A-Za-z0-9_$-]*)(?:[?!])?[ \t]*:[ \t]*/g;
146
+ const TYPED_FIELD_MODIFIERS = [
147
+ "public",
148
+ "private",
149
+ "protected",
150
+ "static",
151
+ "readonly",
152
+ "declare",
153
+ "abstract",
154
+ "override",
155
+ ];
156
+ const MAX_TS_TYPE_ONLY_FIELD_CHARS = 4_096;
157
+ // Same composition as the per-call `new RegExp` the malformed-YAML span walk
158
+ // used to compile for every candidate value start.
159
+ const MALFORMED_YAML_CONTINUES_PATTERN = new RegExp(`^(?:&[A-Za-z0-9_.-]+[ \\t]+|!!?[^ \\t\\r\\n]+[ \\t]+|!<[^>\\r\\n]+>[ \\t]+)*${YAML_BLOCK_HEADER_PATTERN_SOURCE}(?:[ \\t]+#.*)?$`);
160
+ /**
161
+ * (start, brace) offset of every `class`/`interface`/`type` header, from a
162
+ * delimiter-aware linear scan equivalent to matchAll of
163
+ * /\b(?:class|interface|type)\s+[A-Za-z_$][A-Za-z0-9_$]*[^{};]*\{/g (the
164
+ * pattern's greedy `[^{};]*` backtracks one char at a time per candidate, so
165
+ * a header succeeds exactly when the first `{`/`}`/`;` after the ident is
166
+ * `{`; resolving that stopper through an amortized forward scan removed the
167
+ * quadratic blowup on dense failed-header bodies). Deliberately not
168
+ * quote-aware: matches inside
169
+ * strings and comments are golden-pinned semantics. `charge` gates the
170
+ * budget charges: the whole-text index build charges here, while the bounded
171
+ * lookback-slice fallback does not (its caller already charges the slice
172
+ * length, and the slice volume per body is a pre-existing property).
173
+ */
174
+ function collectTypeBodyHeaders(text, charge = true) {
175
+ const length = text.length;
176
+ const starts = [];
177
+ const braces = [];
178
+ if (charge) {
179
+ chargeWork(length);
180
+ }
181
+ // Stopper lookup (first `{`/`}`/`;` at or after a cursor) via one amortized
182
+ // forward scan: region [regionStart, regionStopper) provably holds no
183
+ // stopper, so every candidate cursor inside it reuses regionStopper — a
184
+ // dense run of failed candidates pays one scan, not one per candidate.
185
+ // The absent-stopper sentinel is regionStopper === length, so the
186
+ // text[stopper] === "{" success test fails naturally at EOF. No index
187
+ // array is allocated: the lookback-slice fallback below runs thousands of
188
+ // times per body on the dense shapes, and a per-call Int32Array plus full
189
+ // backward pass dominated the wall clock there.
190
+ let regionStart = 0;
191
+ let regionStopper = -1;
192
+ const stopperAfter = (from) => {
193
+ if (regionStart <= from && from <= regionStopper) {
194
+ return regionStopper;
195
+ }
196
+ let index = from;
197
+ while (index < length) {
198
+ const code = text.charCodeAt(index);
199
+ if (code === 0x7b || code === 0x7d || code === 0x3b) {
200
+ break;
201
+ }
202
+ index++;
203
+ }
204
+ regionStart = from;
205
+ regionStopper = index;
206
+ return index;
207
+ };
208
+ // Fresh locator per invocation: literal alternation plus lookahead, no
209
+ // backtracking structure. The regex \b stays the boundary authority (word
210
+ // chars are [A-Za-z0-9_], so `$class` matches while `xclass` and `Class`
211
+ // do not); lastIndex is managed by hand so a successful header can
212
+ // swallow its span the way matchAll's lastIndex jump did.
213
+ const locator = /\b(?:class|interface|type)(?=\s)/g;
214
+ let keyword;
215
+ while ((keyword = locator.exec(text)) != null) {
216
+ const start = keyword.index;
217
+ let cursor = start + keyword[0].length;
218
+ while (cursor < length && isJsWhitespaceChar(text[cursor] ?? "")) {
219
+ cursor++;
220
+ }
221
+ if (cursor >= length || !isIdentStartCode(text.charCodeAt(cursor))) {
222
+ if (charge) {
223
+ chargeWork(8);
224
+ }
225
+ continue;
226
+ }
227
+ const identStart = cursor;
228
+ while (cursor < length && isIdentCharCode(text.charCodeAt(cursor))) {
229
+ cursor++;
230
+ }
231
+ if (charge) {
232
+ chargeWork(8 + (cursor - identStart));
233
+ }
234
+ const stopper = stopperAfter(cursor);
235
+ // Failed candidates never jump the locator: it is already past the
236
+ // keyword, and the very next keyword may still succeed
237
+ // (`class A; type B {`). Jumping to the failure-stopper + 1 would also
238
+ // be safe; this scan commits to no-jump.
239
+ if (text.charCodeAt(stopper) === 0x7b) {
240
+ starts.push(start);
241
+ braces.push(stopper);
242
+ locator.lastIndex = stopper + 1;
243
+ }
244
+ }
245
+ return { starts, braces };
246
+ }
247
+ function typeBodyIndexFor(text) {
248
+ const analysis = analysisFor(text);
249
+ const cached = analysis?.typeBody;
250
+ if (cached != null) {
251
+ return cached;
252
+ }
253
+ // collectTypeBodyHeaders charges the amortized stopper scan and, per
254
+ // candidate, 8 + identLength — real work with honest budget interrupt
255
+ // points between candidates (plain JS checks, not event-loop yields). This
256
+ // charge covers the stack pass below; mid-enumeration exhaustion unwinds
257
+ // and fails closed instead of leaving a partial index in the session memo.
258
+ const headers = collectTypeBodyHeaders(text);
259
+ chargeWork(text.length);
260
+ const headerStarts = headers.starts;
261
+ const headerBraces = headers.braces;
262
+ const pastEnd = text.length + 1;
263
+ // Per-position close/exceed facts for every `{`, from one stack pass. The
264
+ // stack bases are strictly increasing (unique per open frame), so the frame
265
+ // that newly exceeds MAX_TS_TYPE_DEPTH when depth becomes d is exactly the
266
+ // one with base d - 33, found by binary search.
267
+ const closeAt = new Int32Array(text.length);
268
+ const exceedAt = new Int32Array(text.length);
269
+ closeAt.fill(pastEnd);
270
+ exceedAt.fill(pastEnd);
271
+ const stackPos = [];
272
+ const stackBase = [];
273
+ const stackExceed = [];
274
+ const stackHeaderSlot = [];
275
+ let depth = 0;
276
+ const headerSlots = new Map();
277
+ for (let slot = 0; slot < headerBraces.length; slot++) {
278
+ headerSlots.set(headerBraces[slot] ?? -1, slot);
279
+ }
280
+ for (let index = 0; index < text.length; index++) {
281
+ const code = text.charCodeAt(index);
282
+ if (code === 0x7b) {
283
+ stackPos.push(index);
284
+ stackBase.push(depth);
285
+ stackExceed.push(pastEnd);
286
+ stackHeaderSlot.push(headerSlots.get(index) ?? -1);
287
+ depth++;
288
+ const exceedBase = depth - (MAX_TS_TYPE_DEPTH + 1);
289
+ const frame = binarySearchExact(stackBase, exceedBase);
290
+ if (frame >= 0 && (stackExceed[frame] ?? pastEnd) === pastEnd) {
291
+ stackExceed[frame] = index + 1;
292
+ }
293
+ continue;
294
+ }
295
+ if (code === 0x7d) {
296
+ const pos = stackPos.pop();
297
+ const base = stackBase.pop();
298
+ const exceed = stackExceed.pop();
299
+ const headerSlot = stackHeaderSlot.pop();
300
+ depth--;
301
+ if (pos == null || base == null || exceed == null || headerSlot == null) {
302
+ continue;
303
+ }
304
+ closeAt[pos] = index + 1;
305
+ exceedAt[pos] = exceed;
306
+ continue;
307
+ }
308
+ }
309
+ // Frames never popped: closeAt stays pastEnd; keep any recorded exceed.
310
+ for (let frame = 0; frame < stackPos.length; frame++) {
311
+ const pos = stackPos[frame];
312
+ const exceed = stackExceed[frame];
313
+ if (pos == null || exceed == null) {
314
+ continue;
315
+ }
316
+ exceedAt[pos] = exceed;
317
+ }
318
+ const index = {
319
+ starts: Int32Array.from(headerStarts),
320
+ braces: Int32Array.from(headerBraces),
321
+ closeAt,
322
+ exceedAt,
323
+ pastEnd,
324
+ };
325
+ if (analysis != null) {
326
+ analysis.typeBody = index;
327
+ }
328
+ return index;
329
+ }
330
+ function typedFieldCandidatesFor(text) {
331
+ const analysis = analysisFor(text);
332
+ const cached = analysis?.typedFields;
333
+ if (cached != null) {
334
+ return cached;
335
+ }
336
+ chargeWork(2 * text.length);
337
+ const matchStarts = [];
338
+ const keyStarts = [];
339
+ const keyEnds = [];
340
+ const typeStarts = [];
341
+ for (const match of text.matchAll(TYPED_FIELD_PATTERN)) {
342
+ const rawKey = match[1] ?? "";
343
+ const matchStart = match.index ?? 0;
344
+ const keyStart = matchStart + match[0].lastIndexOf(rawKey);
345
+ matchStarts.push(matchStart);
346
+ keyStarts.push(keyStart);
347
+ keyEnds.push(keyStart + rawKey.length);
348
+ typeStarts.push(matchStart + match[0].length);
349
+ }
350
+ const candidates = {
351
+ matchStarts: Int32Array.from(matchStarts),
352
+ keyStarts: Int32Array.from(keyStarts),
353
+ keyEnds: Int32Array.from(keyEnds),
354
+ typeStarts: Int32Array.from(typeStarts),
355
+ count: matchStarts.length,
356
+ };
357
+ if (analysis != null) {
358
+ analysis.typedFields = candidates;
359
+ }
360
+ return candidates;
361
+ }
362
+ function newlineOffsetsFor(text) {
363
+ const analysis = analysisFor(text);
364
+ const cached = analysis?.newlineOffsets;
365
+ if (cached != null) {
366
+ return cached;
367
+ }
368
+ chargeWork(text.length);
369
+ const offsets = [];
370
+ for (let index = 0; index < text.length; index++) {
371
+ if (text.charCodeAt(index) === 0x0a) {
372
+ offsets.push(index);
373
+ }
374
+ }
375
+ const result = Int32Array.from(offsets);
376
+ if (analysis != null) {
377
+ analysis.newlineOffsets = result;
378
+ }
379
+ return result;
380
+ }
381
+ function nextSemicolonFor(text) {
382
+ const analysis = analysisFor(text);
383
+ const cached = analysis?.nextSemicolon;
384
+ if (cached != null) {
385
+ return cached;
386
+ }
387
+ chargeWork(text.length);
388
+ const result = new Int32Array(text.length + 1);
389
+ result.fill(-1);
390
+ for (let index = text.length - 1; index >= 0; index--) {
391
+ result[index] =
392
+ text.charCodeAt(index) === 0x3b ? index : result[index + 1] ?? -1;
393
+ }
394
+ if (analysis != null) {
395
+ analysis.nextSemicolon = result;
396
+ }
397
+ return result;
398
+ }
399
+ function yamlBlockHeaderIndexFor(text) {
400
+ const analysis = analysisFor(text);
401
+ const cached = analysis?.yamlBlockHeaders;
402
+ if (cached != null) {
403
+ return cached;
404
+ }
405
+ chargeWork(text.length);
406
+ const starts = [0];
407
+ for (let index = 0; index < text.length; index++) {
408
+ if (text.charCodeAt(index) === 0x0a) {
409
+ starts.push(index + 1);
410
+ }
411
+ }
412
+ const result = { lineStarts: Int32Array.from(starts) };
413
+ if (analysis != null) {
414
+ analysis.yamlBlockHeaders = result;
415
+ }
416
+ return result;
417
+ }
418
+ /** Rightmost index with values[index] < target (-1 when none). */
419
+ function binarySearchLastLessThan(values, target) {
420
+ let low = 0;
421
+ let high = values.length - 1;
422
+ let result = -1;
423
+ while (low <= high) {
424
+ const mid = (low + high) >>> 1;
425
+ if ((values[mid] ?? 0) < target) {
426
+ result = mid;
427
+ low = mid + 1;
428
+ }
429
+ else {
430
+ high = mid - 1;
431
+ }
432
+ }
433
+ return result;
434
+ }
435
+ /** Exact-match index in a sorted-ascending array (-1 when absent). */
436
+ function binarySearchExact(values, target) {
437
+ let low = 0;
438
+ let high = values.length - 1;
439
+ while (low <= high) {
440
+ const mid = (low + high) >>> 1;
441
+ const value = values[mid] ?? 0;
442
+ if (value === target) {
443
+ return mid;
444
+ }
445
+ if (value < target) {
446
+ low = mid + 1;
447
+ }
448
+ else {
449
+ high = mid - 1;
450
+ }
451
+ }
452
+ return -1;
453
+ }
454
+ function isJsWordCode(code) {
455
+ return ((code >= 0x30 && code <= 0x39) ||
456
+ (code >= 0x41 && code <= 0x5a) ||
457
+ (code >= 0x61 && code <= 0x7a) ||
458
+ code === 0x5f ||
459
+ code === 0x24);
460
+ }
461
+ function isIdentStartCode(code) {
462
+ return ((code >= 0x41 && code <= 0x5a) ||
463
+ (code >= 0x61 && code <= 0x7a) ||
464
+ code === 0x5f ||
465
+ code === 0x24);
466
+ }
467
+ function isIdentCharCode(code) {
468
+ return isJsWordCode(code);
469
+ }
470
+ function isJsWhitespaceChar(char) {
471
+ return /\s/.test(char);
472
+ }
473
+ function isLineTerminatorCode(code) {
474
+ return (code === 0x0a || code === 0x0d || code === 0x2028 || code === 0x2029);
475
+ }
476
+ /**
477
+ * Brace offset of a `class`/`interface`/`type` header starting exactly at
478
+ * `start` (the slice-relative `\b` window edge: a header beginning exactly at
479
+ * the lookback window start counts even when the preceding character would
480
+ * block the word boundary in the full text). `-1` when no header completes
481
+ * before `limit`.
482
+ */
483
+ function typeBodyHeaderBraceAt(text, start, limit) {
484
+ const keywordLength = text.startsWith("interface", start)
485
+ ? "interface".length
486
+ : text.startsWith("class", start)
487
+ ? "class".length
488
+ : text.startsWith("type", start)
489
+ ? "type".length
490
+ : -1;
491
+ if (keywordLength < 0) {
492
+ return -1;
493
+ }
494
+ let cursor = start + keywordLength;
495
+ if (cursor >= limit || !isJsWhitespaceChar(text[cursor] ?? "")) {
496
+ return -1;
497
+ }
498
+ while (cursor < limit && isJsWhitespaceChar(text[cursor] ?? "")) {
499
+ cursor++;
500
+ }
501
+ if (cursor >= limit || !isIdentStartCode(text.charCodeAt(cursor))) {
502
+ return -1;
503
+ }
504
+ cursor++;
505
+ while (cursor < limit && isIdentCharCode(text.charCodeAt(cursor))) {
506
+ cursor++;
507
+ }
508
+ // `[^{};]*` runs to the first stopper; only `{` can complete the header.
509
+ while (cursor < limit) {
510
+ const code = text.charCodeAt(cursor);
511
+ if (code === 0x7b) {
512
+ return cursor;
513
+ }
514
+ if (code === 0x7d || code === 0x3b) {
515
+ return -1;
516
+ }
517
+ cursor++;
518
+ }
519
+ return -1;
520
+ }
521
+ /**
522
+ * Index-backed equivalent of the legacy isInsideBoundedTypeBody(prefix,
523
+ * offset) where `prefix` was the caller's window slice starting at
524
+ * `windowStart` (0 for full-text callers). Falls back to the exact legacy
525
+ * slice scan when a header match straddles the window start — the one
526
+ * configuration where slice sub-matches exist that a whole-text matchAll
527
+ * never records.
528
+ */
529
+ function isInsideBoundedTypeBodyAt(text, windowStart, offset) {
530
+ const lo = Math.max(0, windowStart, offset - MAX_JS_KEY_LOOKBACK);
531
+ if (lo >= offset) {
532
+ return false;
533
+ }
534
+ const index = typeBodyIndexFor(text);
535
+ chargeWork(8);
536
+ const straddling = binarySearchLastLessThan(index.starts, lo);
537
+ if (straddling >= 0 &&
538
+ (index.braces[straddling] ?? -1) >= lo) {
539
+ chargeWork(offset - lo);
540
+ return isInsideBoundedTypeBodyBefore(text.slice(lo, offset));
541
+ }
542
+ let brace = -1;
543
+ const lastVisible = binarySearchLastLessThan(index.braces, offset);
544
+ if (lastVisible >= 0 &&
545
+ (index.starts[lastVisible] ?? -1) >= lo) {
546
+ brace = index.braces[lastVisible] ?? -1;
547
+ }
548
+ if (lo > 0 && isJsWordCode(text.charCodeAt(lo - 1))) {
549
+ const synthetic = typeBodyHeaderBraceAt(text, lo, offset);
550
+ if (synthetic > brace) {
551
+ brace = synthetic;
552
+ }
553
+ }
554
+ if (brace < 0) {
555
+ return false;
556
+ }
557
+ return ((index.closeAt[brace] ?? index.pastEnd) > offset &&
558
+ (index.exceedAt[brace] ?? index.pastEnd) > offset);
559
+ }
560
+ /** Exact legacy scan over an already-sliced lookback `before`. */
561
+ function isInsideBoundedTypeBodyBefore(before) {
562
+ // Shared header scan on the slice: offset 0 counts as a boundary, exactly
563
+ // the slice-relative \b semantics the slice's own matchAll had. Only the
564
+ // last header's brace feeds the depth walk (legacy last-match semantics).
565
+ // Uncharged: the caller already charges the slice length, and this fallback
566
+ // runs per straddling lookback query (thousands of 32 KiB slices on the
567
+ // dense shapes), so double-charging here would exhaust the budget.
568
+ const classBrace = collectTypeBodyHeaders(before, false).braces.at(-1) ?? -1;
569
+ if (classBrace < 0) {
570
+ return false;
571
+ }
572
+ let depth = 0;
573
+ for (let index = classBrace; index < before.length; index++) {
574
+ if (before[index] === "{") {
575
+ depth++;
576
+ }
577
+ else if (before[index] === "}") {
578
+ depth--;
579
+ if (depth === 0) {
580
+ return false;
581
+ }
582
+ }
583
+ if (depth > MAX_TS_TYPE_DEPTH) {
584
+ return false;
585
+ }
586
+ }
587
+ return depth > 0;
588
+ }
589
+ /**
590
+ * Parses `[ \t]*(modifier[ \t]+)*key[?!]?[ \t]*:[ \t]*` starting exactly at
591
+ * `start` — the typed-field pattern with the boundary alternative elided.
592
+ * This reproduces the regex's greedy modifier consumption with rightmost
593
+ * backtracking (e.g. `readonly : T` parses with key `readonly`), which is
594
+ * what a window slice's slice-relative `^` anchoring would see at position 0.
595
+ */
596
+ function matchTypedFieldContinuationAt(text, start, limit) {
597
+ let cursor = start;
598
+ while (cursor < limit &&
599
+ (text.charCodeAt(cursor) === 0x20 || text.charCodeAt(cursor) === 0x09)) {
600
+ cursor++;
601
+ }
602
+ const modifierStarts = [];
603
+ for (;;) {
604
+ let matched = -1;
605
+ for (const modifier of TYPED_FIELD_MODIFIERS) {
606
+ if (text.startsWith(modifier, cursor)) {
607
+ matched = modifier.length;
608
+ break;
609
+ }
610
+ }
611
+ if (matched < 0 ||
612
+ cursor + matched >= limit ||
613
+ (text.charCodeAt(cursor + matched) !== 0x20 &&
614
+ text.charCodeAt(cursor + matched) !== 0x09)) {
615
+ break;
616
+ }
617
+ modifierStarts.push(cursor);
618
+ cursor += matched;
619
+ while (cursor < limit &&
620
+ (text.charCodeAt(cursor) === 0x20 || text.charCodeAt(cursor) === 0x09)) {
621
+ cursor++;
622
+ }
623
+ }
624
+ for (;;) {
625
+ const keyStart = cursor;
626
+ if (keyStart < limit && isIdentStartCode(text.charCodeAt(keyStart))) {
627
+ let keyEnd = keyStart + 1;
628
+ // The regex key class is [A-Za-z0-9_$-]*: hyphens are legal key
629
+ // continuations (e.g. `api-key: string;`) even though they are not
630
+ // word characters.
631
+ while (keyEnd < limit &&
632
+ (isIdentCharCode(text.charCodeAt(keyEnd)) ||
633
+ text.charCodeAt(keyEnd) === 0x2d)) {
634
+ keyEnd++;
635
+ }
636
+ let afterKey = keyEnd;
637
+ const maybeOptional = text.charCodeAt(afterKey);
638
+ if (maybeOptional === 0x3f || maybeOptional === 0x21) {
639
+ afterKey++;
640
+ }
641
+ while (afterKey < limit &&
642
+ (text.charCodeAt(afterKey) === 0x20 ||
643
+ text.charCodeAt(afterKey) === 0x09)) {
644
+ afterKey++;
645
+ }
646
+ if (afterKey < limit && text.charCodeAt(afterKey) === 0x3a) {
647
+ let typeStart = afterKey + 1;
648
+ while (typeStart < limit &&
649
+ (text.charCodeAt(typeStart) === 0x20 ||
650
+ text.charCodeAt(typeStart) === 0x09)) {
651
+ typeStart++;
652
+ }
653
+ return { keyStart, keyEnd, typeStart };
654
+ }
655
+ }
656
+ if (modifierStarts.length === 0) {
657
+ return undefined;
658
+ }
659
+ cursor = modifierStarts.pop() ?? start;
660
+ }
661
+ }
662
+ /** Memoized whole-text comment mask; only called on body-level strings. */
663
+ function commentMaskedFor(text) {
664
+ const analysis = analysisFor(text);
665
+ const cached = analysis?.commentMasked;
666
+ if (cached != null) {
667
+ return cached;
668
+ }
669
+ chargeWork(2 * text.length);
670
+ const masked = maskJsCommentTrivia(text);
671
+ if (analysis != null) {
672
+ analysis.commentMasked = masked;
673
+ }
674
+ return masked;
675
+ }
14
676
  const PLACEHOLDER_VALUES = new Set([
15
677
  "changeme",
16
678
  "example",
@@ -66,6 +728,9 @@ export function scanSecretPath(rootRelativePath) {
66
728
  return dedupeFindings(findings);
67
729
  }
68
730
  export function scanHighConfidenceSecretContent(text) {
731
+ return withSecretScanSession(text, () => scanHighConfidenceSecretContentInSession(text), () => [budgetExhaustedFinding()]);
732
+ }
733
+ function scanHighConfidenceSecretContentInSession(text) {
69
734
  const findings = [];
70
735
  const privateKeyLine = findPatternLine(text, PRIVATE_KEY_BLOCK_PATTERN);
71
736
  if (privateKeyLine != null) {
@@ -77,20 +742,9 @@ export function scanHighConfidenceSecretContent(text) {
77
742
  findings.push(contentFinding(provenance, line));
78
743
  }
79
744
  }
80
- const lines = text.split(/\r?\n/);
81
- for (let index = 0; index < lines.length; index++) {
82
- const match = ASSIGNMENT_PATTERN.exec(lines[index] ?? "");
83
- if (match == null) {
84
- continue;
85
- }
86
- const value = match[3] ?? "";
87
- const quote = match[2] ?? "";
88
- if (quote.length === 0 && looksLikeCodeExpression(value)) {
89
- continue;
90
- }
91
- if (isHighConfidenceAssignedSecret(value)) {
92
- findings.push(contentFinding("assignment_secret", index + 1));
93
- break;
745
+ for (const assignment of iterateAssignmentSecrets(text)) {
746
+ if (assignment.confidence === "high") {
747
+ findings.push(contentFinding("assignment_secret", assignment.line));
94
748
  }
95
749
  }
96
750
  return dedupeFindings(findings);
@@ -98,25 +752,135 @@ export function scanHighConfidenceSecretContent(text) {
98
752
  export function isBlockingSecretFinding(finding) {
99
753
  return finding.provenance !== "assignment_secret";
100
754
  }
755
+ /**
756
+ * Budget exhaustion here fails closed by removing the whole body: a partial
757
+ * redaction cannot be proven complete.
758
+ */
101
759
  export function redactHighConfidenceAssignmentSecrets(text) {
102
- return text
103
- .split(/(\r?\n)/)
104
- .map((part) => {
105
- if (part === "\n" || part === "\r\n") {
106
- return part;
760
+ return withSecretScanSession(text, () => redactHighConfidenceAssignmentSecretsInSession(text), () => "");
761
+ }
762
+ function redactHighConfidenceAssignmentSecretsInSession(text) {
763
+ const secretAssignments = [...iterateAssignmentSecrets(text)].filter((assignment) => assignment.confidence === "high").sort((left, right) => left.valueStart - right.valueStart);
764
+ let redacted = text;
765
+ for (let index = secretAssignments.length - 1; index >= 0; index--) {
766
+ const assignment = secretAssignments[index];
767
+ if (assignment == null) {
768
+ continue;
107
769
  }
108
- return redactAssignmentSecretLine(part);
109
- })
110
- .join("");
770
+ redacted = `${redacted.slice(0, assignment.valueStart)}${assignment.replacement ?? REDACTION_MARKER}${redacted.slice(assignment.valueEnd)}`;
771
+ }
772
+ return redacted;
111
773
  }
112
- export function assertNoHighConfidenceSecretsInTask(task) {
113
- const findings = scanHighConfidenceSecretContent(task);
774
+ /**
775
+ * Sanitizes one bounded text body. The residual callback is deliberately a
776
+ * separate lexical detector and runs for every opt-out sanitization, including
777
+ * content the primary assignment grammar did not classify.
778
+ */
779
+ export function sanitizeHighConfidenceSecretContent(text, residualDetector = hasConservativeResidualSecret) {
780
+ return withSecretScanSession(text, () => sanitizeHighConfidenceSecretContentInSession(text, residualDetector), () => ({ status: "blocked", content: "", findings: [budgetExhaustedFinding()] }));
781
+ }
782
+ function sanitizeHighConfidenceSecretContentInSession(text, residualDetector) {
783
+ const findings = scanHighConfidenceSecretContent(text);
784
+ if (findings.some(isBlockingSecretFinding)) {
785
+ return { status: "blocked", content: "", findings };
786
+ }
114
787
  if (findings.length === 0) {
788
+ if (residualDetector(text)) {
789
+ return {
790
+ status: "blocked",
791
+ content: "",
792
+ findings: [contentFinding("residual_secret", 1)],
793
+ };
794
+ }
795
+ return { status: "unchanged", content: text, findings };
796
+ }
797
+ const content = redactHighConfidenceAssignmentSecrets(text);
798
+ if (residualDetector(content)) {
799
+ return {
800
+ status: "blocked",
801
+ content: "",
802
+ findings: [contentFinding("residual_secret", 1)],
803
+ };
804
+ }
805
+ return { status: "redacted", content, findings };
806
+ }
807
+ /**
808
+ * Independent fail-closed backstop for every opt-out content body. This does
809
+ * not call the assignment iterator: it recognizes sensitive key contexts and
810
+ * YAML block continuations with a separate, intentionally conservative lexer.
811
+ */
812
+ export function hasConservativeResidualSecret(text) {
813
+ return withSecretScanSession(text, () => hasConservativeResidualSecretInSession(text), () => true);
814
+ }
815
+ function hasConservativeResidualSecretInSession(text) {
816
+ chargeWork(text.length * (1 + WELL_KNOWN_TOKEN_PATTERNS.length));
817
+ if (PRIVATE_KEY_BLOCK_PATTERN.test(text)) {
818
+ return true;
819
+ }
820
+ for (const { pattern } of WELL_KNOWN_TOKEN_PATTERNS) {
821
+ pattern.lastIndex = 0;
822
+ if (pattern.test(text)) {
823
+ return true;
824
+ }
825
+ }
826
+ const yaml = analyzeYamlStructuralSecrets(text);
827
+ if (yaml.unsafeSecretRange) {
828
+ return true;
829
+ }
830
+ if (yaml.parseFailed && hasMalformedYamlSensitiveSecret(text)) {
831
+ return true;
832
+ }
833
+ if (yaml.recognized) {
834
+ return yaml.matches.some((match) => match.confidence === "high");
835
+ }
836
+ const js = analyzeJsStructuralSecrets(text, false);
837
+ if (js.unsafeSensitiveContext ||
838
+ js.matches.some((match) => match.confidence === "high")) {
839
+ return true;
840
+ }
841
+ return [...iterateAssignmentSecrets(text)].some((match) => match.confidence === "high");
842
+ }
843
+ export function hasDisallowedSecretMaterial(text, allowSecrets) {
844
+ if (allowSecrets === true) {
845
+ return false;
846
+ }
847
+ return withSecretScanSession(text, () => hasHighConfidenceSecret(text) || hasConservativeResidualSecret(text), () => true);
848
+ }
849
+ export function assertNoHighConfidenceSecretsInTask(task) {
850
+ if (!hasDisallowedSecretMaterial(task, false)) {
115
851
  return;
116
852
  }
117
- const provenances = findings.map((finding) => finding.provenance).join(", ");
853
+ const findings = scanHighConfidenceSecretContent(task);
854
+ const provenances = findings.length === 0
855
+ ? "residual_secret"
856
+ : findings.map((finding) => finding.provenance).join(", ");
118
857
  throw new InvalidParamsError(`context_builder task contains high-confidence secret material (${provenances})`);
119
858
  }
859
+ export function assertTaskSecretsGated(task, allowSecrets) {
860
+ if (hasDisallowedSecretMaterial(task, allowSecrets)) {
861
+ assertNoHighConfidenceSecretsInTask(task);
862
+ }
863
+ }
864
+ export function resolveAllowSecrets(explicit) {
865
+ if (typeof explicit === "boolean") {
866
+ return explicit;
867
+ }
868
+ const raw = process.env.CLINKX_CONTEXT_ALLOW_SECRETS;
869
+ if (raw == null) {
870
+ return true;
871
+ }
872
+ const value = raw.trim().toLowerCase();
873
+ if (value === "") {
874
+ return true;
875
+ }
876
+ if (value === "false" || value === "0" || value === "no" || value === "off") {
877
+ return false;
878
+ }
879
+ if (value === "true" || value === "1" || value === "yes" || value === "on") {
880
+ return true;
881
+ }
882
+ throw new InvalidParamsError("CLINKX_CONTEXT_ALLOW_SECRETS must be one of true/false/1/0/yes/no/on/off (case-insensitive)");
883
+ }
120
884
  export function hasHighConfidenceSecret(text) {
121
885
  return scanHighConfidenceSecretContent(text).length > 0;
122
886
  }
@@ -148,6 +912,7 @@ function isSecretDirectorySegment(segment) {
148
912
  segment === "credentials");
149
913
  }
150
914
  function findPatternLine(text, pattern) {
915
+ chargeWork(2 * text.length);
151
916
  const lines = text.split(/\r?\n/);
152
917
  for (let index = 0; index < lines.length; index++) {
153
918
  if (pattern.test(lines[index] ?? "")) {
@@ -157,9 +922,86 @@ function findPatternLine(text, pattern) {
157
922
  return undefined;
158
923
  }
159
924
  function looksLikeCodeExpression(value) {
160
- return (/^[A-Za-z_][A-Za-z0-9_:.]*\([^"'`]*\)$/.test(value) ||
161
- /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_.]*$/.test(value) ||
162
- /^[A-Za-z_][A-Za-z0-9_]*_[A-Za-z0-9_]*$/.test(value));
925
+ const expression = unwrapReferenceExpression(value);
926
+ const plainReference = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(expression);
927
+ const explicitlyNamedReference = /(?:reference|ref|source|provider)[0-9]*$/i.test(expression);
928
+ const memberReference = /^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:::|\.)[A-Za-z_$][A-Za-z0-9_$]*)+$/.test(expression);
929
+ return (looksLikeTsTypeExpression(expression) ||
930
+ /^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:::|\.)[A-Za-z_$][A-Za-z0-9_$]*)*\([^"'`\r\n]*\)$/.test(expression) ||
931
+ (plainReference &&
932
+ !SENSITIVE_ASSIGNMENT_KEY_PATTERN.test(expression) &&
933
+ (explicitlyNamedReference ||
934
+ !isHighConfidenceAssignedSecret(expression))) ||
935
+ (memberReference && !looksLikeCredentialShapedDottedLiteral(expression)) ||
936
+ (/^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\?\.|\.)[A-Za-z_$][A-Za-z0-9_$]*(?:\([^"'`\r\n]*\))?|\[[A-Za-z_$][A-Za-z0-9_$]*\])+(?:\?\.[A-Za-z_$][A-Za-z0-9_$]*\([^"'`\r\n]*\))*$/.test(expression) && !looksLikeCredentialShapedDottedLiteral(expression)) ||
937
+ /^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\?\.|\.)[A-Za-z_$][A-Za-z0-9_$]*(?:\([^"'`\r\n]*\))?|\[[A-Za-z_$][A-Za-z0-9_$]*\])*(?:\?\.[A-Za-z_$][A-Za-z0-9_$]*\([^"'`\r\n]*\))*\s*(?:\?\?|\|\|)\s*(?:["']{2}|undefined|null);?$/.test(expression) ||
938
+ /^(?:\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}\r\n]*)?\}|\$[A-Za-z_][A-Za-z0-9_]*)$/.test(expression));
939
+ }
940
+ function looksLikeTsTypeExpression(value, allowNamedType = false) {
941
+ const type = value.trim().replace(/;\s*$/, "").trim();
942
+ if (type.length === 0 ||
943
+ /["'`=]/.test(type) ||
944
+ classifyTsTypeAnnotation(type) !== "valid") {
945
+ return false;
946
+ }
947
+ return (/\b(?:any|bigint|boolean|never|null|number|object|string|symbol|undefined|unknown|void)\b/.test(type) ||
948
+ /\b(?:keyof|readonly|typeof)\b/.test(type) ||
949
+ /[<>{}\[\]|&?]/.test(type) ||
950
+ (allowNamedType &&
951
+ /^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\.|::)[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(type)));
952
+ }
953
+ function unwrapReferenceExpression(value) {
954
+ let expression = value.trim();
955
+ for (let count = 0; count < 16; count++) {
956
+ const before = expression;
957
+ expression = expression
958
+ .replace(/\s+(?:as|satisfies)\s+(?:const|[A-Za-z_$][A-Za-z0-9_$]*(?:\s*<[^<>\r\n]+>)?)$/, "")
959
+ .trim();
960
+ if (hasBalancedOuterParentheses(expression)) {
961
+ expression = expression.slice(1, -1).trim();
962
+ }
963
+ expression = expression
964
+ .replace(/^<[A-Za-z_$][A-Za-z0-9_$]*(?:<[^<>\r\n]+>)?>\s*/, "")
965
+ .trim();
966
+ if (expression === before) {
967
+ break;
968
+ }
969
+ }
970
+ return expression;
971
+ }
972
+ function hasBalancedOuterParentheses(value) {
973
+ if (!value.startsWith("(") || !value.endsWith(")")) {
974
+ return false;
975
+ }
976
+ let depth = 0;
977
+ for (let index = 0; index < value.length; index++) {
978
+ if (value[index] === "(") {
979
+ depth++;
980
+ }
981
+ else if (value[index] === ")") {
982
+ depth--;
983
+ if (depth === 0 && index < value.length - 1) {
984
+ return false;
985
+ }
986
+ if (depth < 0) {
987
+ return false;
988
+ }
989
+ }
990
+ }
991
+ return depth === 0;
992
+ }
993
+ function looksLikeCredentialShapedDottedLiteral(value) {
994
+ const segments = value.split(".");
995
+ if (segments.length < 2) {
996
+ return false;
997
+ }
998
+ if (/^(?:sk|pk|rk|pat)$/i.test(segments[0] ?? "")) {
999
+ return true;
1000
+ }
1001
+ const terminal = segments.at(-1) ?? "";
1002
+ return (terminal.length >= 16 &&
1003
+ !terminal.includes("_") &&
1004
+ isHighConfidenceAssignedSecret(terminal));
163
1005
  }
164
1006
  function isHighConfidenceAssignedSecret(value) {
165
1007
  const normalized = value.trim().toLowerCase();
@@ -169,6 +1011,12 @@ function isHighConfidenceAssignedSecret(value) {
169
1011
  if (PLACEHOLDER_VALUES.has(normalized)) {
170
1012
  return false;
171
1013
  }
1014
+ if (normalized === REDACTION_MARKER) {
1015
+ return false;
1016
+ }
1017
+ if (normalized === REDACTION_MARKER.slice(1, -1)) {
1018
+ return false;
1019
+ }
172
1020
  if (/^(?:x+|\*+|0+|1+|a+)$/.test(normalized)) {
173
1021
  return false;
174
1022
  }
@@ -177,24 +1025,1387 @@ function isHighConfidenceAssignedSecret(value) {
177
1025
  }
178
1026
  return shannonEntropy(value) >= 3;
179
1027
  }
180
- function redactAssignmentSecretLine(line) {
181
- const match = ASSIGNMENT_PATTERN.exec(line);
182
- if (match == null) {
183
- return line;
1028
+ function isSensitiveDecodedKey(value) {
1029
+ const key = value.trim();
1030
+ const terminal = key.split(".").at(-1) ?? key;
1031
+ return [key, terminal].some((candidate) => /^[A-Za-z_$][A-Za-z0-9_$-]*$/.test(candidate) &&
1032
+ SENSITIVE_ASSIGNMENT_KEY_PATTERN.test(candidate));
1033
+ }
1034
+ function isRedactionMarkerValue(value) {
1035
+ const normalized = value.trim();
1036
+ return (normalized.length > 0 &&
1037
+ (normalized.replaceAll(REDACTION_MARKER, "").trim().length === 0 ||
1038
+ normalized
1039
+ .replaceAll(REDACTION_MARKER.slice(1, -1), "")
1040
+ .trim().length === 0));
1041
+ }
1042
+ function analyzeYamlStructuralSecrets(text) {
1043
+ const analysis = analysisFor(text);
1044
+ const cached = analysis?.yaml;
1045
+ if (cached != null) {
1046
+ return cached;
1047
+ }
1048
+ const result = analyzeYamlStructuralSecretsUncached(text);
1049
+ if (analysis != null) {
1050
+ analysis.yaml = result;
1051
+ }
1052
+ return result;
1053
+ }
1054
+ function analyzeYamlStructuralSecretsUncached(text) {
1055
+ chargeWork(text.length);
1056
+ if (text.length > MAX_YAML_SOURCE_CHARS) {
1057
+ return { ...emptyYamlStructuralAnalysis(), recognized: true, exhausted: true };
1058
+ }
1059
+ let documents;
1060
+ try {
1061
+ documents = parseAllDocuments(text, {
1062
+ prettyErrors: false,
1063
+ strict: false,
1064
+ uniqueKeys: false,
1065
+ });
1066
+ }
1067
+ catch {
1068
+ return { ...emptyYamlStructuralAnalysis(), parseFailed: true };
1069
+ }
1070
+ if (documents.some((document) => document.errors.length > 0)) {
1071
+ return {
1072
+ ...emptyYamlStructuralAnalysis(),
1073
+ parseFailed: true,
1074
+ };
1075
+ }
1076
+ if (documents.length === 0 || documents.length > MAX_YAML_DOCUMENTS) {
1077
+ return {
1078
+ ...emptyYamlStructuralAnalysis(),
1079
+ exhausted: documents.length > MAX_YAML_DOCUMENTS,
1080
+ };
1081
+ }
1082
+ const collectionDocument = documents.some((document) => isMap(document.contents) || isSeq(document.contents));
1083
+ if (!collectionDocument) {
1084
+ return emptyYamlStructuralAnalysis();
1085
+ }
1086
+ const analysis = {
1087
+ recognized: false,
1088
+ exhausted: false,
1089
+ unsafeSecretRange: false,
1090
+ parseFailed: false,
1091
+ matches: [],
1092
+ };
1093
+ let hasSensitiveKey = false;
1094
+ let nodesSeen = 0;
1095
+ let currentDocument = documents[0];
1096
+ const resolveMappingKeyScalar = (node) => {
1097
+ let current = node;
1098
+ const active = new Set();
1099
+ for (let depth = 0; depth <= MAX_YAML_DEPTH; depth++) {
1100
+ chargeWork(1);
1101
+ if (current == null || ++nodesSeen > MAX_YAML_NODES) {
1102
+ analysis.exhausted = true;
1103
+ return undefined;
1104
+ }
1105
+ if (isScalar(current)) {
1106
+ return current;
1107
+ }
1108
+ if (!isAlias(current) || active.has(current) || currentDocument == null) {
1109
+ analysis.exhausted = true;
1110
+ return undefined;
1111
+ }
1112
+ active.add(current);
1113
+ current = current.resolve(currentDocument);
1114
+ }
1115
+ analysis.exhausted = true;
1116
+ return undefined;
1117
+ };
1118
+ const visit = (node, sensitiveAncestor, depth, unsafeScalarRange) => {
1119
+ if (node == null || analysis.exhausted) {
1120
+ return;
1121
+ }
1122
+ chargeWork(1);
1123
+ nodesSeen++;
1124
+ if (nodesSeen > MAX_YAML_NODES || depth > MAX_YAML_DEPTH) {
1125
+ analysis.exhausted = true;
1126
+ return;
1127
+ }
1128
+ if (isMap(node)) {
1129
+ for (const pair of node.items) {
1130
+ const pairKey = pair.key;
1131
+ if (sensitiveAncestor) {
1132
+ visit(pairKey, true, depth + 1, false);
1133
+ }
1134
+ const keyNode = isScalar(pairKey)
1135
+ ? pairKey
1136
+ : isAlias(pairKey)
1137
+ ? resolveMappingKeyScalar(pairKey)
1138
+ : undefined;
1139
+ if (isAlias(pairKey) && keyNode == null) {
1140
+ return;
1141
+ }
1142
+ const decodedKey = isScalar(keyNode)
1143
+ ? String(keyNode.value ?? "")
1144
+ : undefined;
1145
+ const sensitiveKey = decodedKey != null && isSensitiveDecodedKey(decodedKey);
1146
+ hasSensitiveKey ||= sensitiveKey;
1147
+ const pairValue = pair.value;
1148
+ const keyEnd = pairKey?.range?.[1];
1149
+ const valueStart = pairValue?.range?.[0];
1150
+ const valueGap = keyEnd == null || valueStart == null
1151
+ ? ""
1152
+ : text.slice(keyEnd, valueStart);
1153
+ if (sensitiveKey &&
1154
+ (valueGap.match(/\n/g)?.length ?? 0) > 256) {
1155
+ if (isScalar(pairValue)) {
1156
+ visit(pairValue, true, depth + 1, true);
1157
+ }
1158
+ analysis.exhausted = true;
1159
+ return;
1160
+ }
1161
+ visit(pairValue, sensitiveAncestor || sensitiveKey, depth + 1, sensitiveKey &&
1162
+ isScalar(pairValue) &&
1163
+ pairValue.type === "PLAIN" &&
1164
+ /\r|\n/.test(valueGap));
1165
+ }
1166
+ return;
1167
+ }
1168
+ if (isSeq(node)) {
1169
+ for (const item of node.items) {
1170
+ visit(item, sensitiveAncestor, depth + 1, false);
1171
+ }
1172
+ return;
1173
+ }
1174
+ if (!sensitiveAncestor) {
1175
+ return;
1176
+ }
1177
+ if (isAlias(node)) {
1178
+ if (currentDocument == null) {
1179
+ analysis.exhausted = true;
1180
+ return;
1181
+ }
1182
+ const referenceStatus = yamlReferenceTreeStatus(node, currentDocument);
1183
+ if (referenceStatus === "safe") {
1184
+ return;
1185
+ }
1186
+ if (referenceStatus === "secret") {
1187
+ analysis.unsafeSecretRange = true;
1188
+ }
1189
+ else {
1190
+ analysis.exhausted = true;
1191
+ }
1192
+ return;
1193
+ }
1194
+ if (!isScalar(node)) {
1195
+ return;
1196
+ }
1197
+ let value;
1198
+ if (typeof node.value === "string") {
1199
+ value = node.value;
1200
+ }
1201
+ else if (node.value instanceof Uint8Array) {
1202
+ const decoded = new TextDecoder("utf-8", { fatal: false }).decode(node.value);
1203
+ const encoded = typeof node.source === "string" ? node.source : "";
1204
+ value = isHighConfidenceAssignedSecret(decoded) ? decoded : encoded;
1205
+ }
1206
+ else if (node.value == null ||
1207
+ typeof node.value === "number" ||
1208
+ typeof node.value === "boolean") {
1209
+ return;
1210
+ }
1211
+ else {
1212
+ analysis.exhausted = true;
1213
+ return;
1214
+ }
1215
+ if (isRedactionMarkerValue(value) ||
1216
+ (node.type === "PLAIN" && looksLikeCodeExpression(value.trim())) ||
1217
+ !isHighConfidenceAssignedSecret(value)) {
1218
+ return;
1219
+ }
1220
+ const range = node.range;
1221
+ const valueStart = range?.[0];
1222
+ const valueEnd = range?.[1];
1223
+ if (valueStart == null ||
1224
+ valueEnd == null ||
1225
+ valueStart < 0 ||
1226
+ valueEnd <= valueStart ||
1227
+ valueEnd > text.length) {
1228
+ analysis.unsafeSecretRange = true;
1229
+ return;
1230
+ }
1231
+ if (unsafeScalarRange) {
1232
+ analysis.unsafeSecretRange = true;
1233
+ return;
1234
+ }
1235
+ analysis.matches.push({
1236
+ keyStart: valueStart,
1237
+ keyEnd: valueStart,
1238
+ valueStart,
1239
+ valueEnd,
1240
+ line: lineNumberAtOffset(text, valueStart),
1241
+ confidence: "high",
1242
+ replacement: redactYamlScalarSource(text.slice(valueStart, valueEnd)),
1243
+ });
1244
+ };
1245
+ for (const document of documents) {
1246
+ currentDocument = document;
1247
+ visit(document.contents, false, 0, false);
1248
+ }
1249
+ analysis.recognized = hasSensitiveKey;
1250
+ return analysis;
1251
+ }
1252
+ function yamlReferenceTreeStatus(node, document) {
1253
+ const active = new Set();
1254
+ const memoizedSafe = new Set();
1255
+ let remaining = MAX_YAML_NODES;
1256
+ const visit = (current, depth) => {
1257
+ if (current == null || depth > MAX_YAML_DEPTH) {
1258
+ return "incomplete";
1259
+ }
1260
+ if (memoizedSafe.has(current)) {
1261
+ return "safe";
1262
+ }
1263
+ if (--remaining < 0 || active.has(current)) {
1264
+ return "incomplete";
1265
+ }
1266
+ active.add(current);
1267
+ let status;
1268
+ if (isAlias(current)) {
1269
+ const resolved = current.resolve(document);
1270
+ status = visit(resolved, depth + 1);
1271
+ }
1272
+ else if (isSeq(current)) {
1273
+ status = combineYamlReferenceStatuses(current.items.map((item) => visit(item, depth + 1)));
1274
+ }
1275
+ else if (isMap(current)) {
1276
+ status = combineYamlReferenceStatuses(current.items.flatMap((pair) => [
1277
+ visit(pair.key, depth + 1),
1278
+ visit(pair.value, depth + 1),
1279
+ ]));
1280
+ }
1281
+ else if (!isScalar(current)) {
1282
+ status = "incomplete";
1283
+ }
1284
+ else if (current.value == null ||
1285
+ typeof current.value === "number" ||
1286
+ typeof current.value === "boolean") {
1287
+ status = "safe";
1288
+ }
1289
+ else if (current.value instanceof Uint8Array) {
1290
+ const decoded = new TextDecoder("utf-8", { fatal: false }).decode(current.value);
1291
+ const encoded = typeof current.source === "string" ? current.source : "";
1292
+ status = isHighConfidenceAssignedSecret(decoded) ||
1293
+ isHighConfidenceAssignedSecret(encoded)
1294
+ ? "secret"
1295
+ : "safe";
1296
+ }
1297
+ else if (typeof current.value !== "string") {
1298
+ status = "incomplete";
1299
+ }
1300
+ else {
1301
+ const safe = isRedactionMarkerValue(current.value) ||
1302
+ !isHighConfidenceAssignedSecret(current.value) ||
1303
+ (current.type === "PLAIN" &&
1304
+ looksLikeCodeExpression(current.value.trim()));
1305
+ status = safe ? "safe" : "secret";
1306
+ }
1307
+ active.delete(current);
1308
+ if (status === "safe") {
1309
+ memoizedSafe.add(current);
1310
+ }
1311
+ return status;
1312
+ };
1313
+ return visit(node, 0);
1314
+ }
1315
+ function combineYamlReferenceStatuses(statuses) {
1316
+ if (statuses.includes("secret")) {
1317
+ return "secret";
1318
+ }
1319
+ return statuses.includes("incomplete") ? "incomplete" : "safe";
1320
+ }
1321
+ function emptyYamlStructuralAnalysis() {
1322
+ return {
1323
+ recognized: false,
1324
+ exhausted: false,
1325
+ parseFailed: false,
1326
+ unsafeSecretRange: false,
1327
+ matches: [],
1328
+ };
1329
+ }
1330
+ function hasMalformedYamlSensitiveSecret(text) {
1331
+ const analysis = analysisFor(text);
1332
+ const cached = analysis?.malformedYamlSecret;
1333
+ if (cached != null) {
1334
+ return cached;
1335
+ }
1336
+ let result = false;
1337
+ // Valid TypeScript class fields are not YAML merely because a nested type
1338
+ // property has a sensitive name. Remove only declarations whose bounded RHS
1339
+ // is an unambiguous reference; literal/object RHS values remain visible to
1340
+ // the fail-closed malformed-content check below.
1341
+ const withoutSafeTypedFields = maskConfidentlySafeTypeOnlyFields(maskConfidentlySafeTypedFields(text));
1342
+ for (const valueStart of malformedYamlSensitiveValueStarts(withoutSafeTypedFields)) {
1343
+ const span = malformedYamlValueSpan(withoutSafeTypedFields, valueStart);
1344
+ const expression = span.trim().replace(/,$/, "").trim();
1345
+ if (looksLikeCodeExpression(expression)) {
1346
+ continue;
1347
+ }
1348
+ if (spanContainsHighEntropyLiteral(span)) {
1349
+ result = true;
1350
+ break;
1351
+ }
1352
+ }
1353
+ if (analysis != null) {
1354
+ analysis.malformedYamlSecret = result;
1355
+ }
1356
+ return result;
1357
+ }
1358
+ function malformedYamlValueSpan(text, valueStart) {
1359
+ const { lineStarts } = yamlBlockHeaderIndexFor(text);
1360
+ const lineSlot = binarySearchLastLessThan(lineStarts, valueStart + 1);
1361
+ let lineIndex = lineSlot < 0 ? 0 : lineSlot;
1362
+ const firstLineEnd = lineEndViaStarts(text, lineStarts, lineIndex);
1363
+ const firstLine = text.slice(valueStart, firstLineEnd);
1364
+ const firstValue = firstLine.replace(/^[ \t]*/, "");
1365
+ const continues = firstValue.length === 0 ||
1366
+ firstValue.startsWith("#") ||
1367
+ MALFORMED_YAML_CONTINUES_PATTERN.test(firstValue);
1368
+ if (!continues) {
1369
+ return firstLine;
1370
+ }
1371
+ const keyLineStart = lineStarts[lineIndex] ?? 0;
1372
+ const keyIndent = leadingIndentWidth(text.slice(keyLineStart, valueStart));
1373
+ let cursor = firstLineEnd;
1374
+ let end = firstLineEnd;
1375
+ let lines = 0;
1376
+ while (cursor < text.length && lines <= 512 && cursor - valueStart < 32_768) {
1377
+ chargeWork(1);
1378
+ if (lineIndex + 1 >= lineStarts.length) {
1379
+ break;
1380
+ }
1381
+ const lineStart = lineStarts[lineIndex + 1] ?? text.length;
1382
+ if (text.charCodeAt(cursor) === 0x0d) {
1383
+ if (text.charCodeAt(cursor + 1) !== 0x0a) {
1384
+ break;
1385
+ }
1386
+ }
1387
+ else if (text.charCodeAt(cursor) !== 0x0a) {
1388
+ break;
1389
+ }
1390
+ const lineEnd = lineEndViaStarts(text, lineStarts, lineIndex + 1);
1391
+ const line = text.slice(lineStart, lineEnd);
1392
+ if (line.trim().length > 0 && leadingIndentWidth(line) <= keyIndent) {
1393
+ break;
1394
+ }
1395
+ end = lineEnd;
1396
+ cursor = lineEnd;
1397
+ lines++;
1398
+ lineIndex++;
1399
+ }
1400
+ return text.slice(valueStart, end);
1401
+ }
1402
+ /** End offset (exclusive of the line terminator) of line `lineIndex`. */
1403
+ function lineEndViaStarts(text, lineStarts, lineIndex) {
1404
+ if (lineIndex + 1 >= lineStarts.length) {
1405
+ return text.length;
1406
+ }
1407
+ const nextStart = lineStarts[lineIndex + 1] ?? text.length;
1408
+ const newline = nextStart - 1;
1409
+ return newline > 0 && text.charCodeAt(newline - 1) === 0x0d
1410
+ ? newline - 1
1411
+ : newline;
1412
+ }
1413
+ function maskConfidentlySafeTypeOnlyFields(text) {
1414
+ // Manual linear scan reproducing
1415
+ // /(^|[;{}\r\n])(prefix)([\s\S]{1,4096}?);/gm.replace semantics exactly:
1416
+ // `^` only at line starts, `;`-terminated lazy type bodies resolved through
1417
+ // the next-semicolon index, and the scan resuming after each match's `;`
1418
+ // whether or not the match was rewritten.
1419
+ const nextSemicolon = nextSemicolonFor(text);
1420
+ const isTypeBodyBoundaryChar = (code) => code === 0x3b ||
1421
+ code === 0x7b ||
1422
+ code === 0x7d ||
1423
+ code === 0x0d ||
1424
+ code === 0x0a;
1425
+ let output = "";
1426
+ let copied = 0;
1427
+ let position = 0;
1428
+ while (position < text.length) {
1429
+ chargeWork(1);
1430
+ const atLineStart = position === 0 || isLineTerminatorCode(text.charCodeAt(position - 1));
1431
+ let boundaryLength = -1;
1432
+ let continuation;
1433
+ if (atLineStart) {
1434
+ continuation = matchTypedFieldContinuationAt(text, position, text.length);
1435
+ if (continuation != null) {
1436
+ boundaryLength = 0;
1437
+ }
1438
+ }
1439
+ if (boundaryLength < 0 && isTypeBodyBoundaryChar(text.charCodeAt(position))) {
1440
+ const afterBoundary = matchTypedFieldContinuationAt(text, position + 1, text.length);
1441
+ if (afterBoundary != null) {
1442
+ boundaryLength = 1;
1443
+ continuation = afterBoundary;
1444
+ }
1445
+ }
1446
+ if (boundaryLength < 0 || continuation == null) {
1447
+ position++;
1448
+ continue;
1449
+ }
1450
+ const typeStart = continuation.typeStart;
1451
+ const terminator = typeStart + 1 <= text.length ? nextSemicolon[typeStart + 1] ?? -1 : -1;
1452
+ if (terminator < 0 || terminator - typeStart > MAX_TS_TYPE_ONLY_FIELD_CHARS) {
1453
+ // The lazy group cannot reach a `;` in range. Prefix backtracking never
1454
+ // rescues the match: a shorter modifier run leaves a keyword followed by
1455
+ // whitespace, which the key production cannot re-parse.
1456
+ position++;
1457
+ continue;
1458
+ }
1459
+ const type = text.slice(typeStart, terminator);
1460
+ if (isInsideBoundedTypeBodyAt(text, 0, position + boundaryLength) &&
1461
+ looksLikeTsTypeExpression(type, true)) {
1462
+ output += text.slice(copied, position);
1463
+ output += text.slice(position, position + boundaryLength);
1464
+ for (let index = position + boundaryLength; index <= terminator; index++) {
1465
+ const code = text.charCodeAt(index);
1466
+ output += code === 0x0a || code === 0x0d ? text[index] ?? "" : " ";
1467
+ }
1468
+ copied = terminator + 1;
1469
+ }
1470
+ position = terminator + 1;
1471
+ }
1472
+ return output + text.slice(copied);
1473
+ }
1474
+ function maskConfidentlySafeTypedFields(text) {
1475
+ const masked = commentMaskedFor(text);
1476
+ const safeSpans = [];
1477
+ let quote;
1478
+ let escaped = false;
1479
+ for (let equalsAt = 0; equalsAt < masked.length; equalsAt++) {
1480
+ chargeWork(1);
1481
+ const character = masked[equalsAt] ?? "";
1482
+ if (quote != null) {
1483
+ if (!escaped && character === quote) {
1484
+ quote = undefined;
1485
+ }
1486
+ escaped = !escaped && character === "\\";
1487
+ continue;
1488
+ }
1489
+ if (character === '"' || character === "'" || character === "`") {
1490
+ quote = character;
1491
+ escaped = false;
1492
+ continue;
1493
+ }
1494
+ if (character !== "=" ||
1495
+ /[=!<>]/.test(masked[equalsAt - 1] ?? "") ||
1496
+ /[=>]/.test(masked[equalsAt + 1] ?? "")) {
1497
+ continue;
1498
+ }
1499
+ chargeWork(32);
1500
+ const windowStart = Math.max(0, equalsAt - MAX_JS_KEY_LOOKBACK);
1501
+ const field = findBoundedTypedField(masked, windowStart, equalsAt);
1502
+ if (field == null || field === "exhausted") {
1503
+ continue;
1504
+ }
1505
+ const rhs = residualJsValueSpan(text, masked, equalsAt + 1);
1506
+ if (rhs.exhausted) {
1507
+ continue;
1508
+ }
1509
+ const value = reconstructStaticJsValue(rhs.content)?.trim();
1510
+ if (value == null || !isSafeTypedFieldReference(rhs.content, value)) {
1511
+ continue;
1512
+ }
1513
+ safeSpans.push({
1514
+ start: field.start,
1515
+ end: equalsAt + 1 + rhs.content.length,
1516
+ });
1517
+ }
1518
+ if (safeSpans.length === 0) {
1519
+ return text;
1520
+ }
1521
+ // Parser/lexer ranges are UTF-16 offsets, so preserve code-unit indexing.
1522
+ const characters = text.split("");
1523
+ for (const span of safeSpans) {
1524
+ for (let index = span.start; index < span.end; index++) {
1525
+ if (characters[index] !== "\n" && characters[index] !== "\r") {
1526
+ characters[index] = " ";
1527
+ }
1528
+ }
1529
+ }
1530
+ return characters.join("");
1531
+ }
1532
+ function isSafeTypedFieldReference(source, value) {
1533
+ const expression = unwrapReferenceExpression(value);
1534
+ const sourceExpression = unwrapReferenceExpression(maskJsCommentTrivia(source));
1535
+ const staticEnvReference = /^(?:process\.env\[["'][A-Za-z_][A-Za-z0-9_]*["']\]|Deno\.env\.get\(["'][A-Za-z_][A-Za-z0-9_]*["']\))$/.test(sourceExpression);
1536
+ return (staticEnvReference ||
1537
+ (!/["'`]/.test(maskJsCommentTrivia(source)) &&
1538
+ (looksLikeCodeExpression(expression) ||
1539
+ /^keyof\s+typeof\s+[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\.|::)[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(expression))));
1540
+ }
1541
+ function malformedYamlSensitiveValueStarts(text) {
1542
+ chargeWork(4 * text.length);
1543
+ const starts = [];
1544
+ const keyToken = String.raw `[A-Za-z_$][A-Za-z0-9_$-]*(?:\.[A-Za-z_$][A-Za-z0-9_$-]*)*`;
1545
+ for (const match of text.matchAll(/^[ \t]*(["'])((?:\\.|(?!\1)[^\r\n])*)\1[ \t]*:/gm)) {
1546
+ const decoded = match[1] === '"'
1547
+ ? decodeJsString(match[2] ?? "")
1548
+ : (match[2] ?? "").replaceAll("''", "'");
1549
+ if (decoded != null && isSensitiveDecodedKey(decoded)) {
1550
+ starts.push((match.index ?? 0) + match[0].length);
1551
+ }
1552
+ }
1553
+ for (const match of text.matchAll(new RegExp(String.raw `^[ \t]*(${keyToken})[ \t]*:`, "gm"))) {
1554
+ if (isSensitiveDecodedKey(match[1] ?? "")) {
1555
+ starts.push((match.index ?? 0) + match[0].length);
1556
+ }
1557
+ }
1558
+ const sensitiveAliases = new Set();
1559
+ for (const match of text.matchAll(new RegExp(String.raw `&([A-Za-z0-9_.-]+)(?:[ \t]+(?:!!?[^ \t\r\n]+|!<[^>\r\n]+>))*[ \t]+(${keyToken})`, "g"))) {
1560
+ if (isSensitiveDecodedKey(match[2] ?? "")) {
1561
+ sensitiveAliases.add(match[1] ?? "");
1562
+ }
1563
+ }
1564
+ const explicitKey = new RegExp(String.raw `^[ \t]*\?[ \t]+(?:(${keyToken})|\*([A-Za-z0-9_.-]+))[ \t]*(?:\r?\n)[ \t]*:`, "gm");
1565
+ for (const match of text.matchAll(explicitKey)) {
1566
+ if (isSensitiveDecodedKey(match[1] ?? "") ||
1567
+ sensitiveAliases.has(match[2] ?? "")) {
1568
+ starts.push((match.index ?? 0) + match[0].length);
1569
+ }
1570
+ }
1571
+ return starts;
1572
+ }
1573
+ function redactYamlScalarSource(source) {
1574
+ if (!/\r|\n/.test(source)) {
1575
+ const leading = /^[ \t]*/.exec(source)?.[0] ?? "";
1576
+ const trailing = /[ \t]*$/.exec(source)?.[0] ?? "";
1577
+ const body = source.slice(leading.length, source.length - trailing.length);
1578
+ const quote = body[0] === '"' || body[0] === "'" ? body[0] : undefined;
1579
+ return `${leading}${quote ?? ""}${REDACTION_MARKER}${quote ?? ""}${trailing}`;
1580
+ }
1581
+ const firstLineEnd = lineEndIndex(source, 0);
1582
+ const firstLine = source.slice(0, firstLineEnd);
1583
+ if (new RegExp(`^[ \\t]*${YAML_BLOCK_HEADER_PATTERN_SOURCE}(?:[ \\t]+#.*)?$`).test(firstLine)) {
1584
+ const newlineLength = source.startsWith("\r\n", firstLineEnd) ? 2 : 1;
1585
+ return `${source.slice(0, firstLineEnd + newlineLength)}${redactYamlBlockPayload(source.slice(firstLineEnd + newlineLength))}`;
1586
+ }
1587
+ const trimmed = source.trim();
1588
+ const quote = trimmed[0] === '"' || trimmed[0] === "'" ? trimmed[0] : undefined;
1589
+ if (quote != null && trimmed.endsWith(quote)) {
1590
+ const leading = /^[ \t]*/.exec(source)?.[0] ?? "";
1591
+ const lineBreaks = source.match(/\r\n|\n/g) ?? [];
1592
+ return `${leading}${quote}${REDACTION_MARKER}${quote}${lineBreaks.join("")}`;
1593
+ }
1594
+ return redactYamlBlockPayload(source);
1595
+ }
1596
+ function lineNumberAtOffset(text, offset) {
1597
+ const newlineOffsets = newlineOffsetsFor(text);
1598
+ return 1 + binarySearchLastLessThan(newlineOffsets, offset) + 1;
1599
+ }
1600
+ function analyzeJsStructuralSecrets(text, primaryRedaction = true) {
1601
+ const analysis = analysisFor(text);
1602
+ const slot = primaryRedaction ? "jsPrimary" : "jsResidual";
1603
+ const cached = analysis?.[slot];
1604
+ if (cached != null) {
1605
+ return cached;
1606
+ }
1607
+ const result = analyzeJsStructuralSecretsUncached(text, primaryRedaction);
1608
+ if (analysis != null) {
1609
+ analysis[slot] = result;
1610
+ }
1611
+ return result;
1612
+ }
1613
+ function analyzeJsStructuralSecretsUncached(text, primaryRedaction) {
1614
+ const masked = commentMaskedFor(text);
1615
+ const matches = [];
1616
+ let unsafeSensitiveContext = false;
1617
+ let quote;
1618
+ let escaped = false;
1619
+ let scanned = 0;
1620
+ for (let cursor = 0; cursor < masked.length; cursor++) {
1621
+ chargeWork(1);
1622
+ if (++scanned > 1_000_000) {
1623
+ return { exhausted: true, unsafeSensitiveContext, matches };
1624
+ }
1625
+ const character = masked[cursor] ?? "";
1626
+ if (quote != null) {
1627
+ if (!escaped && character === quote) {
1628
+ quote = undefined;
1629
+ }
1630
+ escaped = !escaped && character === "\\";
1631
+ continue;
1632
+ }
1633
+ if (character === '"' || character === "'" || character === "`") {
1634
+ quote = character;
1635
+ escaped = false;
1636
+ continue;
1637
+ }
1638
+ if (character !== "=" ||
1639
+ /[=!<>]/.test(masked[cursor - 1] ?? "") ||
1640
+ /[=>]/.test(masked[cursor + 1] ?? "")) {
1641
+ continue;
1642
+ }
1643
+ chargeWork(32);
1644
+ const key = sensitiveJsKeyBefore(text, masked, cursor);
1645
+ if (key === "exhausted") {
1646
+ const span = residualJsValueSpan(text, masked, cursor + 1);
1647
+ unsafeSensitiveContext ||=
1648
+ hasExhaustedSensitiveJsKeyEvidence(masked, cursor) &&
1649
+ spanContainsHighEntropyLiteral(text.slice(cursor + 1));
1650
+ return { exhausted: true, unsafeSensitiveContext, matches };
1651
+ }
1652
+ if (key == null) {
1653
+ if (hasUnclassifiableSensitiveJsKey(text, masked, cursor)) {
1654
+ const span = residualJsValueSpan(text, masked, cursor + 1);
1655
+ if (span.exhausted) {
1656
+ unsafeSensitiveContext ||= spanContainsHighEntropyLiteral(text.slice(cursor + 1));
1657
+ return { exhausted: true, unsafeSensitiveContext, matches };
1658
+ }
1659
+ const unsafe = spanContainsHighEntropyLiteral(span.content);
1660
+ unsafeSensitiveContext ||= unsafe;
1661
+ }
1662
+ continue;
1663
+ }
1664
+ if (primaryRedaction &&
1665
+ /\/\*|\/\/|\r|\n/.test(text.slice(key.end, cursor))) {
1666
+ continue;
1667
+ }
1668
+ const span = residualJsValueSpan(text, masked, cursor + 1);
1669
+ if (span.exhausted) {
1670
+ unsafeSensitiveContext ||= spanContainsHighEntropyLiteral(text.slice(cursor + 1));
1671
+ return { exhausted: true, unsafeSensitiveContext, matches };
1672
+ }
1673
+ const reconstructed = reconstructStaticJsValue(span.content);
1674
+ const containsLiteral = /["'`]/.test(maskJsCommentTrivia(span.content));
1675
+ if (looksLikeCodeExpression(span.content.trim())) {
1676
+ continue;
1677
+ }
1678
+ const typedFieldWindowStart = Math.max(0, cursor - MAX_JS_KEY_LOOKBACK);
1679
+ const typedField = findBoundedTypedField(masked, typedFieldWindowStart, cursor);
1680
+ const safeTypedReference = typedField != null &&
1681
+ typedField !== "exhausted" &&
1682
+ reconstructed != null &&
1683
+ isSafeTypedFieldReference(span.content, reconstructed.trim());
1684
+ if (safeTypedReference) {
1685
+ continue;
1686
+ }
1687
+ if (reconstructed == null) {
1688
+ const unsafe = spanContainsHighEntropyLiteral(span.content);
1689
+ unsafeSensitiveContext ||= unsafe;
1690
+ continue;
1691
+ }
1692
+ if ((!containsLiteral && looksLikeCodeExpression(reconstructed.trim())) ||
1693
+ !isHighConfidenceAssignedSecret(reconstructed)) {
1694
+ continue;
1695
+ }
1696
+ const valueStart = cursor + 1;
1697
+ const valueEnd = valueStart + span.content.length;
1698
+ matches.push({
1699
+ keyStart: key.start,
1700
+ keyEnd: key.end,
1701
+ valueStart,
1702
+ valueEnd,
1703
+ line: lineNumberAtOffset(text, key.start),
1704
+ confidence: "high",
1705
+ replacement: redactJsExpressionSource(span.content),
1706
+ });
1707
+ cursor = Math.max(cursor, valueEnd - 1);
1708
+ }
1709
+ return { exhausted: false, unsafeSensitiveContext, matches };
1710
+ }
1711
+ function hasExhaustedSensitiveJsKeyEvidence(masked, equalsAt) {
1712
+ const prefix = masked.slice(0, equalsAt);
1713
+ const keyPattern = /(?:^|[;{}])[ \t\r\n]*(?:(?:public|private|protected|static|readonly|declare|abstract|override)[ \t\r\n]+)*(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)\b/gi;
1714
+ let lastMatch = null;
1715
+ for (const match of prefix.matchAll(keyPattern)) {
1716
+ lastMatch = match;
1717
+ }
1718
+ if (lastMatch == null) {
1719
+ return false;
1720
+ }
1721
+ const suffix = prefix.slice((lastMatch.index ?? 0) + lastMatch[0].length);
1722
+ return !suffix.includes(";");
1723
+ }
1724
+ function hasUnclassifiableSensitiveJsKey(text, masked, equalsAt) {
1725
+ const start = Math.max(0, equalsAt - 2_048);
1726
+ const prefix = text.slice(start, equalsAt);
1727
+ const maskedPrefix = masked.slice(start, equalsAt);
1728
+ const bracket = /\[[^\]\r\n]{0,384}\]\s*(?:\|\||\?\?|\+)?\s*$/.exec(maskedPrefix);
1729
+ if (bracket != null) {
1730
+ const rawBracket = prefix.slice(bracket.index ?? 0);
1731
+ for (const match of rawBracket.matchAll(/(["'])((?:\\.|(?!\1)[^\r\n])*)\1/g)) {
1732
+ const decoded = decodeJsString(match[2] ?? "");
1733
+ if (decoded != null && isSensitiveDecodedKey(decoded)) {
1734
+ return true;
1735
+ }
1736
+ }
1737
+ }
1738
+ return new RegExp(String.raw `(?:^|[;\r\n])[ \t]*(?:const|let|var)?[ \t]*(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)[ \t]*(?:\|\||\?\?|\+)[ \t]*$`, "i").test(maskedPrefix);
1739
+ }
1740
+ function spanContainsHighEntropyLiteral(span) {
1741
+ for (const match of span.matchAll(/(["'`])((?:\\.|(?!\1)[\s\S])*)\1/g)) {
1742
+ const encoded = match[2] ?? "";
1743
+ const literal = match[1] === "`" ? encoded : decodeJsString(encoded) ?? encoded;
1744
+ if (isHighConfidenceAssignedSecret(literal) ||
1745
+ [...literal.matchAll(/[A-Za-z0-9_./+=-]{16,}/g)].some((part) => isHighConfidenceAssignedSecret(part[0]))) {
1746
+ return true;
1747
+ }
1748
+ }
1749
+ const withoutComments = maskJsCommentTrivia(span)
1750
+ .split(/\r?\n/)
1751
+ .map((line) => line.replace(/(?:^|[ \t])#.*$/, ""))
1752
+ .join("\n");
1753
+ for (const line of withoutComments.split(/\r?\n/)) {
1754
+ const candidate = line
1755
+ .trim()
1756
+ .replace(/^:[ \t]*/, "")
1757
+ .replace(/^["']/, "")
1758
+ .replace(/["']$/, "")
1759
+ .trim();
1760
+ if (!isRedactionMarkerValue(candidate) &&
1761
+ !candidate.includes(REDACTION_MARKER) &&
1762
+ !new RegExp(String.raw `:\s*${YAML_BLOCK_HEADER_PATTERN_SOURCE}(?:\s+#.*)?$`).test(candidate) &&
1763
+ !looksLikeCodeExpression(candidate) &&
1764
+ isHighConfidenceAssignedSecret(candidate)) {
1765
+ return true;
1766
+ }
1767
+ }
1768
+ return [...withoutComments.matchAll(/[A-Za-z0-9_./+=-]{16,}/g)].some((match) => !looksLikeCodeExpression(match[0]) &&
1769
+ isHighConfidenceAssignedSecret(match[0]));
1770
+ }
1771
+ function sensitiveJsKeyBefore(text, masked, equalsAt) {
1772
+ const windowStart = Math.max(0, equalsAt - MAX_JS_KEY_LOOKBACK);
1773
+ const typedField = findSensitiveTypedField(masked, windowStart, equalsAt);
1774
+ if (typedField != null) {
1775
+ return typedField;
1776
+ }
1777
+ const prefix = masked.slice(windowStart, equalsAt);
1778
+ const bracket = /\[\s*(["'])((?:\\.|(?!\1)[^\r\n])*)\1\s*\]\s*$/.exec(prefix);
1779
+ if (bracket != null) {
1780
+ const encoded = bracket[2] ?? "";
1781
+ const decoded = decodeJsString(encoded);
1782
+ if (decoded != null && isSensitiveDecodedKey(decoded)) {
1783
+ const encodedOffset = bracket[0].indexOf(encoded);
1784
+ const start = windowStart + (bracket.index ?? 0) + encodedOffset;
1785
+ return { start, end: start + encoded.length };
1786
+ }
1787
+ }
1788
+ const quoted = /(["'])((?:\\.|(?!\1)[^\r\n])*)\1\s*$/.exec(prefix);
1789
+ if (quoted != null) {
1790
+ const encoded = quoted[2] ?? "";
1791
+ const decoded = decodeJsString(encoded);
1792
+ if (decoded != null && isSensitiveDecodedKey(decoded)) {
1793
+ const encodedOffset = quoted[0].indexOf(encoded);
1794
+ const start = windowStart + (quoted.index ?? 0) + encodedOffset;
1795
+ return { start, end: start + encoded.length };
1796
+ }
1797
+ }
1798
+ const bare = /([A-Za-z_$][A-Za-z0-9_$-]*)\s*$/.exec(prefix);
1799
+ const rawKey = bare?.[1];
1800
+ if (rawKey == null) {
1801
+ if (windowStart > 0) {
1802
+ return "exhausted";
1803
+ }
1804
+ return undefined;
1805
+ }
1806
+ if (!isSensitiveDecodedKey(rawKey)) {
1807
+ if (windowStart > 0) {
1808
+ const beforeKey = prefix
1809
+ .slice(0, bare?.index ?? 0)
1810
+ .trimEnd()
1811
+ .at(-1);
1812
+ if (beforeKey != null && !/[;{}\r\n]/.test(beforeKey)) {
1813
+ return "exhausted";
1814
+ }
1815
+ }
1816
+ return undefined;
1817
+ }
1818
+ const start = windowStart + (bare?.index ?? 0);
1819
+ return { start, end: start + rawKey.length };
1820
+ }
1821
+ function classifyTsTypeAnnotation(value) {
1822
+ const type = value.trim();
1823
+ if (type.length === 0) {
1824
+ return "unclassifiable";
184
1825
  }
185
- const value = match[3] ?? "";
186
- const quote = match[2] ?? "";
187
- if (quote.length === 0 && looksLikeCodeExpression(value)) {
188
- return line;
1826
+ const pairs = {
1827
+ "<": ">",
1828
+ "[": "]",
1829
+ "{": "}",
1830
+ "(": ")",
1831
+ };
1832
+ const closers = new Set(Object.values(pairs));
1833
+ const stack = [];
1834
+ for (let index = 0; index < type.length; index++) {
1835
+ const character = type[index] ?? "";
1836
+ if (character === ";" && stack.length === 0) {
1837
+ return "member_boundary";
1838
+ }
1839
+ if (index >= MAX_TS_TYPE_CHARS ||
1840
+ !/[A-Za-z0-9_$.,;<>\[\]{}()|&?:='" \t\r\n-]/.test(character)) {
1841
+ return "unclassifiable";
1842
+ }
1843
+ if (character === ">" && type[index - 1] === "=") {
1844
+ continue;
1845
+ }
1846
+ const closer = pairs[character];
1847
+ if (closer != null) {
1848
+ if (stack.length >= MAX_TS_TYPE_DEPTH) {
1849
+ return "unclassifiable";
1850
+ }
1851
+ stack.push(closer);
1852
+ }
1853
+ else if (closers.has(character) && stack.pop() !== character) {
1854
+ return "nested_candidate";
1855
+ }
1856
+ }
1857
+ return stack.length === 0 ? "valid" : "unclassifiable";
1858
+ }
1859
+ function findSensitiveTypedField(source, windowStart, windowEnd) {
1860
+ const field = findBoundedTypedField(source, windowStart, windowEnd);
1861
+ if (field === "exhausted" || field == null || field.sensitive) {
1862
+ return field === "exhausted" || field == null
1863
+ ? field
1864
+ : { start: field.start, end: field.end };
1865
+ }
1866
+ return undefined;
1867
+ }
1868
+ function findBoundedTypedField(source, windowStart, windowEnd) {
1869
+ // Index-backed enumeration. A window slice's matchAll could additionally
1870
+ // match at position 0 through the `^` alternative (no boundary character
1871
+ // consumed) — including continuations of a declaration whose boundary sits
1872
+ // just before the window and mid-identifier splits. That candidate comes
1873
+ // first in slice scan order; precomputed matches starting inside it would
1874
+ // have been consumed by the slice scan, so iteration resumes from its end.
1875
+ const candidates = typedFieldCandidatesFor(source);
1876
+ chargeWork(8);
1877
+ const synthetic = matchTypedFieldContinuationAt(source, windowStart, windowEnd);
1878
+ const evaluate = (keyStart, keyEnd, typeStart) => {
1879
+ const rawKey = source.slice(keyStart, keyEnd);
1880
+ if (!isInsideBoundedTypeBodyAt(source, windowStart, keyStart)) {
1881
+ return "continue";
1882
+ }
1883
+ // The legacy window ended at windowEnd, so the type annotation under
1884
+ // examination is bounded by it exactly as the slice was.
1885
+ const type = source.slice(typeStart, windowEnd).trim();
1886
+ const classification = classifyTsTypeAnnotation(type);
1887
+ if (classification === "member_boundary") {
1888
+ return "continue";
1889
+ }
1890
+ if (classification === "nested_candidate") {
1891
+ return "continue";
1892
+ }
1893
+ if (classification === "unclassifiable") {
1894
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(rawKey) &&
1895
+ isSensitiveDecodedKey(rawKey)
1896
+ ? "exhausted"
1897
+ : undefined;
1898
+ }
1899
+ // The first candidate whose entire suffix is a balanced type annotation is
1900
+ // the outer field. Nested object-type properties leave unmatched closers.
1901
+ return {
1902
+ start: keyStart,
1903
+ end: keyEnd,
1904
+ sensitive: isSensitiveDecodedKey(rawKey),
1905
+ };
1906
+ };
1907
+ if (synthetic != null) {
1908
+ chargeWork(4);
1909
+ const outcome = evaluate(synthetic.keyStart, synthetic.keyEnd, synthetic.typeStart);
1910
+ if (outcome !== "continue") {
1911
+ return outcome;
1912
+ }
1913
+ }
1914
+ const resumeFrom = synthetic != null ? synthetic.typeStart : windowStart;
1915
+ let slot = binarySearchLastLessThan(candidates.matchStarts, resumeFrom) + 1;
1916
+ while (slot < candidates.count) {
1917
+ chargeWork(4);
1918
+ const typeStart = candidates.typeStarts[slot] ?? 0;
1919
+ if (typeStart > windowEnd) {
1920
+ break;
1921
+ }
1922
+ const outcome = evaluate(candidates.keyStarts[slot] ?? typeStart, candidates.keyEnds[slot] ?? typeStart, typeStart);
1923
+ if (outcome !== "continue") {
1924
+ return outcome;
1925
+ }
1926
+ slot++;
1927
+ }
1928
+ return undefined;
1929
+ }
1930
+ function findTsFieldAssignmentEquals(text, start, end) {
1931
+ for (let index = start; index < end; index++) {
1932
+ if (text[index] === "=" &&
1933
+ text[index + 1] !== ">" &&
1934
+ !/[=!<>]/.test(text[index - 1] ?? "")) {
1935
+ return index;
1936
+ }
1937
+ }
1938
+ return undefined;
1939
+ }
1940
+ function decodeJsString(value) {
1941
+ try {
1942
+ return value.replace(/\\(?:u\{([0-9A-Fa-f]{1,6})\}|u([0-9A-Fa-f]{4})|x([0-9A-Fa-f]{2})|([\\"'bfnrtv0]))/g, (_whole, braced, unicode, hex, simple) => {
1943
+ if (braced != null || unicode != null || hex != null) {
1944
+ const point = Number.parseInt(braced ?? unicode ?? hex, 16);
1945
+ return String.fromCodePoint(point);
1946
+ }
1947
+ const escapes = {
1948
+ "\\": "\\",
1949
+ '"': '"',
1950
+ "'": "'",
1951
+ b: "\b",
1952
+ f: "\f",
1953
+ n: "\n",
1954
+ r: "\r",
1955
+ t: "\t",
1956
+ v: "\v",
1957
+ "0": "\0",
1958
+ };
1959
+ return escapes[simple] ?? simple;
1960
+ });
189
1961
  }
190
- if (!isHighConfidenceAssignedSecret(value)) {
191
- return line;
1962
+ catch {
1963
+ return undefined;
192
1964
  }
193
- const valueStart = match.index + match[0].lastIndexOf(value);
194
- if (valueStart < match.index) {
195
- return line;
1965
+ }
1966
+ function reconstructStaticJsValue(source) {
1967
+ const masked = maskJsCommentTrivia(source);
1968
+ if (!/["'`]/.test(masked)) {
1969
+ return masked.trim();
1970
+ }
1971
+ let cursor = 0;
1972
+ let value = "";
1973
+ let fragments = 0;
1974
+ while (cursor < masked.length) {
1975
+ while (cursor < masked.length && /[\s()+]/.test(masked[cursor] ?? "")) {
1976
+ cursor++;
1977
+ }
1978
+ if (cursor >= masked.length) {
1979
+ break;
1980
+ }
1981
+ const quote = masked[cursor];
1982
+ if (quote !== '"' && quote !== "'" && quote !== "`") {
1983
+ const bare = masked.slice(cursor).trim();
1984
+ return fragments === 0 ? bare : undefined;
1985
+ }
1986
+ cursor++;
1987
+ let encoded = "";
1988
+ let escaped = false;
1989
+ let dynamicTemplate = false;
1990
+ while (cursor < masked.length) {
1991
+ const character = masked[cursor] ?? "";
1992
+ if (!escaped && character === quote) {
1993
+ break;
1994
+ }
1995
+ if (quote === "`" && !escaped && character === "$" && masked[cursor + 1] === "{") {
1996
+ dynamicTemplate = true;
1997
+ }
1998
+ encoded += source[cursor] ?? "";
1999
+ escaped = !escaped && character === "\\";
2000
+ cursor++;
2001
+ }
2002
+ if (masked[cursor] !== quote || dynamicTemplate) {
2003
+ return undefined;
2004
+ }
2005
+ const decoded = decodeJsString(encoded);
2006
+ if (decoded == null) {
2007
+ return undefined;
2008
+ }
2009
+ value += decoded;
2010
+ fragments++;
2011
+ cursor++;
2012
+ }
2013
+ return fragments > 0 ? value : undefined;
2014
+ }
2015
+ function redactJsExpressionSource(source) {
2016
+ const leading = /^[ \t]*/.exec(source)?.[0] ?? "";
2017
+ const trailing = /[ \t]*$/.exec(source)?.[0] ?? "";
2018
+ const trimmed = source.trim();
2019
+ const single = /^(?:"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*')$/.test(trimmed);
2020
+ const quote = single ? trimmed[0] : "";
2021
+ const lineBreaks = source.match(/\r\n|\n/g) ?? [];
2022
+ return `${leading}${quote}${REDACTION_MARKER}${quote}${lineBreaks.join("")}${trailing}`;
2023
+ }
2024
+ function* iterateAssignmentSecrets(text) {
2025
+ const yaml = analyzeYamlStructuralSecrets(text);
2026
+ if (yaml.recognized && !yaml.exhausted) {
2027
+ yield* yaml.matches;
2028
+ }
2029
+ const js = analyzeJsStructuralSecrets(text, true);
2030
+ if (!js.exhausted) {
2031
+ yield* js.matches;
2032
+ }
2033
+ const commentMasked = commentMaskedFor(text);
2034
+ const nextSemicolon = nextSemicolonFor(commentMasked);
2035
+ chargeWork(2 * text.length);
2036
+ let line = 1;
2037
+ let lineCursor = 0;
2038
+ const keyPattern = new RegExp(ASSIGNMENT_KEY_PATTERN.source, ASSIGNMENT_KEY_PATTERN.flags);
2039
+ let match;
2040
+ while ((match = keyPattern.exec(commentMasked)) != null) {
2041
+ chargeWork(16);
2042
+ const matchStart = match.index;
2043
+ for (let index = lineCursor; index < matchStart; index++) {
2044
+ if (text[index] === "\n") {
2045
+ line++;
2046
+ }
2047
+ }
2048
+ lineCursor = matchStart;
2049
+ const bracketKey = match[5];
2050
+ const memberChain = match[6];
2051
+ const key = match[2] ?? bracketKey ?? memberChain?.split(/\s*(?:\.|::)\s*/).at(-1);
2052
+ if (key == null || !SENSITIVE_ASSIGNMENT_KEY_PATTERN.test(key)) {
2053
+ continue;
2054
+ }
2055
+ const quotedKey = match[1] != null;
2056
+ const keyOffset = match[0].lastIndexOf(key);
2057
+ const keyStart = matchStart + (quotedKey ? 1 : keyOffset);
2058
+ const keyEnd = keyStart + key.length;
2059
+ let cursor = matchStart + match[0].length;
2060
+ while (commentMasked[cursor] === " " || commentMasked[cursor] === "\t") {
2061
+ cursor++;
2062
+ }
2063
+ if (commentMasked[cursor] !== ":" && commentMasked[cursor] !== "=") {
2064
+ continue;
2065
+ }
2066
+ const delimiter = commentMasked[cursor];
2067
+ if (delimiter === "=" && !js.exhausted) {
2068
+ continue;
2069
+ }
2070
+ if (delimiter === ":" && yaml.recognized && !yaml.exhausted) {
2071
+ continue;
2072
+ }
2073
+ if (delimiter === ":") {
2074
+ const declarationStart = text.lastIndexOf("\n", matchStart - 1) + 1;
2075
+ const declarationEnd = lineEndIndex(text, cursor);
2076
+ const typeTerminator = cursor + 1 <= commentMasked.length
2077
+ ? nextSemicolon[cursor + 1] ?? -1
2078
+ : -1;
2079
+ const typeOnlyDeclaration = typeTerminator >= 0 &&
2080
+ typeTerminator <= declarationEnd
2081
+ ? commentMasked.slice(cursor + 1, typeTerminator).trim()
2082
+ : "";
2083
+ if (typeOnlyDeclaration.length > 0 &&
2084
+ isInsideBoundedTypeBodyAt(commentMasked, 0, keyStart) &&
2085
+ looksLikeTsTypeExpression(typeOnlyDeclaration, true)) {
2086
+ continue;
2087
+ }
2088
+ const assignmentEquals = findTsFieldAssignmentEquals(commentMasked, cursor + 1, declarationEnd);
2089
+ if (assignmentEquals != null) {
2090
+ const typedField = findBoundedTypedField(commentMasked, declarationStart, assignmentEquals);
2091
+ if (typedField != null) {
2092
+ continue;
2093
+ }
2094
+ }
2095
+ }
2096
+ cursor++;
2097
+ while (commentMasked[cursor] === " " || commentMasked[cursor] === "\t") {
2098
+ cursor++;
2099
+ }
2100
+ const blockIndicator = delimiter === ":"
2101
+ ? new RegExp(`^(${YAML_BLOCK_HEADER_PATTERN_SOURCE})(?=[ \\t]*(?:#|\\r?$))`, "m").exec(text.slice(cursor))
2102
+ : null;
2103
+ if (blockIndicator != null) {
2104
+ const block = parseYamlBlockScalar(text, matchStart, cursor, blockIndicator[1] ?? "");
2105
+ if (block == null) {
2106
+ continue;
2107
+ }
2108
+ keyPattern.lastIndex = block.valueEnd;
2109
+ const value = text.slice(block.valueStart, block.valueEnd);
2110
+ yield {
2111
+ keyStart,
2112
+ keyEnd,
2113
+ valueStart: block.valueStart,
2114
+ valueEnd: block.valueEnd,
2115
+ line,
2116
+ confidence: isHighConfidenceAssignedSecret(value.trim()) ? "high" : "none",
2117
+ replacement: redactYamlBlockPayload(value),
2118
+ };
2119
+ continue;
2120
+ }
2121
+ const quote = text[cursor] === '"' || text[cursor] === "'" ? text[cursor] : undefined;
2122
+ let valueStart = quote == null ? cursor : cursor + 1;
2123
+ let valueEnd = valueStart;
2124
+ if (quote == null) {
2125
+ if (delimiter === ":") {
2126
+ valueEnd = logicalYamlValueEnd(text, valueStart);
2127
+ }
2128
+ else {
2129
+ while (valueEnd < text.length && !/[\s;,#]/.test(text[valueEnd] ?? "")) {
2130
+ valueEnd++;
2131
+ }
2132
+ }
2133
+ }
2134
+ else {
2135
+ let escaped = false;
2136
+ while (valueEnd < text.length) {
2137
+ const character = text[valueEnd];
2138
+ if (character === "\r" || character === "\n") {
2139
+ break;
2140
+ }
2141
+ if (!escaped && character === quote) {
2142
+ break;
2143
+ }
2144
+ if (!escaped && character === "\\") {
2145
+ escaped = true;
2146
+ }
2147
+ else {
2148
+ escaped = false;
2149
+ }
2150
+ valueEnd++;
2151
+ }
2152
+ if (text[valueEnd] !== quote) {
2153
+ continue;
2154
+ }
2155
+ const tailStart = skipHorizontalWhitespace(text, valueEnd + 1);
2156
+ if (text[tailStart] === "+") {
2157
+ valueStart = cursor;
2158
+ const logicalSpan = residualJsValueSpan(text, commentMaskedFor(text), cursor);
2159
+ valueEnd = cursor + logicalSpan.content.length;
2160
+ }
2161
+ }
2162
+ keyPattern.lastIndex =
2163
+ valueEnd + (quote != null && valueStart !== cursor ? 1 : 0);
2164
+ const value = text.slice(valueStart, valueEnd);
2165
+ const confidence = (quote != null || !looksLikeCodeExpression(value)) &&
2166
+ isHighConfidenceAssignedSecret(value)
2167
+ ? "high"
2168
+ : "none";
2169
+ yield {
2170
+ keyStart,
2171
+ keyEnd,
2172
+ valueStart,
2173
+ valueEnd,
2174
+ line,
2175
+ confidence,
2176
+ ...(/\r|\n/.test(value)
2177
+ ? { replacement: redactYamlBlockPayload(value) }
2178
+ : {}),
2179
+ };
2180
+ }
2181
+ }
2182
+ function logicalYamlValueEnd(text, valueStart) {
2183
+ let end = valueStart;
2184
+ const delimiters = [];
2185
+ let quote;
2186
+ let escaped = false;
2187
+ while (end < text.length) {
2188
+ const character = text[end] ?? "";
2189
+ if (quote != null) {
2190
+ if (!escaped && character === quote) {
2191
+ quote = undefined;
2192
+ }
2193
+ escaped = !escaped && character === "\\";
2194
+ end++;
2195
+ continue;
2196
+ }
2197
+ if (character === '"' || character === "'" || character === "`") {
2198
+ quote = character;
2199
+ escaped = false;
2200
+ end++;
2201
+ continue;
2202
+ }
2203
+ if (character === "/" && text[end + 1] === "*") {
2204
+ if (delimiters.length === 0) {
2205
+ break;
2206
+ }
2207
+ const commentEnd = text.indexOf("*/", end + 2);
2208
+ if (commentEnd < 0) {
2209
+ break;
2210
+ }
2211
+ end = commentEnd + 2;
2212
+ continue;
2213
+ }
2214
+ if (character === "(" || character === "[" || character === "{") {
2215
+ delimiters.push(character);
2216
+ end++;
2217
+ continue;
2218
+ }
2219
+ if (character === ")" || character === "]" || character === "}") {
2220
+ const opener = character === ")" ? "(" : character === "]" ? "[" : "{";
2221
+ if (delimiters.at(-1) !== opener) {
2222
+ break;
2223
+ }
2224
+ delimiters.pop();
2225
+ end++;
2226
+ continue;
2227
+ }
2228
+ if (delimiters.length === 0 && (character === "\r" || character === "\n" || character === ";" || character === ",")) {
2229
+ break;
2230
+ }
2231
+ if (delimiters.length === 0 &&
2232
+ ((character === "#" &&
2233
+ (end === valueStart || /[ \t]/.test(text[end - 1] ?? ""))))) {
2234
+ break;
2235
+ }
2236
+ end++;
2237
+ }
2238
+ while (end > valueStart && /[ \t]/.test(text[end - 1] ?? "")) {
2239
+ end--;
2240
+ }
2241
+ return end;
2242
+ }
2243
+ function skipHorizontalWhitespace(text, start) {
2244
+ let cursor = start;
2245
+ while (text[cursor] === " " || text[cursor] === "\t") {
2246
+ cursor++;
2247
+ }
2248
+ return cursor;
2249
+ }
2250
+ function parseYamlBlockScalar(text, keyStart, valueCursor, indicator) {
2251
+ const headerEnd = lineEndIndex(text, valueCursor + indicator.length);
2252
+ const newlineLength = text.startsWith("\r\n", headerEnd) ? 2 : text[headerEnd] === "\n" ? 1 : 0;
2253
+ if (newlineLength === 0) {
2254
+ return undefined;
2255
+ }
2256
+ const lineStart = text.lastIndexOf("\n", keyStart - 1) + 1;
2257
+ const keyIndent = leadingIndentWidth(text.slice(lineStart, keyStart));
2258
+ const valueStart = headerEnd + newlineLength;
2259
+ let cursor = valueStart;
2260
+ let valueEnd = valueStart;
2261
+ while (cursor < text.length) {
2262
+ const end = lineEndIndex(text, cursor);
2263
+ const lineText = text.slice(cursor, end);
2264
+ if (lineText.trim().length > 0 && leadingIndentWidth(lineText) <= keyIndent) {
2265
+ break;
2266
+ }
2267
+ const lineBreakLength = text.startsWith("\r\n", end) ? 2 : text[end] === "\n" ? 1 : 0;
2268
+ valueEnd = end + lineBreakLength;
2269
+ cursor = valueEnd;
2270
+ if (lineBreakLength === 0) {
2271
+ break;
2272
+ }
2273
+ }
2274
+ return { valueStart, valueEnd };
2275
+ }
2276
+ function redactYamlBlockPayload(value) {
2277
+ return value.replace(/([^\r\n]*)(\r\n|\n|$)/g, (whole, line, newline) => {
2278
+ if (whole.length === 0) {
2279
+ return whole;
2280
+ }
2281
+ const indent = /^[ \t]*/.exec(line)?.[0] ?? "";
2282
+ return line.trim().length === 0 ? `${line}${newline}` : `${indent}${REDACTION_MARKER}${newline}`;
2283
+ });
2284
+ }
2285
+ function lineEndIndex(text, start) {
2286
+ const newline = text.indexOf("\n", start);
2287
+ if (newline < 0) {
2288
+ return text.length;
2289
+ }
2290
+ return newline > 0 && text[newline - 1] === "\r" ? newline - 1 : newline;
2291
+ }
2292
+ function leadingIndentWidth(value) {
2293
+ return /^[ \t]*/.exec(value)?.[0].length ?? 0;
2294
+ }
2295
+ function residualJsValueSpan(text, commentMasked, valueStart) {
2296
+ let cursor = valueStart;
2297
+ let quote;
2298
+ let escaped = false;
2299
+ let nesting = 0;
2300
+ let lineCount = 0;
2301
+ let valueSeenOnLine = false;
2302
+ while (cursor < text.length && cursor - valueStart < 32_768 && lineCount < 64) {
2303
+ const character = commentMasked[cursor] ?? "";
2304
+ if (quote != null) {
2305
+ if (!escaped && character === quote) {
2306
+ quote = undefined;
2307
+ }
2308
+ escaped = !escaped && character === "\\";
2309
+ if (character !== "\r" && character !== "\n" && !/\s/.test(character)) {
2310
+ valueSeenOnLine = true;
2311
+ }
2312
+ cursor++;
2313
+ continue;
2314
+ }
2315
+ if (character === '"' || character === "'" || character === "`") {
2316
+ quote = character;
2317
+ valueSeenOnLine = true;
2318
+ cursor++;
2319
+ continue;
2320
+ }
2321
+ if (character === "(" || character === "[" || character === "{") {
2322
+ nesting++;
2323
+ }
2324
+ else if (character === ")" || character === "]" || character === "}") {
2325
+ nesting = Math.max(0, nesting - 1);
2326
+ }
2327
+ else if ((character === ";" || character === ",") && nesting === 0) {
2328
+ break;
2329
+ }
2330
+ if (character === "\r" || character === "\n") {
2331
+ const before = commentMasked.slice(valueStart, cursor).trimEnd();
2332
+ const afterNewline = commentMasked
2333
+ .slice(character === "\r" && commentMasked[cursor + 1] === "\n" ? cursor + 2 : cursor + 1)
2334
+ .trimStart();
2335
+ const continuation = before.endsWith("+") ||
2336
+ before.endsWith("=") ||
2337
+ afterNewline.startsWith("+") ||
2338
+ nesting > 0 ||
2339
+ quote === "`";
2340
+ if (valueSeenOnLine && !continuation) {
2341
+ break;
2342
+ }
2343
+ if (character === "\r" && commentMasked[cursor + 1] === "\n") {
2344
+ cursor++;
2345
+ }
2346
+ lineCount++;
2347
+ valueSeenOnLine = false;
2348
+ cursor++;
2349
+ continue;
2350
+ }
2351
+ if (!/\s/.test(character)) {
2352
+ valueSeenOnLine = true;
2353
+ }
2354
+ cursor++;
2355
+ }
2356
+ return {
2357
+ content: text.slice(valueStart, cursor),
2358
+ exhausted: cursor < text.length &&
2359
+ (cursor - valueStart >= 32_768 || lineCount >= 64),
2360
+ };
2361
+ }
2362
+ function maskJsCommentTrivia(text) {
2363
+ const output = text.split("");
2364
+ let quote;
2365
+ let escaped = false;
2366
+ for (let index = 0; index < text.length; index++) {
2367
+ const character = text[index] ?? "";
2368
+ if (quote != null) {
2369
+ if (!escaped && character === quote) {
2370
+ quote = undefined;
2371
+ }
2372
+ escaped = !escaped && character === "\\";
2373
+ continue;
2374
+ }
2375
+ if (character === '"' || character === "'" || character === "`") {
2376
+ quote = character;
2377
+ continue;
2378
+ }
2379
+ if (character === "/" && text[index + 1] === "*") {
2380
+ output[index] = " ";
2381
+ output[index + 1] = " ";
2382
+ index += 2;
2383
+ while (index < text.length) {
2384
+ if (text[index] === "*" && text[index + 1] === "/") {
2385
+ output[index] = " ";
2386
+ output[index + 1] = " ";
2387
+ index++;
2388
+ break;
2389
+ }
2390
+ if (text[index] !== "\r" && text[index] !== "\n") {
2391
+ output[index] = " ";
2392
+ }
2393
+ index++;
2394
+ }
2395
+ continue;
2396
+ }
2397
+ if (character === "/" && text[index + 1] === "/") {
2398
+ output[index] = " ";
2399
+ output[index + 1] = " ";
2400
+ index += 2;
2401
+ while (index < text.length && text[index] !== "\r" && text[index] !== "\n") {
2402
+ output[index] = " ";
2403
+ index++;
2404
+ }
2405
+ index--;
2406
+ }
196
2407
  }
197
- return `${line.slice(0, valueStart)}[redacted-sensitive]${line.slice(valueStart + value.length)}`;
2408
+ return output.join("");
198
2409
  }
199
2410
  function shannonEntropy(value) {
200
2411
  const counts = new Map();