@sema-agent/server 7.147.0 → 7.149.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 (790) hide show
  1. package/MIGRATION.md +1468 -24
  2. package/README.md +35 -7
  3. package/README.zh-CN.md +18 -8
  4. package/USAGE.md +444 -193
  5. package/deploy/sema-up/README.md +16 -5
  6. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  7. package/deploy/sema-up/chart/templates/_helpers.tpl +20 -4
  8. package/deploy/sema-up/chart/values.yaml +3 -2
  9. package/deploy/sema-up/cluster-up.sh +74 -176
  10. package/deploy/sema-up/kube-up.sh +29 -49
  11. package/deploy/sema-up/lib/chart-run.sh +23 -0
  12. package/deploy/sema-up/lib/dataroot.sh +3 -3
  13. package/deploy/sema-up/lib/health.sh +62 -13
  14. package/deploy/sema-up/lib/helm-values-cluster.py +18 -0
  15. package/deploy/sema-up/lib/helm-values-kube.py +35 -0
  16. package/deploy/sema-up/lib/regcred.py +8 -0
  17. package/deploy/sema-up/lib/remote/helm-install.sh +3 -0
  18. package/deploy/sema-up/lib/remote/image-import.sh +6 -0
  19. package/deploy/sema-up/lib/remote/machine-precheck.sh +11 -0
  20. package/deploy/sema-up/lib/remote/region-probe.sh +3 -0
  21. package/deploy/sema-up/lib/remote/stack-ready.sh +26 -0
  22. package/deploy/sema-up/lib/remote/vxlan-guard.sh +8 -0
  23. package/deploy/sema-up/lib/render-stack.py +17 -0
  24. package/deploy/sema-up/lib/shell-text.sh +87 -0
  25. package/deploy/sema-up/lib/usage-flags.txt +10 -0
  26. package/deploy/sema-up/lib/usage-head.txt +1 -0
  27. package/deploy/sema-up/lib/usage-tail.txt +13 -0
  28. package/deploy/sema-up/preflight.sh +10 -8
  29. package/deploy/sema-up/sema-up.sh +14 -39
  30. package/deploy/sema-up/smoke.sh +7 -6
  31. package/deploy/sema-up/stack/compose.dataroot.yaml +3 -0
  32. package/deploy/sema-up/stack/compose.yaml +13 -1
  33. package/deploy/sema-up/stack/sema-stack-cluster.yaml +21 -0
  34. package/dist/adoption/plan.d.ts +24 -0
  35. package/dist/adoption/plan.js +53 -1
  36. package/dist/adoption/runner.d.ts +10 -1
  37. package/dist/adoption/runner.js +36 -16
  38. package/dist/approval-card.d.ts +36 -38
  39. package/dist/approval-gate-currency.js +3 -2
  40. package/dist/approval-leg-origin.d.ts +33 -0
  41. package/dist/approval-leg-origin.js +4 -0
  42. package/dist/approval-reconciler.d.ts +4 -13
  43. package/dist/approval-reconciler.js +2 -2
  44. package/dist/approval.d.ts +13 -11
  45. package/dist/approval.js +3 -3
  46. package/dist/audit.d.ts +10 -3
  47. package/dist/audit.js +15 -7
  48. package/dist/auth-bridge.d.ts +9 -15
  49. package/dist/auth-bridge.js +4 -11
  50. package/dist/auth-keys.d.ts +10 -1
  51. package/dist/auth-keys.js +8 -0
  52. package/dist/auto-mode-face.d.ts +10 -7
  53. package/dist/auto-mode-face.js +7 -4
  54. package/dist/background-agent-name.d.ts +19 -1
  55. package/dist/background-agent-name.js +12 -2
  56. package/dist/background-session-reap.d.ts +1 -1
  57. package/dist/bake-runner/main.js +9 -6
  58. package/dist/bench/s1/arms.js +6 -5
  59. package/dist/bench/s1/live-deps.js +5 -2
  60. package/dist/bench/s1/run-firm.js +6 -5
  61. package/dist/bind-host.d.ts +25 -0
  62. package/dist/bind-host.js +25 -0
  63. package/dist/boot/adoption.d.ts +3 -0
  64. package/dist/boot/adoption.js +1 -0
  65. package/dist/boot/deployment-mods.d.ts +1 -1
  66. package/dist/boot/execution-env.d.ts +17 -7
  67. package/dist/boot/execution-env.js +47 -42
  68. package/dist/boot/execution-lane.d.ts +6 -4
  69. package/dist/boot/execution-lane.js +3 -1
  70. package/dist/boot/exit-boundary.d.ts +2 -2
  71. package/dist/boot/exit-boundary.js +6 -5
  72. package/dist/boot/fatal-exit.d.ts +3 -5
  73. package/dist/boot/fatal-exit.js +2 -2
  74. package/dist/boot/leader.d.ts +15 -0
  75. package/dist/boot/leader.js +17 -15
  76. package/dist/boot/memory-consolidation.d.ts +37 -29
  77. package/dist/boot/memory-consolidation.js +11 -5
  78. package/dist/boot/org-delivery.d.ts +3 -2
  79. package/dist/boot/org-memory.js +1 -1
  80. package/dist/boot/parked-revive-gate.js +2 -2
  81. package/dist/boot/reapers.d.ts +4 -3
  82. package/dist/boot/reapers.js +19 -16
  83. package/dist/boot/resolve-spec.d.ts +5 -3
  84. package/dist/boot/resolve-spec.js +60 -70
  85. package/dist/boot/retention-lane.d.ts +4 -4
  86. package/dist/boot/retention-lane.js +2 -2
  87. package/dist/boot/runner-deps.d.ts +28 -7
  88. package/dist/boot/runner-deps.js +14 -5
  89. package/dist/boot/runtime-caps.d.ts +2 -2
  90. package/dist/boot/runtime-caps.js +16 -2
  91. package/dist/boot/session-faces.d.ts +16 -7
  92. package/dist/boot/session-faces.js +114 -94
  93. package/dist/boot/shutdown.d.ts +5 -4
  94. package/dist/boot/shutdown.js +6 -4
  95. package/dist/boot/stage-01-config.d.ts +1 -0
  96. package/dist/boot/stage-01-config.js +13 -4
  97. package/dist/boot/stage-02-web-admin-pull.d.ts +2 -0
  98. package/dist/boot/stage-02-web-admin-pull.js +16 -3
  99. package/dist/boot/stage-03-store-backend.d.ts +4 -0
  100. package/dist/boot/stage-03-store-backend.js +4 -3
  101. package/dist/boot/stage-04-stores.d.ts +3 -0
  102. package/dist/boot/stage-04-stores.js +2 -2
  103. package/dist/boot/stage-05-execution-env.d.ts +8 -3
  104. package/dist/boot/stage-05-execution-env.js +2 -2
  105. package/dist/boot/stage-06-runners.d.ts +12 -13
  106. package/dist/boot/stage-06-runners.js +23 -15
  107. package/dist/boot/stage-07-capability-layer.d.ts +13 -9
  108. package/dist/boot/stage-07-capability-layer.js +5 -3
  109. package/dist/boot/stage-08-reapers.d.ts +13 -9
  110. package/dist/boot/stage-08-reapers.js +3 -3
  111. package/dist/boot/stage-09-leader.d.ts +14 -16
  112. package/dist/boot/stage-09-leader.js +12 -4
  113. package/dist/boot/stage-10-http-server.d.ts +14 -16
  114. package/dist/boot/stage-10-http-server.js +7 -4
  115. package/dist/boot/store-evidence.d.ts +20 -0
  116. package/dist/boot/store-evidence.js +13 -0
  117. package/dist/boot/stores.d.ts +6 -0
  118. package/dist/boot/stores.js +41 -27
  119. package/dist/boot/task-list-lane.d.ts +4 -4
  120. package/dist/boot/web-admin.d.ts +4 -2
  121. package/dist/boot/web-admin.js +55 -35
  122. package/dist/boot/webfetch-summarize-lane.d.ts +1 -1
  123. package/dist/boot-reclaim.d.ts +3 -1
  124. package/dist/boot-reclaim.js +1 -1
  125. package/dist/brain.d.ts +7 -18
  126. package/dist/brain.js +5 -17
  127. package/dist/budget.js +6 -6
  128. package/dist/capabilities/center-plugins.d.ts +7 -1
  129. package/dist/capabilities/center-plugins.js +32 -10
  130. package/dist/capabilities/center-wiring.js +1 -1
  131. package/dist/capabilities/composite-members.d.ts +2 -2
  132. package/dist/capabilities/composite-members.js +2 -2
  133. package/dist/capabilities/repo-tools.d.ts +2 -0
  134. package/dist/capabilities/repo-tools.js +14 -12
  135. package/dist/capabilities/scenarios.d.ts +6 -15
  136. package/dist/capabilities/scenarios.js +11 -16
  137. package/dist/capabilities/skills.d.ts +1 -1
  138. package/dist/capabilities/tool-defer.d.ts +1 -1
  139. package/dist/capability-facts.d.ts +39 -11
  140. package/dist/capability-facts.js +35 -7
  141. package/dist/card-settle-stored-scope.d.ts +56 -0
  142. package/dist/card-settle-stored-scope.js +27 -0
  143. package/dist/card-void.d.ts +8 -13
  144. package/dist/card-void.js +2 -7
  145. package/dist/center-credential-renewer.d.ts +6 -2
  146. package/dist/center-credential-renewer.js +7 -3
  147. package/dist/config-catalog.d.ts +28 -1
  148. package/dist/config-catalog.js +95 -85
  149. package/dist/config-dir-trust.d.ts +37 -4
  150. package/dist/config-dir-trust.js +88 -44
  151. package/dist/config-invariants.d.ts +43 -3
  152. package/dist/config-invariants.js +69 -5
  153. package/dist/config-lkg.d.ts +23 -1
  154. package/dist/config-lkg.js +17 -3
  155. package/dist/config-provider.d.ts +9 -9
  156. package/dist/config-provider.js +25 -110
  157. package/dist/config-pull-failure.d.ts +50 -0
  158. package/dist/config-pull-failure.js +48 -0
  159. package/dist/config-types.d.ts +62 -21
  160. package/dist/config.d.ts +14 -21
  161. package/dist/config.js +141 -64
  162. package/dist/context-window-notice.d.ts +31 -0
  163. package/dist/context-window-notice.js +28 -0
  164. package/dist/delegated-session-owner.d.ts +1 -1
  165. package/dist/deleted-session-ids.d.ts +3 -2
  166. package/dist/deployment-governance.d.ts +3 -3
  167. package/dist/deployment-shape.d.ts +46 -11
  168. package/dist/deployment-shape.js +8 -0
  169. package/dist/egress-redirect.d.ts +25 -7
  170. package/dist/egress-redirect.js +20 -2
  171. package/dist/elicitation.d.ts +6 -5
  172. package/dist/elicitation.js +23 -22
  173. package/dist/engine-identity.d.ts +28 -0
  174. package/dist/engine-identity.js +10 -0
  175. package/dist/entry-problems.d.ts +5 -7
  176. package/dist/entry-problems.js +1 -1
  177. package/dist/env-facts.js +2 -1
  178. package/dist/env-name-allowlist-knobs.d.ts +6 -5
  179. package/dist/env-name-allowlist-knobs.js +5 -0
  180. package/dist/env-witness.d.ts +7 -0
  181. package/dist/env-witness.js +16 -0
  182. package/dist/execution-lane-caps.d.ts +25 -23
  183. package/dist/execution-lane-caps.js +18 -26
  184. package/dist/execution-placement.d.ts +58 -18
  185. package/dist/execution-placement.js +41 -2
  186. package/dist/field-conflict.d.ts +7 -11
  187. package/dist/field-conflict.js +3 -3
  188. package/dist/file-history-capture.d.ts +3 -3
  189. package/dist/file-history-disclosure.js +1 -0
  190. package/dist/fleet/fleet-bus.d.ts +21 -3
  191. package/dist/fleet/fleet-bus.js +35 -25
  192. package/dist/fleet/fleet-reconciler.js +2 -2
  193. package/dist/fleet/serving-lease.js +3 -3
  194. package/dist/fleet-client.js +3 -3
  195. package/dist/fleet-lease.d.ts +0 -1
  196. package/dist/fleet-lease.js +4 -5
  197. package/dist/history-anchor.d.ts +23 -3
  198. package/dist/history-anchor.js +24 -4
  199. package/dist/hitl-delivery.d.ts +90 -0
  200. package/dist/hitl-delivery.js +98 -0
  201. package/dist/hooks/classic-stations.d.ts +8 -11
  202. package/dist/hooks/classic-stations.js +0 -7
  203. package/dist/hooks/command-spawn.d.ts +7 -2
  204. package/dist/hooks/command-spawn.js +13 -6
  205. package/dist/hooks/hook-llm.d.ts +5 -1
  206. package/dist/hooks/hook-llm.js +24 -15
  207. package/dist/hooks/hook-runner.d.ts +72 -57
  208. package/dist/hooks/hook-runner.js +271 -193
  209. package/dist/host-lsp-manager.d.ts +22 -8
  210. package/dist/host-lsp-manager.js +4 -1
  211. package/dist/http/active-run-conflict.d.ts +2 -43
  212. package/dist/http/active-run-conflict.js +3 -7
  213. package/dist/http/admission.d.ts +4 -18
  214. package/dist/http/admission.js +76 -79
  215. package/dist/http/approval-sweeps.d.ts +3 -3
  216. package/dist/http/approval-sweeps.js +56 -31
  217. package/dist/http/caller-trust.d.ts +8 -10
  218. package/dist/http/caller-trust.js +1 -2
  219. package/dist/http/claim-heartbeat.d.ts +49 -0
  220. package/dist/http/claim-heartbeat.js +46 -0
  221. package/dist/http/claimed-run.d.ts +16 -8
  222. package/dist/http/claimed-run.js +8 -0
  223. package/dist/http/dispatch.d.ts +8 -1
  224. package/dist/http/dispatch.js +78 -30
  225. package/dist/http/durable-run-leg.d.ts +11 -4
  226. package/dist/http/durable-run-leg.js +45 -21
  227. package/dist/http/execution-context.d.ts +122 -47
  228. package/dist/http/execution-context.js +135 -43
  229. package/dist/http/health-reader.d.ts +36 -0
  230. package/dist/http/health-reader.js +21 -0
  231. package/dist/http/health-self-report.d.ts +9 -9
  232. package/dist/http/health-self-report.js +4 -4
  233. package/dist/http/idempotency.d.ts +9 -5
  234. package/dist/http/idempotency.js +4 -4
  235. package/dist/http/memory-compliance-failure-reply.d.ts +15 -16
  236. package/dist/http/memory-compliance-failure-reply.js +4 -2
  237. package/dist/http/memory-face-fault-reply.d.ts +67 -0
  238. package/dist/http/memory-face-fault-reply.js +71 -0
  239. package/dist/http/memory-store-refusal-reply.d.ts +1 -1
  240. package/dist/http/memory-store-refusal-reply.js +3 -0
  241. package/dist/http/owner-access.d.ts +392 -67
  242. package/dist/http/owner-access.js +370 -35
  243. package/dist/http/principal-gate.d.ts +39 -89
  244. package/dist/http/principal-gate.js +26 -68
  245. package/dist/http/refusal-body.d.ts +35 -2
  246. package/dist/http/refusal-body.js +34 -0
  247. package/dist/http/request-guards.d.ts +9 -13
  248. package/dist/http/request-guards.js +34 -68
  249. package/dist/http/resume-composition.d.ts +37 -0
  250. package/dist/http/resume-composition.js +18 -0
  251. package/dist/http/resume-ctx-read.d.ts +46 -12
  252. package/dist/http/resume-ctx-read.js +31 -6
  253. package/dist/http/resume-legs.d.ts +54 -19
  254. package/dist/http/resume-legs.js +408 -196
  255. package/dist/http/retired-skill-manifest.d.ts +1 -1
  256. package/dist/http/route-ctx.d.ts +31 -32
  257. package/dist/http/routes/a2a-serve.d.ts +43 -5
  258. package/dist/http/routes/a2a-serve.js +60 -37
  259. package/dist/http/routes/admin-config-refresh.js +2 -2
  260. package/dist/http/routes/admin-drain.js +2 -2
  261. package/dist/http/routes/admin-memory-optout.js +4 -3
  262. package/dist/http/routes/adoption.d.ts +3 -0
  263. package/dist/http/routes/adoption.js +17 -10
  264. package/dist/http/routes/agents-roster.js +13 -7
  265. package/dist/http/routes/approvals-assistant.js +73 -67
  266. package/dist/http/routes/attachments.js +2 -2
  267. package/dist/http/routes/background.d.ts +3 -3
  268. package/dist/http/routes/background.js +9 -17
  269. package/dist/http/routes/capabilities.js +16 -19
  270. package/dist/http/routes/config-catalog.js +2 -2
  271. package/dist/http/routes/devices.d.ts +3 -3
  272. package/dist/http/routes/devices.js +4 -4
  273. package/dist/http/routes/diagnostics.d.ts +29 -3
  274. package/dist/http/routes/diagnostics.js +49 -18
  275. package/dist/http/routes/engine.js +2 -2
  276. package/dist/http/routes/fleet.js +5 -3
  277. package/dist/http/routes/images.js +10 -6
  278. package/dist/http/routes/leader.d.ts +7 -2
  279. package/dist/http/routes/leader.js +12 -3
  280. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  281. package/dist/http/routes/mcp-reconnect.js +7 -15
  282. package/dist/http/routes/memory-bundle.js +4 -7
  283. package/dist/http/routes/memory-compliance.js +4 -4
  284. package/dist/http/routes/memory-consolidation.js +6 -9
  285. package/dist/http/routes/memory-origin.d.ts +2 -0
  286. package/dist/http/routes/memory-origin.js +7 -8
  287. package/dist/http/routes/memory-policy.js +17 -12
  288. package/dist/http/routes/notify-wake.js +10 -19
  289. package/dist/http/routes/observability.js +3 -3
  290. package/dist/http/routes/principal-inbox.d.ts +4 -2
  291. package/dist/http/routes/principal-inbox.js +5 -4
  292. package/dist/http/routes/projects.d.ts +1 -1
  293. package/dist/http/routes/projects.js +3 -2
  294. package/dist/http/routes/retention-ops.js +6 -9
  295. package/dist/http/routes/rules.js +20 -5
  296. package/dist/http/routes/run-memory-capture.d.ts +2 -2
  297. package/dist/http/routes/run-memory-capture.js +6 -12
  298. package/dist/http/routes/run-permission-mode.d.ts +2 -2
  299. package/dist/http/routes/run-permission-mode.js +10 -17
  300. package/dist/http/routes/runs.js +155 -237
  301. package/dist/http/routes/session-memory-sync.d.ts +2 -2
  302. package/dist/http/routes/session-memory-sync.js +7 -13
  303. package/dist/http/routes/session-sync.d.ts +28 -7
  304. package/dist/http/routes/session-sync.js +63 -50
  305. package/dist/http/routes/sessions-list.d.ts +3 -3
  306. package/dist/http/routes/sessions-list.js +9 -9
  307. package/dist/http/routes/sessions.js +87 -124
  308. package/dist/http/routes/shared-memory.js +6 -6
  309. package/dist/http/routes/side-query.js +2 -2
  310. package/dist/http/routes/tasks.js +237 -187
  311. package/dist/http/routes/trace-usage.d.ts +0 -12
  312. package/dist/http/routes/trace-usage.js +40 -46
  313. package/dist/http/routes/workflows.d.ts +39 -4
  314. package/dist/http/routes/workflows.js +42 -14
  315. package/dist/http/run-concurrency-cap.d.ts +20 -12
  316. package/dist/http/run-concurrency-cap.js +39 -26
  317. package/dist/http/run-read-face.d.ts +3 -5
  318. package/dist/http/run-read-face.js +8 -13
  319. package/dist/http/send.d.ts +12 -3
  320. package/dist/http/send.js +42 -6
  321. package/dist/http/server.d.ts +43 -45
  322. package/dist/http/server.js +61 -11
  323. package/dist/http/sse-log.d.ts +17 -10
  324. package/dist/http/sse-log.js +7 -7
  325. package/dist/http/stored-body-replay.d.ts +12 -0
  326. package/dist/http/stored-body-replay.js +2 -2
  327. package/dist/http/submit-ctx.d.ts +17 -8
  328. package/dist/http/submit-ctx.js +8 -5
  329. package/dist/http/submit-replay.d.ts +19 -6
  330. package/dist/http/submit-replay.js +18 -6
  331. package/dist/http/sync-submit-body.d.ts +3 -4
  332. package/dist/http/sync-submit-body.js +4 -5
  333. package/dist/http/wire-types.d.ts +4 -3
  334. package/dist/identity-source.d.ts +36 -5
  335. package/dist/identity-source.js +27 -0
  336. package/dist/index.d.ts +4 -1
  337. package/dist/index.js +2 -0
  338. package/dist/key-resolver.d.ts +32 -0
  339. package/dist/key-resolver.js +23 -9
  340. package/dist/leader/diffup.js +2 -1
  341. package/dist/leader/endpoint.d.ts +26 -5
  342. package/dist/leader/endpoint.js +154 -14
  343. package/dist/leader/fanout.d.ts +3 -0
  344. package/dist/leader/fanout.js +78 -63
  345. package/dist/leader/leader.d.ts +16 -0
  346. package/dist/leader/leader.js +43 -3
  347. package/dist/leader/merge.d.ts +9 -1
  348. package/dist/leader/merge.js +16 -1
  349. package/dist/leader/repair-wire.js +1 -1
  350. package/dist/leader/wire.d.ts +36 -3
  351. package/dist/leader/wire.js +59 -18
  352. package/dist/leg-facts.d.ts +7 -2
  353. package/dist/leg-facts.js +1 -0
  354. package/dist/local-store-declaration.d.ts +54 -0
  355. package/dist/local-store-declaration.js +59 -0
  356. package/dist/lsp-evict.d.ts +6 -4
  357. package/dist/lsp-evict.js +2 -3
  358. package/dist/mcp-assertion-header.d.ts +1 -1
  359. package/dist/mcp-assertion-header.js +1 -1
  360. package/dist/memory-operator-faces.d.ts +5 -4
  361. package/dist/memory-operator-faces.js +1 -1
  362. package/dist/memory-project-layer.js +1 -1
  363. package/dist/memory-scope.d.ts +33 -6
  364. package/dist/memory-scope.js +68 -19
  365. package/dist/memory-store-refusal.d.ts +5 -2
  366. package/dist/memory-sync-client.d.ts +3 -2
  367. package/dist/memory-sync-client.js +8 -5
  368. package/dist/model-compat.d.ts +17 -8
  369. package/dist/model-compat.js +6 -2
  370. package/dist/model-context-window.d.ts +57 -0
  371. package/dist/model-context-window.js +21 -0
  372. package/dist/model-entry-headers.d.ts +46 -4
  373. package/dist/model-entry-headers.js +2 -2
  374. package/dist/model-entry-refusal.d.ts +40 -20
  375. package/dist/model-entry-refusal.js +57 -30
  376. package/dist/model-plane-face.d.ts +19 -11
  377. package/dist/model-plane-face.js +9 -1
  378. package/dist/model-plane-probe.d.ts +2 -0
  379. package/dist/model-plane-probe.js +17 -0
  380. package/dist/model-provider.d.ts +3 -3
  381. package/dist/model-reach-scope.d.ts +2 -1
  382. package/dist/model-route-endpoint.d.ts +9 -5
  383. package/dist/model-route-endpoint.js +29 -7
  384. package/dist/model-select.d.ts +16 -0
  385. package/dist/model-select.js +4 -0
  386. package/dist/mountinfo.d.ts +37 -0
  387. package/dist/mountinfo.js +47 -0
  388. package/dist/namespaced-rule-name.d.ts +23 -14
  389. package/dist/namespaced-rule-name.js +8 -11
  390. package/dist/net-address.d.ts +1 -1
  391. package/dist/observability/corrupt-read-seat.d.ts +4 -4
  392. package/dist/observability/corrupt-read-seat.js +5 -3
  393. package/dist/observability/fail-open.d.ts +72 -40
  394. package/dist/observability/fail-open.js +74 -40
  395. package/dist/observability/logger.d.ts +22 -1
  396. package/dist/observability/logger.js +89 -12
  397. package/dist/observability/metrics.js +4 -5
  398. package/dist/observability/otel-exporter.js +2 -1
  399. package/dist/observability/principal-context.d.ts +14 -7
  400. package/dist/observability/principal-context.js +8 -3
  401. package/dist/observability/run-terminal.d.ts +7 -1
  402. package/dist/observability/run-terminal.js +6 -2
  403. package/dist/observability/secret-env-scrub.d.ts +1 -1
  404. package/dist/observability/secret-env-scrub.js +1 -1
  405. package/dist/orchestration/hardened-vm-runner.d.ts +1 -0
  406. package/dist/orchestration/hardened-vm-runner.js +11 -5
  407. package/dist/orchestration/hardened-vm-worker-runner.d.ts +0 -14
  408. package/dist/orchestration/hardened-vm-worker-runner.js +5 -0
  409. package/dist/orchestration/hardened-vm-worker.js +2 -1
  410. package/dist/orchestration/workflow-completion-inbox.d.ts +3 -3
  411. package/dist/orchestration/workflow-completion-inbox.js +3 -2
  412. package/dist/org-delivery-gate.d.ts +36 -10
  413. package/dist/org-delivery-gate.js +12 -8
  414. package/dist/outbound-dispatcher.d.ts +1 -1
  415. package/dist/outbound-dispatcher.js +6 -12
  416. package/dist/owner-brands.d.ts +81 -0
  417. package/dist/owner-brands.js +42 -0
  418. package/dist/owner-faces.d.ts +532 -0
  419. package/dist/owner-faces.js +126 -0
  420. package/dist/park-row-commit.d.ts +67 -0
  421. package/dist/park-row-commit.js +76 -0
  422. package/dist/parked-decide.d.ts +76 -12
  423. package/dist/parked-decide.js +152 -11
  424. package/dist/parked-revive-surface.d.ts +14 -4
  425. package/dist/parked-revive-surface.js +5 -4
  426. package/dist/peer-directory.d.ts +7 -9
  427. package/dist/peer-directory.js +3 -2
  428. package/dist/permission-rule-vocab.d.ts +53 -8
  429. package/dist/permission-rule-vocab.js +30 -2
  430. package/dist/plugins/adoption-log-sql.d.ts +3 -25
  431. package/dist/plugins/adoption-log-sql.js +23 -21
  432. package/dist/plugins/approval-ask-retired-delete-by-task-m50.d.ts +7 -0
  433. package/dist/plugins/approval-ask-retired-delete-by-task-m50.js +17 -0
  434. package/dist/plugins/approval-ask-store-file.d.ts +18 -7
  435. package/dist/plugins/approval-ask-store-file.js +37 -14
  436. package/dist/plugins/approval-ask-store-memory.d.ts +12 -4
  437. package/dist/plugins/approval-ask-store-memory.js +24 -10
  438. package/dist/plugins/approval-ask-store-sql.d.ts +40 -21
  439. package/dist/plugins/approval-ask-store-sql.js +44 -15
  440. package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
  441. package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
  442. package/dist/plugins/approval-exemption-skill-m46.d.ts +14 -0
  443. package/dist/plugins/approval-exemption-skill-m46.js +21 -0
  444. package/dist/plugins/approval-exemption-store.d.ts +32 -2
  445. package/dist/plugins/approval-exemption-store.js +42 -19
  446. package/dist/plugins/approval-nonce-store.d.ts +39 -12
  447. package/dist/plugins/approval-nonce-store.js +72 -23
  448. package/dist/plugins/blob-backend.d.ts +2 -0
  449. package/dist/plugins/blob-backend.js +9 -6
  450. package/dist/plugins/caching-session-store.d.ts +13 -1
  451. package/dist/plugins/caching-session-store.js +43 -4
  452. package/dist/plugins/checkpoint-index-columns.d.ts +32 -5
  453. package/dist/plugins/checkpoint-index-columns.js +36 -1
  454. package/dist/plugins/checkpoint-store-face.d.ts +83 -10
  455. package/dist/plugins/checkpoint-store-face.js +18 -1
  456. package/dist/plugins/device-store-sql.js +1 -0
  457. package/dist/plugins/ensure-index.d.ts +13 -1
  458. package/dist/plugins/ensure-index.js +154 -21
  459. package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -18
  460. package/dist/plugins/ensure-schema-lock-policy.js +76 -9
  461. package/dist/plugins/file-run-store.d.ts +85 -32
  462. package/dist/plugins/file-run-store.js +177 -94
  463. package/dist/plugins/host-platform.d.ts +24 -3
  464. package/dist/plugins/host-platform.js +15 -1
  465. package/dist/plugins/image-bake-store-sql.js +2 -0
  466. package/dist/plugins/imported-layer-root-retirement.d.ts +2 -2
  467. package/dist/plugins/k8s-pod-reaper.d.ts +1 -3
  468. package/dist/plugins/k8s-pod-reaper.js +0 -1
  469. package/dist/plugins/leader-run-store-sql.d.ts +22 -0
  470. package/dist/plugins/leader-run-store-sql.js +34 -4
  471. package/dist/plugins/local-checkpoint-store.d.ts +62 -23
  472. package/dist/plugins/local-checkpoint-store.js +197 -77
  473. package/dist/plugins/local-data-dir-modes-m39.js +2 -1
  474. package/dist/plugins/local-data-root-admission.d.ts +52 -0
  475. package/dist/plugins/local-data-root-admission.js +100 -0
  476. package/dist/plugins/local-docker-capacity.d.ts +93 -0
  477. package/dist/plugins/local-docker-capacity.js +194 -0
  478. package/dist/plugins/local-docker-reaper.d.ts +92 -0
  479. package/dist/plugins/local-docker-reaper.js +102 -0
  480. package/dist/plugins/local-engine-high-water.d.ts +29 -6
  481. package/dist/plugins/local-engine-high-water.js +27 -19
  482. package/dist/plugins/local-session-store.d.ts +458 -140
  483. package/dist/plugins/local-session-store.js +906 -292
  484. package/dist/plugins/local-session-window.d.ts +3 -2
  485. package/dist/plugins/local-session-window.js +3 -2
  486. package/dist/plugins/local-sidecar-quarantine.js +3 -2
  487. package/dist/plugins/local-task-attachment-store.d.ts +18 -1
  488. package/dist/plugins/local-task-attachment-store.js +22 -1
  489. package/dist/plugins/memory-embedder.js +9 -7
  490. package/dist/plugins/memory-engine-pg.d.ts +3 -2
  491. package/dist/plugins/memory-engine-pg.js +14 -5
  492. package/dist/plugins/memory-engine-sql-apply.d.ts +8 -4
  493. package/dist/plugins/memory-engine-sql-apply.js +6 -8
  494. package/dist/plugins/memory-engine-tidb.d.ts +4 -2
  495. package/dist/plugins/memory-engine-tidb.js +11 -2
  496. package/dist/plugins/memory-optout-grant-store-sql.d.ts +1 -1
  497. package/dist/plugins/memory-optout-grant-store-sql.js +3 -3
  498. package/dist/plugins/memory-run-store.d.ts +53 -36
  499. package/dist/plugins/memory-run-store.js +116 -88
  500. package/dist/plugins/one-time-migration-marker.d.ts +26 -0
  501. package/dist/plugins/one-time-migration-marker.js +13 -0
  502. package/dist/plugins/one-time-migrations.d.ts +214 -47
  503. package/dist/plugins/one-time-migrations.js +481 -135
  504. package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
  505. package/dist/plugins/org-delivery-quota-store.js +31 -15
  506. package/dist/plugins/permission-rule-store-file.d.ts +3 -3
  507. package/dist/plugins/permission-rule-store-sql.d.ts +5 -64
  508. package/dist/plugins/permission-rule-store-sql.js +11 -95
  509. package/dist/plugins/pg-pool.d.ts +2 -32
  510. package/dist/plugins/pg-pool.js +24 -17
  511. package/dist/plugins/remote-env-device.d.ts +3 -2
  512. package/dist/plugins/remote-env-e2b.d.ts +8 -32
  513. package/dist/plugins/remote-env-e2b.js +44 -32
  514. package/dist/plugins/remote-env-host.d.ts +51 -17
  515. package/dist/plugins/remote-env-host.js +111 -42
  516. package/dist/plugins/remote-env-k8s.d.ts +40 -45
  517. package/dist/plugins/remote-env-k8s.js +111 -60
  518. package/dist/plugins/remote-env-local-docker.d.ts +152 -15
  519. package/dist/plugins/remote-env-local-docker.js +352 -72
  520. package/dist/plugins/remote-scratchpad.js +4 -0
  521. package/dist/plugins/retention-deployment.d.ts +49 -17
  522. package/dist/plugins/retention-deployment.js +39 -8
  523. package/dist/plugins/run-preview-m45.d.ts +11 -0
  524. package/dist/plugins/run-preview-m45.js +19 -0
  525. package/dist/plugins/run-store-sql.d.ts +82 -75
  526. package/dist/plugins/run-store-sql.js +164 -125
  527. package/dist/plugins/s3-answer.d.ts +2 -1
  528. package/dist/plugins/sandbox-reaper.d.ts +26 -0
  529. package/dist/plugins/sandbox-reaper.js +2 -0
  530. package/dist/plugins/scenario-prefix-switch-m42.d.ts +56 -0
  531. package/dist/plugins/scenario-prefix-switch-m42.js +56 -0
  532. package/dist/plugins/scheduler-support.d.ts +16 -1
  533. package/dist/plugins/scheduler-support.js +22 -2
  534. package/dist/plugins/schema-consistency.d.ts +131 -14
  535. package/dist/plugins/schema-consistency.js +388 -18
  536. package/dist/plugins/schema-drift-sentence.d.ts +10 -6
  537. package/dist/plugins/schema-drift-sentence.js +15 -7
  538. package/dist/plugins/schema-ensure-legs.d.ts +9 -0
  539. package/dist/plugins/schema-ensure-legs.js +1 -1
  540. package/dist/plugins/schema-indexes.js +3 -0
  541. package/dist/plugins/schema-live-columns.d.ts +14 -5
  542. package/dist/plugins/schema-live-columns.js +10 -10
  543. package/dist/plugins/send-user-file.js +2 -1
  544. package/dist/plugins/session-birth.d.ts +37 -0
  545. package/dist/plugins/session-birth.js +38 -0
  546. package/dist/plugins/session-epoch-anchor.d.ts +40 -12
  547. package/dist/plugins/session-epoch-anchor.js +33 -11
  548. package/dist/plugins/session-ext-table.d.ts +27 -3
  549. package/dist/plugins/session-ext-table.js +19 -6
  550. package/dist/plugins/session-fork.d.ts +15 -1
  551. package/dist/plugins/session-fork.js +3 -0
  552. package/dist/plugins/session-ownership.d.ts +1 -1
  553. package/dist/plugins/session-placement.d.ts +27 -20
  554. package/dist/plugins/session-placement.js +15 -12
  555. package/dist/plugins/session-row-sql.d.ts +12 -0
  556. package/dist/plugins/session-row-sql.js +3 -3
  557. package/dist/plugins/session-run-rollup.d.ts +29 -0
  558. package/dist/plugins/session-run-rollup.js +40 -0
  559. package/dist/plugins/session-store-sql.d.ts +38 -12
  560. package/dist/plugins/session-store-sql.js +130 -30
  561. package/dist/plugins/session-withdraw-sql.d.ts +36 -0
  562. package/dist/plugins/session-withdraw-sql.js +42 -0
  563. package/dist/plugins/shared-memory-store-sql.d.ts +0 -69
  564. package/dist/plugins/shared-memory-store-sql.js +2 -1
  565. package/dist/plugins/shell-sentinel-owner-m48.d.ts +42 -0
  566. package/dist/plugins/shell-sentinel-owner-m48.js +9 -0
  567. package/dist/plugins/snapshot-bucket-presence.d.ts +2 -1
  568. package/dist/plugins/sql-driver.d.ts +1 -1
  569. package/dist/plugins/sql-driver.js +2 -1
  570. package/dist/plugins/sql-errors.d.ts +32 -5
  571. package/dist/plugins/sql-errors.js +55 -7
  572. package/dist/plugins/sql-json-column.d.ts +8 -7
  573. package/dist/plugins/sql-json-column.js +10 -6
  574. package/dist/plugins/stop-then-start-sentence.d.ts +16 -0
  575. package/dist/plugins/stop-then-start-sentence.js +7 -0
  576. package/dist/plugins/store-backend.d.ts +99 -16
  577. package/dist/plugins/store-backend.js +230 -65
  578. package/dist/plugins/store-contracts.d.ts +123 -33
  579. package/dist/plugins/store-contracts.js +38 -1
  580. package/dist/plugins/store-fault.d.ts +28 -2
  581. package/dist/plugins/store-fault.js +20 -2
  582. package/dist/plugins/task-attachment-store.d.ts +15 -3
  583. package/dist/plugins/task-attachment-store.js +22 -9
  584. package/dist/plugins/tidb-pool.d.ts +11 -6
  585. package/dist/plugins/tidb-pool.js +39 -18
  586. package/dist/plugins/web-search.js +5 -3
  587. package/dist/plugins/workflow-run-store-sql.d.ts +6 -1
  588. package/dist/plugins/workflow-run-store-sql.js +11 -5
  589. package/dist/plugins/workspace-custody.d.ts +44 -0
  590. package/dist/plugins/workspace-custody.js +47 -0
  591. package/dist/principal-jwt.d.ts +4 -4
  592. package/dist/principal-jwt.js +20 -5
  593. package/dist/principal-shape.d.ts +41 -0
  594. package/dist/principal-shape.js +19 -0
  595. package/dist/project-identity.d.ts +8 -7
  596. package/dist/project-identity.js +40 -17
  597. package/dist/project-layer-key.d.ts +13 -2
  598. package/dist/project-layer-key.js +9 -1
  599. package/dist/question.d.ts +13 -23
  600. package/dist/question.js +21 -39
  601. package/dist/read-deny-floor-notice.d.ts +36 -0
  602. package/dist/read-deny-floor-notice.js +40 -0
  603. package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
  604. package/dist/request-declaration-disposition.js +39 -0
  605. package/dist/request-declaration-refusal.d.ts +35 -12
  606. package/dist/request-declaration-refusal.js +21 -9
  607. package/dist/request-form.d.ts +58 -0
  608. package/dist/request-form.js +72 -0
  609. package/dist/request-image-urls.d.ts +8 -2
  610. package/dist/request-image-urls.js +2 -1
  611. package/dist/request-images.d.ts +20 -0
  612. package/dist/request-images.js +59 -0
  613. package/dist/request-key-closure.d.ts +6 -7
  614. package/dist/request-key-closure.js +15 -2
  615. package/dist/resource-suspend.d.ts +7 -16
  616. package/dist/resource-suspend.js +1 -1
  617. package/dist/retired-name-host-notice.d.ts +21 -0
  618. package/dist/retired-name-host-notice.js +12 -0
  619. package/dist/rule-entry-list.d.ts +2 -2
  620. package/dist/rule-entry-list.js +3 -4
  621. package/dist/rule-issue-reading.d.ts +58 -0
  622. package/dist/rule-issue-reading.js +14 -0
  623. package/dist/rules-consent.d.ts +5 -3
  624. package/dist/rules-consent.js +1 -0
  625. package/dist/run-acceptance.d.ts +3 -1
  626. package/dist/run-acceptance.js +3 -0
  627. package/dist/run-cancel-card.d.ts +28 -0
  628. package/dist/run-cancel-card.js +19 -0
  629. package/dist/run-cancel-context.d.ts +10 -0
  630. package/dist/run-cancel-context.js +1 -0
  631. package/dist/run-local.d.ts +14 -6
  632. package/dist/run-local.js +117 -90
  633. package/dist/run-stale-default.d.ts +7 -0
  634. package/dist/run-stale-default.js +2 -0
  635. package/dist/runs.d.ts +56 -21
  636. package/dist/runs.js +73 -43
  637. package/dist/runtime-caps-resolver.d.ts +21 -6
  638. package/dist/runtime-caps-resolver.js +17 -4
  639. package/dist/runtime-governance.d.ts +2 -2
  640. package/dist/runtime-governance.js +5 -3
  641. package/dist/sealed-key-layout.d.ts +12 -5
  642. package/dist/sealed-key-layout.js +5 -0
  643. package/dist/sealed-key.d.ts +12 -2
  644. package/dist/sealed-key.js +115 -27
  645. package/dist/security.d.ts +128 -158
  646. package/dist/security.js +163 -59
  647. package/dist/sema-config-dir.d.ts +20 -0
  648. package/dist/sema-config-dir.js +25 -0
  649. package/dist/server-secret-env.d.ts +55 -25
  650. package/dist/server-secret-env.js +75 -22
  651. package/dist/session-agent.d.ts +3 -2
  652. package/dist/session-children-retire.d.ts +13 -8
  653. package/dist/session-children-retire.js +9 -7
  654. package/dist/session-deletion.d.ts +88 -0
  655. package/dist/session-deletion.js +8 -0
  656. package/dist/session-erase.d.ts +145 -0
  657. package/dist/session-erase.js +115 -0
  658. package/dist/session-error-wire.d.ts +5 -5
  659. package/dist/session-error-wire.js +1 -1
  660. package/dist/session-incarnation.d.ts +52 -5
  661. package/dist/session-incarnation.js +19 -2
  662. package/dist/session-policy-wire.d.ts +6 -3
  663. package/dist/session-policy-wire.js +11 -1
  664. package/dist/session-resident-background.d.ts +0 -71
  665. package/dist/session-resident-background.js +4 -3
  666. package/dist/session-row.d.ts +2 -1
  667. package/dist/session-rules-seat.d.ts +12 -0
  668. package/dist/session-rules-seat.js +19 -0
  669. package/dist/session-titler.d.ts +7 -15
  670. package/dist/session-titler.js +1 -1
  671. package/dist/session-workspace.d.ts +3 -3
  672. package/dist/spec-fields.d.ts +57 -3
  673. package/dist/spec-fields.js +72 -51
  674. package/dist/store-admission.d.ts +33 -0
  675. package/dist/store-admission.js +49 -0
  676. package/dist/store-posture.d.ts +141 -20
  677. package/dist/store-posture.js +205 -3
  678. package/dist/stored-request-hooks-m20.js +2 -2
  679. package/dist/stores-sql/background-agent-store-sql.d.ts +11 -2
  680. package/dist/stores-sql/background-agent-store-sql.js +73 -20
  681. package/dist/stores-sql/checkpoint-store-sql.d.ts +34 -29
  682. package/dist/stores-sql/checkpoint-store-sql.js +78 -49
  683. package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
  684. package/dist/stores-sql/file-history-store-sql.js +3 -5
  685. package/dist/stores-sql/mailbox-store-sql.d.ts +7 -4
  686. package/dist/stores-sql/mailbox-store-sql.js +31 -32
  687. package/dist/stores-sql/pg-session-storage.d.ts +7 -5
  688. package/dist/stores-sql/pg-session-storage.js +54 -46
  689. package/dist/stores-sql/retention-store-sql.d.ts +87 -19
  690. package/dist/stores-sql/retention-store-sql.js +92 -27
  691. package/dist/stores-sql/session-policy-store-sql.d.ts +32 -10
  692. package/dist/stores-sql/session-policy-store-sql.js +44 -36
  693. package/dist/stores-sql/session-write-hooks.d.ts +12 -6
  694. package/dist/stores-sql/tidb-session-storage.d.ts +4 -4
  695. package/dist/stores-sql/tidb-session-storage.js +13 -8
  696. package/dist/stores-sql/tidb-session-store.d.ts +36 -17
  697. package/dist/stores-sql/tidb-session-store.js +69 -39
  698. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  699. package/dist/stores-sql/tool-result-store-sql.js +4 -6
  700. package/dist/stores-sql/windowed-session-storage.d.ts +5 -0
  701. package/dist/stores-sql/windowed-session-storage.js +9 -0
  702. package/dist/submission-exit.d.ts +50 -0
  703. package/dist/submission-exit.js +108 -0
  704. package/dist/task-a2a.d.ts +3 -8
  705. package/dist/task-a2a.js +28 -30
  706. package/dist/task-cwd.d.ts +43 -7
  707. package/dist/task-cwd.js +11 -0
  708. package/dist/task-mcp.d.ts +3 -3
  709. package/dist/task-mcp.js +4 -4
  710. package/dist/task-settings.d.ts +14 -10
  711. package/dist/task-settings.js +19 -7
  712. package/dist/terminal.d.ts +27 -43
  713. package/dist/terminal.js +49 -14
  714. package/dist/tool-approval.d.ts +173 -163
  715. package/dist/tool-approval.js +247 -232
  716. package/dist/trace/core-keyset-guard.d.ts +13 -6
  717. package/dist/trace/core-vocab-wire.d.ts +95 -0
  718. package/dist/trace/core-vocab-wire.js +15 -0
  719. package/dist/trace/engine-notice-wire.d.ts +24 -21
  720. package/dist/trace/engine-notice-wire.js +7 -3
  721. package/dist/trace/exit-seat.d.ts +30 -4
  722. package/dist/trace/exit-seat.js +40 -36
  723. package/dist/trace/extension-call-face.d.ts +3 -2
  724. package/dist/trace/extension-call-face.js +3 -1
  725. package/dist/trace/ledger-events.d.ts +4 -7
  726. package/dist/trace/ledger-sink.d.ts +4 -31
  727. package/dist/trace/ledger-sink.js +0 -11
  728. package/dist/trace/model-segment.d.ts +5 -5
  729. package/dist/trace/model-segment.js +5 -5
  730. package/dist/trace/project.d.ts +59 -44
  731. package/dist/trace/project.js +60 -26
  732. package/dist/trace/redact.d.ts +135 -27
  733. package/dist/trace/redact.js +884 -260
  734. package/dist/trace/self-minted-wire.d.ts +567 -20
  735. package/dist/trace/self-minted-wire.js +205 -24
  736. package/dist/trace/wire-projection-faces.d.ts +5 -6
  737. package/dist/trace/wire-projection-faces.js +2 -2
  738. package/dist/upstream-body.d.ts +23 -0
  739. package/dist/upstream-body.js +16 -0
  740. package/dist/url-refusal.d.ts +3 -3
  741. package/dist/url-refusal.js +11 -4
  742. package/dist/user-authored-text.d.ts +1 -1
  743. package/dist/user-input-ledger.d.ts +7 -8
  744. package/dist/user-input-ledger.js +1 -1
  745. package/dist/web-admin-client/apply-effective.d.ts +6 -3
  746. package/dist/web-admin-client/apply-effective.js +33 -23
  747. package/dist/web-admin-client/apply-ledger.d.ts +16 -7
  748. package/dist/web-admin-client/apply-ledger.js +5 -2
  749. package/dist/web-admin-client/center-request.d.ts +2 -2
  750. package/dist/web-admin-client/center-request.js +15 -15
  751. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  752. package/dist/web-admin-client/effective-keys.js +1 -1
  753. package/dist/web-admin-client/entry-metadata.d.ts +8 -0
  754. package/dist/web-admin-client/entry-metadata.js +13 -0
  755. package/dist/web-admin-client/http-client.d.ts +1 -1
  756. package/dist/web-admin-client/http-client.js +23 -4
  757. package/dist/web-admin-client/mcp-revocation.d.ts +1 -1
  758. package/dist/web-admin-client/mcp-revocation.js +2 -1
  759. package/dist/web-admin-client/read-warnings.d.ts +1 -0
  760. package/dist/web-admin-client/read-warnings.js +1 -0
  761. package/dist/web-admin-client/restart-signal.js +8 -4
  762. package/dist/web-admin-client/skills-mcp.d.ts +2 -2
  763. package/dist/web-admin-client/types.d.ts +1 -2
  764. package/dist/workspace-space.d.ts +3 -2
  765. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +4 -4
  766. package/docs/error-text-lexicon.json +317 -0
  767. package/package.json +8 -3
  768. package/scripts/drain-check-local.mjs +118 -147
  769. package/scripts/drain-check.mysql.sql +22 -2
  770. package/scripts/drain-check.pg.sql +22 -2
  771. package/scripts/embed-minimal.mjs +183 -0
  772. package/wire-changes/server@7.148.0.json +1517 -0
  773. package/wire-changes/server@7.149.0.json +372 -0
  774. package/dist/http/memory-control-plane-corrupt-reply.d.ts +0 -25
  775. package/dist/http/memory-control-plane-corrupt-reply.js +0 -19
  776. package/dist/http/memory-layer-reply.d.ts +0 -30
  777. package/dist/http/memory-layer-reply.js +0 -24
  778. package/dist/lsp/e2b-bridge.d.ts +0 -57
  779. package/dist/lsp/e2b-bridge.js +0 -139
  780. package/dist/lsp/e2b-manager.d.ts +0 -93
  781. package/dist/lsp/e2b-manager.js +0 -178
  782. package/dist/lsp/lsp-frames.d.ts +0 -14
  783. package/dist/lsp/lsp-frames.js +0 -25
  784. package/dist/lsp/manager.d.ts +0 -129
  785. package/dist/lsp/manager.js +0 -127
  786. package/dist/lsp/types.d.ts +0 -12
  787. package/dist/lsp/types.js +0 -2
  788. package/dist/lsp/ws-transport.d.ts +0 -40
  789. package/dist/lsp/ws-transport.js +0 -210
  790. package/dist/plain-string-record.js +0 -70
@@ -29,6 +29,7 @@ export function mapNormalizedToOriginal(text, normalized) {
29
29
  return map;
30
30
  }
31
31
  const SPELLED_FORMAT = 1, SPELLED_CONTROL = 2, RAW_CONTROL = 4, RAW_WHITESPACE = 8;
32
+ const RAW_IGNORABLE = 16, SPELLED_IGNORABLE = 32;
32
33
  const INVISIBLE_UNIT_CLASS = (() => {
33
34
  const inSet = new RegExp(`[${CONTROL_AND_BIDI_CHARS}]`);
34
35
  const table = new Uint8Array(0x10000);
@@ -39,32 +40,72 @@ const INVISIBLE_UNIT_CLASS = (() => {
39
40
  }
40
41
  return table;
41
42
  })();
42
- const RAW_STRIP_SET_RE = (() => {
43
- let body = "";
44
- for (let unit = 0; unit < 0x10000; unit++) {
45
- if (INVISIBLE_UNIT_CLASS[unit] === 0)
46
- continue;
47
- let last = unit;
48
- while (last + 1 < 0x10000 && INVISIBLE_UNIT_CLASS[last + 1] !== 0)
49
- last += 1;
50
- const hex = (u) => `\\u${u.toString(16).padStart(4, "0")}`;
51
- body += last === unit ? hex(unit) : `${hex(unit)}-${hex(last)}`;
52
- unit = last;
53
- }
54
- return new RegExp(`[${body}]`, "g");
43
+ const READING_IGNORABLE_CLASS = String.raw `\p{Default_Ignorable_Code_Point}\p{Mn}\p{Cc}`;
44
+ const READING_IGNORABLE_RE = new RegExp(`^[${READING_IGNORABLE_CLASS}]$`, "u");
45
+ function isReadingIgnorable(cp) {
46
+ if (cp < 0 || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff))
47
+ return false;
48
+ return READING_IGNORABLE_RE.test(String.fromCodePoint(cp));
49
+ }
50
+ export function isReadingIgnorableForTest(cp) {
51
+ return isReadingIgnorable(cp);
52
+ }
53
+ const RAW_UNIT_CLASS = (() => {
54
+ const table = new Uint8Array(0x10000);
55
+ for (let unit = 0; unit < table.length; unit++) {
56
+ const cls = INVISIBLE_UNIT_CLASS[unit];
57
+ if (cls !== 0)
58
+ table[unit] = cls === 2 ? RAW_WHITESPACE : RAW_CONTROL;
59
+ }
60
+ const units = [];
61
+ for (let c = 0; c < 0x10000; c++)
62
+ if (c < 0xd800 || c > 0xdfff)
63
+ units.push(String.fromCharCode(c));
64
+ for (const m of units.join("").matchAll(new RegExp(`[${READING_IGNORABLE_CLASS}]`, "gu"))) {
65
+ const unit = m[0].charCodeAt(0);
66
+ if (table[unit] === 0)
67
+ table[unit] = RAW_IGNORABLE;
68
+ }
69
+ return table;
55
70
  })();
71
+ const ASTRAL_IGNORABLE_MEMO = new Map();
72
+ function astralIgnorableAt(cp) {
73
+ let hit = ASTRAL_IGNORABLE_MEMO.get(cp);
74
+ if (hit === undefined) {
75
+ hit = isReadingIgnorable(cp);
76
+ if (ASTRAL_IGNORABLE_MEMO.size < 4096)
77
+ ASTRAL_IGNORABLE_MEMO.set(cp, hit);
78
+ }
79
+ return hit;
80
+ }
56
81
  function collectRawControlCuts(s, allowed) {
57
82
  const cuts = [];
58
83
  let present = 0;
59
- if ((allowed & (RAW_CONTROL | RAW_WHITESPACE)) === 0)
84
+ if ((allowed & (RAW_CONTROL | RAW_WHITESPACE | RAW_IGNORABLE)) === 0)
60
85
  return { cuts, present };
61
- RAW_STRIP_SET_RE.lastIndex = 0;
62
- for (let m = RAW_STRIP_SET_RE.exec(s); m !== null; m = RAW_STRIP_SET_RE.exec(s)) {
63
- const cls = INVISIBLE_UNIT_CLASS[s.charCodeAt(m.index)] === 2 ? RAW_WHITESPACE : RAW_CONTROL;
64
- if ((cls & allowed) === 0)
65
- continue;
66
- cuts.push(m.index, m.index + 1, cls);
67
- present |= cls;
86
+ const n = s.length;
87
+ for (let i = 0; i < n; i++) {
88
+ const unit = s.charCodeAt(i);
89
+ let cls = RAW_UNIT_CLASS[unit];
90
+ let len = 1;
91
+ if (cls === 0) {
92
+ if ((unit & 0xfc00) !== 0xd800 || i + 1 >= n)
93
+ continue;
94
+ const low = s.charCodeAt(i + 1);
95
+ if ((low & 0xfc00) !== 0xdc00)
96
+ continue;
97
+ if (!astralIgnorableAt(((unit - 0xd800) << 10) + (low - 0xdc00) + 0x10000)) {
98
+ i += 1;
99
+ continue;
100
+ }
101
+ cls = RAW_IGNORABLE;
102
+ len = 2;
103
+ }
104
+ if ((cls & allowed) !== 0) {
105
+ cuts.push(i, i + len, cls);
106
+ present |= cls;
107
+ }
108
+ i += len - 1;
68
109
  }
69
110
  return { cuts, present };
70
111
  }
@@ -83,7 +124,8 @@ function collectSpelledCuts(s) {
83
124
  const cp = readSpelledCodePoint(s, body, read);
84
125
  if (cp === ESCAPE_TRUNCATED)
85
126
  return { cuts, present, open: i };
86
- const cls = cp < 0 ? 0 : isFormatCodePoint(cp) ? SPELLED_FORMAT : cp < 0x10000 && INVISIBLE_UNIT_CLASS[cp] === 1 ? SPELLED_CONTROL : 0;
127
+ const unitClass = cp >= 0 && cp < 0x10000 ? INVISIBLE_UNIT_CLASS[cp] : 0;
128
+ const cls = cp < 0 ? 0 : isFormatCodePoint(cp) ? SPELLED_FORMAT : unitClass === 1 ? SPELLED_CONTROL : unitClass === 2 ? 0 : isReadingIgnorable(cp) ? SPELLED_IGNORABLE : 0;
87
129
  if (cls !== 0) {
88
130
  cuts.push(i, read.end, cls);
89
131
  present |= cls;
@@ -465,6 +507,88 @@ let anyValueArm;
465
507
  function anyValueArmRe() {
466
508
  return (anyValueArm ??= new RegExp(`(?:${VALUE_ARM_BODIES.join("|")})`, "y"));
467
509
  }
510
+ const SCAN_UNATTEMPTED = -1;
511
+ class ScanReader {
512
+ #t;
513
+ #n;
514
+ #reg = SCAN_UNATTEMPTED;
515
+ #hit = false;
516
+ #open;
517
+ #unattributed = false;
518
+ constructor(t) {
519
+ this.#t = t;
520
+ this.#n = t.length;
521
+ this.#open = t.length;
522
+ }
523
+ #mark() {
524
+ this.#hit = true;
525
+ if (this.#reg === SCAN_UNATTEMPTED) {
526
+ this.#unattributed = true;
527
+ this.#open = 0;
528
+ }
529
+ else if (this.#reg < this.#open)
530
+ this.#open = this.#reg;
531
+ }
532
+ charCodeAt(i) {
533
+ if (i < this.#n)
534
+ return this.#t.charCodeAt(i);
535
+ this.#mark();
536
+ return NaN;
537
+ }
538
+ atEnd(i) {
539
+ if (i < this.#n)
540
+ return false;
541
+ this.#mark();
542
+ return true;
543
+ }
544
+ readEnd() {
545
+ this.#mark();
546
+ return this.#n;
547
+ }
548
+ startsWith(lit, at) {
549
+ if (at + lit.length <= this.#n)
550
+ return this.#t.startsWith(lit, at);
551
+ for (let k = 0; at + k < this.#n; k++)
552
+ if (this.#t.charCodeAt(at + k) !== lit.charCodeAt(k))
553
+ return false;
554
+ this.#mark();
555
+ return false;
556
+ }
557
+ slice(a, b) {
558
+ if (b > this.#n)
559
+ this.#mark();
560
+ return this.#t.slice(a, b);
561
+ }
562
+ indexOf(lit, from = 0) {
563
+ return this.#t.indexOf(lit, from);
564
+ }
565
+ search(re, from) {
566
+ re.lastIndex = from;
567
+ return re.exec(this.#t);
568
+ }
569
+ lastIndexOf(lit) {
570
+ return this.#t.lastIndexOf(lit);
571
+ }
572
+ attempt(from) {
573
+ this.#reg = from;
574
+ this.#hit = false;
575
+ }
576
+ reachedEnd() {
577
+ return this.#hit;
578
+ }
579
+ begin() {
580
+ this.#reg = SCAN_UNATTEMPTED;
581
+ this.#hit = false;
582
+ }
583
+ get open() {
584
+ return this.#open;
585
+ }
586
+ get unattributed() {
587
+ return this.#unattributed;
588
+ }
589
+ }
590
+ const VIEW_END = 0x3fffffff;
591
+ const segmentEnds = (s, i, limit) => i >= limit || s.atEnd(i);
468
592
  const ARM_RANK = { structural: 0, keyword: 1, shape: 2, fragment: 3 };
469
593
  let poolSpanCount = 0;
470
594
  let poolArmCount = 0;
@@ -737,6 +861,112 @@ function deriveLiteralHead(re) {
737
861
  first: unionMembers(per.map((p) => p.first)),
738
862
  };
739
863
  }
864
+ function expandHeadUnits(src, flags) {
865
+ if (/[iuv]/.test(flags))
866
+ throw new Error(`redact: head-unit expansion does not support the i/u/v flags (/${src}/${flags})`);
867
+ const fail = (at, why) => {
868
+ throw new Error(`redact: head-unit expansion cannot read ${why} at ${at} in /${src}/`);
869
+ };
870
+ const LIMIT = 4096;
871
+ const seqOf = (from) => {
872
+ let seqs = [[]];
873
+ let i = from;
874
+ while (i < src.length && src[i] !== "|" && src[i] !== ")") {
875
+ let atom;
876
+ const c = src[i];
877
+ if (c === "(") {
878
+ if (!src.startsWith("(?:", i))
879
+ return fail(i, "a group other than a non-capturing one");
880
+ const inner = altsOf(i + 3);
881
+ if (src[inner.next] !== ")")
882
+ return fail(i, "an unbalanced group");
883
+ atom = inner.seqs;
884
+ i = inner.next + 1;
885
+ }
886
+ else if (c === "[") {
887
+ let k = i + 1;
888
+ while (k < src.length && src[k] !== "]")
889
+ k += src[k] === "\\" ? 2 : 1;
890
+ if (k >= src.length)
891
+ return fail(i, "an unterminated character class");
892
+ atom = [[codeUnitMembers(src.slice(i, k + 1))]];
893
+ i = k + 1;
894
+ }
895
+ else if (c === "\\") {
896
+ const e = src[i + 1];
897
+ if (e === undefined || !(HEAD_CLASS_ESCAPES.has(e) || HEAD_LITERAL_ESCAPES.has(e)))
898
+ return fail(i, "an escape that is not one code unit");
899
+ atom = [[codeUnitMembers(src.slice(i, i + 2))]];
900
+ i += 2;
901
+ }
902
+ else if (c === "." || c === "^" || c === "$" || c === "*" || c === "+" || c === "?" || c === "{") {
903
+ return fail(i, JSON.stringify(c));
904
+ }
905
+ else {
906
+ atom = [[codeUnitMembers(c)]];
907
+ i += 1;
908
+ }
909
+ let lo = 1;
910
+ let hi = 1;
911
+ if (src[i] === "?") {
912
+ lo = 0;
913
+ i += 1;
914
+ }
915
+ else if (src[i] === "{") {
916
+ const m = /^\{(\d+)(?:,(\d+))?\}/.exec(src.slice(i));
917
+ if (m === null)
918
+ return fail(i, "an unbounded quantifier");
919
+ lo = Number(m[1]);
920
+ hi = m[2] === undefined ? lo : Number(m[2]);
921
+ i += m[0].length;
922
+ }
923
+ else if (src[i] === "*" || src[i] === "+")
924
+ return fail(i, "an unbounded quantifier");
925
+ let reps = [];
926
+ let power = [[]];
927
+ for (let k = 0; k <= hi; k++) {
928
+ if (k >= lo)
929
+ reps = reps.concat(power);
930
+ if (k < hi)
931
+ power = power.flatMap((p) => atom.map((a) => [...p, ...a]));
932
+ }
933
+ seqs = seqs.flatMap((s) => reps.map((r) => [...s, ...r]));
934
+ if (seqs.length > LIMIT)
935
+ return fail(i, "a head with too many spellings");
936
+ }
937
+ return { seqs, next: i };
938
+ };
939
+ const altsOf = (from) => {
940
+ const seqs = [];
941
+ let i = from;
942
+ for (;;) {
943
+ const one = seqOf(i);
944
+ seqs.push(...one.seqs);
945
+ i = one.next;
946
+ if (src[i] !== "|")
947
+ break;
948
+ i += 1;
949
+ }
950
+ return { seqs, next: i };
951
+ };
952
+ const all = altsOf(0);
953
+ if (all.next !== src.length)
954
+ return fail(all.next, "trailing syntax");
955
+ if (all.seqs.some((s) => s.length === 0))
956
+ return fail(0, "an empty head spelling (every position would be feasible)");
957
+ return all.seqs;
958
+ }
959
+ function headFeasibleAt(seqs, text, p) {
960
+ const room = text.length - p;
961
+ outer: for (const seq of seqs) {
962
+ const m = Math.min(seq.length, room);
963
+ for (let k = 0; k < m; k++)
964
+ if (unitBit(seq[k], text.charCodeAt(p + k)) === 0)
965
+ continue outer;
966
+ return true;
967
+ }
968
+ return false;
969
+ }
740
970
  const repeatLen = (k, len) => (k === 0 ? 0 : k * len);
741
971
  function readReachAlternatives(src, from) {
742
972
  const alts = [];
@@ -921,17 +1151,56 @@ function resolveSpanOverlaps(spans) {
921
1151
  }
922
1152
  return kept;
923
1153
  }
924
- function applySpans(text, spans) {
925
- if (spans.length === 0)
926
- return { out: text, count: 0 };
927
- const parts = [];
928
- let copied = 0;
1154
+ const NO_JOINTS = Object.freeze([]);
1155
+ const sortedJoints = (joints) => [...new Set(joints)].sort((a, b) => a - b);
1156
+ function firstJointAfter(at, start) {
1157
+ let lo = 0;
1158
+ let hi = at.length;
1159
+ while (lo < hi) {
1160
+ const mid = (lo + hi) >>> 1;
1161
+ if (at[mid] <= start)
1162
+ lo = mid + 1;
1163
+ else
1164
+ hi = mid;
1165
+ }
1166
+ return lo;
1167
+ }
1168
+ function jointsCrossedBy(spans, joints) {
1169
+ if (joints.length === 0)
1170
+ return [];
1171
+ const at = sortedJoints(joints);
1172
+ const crossed = new Set();
1173
+ for (const s of spans)
1174
+ for (let i = firstJointAfter(at, s.start); i < at.length && at[i] < s.end; i++)
1175
+ crossed.add(at[i]);
1176
+ return [...crossed].sort((a, b) => a - b);
1177
+ }
1178
+ function cutAtJoints(spans, joints) {
1179
+ const at = sortedJoints(joints);
1180
+ const out = [];
929
1181
  for (const s of spans) {
930
- parts.push(text.slice(copied, s.start), s.repl);
931
- copied = s.end;
1182
+ let start = s.start;
1183
+ for (let i = firstJointAfter(at, s.start); i < at.length && at[i] < s.end; i++) {
1184
+ out.push({ ...s, start, end: at[i] });
1185
+ start = at[i];
1186
+ }
1187
+ out.push(start === s.start ? s : { ...s, start });
932
1188
  }
933
- parts.push(text.slice(copied));
934
- return { out: parts.join(""), count: spans.length };
1189
+ return out;
1190
+ }
1191
+ const poolTie = (a, b) => a.rank - b.rank || a.order - b.order;
1192
+ export function mergeRedactionSpans(spans, tie = () => 0, joints = NO_JOINTS) {
1193
+ const order = (joints.length === 0 ? [...spans] : cutAtJoints(spans, joints)).sort((a, b) => a.start - b.start || b.end - a.end || tie(a, b));
1194
+ const committed = joints.length === 0 ? undefined : new Set(joints);
1195
+ const out = [];
1196
+ for (const s of order) {
1197
+ const last = out[out.length - 1];
1198
+ if (last === undefined || s.start > last.end || (s.start === last.end && committed?.has(s.start) === true))
1199
+ out.push(s);
1200
+ else if (s.end > last.end)
1201
+ out[out.length - 1] = { ...last, end: s.end };
1202
+ }
1203
+ return out;
935
1204
  }
936
1205
  function collectValueArmSpans(arm, text, out) {
937
1206
  const { g, y, back, repl, rank, order } = arm;
@@ -1163,7 +1432,7 @@ const SHAPE_SECRET_PATTERNS = [
1163
1432
  [new RegExp(String.raw `xox[baprs]-${atLeast("[A-Za-z0-9-]", 10)}`, "g"), "«redacted:slack-token»"],
1164
1433
  [new RegExp(String.raw `AKIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
1165
1434
  [new RegExp(String.raw `ASIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
1166
- [new RegExp(String.raw `(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-${atLeast("[A-Za-z0-9_-]", 20)}|(?:sk|rk)_(?:live|test|org)_${atLeast("[A-Za-z0-9]", 24)}|pypi-${atLeast("[A-Za-z0-9_-]", 85)})`, "g"), "«redacted:key»"],
1435
+ [new RegExp(String.raw `(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-${atLeast("[A-Za-z0-9_-]", 20)}|(?:sk|rk)_(?:live|test|org)_${atLeast("[A-Za-z0-9]", 24)}|pypi-${atLeast("[A-Za-z0-9_-]", 85)}|(?<![A-Za-z0-9])(?:npm_${atLeast("[A-Za-z0-9]", 36)}|hf_${atLeast("[A-Za-z0-9]", 34)}|ya29\.${atLeast("[A-Za-z0-9._-]", 30)}))`, "g"), "«redacted:key»"],
1167
1436
  [new RegExp(String.raw `xapp-\d-${atLeast("[A-Za-z0-9-]", 20)}`, "g"), "«redacted:slack-token»"],
1168
1437
  [valueArm("sk-", "[A-Za-z0-9_-]", SK_FULL_ARM_MIN, "self-evident"), "«redacted:key»"],
1169
1438
  ];
@@ -1176,16 +1445,16 @@ const isIdentStartCode = (c) => (c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= C
1176
1445
  const isIdentCode = (c) => isIdentStartCode(c) || (c >= CH_0 && c <= CH_9) || c === CH_UNDERSCORE || c === CH_MINUS;
1177
1446
  function pemEscapeLetterAt(s, i, limit) {
1178
1447
  let j = i;
1179
- while (j < limit && s.charCodeAt(j) === CH_BACKSLASH)
1448
+ while (!segmentEnds(s, j, limit) && s.charCodeAt(j) === CH_BACKSLASH)
1180
1449
  j += 1;
1181
- if (j === i || j >= limit)
1450
+ if (j === i || segmentEnds(s, j, limit))
1182
1451
  return -1;
1183
1452
  const n = s.charCodeAt(j);
1184
1453
  return n === CH_r || n === CH_n || n === CH_t ? j : -1;
1185
1454
  }
1186
1455
  function skipPemSeps(scan, s, i, limit) {
1187
1456
  for (;;) {
1188
- if (i >= limit)
1457
+ if (segmentEnds(s, i, limit))
1189
1458
  return i;
1190
1459
  if (tokenEnd(scan, i) >= 0)
1191
1460
  return i;
@@ -1205,7 +1474,7 @@ function skipPemSeps(scan, s, i, limit) {
1205
1474
  }
1206
1475
  }
1207
1476
  function pemLineBreakLen(s, i, limit) {
1208
- if (i >= limit)
1477
+ if (segmentEnds(s, i, limit))
1209
1478
  return 0;
1210
1479
  const c = s.charCodeAt(i);
1211
1480
  if (c === CH_CR || c === CH_LF)
@@ -1218,7 +1487,7 @@ function pemLineBreakLen(s, i, limit) {
1218
1487
  return 0;
1219
1488
  }
1220
1489
  function pemInlineSepLen(s, i, limit) {
1221
- if (i >= limit)
1490
+ if (segmentEnds(s, i, limit))
1222
1491
  return 0;
1223
1492
  const c = s.charCodeAt(i);
1224
1493
  if (c === CH_SPACE || c === CH_TAB)
@@ -1231,16 +1500,20 @@ function pemInlineSepLen(s, i, limit) {
1231
1500
  return 0;
1232
1501
  }
1233
1502
  function probePemHeaderToken(scan, s, p, limit) {
1234
- if (p >= limit || tokenEnd(scan, p) >= 0 || !isIdentStartCode(s.charCodeAt(p)))
1503
+ if (segmentEnds(s, p, limit))
1504
+ return -1;
1505
+ if (tokenEnd(scan, p) >= 0 || !isIdentStartCode(s.charCodeAt(p)))
1235
1506
  return -1;
1236
1507
  p += 1;
1237
1508
  let identLen = 1;
1238
- while (identLen < PEM_HEADER_IDENT_MAX && p < limit && tokenEnd(scan, p) < 0 && isIdentCode(s.charCodeAt(p))) {
1509
+ while (identLen < PEM_HEADER_IDENT_MAX && !segmentEnds(s, p, limit) && tokenEnd(scan, p) < 0 && isIdentCode(s.charCodeAt(p))) {
1239
1510
  p += 1;
1240
1511
  identLen += 1;
1241
1512
  }
1242
1513
  for (;;) {
1243
- if (p >= limit || tokenEnd(scan, p) >= 0)
1514
+ if (segmentEnds(s, p, limit))
1515
+ return -1;
1516
+ if (tokenEnd(scan, p) >= 0)
1244
1517
  return -1;
1245
1518
  const c = s.charCodeAt(p);
1246
1519
  if (c === CH_SPACE || c === CH_TAB) {
@@ -1261,7 +1534,7 @@ function setPemHdr(scan, search, contentEnd, next, hitLimit, jsonEnd = false) {
1261
1534
  }
1262
1535
  function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1263
1536
  const start = skipPemSeps(scan, s, i, limit);
1264
- if (start >= limit)
1537
+ if (segmentEnds(s, start, limit))
1265
1538
  return false;
1266
1539
  const afterToken = probePemHeaderToken(scan, s, start, limit);
1267
1540
  if (afterToken < 0 && !allowBare)
@@ -1270,7 +1543,7 @@ function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1270
1543
  if (afterToken >= 0 && probePemHeaderToken(scan, s, p, limit) >= 0)
1271
1544
  return setPemHdr(scan, start, p, p, false);
1272
1545
  for (;;) {
1273
- if (p >= limit)
1546
+ if (segmentEnds(s, p, limit))
1274
1547
  return setPemHdr(scan, start, p, p, true);
1275
1548
  const unit = crossEnd(scan, p);
1276
1549
  if (unit >= 0) {
@@ -1292,9 +1565,9 @@ function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1292
1565
  return setPemHdr(scan, start, p, p + 1, false);
1293
1566
  if (c === CH_BACKSLASH) {
1294
1567
  let j = p;
1295
- while (j < limit && s.charCodeAt(j) === CH_BACKSLASH)
1568
+ while (!segmentEnds(s, j, limit) && s.charCodeAt(j) === CH_BACKSLASH)
1296
1569
  j += 1;
1297
- const n = j < limit ? s.charCodeAt(j) : -1;
1570
+ const n = segmentEnds(s, j, limit) ? -1 : s.charCodeAt(j);
1298
1571
  if (n !== CH_r && n !== CH_n && n !== CH_t) {
1299
1572
  p = json && (j - p) % 2 === 0 && n === CH_QUOTE ? j : Math.min(limit, j + 1);
1300
1573
  continue;
@@ -1316,7 +1589,7 @@ function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1316
1589
  break;
1317
1590
  q += u;
1318
1591
  }
1319
- if (q >= limit)
1592
+ if (segmentEnds(s, q, limit))
1320
1593
  return setPemHdr(scan, start, p, q, true);
1321
1594
  if (probePemHeaderToken(scan, s, q, limit) >= 0)
1322
1595
  return setPemHdr(scan, start, p, q, false);
@@ -1329,7 +1602,7 @@ function parsePemBody(scan, s, i, limit) {
1329
1602
  for (;;) {
1330
1603
  const q = skipPemSeps(scan, s, p, limit);
1331
1604
  let r = q;
1332
- while (r < limit && tokenEnd(scan, r) < 0 && isBase64Code(s.charCodeAt(r)))
1605
+ while (!segmentEnds(s, r, limit) && tokenEnd(scan, r) < 0 && isBase64Code(s.charCodeAt(r)))
1333
1606
  r += 1;
1334
1607
  if (r - q < PEM_BODY_RUN_MIN)
1335
1608
  break;
@@ -1439,13 +1712,24 @@ function findPemFence(scan, text, from) {
1439
1712
  }
1440
1713
  const SSH2_FENCE_OPEN = "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----";
1441
1714
  const SSH2_FENCE_CLOSE = "---- END SSH2 ENCRYPTED PRIVATE KEY ----";
1442
- const SSH2_BODY_MAX = 8192;
1715
+ const ARMOR_BODY_MAX = (() => {
1716
+ const caps = STRUCTURAL_SECRET_PATTERNS.flatMap(([re]) => {
1717
+ const m = /\[\\s\\S\]\{0,(\d+)\}\?/.exec(re.source);
1718
+ return m === null ? [] : [Number(m[1])];
1719
+ });
1720
+ const first = caps[0];
1721
+ if (caps.length !== 2 || first === undefined || caps.some((c) => c !== first)) {
1722
+ throw new Error(`redact: the PEM and SSH2 armor rows of the structural table must each declare one body cap of the same size (read ${JSON.stringify(caps)})`);
1723
+ }
1724
+ return first;
1725
+ })();
1726
+ export const MAX_RECOGNITION_SPAN = SSH2_FENCE_OPEN.length + ARMOR_BODY_MAX + SSH2_FENCE_CLOSE.length;
1443
1727
  function armorCloseAt(scan, text, kind, bodyFrom, max) {
1444
- const near = firstDelim(scan, text, kind, bodyFrom, false);
1728
+ const near = firstDelim(scan, kind, bodyFrom, false);
1445
1729
  if (near < 0)
1446
1730
  return -1;
1447
- const free = firstDelim(scan, text, kind, bodyFrom, true);
1448
- const far = free >= 0 ? free : lastNotHiddenDelim(scan, text, kind, near, text.length);
1731
+ const free = firstDelim(scan, kind, bodyFrom, true);
1732
+ const far = free >= 0 ? free : lastNotHiddenDelim(scan, kind, near, VIEW_END);
1449
1733
  if (far > near && blockWithin(scan, text, kind, bodyFrom, far, max))
1450
1734
  return far;
1451
1735
  return blockWithin(scan, text, kind, bodyFrom, near, max) ? near : -1;
@@ -1459,8 +1743,8 @@ function blockWithin(scan, text, kind, bodyFrom, close, max) {
1459
1743
  }
1460
1744
  function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1461
1745
  const fam = familyCode("ssh2-armor");
1462
- const opens = delimPositions(scan, text, "ssh2-open");
1463
- const closes = delimPositions(scan, text, "ssh2-close");
1746
+ const opens = delimPositions(scan, "ssh2-open");
1747
+ const closes = delimPositions(scan, "ssh2-close");
1464
1748
  const blockEnds = [];
1465
1749
  let firstLive = 0;
1466
1750
  let freshFrom = -1;
@@ -1475,13 +1759,13 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1475
1759
  }
1476
1760
  if (rooted && prevEnd >= 0) {
1477
1761
  scan.self = readingKey(fam, c);
1478
- while (firstLive < blockEnds.length && uncoveredWithin(scan, blockEnds[firstLive], c + SSH2_FENCE_CLOSE.length) > SSH2_BODY_MAX + SSH2_FENCE_CLOSE.length)
1762
+ while (firstLive < blockEnds.length && uncoveredWithin(scan, blockEnds[firstLive], c + SSH2_FENCE_CLOSE.length) > ARMOR_BODY_MAX + SSH2_FENCE_CLOSE.length)
1479
1763
  firstLive += 1;
1480
1764
  for (let j = firstLive; j < blockEnds.length; j++) {
1481
1765
  const from = blockEnds[j];
1482
1766
  if (from > c)
1483
1767
  break;
1484
- if ((from === freshFrom && blockWithin(scan, text, "ssh2-close", freshEnd, c, SSH2_BODY_MAX - 1)) || blockWithin(scan, text, "ssh2-close", from, c, SSH2_BODY_MAX)) {
1768
+ if ((from === freshFrom && blockWithin(scan, text, "ssh2-close", freshEnd, c, ARMOR_BODY_MAX - 1)) || blockWithin(scan, text, "ssh2-close", from, c, ARMOR_BODY_MAX)) {
1485
1769
  span(from, c + SSH2_FENCE_CLOSE.length, c, NOT_CLOSED);
1486
1770
  freshFrom = from;
1487
1771
  freshEnd = c + SSH2_FENCE_CLOSE.length;
@@ -1506,7 +1790,7 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1506
1790
  openAt = k < opens.length ? opens[k] : -1;
1507
1791
  return openAt;
1508
1792
  };
1509
- const truncatedEnd = createTruncatedBlockEnd(scan, text, json, (from) => firstDelim(scan, text, "ssh2-close", from, true));
1793
+ const truncatedEnd = createTruncatedBlockEnd(scan, text, json, (from) => firstDelim(scan, "ssh2-close", from, true));
1510
1794
  for (let oi = 0; oi < opens.length;) {
1511
1795
  const fence = opens[oi];
1512
1796
  const bodyFrom = fence + SSH2_FENCE_OPEN.length;
@@ -1515,30 +1799,31 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1515
1799
  continue;
1516
1800
  }
1517
1801
  scan.self = readingKey(fam, fence);
1518
- if (isHead && measureWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
1519
- span(fence, text.length, fence, NOT_CLOSED, true);
1802
+ text.attempt(fence);
1803
+ if (isHead && measureWithin(scan, bodyFrom, text.readEnd(), ARMOR_BODY_MAX)) {
1804
+ span(fence, text.readEnd(), fence, NOT_CLOSED, true);
1520
1805
  return;
1521
1806
  }
1522
1807
  let resume = fence + 1;
1523
1808
  if (!isHead) {
1524
1809
  const open = nextFreeOpen(bodyFrom);
1525
- const limit = open < 0 ? text.length : open;
1810
+ const limit = open < 0 ? VIEW_END : open;
1526
1811
  const readEnd = (crossEndsRun) => {
1527
1812
  const t = truncatedEnd(bodyFrom, limit, crossEndsRun);
1528
1813
  const bodyEnd = t < 0 ? -1 : armorBodyEnd(scan, text, json, bodyFrom, limit, false, crossEndsRun);
1529
1814
  return bodyEnd < 0 ? -1 : Math.max(bodyEnd, t);
1530
1815
  };
1531
1816
  const far = readEnd(false);
1532
- const near = far >= 0 && !measureWithin(scan, bodyFrom, far, SSH2_BODY_MAX) ? readEnd(true) : -1;
1817
+ const near = far >= 0 && !measureWithin(scan, bodyFrom, far, ARMOR_BODY_MAX) ? readEnd(true) : -1;
1533
1818
  const end = near >= 0 && near < far ? near : far;
1534
- if (end >= 0 && measureWithin(scan, bodyFrom, end, SSH2_BODY_MAX)) {
1819
+ if (end >= 0 && measureWithin(scan, bodyFrom, end, ARMOR_BODY_MAX)) {
1535
1820
  span(fence, end, fence, NOT_CLOSED);
1536
1821
  resume = end;
1537
1822
  }
1538
1823
  }
1539
- const close = armorCloseAt(scan, text, "ssh2-close", bodyFrom, SSH2_BODY_MAX);
1824
+ const close = armorCloseAt(scan, text, "ssh2-close", bodyFrom, ARMOR_BODY_MAX);
1540
1825
  if (close < 0) {
1541
- if (isHead && firstDelim(scan, text, "ssh2-close", bodyFrom, false) < 0)
1826
+ if (isHead && firstDelim(scan, "ssh2-close", bodyFrom, false) < 0)
1542
1827
  return;
1543
1828
  oi = lowerBound(opens, resume);
1544
1829
  continue;
@@ -1549,16 +1834,16 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1549
1834
  }
1550
1835
  }
1551
1836
  const PEM_END_OPEN = "-----END";
1552
- const PEM_FENCE_BODY_MAX = 8192;
1553
1837
  const PEM_LINE_INVALID = -1;
1554
1838
  const PEM_LINE_OPEN = -2;
1555
1839
  function pemFenceLineEnd(text, labelFrom) {
1556
1840
  const close = text.indexOf(PEM_FENCE_CLOSE, labelFrom);
1557
1841
  if (close < 0) {
1558
- let to = text.length;
1842
+ const n = text.readEnd();
1843
+ let to = n;
1559
1844
  while (to > labelFrom && text.charCodeAt(to - 1) === 45)
1560
1845
  to -= 1;
1561
- const stillPossible = text.length - to >= 2 ? isPemFenceLabel(text, labelFrom, to) : !pemLabelDashRuleBroken(text, labelFrom, to);
1846
+ const stillPossible = n - to >= 2 ? isPemFenceLabel(text, labelFrom, to) : !pemLabelDashRuleBroken(text, labelFrom, to);
1562
1847
  return stillPossible ? PEM_LINE_OPEN : PEM_LINE_INVALID;
1563
1848
  }
1564
1849
  return isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : PEM_LINE_INVALID;
@@ -1569,23 +1854,24 @@ function collectPemFenceSpans(scan, text, span, isHead) {
1569
1854
  const fence = text.indexOf(PEM_FENCE_OPEN, from);
1570
1855
  if (fence < 0)
1571
1856
  return;
1857
+ text.attempt(fence);
1572
1858
  const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
1573
1859
  if (bodyFrom === PEM_LINE_INVALID) {
1574
1860
  from = fence + 1;
1575
1861
  continue;
1576
1862
  }
1577
- if (lineHeld(scan, fence, bodyFrom === PEM_LINE_OPEN ? text.length : bodyFrom, readingKey(fam, fence))) {
1863
+ if (lineHeld(scan, fence, bodyFrom === PEM_LINE_OPEN ? text.readEnd() : bodyFrom, readingKey(fam, fence))) {
1578
1864
  from = fence + 1;
1579
1865
  continue;
1580
1866
  }
1581
1867
  scan.self = readingKey(fam, fence);
1582
- const near = bodyFrom === PEM_LINE_OPEN ? -1 : firstDelim(scan, text, "pem-end", bodyFrom, false);
1583
- if (near < 0 || (isHead && firstDelim(scan, text, "pem-end", bodyFrom, true) < 0)) {
1868
+ const near = bodyFrom === PEM_LINE_OPEN ? -1 : firstDelim(scan, "pem-end", bodyFrom, false);
1869
+ if (near < 0 || (isHead && firstDelim(scan, "pem-end", bodyFrom, true) < 0)) {
1584
1870
  if (isHead)
1585
- span(fence, text.length, fence, NOT_CLOSED, true);
1871
+ span(fence, text.readEnd(), fence, NOT_CLOSED, true);
1586
1872
  return;
1587
1873
  }
1588
- const endAt = armorCloseAt(scan, text, "pem-end", bodyFrom, PEM_FENCE_BODY_MAX);
1874
+ const endAt = armorCloseAt(scan, text, "pem-end", bodyFrom, ARMOR_BODY_MAX);
1589
1875
  if (endAt < 0) {
1590
1876
  from = fence + 1;
1591
1877
  continue;
@@ -1597,8 +1883,8 @@ function collectPemFenceSpans(scan, text, span, isHead) {
1597
1883
  }
1598
1884
  function truncationWindowEnd(text, from, json) {
1599
1885
  if (!json)
1600
- return text.length;
1601
- for (let i = from; i < text.length; i++) {
1886
+ return text.readEnd();
1887
+ for (let i = from; !text.atEnd(i); i++) {
1602
1888
  const c = text.charCodeAt(i);
1603
1889
  if (c === CH_BACKSLASH) {
1604
1890
  i += 1;
@@ -1607,7 +1893,7 @@ function truncationWindowEnd(text, from, json) {
1607
1893
  if (c === CH_QUOTE)
1608
1894
  return i;
1609
1895
  }
1610
- return text.length;
1896
+ return text.readEnd();
1611
1897
  }
1612
1898
  function armorBodyEnd(scan, text, json, from, limit, allowBare, crossEndsRun) {
1613
1899
  let pos = from;
@@ -1649,13 +1935,19 @@ function collectPemFragmentSpans(scan, text, json, span, isHead) {
1649
1935
  const fam = familyCode("pem-armor");
1650
1936
  let cur = findPemFence(scan, text, 0);
1651
1937
  let inherited = false;
1652
- const truncatedEnd = createTruncatedBlockEnd(scan, text, json, (from) => firstDelim(scan, text, "pem-end", from, true));
1938
+ const truncatedEnd = createTruncatedBlockEnd(scan, text, json, (from) => firstDelim(scan, "pem-end", from, true));
1653
1939
  while (cur !== undefined) {
1654
1940
  const next = findPemFence(scan, text, cur.after);
1655
- const limit = next === undefined ? text.length : next.fence;
1941
+ const limit = next === undefined ? VIEW_END : next.fence;
1656
1942
  scan.self = readingKey(fam, cur.fence);
1943
+ text.attempt(cur.fence);
1657
1944
  const bodyEnd = armorBodyEnd(scan, text, json, cur.after, limit, inherited, false);
1658
1945
  inherited = scan.hdr.hitLimit;
1946
+ if (isHead && text.reachedEnd()) {
1947
+ span(cur.fence, text.readEnd(), cur.fence, NOT_CLOSED, bodyEnd < 0);
1948
+ cur = next;
1949
+ continue;
1950
+ }
1659
1951
  const end = isHead || bodyEnd < 0 ? bodyEnd : Math.max(bodyEnd, truncatedEnd(cur.after, limit, false));
1660
1952
  if (end >= 0)
1661
1953
  span(cur.fence, end, cur.fence, NOT_CLOSED);
@@ -1664,22 +1956,21 @@ function collectPemFragmentSpans(scan, text, json, span, isHead) {
1664
1956
  }
1665
1957
  function collectHeadlessPpkSpans(scan, text, span, isHead) {
1666
1958
  const KEY = PPK_LINES_FIELD;
1667
- const n = text.length;
1668
1959
  const LINE_NONE = -1;
1669
1960
  const LINE_OPEN = -2;
1670
1961
  const openAt = (i) => {
1671
1962
  if (!isHead)
1672
1963
  return false;
1673
1964
  let k = i;
1674
- while (k < n && tokenEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
1965
+ while (!text.atEnd(k) && tokenEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
1675
1966
  k += 1;
1676
- return k >= n;
1967
+ return text.atEnd(k);
1677
1968
  };
1678
1969
  const isB64 = (c) => (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 43 || c === 47 || c === 61;
1679
1970
  const b64At = (i) => tokenEnd(scan, i) < 0 && isB64(text.charCodeAt(i));
1680
- const skipHs = (i) => { while (i < n && tokenEnd(scan, i) < 0 && (text.charCodeAt(i) === 32 || text.charCodeAt(i) === 9))
1971
+ const skipHs = (i) => { while (!text.atEnd(i) && tokenEnd(scan, i) < 0 && (text.charCodeAt(i) === 32 || text.charCodeAt(i) === 9))
1681
1972
  i += 1; return i; };
1682
- const lineEndHidden = (i) => i < n && delimClass(scan, i) === DELIM_HIDDEN;
1973
+ const lineEndHidden = (i) => !text.atEnd(i) && delimClass(scan, i) === DELIM_HIDDEN;
1683
1974
  const skipNewlines = (i) => {
1684
1975
  let q = skipHs(i);
1685
1976
  let seen = false;
@@ -1722,7 +2013,7 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1722
2013
  if (lineEndHidden(i))
1723
2014
  return 0;
1724
2015
  let k = i;
1725
- while (k < n && text.charCodeAt(k) === 92)
2016
+ while (!text.atEnd(k) && text.charCodeAt(k) === 92)
1726
2017
  k += 1;
1727
2018
  if (k === i)
1728
2019
  return 0;
@@ -1731,7 +2022,7 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1731
2022
  };
1732
2023
  const atLineEnd = (i) => {
1733
2024
  const b = skipHs(i);
1734
- if (b >= n || openAt(b))
2025
+ if (text.atEnd(b) || openAt(b))
1735
2026
  return true;
1736
2027
  if (tokenEnd(scan, b) >= 0 || lineEndHidden(b))
1737
2028
  return false;
@@ -1747,13 +2038,13 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1747
2038
  if (mk === 43 || mk === 45)
1748
2039
  q = skipHs(q + 1);
1749
2040
  let r = q;
1750
- while (r < n && b64At(r))
2041
+ while (!text.atEnd(r) && b64At(r))
1751
2042
  r += 1;
1752
2043
  if (r - q < 4) {
1753
- if (isHead && r >= n)
2044
+ if (isHead && text.atEnd(r))
1754
2045
  return LINE_OPEN;
1755
2046
  r = q0;
1756
- while (r < n && b64At(r))
2047
+ while (!text.atEnd(r) && b64At(r))
1757
2048
  r += 1;
1758
2049
  if (r - q0 < 4)
1759
2050
  return LINE_NONE;
@@ -1765,11 +2056,12 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1765
2056
  const at = text.indexOf(KEY, search);
1766
2057
  if (at === -1)
1767
2058
  break;
2059
+ text.attempt(at);
1768
2060
  let p = at + KEY.length;
1769
2061
  const d0 = p;
1770
- while (p < n && tokenEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
2062
+ while (!text.atEnd(p) && tokenEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
1771
2063
  p += 1;
1772
- if (p === d0 && p < n) {
2064
+ if (p === d0 && !text.atEnd(p)) {
1773
2065
  search = at + KEY.length;
1774
2066
  continue;
1775
2067
  }
@@ -1784,7 +2076,7 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1784
2076
  const r = bodyLineAt(from);
1785
2077
  if (r === LINE_NONE)
1786
2078
  break;
1787
- end = r === LINE_OPEN ? n : r;
2079
+ end = r === LINE_OPEN ? text.readEnd() : r;
1788
2080
  if (r === LINE_OPEN) {
1789
2081
  open = true;
1790
2082
  break;
@@ -1806,7 +2098,7 @@ const FRAGMENT_SECRET_PATTERNS = [
1806
2098
  [valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
1807
2099
  [valueArm("AKIA", "[0-9A-Z]", 6, "needs-left-boundary"), "«redacted:aws-key:partial»"],
1808
2100
  [valueArm("ASIA", "[0-9A-Z]", 8, "needs-left-boundary"), "«redacted:aws-key:partial»"],
1809
- [valueArm("(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-|(?:sk|rk)_(?:live|test|org)_|pypi-)", "[A-Za-z0-9_-]", 8, "needs-left-boundary"), "«redacted:key:partial»"],
2101
+ [valueArm("(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-|(?:sk|rk)_(?:live|test|org)_|pypi-|npm_|hf_|ya29\\.)", "[A-Za-z0-9_-]", 8, "needs-left-boundary"), "«redacted:key:partial»"],
1810
2102
  [valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
1811
2103
  ];
1812
2104
  const DISPLAY_SAFE_URL_TOKEN = "«redacted:url»";
@@ -1890,7 +2182,7 @@ const isControlCode = (c) => c < 0x20 || c === 0x7f;
1890
2182
  const TOKEN_UNIT_Y = new RegExp(REDACTION_TOKEN_RE_SRC, "y");
1891
2183
  function credentialParamValueStart(text, p, json) {
1892
2184
  let encoded = false;
1893
- for (let i = p; i < text.length; i++) {
2185
+ for (let i = p; !text.atEnd(i); i++) {
1894
2186
  const c = text.charCodeAt(i);
1895
2187
  if (c === CH_EQ) {
1896
2188
  if (i === p)
@@ -2374,7 +2666,8 @@ function reachAt(ix, d, self) {
2374
2666
  return ix.dEnd2[d] < 0 ? -1 : ix.reach2[d];
2375
2667
  }
2376
2668
  const FOOT_NONE = 0x7fffffff;
2377
- function buildForensicScan(tokens, rows, armCount) {
2669
+ const NO_DELIM_POSITIONS = new Int32Array(0);
2670
+ function buildForensicScan(tokens, rows, armCount, positionsOf) {
2378
2671
  return {
2379
2672
  rows,
2380
2673
  tokens,
@@ -2385,6 +2678,7 @@ function buildForensicScan(tokens, rows, armCount) {
2385
2678
  footLo: FOOT_NONE,
2386
2679
  footHi: -1,
2387
2680
  armFoot: new Int32Array(2 * armCount),
2681
+ execs: [],
2388
2682
  lastAt: -1,
2389
2683
  headsRead: { a: NO_URL_SEGMENTS, n: 0 },
2390
2684
  headsWrite: { a: NO_URL_SEGMENTS, n: 0 },
@@ -2394,6 +2688,7 @@ function buildForensicScan(tokens, rows, armCount) {
2394
2688
  opsWrite: { a: NO_URL_SEGMENTS, n: 0, cut: OPS_UNCUT },
2395
2689
  hdr: { search: 0, contentEnd: 0, next: 0, hitLimit: false, jsonEnd: false },
2396
2690
  delimPos: new Map(),
2691
+ positionsOf,
2397
2692
  delimIx: new Map(),
2398
2693
  delimIxRows: rows,
2399
2694
  };
@@ -2404,6 +2699,11 @@ function noteEvidenceRead(scan, p) {
2404
2699
  if (p > scan.footHi)
2405
2700
  scan.footHi = p;
2406
2701
  }
2702
+ function noteExecution(scan, lo, hi, from) {
2703
+ if (lo > hi)
2704
+ return;
2705
+ scan.execs.push(lo, hi, from);
2706
+ }
2407
2707
  function footprintClean(dirty, lo, hi) {
2408
2708
  if (lo > hi)
2409
2709
  return true;
@@ -2660,16 +2960,21 @@ export function unitCountsWithinForTest(table, queries) {
2660
2960
  anchor: Int32Array.from(sorted, keyOf),
2661
2961
  closeAt: new Int32Array(sorted.length).fill(NOT_CLOSED),
2662
2962
  };
2663
- const scan = buildForensicScan(NO_TOKEN_UNITS, rows, 1);
2963
+ const scan = buildForensicScan(NO_TOKEN_UNITS, rows, 1, () => NO_DELIM_POSITIONS);
2664
2964
  return queries.map(([from, to, max, self]) => {
2665
2965
  scan.self = self === undefined ? NO_SELF : readingKey(familyCode("prose"), self);
2666
2966
  return measureWithin(scan, from, to, max);
2667
2967
  });
2668
2968
  }
2669
- function delimPositions(scan, text, kind) {
2969
+ function delimPositions(scan, kind) {
2670
2970
  const hit = scan.delimPos.get(kind);
2671
2971
  if (hit !== undefined)
2672
2972
  return hit;
2973
+ const pos = scan.positionsOf(kind);
2974
+ scan.delimPos.set(kind, pos);
2975
+ return pos;
2976
+ }
2977
+ function delimPositionsOf(text, kind) {
2673
2978
  const out = [];
2674
2979
  switch (kind) {
2675
2980
  case "ssh2-open":
@@ -2680,11 +2985,13 @@ function delimPositions(scan, text, kind) {
2680
2985
  ssh2ScanBytes += text.length;
2681
2986
  break;
2682
2987
  }
2683
- case "pem-end":
2988
+ case "pem-end": {
2989
+ const lines = new ScanReader(text);
2684
2990
  for (let b = text.indexOf(PEM_END_OPEN); b >= 0; b = text.indexOf(PEM_END_OPEN, b + 1))
2685
- if (pemFenceLineEnd(text, b + PEM_END_OPEN.length) >= 0)
2991
+ if (pemFenceLineEnd(lines, b + PEM_END_OPEN.length) >= 0)
2686
2992
  out.push(b);
2687
2993
  break;
2994
+ }
2688
2995
  case "at":
2689
2996
  case "at-json":
2690
2997
  for (let p = text.indexOf("@"); p >= 0; p = text.indexOf("@", p + 1)) {
@@ -2699,11 +3006,9 @@ function delimPositions(scan, text, kind) {
2699
3006
  }
2700
3007
  break;
2701
3008
  }
2702
- const pos = Int32Array.from(out);
2703
- scan.delimPos.set(kind, pos);
2704
- return pos;
3009
+ return Int32Array.from(out);
2705
3010
  }
2706
- function delimIndexOf(scan, text, kind) {
3011
+ function delimIndexOf(scan, kind) {
2707
3012
  const t = scan.rows;
2708
3013
  if (scan.delimIxRows !== t) {
2709
3014
  scan.delimIx = new Map();
@@ -2713,7 +3018,7 @@ function delimIndexOf(scan, text, kind) {
2713
3018
  const hit = byKind.get(kind);
2714
3019
  if (hit !== undefined)
2715
3020
  return hit;
2716
- const pos = delimPositions(scan, text, kind);
3021
+ const pos = delimPositions(scan, kind);
2717
3022
  const n = pos.length;
2718
3023
  const nextNotHidden = new Int32Array(n + 1);
2719
3024
  const nextFree = new Int32Array(n + 1);
@@ -2773,9 +3078,9 @@ function bucketFirstFrom(b, i, n) {
2773
3078
  const k = lowerBound(b, i);
2774
3079
  return k < b.length ? b[k] : n;
2775
3080
  }
2776
- function firstDelim(scan, text, kind, from, wantFree) {
3081
+ function firstDelim(scan, kind, from, wantFree) {
2777
3082
  noteEvidenceRead(scan, from);
2778
- const di = delimIndexOf(scan, text, kind);
3083
+ const di = delimIndexOf(scan, kind);
2779
3084
  const n = di.pos.length;
2780
3085
  const bucket = (wantFree ? di.onlyCoveredBy : di.onlyHiddenBy).get(scan.self);
2781
3086
  const firstAt = (i) => (i >= n ? n : Math.min(wantFree ? di.nextFree[i] : di.nextNotHidden[i], bucketFirstFrom(bucket, i, n)));
@@ -2789,11 +3094,11 @@ function firstDelim(scan, text, kind, from, wantFree) {
2789
3094
  j = firstAt(lowerBound(di.pos, own.maxClose[k]));
2790
3095
  }
2791
3096
  const at = j >= n ? -1 : di.pos[j];
2792
- noteEvidenceRead(scan, at < 0 ? text.length : at);
3097
+ noteEvidenceRead(scan, at < 0 ? VIEW_END : at);
2793
3098
  return at;
2794
3099
  }
2795
- function lastNotHiddenDelim(scan, text, kind, a, b) {
2796
- const di = delimIndexOf(scan, text, kind);
3100
+ function lastNotHiddenDelim(scan, kind, a, b) {
3101
+ const di = delimIndexOf(scan, kind);
2797
3102
  const bucket = di.onlyHiddenBy.get(scan.self);
2798
3103
  const lastAt = (j) => {
2799
3104
  let best = di.prevNotHidden[j];
@@ -2820,13 +3125,12 @@ function lastNotHiddenDelim(scan, text, kind, a, b) {
2820
3125
  return best >= 0 && di.pos[best] >= a ? di.pos[best] : -1;
2821
3126
  }
2822
3127
  function scanUrlRun(scan, text, i, kind, json) {
2823
- const n = text.length;
2824
3128
  noteEvidenceRead(scan, i);
2825
3129
  scan.lastAt = -1;
2826
- run: while (i < n) {
3130
+ run: while (!text.atEnd(i)) {
2827
3131
  const unit = crossEndAt(scan, i);
2828
3132
  if (unit >= 0) {
2829
- const at = lastNotHiddenDelim(scan, text, json ? "at-json" : "at", i, unit);
3133
+ const at = lastNotHiddenDelim(scan, json ? "at-json" : "at", i, unit);
2830
3134
  if (at > scan.lastAt)
2831
3135
  scan.lastAt = at;
2832
3136
  i = unit;
@@ -2834,7 +3138,7 @@ function scanUrlRun(scan, text, i, kind, json) {
2834
3138
  }
2835
3139
  const c = text.charCodeAt(i);
2836
3140
  if (json && c === 92) {
2837
- if (i + 1 < n && !isLineTerminator(text.charCodeAt(i + 1))) {
3141
+ if (!text.atEnd(i + 1) && !isLineTerminator(text.charCodeAt(i + 1))) {
2838
3142
  i += 2;
2839
3143
  continue;
2840
3144
  }
@@ -2871,7 +3175,7 @@ function matchUserinfoAt(scan, text, p, json, isHead) {
2871
3175
  const probeEnd = scanUrlRun(scan, text, p, "plain-user", json);
2872
3176
  const keyId = probeEnd > p && text.charCodeAt(probeEnd) === 58 && readableCover(scan, p, probeEnd);
2873
3177
  const runEnd = scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
2874
- if (isHead && runEnd >= text.length && runEnd > p)
3178
+ if (isHead && runEnd > p && text.atEnd(runEnd))
2875
3179
  return runEnd;
2876
3180
  const at = scan.lastAt;
2877
3181
  if (at <= p)
@@ -2879,6 +3183,7 @@ function matchUserinfoAt(scan, text, p, json, isHead) {
2879
3183
  return at;
2880
3184
  }
2881
3185
  const URL_SEG_STRIDE = 4;
3186
+ const URL_PARAM_DELIMS = /[?&#]/g;
2882
3187
  const URL_SEG_MAX_INTS = 3 * 1_048_576;
2883
3188
  const NO_URL_SEGMENTS = new Int32Array(0);
2884
3189
  const ANCHOR_SEG_STRIDE = 4;
@@ -2908,12 +3213,12 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2908
3213
  const phn = dirty === undefined ? 0 : hr.n;
2909
3214
  let pc = 0;
2910
3215
  for (let from = 0;;) {
2911
- head.lastIndex = from;
2912
- const m = head.exec(text);
3216
+ const m = text.search(head, from);
2913
3217
  if (m === null)
2914
3218
  break;
2915
3219
  const colon = m.index;
2916
3220
  const start = colon + 3;
3221
+ text.attempt(start);
2917
3222
  while (pc < phn && ph[pc] < colon)
2918
3223
  pc += URL_SEG_STRIDE;
2919
3224
  const known = pc < phn && ph[pc] === colon;
@@ -2934,8 +3239,9 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2934
3239
  lo = scan.footLo;
2935
3240
  hi = scan.footHi;
2936
3241
  urlSegmentRuns += 1;
3242
+ noteExecution(scan, lo, hi, start);
2937
3243
  if (at >= 0 && at !== (known ? ph[pc + 1] : -2)) {
2938
- const undecided = isHead && at >= text.length;
3244
+ const undecided = isHead && text.atEnd(at);
2939
3245
  span(start, at, colon, undecided ? NOT_CLOSED : at, undecided);
2940
3246
  }
2941
3247
  }
@@ -2957,13 +3263,15 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2957
3263
  const pp = pr.a;
2958
3264
  const ppn = dirty === undefined ? 0 : pr.n;
2959
3265
  let qc = 0;
2960
- for (let i = 0; i < text.length; i++) {
2961
- const c = text.charCodeAt(i);
2962
- if (c !== 63 && c !== 38 && c !== 35)
2963
- continue;
3266
+ let resume = 0;
3267
+ for (let m = text.search(URL_PARAM_DELIMS, 0); m !== null; m = text.search(URL_PARAM_DELIMS, resume)) {
3268
+ const i = m.index;
3269
+ resume = i + 1;
3270
+ text.attempt(i + 1);
2964
3271
  const valueStart = credentialParamValueStart(text, i + 1, json);
2965
3272
  if (valueStart < 0)
2966
3273
  continue;
3274
+ text.attempt(valueStart);
2967
3275
  while (qc < ppn && pp[qc] < i)
2968
3276
  qc += URL_SEG_STRIDE;
2969
3277
  const known = qc < ppn && pp[qc] === i;
@@ -2984,6 +3292,7 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2984
3292
  lo = scan.footLo;
2985
3293
  hi = scan.footHi;
2986
3294
  urlSegmentRuns += 1;
3295
+ noteExecution(scan, lo, hi, valueStart);
2987
3296
  if (valueEnd > valueStart && valueEnd !== (known ? pp[qc + 1] : -2))
2988
3297
  span(valueStart, valueEnd, i, NOT_CLOSED);
2989
3298
  }
@@ -3000,7 +3309,7 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
3000
3309
  armLo = lo;
3001
3310
  if (hi > armHi)
3002
3311
  armHi = hi;
3003
- i = valueEnd - 1;
3312
+ resume = valueEnd;
3004
3313
  }
3005
3314
  scan.headsRead = hw;
3006
3315
  scan.headsWrite = hr;
@@ -3013,10 +3322,9 @@ const JSON_FIELD_ANCHOR = '"';
3013
3322
  const JSON_FIELD_HEAD_MAX = Math.max(...JSON_FIELD_QUOTE_FORMS.map((f) => f.length));
3014
3323
  function jsonSecretFieldValueQuoteAt(text, p, depth) {
3015
3324
  const form = JSON_FIELD_QUOTE_FORMS[depth];
3016
- const n = text.length;
3017
3325
  const nameFrom = p + form.length;
3018
3326
  let i = nameFrom;
3019
- for (; i < n; i++) {
3327
+ for (; !text.atEnd(i); i++) {
3020
3328
  const c = text.charCodeAt(i);
3021
3329
  if (!isJsonFieldNameCode(c))
3022
3330
  break;
@@ -3024,12 +3332,12 @@ function jsonSecretFieldValueQuoteAt(text, p, depth) {
3024
3332
  if (i === nameFrom || !text.startsWith(form, i) || credentialNameAnchor(text.slice(nameFrom, i)) === undefined)
3025
3333
  return -1;
3026
3334
  i += form.length;
3027
- while (i < n && isRegexSpace(text.charCodeAt(i)))
3335
+ while (!text.atEnd(i) && isRegexSpace(text.charCodeAt(i)))
3028
3336
  i += 1;
3029
3337
  if (text.charCodeAt(i) !== CH_COLON)
3030
3338
  return -1;
3031
3339
  i += 1;
3032
- while (i < n && isRegexSpace(text.charCodeAt(i)))
3340
+ while (!text.atEnd(i) && isRegexSpace(text.charCodeAt(i)))
3033
3341
  i += 1;
3034
3342
  return text.startsWith(form, i) ? i : -1;
3035
3343
  }
@@ -3044,7 +3352,7 @@ const packUnit8 = (kind, next) => next * 8 + kind;
3044
3352
  const unit8Kind = (u) => (u % 8);
3045
3353
  const unit8Next = (u) => (u - (u % 8)) / 8;
3046
3354
  function escapedUnitAt(text, i, depth) {
3047
- if (i >= text.length)
3355
+ if (text.atEnd(i))
3048
3356
  return packUnit8(U_END, i);
3049
3357
  if (depth === 0) {
3050
3358
  const c = text.charCodeAt(i);
@@ -3111,12 +3419,13 @@ function collectJsonSecretFieldSpans(text, span, isHead) {
3111
3419
  const start = p - (form.length - 1);
3112
3420
  if (start < p - run)
3113
3421
  continue;
3422
+ text.attempt(start);
3114
3423
  const q = jsonSecretFieldValueQuoteAt(text, start, d);
3115
3424
  const found = q < 0 ? -1 : jsonStringEndAt(text, q + form.length, d, isHead);
3116
3425
  if (found === -1)
3117
3426
  continue;
3118
3427
  const open = found === STRING_OPEN;
3119
- const end = open ? text.length : found;
3428
+ const end = open ? text.readEnd() : found;
3120
3429
  span(q, end, q, open ? NOT_CLOSED : end - form.length, open, JSON_FIELD_VALUE_REPLS[d]);
3121
3430
  from = scanResumeAfterHit(start, end, end - q);
3122
3431
  next = text.indexOf(JSON_FIELD_ANCHOR, from);
@@ -3171,7 +3480,7 @@ function readTextUnit(text, i, json) {
3171
3480
  return packUnit(c, 1);
3172
3481
  if (c === CH_QUOTE)
3173
3482
  return packUnit(JSON_STRING_END, 1);
3174
- if (c !== CH_BACKSLASH || i + 1 >= text.length)
3483
+ if (c !== CH_BACKSLASH || text.atEnd(i + 1))
3175
3484
  return packUnit(c, 1);
3176
3485
  switch (text.charCodeAt(i + 1)) {
3177
3486
  case CH_n: return packUnit(CH_LF, 2);
@@ -3281,9 +3590,8 @@ function anchorHeadAt(scan, text, op, json) {
3281
3590
  return { strength, quotedName: quote !== 0, spacedLeft, listSep, runStart };
3282
3591
  }
3283
3592
  function quotedValueEnd(text, cs, quote, json, isHead) {
3284
- const n = text.length;
3285
3593
  let i = cs;
3286
- while (i < n) {
3594
+ while (!text.atEnd(i)) {
3287
3595
  const unit = readAnchorUnit(text, i, json);
3288
3596
  const ch = unitChOf(unit);
3289
3597
  if (ch === quote)
@@ -3292,7 +3600,7 @@ function quotedValueEnd(text, cs, quote, json, isHead) {
3292
3600
  return -1;
3293
3601
  i += unitLenOf(unit);
3294
3602
  if (ch === CH_BACKSLASH && quote !== JSON_STRING_END) {
3295
- if (i >= n)
3603
+ if (text.atEnd(i))
3296
3604
  break;
3297
3605
  const escaped = readAnchorUnit(text, i, json);
3298
3606
  if (unitChOf(escaped) === JSON_STRING_END)
@@ -3307,6 +3615,7 @@ function buildBareValueMemo() {
3307
3615
  return {
3308
3616
  from: new Int32Array(4),
3309
3617
  at: new Int32Array(4).fill(-1),
3618
+ end: new Uint8Array(4),
3310
3619
  netFrom: new Int32Array(4).fill(NO_MEMO_GROUP),
3311
3620
  net: new Int32Array(16),
3312
3621
  runFrom: new Int32Array(4).fill(NO_MEMO_GROUP),
@@ -3315,14 +3624,17 @@ function buildBareValueMemo() {
3315
3624
  }
3316
3625
  const listSepSlot = (listSep) => (listSep === 0 ? 0 : listSep === CH_AMP ? 1 : listSep === CH_SEMI ? 2 : 3);
3317
3626
  function bareValueEnd(text, s, json, listSep, memo) {
3318
- const n = text.length;
3319
3627
  const k = listSepSlot(listSep);
3320
3628
  let i;
3321
- if (memo.from[k] <= s && s <= memo.at[k])
3629
+ if (memo.from[k] <= s && s <= memo.at[k]) {
3322
3630
  i = memo.at[k];
3631
+ if (memo.end[k] === 1)
3632
+ text.readEnd();
3633
+ }
3323
3634
  else {
3324
3635
  i = s;
3325
- while (i < n) {
3636
+ let reachedEnd = false;
3637
+ while (!(reachedEnd = text.atEnd(i))) {
3326
3638
  const unit = readAnchorUnit(text, i, json);
3327
3639
  const ch = unitChOf(unit);
3328
3640
  if (ch === JSON_STRING_END || isRegexSpace(ch))
@@ -3331,7 +3643,7 @@ function bareValueEnd(text, s, json, listSep, memo) {
3331
3643
  break;
3332
3644
  const next = i + unitLenOf(unit);
3333
3645
  if (isQuoteUnit(ch)) {
3334
- if (next >= n)
3646
+ if ((reachedEnd = text.atEnd(next)))
3335
3647
  break;
3336
3648
  if (endsValueUnit(unitChOf(readAnchorUnit(text, next, json))))
3337
3649
  break;
@@ -3340,6 +3652,7 @@ function bareValueEnd(text, s, json, listSep, memo) {
3340
3652
  }
3341
3653
  memo.from[k] = s;
3342
3654
  memo.at[k] = i;
3655
+ memo.end[k] = reachedEnd ? 1 : 0;
3343
3656
  memo.netFrom[k] = NO_MEMO_GROUP;
3344
3657
  memo.runFrom[k] = NO_MEMO_GROUP;
3345
3658
  }
@@ -3392,21 +3705,20 @@ function bareValueEnd(text, s, json, listSep, memo) {
3392
3705
  return e;
3393
3706
  }
3394
3707
  function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3395
- const n = text.length;
3396
- if (s >= n)
3708
+ if (text.atEnd(s))
3397
3709
  return undefined;
3398
3710
  const firstUnit = readAnchorUnit(text, s, json);
3399
3711
  const first = unitChOf(firstUnit);
3400
3712
  let vs = s;
3401
3713
  if (isQuoteUnit(first) || first === JSON_STRING_END) {
3402
3714
  vs = s + unitLenOf(firstUnit);
3403
- if (vs >= n)
3715
+ if (text.atEnd(vs))
3404
3716
  return undefined;
3405
3717
  if (!isAsciiAlnumCode(unitChOf(readAnchorUnit(text, vs, json))))
3406
3718
  return undefined;
3407
3719
  const close = quotedValueEnd(text, vs, first, json, isHead);
3408
3720
  if (close === STRING_OPEN)
3409
- return { start: vs, end: n, quoted: true, open: true };
3721
+ return { start: vs, end: text.readEnd(), quoted: true, open: true };
3410
3722
  if (close >= 0)
3411
3723
  return { start: vs, end: close, quoted: true };
3412
3724
  }
@@ -3429,11 +3741,11 @@ function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3429
3741
  }
3430
3742
  if (isProgramIdentStartCode(text.charCodeAt(vs))) {
3431
3743
  let k = vs;
3432
- while (k < n && isProgramIdentCode(text.charCodeAt(k)))
3744
+ while (!text.atEnd(k) && isProgramIdentCode(text.charCodeAt(k)))
3433
3745
  k += 1;
3434
3746
  anchoredScanUnits += k - vs;
3435
3747
  const after = text.charCodeAt(k);
3436
- if (after === CH_LPAREN || after === CH_LBRACKET || (after === CH_DOT && k + 1 < n && isProgramIdentStartCode(text.charCodeAt(k + 1))))
3748
+ if (after === CH_LPAREN || after === CH_LBRACKET || (after === CH_DOT && !text.atEnd(k + 1) && isProgramIdentStartCode(text.charCodeAt(k + 1))))
3437
3749
  return undefined;
3438
3750
  }
3439
3751
  const e = bareValueEnd(text, vs, json, listSep, memo);
@@ -3452,7 +3764,7 @@ function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3452
3764
  case "goes-on":
3453
3765
  return undefined;
3454
3766
  case "undecided":
3455
- return { start: vs, end: text.length, quoted: false, open: true };
3767
+ return { start: vs, end: text.readEnd(), quoted: false, open: true };
3456
3768
  default: {
3457
3769
  const unhandled = rest;
3458
3770
  throw new Error(`redact: unhandled line-key rest ${String(unhandled)}`);
@@ -3462,9 +3774,8 @@ function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3462
3774
  return { start: vs, end: e, quoted: false };
3463
3775
  }
3464
3776
  function lineKeyRestAt(text, e, json, isHead) {
3465
- const n = text.length;
3466
3777
  let i = e;
3467
- while (i < n) {
3778
+ while (!text.atEnd(i)) {
3468
3779
  const unit = readAnchorUnit(text, i, json);
3469
3780
  const ch = unitChOf(unit);
3470
3781
  if (isInlineSpaceUnit(ch) || (ch >= 0 && INVISIBLE_UNIT_CLASS[ch] === 1)) {
@@ -3526,9 +3837,8 @@ function declarativeBareAt(text, head, spacedRight, json) {
3526
3837
  return spacedRight && lineHeadBefore(text, head.runStart, json) ? "line-key" : "never";
3527
3838
  }
3528
3839
  function schemeValueAt(text, s, json) {
3529
- const n = text.length;
3530
3840
  let i = s;
3531
- if (i >= n)
3841
+ if (text.atEnd(i))
3532
3842
  return undefined;
3533
3843
  const opening = readAnchorUnit(text, i, json);
3534
3844
  if (isQuoteUnit(unitChOf(opening)) || unitChOf(opening) === JSON_STRING_END)
@@ -3540,14 +3850,14 @@ function schemeValueAt(text, s, json) {
3540
3850
  anchoredScanUnits += REDACTION_MARK.length;
3541
3851
  }
3542
3852
  else {
3543
- while (i < n && i - w <= STRONG_AUTH_SCHEME_MAX && (isIdentStartCode(text.charCodeAt(i))))
3853
+ while (!text.atEnd(i) && i - w <= STRONG_AUTH_SCHEME_MAX && (isIdentStartCode(text.charCodeAt(i))))
3544
3854
  i += 1;
3545
3855
  anchoredScanUnits += i - w;
3546
3856
  if (i - w > STRONG_AUTH_SCHEME_MAX || !STRONG_AUTH_SCHEMES.has(text.slice(w, i).toLowerCase()))
3547
3857
  return undefined;
3548
3858
  }
3549
3859
  let separated = false;
3550
- while (i < n) {
3860
+ while (!text.atEnd(i)) {
3551
3861
  const unit = readAnchorUnit(text, i, json);
3552
3862
  const ch = unitChOf(unit);
3553
3863
  if (!isInlineSpaceUnit(ch) && !isSchemeDelimiterCode(ch))
@@ -3560,30 +3870,30 @@ function schemeValueAt(text, s, json) {
3560
3870
  const vs = i;
3561
3871
  if (engineMarked) {
3562
3872
  let k = i;
3563
- while (k < n && isToken68Code(text.charCodeAt(k)))
3873
+ while (!text.atEnd(k) && isToken68Code(text.charCodeAt(k)))
3564
3874
  k += 1;
3565
- while (k < n && text.charCodeAt(k) === CH_EQ)
3875
+ while (!text.atEnd(k) && text.charCodeAt(k) === CH_EQ)
3566
3876
  k += 1;
3567
3877
  if (k - vs >= BEARER_VALUE_MIN) {
3568
3878
  anchoredScanUnits += k - vs;
3569
3879
  return { start: vs, end: k, quoted: false };
3570
3880
  }
3571
3881
  }
3572
- if (i >= n || !isAsciiAlnumCode(text.charCodeAt(i)))
3882
+ if (text.atEnd(i) || !isAsciiAlnumCode(text.charCodeAt(i)))
3573
3883
  return undefined;
3574
- while (i < n && isToken68Code(text.charCodeAt(i)))
3884
+ while (!text.atEnd(i) && isToken68Code(text.charCodeAt(i)))
3575
3885
  i += 1;
3576
- while (i < n && text.charCodeAt(i) === CH_EQ)
3886
+ while (!text.atEnd(i) && text.charCodeAt(i) === CH_EQ)
3577
3887
  i += 1;
3578
3888
  anchoredScanUnits += i - vs;
3579
3889
  const e = i;
3580
- while (i < n) {
3890
+ while (!text.atEnd(i)) {
3581
3891
  const unit = readAnchorUnit(text, i, json);
3582
3892
  if (!isInlineSpaceUnit(unitChOf(unit)))
3583
3893
  break;
3584
3894
  i += unitLenOf(unit);
3585
3895
  }
3586
- if (i < n && !endsHeaderValueUnit(unitChOf(readAnchorUnit(text, i, json))))
3896
+ if (!text.atEnd(i) && !endsHeaderValueUnit(unitChOf(readAnchorUnit(text, i, json))))
3587
3897
  return undefined;
3588
3898
  return { start: vs, end: e, quoted: false };
3589
3899
  }
@@ -3591,10 +3901,9 @@ function anchoredValueAt(scan, text, op, json, span, isHead, memo) {
3591
3901
  const head = anchorHeadAt(scan, text, op, json);
3592
3902
  if (head === undefined)
3593
3903
  return;
3594
- const n = text.length;
3595
3904
  let s = op + 1;
3596
3905
  let spacedRight = false;
3597
- while (s < n) {
3906
+ while (!text.atEnd(s)) {
3598
3907
  const unit = readAnchorUnit(text, s, json);
3599
3908
  if (!isInlineSpaceUnit(unitChOf(unit)))
3600
3909
  break;
@@ -3661,6 +3970,7 @@ function collectAnchoredValueSpans(scan, text, json, span, isHead) {
3661
3970
  if (!known && dirty !== undefined && op < rd.cut)
3662
3971
  continue;
3663
3972
  scan.self = readingKey(anchorFamily, op);
3973
+ text.attempt(op + 1);
3664
3974
  let lo;
3665
3975
  let hi;
3666
3976
  let answer;
@@ -3676,6 +3986,7 @@ function collectAnchoredValueSpans(scan, text, json, span, isHead) {
3676
3986
  hi = scan.footHi;
3677
3987
  if (lo > hi)
3678
3988
  continue;
3989
+ noteExecution(scan, lo, hi, op + 1);
3679
3990
  answer = nameBoundAt(scan, lo, hi);
3680
3991
  }
3681
3992
  if (wr.cut === OPS_UNCUT) {
@@ -3730,10 +4041,9 @@ export function resetDiagnosticScanUnitsForTest() {
3730
4041
  diagnosticScanUnits = 0;
3731
4042
  }
3732
4043
  function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3733
- const n = text.length;
3734
4044
  const readRun = (from, isValue) => {
3735
4045
  let i = from;
3736
- while (i < n) {
4046
+ while (!text.atEnd(i)) {
3737
4047
  const unit = readTextUnit(text, i, json);
3738
4048
  diagnosticScanUnits += 1;
3739
4049
  if (!isValue(unitChOf(unit)))
@@ -3782,6 +4092,7 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3782
4092
  }
3783
4093
  if (nameStart < 0)
3784
4094
  continue;
4095
+ text.attempt(nameStart);
3785
4096
  let s;
3786
4097
  let end;
3787
4098
  if (inValue) {
@@ -3790,14 +4101,14 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3790
4101
  }
3791
4102
  else {
3792
4103
  s = skipBlank(op + 1);
3793
- if (s < n) {
4104
+ if (!text.atEnd(s)) {
3794
4105
  const unit = readTextUnit(text, s, json);
3795
4106
  if (unitChOf(unit) === CH_QUOTE)
3796
4107
  s += unitLenOf(unit);
3797
4108
  }
3798
- if (s >= n) {
4109
+ if (text.atEnd(s)) {
3799
4110
  if (isHead)
3800
- span(nameStart, n, op, NOT_CLOSED, true);
4111
+ span(nameStart, text.readEnd(), op, NOT_CLOSED, true);
3801
4112
  continue;
3802
4113
  }
3803
4114
  end = readRun(s, isDiagnosticValueUnit);
@@ -3805,11 +4116,11 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3805
4116
  }
3806
4117
  if (end > s && nameTier === "auth-scheme" && readRun(s, isAsciiLetterCode) === end) {
3807
4118
  const k = skipBlank(end);
3808
- if (k > end && k >= n && isHead) {
3809
- span(nameStart, n, op, NOT_CLOSED, true);
4119
+ if (k > end && isHead && text.atEnd(k)) {
4120
+ span(nameStart, text.readEnd(), op, NOT_CLOSED, true);
3810
4121
  continue;
3811
4122
  }
3812
- if (k > end && k < n) {
4123
+ if (k > end && !text.atEnd(k)) {
3813
4124
  const credEnd = readRun(k, isDiagnosticSchemeValueUnit);
3814
4125
  if (credEnd > k)
3815
4126
  end = credEnd;
@@ -3821,10 +4132,10 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3821
4132
  }
3822
4133
  }
3823
4134
  let schemeEnd = 0;
3824
- DIAGNOSTIC_SCHEME_RE.lastIndex = 0;
3825
- for (let m = DIAGNOSTIC_SCHEME_RE.exec(text); m !== null; m = DIAGNOSTIC_SCHEME_RE.exec(text)) {
4135
+ for (let m = text.search(DIAGNOSTIC_SCHEME_RE, 0); m !== null; m = text.search(DIAGNOSTIC_SCHEME_RE, m.index + m[0].length)) {
3826
4136
  const at = m.index;
3827
4137
  const q = at + m[0].length;
4138
+ text.attempt(at);
3828
4139
  if (q < schemeEnd)
3829
4140
  continue;
3830
4141
  if (at > 0 && isRegexWordCode(text.charCodeAt(at - 1)) && deletedAt(at) === "none")
@@ -3832,9 +4143,9 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3832
4143
  const k = skipBlank(q);
3833
4144
  if (k === q && deletedAt(q) !== "blank")
3834
4145
  continue;
3835
- if (k >= n) {
4146
+ if (text.atEnd(k)) {
3836
4147
  if (isHead)
3837
- span(at, n, at, NOT_CLOSED, true);
4148
+ span(at, text.readEnd(), at, NOT_CLOSED, true);
3838
4149
  continue;
3839
4150
  }
3840
4151
  const end = readRun(k, isDiagnosticSchemeValueUnit);
@@ -3852,12 +4163,14 @@ export function structuralScannerForTest(arm) {
3852
4163
  };
3853
4164
  switch (arm) {
3854
4165
  case "json-secret-field":
3855
- return { declared: new RegExp(JSON_SECRET_FIELD_PATTERN[0].source, JSON_SECRET_FIELD_PATTERN[0].flags), spans: collect(collectJsonSecretFieldSpans) };
4166
+ return { declared: new RegExp(JSON_SECRET_FIELD_PATTERN[0].source, JSON_SECRET_FIELD_PATTERN[0].flags), spans: collect((text, span, isHead) => collectJsonSecretFieldSpans(new ScanReader(text), span, isHead)) };
3856
4167
  case "pem-fence":
3857
- return { declared: new RegExp(PEM_FENCE_PATTERN[0].source, PEM_FENCE_PATTERN[0].flags), spans: collect((text, span, isHead) => collectPemFenceSpans(buildForensicScan(NO_TOKEN_UNITS, NO_ROWS, 1), text, span, isHead)) };
4168
+ return { declared: new RegExp(PEM_FENCE_PATTERN[0].source, PEM_FENCE_PATTERN[0].flags), spans: collect((text, span, isHead) => collectPemFenceSpans(buildForensicScan(NO_TOKEN_UNITS, NO_ROWS, 1, (kind) => delimPositionsOf(text, kind)), new ScanReader(text), span, isHead)) };
3858
4169
  }
3859
4170
  }
3860
4171
  let ppkScanBytes = 0;
4172
+ const TO_END = -1;
4173
+ const EACH_HEAD = -1;
3861
4174
  export function ppkScanBytesForTest() { return ppkScanBytes; }
3862
4175
  export function resetPpkScanBytesForTest() { ppkScanBytes = 0; }
3863
4176
  function collectPpkContainerSpans(scan, text, eat, span, isHead) {
@@ -3872,14 +4185,14 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3872
4185
  if (c !== undefined && c >= from)
3873
4186
  return c;
3874
4187
  const p = text.indexOf(needle, from);
3875
- ppkScanBytes += (p === -1 ? text.length : p) - from;
4188
+ ppkScanBytes += p === -1 ? 0 : p - from;
3876
4189
  const v = p === -1 ? Number.POSITIVE_INFINITY : p;
3877
4190
  cache.set(needle, v);
3878
4191
  return v;
3879
4192
  };
3880
4193
  const versionEnd = (at) => {
3881
4194
  let p = at + HEAD.length;
3882
- while (p < text.length && tokenEnd(scan, p) < 0) {
4195
+ while (!text.atEnd(p) && tokenEnd(scan, p) < 0) {
3883
4196
  const c = text.charCodeAt(p);
3884
4197
  if (c >= 48 && c <= 57)
3885
4198
  p += 1;
@@ -3894,11 +4207,12 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3894
4207
  };
3895
4208
  const isOpenHead = (at) => {
3896
4209
  const p = versionEnd(at);
3897
- return p >= text.length || (p === text.length - 1 && p > at + HEAD.length && text.charCodeAt(p) === 58);
4210
+ return text.atEnd(p) || (text.atEnd(p + 1) && p > at + HEAD.length && text.charCodeAt(p) === 58);
3898
4211
  };
3899
- const nextVersionedHead = (from) => {
4212
+ const nextVersionedHead = (from, attemptAt) => {
3900
4213
  let p = find(HEAD, from);
3901
4214
  for (; p !== Number.POSITIVE_INFINITY; p = find(HEAD, p + HEAD.length)) {
4215
+ text.attempt(attemptAt === EACH_HEAD ? p : attemptAt);
3902
4216
  const lineEnd = versionedHeadEnd(p);
3903
4217
  if (lineEnd >= 0 && !lineHeld(scan, p, lineEnd, readingKey(fam, p)))
3904
4218
  break;
@@ -3907,7 +4221,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3907
4221
  };
3908
4222
  const hexRunEnd = (from) => {
3909
4223
  let e = from;
3910
- while (e < text.length && tokenEnd(scan, e) < 0) {
4224
+ while (!text.atEnd(e) && tokenEnd(scan, e) < 0) {
3911
4225
  const c = text.charCodeAt(e);
3912
4226
  if ((c >= 48 && c <= 57) || (c >= 97 && c <= 102) || (c >= 65 && c <= 70))
3913
4227
  e += 1;
@@ -3916,12 +4230,13 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3916
4230
  }
3917
4231
  return e;
3918
4232
  };
3919
- let at = nextVersionedHead(0);
4233
+ let at = nextVersionedHead(0, EACH_HEAD);
3920
4234
  while (at !== Number.POSITIVE_INFINITY) {
3921
4235
  scan.self = readingKey(fam, at);
4236
+ text.attempt(at);
3922
4237
  let pos = at + HEAD.length + 1;
3923
4238
  let closeAt = NOT_CLOSED;
3924
- let end = text.length;
4239
+ let end = TO_END;
3925
4240
  let ateToEnd = !isHead;
3926
4241
  let open = false;
3927
4242
  for (;;) {
@@ -3942,7 +4257,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3942
4257
  pos = hexStart;
3943
4258
  continue;
3944
4259
  }
3945
- const limit = nextVersionedHead(hexEnd);
4260
+ const limit = nextVersionedHead(hexEnd, at);
3946
4261
  const straggler = Math.min(find(LINES, hexEnd), find(MAC, hexEnd), find(HASH, hexEnd));
3947
4262
  let gapBlank = true;
3948
4263
  if (limit !== Number.POSITIVE_INFINITY) {
@@ -3956,7 +4271,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3956
4271
  }
3957
4272
  const undecided = isHead && limit === Number.POSITIVE_INFINITY;
3958
4273
  ateToEnd = straggler < limit || !gapBlank;
3959
- end = ateToEnd || undecided ? text.length : hexEnd;
4274
+ end = ateToEnd || undecided ? TO_END : hexEnd;
3960
4275
  open = undecided;
3961
4276
  if (end === hexEnd)
3962
4277
  closeAt = term;
@@ -3964,14 +4279,24 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3964
4279
  }
3965
4280
  if (ateToEnd && eat !== undefined && eat.from < 0) {
3966
4281
  eat.from = at;
3967
- eat.len = text.length;
4282
+ eat.len = text.readEnd();
3968
4283
  }
3969
- span(at, end, at, closeAt, open);
3970
- at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
4284
+ span(at, end === TO_END ? text.readEnd() : end, at, closeAt, open);
4285
+ at = end === TO_END ? Number.POSITIVE_INFINITY : nextVersionedHead(end, EACH_HEAD);
3971
4286
  }
3972
4287
  const last = isHead ? text.lastIndexOf(HEAD) : -1;
3973
- if (last >= 0 && isOpenHead(last) && !lineHeld(scan, last, text.length, readingKey(fam, last)))
3974
- span(last, text.length, last, NOT_CLOSED, true);
4288
+ if (last >= 0)
4289
+ text.attempt(last);
4290
+ if (last >= 0 && isOpenHead(last) && !lineHeld(scan, last, text.readEnd(), readingKey(fam, last)))
4291
+ span(last, text.readEnd(), last, NOT_CLOSED, true);
4292
+ }
4293
+ function anchoredHeadOf(prior) {
4294
+ switch (prior.kind) {
4295
+ case "anchored":
4296
+ return { kind: "anchors", anchors: prior.anchors };
4297
+ case "bounded":
4298
+ throw new Error("redact: a bounded arm declares no anchor literal to read its head from");
4299
+ }
3975
4300
  }
3976
4301
  const NO_GAP = (_unit) => false;
3977
4302
  const armPool = (() => {
@@ -3981,6 +4306,9 @@ const armPool = (() => {
3981
4306
  throw new Error(`redact: arm-pool replacement ${JSON.stringify(token)} may only use a leading $1 (the run kept outside the span)`);
3982
4307
  return body;
3983
4308
  };
4309
+ const headUnderivable = (re) => {
4310
+ throw new Error(`redact: regex arm /${re.source}/ has no derivable head — the live frontier cannot tell where an attempt that reads past the end of the text starts`);
4311
+ };
3984
4312
  const regexMember = ([re, token], rank, order) => {
3985
4313
  if (token.startsWith("$1"))
3986
4314
  throw new Error(`redact: regex arm /${re.source}/ cannot carry an outside-span $1 prefix — that form belongs to the structural scanners`);
@@ -4003,7 +4331,7 @@ const armPool = (() => {
4003
4331
  phase: 1,
4004
4332
  lexical: LEXICAL_KEYWORD_ARMS.has(re),
4005
4333
  diagnosticOnly: false,
4006
- collect: (text, _json, _eat, out, isHead) => {
4334
+ collect: (text, _view, _json, _eat, out, isHead) => {
4007
4335
  if (boundary === undefined)
4008
4336
  collectRegexSpans(arm, text, out);
4009
4337
  else
@@ -4020,9 +4348,10 @@ const armPool = (() => {
4020
4348
  family: "prose",
4021
4349
  reach: regexDecisionReach(re, LEXICAL_KEYWORD_ARMS.has(re)),
4022
4350
  prior: { kind: "bounded", gap: keywordTail === undefined ? NO_GAP : (c) => keywordTail.isSeparator(String.fromCharCode(c)) },
4351
+ head: { kind: "units", seqs: keywordTail !== undefined ? keywordTail.forms.map((f) => [...f].map((ch) => codeUnitMembers(ch))) : d.src !== undefined ? expandHeadUnits(d.src, re.flags) : headUnderivable(re) },
4023
4352
  };
4024
4353
  };
4025
- const scannerMember = (token, order, arm, reach, family, prior, rank = ARM_RANK.structural, lexical = false, diagnosticOnly = false) => {
4354
+ const scannerMember = (token, order, arm, reach, family, prior, head, rank = ARM_RANK.structural, lexical = false, diagnosticOnly = false) => {
4026
4355
  const repl = spanRepl(token);
4027
4356
  return {
4028
4357
  token,
@@ -4031,16 +4360,17 @@ const armPool = (() => {
4031
4360
  phase: 1,
4032
4361
  lexical,
4033
4362
  diagnosticOnly,
4034
- collect: (text, json, eat, out, isHead, deletedAt) => arm(text, json, eat, (start, end, anchor, closeAt, undecided, r = repl) => out.push(undecided === true ? { start, end, repl: r, rank, order, undecided, anchor, closeAt } : { start, end, repl: r, rank, order, anchor, closeAt }), isHead, deletedAt),
4363
+ collect: (_text, view, json, eat, out, isHead, deletedAt) => arm(view, json, eat, (start, end, anchor, closeAt, undecided, r = repl) => out.push(undecided === true ? { start, end, repl: r, rank, order, undecided, anchor, closeAt } : { start, end, repl: r, rank, order, anchor, closeAt }), isHead, deletedAt),
4035
4364
  retry: undefined,
4036
4365
  headSrc: undefined,
4037
4366
  headFirst: undefined,
4038
4367
  family,
4039
4368
  reach,
4040
4369
  prior,
4370
+ head,
4041
4371
  };
4042
4372
  };
4043
- const forensicMember = (token, order, arm, reach, family, prior, rank = ARM_RANK.structural, lexical = false) => {
4373
+ const forensicMember = (token, order, arm, reach, family, grain, prior, head, rank = ARM_RANK.structural, lexical = false) => {
4044
4374
  const repl = spanRepl(token);
4045
4375
  return {
4046
4376
  token,
@@ -4049,41 +4379,47 @@ const armPool = (() => {
4049
4379
  phase: 2,
4050
4380
  lexical,
4051
4381
  diagnosticOnly: false,
4052
- collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end, anchor, closeAt, undecided, r = repl) => out.push(undecided === true ? { start, end, repl: r, rank, order, undecided, anchor, closeAt } : { start, end, repl: r, rank, order, anchor, closeAt }), isHead),
4382
+ collect: (scan, view, json, eat, out, isHead) => arm(scan, view, json, eat, (start, end, anchor, closeAt, undecided, r = repl) => out.push(undecided === true ? { start, end, repl: r, rank, order, undecided, anchor, closeAt } : { start, end, repl: r, rank, order, anchor, closeAt }), isHead),
4053
4383
  retry: undefined,
4054
4384
  headSrc: undefined,
4055
4385
  headFirst: undefined,
4056
4386
  family,
4057
4387
  repl,
4388
+ grain,
4058
4389
  reach,
4059
4390
  prior,
4391
+ head,
4060
4392
  };
4061
4393
  };
4394
+ const PEM_ARMOR_PRIOR = { kind: "anchored", anchors: [PEM_FENCE_OPEN, PEM_END_OPEN] };
4395
+ const SSH2_ARMOR_PRIOR = { kind: "anchored", anchors: [SSH2_FENCE_OPEN, SSH2_FENCE_CLOSE] };
4396
+ const PPK_CONTAINER_PRIOR = { kind: "anchored", anchors: [PPK_FILE_HEAD] };
4397
+ const HEADLESS_PPK_PRIOR = { kind: "anchored", anchors: [PPK_LINES_FIELD] };
4062
4398
  const structuralScannerMember = (row, order) => {
4063
4399
  if (row === PEM_FENCE_PATTERN)
4064
- return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectPemFenceSpans(scan, text, span, isHead), PEM_FENCE_OPEN.length, "pem-armor", { kind: "anchored", anchors: [PEM_FENCE_OPEN, PEM_END_OPEN] });
4400
+ return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectPemFenceSpans(scan, text, span, isHead), PEM_FENCE_OPEN.length, "pem-armor", "arm", PEM_ARMOR_PRIOR, anchoredHeadOf(PEM_ARMOR_PRIOR));
4065
4401
  if (row === SSH2_FENCE_PATTERN)
4066
- return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, json, span, isHead), SSH2_FENCE_OPEN.length, "ssh2-armor", { kind: "anchored", anchors: [SSH2_FENCE_OPEN, SSH2_FENCE_CLOSE] });
4402
+ return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, json, span, isHead), SSH2_FENCE_OPEN.length, "ssh2-armor", "arm", SSH2_ARMOR_PRIOR, anchoredHeadOf(SSH2_ARMOR_PRIOR));
4067
4403
  if (row === URL_CREDENTIAL_PATTERN)
4068
- return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead), "://".length, "url", { kind: "bounded", gap: NO_GAP });
4404
+ return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead), "://".length, "url", "segment", { kind: "bounded", gap: NO_GAP }, { kind: "anchors", anchors: ["://"] });
4069
4405
  if (row === JSON_SECRET_FIELD_PATTERN)
4070
- return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead), JSON_FIELD_HEAD_MAX, "json-field", { kind: "bounded", gap: isRegexSpace });
4406
+ return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead), JSON_FIELD_HEAD_MAX, "json-field", { kind: "bounded", gap: isRegexSpace }, { kind: "anchors", anchors: [JSON_FIELD_ANCHOR] });
4071
4407
  throw new Error(`redact: structural row /${row[0].source}/ has no hand-written scanner — structural grammars are declared as regexes but never executed as one (S-102 / S-265)`);
4072
4408
  };
4073
4409
  const members = [];
4074
4410
  for (const row of STRUCTURAL_SECRET_PATTERNS)
4075
4411
  members.push(structuralScannerMember(row, members.length));
4076
- members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead), PPK_FILE_HEAD.length, "putty-ppk", { kind: "anchored", anchors: [PPK_FILE_HEAD] }));
4077
- members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectPemFragmentSpans(scan, text, json, span, isHead), PEM_FENCE_OPEN.length, "pem-armor", { kind: "anchored", anchors: [PEM_FENCE_OPEN, PEM_END_OPEN] }));
4078
- members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead), PPK_LINES_FIELD.length, "putty-ppk", { kind: "anchored", anchors: [PPK_LINES_FIELD] }));
4079
- members.push(forensicMember(ANCHORED_VALUE_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectAnchoredValueSpans(scan, text, json, span, isHead), Math.max(BEARER_VALUE_MIN, BARE_WORD_MAX + 1, STRONG_AUTH_SCHEME_MAX + 1), "anchor", { kind: "bounded", gap: (c) => isInlineSpaceUnit(c) || isSchemeDelimiterCode(c) }, ARM_RANK.keyword, true));
4412
+ members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead), PPK_FILE_HEAD.length, "putty-ppk", "arm", PPK_CONTAINER_PRIOR, anchoredHeadOf(PPK_CONTAINER_PRIOR)));
4413
+ members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectPemFragmentSpans(scan, text, json, span, isHead), PEM_FENCE_OPEN.length, "pem-armor", "arm", PEM_ARMOR_PRIOR, anchoredHeadOf(PEM_ARMOR_PRIOR)));
4414
+ members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead), PPK_LINES_FIELD.length, "putty-ppk", "arm", HEADLESS_PPK_PRIOR, anchoredHeadOf(HEADLESS_PPK_PRIOR)));
4415
+ members.push(forensicMember(ANCHORED_VALUE_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectAnchoredValueSpans(scan, text, json, span, isHead), Math.max(BEARER_VALUE_MIN, BARE_WORD_MAX + 1, STRONG_AUTH_SCHEME_MAX + 1), "anchor", "segment", { kind: "bounded", gap: (c) => isInlineSpaceUnit(c) || isSchemeDelimiterCode(c) }, { kind: "anchors", anchors: ["=", ":"] }, ARM_RANK.keyword, true));
4080
4416
  for (const row of KEYWORD_SECRET_PATTERNS)
4081
4417
  members.push(regexMember(row, ARM_RANK.keyword, members.length));
4082
4418
  for (const row of SHAPE_SECRET_PATTERNS)
4083
4419
  members.push(regexMember(row, ARM_RANK.shape, members.length));
4084
4420
  for (const row of FRAGMENT_SECRET_PATTERNS)
4085
4421
  members.push(regexMember(row, ARM_RANK.fragment, members.length));
4086
- members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead, deletedAt) => collectDiagnosticValueSpans(text, json, span, isHead, deletedAt), Math.max(CREDENTIAL_NAME_MAX, STRONG_AUTH_SCHEME_MAX) + 2, "prose", { kind: "bounded", gap: isRegexSpace }, ARM_RANK.keyword, true, true));
4422
+ members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead, deletedAt) => collectDiagnosticValueSpans(text, json, span, isHead, deletedAt), Math.max(CREDENTIAL_NAME_MAX, STRONG_AUTH_SCHEME_MAX) + 2, "prose", { kind: "bounded", gap: isRegexSpace }, { kind: "anchors", anchors: ["=", ":"] }, ARM_RANK.keyword, true, true));
4087
4423
  for (const [re] of KEYWORD_SECRET_PATTERNS) {
4088
4424
  if (!LEXICAL_KEYWORD_ARMS.has(re))
4089
4425
  throw new Error(`redact: keyword arm /${re.source}/ is not registered as lexical — build it with keywordArm() so the bounded-unit scope can exclude it`);
@@ -4116,11 +4452,51 @@ const armPool = (() => {
4116
4452
  poolArmCount = members.length;
4117
4453
  poolBoundaryArmCount = retryMembers.length;
4118
4454
  const reaches = members.map((m) => ({ token: m.token, order: m.order, reach: m.reach }));
4455
+ const viewOpenOf = (text, lane, spans, scanned) => {
4456
+ const n = text.length;
4457
+ let open = scanned;
4458
+ const visit = (m) => {
4459
+ const h = m.head;
4460
+ switch (h.kind) {
4461
+ case "units":
4462
+ for (let p = Math.max(0, n - m.reach); p < open; p++) {
4463
+ if (headFeasibleAt(h.seqs, text, p)) {
4464
+ open = p;
4465
+ break;
4466
+ }
4467
+ }
4468
+ return;
4469
+ case "anchors":
4470
+ for (const a of h.anchors) {
4471
+ for (let k = Math.min(a.length - 1, n); k > 0 && n - k < open; k--) {
4472
+ if (text.endsWith(a.slice(0, k))) {
4473
+ open = n - k;
4474
+ break;
4475
+ }
4476
+ }
4477
+ }
4478
+ return;
4479
+ default: {
4480
+ const unhandled = h;
4481
+ throw new Error(`redact: unhandled head reading ${String(unhandled)}`);
4482
+ }
4483
+ }
4484
+ };
4485
+ for (const m of lane.phase1)
4486
+ visit(m);
4487
+ for (const m of lane.phase2)
4488
+ visit(m);
4489
+ for (const sp of spans)
4490
+ if (sp.end >= n && sp.start < open && members[sp.order].head.kind === "units")
4491
+ open = sp.start;
4492
+ return open;
4493
+ };
4119
4494
  const run = (text, wantCount, json, eat, scope, reach, engineMarked, deletedAt) => {
4120
4495
  readings += 1;
4121
4496
  const isHead = reach !== undefined;
4122
4497
  const lane = LANES[scope];
4123
4498
  const spans = [];
4499
+ const view = new ScanReader(text);
4124
4500
  let tried;
4125
4501
  const untriedFrom = (nu, p) => {
4126
4502
  let q = p;
@@ -4170,11 +4546,14 @@ const armPool = (() => {
4170
4546
  at = freshCovered(before);
4171
4547
  }
4172
4548
  };
4173
- for (const m of lane.phase1)
4174
- m.collect(text, json, eat, spans, isHead, deletedAt);
4549
+ for (const m of lane.phase1) {
4550
+ view.begin();
4551
+ m.collect(text, view, json, eat, spans, isHead, deletedAt);
4552
+ }
4175
4553
  closeCoveragePositions(0);
4554
+ let evidence;
4176
4555
  if (lane.phase2.length > 0) {
4177
- const scan = buildForensicScan(tokenUnits(text), buildEvidenceRows(text), lane.phase2.length);
4556
+ const scan = buildForensicScan(tokenUnits(text), buildEvidenceRows(text), lane.phase2.length, (kind) => delimPositionsOf(text, kind));
4178
4557
  const reuse = forensicRescanMode === "incremental";
4179
4558
  const foot = scan.armFoot;
4180
4559
  let dirty;
@@ -4193,15 +4572,29 @@ const armPool = (() => {
4193
4572
  scan.self = NO_SELF;
4194
4573
  scan.footLo = FOOT_NONE;
4195
4574
  scan.footHi = -1;
4196
- lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans, isHead);
4575
+ const arm = lane.phase2[j];
4576
+ view.begin();
4577
+ arm.collect(scan, view, json, level === 0 ? eat : undefined, spans, isHead);
4197
4578
  foot[2 * j] = scan.footLo;
4198
4579
  foot[2 * j + 1] = scan.footHi;
4199
4580
  forensicRuns += 1;
4581
+ switch (arm.grain) {
4582
+ case "arm":
4583
+ noteExecution(scan, scan.footLo, scan.footHi, scan.footLo);
4584
+ break;
4585
+ case "segment":
4586
+ break;
4587
+ default: {
4588
+ const unhandled = arm.grain;
4589
+ throw new Error(`redact: unhandled evidence grain ${String(unhandled)}`);
4590
+ }
4591
+ }
4200
4592
  }
4201
4593
  closeCoveragePositions(before);
4202
4594
  forensicLevels += 1;
4203
4595
  const grown = foldEvidenceRows(scan.rows, spans, folded, (o) => familyCodeOfOrder[o]);
4204
4596
  folded = spans.length;
4597
+ evidence = { execs: scan.execs, rows: grown.next };
4205
4598
  if (grown.dirty.starts.length === 0)
4206
4599
  break;
4207
4600
  if (level + 1 >= FORENSIC_LEVEL_MAX) {
@@ -4224,9 +4617,12 @@ const armPool = (() => {
4224
4617
  }
4225
4618
  poolSpanCount += spans.length;
4226
4619
  const survives = (sp) => !engineMarked(sp.start, sp.end, sp.repl) && (sp.undecided !== true || reach === undefined || reach(sp.start));
4227
- const kept = resolveSpanOverlaps(spans.filter(survives));
4620
+ const cover = spans.filter(survives);
4621
+ const kept = resolveSpanOverlaps(cover);
4622
+ const open = isHead ? viewOpenOf(text, lane, spans, view.open) : text.length;
4623
+ const unattributed = view.unattributed;
4228
4624
  if (!wantCount || kept.length === 0)
4229
- return { hits: NO_HITS, kept };
4625
+ return { hits: NO_HITS, kept, cover, evidence, open, unattributed };
4230
4626
  const tokenOf = (sp) => members[sp.order].token;
4231
4627
  const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full" && survives(sp)).sort((a, b) => a.start - b.start);
4232
4628
  const byArmedToken = new Map();
@@ -4248,9 +4644,9 @@ const armPool = (() => {
4248
4644
  cur.count += 1;
4249
4645
  stamped.push({ ...sp, hit: { token, arm } });
4250
4646
  }
4251
- return { hits: [...byArmedToken.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm })), kept: stamped };
4647
+ return { hits: [...byArmedToken.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm })), kept: stamped, cover, evidence, open, unattributed };
4252
4648
  };
4253
- return Object.assign(run, { reaches, pendingTailUnits: Math.max(...reaches.map((r) => r.reach)), priors: members.map((m) => m.prior) });
4649
+ return Object.assign(run, { reaches, maxDecisionReach: Math.max(...reaches.map((r) => r.reach)), priors: members.map((m) => m.prior) });
4254
4650
  })();
4255
4651
  let onRedaction;
4256
4652
  export function setRedactionObserver(fn) {
@@ -4274,9 +4670,9 @@ export const WHOLE_EXTENT = { kind: "whole" };
4274
4670
  function invisibleClassesOf(layout) {
4275
4671
  switch (layout) {
4276
4672
  case "multi-line":
4277
- return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL;
4673
+ return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_IGNORABLE;
4278
4674
  case "single-line":
4279
- return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_WHITESPACE;
4675
+ return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_WHITESPACE | RAW_IGNORABLE;
4280
4676
  default: {
4281
4677
  const unhandled = layout;
4282
4678
  throw new Error(`redact: unhandled layout ${String(unhandled)}`);
@@ -4317,14 +4713,19 @@ export function readingHeadEnd(text, layout, units, from = 0) {
4317
4713
  }
4318
4714
  return text.length;
4319
4715
  }
4320
- function runRedaction(text, observe, json, wantHits, scope, layout, extent, observeFrom = 0) {
4716
+ function runRedaction(text, observe, json, wantHits, scope, layout, extent, observeFrom = 0, joints = NO_JOINTS) {
4321
4717
  scannedUnits += text.length;
4322
4718
  const wantCount = wantHits || (onRedaction !== undefined && observe);
4323
4719
  const eat = observe ? { from: -1, len: 0, capFrom: -1, capLen: 0 } : undefined;
4324
4720
  let spans = [];
4721
+ const cover = [];
4325
4722
  let hits = NO_HITS;
4326
4723
  let readsSoFar = 0;
4327
4724
  let unioned = false;
4725
+ const evidence = [];
4726
+ let lattice = text.length;
4727
+ let viewOpen = text.length;
4728
+ let unattributed = false;
4328
4729
  const read = (layers, view) => {
4329
4730
  const toOriginal = createViewToOriginal(layers);
4330
4731
  const reach = extent.kind === "head" ? (viewStart) => toOriginal.start(viewStart) < extent.emitEnd : undefined;
@@ -4343,7 +4744,17 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent, obse
4343
4744
  };
4344
4745
  const r = applyRules(view, wantCount, json, eat, scope, reach, engineMarked, deletedAt);
4345
4746
  forgetEscapedSeparatorTails();
4747
+ if (r.evidence !== undefined && (r.evidence.execs.length > 0 || r.evidence.rows.starts.length > 0)) {
4748
+ const viewLength = view.length;
4749
+ evidence.push({ execs: r.evidence.execs, rows: r.evidence.rows, at: (v) => (v >= viewLength ? text.length : toOriginal.start(v)) });
4750
+ }
4751
+ if (r.open < view.length)
4752
+ viewOpen = Math.min(viewOpen, toOriginal.start(r.open));
4753
+ if (r.unattributed)
4754
+ unattributed = true;
4346
4755
  readsSoFar += 1;
4756
+ for (const sp of toOriginal.spans(r.cover))
4757
+ cover.push(sp);
4347
4758
  if (readsSoFar === 1) {
4348
4759
  spans = toOriginal.spans(r.kept);
4349
4760
  hits = r.hits;
@@ -4360,6 +4771,7 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent, obse
4360
4771
  const rfold = deleteInvisibleCharacters(text, raw, rmask);
4361
4772
  const normalized = normalizeForRedaction(rfold.text);
4362
4773
  const spelled = collectSpelledCuts(normalized);
4774
+ lattice = Math.min(lattice, latticeOpenOf(text.length, rfold, normalized, spelled.open));
4363
4775
  for (let smask = 0;; smask = (smask - spelled.present) & spelled.present) {
4364
4776
  const sfold = deleteInvisibleCharacters(normalized, spelled, smask);
4365
4777
  read([{ fold: sfold }, { input: rfold.text, view: normalized }, { fold: rfold }], sfold.text);
@@ -4375,9 +4787,21 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent, obse
4375
4787
  recordFailOpen("server.redact.ppk-eat-to-end", `from=${eat.from} len=${eat.len}`);
4376
4788
  if (eat !== undefined && eat.capFrom >= 0)
4377
4789
  recordFailOpen("server.redact.forensic-level-cap", `from=${eat.capFrom} len=${eat.capLen}`);
4790
+ if (observe && unattributed)
4791
+ recordFailOpen("server.redact.scan-view-unattributed", `len=${text.length}`);
4378
4792
  if (observe)
4379
4793
  reportHits(observeFrom === 0 ? hits : hitsOfSpans(spans.filter((sp) => sp.end > observeFrom)));
4380
- return { text: applySpans(text, spans).out, hits, spans };
4794
+ const groups = mergeRedactionSpans(cover, poolTie, joints);
4795
+ return { text: renderRedactionSpans(text, groups, 0, text.length), hits, spans: groups, claims: spans, crossed: jointsCrossedBy(spans, joints), evidence, lattice, view: viewOpen };
4796
+ }
4797
+ function latticeOpenOf(n, rfold, normalized, open) {
4798
+ let at = n;
4799
+ if (open >= 0)
4800
+ at = createViewToOriginal([{ input: rfold.text, view: normalized }, { fold: rfold }]).start(open);
4801
+ const last = rfold.text.length - 1;
4802
+ if (last >= 0 && (rfold.text.charCodeAt(last) & 0xfc00) === 0xd800)
4803
+ at = Math.min(at, createViewToOriginal([{ fold: rfold }]).start(last));
4804
+ return at;
4381
4805
  }
4382
4806
  function hitsOfSpans(spans) {
4383
4807
  const by = new Map();
@@ -4415,8 +4839,12 @@ export function redactSecrets(text, opts) {
4415
4839
  if (before === "")
4416
4840
  return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT).text;
4417
4841
  const whole = before + text;
4418
- const r = runRedaction(whole, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT, before.length);
4419
- return renderRange(whole, r.spans, before.length, whole.length);
4842
+ const r = runRedaction(whole, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT, before.length, [before.length]);
4843
+ return renderRedactionSpans(whole, r.spans, before.length, whole.length);
4844
+ }
4845
+ export function isCredentialShapedUnit(unit) {
4846
+ const r = runRedaction(unit, false, false, true, "bounded-unit", "multi-line", WHOLE_EXTENT);
4847
+ return r.claims.some((claim) => claim.start === 0 && claim.hit?.arm === "full");
4420
4848
  }
4421
4849
  export function redactSecretsWithArms(text, opts) {
4422
4850
  const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", WHOLE_EXTENT);
@@ -4426,21 +4854,136 @@ export function redactionSpansForTest(text, opts) {
4426
4854
  const r = runRedaction(text, false, opts?.json === true, false, opts?.scope ?? "free-text", "multi-line", WHOLE_EXTENT);
4427
4855
  return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
4428
4856
  }
4429
- export function proposeRedactionSpans(text, scope, layout, extent) {
4430
- return runRedaction(text, true, false, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
4857
+ export function proposeRedactionSpans(text, scope, layout, extent, json) {
4858
+ return runRedaction(text, true, json, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
4431
4859
  }
4432
- export const PENDING_TAIL_UNITS = armPool.pendingTailUnits;
4860
+ export const MAX_DECISION_REACH = armPool.maxDecisionReach;
4433
4861
  export function regexDecisionReachForTest(re, lexical) {
4434
4862
  return regexDecisionReach(re, lexical);
4435
4863
  }
4436
- export function pendingTailReachForTest() {
4864
+ export function decisionReachForTest() {
4437
4865
  return armPool.reaches;
4438
4866
  }
4439
- function pendingTailStart(text, units) {
4440
- const r = readShortest(text, "multi-line");
4441
- return r.visible <= units ? 0 : createViewToOriginal(r.layers).start(r.visible - units);
4867
+ const FRONTIER_TERMS = {
4868
+ view: (m) => m.view,
4869
+ lattice: (m) => m.lattice,
4870
+ token: (m) => m.token,
4871
+ evidence: (m, f) => m.evidence(f),
4872
+ coverage: (m) => m.n,
4873
+ };
4874
+ const READ_PORTS = Object.keys(FRONTIER_TERMS);
4875
+ function settledFrontier(m) {
4876
+ let f = m.n;
4877
+ for (;;) {
4878
+ let next = f;
4879
+ for (const k of READ_PORTS)
4880
+ next = Math.min(next, FRONTIER_TERMS[k](m, next));
4881
+ if (next >= f)
4882
+ return f;
4883
+ f = next;
4884
+ }
4885
+ }
4886
+ function tokenTailOpen(text) {
4887
+ const n = text.length;
4888
+ const p = text.lastIndexOf("«");
4889
+ if (p < 0)
4890
+ return n;
4891
+ const e = tokenUnitEnd(text, p);
4892
+ if (e < 0)
4893
+ return n - p < TOKEN_UNIT_REACH && text.indexOf("»", p) < 0 ? p : n;
4894
+ if (e !== n)
4895
+ return n;
4896
+ let start = p;
4897
+ while (start > 0) {
4898
+ const q = text.lastIndexOf("«", start - 1);
4899
+ if (q < 0 || tokenUnitEnd(text, q) < start)
4900
+ break;
4901
+ start = q;
4902
+ }
4903
+ return start;
4904
+ }
4905
+ const TOKEN_UNIT_REACH = regexDecisionReach(TOKEN_UNIT_Y, false);
4906
+ function createEvidenceFrontier(readings) {
4907
+ const prepared = readings.filter((r) => r.execs.length > 0).map((r) => {
4908
+ const E = r.execs.length / 3;
4909
+ const order = Array.from({ length: E }, (_, i) => i);
4910
+ const hi = Float64Array.from(order, (i) => r.at(r.execs[3 * i + 1]));
4911
+ const from = Float64Array.from(order, (i) => r.at(r.execs[3 * i + 2]));
4912
+ order.sort((x, y) => hi[y] - hi[x]);
4913
+ const t = r.rows;
4914
+ const iv = [];
4915
+ for (let k = 0; k < t.starts.length; k++) {
4916
+ const s = r.at(t.starts[k]);
4917
+ const c = t.closeAt[k];
4918
+ const d = Math.max(r.at(t.ends[k] - 1) + 1, c >= 0 ? r.at(c) + 1 : 0);
4919
+ if (d - s >= 2)
4920
+ iv.push([s, d]);
4921
+ }
4922
+ iv.sort((a, b) => a[0] - b[0]);
4923
+ const segS = [];
4924
+ const segE = [];
4925
+ for (const [s, d] of iv) {
4926
+ const last = segE.length - 1;
4927
+ if (last >= 0 && s < segE[last])
4928
+ segE[last] = Math.max(segE[last], d);
4929
+ else {
4930
+ segS.push(s);
4931
+ segE.push(d);
4932
+ }
4933
+ }
4934
+ const horizon = (f) => {
4935
+ let a = 0;
4936
+ let b = segS.length;
4937
+ while (a < b) {
4938
+ const mid = (a + b) >>> 1;
4939
+ if (segS[mid] < f)
4940
+ a = mid + 1;
4941
+ else
4942
+ b = mid;
4943
+ }
4944
+ return a > 0 && f < segE[a - 1] ? segS[a - 1] : f;
4945
+ };
4946
+ return { order, hi, from, horizon };
4947
+ });
4948
+ if (prepared.length === 0)
4949
+ return (f) => f;
4950
+ return (f) => {
4951
+ let out = f;
4952
+ for (const p of prepared) {
4953
+ let cur = f;
4954
+ let least = Infinity;
4955
+ for (let k = 0;;) {
4956
+ const fev = p.horizon(cur);
4957
+ while (k < p.order.length && p.hi[p.order[k]] >= fev)
4958
+ least = Math.min(least, p.from[p.order[k++]]);
4959
+ if (least >= cur)
4960
+ break;
4961
+ cur = least;
4962
+ }
4963
+ out = Math.min(out, cur);
4964
+ }
4965
+ return out;
4966
+ };
4967
+ }
4968
+ export function evidenceFrontierForTest(execs, rows, f) {
4969
+ const t = {
4970
+ starts: Int32Array.from(rows, (r) => r[0]),
4971
+ ends: Int32Array.from(rows, (r) => r[1]),
4972
+ family: new Uint8Array(rows.length),
4973
+ anchor: Int32Array.from(rows, (r) => r[0]),
4974
+ closeAt: Int32Array.from(rows, (r) => r[2]),
4975
+ };
4976
+ return createEvidenceFrontier([{ execs: execs.flat(), rows: t, at: (v) => v }])(f);
4442
4977
  }
4443
- function renderRange(text, spans, from, until) {
4978
+ export function forensicExecutionsForTest(text) {
4979
+ const out = [];
4980
+ for (const r of runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length }).evidence) {
4981
+ for (let k = 0; k < r.execs.length; k += 3)
4982
+ out.push([r.at(r.execs[k]), r.at(r.execs[k + 1]), r.at(r.execs[k + 2])]);
4983
+ }
4984
+ return out;
4985
+ }
4986
+ export function renderRedactionSpans(text, spans, from, until) {
4444
4987
  if (from >= until)
4445
4988
  return "";
4446
4989
  const parts = [];
@@ -4459,9 +5002,11 @@ function renderRange(text, spans, from, until) {
4459
5002
  parts.push(text.slice(at, until));
4460
5003
  return parts.join("");
4461
5004
  }
4462
- const STREAM_RESCAN_MIN = 64;
4463
- const streamRescanStep = (n) => Math.max(STREAM_RESCAN_MIN, n >>> 5);
5005
+ const streamRescanStep = (n) => Math.max(1, n >>> 5);
4464
5006
  const STREAM_WINDOW_HORIZON = 2;
5007
+ if (STREAM_WINDOW_HORIZON * MAX_DECISION_REACH > MAX_RECOGNITION_SPAN) {
5008
+ throw new Error(`redact: the horizon of the scan window (${STREAM_WINDOW_HORIZON} × ${MAX_DECISION_REACH} units) exceeds the longest recognition span (${MAX_RECOGNITION_SPAN}) — the exit seat's margin would be shorter than what a decision reads`);
5009
+ }
4465
5010
  const STREAM_WINDOW_MIN = 2048;
4466
5011
  function deriveStreamWindowAlphabets(priors) {
4467
5012
  const blank = new Uint8Array(0x10000);
@@ -4481,7 +5026,7 @@ function deriveStreamWindowAlphabets(priors) {
4481
5026
  throw new Error("redact: an anchored arm declares an empty anchor literal");
4482
5027
  for (let i = 0; i < anchor.length; i++) {
4483
5028
  const c = anchor.charCodeAt(i);
4484
- if (INVISIBLE_UNIT_CLASS[c] !== 0 || FORMAT_CODEPOINT_TABLE[c] === 1 || (c & 0xf800) === 0xd800 || c === CH_BACKSLASH) {
5029
+ if (INVISIBLE_UNIT_CLASS[c] !== 0 || FORMAT_CODEPOINT_TABLE[c] === 1 || isReadingIgnorable(c) || (c & 0xf800) === 0xd800 || c === CH_BACKSLASH) {
4485
5030
  throw new Error(`redact: anchor literal ${JSON.stringify(anchor)} holds a unit some reading deletes or reads as an escape (index ${i}) — its presence is not monotone under deletion, so the stream window's delimiter check on the most-deleted reading would be incomplete`);
4486
5031
  }
4487
5032
  }
@@ -4545,15 +5090,70 @@ function windowAdvancePoint(text, units, alphabets = STREAM_WINDOW_ALPHABETS) {
4545
5090
  return at <= 0 ? 0 : text.lastIndexOf("\n", at - 1) + 1;
4546
5091
  }
4547
5092
  function windowIndependentAt(text, q) {
4548
- const full = runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length }).spans;
5093
+ return windowVerdictAt(text, q) === "independent";
5094
+ }
5095
+ function windowVerdictAt(text, q) {
5096
+ const fullRun = runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length });
5097
+ const full = fullRun.spans;
4549
5098
  if (full.some((s) => s.start < q && s.end > q))
4550
- return false;
5099
+ return "span-straddles";
5100
+ if (fullRun.evidence.some((r) => footprintStraddles(r, q)))
5101
+ return "footprint-straddles";
5102
+ if (fullRun.evidence.some((r) => rowStraddles(r, q)))
5103
+ return "row-straddles";
4551
5104
  const after = full.filter((s) => s.start >= q);
4552
5105
  const tail = text.slice(q);
4553
- const alone = runRedaction(tail, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: tail.length }).spans;
5106
+ const aloneRun = runRedaction(tail, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: tail.length });
5107
+ const alone = aloneRun.spans;
4554
5108
  if (after.length !== alone.length)
5109
+ return "spans-differ";
5110
+ if (!after.every((a, i) => a.start - q === alone[i].start && a.end - q === alone[i].end && a.repl === alone[i].repl))
5111
+ return "spans-differ";
5112
+ if (!sameRowSet(rowKeysFrom(fullRun.evidence, q), rowKeysFrom(aloneRun.evidence, 0)))
5113
+ return "rows-differ";
5114
+ return fullRun.view < q ? "view-straddles" : "independent";
5115
+ }
5116
+ export function windowVerdictAtForTest(text, q) {
5117
+ return windowVerdictAt(text, q);
5118
+ }
5119
+ function footprintStraddles(r, q) {
5120
+ for (let k = 0; k < r.execs.length; k += 3)
5121
+ if (r.at(r.execs[k]) < q && r.at(r.execs[k + 1]) >= q)
5122
+ return true;
5123
+ return false;
5124
+ }
5125
+ function rowStraddles(r, q) {
5126
+ const t = r.rows;
5127
+ for (let k = 0; k < t.starts.length; k++) {
5128
+ if (r.at(t.starts[k]) >= q)
5129
+ continue;
5130
+ const c = t.closeAt[k];
5131
+ if (Math.max(r.at(t.ends[k] - 1) + 1, c >= 0 ? r.at(c) + 1 : 0) > q)
5132
+ return true;
5133
+ }
5134
+ return false;
5135
+ }
5136
+ function rowKeysFrom(readings, q) {
5137
+ const keys = new Set();
5138
+ for (const r of readings) {
5139
+ const t = r.rows;
5140
+ for (let k = 0; k < t.starts.length; k++) {
5141
+ const s = r.at(t.starts[k]);
5142
+ if (s < q)
5143
+ continue;
5144
+ const c = t.closeAt[k];
5145
+ keys.add(`${s - q},${r.at(t.ends[k] - 1) + 1 - q},${t.family[k]},${r.at(t.anchor[k]) - q},${c >= 0 ? r.at(c) - q : -1}`);
5146
+ }
5147
+ }
5148
+ return keys;
5149
+ }
5150
+ function sameRowSet(a, b) {
5151
+ if (a.size !== b.size)
4555
5152
  return false;
4556
- return after.every((a, i) => a.start - q === alone[i].start && a.end - q === alone[i].end && a.repl === alone[i].repl);
5153
+ for (const k of a)
5154
+ if (!b.has(k))
5155
+ return false;
5156
+ return true;
4557
5157
  }
4558
5158
  export function windowAdvancePointForTest(text, units, extra = []) {
4559
5159
  return windowAdvancePoint(text, units, extra.length === 0 ? STREAM_WINDOW_ALPHABETS : deriveStreamWindowAlphabets([...armPool.priors, ...extra]));
@@ -4562,18 +5162,53 @@ export function windowIndependentAtForTest(text, q) {
4562
5162
  return windowIndependentAt(text, q);
4563
5163
  }
4564
5164
  export function createRedactionStream() {
4565
- return openRedactionStream(PENDING_TAIL_UNITS);
5165
+ return openRedactionStream();
4566
5166
  }
4567
5167
  export function createRedactionStreamWithoutWindowForTest() {
4568
- return openRedactionStream(PENDING_TAIL_UNITS, false);
5168
+ return openRedactionStream(false);
4569
5169
  }
4570
5170
  export function createRedactionStreamWithCertifierForTest(certify) {
4571
- return openRedactionStream(PENDING_TAIL_UNITS, true, certify);
4572
- }
4573
- export function createRedactionStreamWithTailForTest(units) {
4574
- return openRedactionStream(units);
4575
- }
4576
- function openRedactionStream(pendingTail, windowed = true, certify = windowIndependentAt) {
5171
+ return openRedactionStream(true, certify);
5172
+ }
5173
+ export function createRedactionStreamWithFrontierForTest(adjust) {
5174
+ return openRedactionStream(true, windowIndependentAt, adjust);
5175
+ }
5176
+ function settledCut(win, joints = NO_JOINTS) {
5177
+ const n = win.length;
5178
+ let rounds = 1;
5179
+ let r = runRedaction(win, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: n }, 0, joints);
5180
+ const lattice = r.lattice;
5181
+ const token = tokenTailOpen(win);
5182
+ let scanned = n;
5183
+ for (let tail = Math.min(lattice, token), back = 1; tail < scanned; back *= 2) {
5184
+ scanned = Math.max(0, Math.min(tail, scanned - back));
5185
+ const head = win.slice(0, scanned);
5186
+ r = runRedaction(head, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: scanned }, 0, joints);
5187
+ rounds += 1;
5188
+ tail = Math.min(r.lattice, tokenTailOpen(head));
5189
+ }
5190
+ const spans = r.spans;
5191
+ const inputs = { n, view: r.view, lattice, token, evidence: createEvidenceFrontier(r.evidence) };
5192
+ const frontier = settledFrontier(inputs);
5193
+ let cut = frontier;
5194
+ for (const s of spans)
5195
+ if (s.start < cut && (s.end >= cut || s.end >= scanned))
5196
+ cut = s.start;
5197
+ return { cut, spans, inputs, frontier, rounds };
5198
+ }
5199
+ export function settledCutsForTest(text) {
5200
+ const s = settledCut(text);
5201
+ const terms = Object.fromEntries(READ_PORTS.map((k) => [k, FRONTIER_TERMS[k](s.inputs, s.frontier)]));
5202
+ return { cut: s.cut, settled: renderRedactionSpans(text, s.spans, 0, s.cut), frontier: s.frontier, terms, rounds: s.rounds };
5203
+ }
5204
+ export function createScanViewForTest(text) {
5205
+ const r = new ScanReader(text);
5206
+ return { view: r, open: () => r.open, unattributed: () => r.unattributed };
5207
+ }
5208
+ export function expandHeadUnitsForTest(src, flags = "g") {
5209
+ return expandHeadUnits(src, flags).length;
5210
+ }
5211
+ function openRedactionStream(windowed = true, certify = windowIndependentAt, adjust = 0) {
4577
5212
  let past = [];
4578
5213
  let total = 0;
4579
5214
  let win = "";
@@ -4584,6 +5219,8 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4584
5219
  let sent = "";
4585
5220
  let scannedAt = 0;
4586
5221
  let evaluatedAt = -1;
5222
+ const commits = [];
5223
+ const jointsInWindow = () => (commits.length === 0 ? NO_JOINTS : commits.filter((c) => c > base).map((c) => c - base));
4587
5224
  let retryAt = 0;
4588
5225
  const whole = () => (past.length === 0 ? win : past.join("") + win);
4589
5226
  const advanceWindow = () => {
@@ -4592,7 +5229,7 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4592
5229
  evaluatedAt = total;
4593
5230
  retryAt = win.length + (win.length >> 1);
4594
5231
  windowEvaluations += 1;
4595
- const q = windowAdvancePoint(win, STREAM_WINDOW_HORIZON * pendingTail);
5232
+ const q = windowAdvancePoint(win, STREAM_WINDOW_HORIZON * MAX_DECISION_REACH);
4596
5233
  if (q < 0) {
4597
5234
  retryAt = 0;
4598
5235
  return;
@@ -4615,25 +5252,18 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4615
5252
  total += chunk.length;
4616
5253
  },
4617
5254
  takeSettled() {
4618
- if (total - released <= pendingTail)
4619
- return "";
4620
- if (total - scannedAt < streamRescanStep(total))
5255
+ if (released === total || total - scannedAt < streamRescanStep(total))
4621
5256
  return "";
4622
5257
  scannedAt = total;
4623
- const emitEnd = pendingTailStart(win, pendingTail);
4624
5258
  const from = released - base;
4625
- if (emitEnd <= from)
4626
- return "";
4627
- const spans = runRedaction(win, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd }).spans;
4628
- let cut = emitEnd;
4629
- for (const s of spans)
4630
- if (s.start < cut && s.end > cut)
4631
- cut = s.start;
5259
+ const settled = settledCut(win, jointsInWindow());
5260
+ const spans = settled.spans;
5261
+ let cut = Math.min(win.length, settled.cut + adjust);
4632
5262
  if (cut > 0 && cut < win.length && (win.charCodeAt(cut - 1) & 0xfc00) === 0xd800 && (win.charCodeAt(cut) & 0xfc00) === 0xdc00)
4633
5263
  cut -= 1;
4634
5264
  if (cut <= from)
4635
5265
  return "";
4636
- const out = renderRange(win, spans, from, cut);
5266
+ const out = renderRedactionSpans(win, spans, from, cut);
4637
5267
  released = base + cut;
4638
5268
  sent += out;
4639
5269
  return out;
@@ -4641,9 +5271,10 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4641
5271
  takeAll() {
4642
5272
  if (released === total)
4643
5273
  return "";
4644
- const spans = runRedaction(win, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans;
4645
- const out = renderRange(win, spans, released - base, win.length);
5274
+ const spans = runRedaction(win, false, false, false, "free-text", "multi-line", WHOLE_EXTENT, 0, jointsInWindow()).spans;
5275
+ const out = renderRedactionSpans(win, spans, released - base, win.length);
4646
5276
  released = scannedAt = total;
5277
+ commits.push(total);
4647
5278
  sent += out;
4648
5279
  return out;
4649
5280
  },
@@ -4651,12 +5282,13 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4651
5282
  if (deliveredFrom === total)
4652
5283
  return { rest: "", diverged: false };
4653
5284
  const buf = whole();
4654
- const spans = runRedaction(buf, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans;
4655
- const split = released > deliveredFrom && spans.some((s) => s.start < released && s.end > released);
4656
- return { rest: renderRange(buf, spans, released, buf.length), diverged: split || renderRange(buf, spans, deliveredFrom, released) !== sent };
5285
+ const { spans, crossed } = runRedaction(buf, false, false, false, "free-text", "multi-line", WHOLE_EXTENT, 0, commits);
5286
+ const split = released > deliveredFrom && (spans.some((s) => s.start < released && s.end > released) || crossed.some((j) => j > deliveredFrom));
5287
+ return { rest: renderRedactionSpans(buf, spans, released, buf.length), diverged: split || renderRedactionSpans(buf, spans, deliveredFrom, released) !== sent };
4657
5288
  },
4658
5289
  skip() {
4659
5290
  released = deliveredFrom = scannedAt = total;
5291
+ commits.push(total);
4660
5292
  sent = "";
4661
5293
  },
4662
5294
  endBlock() {
@@ -4824,10 +5456,6 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
4824
5456
  }
4825
5457
  return false;
4826
5458
  }
4827
- let displaySafeFailClosedWarned = false;
4828
- export function resetDisplaySafeFailClosedWarnForTest() {
4829
- displaySafeFailClosedWarned = false;
4830
- }
4831
5459
  export function buildDisplaySafeUrl(raw, positionalLaw = displayUrlOf) {
4832
5460
  return buildDisplaySafeUrlChecked(raw, positionalLaw).text;
4833
5461
  }
@@ -4863,10 +5491,6 @@ export function buildDisplaySafeRefusal(text, sourceUrls) {
4863
5491
  const REFUSAL_WITHHELD_TEXT = "[route refusal text withheld: it quotes a configured URL that carries a credential, and the upstream wording cannot be re-redacted safely once the URL has been rewritten for display. " +
4864
5492
  "The machine-readable verdict is on the pairing field beside this one; fix the configuration so the credential is not in the URL.]";
4865
5493
  function noteDisplaySafeFailClosed(level) {
4866
- if (displaySafeFailClosedWarned)
4867
- return;
4868
- displaySafeFailClosedWarned = true;
4869
- process.emitWarning(`display-safe URL composition fell back to fail-closed (${level}): a part of the configured URL was judged to carry a credential that the composition cannot redact at byte granularity, so that whole part is withheld. ` +
4870
- `The product is safe, but this deployment has an input hitting the composition seam — report it upstream. No original bytes are logged.`, "SemaRedactionFailClosed");
5494
+ recordFailOpen("server.redact.display-safe-fail-closed", `level=${level}`);
4871
5495
  }
4872
5496
  //# sourceMappingURL=redact.js.map