@sema-agent/server 7.146.0 → 7.148.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 (850) hide show
  1. package/MIGRATION.md +1423 -24
  2. package/README.md +21 -8
  3. package/README.zh-CN.md +15 -9
  4. package/USAGE.md +537 -216
  5. package/deploy/facts/lanes.json +7 -0
  6. package/deploy/sema-up/README.md +18 -7
  7. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +42 -4
  9. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
  10. package/deploy/sema-up/chart/templates/server.yaml +5 -4
  11. package/deploy/sema-up/chart/values.yaml +4 -3
  12. package/deploy/sema-up/cluster-up.sh +75 -176
  13. package/deploy/sema-up/kube-up.sh +29 -49
  14. package/deploy/sema-up/lib/chart-run.sh +23 -0
  15. package/deploy/sema-up/lib/dataroot.sh +3 -3
  16. package/deploy/sema-up/lib/flagshape.sh +4 -0
  17. package/deploy/sema-up/lib/health.sh +62 -13
  18. package/deploy/sema-up/lib/helm-values-cluster.py +18 -0
  19. package/deploy/sema-up/lib/helm-values-kube.py +35 -0
  20. package/deploy/sema-up/lib/lanes.json +2 -5
  21. package/deploy/sema-up/lib/regcred.py +8 -0
  22. package/deploy/sema-up/lib/remote/helm-install.sh +3 -0
  23. package/deploy/sema-up/lib/remote/image-import.sh +6 -0
  24. package/deploy/sema-up/lib/remote/machine-precheck.sh +11 -0
  25. package/deploy/sema-up/lib/remote/region-probe.sh +3 -0
  26. package/deploy/sema-up/lib/remote/stack-ready.sh +26 -0
  27. package/deploy/sema-up/lib/remote/vxlan-guard.sh +8 -0
  28. package/deploy/sema-up/lib/render-stack.py +17 -0
  29. package/deploy/sema-up/lib/shell-text.sh +87 -0
  30. package/deploy/sema-up/lib/usage-flags.txt +10 -0
  31. package/deploy/sema-up/lib/usage-head.txt +1 -0
  32. package/deploy/sema-up/lib/usage-tail.txt +13 -0
  33. package/deploy/sema-up/preflight.sh +10 -8
  34. package/deploy/sema-up/sema-up.sh +26 -48
  35. package/deploy/sema-up/smoke.sh +7 -6
  36. package/deploy/sema-up/stack/compose.dataroot.yaml +3 -0
  37. package/deploy/sema-up/stack/compose.yaml +18 -5
  38. package/deploy/sema-up/stack/sema-stack-cluster.yaml +25 -3
  39. package/dist/adoption/plan.d.ts +24 -0
  40. package/dist/adoption/plan.js +53 -1
  41. package/dist/adoption/runner.d.ts +10 -1
  42. package/dist/adoption/runner.js +36 -16
  43. package/dist/adoption/wire.d.ts +3 -3
  44. package/dist/approval-card.d.ts +32 -34
  45. package/dist/approval-gate-currency.js +3 -2
  46. package/dist/approval-reconciler.d.ts +4 -13
  47. package/dist/approval-reconciler.js +2 -2
  48. package/dist/approval.d.ts +16 -14
  49. package/dist/approval.js +3 -3
  50. package/dist/audit.d.ts +10 -3
  51. package/dist/audit.js +17 -9
  52. package/dist/auth-bridge.d.ts +0 -14
  53. package/dist/auth-bridge.js +0 -7
  54. package/dist/auto-mode-face.d.ts +10 -7
  55. package/dist/auto-mode-face.js +7 -4
  56. package/dist/background-agent-name.d.ts +19 -1
  57. package/dist/background-agent-name.js +12 -2
  58. package/dist/background-session-reap.d.ts +1 -1
  59. package/dist/bake-runner/main.js +9 -6
  60. package/dist/bench/s1/arms.js +6 -5
  61. package/dist/bench/s1/live-deps.js +8 -3
  62. package/dist/bench/s1/run-firm.js +6 -5
  63. package/dist/bind-host.d.ts +25 -0
  64. package/dist/bind-host.js +25 -0
  65. package/dist/boot/adoption.d.ts +3 -0
  66. package/dist/boot/adoption.js +1 -0
  67. package/dist/boot/crash-last.js +2 -1
  68. package/dist/boot/deployment-mods.d.ts +1 -1
  69. package/dist/boot/device-lane.js +5 -5
  70. package/dist/boot/engine-lease.d.ts +12 -11
  71. package/dist/boot/execution-env.d.ts +19 -8
  72. package/dist/boot/execution-env.js +80 -62
  73. package/dist/boot/execution-lane.d.ts +37 -9
  74. package/dist/boot/execution-lane.js +74 -20
  75. package/dist/boot/exit-boundary.d.ts +3 -3
  76. package/dist/boot/exit-boundary.js +6 -5
  77. package/dist/boot/exit-last.js +2 -1
  78. package/dist/boot/fatal-exit.d.ts +3 -5
  79. package/dist/boot/fatal-exit.js +2 -2
  80. package/dist/boot/governance-seams.d.ts +2 -2
  81. package/dist/boot/leader.d.ts +15 -0
  82. package/dist/boot/leader.js +18 -16
  83. package/dist/boot/memory-boundary.d.ts +11 -11
  84. package/dist/boot/memory-boundary.js +3 -3
  85. package/dist/boot/memory-consolidation.d.ts +50 -53
  86. package/dist/boot/memory-consolidation.js +11 -18
  87. package/dist/boot/org-memory.js +4 -3
  88. package/dist/boot/parked-revive-gate.d.ts +1 -1
  89. package/dist/boot/parked-revive-gate.js +3 -3
  90. package/dist/boot/reapers.d.ts +4 -3
  91. package/dist/boot/reapers.js +20 -17
  92. package/dist/boot/resolve-spec.d.ts +5 -3
  93. package/dist/boot/resolve-spec.js +133 -165
  94. package/dist/boot/retention-lane.d.ts +7 -45
  95. package/dist/boot/retention-lane.js +2 -2
  96. package/dist/boot/runner-deps.d.ts +28 -7
  97. package/dist/boot/runner-deps.js +11 -4
  98. package/dist/boot/runtime-caps.d.ts +2 -2
  99. package/dist/boot/runtime-caps.js +16 -2
  100. package/dist/boot/session-faces.d.ts +17 -7
  101. package/dist/boot/session-faces.js +122 -96
  102. package/dist/boot/shutdown.d.ts +5 -4
  103. package/dist/boot/shutdown.js +6 -4
  104. package/dist/boot/stage-01-config.d.ts +1 -1
  105. package/dist/boot/stage-01-config.js +14 -2
  106. package/dist/boot/stage-02-web-admin-pull.d.ts +2 -1
  107. package/dist/boot/stage-02-web-admin-pull.js +15 -8
  108. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  109. package/dist/boot/stage-03-store-backend.js +4 -3
  110. package/dist/boot/stage-04-stores.d.ts +3 -1
  111. package/dist/boot/stage-04-stores.js +2 -2
  112. package/dist/boot/stage-05-execution-env.d.ts +9 -5
  113. package/dist/boot/stage-05-execution-env.js +5 -5
  114. package/dist/boot/stage-06-runners.d.ts +13 -15
  115. package/dist/boot/stage-06-runners.js +24 -18
  116. package/dist/boot/stage-07-capability-layer.d.ts +14 -11
  117. package/dist/boot/stage-07-capability-layer.js +9 -12
  118. package/dist/boot/stage-08-reapers.d.ts +14 -11
  119. package/dist/boot/stage-08-reapers.js +3 -3
  120. package/dist/boot/stage-09-leader.d.ts +16 -18
  121. package/dist/boot/stage-09-leader.js +12 -4
  122. package/dist/boot/stage-10-http-server.d.ts +16 -18
  123. package/dist/boot/stage-10-http-server.js +9 -5
  124. package/dist/boot/store-evidence.d.ts +20 -0
  125. package/dist/boot/store-evidence.js +13 -0
  126. package/dist/boot/stores.d.ts +12 -0
  127. package/dist/boot/stores.js +39 -17
  128. package/dist/boot/task-list-lane.d.ts +4 -4
  129. package/dist/boot/web-admin.d.ts +4 -2
  130. package/dist/boot/web-admin.js +58 -38
  131. package/dist/boot/webfetch-summarize-lane.d.ts +1 -1
  132. package/dist/boot-reclaim.d.ts +3 -1
  133. package/dist/boot-reclaim.js +1 -1
  134. package/dist/brain.d.ts +7 -18
  135. package/dist/brain.js +5 -17
  136. package/dist/budget.js +6 -6
  137. package/dist/capabilities/center-plugins.d.ts +9 -1
  138. package/dist/capabilities/center-plugins.js +37 -11
  139. package/dist/capabilities/center-wiring.js +1 -1
  140. package/dist/capabilities/composite-members.d.ts +3 -2
  141. package/dist/capabilities/composite-members.js +3 -2
  142. package/dist/capabilities/execution-lane.d.ts +7 -7
  143. package/dist/capabilities/execution-lane.js +2 -2
  144. package/dist/capabilities/hands-lane.d.ts +1 -1
  145. package/dist/capabilities/repo-tools.d.ts +2 -0
  146. package/dist/capabilities/repo-tools.js +14 -12
  147. package/dist/capabilities/scenarios.d.ts +38 -33
  148. package/dist/capabilities/scenarios.js +24 -25
  149. package/dist/capabilities/settings-contract.d.ts +29 -0
  150. package/dist/capabilities/settings-contract.js +14 -0
  151. package/dist/capabilities/skills.d.ts +6 -6
  152. package/dist/capabilities/tool-defer.d.ts +1 -1
  153. package/dist/capability-facts.d.ts +69 -4
  154. package/dist/capability-facts.js +77 -5
  155. package/dist/card-void.d.ts +4 -4
  156. package/dist/card-void.js +1 -1
  157. package/dist/center-credential-renewer.d.ts +6 -2
  158. package/dist/center-credential-renewer.js +10 -5
  159. package/dist/config-catalog.d.ts +16 -1
  160. package/dist/config-catalog.js +68 -61
  161. package/dist/config-dir-trust.d.ts +37 -4
  162. package/dist/config-dir-trust.js +88 -44
  163. package/dist/config-invariants.d.ts +58 -4
  164. package/dist/config-invariants.js +104 -23
  165. package/dist/config-lkg.d.ts +24 -2
  166. package/dist/config-lkg.js +22 -9
  167. package/dist/config-provider.d.ts +9 -9
  168. package/dist/config-provider.js +25 -108
  169. package/dist/config-pull-failure.d.ts +50 -0
  170. package/dist/config-pull-failure.js +48 -0
  171. package/dist/config-types.d.ts +215 -202
  172. package/dist/config.d.ts +69 -14
  173. package/dist/config.js +188 -96
  174. package/dist/degenerate-instrument.js +1 -1
  175. package/dist/delegated-session-owner.d.ts +1 -1
  176. package/dist/deleted-session-ids.d.ts +3 -2
  177. package/dist/deploy-facts.js +3 -2
  178. package/dist/deployment-governance.d.ts +3 -3
  179. package/dist/deployment-shape.d.ts +51 -16
  180. package/dist/deployment-shape.js +31 -6
  181. package/dist/egress-redirect.d.ts +25 -7
  182. package/dist/egress-redirect.js +20 -2
  183. package/dist/elicitation.d.ts +6 -5
  184. package/dist/elicitation.js +23 -22
  185. package/dist/engine-identity.d.ts +28 -0
  186. package/dist/engine-identity.js +10 -0
  187. package/dist/entry-problems.d.ts +5 -7
  188. package/dist/entry-problems.js +1 -1
  189. package/dist/env-facts.d.ts +7 -5
  190. package/dist/env-facts.js +12 -7
  191. package/dist/env-witness.d.ts +7 -0
  192. package/dist/env-witness.js +16 -0
  193. package/dist/execution-config-root.d.ts +5 -5
  194. package/dist/execution-config-root.js +5 -5
  195. package/dist/execution-env-purpose.d.ts +14 -0
  196. package/dist/execution-env-purpose.js +29 -0
  197. package/dist/execution-lane-caps.d.ts +53 -103
  198. package/dist/execution-lane-caps.js +51 -83
  199. package/dist/execution-placement.d.ts +65 -9
  200. package/dist/execution-placement.js +58 -0
  201. package/dist/field-conflict.d.ts +7 -11
  202. package/dist/field-conflict.js +3 -3
  203. package/dist/file-history-capture.d.ts +3 -3
  204. package/dist/file-history-disclosure.d.ts +2 -2
  205. package/dist/file-history-disclosure.js +1 -0
  206. package/dist/fleet/drain-entry.d.ts +17 -10
  207. package/dist/fleet/drain-entry.js +1 -1
  208. package/dist/fleet/fleet-bus.d.ts +21 -3
  209. package/dist/fleet/fleet-bus.js +35 -25
  210. package/dist/fleet/fleet-reconciler.js +2 -2
  211. package/dist/fleet/serving-lease.js +6 -7
  212. package/dist/fleet-client.js +3 -3
  213. package/dist/fleet-lease.d.ts +0 -1
  214. package/dist/fleet-lease.js +4 -5
  215. package/dist/history-anchor.d.ts +42 -0
  216. package/dist/history-anchor.js +34 -0
  217. package/dist/hitl-delivery.d.ts +90 -0
  218. package/dist/hitl-delivery.js +98 -0
  219. package/dist/hooks/classic-stations.d.ts +8 -11
  220. package/dist/hooks/classic-stations.js +0 -7
  221. package/dist/hooks/command-spawn.d.ts +1 -4
  222. package/dist/hooks/command-spawn.js +0 -11
  223. package/dist/hooks/hook-llm.d.ts +5 -1
  224. package/dist/hooks/hook-llm.js +24 -15
  225. package/dist/hooks/hook-runner.d.ts +119 -65
  226. package/dist/hooks/hook-runner.js +415 -217
  227. package/dist/host-lsp-manager.d.ts +22 -8
  228. package/dist/host-lsp-manager.js +4 -1
  229. package/dist/http/active-run-conflict.d.ts +2 -43
  230. package/dist/http/active-run-conflict.js +3 -7
  231. package/dist/http/admission.d.ts +4 -18
  232. package/dist/http/admission.js +75 -78
  233. package/dist/http/approval-sweeps.d.ts +5 -8
  234. package/dist/http/approval-sweeps.js +55 -30
  235. package/dist/http/caller-trust.d.ts +8 -10
  236. package/dist/http/caller-trust.js +1 -2
  237. package/dist/http/checkpoint-ctx.d.ts +17 -0
  238. package/dist/http/checkpoint-ctx.js +6 -0
  239. package/dist/http/claim-heartbeat.d.ts +49 -0
  240. package/dist/http/claim-heartbeat.js +46 -0
  241. package/dist/http/claim-latency.d.ts +10 -0
  242. package/dist/http/claim-latency.js +12 -0
  243. package/dist/http/claimed-run.d.ts +4 -3
  244. package/dist/http/dispatch.d.ts +8 -1
  245. package/dist/http/dispatch.js +77 -31
  246. package/dist/http/durable-run-leg.d.ts +3 -3
  247. package/dist/http/durable-run-leg.js +18 -12
  248. package/dist/http/execution-context.d.ts +18 -42
  249. package/dist/http/execution-context.js +11 -5
  250. package/dist/http/health-reader.d.ts +36 -0
  251. package/dist/http/health-reader.js +21 -0
  252. package/dist/http/health-self-report.d.ts +9 -9
  253. package/dist/http/health-self-report.js +4 -4
  254. package/dist/http/idempotency.d.ts +9 -5
  255. package/dist/http/idempotency.js +4 -4
  256. package/dist/http/memory-compliance-failure-reply.d.ts +15 -16
  257. package/dist/http/memory-compliance-failure-reply.js +7 -2
  258. package/dist/http/memory-face-fault-reply.d.ts +67 -0
  259. package/dist/http/memory-face-fault-reply.js +71 -0
  260. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  261. package/dist/http/memory-store-refusal-reply.js +6 -0
  262. package/dist/http/owner-access.d.ts +391 -56
  263. package/dist/http/owner-access.js +372 -33
  264. package/dist/http/principal-gate.d.ts +26 -94
  265. package/dist/http/principal-gate.js +24 -81
  266. package/dist/http/refusal-body.d.ts +35 -2
  267. package/dist/http/refusal-body.js +34 -0
  268. package/dist/http/request-guards.d.ts +10 -14
  269. package/dist/http/request-guards.js +34 -68
  270. package/dist/http/resume-composition.d.ts +37 -0
  271. package/dist/http/resume-composition.js +18 -0
  272. package/dist/http/resume-ctx-read.d.ts +35 -12
  273. package/dist/http/resume-ctx-read.js +25 -6
  274. package/dist/http/resume-legs.d.ts +24 -24
  275. package/dist/http/resume-legs.js +260 -171
  276. package/dist/http/retired-skill-manifest.d.ts +1 -1
  277. package/dist/http/rewind-files-reply.d.ts +34 -0
  278. package/dist/http/rewind-files-reply.js +36 -0
  279. package/dist/http/route-ctx.d.ts +56 -47
  280. package/dist/http/route-table.js +2 -0
  281. package/dist/http/routes/a2a-serve.d.ts +43 -5
  282. package/dist/http/routes/a2a-serve.js +58 -37
  283. package/dist/http/routes/admin-config-refresh.js +2 -2
  284. package/dist/http/routes/admin-drain.js +2 -2
  285. package/dist/http/routes/admin-memory-optout.js +4 -3
  286. package/dist/http/routes/adoption.d.ts +3 -0
  287. package/dist/http/routes/adoption.js +17 -10
  288. package/dist/http/routes/agents-roster.js +13 -7
  289. package/dist/http/routes/approvals-assistant.js +73 -88
  290. package/dist/http/routes/attachments.js +2 -2
  291. package/dist/http/routes/background.d.ts +3 -3
  292. package/dist/http/routes/background.js +9 -17
  293. package/dist/http/routes/capabilities.js +23 -20
  294. package/dist/http/routes/config-catalog.js +2 -2
  295. package/dist/http/routes/devices.d.ts +3 -3
  296. package/dist/http/routes/devices.js +5 -5
  297. package/dist/http/routes/diagnostics.d.ts +27 -3
  298. package/dist/http/routes/diagnostics.js +27 -21
  299. package/dist/http/routes/engine.js +2 -2
  300. package/dist/http/routes/fleet.js +5 -3
  301. package/dist/http/routes/images.js +10 -6
  302. package/dist/http/routes/leader.d.ts +7 -2
  303. package/dist/http/routes/leader.js +12 -3
  304. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  305. package/dist/http/routes/mcp-reconnect.js +7 -15
  306. package/dist/http/routes/memory-bundle.js +4 -7
  307. package/dist/http/routes/memory-compliance.js +4 -4
  308. package/dist/http/routes/memory-consolidation.js +6 -9
  309. package/dist/http/routes/memory-origin.d.ts +3 -1
  310. package/dist/http/routes/memory-origin.js +7 -8
  311. package/dist/http/routes/memory-policy.js +17 -12
  312. package/dist/http/routes/notify-wake.js +12 -20
  313. package/dist/http/routes/observability.js +3 -3
  314. package/dist/http/routes/principal-inbox.d.ts +1 -1
  315. package/dist/http/routes/principal-inbox.js +2 -2
  316. package/dist/http/routes/projects.d.ts +70 -0
  317. package/dist/http/routes/projects.js +87 -0
  318. package/dist/http/routes/retention-ops.js +6 -9
  319. package/dist/http/routes/rules.js +50 -7
  320. package/dist/http/routes/run-memory-capture.d.ts +2 -2
  321. package/dist/http/routes/run-memory-capture.js +6 -12
  322. package/dist/http/routes/run-permission-mode.d.ts +2 -2
  323. package/dist/http/routes/run-permission-mode.js +10 -17
  324. package/dist/http/routes/runs.js +148 -233
  325. package/dist/http/routes/session-memory-sync.d.ts +2 -2
  326. package/dist/http/routes/session-memory-sync.js +7 -13
  327. package/dist/http/routes/session-sync.d.ts +4 -2
  328. package/dist/http/routes/session-sync.js +48 -30
  329. package/dist/http/routes/sessions-list.d.ts +3 -3
  330. package/dist/http/routes/sessions-list.js +9 -9
  331. package/dist/http/routes/sessions.d.ts +7 -1
  332. package/dist/http/routes/sessions.js +177 -112
  333. package/dist/http/routes/shared-memory.js +6 -6
  334. package/dist/http/routes/side-query.js +2 -2
  335. package/dist/http/routes/tasks.js +144 -122
  336. package/dist/http/routes/trace-usage.d.ts +0 -12
  337. package/dist/http/routes/trace-usage.js +40 -46
  338. package/dist/http/routes/workflows.d.ts +39 -4
  339. package/dist/http/routes/workflows.js +42 -14
  340. package/dist/http/run-concurrency-cap.d.ts +20 -12
  341. package/dist/http/run-concurrency-cap.js +39 -26
  342. package/dist/http/run-read-face.d.ts +3 -5
  343. package/dist/http/run-read-face.js +8 -13
  344. package/dist/http/send.d.ts +12 -3
  345. package/dist/http/send.js +42 -6
  346. package/dist/http/server.d.ts +63 -50
  347. package/dist/http/server.js +52 -16
  348. package/dist/http/sse-log.d.ts +17 -10
  349. package/dist/http/sse-log.js +7 -7
  350. package/dist/http/stored-body-replay.d.ts +17 -3
  351. package/dist/http/stored-body-replay.js +11 -2
  352. package/dist/http/submit-ctx.d.ts +16 -8
  353. package/dist/http/submit-ctx.js +6 -2
  354. package/dist/http/submit-replay.d.ts +19 -6
  355. package/dist/http/submit-replay.js +18 -6
  356. package/dist/http/sync-submit-body.d.ts +3 -4
  357. package/dist/http/sync-submit-body.js +4 -5
  358. package/dist/http/wire-types.d.ts +23 -13
  359. package/dist/identity-source.d.ts +36 -5
  360. package/dist/identity-source.js +27 -0
  361. package/dist/index.d.ts +3 -2
  362. package/dist/index.js +2 -1
  363. package/dist/instruction-source-notice.d.ts +1 -1
  364. package/dist/key-resolver.d.ts +32 -0
  365. package/dist/key-resolver.js +23 -9
  366. package/dist/leader/diffup.js +2 -1
  367. package/dist/leader/endpoint.d.ts +26 -5
  368. package/dist/leader/endpoint.js +154 -14
  369. package/dist/leader/fanout.d.ts +3 -0
  370. package/dist/leader/fanout.js +78 -63
  371. package/dist/leader/leader.d.ts +16 -0
  372. package/dist/leader/leader.js +43 -3
  373. package/dist/leader/merge.d.ts +9 -1
  374. package/dist/leader/merge.js +16 -1
  375. package/dist/leader/repair-wire.js +1 -1
  376. package/dist/leader/wire.d.ts +36 -3
  377. package/dist/leader/wire.js +59 -18
  378. package/dist/lsp-evict.d.ts +6 -4
  379. package/dist/lsp-evict.js +2 -3
  380. package/dist/memory-embedder-reembed.d.ts +38 -0
  381. package/dist/memory-embedder-reembed.js +20 -0
  382. package/dist/memory-incident-sink.d.ts +0 -28
  383. package/dist/memory-incident-sink.js +5 -0
  384. package/dist/memory-operator-faces.d.ts +5 -4
  385. package/dist/memory-operator-faces.js +1 -1
  386. package/dist/memory-scope.d.ts +31 -4
  387. package/dist/memory-scope.js +65 -16
  388. package/dist/memory-store-refusal.d.ts +20 -4
  389. package/dist/memory-store-refusal.js +1 -1
  390. package/dist/memory-sync-client.d.ts +5 -2
  391. package/dist/memory-sync-client.js +19 -14
  392. package/dist/memory-sync.js +1 -1
  393. package/dist/model-compat.d.ts +51 -28
  394. package/dist/model-compat.js +55 -23
  395. package/dist/model-context-window.d.ts +18 -0
  396. package/dist/model-context-window.js +2 -0
  397. package/dist/model-entry-headers.d.ts +46 -4
  398. package/dist/model-entry-headers.js +2 -2
  399. package/dist/model-entry-refusal.d.ts +46 -17
  400. package/dist/model-entry-refusal.js +47 -27
  401. package/dist/model-plane-face.d.ts +19 -11
  402. package/dist/model-plane-face.js +9 -1
  403. package/dist/model-plane-probe.d.ts +2 -0
  404. package/dist/model-plane-probe.js +17 -0
  405. package/dist/model-provider.d.ts +3 -3
  406. package/dist/model-route-endpoint.d.ts +9 -5
  407. package/dist/model-route-endpoint.js +29 -7
  408. package/dist/model-select.d.ts +16 -0
  409. package/dist/model-select.js +4 -0
  410. package/dist/mountinfo.d.ts +37 -0
  411. package/dist/mountinfo.js +47 -0
  412. package/dist/namespaced-rule-name.d.ts +28 -2
  413. package/dist/namespaced-rule-name.js +10 -1
  414. package/dist/net-address.d.ts +1 -1
  415. package/dist/observability/corrupt-read-seat.d.ts +4 -4
  416. package/dist/observability/corrupt-read-seat.js +5 -3
  417. package/dist/observability/fail-open.d.ts +75 -47
  418. package/dist/observability/fail-open.js +77 -47
  419. package/dist/observability/logger.d.ts +22 -1
  420. package/dist/observability/logger.js +89 -12
  421. package/dist/observability/metrics.d.ts +2 -0
  422. package/dist/observability/metrics.js +5 -4
  423. package/dist/observability/otel-exporter.js +2 -1
  424. package/dist/observability/principal-context.d.ts +14 -7
  425. package/dist/observability/principal-context.js +8 -3
  426. package/dist/observability/run-terminal.d.ts +7 -1
  427. package/dist/observability/run-terminal.js +5 -2
  428. package/dist/observability/secret-env-scrub.d.ts +30 -4
  429. package/dist/observability/secret-env-scrub.js +15 -3
  430. package/dist/orchestration/hardened-vm-runner.d.ts +1 -0
  431. package/dist/orchestration/hardened-vm-runner.js +11 -5
  432. package/dist/orchestration/hardened-vm-worker-runner.d.ts +0 -14
  433. package/dist/orchestration/hardened-vm-worker-runner.js +5 -0
  434. package/dist/orchestration/hardened-vm-worker.js +2 -1
  435. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  436. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  437. package/dist/orchestration/workflow-completion-inbox.d.ts +29 -9
  438. package/dist/orchestration/workflow-completion-inbox.js +65 -46
  439. package/dist/org-delivery-gate.d.ts +5 -4
  440. package/dist/org-delivery-gate.js +7 -6
  441. package/dist/org-memory-admission.d.ts +5 -2
  442. package/dist/org-memory-admission.js +25 -10
  443. package/dist/outbound-dispatcher.d.ts +1 -1
  444. package/dist/outbound-dispatcher.js +6 -12
  445. package/dist/owner-brands.d.ts +81 -0
  446. package/dist/owner-brands.js +42 -0
  447. package/dist/owner-faces.d.ts +531 -0
  448. package/dist/owner-faces.js +126 -0
  449. package/dist/parked-decide.d.ts +100 -31
  450. package/dist/parked-decide.js +162 -48
  451. package/dist/parked-revive-surface.d.ts +14 -4
  452. package/dist/parked-revive-surface.js +5 -4
  453. package/dist/peer-directory.d.ts +6 -8
  454. package/dist/peer-directory.js +3 -2
  455. package/dist/permission-rule-vocab.d.ts +53 -8
  456. package/dist/permission-rule-vocab.js +30 -2
  457. package/dist/plugins/adoption-log-sql.d.ts +0 -22
  458. package/dist/plugins/adoption-log-sql.js +5 -3
  459. package/dist/plugins/approval-ask-retired-delete-by-task-m50.d.ts +7 -0
  460. package/dist/plugins/approval-ask-retired-delete-by-task-m50.js +17 -0
  461. package/dist/plugins/approval-ask-store-file.d.ts +28 -9
  462. package/dist/plugins/approval-ask-store-file.js +74 -18
  463. package/dist/plugins/approval-ask-store-memory.d.ts +17 -7
  464. package/dist/plugins/approval-ask-store-memory.js +35 -12
  465. package/dist/plugins/approval-ask-store-sql.d.ts +42 -20
  466. package/dist/plugins/approval-ask-store-sql.js +44 -11
  467. package/dist/plugins/approval-exemption-skill-m46.d.ts +20 -0
  468. package/dist/plugins/approval-exemption-skill-m46.js +25 -0
  469. package/dist/plugins/approval-exemption-store.d.ts +6 -1
  470. package/dist/plugins/approval-exemption-store.js +14 -0
  471. package/dist/plugins/approval-nonce-store.d.ts +39 -12
  472. package/dist/plugins/approval-nonce-store.js +72 -23
  473. package/dist/plugins/blob-backend.d.ts +40 -195
  474. package/dist/plugins/blob-backend.js +41 -234
  475. package/dist/plugins/caching-session-store.d.ts +22 -5
  476. package/dist/plugins/caching-session-store.js +43 -4
  477. package/dist/plugins/checkpoint-index-columns.d.ts +33 -6
  478. package/dist/plugins/checkpoint-index-columns.js +39 -3
  479. package/dist/plugins/checkpoint-store-face.d.ts +84 -11
  480. package/dist/plugins/checkpoint-store-face.js +18 -1
  481. package/dist/plugins/device-store-sql.d.ts +2 -2
  482. package/dist/plugins/device-store-sql.js +6 -10
  483. package/dist/plugins/ensure-index.d.ts +13 -1
  484. package/dist/plugins/ensure-index.js +154 -21
  485. package/dist/plugins/ensure-schema-lock-policy.d.ts +0 -15
  486. package/dist/plugins/ensure-schema-lock-policy.js +3 -2
  487. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  488. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  489. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  490. package/dist/plugins/execution-placement-sql.js +94 -54
  491. package/dist/plugins/file-run-store.d.ts +89 -35
  492. package/dist/plugins/file-run-store.js +197 -142
  493. package/dist/plugins/host-platform.d.ts +24 -3
  494. package/dist/plugins/host-platform.js +15 -1
  495. package/dist/plugins/image-bake-store-sql.js +2 -0
  496. package/dist/plugins/imported-layer-root-retirement.d.ts +19 -10
  497. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  498. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  499. package/dist/plugins/inbox-payload-column.js +13 -0
  500. package/dist/plugins/k8s-pod-reaper.d.ts +1 -3
  501. package/dist/plugins/k8s-pod-reaper.js +0 -1
  502. package/dist/plugins/leader-run-store-sql.d.ts +22 -0
  503. package/dist/plugins/leader-run-store-sql.js +34 -4
  504. package/dist/plugins/local-checkpoint-store.d.ts +63 -23
  505. package/dist/plugins/local-checkpoint-store.js +204 -81
  506. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  507. package/dist/plugins/local-data-dir-modes-m39.js +183 -0
  508. package/dist/plugins/local-docker-capacity.d.ts +93 -0
  509. package/dist/plugins/local-docker-capacity.js +194 -0
  510. package/dist/plugins/local-docker-reaper.d.ts +92 -0
  511. package/dist/plugins/local-docker-reaper.js +102 -0
  512. package/dist/plugins/local-engine-high-water.d.ts +13 -0
  513. package/dist/plugins/local-engine-high-water.js +62 -0
  514. package/dist/plugins/local-session-store.d.ts +442 -143
  515. package/dist/plugins/local-session-store.js +941 -289
  516. package/dist/plugins/local-session-window.d.ts +3 -2
  517. package/dist/plugins/local-session-window.js +3 -2
  518. package/dist/plugins/local-sidecar-quarantine.js +3 -2
  519. package/dist/plugins/local-task-attachment-store.d.ts +20 -1
  520. package/dist/plugins/local-task-attachment-store.js +32 -12
  521. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  522. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  523. package/dist/plugins/memory-embedder.d.ts +1 -1
  524. package/dist/plugins/memory-embedder.js +11 -9
  525. package/dist/plugins/memory-engine-pg.d.ts +7 -5
  526. package/dist/plugins/memory-engine-pg.js +18 -9
  527. package/dist/plugins/memory-engine-sql-apply.d.ts +21 -8
  528. package/dist/plugins/memory-engine-sql-apply.js +7 -9
  529. package/dist/plugins/memory-engine-tidb.d.ts +12 -7
  530. package/dist/plugins/memory-engine-tidb.js +18 -10
  531. package/dist/plugins/memory-optout-grant-store-sql.d.ts +1 -1
  532. package/dist/plugins/memory-optout-grant-store-sql.js +3 -3
  533. package/dist/plugins/memory-run-store.d.ts +39 -27
  534. package/dist/plugins/memory-run-store.js +72 -70
  535. package/dist/plugins/one-time-migration-marker.d.ts +10 -0
  536. package/dist/plugins/one-time-migration-marker.js +7 -0
  537. package/dist/plugins/one-time-migrations.d.ts +325 -74
  538. package/dist/plugins/one-time-migrations.js +705 -230
  539. package/dist/plugins/org-delivery-quota-store.js +2 -1
  540. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  541. package/dist/plugins/owner-only-file-mode.js +141 -0
  542. package/dist/plugins/permission-rule-store-file.d.ts +11 -11
  543. package/dist/plugins/permission-rule-store-file.js +14 -2
  544. package/dist/plugins/permission-rule-store-sql.d.ts +4 -63
  545. package/dist/plugins/permission-rule-store-sql.js +6 -86
  546. package/dist/plugins/pg-pool.d.ts +3 -32
  547. package/dist/plugins/pg-pool.js +27 -11
  548. package/dist/plugins/release-version.d.ts +20 -0
  549. package/dist/plugins/release-version.js +28 -0
  550. package/dist/plugins/remote-env-adb.d.ts +20 -4
  551. package/dist/plugins/remote-env-adb.js +39 -11
  552. package/dist/plugins/remote-env-e2b.d.ts +19 -28
  553. package/dist/plugins/remote-env-e2b.js +40 -29
  554. package/dist/plugins/remote-env-host.d.ts +11 -10
  555. package/dist/plugins/remote-env-host.js +40 -27
  556. package/dist/plugins/remote-env-k8s.d.ts +45 -47
  557. package/dist/plugins/remote-env-k8s.js +122 -62
  558. package/dist/plugins/remote-env-local-docker.d.ts +152 -15
  559. package/dist/plugins/remote-env-local-docker.js +370 -75
  560. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  561. package/dist/plugins/remote-env-ssh.js +26 -24
  562. package/dist/plugins/remote-scratchpad.js +4 -0
  563. package/dist/plugins/remote-shell.d.ts +59 -30
  564. package/dist/plugins/remote-shell.js +175 -62
  565. package/dist/plugins/retention-declaration.d.ts +37 -0
  566. package/dist/plugins/retention-declaration.js +9 -0
  567. package/dist/plugins/retention-deployment.d.ts +158 -0
  568. package/dist/plugins/retention-deployment.js +119 -0
  569. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  570. package/dist/plugins/retention-lane-store-sql.js +103 -1
  571. package/dist/plugins/retention-sql.d.ts +29 -0
  572. package/dist/plugins/retention-sql.js +8 -0
  573. package/dist/plugins/run-preview-m45.d.ts +11 -0
  574. package/dist/plugins/run-preview-m45.js +19 -0
  575. package/dist/plugins/run-store-sql.d.ts +56 -68
  576. package/dist/plugins/run-store-sql.js +127 -116
  577. package/dist/plugins/s3-answer.d.ts +2 -1
  578. package/dist/plugins/sandbox-reaper.d.ts +26 -0
  579. package/dist/plugins/sandbox-reaper.js +2 -0
  580. package/dist/plugins/scenario-prefix-switch-m42.d.ts +54 -0
  581. package/dist/plugins/scenario-prefix-switch-m42.js +55 -0
  582. package/dist/plugins/scheduler-support.d.ts +16 -1
  583. package/dist/plugins/scheduler-support.js +22 -2
  584. package/dist/plugins/schema-consistency.d.ts +42 -15
  585. package/dist/plugins/schema-consistency.js +213 -13
  586. package/dist/plugins/schema-drift-sentence.d.ts +4 -1
  587. package/dist/plugins/schema-drift-sentence.js +4 -4
  588. package/dist/plugins/schema-ensure-legs.d.ts +10 -1
  589. package/dist/plugins/schema-ensure-legs.js +12 -2
  590. package/dist/plugins/schema-indexes.js +3 -0
  591. package/dist/plugins/schema-live-columns.d.ts +14 -5
  592. package/dist/plugins/schema-live-columns.js +10 -10
  593. package/dist/plugins/send-user-file.js +2 -1
  594. package/dist/plugins/session-birth.d.ts +37 -0
  595. package/dist/plugins/session-birth.js +38 -0
  596. package/dist/plugins/session-epoch-anchor.d.ts +40 -12
  597. package/dist/plugins/session-epoch-anchor.js +33 -11
  598. package/dist/plugins/session-ext-table.d.ts +98 -0
  599. package/dist/plugins/session-ext-table.js +134 -0
  600. package/dist/plugins/session-fork.d.ts +16 -2
  601. package/dist/plugins/session-fork.js +3 -0
  602. package/dist/plugins/session-ownership.d.ts +1 -1
  603. package/dist/plugins/session-placement.d.ts +22 -16
  604. package/dist/plugins/session-placement.js +15 -12
  605. package/dist/plugins/session-row-sql.d.ts +20 -7
  606. package/dist/plugins/session-row-sql.js +16 -8
  607. package/dist/plugins/session-run-rollup.d.ts +29 -0
  608. package/dist/plugins/session-run-rollup.js +40 -0
  609. package/dist/plugins/session-store-sql.d.ts +157 -0
  610. package/dist/plugins/session-store-sql.js +398 -0
  611. package/dist/plugins/session-withdraw-sql.d.ts +36 -0
  612. package/dist/plugins/session-withdraw-sql.js +42 -0
  613. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  614. package/dist/plugins/session-workspace-cell.js +2 -2
  615. package/dist/plugins/shared-memory-store-sql.d.ts +10 -69
  616. package/dist/plugins/shared-memory-store-sql.js +15 -14
  617. package/dist/plugins/shell-sentinel-owner-m48.d.ts +20 -0
  618. package/dist/plugins/shell-sentinel-owner-m48.js +3 -0
  619. package/dist/plugins/snapshot-bucket-presence.d.ts +2 -1
  620. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  621. package/dist/plugins/sql-driver.d.ts +24 -8
  622. package/dist/plugins/sql-driver.js +43 -9
  623. package/dist/plugins/sql-errors.d.ts +35 -5
  624. package/dist/plugins/sql-errors.js +55 -3
  625. package/dist/plugins/sql-json-column.d.ts +8 -7
  626. package/dist/plugins/sql-json-column.js +10 -6
  627. package/dist/plugins/store-backend.d.ts +114 -18
  628. package/dist/plugins/store-backend.js +340 -87
  629. package/dist/plugins/store-contracts.d.ts +97 -30
  630. package/dist/plugins/store-contracts.js +23 -1
  631. package/dist/plugins/store-fault.d.ts +28 -2
  632. package/dist/plugins/store-fault.js +20 -2
  633. package/dist/plugins/task-attachment-store.d.ts +21 -11
  634. package/dist/plugins/task-attachment-store.js +37 -19
  635. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  636. package/dist/plugins/text-column-capacity.d.ts +21 -0
  637. package/dist/plugins/text-column-capacity.js +5 -0
  638. package/dist/plugins/tidb-pool.d.ts +3 -2
  639. package/dist/plugins/tidb-pool.js +42 -39
  640. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  641. package/dist/plugins/web-search.js +5 -3
  642. package/dist/plugins/workflow-run-store-sql.d.ts +6 -1
  643. package/dist/plugins/workflow-run-store-sql.js +11 -5
  644. package/dist/principal-jwt.d.ts +4 -4
  645. package/dist/principal-jwt.js +20 -5
  646. package/dist/principal-shape.d.ts +41 -0
  647. package/dist/principal-shape.js +19 -0
  648. package/dist/question.d.ts +13 -23
  649. package/dist/question.js +21 -39
  650. package/dist/read-face-posture.d.ts +6 -6
  651. package/dist/read-face-posture.js +2 -2
  652. package/dist/request-declaration-refusal.d.ts +35 -12
  653. package/dist/request-declaration-refusal.js +21 -9
  654. package/dist/request-form.d.ts +58 -0
  655. package/dist/request-form.js +72 -0
  656. package/dist/request-image-urls.d.ts +8 -2
  657. package/dist/request-image-urls.js +2 -1
  658. package/dist/request-images.d.ts +20 -0
  659. package/dist/request-images.js +59 -0
  660. package/dist/request-key-closure.d.ts +6 -7
  661. package/dist/request-key-closure.js +15 -2
  662. package/dist/request-value-closure.d.ts +5 -5
  663. package/dist/request-value-closure.js +1 -1
  664. package/dist/resource-suspend.d.ts +7 -16
  665. package/dist/resource-suspend.js +1 -1
  666. package/dist/retired-name-host-notice.d.ts +21 -0
  667. package/dist/retired-name-host-notice.js +12 -0
  668. package/dist/rule-entry-list.d.ts +2 -2
  669. package/dist/rule-entry-list.js +3 -4
  670. package/dist/rule-issue-reading.d.ts +58 -0
  671. package/dist/rule-issue-reading.js +14 -0
  672. package/dist/rules-consent.d.ts +46 -16
  673. package/dist/rules-consent.js +102 -53
  674. package/dist/run-acceptance.d.ts +3 -1
  675. package/dist/run-acceptance.js +3 -0
  676. package/dist/run-local.d.ts +14 -6
  677. package/dist/run-local.js +109 -93
  678. package/dist/run-stale-default.d.ts +7 -0
  679. package/dist/run-stale-default.js +2 -0
  680. package/dist/runs.d.ts +73 -44
  681. package/dist/runs.js +65 -43
  682. package/dist/runtime-caps-resolver.d.ts +36 -14
  683. package/dist/runtime-caps-resolver.js +31 -14
  684. package/dist/runtime-governance.d.ts +2 -2
  685. package/dist/runtime-governance.js +5 -3
  686. package/dist/sealed-key-layout.d.ts +12 -6
  687. package/dist/sealed-key-layout.js +5 -1
  688. package/dist/sealed-key.d.ts +12 -2
  689. package/dist/sealed-key.js +116 -26
  690. package/dist/security.d.ts +133 -161
  691. package/dist/security.js +163 -59
  692. package/dist/sema-config-dir.d.ts +20 -0
  693. package/dist/sema-config-dir.js +27 -2
  694. package/dist/server-secret-env.d.ts +23 -5
  695. package/dist/server-secret-env.js +14 -4
  696. package/dist/service-version.d.ts +2 -1
  697. package/dist/service-version.js +2 -2
  698. package/dist/session-children-retire.d.ts +7 -5
  699. package/dist/session-children-retire.js +7 -2
  700. package/dist/session-deletion.d.ts +88 -0
  701. package/dist/session-deletion.js +8 -0
  702. package/dist/session-erase.d.ts +145 -0
  703. package/dist/session-erase.js +115 -0
  704. package/dist/session-error-wire.d.ts +6 -6
  705. package/dist/session-error-wire.js +1 -1
  706. package/dist/session-incarnation.d.ts +60 -10
  707. package/dist/session-incarnation.js +19 -2
  708. package/dist/session-policy-wire.d.ts +6 -3
  709. package/dist/session-policy-wire.js +11 -1
  710. package/dist/session-resident-background.d.ts +29 -47
  711. package/dist/session-resident-background.js +77 -8
  712. package/dist/session-row.d.ts +3 -2
  713. package/dist/session-rules-seat.d.ts +12 -0
  714. package/dist/session-rules-seat.js +19 -0
  715. package/dist/session-sync-kernel.d.ts +1 -1
  716. package/dist/session-titler.d.ts +7 -15
  717. package/dist/session-titler.js +1 -1
  718. package/dist/session-workspace.d.ts +11 -2
  719. package/dist/session-workspace.js +5 -0
  720. package/dist/sighup-idle.d.ts +1 -1
  721. package/dist/spec-fields.d.ts +57 -3
  722. package/dist/spec-fields.js +72 -51
  723. package/dist/store-posture.d.ts +138 -21
  724. package/dist/store-posture.js +188 -3
  725. package/dist/stored-request-hooks-m20.d.ts +3 -2
  726. package/dist/stored-request-hooks-m20.js +21 -16
  727. package/dist/stores-sql/background-agent-store-sql.d.ts +11 -2
  728. package/dist/stores-sql/background-agent-store-sql.js +73 -20
  729. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  730. package/dist/stores-sql/blob-index-sql.js +154 -0
  731. package/dist/stores-sql/checkpoint-store-sql.d.ts +34 -29
  732. package/dist/stores-sql/checkpoint-store-sql.js +86 -71
  733. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  734. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  735. package/dist/stores-sql/mailbox-store-sql.d.ts +7 -4
  736. package/dist/stores-sql/mailbox-store-sql.js +31 -32
  737. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +30 -81
  738. package/dist/{plugins → stores-sql}/pg-session-storage.js +123 -244
  739. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +197 -223
  740. package/dist/{plugins → stores-sql}/retention-store-sql.js +148 -207
  741. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  742. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  743. package/dist/stores-sql/session-policy-store-sql.d.ts +33 -11
  744. package/dist/stores-sql/session-policy-store-sql.js +44 -36
  745. package/dist/stores-sql/session-write-hooks.d.ts +65 -0
  746. package/dist/stores-sql/session-write-hooks.js +2 -0
  747. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +14 -15
  748. package/dist/{plugins → stores-sql}/tidb-session-storage.js +22 -28
  749. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +67 -138
  750. package/dist/{plugins → stores-sql}/tidb-session-store.js +130 -200
  751. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  752. package/dist/stores-sql/tool-result-store-sql.js +1 -1
  753. package/dist/stores-sql/windowed-session-storage.d.ts +61 -0
  754. package/dist/stores-sql/windowed-session-storage.js +70 -0
  755. package/dist/submission-exit.d.ts +50 -0
  756. package/dist/submission-exit.js +108 -0
  757. package/dist/task-a2a.d.ts +7 -1
  758. package/dist/task-a2a.js +18 -17
  759. package/dist/task-cwd.d.ts +31 -3
  760. package/dist/task-cwd.js +25 -1
  761. package/dist/task-mcp.d.ts +11 -3
  762. package/dist/task-mcp.js +16 -19
  763. package/dist/task-settings.d.ts +23 -14
  764. package/dist/task-settings.js +35 -13
  765. package/dist/terminal.d.ts +25 -43
  766. package/dist/terminal.js +31 -11
  767. package/dist/tool-approval.d.ts +152 -137
  768. package/dist/tool-approval.js +199 -199
  769. package/dist/trace/core-keyset-guard.d.ts +22 -9
  770. package/dist/trace/core-vocab-wire.d.ts +95 -0
  771. package/dist/trace/core-vocab-wire.js +15 -0
  772. package/dist/trace/engine-notice-wire.d.ts +1 -1
  773. package/dist/trace/engine-notice-wire.js +2 -0
  774. package/dist/trace/exit-seat.d.ts +52 -4
  775. package/dist/trace/exit-seat.js +43 -36
  776. package/dist/trace/extension-call-face.d.ts +3 -2
  777. package/dist/trace/extension-call-face.js +3 -1
  778. package/dist/trace/ledger-events.d.ts +4 -7
  779. package/dist/trace/ledger-sink.d.ts +1 -30
  780. package/dist/trace/ledger-sink.js +0 -11
  781. package/dist/trace/model-segment.d.ts +5 -5
  782. package/dist/trace/model-segment.js +5 -5
  783. package/dist/trace/project.d.ts +88 -58
  784. package/dist/trace/project.js +64 -28
  785. package/dist/trace/redact.d.ts +124 -28
  786. package/dist/trace/redact.js +936 -328
  787. package/dist/trace/self-minted-wire.d.ts +750 -29
  788. package/dist/trace/self-minted-wire.js +262 -25
  789. package/dist/trace/sema-provenance.d.ts +45 -41
  790. package/dist/trace/sema-provenance.js +81 -10
  791. package/dist/trace/task-notification-facets.d.ts +70 -2
  792. package/dist/trace/task-notification-facets.js +71 -1
  793. package/dist/trace/wire-projection-faces.d.ts +43 -8
  794. package/dist/trace/wire-projection-faces.js +28 -3
  795. package/dist/unread-request-members.d.ts +66 -0
  796. package/dist/unread-request-members.js +41 -0
  797. package/dist/upstream-body.d.ts +23 -0
  798. package/dist/upstream-body.js +16 -0
  799. package/dist/url-refusal.d.ts +3 -3
  800. package/dist/url-refusal.js +11 -4
  801. package/dist/user-authored-text.d.ts +1 -1
  802. package/dist/user-input-ledger.d.ts +7 -8
  803. package/dist/user-input-ledger.js +1 -1
  804. package/dist/web-admin-client/apply-effective.d.ts +6 -3
  805. package/dist/web-admin-client/apply-effective.js +33 -40
  806. package/dist/web-admin-client/apply-ledger.d.ts +16 -7
  807. package/dist/web-admin-client/apply-ledger.js +5 -2
  808. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  809. package/dist/web-admin-client/candidate-gate.js +53 -5
  810. package/dist/web-admin-client/center-request.d.ts +6 -3
  811. package/dist/web-admin-client/center-request.js +17 -16
  812. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  813. package/dist/web-admin-client/effective-keys.js +1 -2
  814. package/dist/web-admin-client/entry-metadata.d.ts +8 -0
  815. package/dist/web-admin-client/entry-metadata.js +13 -0
  816. package/dist/web-admin-client/facade.d.ts +1 -1
  817. package/dist/web-admin-client/http-client.d.ts +2 -1
  818. package/dist/web-admin-client/http-client.js +40 -13
  819. package/dist/web-admin-client/mcp-revocation.d.ts +1 -1
  820. package/dist/web-admin-client/mcp-revocation.js +2 -1
  821. package/dist/web-admin-client/read-warnings.d.ts +1 -0
  822. package/dist/web-admin-client/read-warnings.js +1 -0
  823. package/dist/web-admin-client/restart-signal.js +13 -6
  824. package/dist/web-admin-client/skills-mcp.d.ts +22 -8
  825. package/dist/web-admin-client/skills-mcp.js +26 -21
  826. package/dist/web-admin-client/types.d.ts +35 -51
  827. package/dist/workspace-space.d.ts +3 -2
  828. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +4 -4
  829. package/package.json +16 -12
  830. package/scripts/drain-check-local.mjs +118 -173
  831. package/scripts/drain-check.mysql.sql +20 -16
  832. package/scripts/drain-check.pg.sql +20 -16
  833. package/wire-changes/server@7.147.0.json +813 -0
  834. package/wire-changes/server@7.148.0.json +1517 -0
  835. package/dist/http/memory-control-plane-corrupt-reply.d.ts +0 -25
  836. package/dist/http/memory-control-plane-corrupt-reply.js +0 -19
  837. package/dist/http/memory-layer-reply.d.ts +0 -30
  838. package/dist/http/memory-layer-reply.js +0 -24
  839. package/dist/lsp/e2b-bridge.d.ts +0 -57
  840. package/dist/lsp/e2b-bridge.js +0 -139
  841. package/dist/lsp/e2b-manager.d.ts +0 -93
  842. package/dist/lsp/e2b-manager.js +0 -178
  843. package/dist/lsp/lsp-frames.d.ts +0 -14
  844. package/dist/lsp/lsp-frames.js +0 -25
  845. package/dist/lsp/manager.d.ts +0 -129
  846. package/dist/lsp/manager.js +0 -127
  847. package/dist/lsp/types.d.ts +0 -12
  848. package/dist/lsp/types.js +0 -2
  849. package/dist/lsp/ws-transport.d.ts +0 -40
  850. package/dist/lsp/ws-transport.js +0 -210
@@ -1,5 +1,5 @@
1
1
  import { REDACTION_MARK, displayUrlOf, stripFormatCharacters } from "@sema-agent/core";
2
- import { CONTROL_AND_BIDI_CHARS, CREDENTIAL_PARAM_NAMES, isHierarchicalUrl } from "@sema-agent/settings-schema";
2
+ import { CONTROL_AND_BIDI_CHARS, CREDENTIAL_PARAM_NAMES, credentialParamWordOf, isHierarchicalUrl } from "@sema-agent/settings-schema";
3
3
  import { recordFailOpen } from "../observability/fail-open.js";
4
4
  const normalizeForRedaction = stripFormatCharacters;
5
5
  export function mapNormalizedToOriginal(text, normalized) {
@@ -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;
@@ -1070,14 +1339,9 @@ const CREDENTIAL_NAME_ANCHORS = {
1070
1339
  "x-amz-signature": "declarative", "x-amz-security-token": "declarative", "x-amz-credential": "declarative",
1071
1340
  sas: "structure-only",
1072
1341
  };
1073
- const CREDENTIAL_NAME_ALT = (() => {
1074
- for (const w of CREDENTIAL_PARAM_NAMES) {
1075
- if (!/^[a-z][a-z0-9_-]*$/.test(w))
1076
- throw new Error(`redact: credential name ${JSON.stringify(w)} must be lowercase [a-z0-9_-] (the alternation is built without escaping)`);
1077
- }
1078
- return [...CREDENTIAL_PARAM_NAMES].sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).join("|");
1079
- })();
1080
1342
  const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_PARAM_NAMES.map((w) => w.length));
1343
+ const JSON_FIELD_NAME_CLASS = "[A-Za-z0-9_.-]";
1344
+ const isJsonFieldNameCode = (c) => isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS || c === CH_DOT;
1081
1345
  const JSON_FIELD_QUOTE_FORMS = ['"', '\\"', '\\\\\\"'];
1082
1346
  for (let d = 0; d < JSON_FIELD_QUOTE_FORMS.length; d++) {
1083
1347
  if (JSON_FIELD_QUOTE_FORMS[d] !== "\\".repeat(2 ** d - 1) + '"')
@@ -1096,15 +1360,26 @@ function buildJsonSecretFieldSource() {
1096
1360
  q = b + q;
1097
1361
  b = b + b;
1098
1362
  }
1099
- alts.push(String.raw `(${q}(?:${CREDENTIAL_NAME_ALT})${q}\s*:\s*)${q}(?:${o}|${l}|${b}(?:${o}|${b}|${q}))*${q}`);
1363
+ alts.push(String.raw `(${q}(${JSON_FIELD_NAME_CLASS}+)${q}\s*:\s*)${q}(?:${o}|${l}|${b}(?:${o}|${b}|${q}))*${q}`);
1100
1364
  }
1101
1365
  return alts.join("|");
1102
1366
  }
1103
1367
  const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
1104
1368
  function credentialNameAnchor(name) {
1105
- return CREDENTIAL_ANCHOR_OF.get(asciiLower(name));
1369
+ let word = CREDENTIAL_WORD_MEMO.get(name);
1370
+ if (word === undefined) {
1371
+ word = credentialParamWordOf(name) ?? null;
1372
+ if (name.length <= CREDENTIAL_WORD_MEMO_NAME_MAX) {
1373
+ if (CREDENTIAL_WORD_MEMO.size >= CREDENTIAL_WORD_MEMO_SIZE)
1374
+ CREDENTIAL_WORD_MEMO.clear();
1375
+ CREDENTIAL_WORD_MEMO.set(name, word);
1376
+ }
1377
+ }
1378
+ return word === null ? undefined : CREDENTIAL_NAME_ANCHORS[word];
1106
1379
  }
1107
- const CREDENTIAL_ANCHOR_OF = new Map(Object.entries(CREDENTIAL_NAME_ANCHORS));
1380
+ const CREDENTIAL_WORD_MEMO = new Map();
1381
+ const CREDENTIAL_WORD_MEMO_SIZE = 1024;
1382
+ const CREDENTIAL_WORD_MEMO_NAME_MAX = 64;
1108
1383
  const BARE_WORD_READINGS = {
1109
1384
  null: "absent-or-boolean", nil: "absent-or-boolean", none: "absent-or-boolean", undefined: "absent-or-boolean",
1110
1385
  true: "absent-or-boolean", false: "absent-or-boolean", yes: "absent-or-boolean", no: "absent-or-boolean",
@@ -1157,7 +1432,7 @@ const SHAPE_SECRET_PATTERNS = [
1157
1432
  [new RegExp(String.raw `xox[baprs]-${atLeast("[A-Za-z0-9-]", 10)}`, "g"), "«redacted:slack-token»"],
1158
1433
  [new RegExp(String.raw `AKIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
1159
1434
  [new RegExp(String.raw `ASIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
1160
- [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»"],
1161
1436
  [new RegExp(String.raw `xapp-\d-${atLeast("[A-Za-z0-9-]", 20)}`, "g"), "«redacted:slack-token»"],
1162
1437
  [valueArm("sk-", "[A-Za-z0-9_-]", SK_FULL_ARM_MIN, "self-evident"), "«redacted:key»"],
1163
1438
  ];
@@ -1170,16 +1445,16 @@ const isIdentStartCode = (c) => (c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= C
1170
1445
  const isIdentCode = (c) => isIdentStartCode(c) || (c >= CH_0 && c <= CH_9) || c === CH_UNDERSCORE || c === CH_MINUS;
1171
1446
  function pemEscapeLetterAt(s, i, limit) {
1172
1447
  let j = i;
1173
- while (j < limit && s.charCodeAt(j) === CH_BACKSLASH)
1448
+ while (!segmentEnds(s, j, limit) && s.charCodeAt(j) === CH_BACKSLASH)
1174
1449
  j += 1;
1175
- if (j === i || j >= limit)
1450
+ if (j === i || segmentEnds(s, j, limit))
1176
1451
  return -1;
1177
1452
  const n = s.charCodeAt(j);
1178
1453
  return n === CH_r || n === CH_n || n === CH_t ? j : -1;
1179
1454
  }
1180
1455
  function skipPemSeps(scan, s, i, limit) {
1181
1456
  for (;;) {
1182
- if (i >= limit)
1457
+ if (segmentEnds(s, i, limit))
1183
1458
  return i;
1184
1459
  if (tokenEnd(scan, i) >= 0)
1185
1460
  return i;
@@ -1199,7 +1474,7 @@ function skipPemSeps(scan, s, i, limit) {
1199
1474
  }
1200
1475
  }
1201
1476
  function pemLineBreakLen(s, i, limit) {
1202
- if (i >= limit)
1477
+ if (segmentEnds(s, i, limit))
1203
1478
  return 0;
1204
1479
  const c = s.charCodeAt(i);
1205
1480
  if (c === CH_CR || c === CH_LF)
@@ -1212,7 +1487,7 @@ function pemLineBreakLen(s, i, limit) {
1212
1487
  return 0;
1213
1488
  }
1214
1489
  function pemInlineSepLen(s, i, limit) {
1215
- if (i >= limit)
1490
+ if (segmentEnds(s, i, limit))
1216
1491
  return 0;
1217
1492
  const c = s.charCodeAt(i);
1218
1493
  if (c === CH_SPACE || c === CH_TAB)
@@ -1225,16 +1500,20 @@ function pemInlineSepLen(s, i, limit) {
1225
1500
  return 0;
1226
1501
  }
1227
1502
  function probePemHeaderToken(scan, s, p, limit) {
1228
- 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)))
1229
1506
  return -1;
1230
1507
  p += 1;
1231
1508
  let identLen = 1;
1232
- 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))) {
1233
1510
  p += 1;
1234
1511
  identLen += 1;
1235
1512
  }
1236
1513
  for (;;) {
1237
- if (p >= limit || tokenEnd(scan, p) >= 0)
1514
+ if (segmentEnds(s, p, limit))
1515
+ return -1;
1516
+ if (tokenEnd(scan, p) >= 0)
1238
1517
  return -1;
1239
1518
  const c = s.charCodeAt(p);
1240
1519
  if (c === CH_SPACE || c === CH_TAB) {
@@ -1255,7 +1534,7 @@ function setPemHdr(scan, search, contentEnd, next, hitLimit, jsonEnd = false) {
1255
1534
  }
1256
1535
  function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1257
1536
  const start = skipPemSeps(scan, s, i, limit);
1258
- if (start >= limit)
1537
+ if (segmentEnds(s, start, limit))
1259
1538
  return false;
1260
1539
  const afterToken = probePemHeaderToken(scan, s, start, limit);
1261
1540
  if (afterToken < 0 && !allowBare)
@@ -1264,7 +1543,7 @@ function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1264
1543
  if (afterToken >= 0 && probePemHeaderToken(scan, s, p, limit) >= 0)
1265
1544
  return setPemHdr(scan, start, p, p, false);
1266
1545
  for (;;) {
1267
- if (p >= limit)
1546
+ if (segmentEnds(s, p, limit))
1268
1547
  return setPemHdr(scan, start, p, p, true);
1269
1548
  const unit = crossEnd(scan, p);
1270
1549
  if (unit >= 0) {
@@ -1286,9 +1565,9 @@ function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1286
1565
  return setPemHdr(scan, start, p, p + 1, false);
1287
1566
  if (c === CH_BACKSLASH) {
1288
1567
  let j = p;
1289
- while (j < limit && s.charCodeAt(j) === CH_BACKSLASH)
1568
+ while (!segmentEnds(s, j, limit) && s.charCodeAt(j) === CH_BACKSLASH)
1290
1569
  j += 1;
1291
- const n = j < limit ? s.charCodeAt(j) : -1;
1570
+ const n = segmentEnds(s, j, limit) ? -1 : s.charCodeAt(j);
1292
1571
  if (n !== CH_r && n !== CH_n && n !== CH_t) {
1293
1572
  p = json && (j - p) % 2 === 0 && n === CH_QUOTE ? j : Math.min(limit, j + 1);
1294
1573
  continue;
@@ -1310,7 +1589,7 @@ function parsePemHeaderLine(scan, s, i, limit, allowBare, json) {
1310
1589
  break;
1311
1590
  q += u;
1312
1591
  }
1313
- if (q >= limit)
1592
+ if (segmentEnds(s, q, limit))
1314
1593
  return setPemHdr(scan, start, p, q, true);
1315
1594
  if (probePemHeaderToken(scan, s, q, limit) >= 0)
1316
1595
  return setPemHdr(scan, start, p, q, false);
@@ -1323,7 +1602,7 @@ function parsePemBody(scan, s, i, limit) {
1323
1602
  for (;;) {
1324
1603
  const q = skipPemSeps(scan, s, p, limit);
1325
1604
  let r = q;
1326
- 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)))
1327
1606
  r += 1;
1328
1607
  if (r - q < PEM_BODY_RUN_MIN)
1329
1608
  break;
@@ -1433,13 +1712,24 @@ function findPemFence(scan, text, from) {
1433
1712
  }
1434
1713
  const SSH2_FENCE_OPEN = "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----";
1435
1714
  const SSH2_FENCE_CLOSE = "---- END SSH2 ENCRYPTED PRIVATE KEY ----";
1436
- 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;
1437
1727
  function armorCloseAt(scan, text, kind, bodyFrom, max) {
1438
- const near = firstDelim(scan, text, kind, bodyFrom, false);
1728
+ const near = firstDelim(scan, kind, bodyFrom, false);
1439
1729
  if (near < 0)
1440
1730
  return -1;
1441
- const free = firstDelim(scan, text, kind, bodyFrom, true);
1442
- 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);
1443
1733
  if (far > near && blockWithin(scan, text, kind, bodyFrom, far, max))
1444
1734
  return far;
1445
1735
  return blockWithin(scan, text, kind, bodyFrom, near, max) ? near : -1;
@@ -1453,8 +1743,8 @@ function blockWithin(scan, text, kind, bodyFrom, close, max) {
1453
1743
  }
1454
1744
  function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1455
1745
  const fam = familyCode("ssh2-armor");
1456
- const opens = delimPositions(scan, text, "ssh2-open");
1457
- const closes = delimPositions(scan, text, "ssh2-close");
1746
+ const opens = delimPositions(scan, "ssh2-open");
1747
+ const closes = delimPositions(scan, "ssh2-close");
1458
1748
  const blockEnds = [];
1459
1749
  let firstLive = 0;
1460
1750
  let freshFrom = -1;
@@ -1469,13 +1759,13 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1469
1759
  }
1470
1760
  if (rooted && prevEnd >= 0) {
1471
1761
  scan.self = readingKey(fam, c);
1472
- 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)
1473
1763
  firstLive += 1;
1474
1764
  for (let j = firstLive; j < blockEnds.length; j++) {
1475
1765
  const from = blockEnds[j];
1476
1766
  if (from > c)
1477
1767
  break;
1478
- 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)) {
1479
1769
  span(from, c + SSH2_FENCE_CLOSE.length, c, NOT_CLOSED);
1480
1770
  freshFrom = from;
1481
1771
  freshEnd = c + SSH2_FENCE_CLOSE.length;
@@ -1500,7 +1790,7 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1500
1790
  openAt = k < opens.length ? opens[k] : -1;
1501
1791
  return openAt;
1502
1792
  };
1503
- 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));
1504
1794
  for (let oi = 0; oi < opens.length;) {
1505
1795
  const fence = opens[oi];
1506
1796
  const bodyFrom = fence + SSH2_FENCE_OPEN.length;
@@ -1509,30 +1799,31 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1509
1799
  continue;
1510
1800
  }
1511
1801
  scan.self = readingKey(fam, fence);
1512
- if (isHead && measureWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
1513
- 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);
1514
1805
  return;
1515
1806
  }
1516
1807
  let resume = fence + 1;
1517
1808
  if (!isHead) {
1518
1809
  const open = nextFreeOpen(bodyFrom);
1519
- const limit = open < 0 ? text.length : open;
1810
+ const limit = open < 0 ? VIEW_END : open;
1520
1811
  const readEnd = (crossEndsRun) => {
1521
1812
  const t = truncatedEnd(bodyFrom, limit, crossEndsRun);
1522
1813
  const bodyEnd = t < 0 ? -1 : armorBodyEnd(scan, text, json, bodyFrom, limit, false, crossEndsRun);
1523
1814
  return bodyEnd < 0 ? -1 : Math.max(bodyEnd, t);
1524
1815
  };
1525
1816
  const far = readEnd(false);
1526
- 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;
1527
1818
  const end = near >= 0 && near < far ? near : far;
1528
- if (end >= 0 && measureWithin(scan, bodyFrom, end, SSH2_BODY_MAX)) {
1819
+ if (end >= 0 && measureWithin(scan, bodyFrom, end, ARMOR_BODY_MAX)) {
1529
1820
  span(fence, end, fence, NOT_CLOSED);
1530
1821
  resume = end;
1531
1822
  }
1532
1823
  }
1533
- const close = armorCloseAt(scan, text, "ssh2-close", bodyFrom, SSH2_BODY_MAX);
1824
+ const close = armorCloseAt(scan, text, "ssh2-close", bodyFrom, ARMOR_BODY_MAX);
1534
1825
  if (close < 0) {
1535
- if (isHead && firstDelim(scan, text, "ssh2-close", bodyFrom, false) < 0)
1826
+ if (isHead && firstDelim(scan, "ssh2-close", bodyFrom, false) < 0)
1536
1827
  return;
1537
1828
  oi = lowerBound(opens, resume);
1538
1829
  continue;
@@ -1543,16 +1834,16 @@ function collectSsh2FenceSpans(scan, text, json, span, isHead) {
1543
1834
  }
1544
1835
  }
1545
1836
  const PEM_END_OPEN = "-----END";
1546
- const PEM_FENCE_BODY_MAX = 8192;
1547
1837
  const PEM_LINE_INVALID = -1;
1548
1838
  const PEM_LINE_OPEN = -2;
1549
1839
  function pemFenceLineEnd(text, labelFrom) {
1550
1840
  const close = text.indexOf(PEM_FENCE_CLOSE, labelFrom);
1551
1841
  if (close < 0) {
1552
- let to = text.length;
1842
+ const n = text.readEnd();
1843
+ let to = n;
1553
1844
  while (to > labelFrom && text.charCodeAt(to - 1) === 45)
1554
1845
  to -= 1;
1555
- 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);
1556
1847
  return stillPossible ? PEM_LINE_OPEN : PEM_LINE_INVALID;
1557
1848
  }
1558
1849
  return isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : PEM_LINE_INVALID;
@@ -1563,23 +1854,24 @@ function collectPemFenceSpans(scan, text, span, isHead) {
1563
1854
  const fence = text.indexOf(PEM_FENCE_OPEN, from);
1564
1855
  if (fence < 0)
1565
1856
  return;
1857
+ text.attempt(fence);
1566
1858
  const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
1567
1859
  if (bodyFrom === PEM_LINE_INVALID) {
1568
1860
  from = fence + 1;
1569
1861
  continue;
1570
1862
  }
1571
- 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))) {
1572
1864
  from = fence + 1;
1573
1865
  continue;
1574
1866
  }
1575
1867
  scan.self = readingKey(fam, fence);
1576
- const near = bodyFrom === PEM_LINE_OPEN ? -1 : firstDelim(scan, text, "pem-end", bodyFrom, false);
1577
- 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)) {
1578
1870
  if (isHead)
1579
- span(fence, text.length, fence, NOT_CLOSED, true);
1871
+ span(fence, text.readEnd(), fence, NOT_CLOSED, true);
1580
1872
  return;
1581
1873
  }
1582
- const endAt = armorCloseAt(scan, text, "pem-end", bodyFrom, PEM_FENCE_BODY_MAX);
1874
+ const endAt = armorCloseAt(scan, text, "pem-end", bodyFrom, ARMOR_BODY_MAX);
1583
1875
  if (endAt < 0) {
1584
1876
  from = fence + 1;
1585
1877
  continue;
@@ -1591,8 +1883,8 @@ function collectPemFenceSpans(scan, text, span, isHead) {
1591
1883
  }
1592
1884
  function truncationWindowEnd(text, from, json) {
1593
1885
  if (!json)
1594
- return text.length;
1595
- for (let i = from; i < text.length; i++) {
1886
+ return text.readEnd();
1887
+ for (let i = from; !text.atEnd(i); i++) {
1596
1888
  const c = text.charCodeAt(i);
1597
1889
  if (c === CH_BACKSLASH) {
1598
1890
  i += 1;
@@ -1601,7 +1893,7 @@ function truncationWindowEnd(text, from, json) {
1601
1893
  if (c === CH_QUOTE)
1602
1894
  return i;
1603
1895
  }
1604
- return text.length;
1896
+ return text.readEnd();
1605
1897
  }
1606
1898
  function armorBodyEnd(scan, text, json, from, limit, allowBare, crossEndsRun) {
1607
1899
  let pos = from;
@@ -1643,13 +1935,19 @@ function collectPemFragmentSpans(scan, text, json, span, isHead) {
1643
1935
  const fam = familyCode("pem-armor");
1644
1936
  let cur = findPemFence(scan, text, 0);
1645
1937
  let inherited = false;
1646
- 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));
1647
1939
  while (cur !== undefined) {
1648
1940
  const next = findPemFence(scan, text, cur.after);
1649
- const limit = next === undefined ? text.length : next.fence;
1941
+ const limit = next === undefined ? VIEW_END : next.fence;
1650
1942
  scan.self = readingKey(fam, cur.fence);
1943
+ text.attempt(cur.fence);
1651
1944
  const bodyEnd = armorBodyEnd(scan, text, json, cur.after, limit, inherited, false);
1652
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
+ }
1653
1951
  const end = isHead || bodyEnd < 0 ? bodyEnd : Math.max(bodyEnd, truncatedEnd(cur.after, limit, false));
1654
1952
  if (end >= 0)
1655
1953
  span(cur.fence, end, cur.fence, NOT_CLOSED);
@@ -1658,22 +1956,21 @@ function collectPemFragmentSpans(scan, text, json, span, isHead) {
1658
1956
  }
1659
1957
  function collectHeadlessPpkSpans(scan, text, span, isHead) {
1660
1958
  const KEY = PPK_LINES_FIELD;
1661
- const n = text.length;
1662
1959
  const LINE_NONE = -1;
1663
1960
  const LINE_OPEN = -2;
1664
1961
  const openAt = (i) => {
1665
1962
  if (!isHead)
1666
1963
  return false;
1667
1964
  let k = i;
1668
- while (k < n && tokenEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
1965
+ while (!text.atEnd(k) && tokenEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
1669
1966
  k += 1;
1670
- return k >= n;
1967
+ return text.atEnd(k);
1671
1968
  };
1672
1969
  const isB64 = (c) => (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 43 || c === 47 || c === 61;
1673
1970
  const b64At = (i) => tokenEnd(scan, i) < 0 && isB64(text.charCodeAt(i));
1674
- 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))
1675
1972
  i += 1; return i; };
1676
- const lineEndHidden = (i) => i < n && delimClass(scan, i) === DELIM_HIDDEN;
1973
+ const lineEndHidden = (i) => !text.atEnd(i) && delimClass(scan, i) === DELIM_HIDDEN;
1677
1974
  const skipNewlines = (i) => {
1678
1975
  let q = skipHs(i);
1679
1976
  let seen = false;
@@ -1716,7 +2013,7 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1716
2013
  if (lineEndHidden(i))
1717
2014
  return 0;
1718
2015
  let k = i;
1719
- while (k < n && text.charCodeAt(k) === 92)
2016
+ while (!text.atEnd(k) && text.charCodeAt(k) === 92)
1720
2017
  k += 1;
1721
2018
  if (k === i)
1722
2019
  return 0;
@@ -1725,7 +2022,7 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1725
2022
  };
1726
2023
  const atLineEnd = (i) => {
1727
2024
  const b = skipHs(i);
1728
- if (b >= n || openAt(b))
2025
+ if (text.atEnd(b) || openAt(b))
1729
2026
  return true;
1730
2027
  if (tokenEnd(scan, b) >= 0 || lineEndHidden(b))
1731
2028
  return false;
@@ -1741,13 +2038,13 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1741
2038
  if (mk === 43 || mk === 45)
1742
2039
  q = skipHs(q + 1);
1743
2040
  let r = q;
1744
- while (r < n && b64At(r))
2041
+ while (!text.atEnd(r) && b64At(r))
1745
2042
  r += 1;
1746
2043
  if (r - q < 4) {
1747
- if (isHead && r >= n)
2044
+ if (isHead && text.atEnd(r))
1748
2045
  return LINE_OPEN;
1749
2046
  r = q0;
1750
- while (r < n && b64At(r))
2047
+ while (!text.atEnd(r) && b64At(r))
1751
2048
  r += 1;
1752
2049
  if (r - q0 < 4)
1753
2050
  return LINE_NONE;
@@ -1759,11 +2056,12 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1759
2056
  const at = text.indexOf(KEY, search);
1760
2057
  if (at === -1)
1761
2058
  break;
2059
+ text.attempt(at);
1762
2060
  let p = at + KEY.length;
1763
2061
  const d0 = p;
1764
- 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)
1765
2063
  p += 1;
1766
- if (p === d0 && p < n) {
2064
+ if (p === d0 && !text.atEnd(p)) {
1767
2065
  search = at + KEY.length;
1768
2066
  continue;
1769
2067
  }
@@ -1778,7 +2076,7 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1778
2076
  const r = bodyLineAt(from);
1779
2077
  if (r === LINE_NONE)
1780
2078
  break;
1781
- end = r === LINE_OPEN ? n : r;
2079
+ end = r === LINE_OPEN ? text.readEnd() : r;
1782
2080
  if (r === LINE_OPEN) {
1783
2081
  open = true;
1784
2082
  break;
@@ -1800,7 +2098,7 @@ const FRAGMENT_SECRET_PATTERNS = [
1800
2098
  [valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
1801
2099
  [valueArm("AKIA", "[0-9A-Z]", 6, "needs-left-boundary"), "«redacted:aws-key:partial»"],
1802
2100
  [valueArm("ASIA", "[0-9A-Z]", 8, "needs-left-boundary"), "«redacted:aws-key:partial»"],
1803
- [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»"],
1804
2102
  [valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
1805
2103
  ];
1806
2104
  const DISPLAY_SAFE_URL_TOKEN = "«redacted:url»";
@@ -1882,46 +2180,26 @@ const isRegexSpace = (c) => c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c =
1882
2180
  const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
1883
2181
  const isControlCode = (c) => c < 0x20 || c === 0x7f;
1884
2182
  const TOKEN_UNIT_Y = new RegExp(REDACTION_TOKEN_RE_SRC, "y");
1885
- const CREDENTIAL_NAME_SCAN_MAX = CREDENTIAL_NAME_MAX * 3 + 1;
1886
- function decodeAsciiName(name) {
1887
- let out = "";
1888
- for (let i = 0; i < name.length; i++) {
1889
- const c = name.charCodeAt(i);
1890
- if (c !== 37) {
1891
- out += name[i];
1892
- continue;
1893
- }
1894
- const hex = name.slice(i + 1, i + 3);
1895
- if (hex.length !== 2 || !/^[0-9A-Fa-f]{2}$/.test(hex))
1896
- return undefined;
1897
- const b = Number.parseInt(hex, 16);
1898
- if (b >= 0x80)
1899
- return undefined;
1900
- out += String.fromCharCode(b);
1901
- i += 2;
1902
- }
1903
- return out;
1904
- }
1905
- function credentialParamValueStart(text, p) {
1906
- const limit = Math.min(text.length, p + CREDENTIAL_NAME_SCAN_MAX);
1907
- let hasPct = false;
1908
- for (let i = p; i < limit; i++) {
2183
+ function credentialParamValueStart(text, p, json) {
2184
+ let encoded = false;
2185
+ for (let i = p; !text.atEnd(i); i++) {
1909
2186
  const c = text.charCodeAt(i);
1910
- if (c === 37) {
1911
- hasPct = true;
1912
- continue;
1913
- }
1914
2187
  if (c === CH_EQ) {
1915
2188
  if (i === p)
1916
2189
  return -1;
1917
- const name = hasPct ? decodeAsciiName(text.slice(p, i)) : text.slice(p, i);
1918
- return name !== undefined && credentialNameAnchor(name) !== undefined ? i + 1 : -1;
2190
+ const raw = text.slice(p, i);
2191
+ return credentialNameAnchor(encoded ? receiverParamName(raw) : raw) !== undefined ? i + 1 : -1;
1919
2192
  }
1920
- if (!(isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS))
2193
+ if (c === 63 || c === 38 || c === 35 || isRegexSpace(c) || isControlCode(c) || (json && c === 34))
1921
2194
  return -1;
2195
+ if (c === 37 || c === 43)
2196
+ encoded = true;
1922
2197
  }
1923
2198
  return -1;
1924
2199
  }
2200
+ function receiverParamName(raw) {
2201
+ return new URLSearchParams(`${raw}=`).keys().next().value ?? raw;
2202
+ }
1925
2203
  function tokenUnitEnd(text, p) {
1926
2204
  TOKEN_UNIT_Y.lastIndex = p;
1927
2205
  return TOKEN_UNIT_Y.test(text) ? TOKEN_UNIT_Y.lastIndex : -1;
@@ -2388,7 +2666,8 @@ function reachAt(ix, d, self) {
2388
2666
  return ix.dEnd2[d] < 0 ? -1 : ix.reach2[d];
2389
2667
  }
2390
2668
  const FOOT_NONE = 0x7fffffff;
2391
- function buildForensicScan(tokens, rows, armCount) {
2669
+ const NO_DELIM_POSITIONS = new Int32Array(0);
2670
+ function buildForensicScan(tokens, rows, armCount, positionsOf) {
2392
2671
  return {
2393
2672
  rows,
2394
2673
  tokens,
@@ -2399,6 +2678,7 @@ function buildForensicScan(tokens, rows, armCount) {
2399
2678
  footLo: FOOT_NONE,
2400
2679
  footHi: -1,
2401
2680
  armFoot: new Int32Array(2 * armCount),
2681
+ execs: [],
2402
2682
  lastAt: -1,
2403
2683
  headsRead: { a: NO_URL_SEGMENTS, n: 0 },
2404
2684
  headsWrite: { a: NO_URL_SEGMENTS, n: 0 },
@@ -2408,6 +2688,7 @@ function buildForensicScan(tokens, rows, armCount) {
2408
2688
  opsWrite: { a: NO_URL_SEGMENTS, n: 0, cut: OPS_UNCUT },
2409
2689
  hdr: { search: 0, contentEnd: 0, next: 0, hitLimit: false, jsonEnd: false },
2410
2690
  delimPos: new Map(),
2691
+ positionsOf,
2411
2692
  delimIx: new Map(),
2412
2693
  delimIxRows: rows,
2413
2694
  };
@@ -2418,6 +2699,11 @@ function noteEvidenceRead(scan, p) {
2418
2699
  if (p > scan.footHi)
2419
2700
  scan.footHi = p;
2420
2701
  }
2702
+ function noteExecution(scan, lo, hi, from) {
2703
+ if (lo > hi)
2704
+ return;
2705
+ scan.execs.push(lo, hi, from);
2706
+ }
2421
2707
  function footprintClean(dirty, lo, hi) {
2422
2708
  if (lo > hi)
2423
2709
  return true;
@@ -2674,16 +2960,21 @@ export function unitCountsWithinForTest(table, queries) {
2674
2960
  anchor: Int32Array.from(sorted, keyOf),
2675
2961
  closeAt: new Int32Array(sorted.length).fill(NOT_CLOSED),
2676
2962
  };
2677
- const scan = buildForensicScan(NO_TOKEN_UNITS, rows, 1);
2963
+ const scan = buildForensicScan(NO_TOKEN_UNITS, rows, 1, () => NO_DELIM_POSITIONS);
2678
2964
  return queries.map(([from, to, max, self]) => {
2679
2965
  scan.self = self === undefined ? NO_SELF : readingKey(familyCode("prose"), self);
2680
2966
  return measureWithin(scan, from, to, max);
2681
2967
  });
2682
2968
  }
2683
- function delimPositions(scan, text, kind) {
2969
+ function delimPositions(scan, kind) {
2684
2970
  const hit = scan.delimPos.get(kind);
2685
2971
  if (hit !== undefined)
2686
2972
  return hit;
2973
+ const pos = scan.positionsOf(kind);
2974
+ scan.delimPos.set(kind, pos);
2975
+ return pos;
2976
+ }
2977
+ function delimPositionsOf(text, kind) {
2687
2978
  const out = [];
2688
2979
  switch (kind) {
2689
2980
  case "ssh2-open":
@@ -2694,11 +2985,13 @@ function delimPositions(scan, text, kind) {
2694
2985
  ssh2ScanBytes += text.length;
2695
2986
  break;
2696
2987
  }
2697
- case "pem-end":
2988
+ case "pem-end": {
2989
+ const lines = new ScanReader(text);
2698
2990
  for (let b = text.indexOf(PEM_END_OPEN); b >= 0; b = text.indexOf(PEM_END_OPEN, b + 1))
2699
- if (pemFenceLineEnd(text, b + PEM_END_OPEN.length) >= 0)
2991
+ if (pemFenceLineEnd(lines, b + PEM_END_OPEN.length) >= 0)
2700
2992
  out.push(b);
2701
2993
  break;
2994
+ }
2702
2995
  case "at":
2703
2996
  case "at-json":
2704
2997
  for (let p = text.indexOf("@"); p >= 0; p = text.indexOf("@", p + 1)) {
@@ -2713,11 +3006,9 @@ function delimPositions(scan, text, kind) {
2713
3006
  }
2714
3007
  break;
2715
3008
  }
2716
- const pos = Int32Array.from(out);
2717
- scan.delimPos.set(kind, pos);
2718
- return pos;
3009
+ return Int32Array.from(out);
2719
3010
  }
2720
- function delimIndexOf(scan, text, kind) {
3011
+ function delimIndexOf(scan, kind) {
2721
3012
  const t = scan.rows;
2722
3013
  if (scan.delimIxRows !== t) {
2723
3014
  scan.delimIx = new Map();
@@ -2727,7 +3018,7 @@ function delimIndexOf(scan, text, kind) {
2727
3018
  const hit = byKind.get(kind);
2728
3019
  if (hit !== undefined)
2729
3020
  return hit;
2730
- const pos = delimPositions(scan, text, kind);
3021
+ const pos = delimPositions(scan, kind);
2731
3022
  const n = pos.length;
2732
3023
  const nextNotHidden = new Int32Array(n + 1);
2733
3024
  const nextFree = new Int32Array(n + 1);
@@ -2787,9 +3078,9 @@ function bucketFirstFrom(b, i, n) {
2787
3078
  const k = lowerBound(b, i);
2788
3079
  return k < b.length ? b[k] : n;
2789
3080
  }
2790
- function firstDelim(scan, text, kind, from, wantFree) {
3081
+ function firstDelim(scan, kind, from, wantFree) {
2791
3082
  noteEvidenceRead(scan, from);
2792
- const di = delimIndexOf(scan, text, kind);
3083
+ const di = delimIndexOf(scan, kind);
2793
3084
  const n = di.pos.length;
2794
3085
  const bucket = (wantFree ? di.onlyCoveredBy : di.onlyHiddenBy).get(scan.self);
2795
3086
  const firstAt = (i) => (i >= n ? n : Math.min(wantFree ? di.nextFree[i] : di.nextNotHidden[i], bucketFirstFrom(bucket, i, n)));
@@ -2803,11 +3094,11 @@ function firstDelim(scan, text, kind, from, wantFree) {
2803
3094
  j = firstAt(lowerBound(di.pos, own.maxClose[k]));
2804
3095
  }
2805
3096
  const at = j >= n ? -1 : di.pos[j];
2806
- noteEvidenceRead(scan, at < 0 ? text.length : at);
3097
+ noteEvidenceRead(scan, at < 0 ? VIEW_END : at);
2807
3098
  return at;
2808
3099
  }
2809
- function lastNotHiddenDelim(scan, text, kind, a, b) {
2810
- const di = delimIndexOf(scan, text, kind);
3100
+ function lastNotHiddenDelim(scan, kind, a, b) {
3101
+ const di = delimIndexOf(scan, kind);
2811
3102
  const bucket = di.onlyHiddenBy.get(scan.self);
2812
3103
  const lastAt = (j) => {
2813
3104
  let best = di.prevNotHidden[j];
@@ -2834,13 +3125,12 @@ function lastNotHiddenDelim(scan, text, kind, a, b) {
2834
3125
  return best >= 0 && di.pos[best] >= a ? di.pos[best] : -1;
2835
3126
  }
2836
3127
  function scanUrlRun(scan, text, i, kind, json) {
2837
- const n = text.length;
2838
3128
  noteEvidenceRead(scan, i);
2839
3129
  scan.lastAt = -1;
2840
- run: while (i < n) {
3130
+ run: while (!text.atEnd(i)) {
2841
3131
  const unit = crossEndAt(scan, i);
2842
3132
  if (unit >= 0) {
2843
- const at = lastNotHiddenDelim(scan, text, json ? "at-json" : "at", i, unit);
3133
+ const at = lastNotHiddenDelim(scan, json ? "at-json" : "at", i, unit);
2844
3134
  if (at > scan.lastAt)
2845
3135
  scan.lastAt = at;
2846
3136
  i = unit;
@@ -2848,7 +3138,7 @@ function scanUrlRun(scan, text, i, kind, json) {
2848
3138
  }
2849
3139
  const c = text.charCodeAt(i);
2850
3140
  if (json && c === 92) {
2851
- if (i + 1 < n && !isLineTerminator(text.charCodeAt(i + 1))) {
3141
+ if (!text.atEnd(i + 1) && !isLineTerminator(text.charCodeAt(i + 1))) {
2852
3142
  i += 2;
2853
3143
  continue;
2854
3144
  }
@@ -2885,7 +3175,7 @@ function matchUserinfoAt(scan, text, p, json, isHead) {
2885
3175
  const probeEnd = scanUrlRun(scan, text, p, "plain-user", json);
2886
3176
  const keyId = probeEnd > p && text.charCodeAt(probeEnd) === 58 && readableCover(scan, p, probeEnd);
2887
3177
  const runEnd = scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
2888
- if (isHead && runEnd >= text.length && runEnd > p)
3178
+ if (isHead && runEnd > p && text.atEnd(runEnd))
2889
3179
  return runEnd;
2890
3180
  const at = scan.lastAt;
2891
3181
  if (at <= p)
@@ -2893,6 +3183,7 @@ function matchUserinfoAt(scan, text, p, json, isHead) {
2893
3183
  return at;
2894
3184
  }
2895
3185
  const URL_SEG_STRIDE = 4;
3186
+ const URL_PARAM_DELIMS = /[?&#]/g;
2896
3187
  const URL_SEG_MAX_INTS = 3 * 1_048_576;
2897
3188
  const NO_URL_SEGMENTS = new Int32Array(0);
2898
3189
  const ANCHOR_SEG_STRIDE = 4;
@@ -2922,12 +3213,12 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2922
3213
  const phn = dirty === undefined ? 0 : hr.n;
2923
3214
  let pc = 0;
2924
3215
  for (let from = 0;;) {
2925
- head.lastIndex = from;
2926
- const m = head.exec(text);
3216
+ const m = text.search(head, from);
2927
3217
  if (m === null)
2928
3218
  break;
2929
3219
  const colon = m.index;
2930
3220
  const start = colon + 3;
3221
+ text.attempt(start);
2931
3222
  while (pc < phn && ph[pc] < colon)
2932
3223
  pc += URL_SEG_STRIDE;
2933
3224
  const known = pc < phn && ph[pc] === colon;
@@ -2948,8 +3239,9 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2948
3239
  lo = scan.footLo;
2949
3240
  hi = scan.footHi;
2950
3241
  urlSegmentRuns += 1;
3242
+ noteExecution(scan, lo, hi, start);
2951
3243
  if (at >= 0 && at !== (known ? ph[pc + 1] : -2)) {
2952
- const undecided = isHead && at >= text.length;
3244
+ const undecided = isHead && text.atEnd(at);
2953
3245
  span(start, at, colon, undecided ? NOT_CLOSED : at, undecided);
2954
3246
  }
2955
3247
  }
@@ -2971,13 +3263,15 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2971
3263
  const pp = pr.a;
2972
3264
  const ppn = dirty === undefined ? 0 : pr.n;
2973
3265
  let qc = 0;
2974
- for (let i = 0; i < text.length; i++) {
2975
- const c = text.charCodeAt(i);
2976
- if (c !== 63 && c !== 38 && c !== 35)
2977
- continue;
2978
- const valueStart = credentialParamValueStart(text, i + 1);
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);
3271
+ const valueStart = credentialParamValueStart(text, i + 1, json);
2979
3272
  if (valueStart < 0)
2980
3273
  continue;
3274
+ text.attempt(valueStart);
2981
3275
  while (qc < ppn && pp[qc] < i)
2982
3276
  qc += URL_SEG_STRIDE;
2983
3277
  const known = qc < ppn && pp[qc] === i;
@@ -2998,6 +3292,7 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
2998
3292
  lo = scan.footLo;
2999
3293
  hi = scan.footHi;
3000
3294
  urlSegmentRuns += 1;
3295
+ noteExecution(scan, lo, hi, valueStart);
3001
3296
  if (valueEnd > valueStart && valueEnd !== (known ? pp[qc + 1] : -2))
3002
3297
  span(valueStart, valueEnd, i, NOT_CLOSED);
3003
3298
  }
@@ -3014,7 +3309,7 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
3014
3309
  armLo = lo;
3015
3310
  if (hi > armHi)
3016
3311
  armHi = hi;
3017
- i = valueEnd - 1;
3312
+ resume = valueEnd;
3018
3313
  }
3019
3314
  scan.headsRead = hw;
3020
3315
  scan.headsWrite = hr;
@@ -3024,28 +3319,25 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
3024
3319
  scan.footHi = armHi;
3025
3320
  }
3026
3321
  const JSON_FIELD_ANCHOR = '"';
3027
- const JSON_FIELD_BACK = Math.max(...JSON_FIELD_QUOTE_FORMS.map((f) => f.length));
3322
+ const JSON_FIELD_HEAD_MAX = Math.max(...JSON_FIELD_QUOTE_FORMS.map((f) => f.length));
3028
3323
  function jsonSecretFieldValueQuoteAt(text, p, depth) {
3029
3324
  const form = JSON_FIELD_QUOTE_FORMS[depth];
3030
- const n = text.length;
3031
3325
  const nameFrom = p + form.length;
3032
3326
  let i = nameFrom;
3033
- for (; i < n; i++) {
3327
+ for (; !text.atEnd(i); i++) {
3034
3328
  const c = text.charCodeAt(i);
3035
- if (!(isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS))
3329
+ if (!isJsonFieldNameCode(c))
3036
3330
  break;
3037
- if (i - nameFrom >= CREDENTIAL_NAME_MAX)
3038
- return -1;
3039
3331
  }
3040
3332
  if (i === nameFrom || !text.startsWith(form, i) || credentialNameAnchor(text.slice(nameFrom, i)) === undefined)
3041
3333
  return -1;
3042
3334
  i += form.length;
3043
- while (i < n && isRegexSpace(text.charCodeAt(i)))
3335
+ while (!text.atEnd(i) && isRegexSpace(text.charCodeAt(i)))
3044
3336
  i += 1;
3045
3337
  if (text.charCodeAt(i) !== CH_COLON)
3046
3338
  return -1;
3047
3339
  i += 1;
3048
- while (i < n && isRegexSpace(text.charCodeAt(i)))
3340
+ while (!text.atEnd(i) && isRegexSpace(text.charCodeAt(i)))
3049
3341
  i += 1;
3050
3342
  return text.startsWith(form, i) ? i : -1;
3051
3343
  }
@@ -3060,7 +3352,7 @@ const packUnit8 = (kind, next) => next * 8 + kind;
3060
3352
  const unit8Kind = (u) => (u % 8);
3061
3353
  const unit8Next = (u) => (u - (u % 8)) / 8;
3062
3354
  function escapedUnitAt(text, i, depth) {
3063
- if (i >= text.length)
3355
+ if (text.atEnd(i))
3064
3356
  return packUnit8(U_END, i);
3065
3357
  if (depth === 0) {
3066
3358
  const c = text.charCodeAt(i);
@@ -3119,7 +3411,7 @@ function collectJsonSecretFieldSpans(text, span, isHead) {
3119
3411
  let from = 0;
3120
3412
  for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
3121
3413
  let run = 0;
3122
- while (run < JSON_FIELD_BACK - 1 && p - run - 1 >= from && text.charCodeAt(p - run - 1) === CH_BACKSLASH)
3414
+ while (run < JSON_FIELD_HEAD_MAX - 1 && p - run - 1 >= from && text.charCodeAt(p - run - 1) === CH_BACKSLASH)
3123
3415
  run += 1;
3124
3416
  let next = -1;
3125
3417
  for (let d = JSON_FIELD_QUOTE_FORMS.length - 1; d >= 0 && next < 0; d--) {
@@ -3127,14 +3419,15 @@ function collectJsonSecretFieldSpans(text, span, isHead) {
3127
3419
  const start = p - (form.length - 1);
3128
3420
  if (start < p - run)
3129
3421
  continue;
3422
+ text.attempt(start);
3130
3423
  const q = jsonSecretFieldValueQuoteAt(text, start, d);
3131
3424
  const found = q < 0 ? -1 : jsonStringEndAt(text, q + form.length, d, isHead);
3132
3425
  if (found === -1)
3133
3426
  continue;
3134
3427
  const open = found === STRING_OPEN;
3135
- const end = open ? text.length : found;
3428
+ const end = open ? text.readEnd() : found;
3136
3429
  span(q, end, q, open ? NOT_CLOSED : end - form.length, open, JSON_FIELD_VALUE_REPLS[d]);
3137
- from = scanResumeAfterHit(start, end, JSON_FIELD_BACK);
3430
+ from = scanResumeAfterHit(start, end, end - q);
3138
3431
  next = text.indexOf(JSON_FIELD_ANCHOR, from);
3139
3432
  if (next < 0)
3140
3433
  return;
@@ -3187,7 +3480,7 @@ function readTextUnit(text, i, json) {
3187
3480
  return packUnit(c, 1);
3188
3481
  if (c === CH_QUOTE)
3189
3482
  return packUnit(JSON_STRING_END, 1);
3190
- if (c !== CH_BACKSLASH || i + 1 >= text.length)
3483
+ if (c !== CH_BACKSLASH || text.atEnd(i + 1))
3191
3484
  return packUnit(c, 1);
3192
3485
  switch (text.charCodeAt(i + 1)) {
3193
3486
  case CH_n: return packUnit(CH_LF, 2);
@@ -3245,16 +3538,13 @@ function anchorHeadAt(scan, text, op, json) {
3245
3538
  i -= quoteLen;
3246
3539
  }
3247
3540
  const nameEnd = i + 1;
3248
- const floor = Math.max(-1, i - (CREDENTIAL_NAME_MAX + 2));
3249
3541
  let j = i;
3250
- while (j > floor && isIdentCode(text.charCodeAt(j)))
3542
+ while (j >= 0 && isIdentCode(text.charCodeAt(j)))
3251
3543
  j -= 1;
3252
3544
  anchoredScanUnits += nameEnd - (j + 1);
3253
3545
  const bound = j + 1 < nameEnd ? nameBoundAt(scan, Math.max(j, 0), nameEnd) : -1;
3254
3546
  if (bound > j)
3255
3547
  j = bound - 1;
3256
- else if (j === floor && j >= 0 && isIdentCode(text.charCodeAt(j)))
3257
- return undefined;
3258
3548
  let nameStart = j + 1;
3259
3549
  let afterEscape = false;
3260
3550
  if (j >= 0 && text.charCodeAt(j) === CH_BACKSLASH && escapedAt(text, j + 1)) {
@@ -3300,9 +3590,8 @@ function anchorHeadAt(scan, text, op, json) {
3300
3590
  return { strength, quotedName: quote !== 0, spacedLeft, listSep, runStart };
3301
3591
  }
3302
3592
  function quotedValueEnd(text, cs, quote, json, isHead) {
3303
- const n = text.length;
3304
3593
  let i = cs;
3305
- while (i < n) {
3594
+ while (!text.atEnd(i)) {
3306
3595
  const unit = readAnchorUnit(text, i, json);
3307
3596
  const ch = unitChOf(unit);
3308
3597
  if (ch === quote)
@@ -3311,7 +3600,7 @@ function quotedValueEnd(text, cs, quote, json, isHead) {
3311
3600
  return -1;
3312
3601
  i += unitLenOf(unit);
3313
3602
  if (ch === CH_BACKSLASH && quote !== JSON_STRING_END) {
3314
- if (i >= n)
3603
+ if (text.atEnd(i))
3315
3604
  break;
3316
3605
  const escaped = readAnchorUnit(text, i, json);
3317
3606
  if (unitChOf(escaped) === JSON_STRING_END)
@@ -3326,6 +3615,7 @@ function buildBareValueMemo() {
3326
3615
  return {
3327
3616
  from: new Int32Array(4),
3328
3617
  at: new Int32Array(4).fill(-1),
3618
+ end: new Uint8Array(4),
3329
3619
  netFrom: new Int32Array(4).fill(NO_MEMO_GROUP),
3330
3620
  net: new Int32Array(16),
3331
3621
  runFrom: new Int32Array(4).fill(NO_MEMO_GROUP),
@@ -3334,14 +3624,17 @@ function buildBareValueMemo() {
3334
3624
  }
3335
3625
  const listSepSlot = (listSep) => (listSep === 0 ? 0 : listSep === CH_AMP ? 1 : listSep === CH_SEMI ? 2 : 3);
3336
3626
  function bareValueEnd(text, s, json, listSep, memo) {
3337
- const n = text.length;
3338
3627
  const k = listSepSlot(listSep);
3339
3628
  let i;
3340
- if (memo.from[k] <= s && s <= memo.at[k])
3629
+ if (memo.from[k] <= s && s <= memo.at[k]) {
3341
3630
  i = memo.at[k];
3631
+ if (memo.end[k] === 1)
3632
+ text.readEnd();
3633
+ }
3342
3634
  else {
3343
3635
  i = s;
3344
- while (i < n) {
3636
+ let reachedEnd = false;
3637
+ while (!(reachedEnd = text.atEnd(i))) {
3345
3638
  const unit = readAnchorUnit(text, i, json);
3346
3639
  const ch = unitChOf(unit);
3347
3640
  if (ch === JSON_STRING_END || isRegexSpace(ch))
@@ -3350,7 +3643,7 @@ function bareValueEnd(text, s, json, listSep, memo) {
3350
3643
  break;
3351
3644
  const next = i + unitLenOf(unit);
3352
3645
  if (isQuoteUnit(ch)) {
3353
- if (next >= n)
3646
+ if ((reachedEnd = text.atEnd(next)))
3354
3647
  break;
3355
3648
  if (endsValueUnit(unitChOf(readAnchorUnit(text, next, json))))
3356
3649
  break;
@@ -3359,6 +3652,7 @@ function bareValueEnd(text, s, json, listSep, memo) {
3359
3652
  }
3360
3653
  memo.from[k] = s;
3361
3654
  memo.at[k] = i;
3655
+ memo.end[k] = reachedEnd ? 1 : 0;
3362
3656
  memo.netFrom[k] = NO_MEMO_GROUP;
3363
3657
  memo.runFrom[k] = NO_MEMO_GROUP;
3364
3658
  }
@@ -3411,21 +3705,20 @@ function bareValueEnd(text, s, json, listSep, memo) {
3411
3705
  return e;
3412
3706
  }
3413
3707
  function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3414
- const n = text.length;
3415
- if (s >= n)
3708
+ if (text.atEnd(s))
3416
3709
  return undefined;
3417
3710
  const firstUnit = readAnchorUnit(text, s, json);
3418
3711
  const first = unitChOf(firstUnit);
3419
3712
  let vs = s;
3420
3713
  if (isQuoteUnit(first) || first === JSON_STRING_END) {
3421
3714
  vs = s + unitLenOf(firstUnit);
3422
- if (vs >= n)
3715
+ if (text.atEnd(vs))
3423
3716
  return undefined;
3424
3717
  if (!isAsciiAlnumCode(unitChOf(readAnchorUnit(text, vs, json))))
3425
3718
  return undefined;
3426
3719
  const close = quotedValueEnd(text, vs, first, json, isHead);
3427
3720
  if (close === STRING_OPEN)
3428
- return { start: vs, end: n, quoted: true, open: true };
3721
+ return { start: vs, end: text.readEnd(), quoted: true, open: true };
3429
3722
  if (close >= 0)
3430
3723
  return { start: vs, end: close, quoted: true };
3431
3724
  }
@@ -3448,11 +3741,11 @@ function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3448
3741
  }
3449
3742
  if (isProgramIdentStartCode(text.charCodeAt(vs))) {
3450
3743
  let k = vs;
3451
- while (k < n && isProgramIdentCode(text.charCodeAt(k)))
3744
+ while (!text.atEnd(k) && isProgramIdentCode(text.charCodeAt(k)))
3452
3745
  k += 1;
3453
3746
  anchoredScanUnits += k - vs;
3454
3747
  const after = text.charCodeAt(k);
3455
- 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))))
3456
3749
  return undefined;
3457
3750
  }
3458
3751
  const e = bareValueEnd(text, vs, json, listSep, memo);
@@ -3471,7 +3764,7 @@ function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3471
3764
  case "goes-on":
3472
3765
  return undefined;
3473
3766
  case "undecided":
3474
- return { start: vs, end: text.length, quoted: false, open: true };
3767
+ return { start: vs, end: text.readEnd(), quoted: false, open: true };
3475
3768
  default: {
3476
3769
  const unhandled = rest;
3477
3770
  throw new Error(`redact: unhandled line-key rest ${String(unhandled)}`);
@@ -3481,9 +3774,8 @@ function literalValueAt(text, s, json, bare, listSep, isHead, memo) {
3481
3774
  return { start: vs, end: e, quoted: false };
3482
3775
  }
3483
3776
  function lineKeyRestAt(text, e, json, isHead) {
3484
- const n = text.length;
3485
3777
  let i = e;
3486
- while (i < n) {
3778
+ while (!text.atEnd(i)) {
3487
3779
  const unit = readAnchorUnit(text, i, json);
3488
3780
  const ch = unitChOf(unit);
3489
3781
  if (isInlineSpaceUnit(ch) || (ch >= 0 && INVISIBLE_UNIT_CLASS[ch] === 1)) {
@@ -3545,9 +3837,8 @@ function declarativeBareAt(text, head, spacedRight, json) {
3545
3837
  return spacedRight && lineHeadBefore(text, head.runStart, json) ? "line-key" : "never";
3546
3838
  }
3547
3839
  function schemeValueAt(text, s, json) {
3548
- const n = text.length;
3549
3840
  let i = s;
3550
- if (i >= n)
3841
+ if (text.atEnd(i))
3551
3842
  return undefined;
3552
3843
  const opening = readAnchorUnit(text, i, json);
3553
3844
  if (isQuoteUnit(unitChOf(opening)) || unitChOf(opening) === JSON_STRING_END)
@@ -3559,14 +3850,14 @@ function schemeValueAt(text, s, json) {
3559
3850
  anchoredScanUnits += REDACTION_MARK.length;
3560
3851
  }
3561
3852
  else {
3562
- 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))))
3563
3854
  i += 1;
3564
3855
  anchoredScanUnits += i - w;
3565
3856
  if (i - w > STRONG_AUTH_SCHEME_MAX || !STRONG_AUTH_SCHEMES.has(text.slice(w, i).toLowerCase()))
3566
3857
  return undefined;
3567
3858
  }
3568
3859
  let separated = false;
3569
- while (i < n) {
3860
+ while (!text.atEnd(i)) {
3570
3861
  const unit = readAnchorUnit(text, i, json);
3571
3862
  const ch = unitChOf(unit);
3572
3863
  if (!isInlineSpaceUnit(ch) && !isSchemeDelimiterCode(ch))
@@ -3579,30 +3870,30 @@ function schemeValueAt(text, s, json) {
3579
3870
  const vs = i;
3580
3871
  if (engineMarked) {
3581
3872
  let k = i;
3582
- while (k < n && isToken68Code(text.charCodeAt(k)))
3873
+ while (!text.atEnd(k) && isToken68Code(text.charCodeAt(k)))
3583
3874
  k += 1;
3584
- while (k < n && text.charCodeAt(k) === CH_EQ)
3875
+ while (!text.atEnd(k) && text.charCodeAt(k) === CH_EQ)
3585
3876
  k += 1;
3586
3877
  if (k - vs >= BEARER_VALUE_MIN) {
3587
3878
  anchoredScanUnits += k - vs;
3588
3879
  return { start: vs, end: k, quoted: false };
3589
3880
  }
3590
3881
  }
3591
- if (i >= n || !isAsciiAlnumCode(text.charCodeAt(i)))
3882
+ if (text.atEnd(i) || !isAsciiAlnumCode(text.charCodeAt(i)))
3592
3883
  return undefined;
3593
- while (i < n && isToken68Code(text.charCodeAt(i)))
3884
+ while (!text.atEnd(i) && isToken68Code(text.charCodeAt(i)))
3594
3885
  i += 1;
3595
- while (i < n && text.charCodeAt(i) === CH_EQ)
3886
+ while (!text.atEnd(i) && text.charCodeAt(i) === CH_EQ)
3596
3887
  i += 1;
3597
3888
  anchoredScanUnits += i - vs;
3598
3889
  const e = i;
3599
- while (i < n) {
3890
+ while (!text.atEnd(i)) {
3600
3891
  const unit = readAnchorUnit(text, i, json);
3601
3892
  if (!isInlineSpaceUnit(unitChOf(unit)))
3602
3893
  break;
3603
3894
  i += unitLenOf(unit);
3604
3895
  }
3605
- if (i < n && !endsHeaderValueUnit(unitChOf(readAnchorUnit(text, i, json))))
3896
+ if (!text.atEnd(i) && !endsHeaderValueUnit(unitChOf(readAnchorUnit(text, i, json))))
3606
3897
  return undefined;
3607
3898
  return { start: vs, end: e, quoted: false };
3608
3899
  }
@@ -3610,10 +3901,9 @@ function anchoredValueAt(scan, text, op, json, span, isHead, memo) {
3610
3901
  const head = anchorHeadAt(scan, text, op, json);
3611
3902
  if (head === undefined)
3612
3903
  return;
3613
- const n = text.length;
3614
3904
  let s = op + 1;
3615
3905
  let spacedRight = false;
3616
- while (s < n) {
3906
+ while (!text.atEnd(s)) {
3617
3907
  const unit = readAnchorUnit(text, s, json);
3618
3908
  if (!isInlineSpaceUnit(unitChOf(unit)))
3619
3909
  break;
@@ -3680,6 +3970,7 @@ function collectAnchoredValueSpans(scan, text, json, span, isHead) {
3680
3970
  if (!known && dirty !== undefined && op < rd.cut)
3681
3971
  continue;
3682
3972
  scan.self = readingKey(anchorFamily, op);
3973
+ text.attempt(op + 1);
3683
3974
  let lo;
3684
3975
  let hi;
3685
3976
  let answer;
@@ -3695,6 +3986,7 @@ function collectAnchoredValueSpans(scan, text, json, span, isHead) {
3695
3986
  hi = scan.footHi;
3696
3987
  if (lo > hi)
3697
3988
  continue;
3989
+ noteExecution(scan, lo, hi, op + 1);
3698
3990
  answer = nameBoundAt(scan, lo, hi);
3699
3991
  }
3700
3992
  if (wr.cut === OPS_UNCUT) {
@@ -3749,10 +4041,9 @@ export function resetDiagnosticScanUnitsForTest() {
3749
4041
  diagnosticScanUnits = 0;
3750
4042
  }
3751
4043
  function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3752
- const n = text.length;
3753
4044
  const readRun = (from, isValue) => {
3754
4045
  let i = from;
3755
- while (i < n) {
4046
+ while (!text.atEnd(i)) {
3756
4047
  const unit = readTextUnit(text, i, json);
3757
4048
  diagnosticScanUnits += 1;
3758
4049
  if (!isValue(unitChOf(unit)))
@@ -3763,6 +4054,7 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3763
4054
  };
3764
4055
  const skipBlank = (from) => readRun(from, isRegexSpace);
3765
4056
  let valueEnd = 0;
4057
+ let plainEnd = 0;
3766
4058
  let nextEq = text.indexOf("=");
3767
4059
  let nextColon = text.indexOf(":");
3768
4060
  while (nextEq >= 0 || nextColon >= 0) {
@@ -3775,26 +4067,23 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3775
4067
  op = nextColon;
3776
4068
  nextColon = text.indexOf(":", op + 1);
3777
4069
  }
3778
- if (op + 1 < valueEnd)
3779
- continue;
4070
+ const inValue = op + 1 < plainEnd;
3780
4071
  let e = op;
3781
4072
  while (e > 0 && isRegexSpace(text.charCodeAt(e - 1)))
3782
4073
  e -= 1;
3783
4074
  diagnosticScanUnits += op - e;
3784
4075
  if (e === 0 || !isRegexWordCode(text.charCodeAt(e - 1)))
3785
4076
  continue;
3786
- const floor = Math.max(0, e - CREDENTIAL_NAME_MAX - 1);
3787
4077
  let runStart = e;
3788
- while (runStart > floor && isDiagnosticNameCode(text.charCodeAt(runStart - 1)))
4078
+ while (runStart > 0 && isDiagnosticNameCode(text.charCodeAt(runStart - 1)))
3789
4079
  runStart -= 1;
3790
4080
  diagnosticScanUnits += e - runStart;
3791
- const runOpen = runStart === floor && floor > 0 && isDiagnosticNameCode(text.charCodeAt(floor - 1));
3792
4081
  let nameStart = -1;
3793
4082
  let nameTier;
3794
- for (let q = runStart; q < e && nameStart < 0; q++) {
3795
- if (e - q > CREDENTIAL_NAME_MAX || !isRegexWordCode(text.charCodeAt(q)))
4083
+ for (let q = runStart; q < e && nameStart < 0; q = q === runStart ? Math.max(runStart + 1, e - CREDENTIAL_NAME_MAX) : q + 1) {
4084
+ if (!isRegexWordCode(text.charCodeAt(q)))
3796
4085
  continue;
3797
- const bounded = (q === runStart && !runOpen) || text.charCodeAt(q - 1) === CH_MINUS || deletedAt(q) !== "none";
4086
+ const bounded = q === runStart || text.charCodeAt(q - 1) === CH_MINUS || deletedAt(q) !== "none";
3798
4087
  const tier = bounded ? credentialNameAnchor(text.slice(q, e)) : undefined;
3799
4088
  if (tier !== undefined && diagnosticNameSlot(tier)) {
3800
4089
  nameStart = q;
@@ -3803,40 +4092,50 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3803
4092
  }
3804
4093
  if (nameStart < 0)
3805
4094
  continue;
3806
- let s = skipBlank(op + 1);
3807
- if (s < n) {
3808
- const unit = readTextUnit(text, s, json);
3809
- if (unitChOf(unit) === CH_QUOTE)
3810
- s += unitLenOf(unit);
4095
+ text.attempt(nameStart);
4096
+ let s;
4097
+ let end;
4098
+ if (inValue) {
4099
+ s = op + 1;
4100
+ end = plainEnd;
3811
4101
  }
3812
- if (s >= n) {
3813
- if (isHead)
3814
- span(nameStart, n, op, NOT_CLOSED, true);
3815
- continue;
4102
+ else {
4103
+ s = skipBlank(op + 1);
4104
+ if (!text.atEnd(s)) {
4105
+ const unit = readTextUnit(text, s, json);
4106
+ if (unitChOf(unit) === CH_QUOTE)
4107
+ s += unitLenOf(unit);
4108
+ }
4109
+ if (text.atEnd(s)) {
4110
+ if (isHead)
4111
+ span(nameStart, text.readEnd(), op, NOT_CLOSED, true);
4112
+ continue;
4113
+ }
4114
+ end = readRun(s, isDiagnosticValueUnit);
4115
+ plainEnd = end;
3816
4116
  }
3817
- let end = readRun(s, isDiagnosticValueUnit);
3818
4117
  if (end > s && nameTier === "auth-scheme" && readRun(s, isAsciiLetterCode) === end) {
3819
4118
  const k = skipBlank(end);
3820
- if (k > end && k >= n && isHead) {
3821
- span(nameStart, n, op, NOT_CLOSED, true);
4119
+ if (k > end && isHead && text.atEnd(k)) {
4120
+ span(nameStart, text.readEnd(), op, NOT_CLOSED, true);
3822
4121
  continue;
3823
4122
  }
3824
- if (k > end && k < n) {
4123
+ if (k > end && !text.atEnd(k)) {
3825
4124
  const credEnd = readRun(k, isDiagnosticSchemeValueUnit);
3826
4125
  if (credEnd > k)
3827
4126
  end = credEnd;
3828
4127
  }
3829
4128
  }
3830
- if (end > s) {
4129
+ if (end > s && end > valueEnd) {
3831
4130
  span(s, end, op, NOT_CLOSED);
3832
4131
  valueEnd = end;
3833
4132
  }
3834
4133
  }
3835
4134
  let schemeEnd = 0;
3836
- DIAGNOSTIC_SCHEME_RE.lastIndex = 0;
3837
- 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)) {
3838
4136
  const at = m.index;
3839
4137
  const q = at + m[0].length;
4138
+ text.attempt(at);
3840
4139
  if (q < schemeEnd)
3841
4140
  continue;
3842
4141
  if (at > 0 && isRegexWordCode(text.charCodeAt(at - 1)) && deletedAt(at) === "none")
@@ -3844,9 +4143,9 @@ function collectDiagnosticValueSpans(text, json, span, isHead, deletedAt) {
3844
4143
  const k = skipBlank(q);
3845
4144
  if (k === q && deletedAt(q) !== "blank")
3846
4145
  continue;
3847
- if (k >= n) {
4146
+ if (text.atEnd(k)) {
3848
4147
  if (isHead)
3849
- span(at, n, at, NOT_CLOSED, true);
4148
+ span(at, text.readEnd(), at, NOT_CLOSED, true);
3850
4149
  continue;
3851
4150
  }
3852
4151
  const end = readRun(k, isDiagnosticSchemeValueUnit);
@@ -3864,12 +4163,14 @@ export function structuralScannerForTest(arm) {
3864
4163
  };
3865
4164
  switch (arm) {
3866
4165
  case "json-secret-field":
3867
- 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)) };
3868
4167
  case "pem-fence":
3869
- 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)) };
3870
4169
  }
3871
4170
  }
3872
4171
  let ppkScanBytes = 0;
4172
+ const TO_END = -1;
4173
+ const EACH_HEAD = -1;
3873
4174
  export function ppkScanBytesForTest() { return ppkScanBytes; }
3874
4175
  export function resetPpkScanBytesForTest() { ppkScanBytes = 0; }
3875
4176
  function collectPpkContainerSpans(scan, text, eat, span, isHead) {
@@ -3884,14 +4185,14 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3884
4185
  if (c !== undefined && c >= from)
3885
4186
  return c;
3886
4187
  const p = text.indexOf(needle, from);
3887
- ppkScanBytes += (p === -1 ? text.length : p) - from;
4188
+ ppkScanBytes += p === -1 ? 0 : p - from;
3888
4189
  const v = p === -1 ? Number.POSITIVE_INFINITY : p;
3889
4190
  cache.set(needle, v);
3890
4191
  return v;
3891
4192
  };
3892
4193
  const versionEnd = (at) => {
3893
4194
  let p = at + HEAD.length;
3894
- while (p < text.length && tokenEnd(scan, p) < 0) {
4195
+ while (!text.atEnd(p) && tokenEnd(scan, p) < 0) {
3895
4196
  const c = text.charCodeAt(p);
3896
4197
  if (c >= 48 && c <= 57)
3897
4198
  p += 1;
@@ -3906,11 +4207,12 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3906
4207
  };
3907
4208
  const isOpenHead = (at) => {
3908
4209
  const p = versionEnd(at);
3909
- 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);
3910
4211
  };
3911
- const nextVersionedHead = (from) => {
4212
+ const nextVersionedHead = (from, attemptAt) => {
3912
4213
  let p = find(HEAD, from);
3913
4214
  for (; p !== Number.POSITIVE_INFINITY; p = find(HEAD, p + HEAD.length)) {
4215
+ text.attempt(attemptAt === EACH_HEAD ? p : attemptAt);
3914
4216
  const lineEnd = versionedHeadEnd(p);
3915
4217
  if (lineEnd >= 0 && !lineHeld(scan, p, lineEnd, readingKey(fam, p)))
3916
4218
  break;
@@ -3919,7 +4221,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3919
4221
  };
3920
4222
  const hexRunEnd = (from) => {
3921
4223
  let e = from;
3922
- while (e < text.length && tokenEnd(scan, e) < 0) {
4224
+ while (!text.atEnd(e) && tokenEnd(scan, e) < 0) {
3923
4225
  const c = text.charCodeAt(e);
3924
4226
  if ((c >= 48 && c <= 57) || (c >= 97 && c <= 102) || (c >= 65 && c <= 70))
3925
4227
  e += 1;
@@ -3928,12 +4230,13 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3928
4230
  }
3929
4231
  return e;
3930
4232
  };
3931
- let at = nextVersionedHead(0);
4233
+ let at = nextVersionedHead(0, EACH_HEAD);
3932
4234
  while (at !== Number.POSITIVE_INFINITY) {
3933
4235
  scan.self = readingKey(fam, at);
4236
+ text.attempt(at);
3934
4237
  let pos = at + HEAD.length + 1;
3935
4238
  let closeAt = NOT_CLOSED;
3936
- let end = text.length;
4239
+ let end = TO_END;
3937
4240
  let ateToEnd = !isHead;
3938
4241
  let open = false;
3939
4242
  for (;;) {
@@ -3954,7 +4257,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3954
4257
  pos = hexStart;
3955
4258
  continue;
3956
4259
  }
3957
- const limit = nextVersionedHead(hexEnd);
4260
+ const limit = nextVersionedHead(hexEnd, at);
3958
4261
  const straggler = Math.min(find(LINES, hexEnd), find(MAC, hexEnd), find(HASH, hexEnd));
3959
4262
  let gapBlank = true;
3960
4263
  if (limit !== Number.POSITIVE_INFINITY) {
@@ -3968,7 +4271,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3968
4271
  }
3969
4272
  const undecided = isHead && limit === Number.POSITIVE_INFINITY;
3970
4273
  ateToEnd = straggler < limit || !gapBlank;
3971
- end = ateToEnd || undecided ? text.length : hexEnd;
4274
+ end = ateToEnd || undecided ? TO_END : hexEnd;
3972
4275
  open = undecided;
3973
4276
  if (end === hexEnd)
3974
4277
  closeAt = term;
@@ -3976,14 +4279,24 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
3976
4279
  }
3977
4280
  if (ateToEnd && eat !== undefined && eat.from < 0) {
3978
4281
  eat.from = at;
3979
- eat.len = text.length;
4282
+ eat.len = text.readEnd();
3980
4283
  }
3981
- span(at, end, at, closeAt, open);
3982
- 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);
3983
4286
  }
3984
4287
  const last = isHead ? text.lastIndexOf(HEAD) : -1;
3985
- if (last >= 0 && isOpenHead(last) && !lineHeld(scan, last, text.length, readingKey(fam, last)))
3986
- 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
+ }
3987
4300
  }
3988
4301
  const NO_GAP = (_unit) => false;
3989
4302
  const armPool = (() => {
@@ -3993,6 +4306,9 @@ const armPool = (() => {
3993
4306
  throw new Error(`redact: arm-pool replacement ${JSON.stringify(token)} may only use a leading $1 (the run kept outside the span)`);
3994
4307
  return body;
3995
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
+ };
3996
4312
  const regexMember = ([re, token], rank, order) => {
3997
4313
  if (token.startsWith("$1"))
3998
4314
  throw new Error(`redact: regex arm /${re.source}/ cannot carry an outside-span $1 prefix — that form belongs to the structural scanners`);
@@ -4015,7 +4331,7 @@ const armPool = (() => {
4015
4331
  phase: 1,
4016
4332
  lexical: LEXICAL_KEYWORD_ARMS.has(re),
4017
4333
  diagnosticOnly: false,
4018
- collect: (text, _json, _eat, out, isHead) => {
4334
+ collect: (text, _view, _json, _eat, out, isHead) => {
4019
4335
  if (boundary === undefined)
4020
4336
  collectRegexSpans(arm, text, out);
4021
4337
  else
@@ -4032,9 +4348,10 @@ const armPool = (() => {
4032
4348
  family: "prose",
4033
4349
  reach: regexDecisionReach(re, LEXICAL_KEYWORD_ARMS.has(re)),
4034
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) },
4035
4352
  };
4036
4353
  };
4037
- 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) => {
4038
4355
  const repl = spanRepl(token);
4039
4356
  return {
4040
4357
  token,
@@ -4043,16 +4360,17 @@ const armPool = (() => {
4043
4360
  phase: 1,
4044
4361
  lexical,
4045
4362
  diagnosticOnly,
4046
- 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),
4047
4364
  retry: undefined,
4048
4365
  headSrc: undefined,
4049
4366
  headFirst: undefined,
4050
4367
  family,
4051
4368
  reach,
4052
4369
  prior,
4370
+ head,
4053
4371
  };
4054
4372
  };
4055
- 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) => {
4056
4374
  const repl = spanRepl(token);
4057
4375
  return {
4058
4376
  token,
@@ -4061,41 +4379,47 @@ const armPool = (() => {
4061
4379
  phase: 2,
4062
4380
  lexical,
4063
4381
  diagnosticOnly: false,
4064
- 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),
4065
4383
  retry: undefined,
4066
4384
  headSrc: undefined,
4067
4385
  headFirst: undefined,
4068
4386
  family,
4069
4387
  repl,
4388
+ grain,
4070
4389
  reach,
4071
4390
  prior,
4391
+ head,
4072
4392
  };
4073
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] };
4074
4398
  const structuralScannerMember = (row, order) => {
4075
4399
  if (row === PEM_FENCE_PATTERN)
4076
- 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));
4077
4401
  if (row === SSH2_FENCE_PATTERN)
4078
- 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));
4079
4403
  if (row === URL_CREDENTIAL_PATTERN)
4080
- 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: ["://"] });
4081
4405
  if (row === JSON_SECRET_FIELD_PATTERN)
4082
- return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead), JSON_FIELD_BACK, "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] });
4083
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)`);
4084
4408
  };
4085
4409
  const members = [];
4086
4410
  for (const row of STRUCTURAL_SECRET_PATTERNS)
4087
4411
  members.push(structuralScannerMember(row, members.length));
4088
- 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] }));
4089
- 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] }));
4090
- 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] }));
4091
- 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));
4092
4416
  for (const row of KEYWORD_SECRET_PATTERNS)
4093
4417
  members.push(regexMember(row, ARM_RANK.keyword, members.length));
4094
4418
  for (const row of SHAPE_SECRET_PATTERNS)
4095
4419
  members.push(regexMember(row, ARM_RANK.shape, members.length));
4096
4420
  for (const row of FRAGMENT_SECRET_PATTERNS)
4097
4421
  members.push(regexMember(row, ARM_RANK.fragment, members.length));
4098
- 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));
4099
4423
  for (const [re] of KEYWORD_SECRET_PATTERNS) {
4100
4424
  if (!LEXICAL_KEYWORD_ARMS.has(re))
4101
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`);
@@ -4128,11 +4452,51 @@ const armPool = (() => {
4128
4452
  poolArmCount = members.length;
4129
4453
  poolBoundaryArmCount = retryMembers.length;
4130
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
+ };
4131
4494
  const run = (text, wantCount, json, eat, scope, reach, engineMarked, deletedAt) => {
4132
4495
  readings += 1;
4133
4496
  const isHead = reach !== undefined;
4134
4497
  const lane = LANES[scope];
4135
4498
  const spans = [];
4499
+ const view = new ScanReader(text);
4136
4500
  let tried;
4137
4501
  const untriedFrom = (nu, p) => {
4138
4502
  let q = p;
@@ -4182,11 +4546,14 @@ const armPool = (() => {
4182
4546
  at = freshCovered(before);
4183
4547
  }
4184
4548
  };
4185
- for (const m of lane.phase1)
4186
- 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
+ }
4187
4553
  closeCoveragePositions(0);
4554
+ let evidence;
4188
4555
  if (lane.phase2.length > 0) {
4189
- 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));
4190
4557
  const reuse = forensicRescanMode === "incremental";
4191
4558
  const foot = scan.armFoot;
4192
4559
  let dirty;
@@ -4205,15 +4572,29 @@ const armPool = (() => {
4205
4572
  scan.self = NO_SELF;
4206
4573
  scan.footLo = FOOT_NONE;
4207
4574
  scan.footHi = -1;
4208
- 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);
4209
4578
  foot[2 * j] = scan.footLo;
4210
4579
  foot[2 * j + 1] = scan.footHi;
4211
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
+ }
4212
4592
  }
4213
4593
  closeCoveragePositions(before);
4214
4594
  forensicLevels += 1;
4215
4595
  const grown = foldEvidenceRows(scan.rows, spans, folded, (o) => familyCodeOfOrder[o]);
4216
4596
  folded = spans.length;
4597
+ evidence = { execs: scan.execs, rows: grown.next };
4217
4598
  if (grown.dirty.starts.length === 0)
4218
4599
  break;
4219
4600
  if (level + 1 >= FORENSIC_LEVEL_MAX) {
@@ -4236,9 +4617,12 @@ const armPool = (() => {
4236
4617
  }
4237
4618
  poolSpanCount += spans.length;
4238
4619
  const survives = (sp) => !engineMarked(sp.start, sp.end, sp.repl) && (sp.undecided !== true || reach === undefined || reach(sp.start));
4239
- 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;
4240
4624
  if (!wantCount || kept.length === 0)
4241
- return { hits: NO_HITS, kept };
4625
+ return { hits: NO_HITS, kept, cover, evidence, open, unattributed };
4242
4626
  const tokenOf = (sp) => members[sp.order].token;
4243
4627
  const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full" && survives(sp)).sort((a, b) => a.start - b.start);
4244
4628
  const byArmedToken = new Map();
@@ -4260,9 +4644,9 @@ const armPool = (() => {
4260
4644
  cur.count += 1;
4261
4645
  stamped.push({ ...sp, hit: { token, arm } });
4262
4646
  }
4263
- 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 };
4264
4648
  };
4265
- 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) });
4266
4650
  })();
4267
4651
  let onRedaction;
4268
4652
  export function setRedactionObserver(fn) {
@@ -4286,9 +4670,9 @@ export const WHOLE_EXTENT = { kind: "whole" };
4286
4670
  function invisibleClassesOf(layout) {
4287
4671
  switch (layout) {
4288
4672
  case "multi-line":
4289
- return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL;
4673
+ return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_IGNORABLE;
4290
4674
  case "single-line":
4291
- return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_WHITESPACE;
4675
+ return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_WHITESPACE | RAW_IGNORABLE;
4292
4676
  default: {
4293
4677
  const unhandled = layout;
4294
4678
  throw new Error(`redact: unhandled layout ${String(unhandled)}`);
@@ -4329,14 +4713,19 @@ export function readingHeadEnd(text, layout, units, from = 0) {
4329
4713
  }
4330
4714
  return text.length;
4331
4715
  }
4332
- 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) {
4333
4717
  scannedUnits += text.length;
4334
4718
  const wantCount = wantHits || (onRedaction !== undefined && observe);
4335
4719
  const eat = observe ? { from: -1, len: 0, capFrom: -1, capLen: 0 } : undefined;
4336
4720
  let spans = [];
4721
+ const cover = [];
4337
4722
  let hits = NO_HITS;
4338
4723
  let readsSoFar = 0;
4339
4724
  let unioned = false;
4725
+ const evidence = [];
4726
+ let lattice = text.length;
4727
+ let viewOpen = text.length;
4728
+ let unattributed = false;
4340
4729
  const read = (layers, view) => {
4341
4730
  const toOriginal = createViewToOriginal(layers);
4342
4731
  const reach = extent.kind === "head" ? (viewStart) => toOriginal.start(viewStart) < extent.emitEnd : undefined;
@@ -4355,7 +4744,17 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent, obse
4355
4744
  };
4356
4745
  const r = applyRules(view, wantCount, json, eat, scope, reach, engineMarked, deletedAt);
4357
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;
4358
4755
  readsSoFar += 1;
4756
+ for (const sp of toOriginal.spans(r.cover))
4757
+ cover.push(sp);
4359
4758
  if (readsSoFar === 1) {
4360
4759
  spans = toOriginal.spans(r.kept);
4361
4760
  hits = r.hits;
@@ -4372,6 +4771,7 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent, obse
4372
4771
  const rfold = deleteInvisibleCharacters(text, raw, rmask);
4373
4772
  const normalized = normalizeForRedaction(rfold.text);
4374
4773
  const spelled = collectSpelledCuts(normalized);
4774
+ lattice = Math.min(lattice, latticeOpenOf(text.length, rfold, normalized, spelled.open));
4375
4775
  for (let smask = 0;; smask = (smask - spelled.present) & spelled.present) {
4376
4776
  const sfold = deleteInvisibleCharacters(normalized, spelled, smask);
4377
4777
  read([{ fold: sfold }, { input: rfold.text, view: normalized }, { fold: rfold }], sfold.text);
@@ -4387,9 +4787,21 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent, obse
4387
4787
  recordFailOpen("server.redact.ppk-eat-to-end", `from=${eat.from} len=${eat.len}`);
4388
4788
  if (eat !== undefined && eat.capFrom >= 0)
4389
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}`);
4390
4792
  if (observe)
4391
4793
  reportHits(observeFrom === 0 ? hits : hitsOfSpans(spans.filter((sp) => sp.end > observeFrom)));
4392
- 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, 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;
4393
4805
  }
4394
4806
  function hitsOfSpans(spans) {
4395
4807
  const by = new Map();
@@ -4427,8 +4839,8 @@ export function redactSecrets(text, opts) {
4427
4839
  if (before === "")
4428
4840
  return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT).text;
4429
4841
  const whole = before + text;
4430
- const r = runRedaction(whole, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT, before.length);
4431
- 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);
4432
4844
  }
4433
4845
  export function redactSecretsWithArms(text, opts) {
4434
4846
  const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", WHOLE_EXTENT);
@@ -4438,21 +4850,136 @@ export function redactionSpansForTest(text, opts) {
4438
4850
  const r = runRedaction(text, false, opts?.json === true, false, opts?.scope ?? "free-text", "multi-line", WHOLE_EXTENT);
4439
4851
  return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
4440
4852
  }
4441
- export function proposeRedactionSpans(text, scope, layout, extent) {
4442
- return runRedaction(text, true, false, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
4853
+ export function proposeRedactionSpans(text, scope, layout, extent, json) {
4854
+ return runRedaction(text, true, json, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
4443
4855
  }
4444
- export const PENDING_TAIL_UNITS = armPool.pendingTailUnits;
4856
+ export const MAX_DECISION_REACH = armPool.maxDecisionReach;
4445
4857
  export function regexDecisionReachForTest(re, lexical) {
4446
4858
  return regexDecisionReach(re, lexical);
4447
4859
  }
4448
- export function pendingTailReachForTest() {
4860
+ export function decisionReachForTest() {
4449
4861
  return armPool.reaches;
4450
4862
  }
4451
- function pendingTailStart(text, units) {
4452
- const r = readShortest(text, "multi-line");
4453
- return r.visible <= units ? 0 : createViewToOriginal(r.layers).start(r.visible - units);
4863
+ const FRONTIER_TERMS = {
4864
+ view: (m) => m.view,
4865
+ lattice: (m) => m.lattice,
4866
+ token: (m) => m.token,
4867
+ evidence: (m, f) => m.evidence(f),
4868
+ coverage: (m) => m.n,
4869
+ };
4870
+ const READ_PORTS = Object.keys(FRONTIER_TERMS);
4871
+ function settledFrontier(m) {
4872
+ let f = m.n;
4873
+ for (;;) {
4874
+ let next = f;
4875
+ for (const k of READ_PORTS)
4876
+ next = Math.min(next, FRONTIER_TERMS[k](m, next));
4877
+ if (next >= f)
4878
+ return f;
4879
+ f = next;
4880
+ }
4881
+ }
4882
+ function tokenTailOpen(text) {
4883
+ const n = text.length;
4884
+ const p = text.lastIndexOf("«");
4885
+ if (p < 0)
4886
+ return n;
4887
+ const e = tokenUnitEnd(text, p);
4888
+ if (e < 0)
4889
+ return n - p < TOKEN_UNIT_REACH && text.indexOf("»", p) < 0 ? p : n;
4890
+ if (e !== n)
4891
+ return n;
4892
+ let start = p;
4893
+ while (start > 0) {
4894
+ const q = text.lastIndexOf("«", start - 1);
4895
+ if (q < 0 || tokenUnitEnd(text, q) < start)
4896
+ break;
4897
+ start = q;
4898
+ }
4899
+ return start;
4900
+ }
4901
+ const TOKEN_UNIT_REACH = regexDecisionReach(TOKEN_UNIT_Y, false);
4902
+ function createEvidenceFrontier(readings) {
4903
+ const prepared = readings.filter((r) => r.execs.length > 0).map((r) => {
4904
+ const E = r.execs.length / 3;
4905
+ const order = Array.from({ length: E }, (_, i) => i);
4906
+ const hi = Float64Array.from(order, (i) => r.at(r.execs[3 * i + 1]));
4907
+ const from = Float64Array.from(order, (i) => r.at(r.execs[3 * i + 2]));
4908
+ order.sort((x, y) => hi[y] - hi[x]);
4909
+ const t = r.rows;
4910
+ const iv = [];
4911
+ for (let k = 0; k < t.starts.length; k++) {
4912
+ const s = r.at(t.starts[k]);
4913
+ const c = t.closeAt[k];
4914
+ const d = Math.max(r.at(t.ends[k] - 1) + 1, c >= 0 ? r.at(c) + 1 : 0);
4915
+ if (d - s >= 2)
4916
+ iv.push([s, d]);
4917
+ }
4918
+ iv.sort((a, b) => a[0] - b[0]);
4919
+ const segS = [];
4920
+ const segE = [];
4921
+ for (const [s, d] of iv) {
4922
+ const last = segE.length - 1;
4923
+ if (last >= 0 && s < segE[last])
4924
+ segE[last] = Math.max(segE[last], d);
4925
+ else {
4926
+ segS.push(s);
4927
+ segE.push(d);
4928
+ }
4929
+ }
4930
+ const horizon = (f) => {
4931
+ let a = 0;
4932
+ let b = segS.length;
4933
+ while (a < b) {
4934
+ const mid = (a + b) >>> 1;
4935
+ if (segS[mid] < f)
4936
+ a = mid + 1;
4937
+ else
4938
+ b = mid;
4939
+ }
4940
+ return a > 0 && f < segE[a - 1] ? segS[a - 1] : f;
4941
+ };
4942
+ return { order, hi, from, horizon };
4943
+ });
4944
+ if (prepared.length === 0)
4945
+ return (f) => f;
4946
+ return (f) => {
4947
+ let out = f;
4948
+ for (const p of prepared) {
4949
+ let cur = f;
4950
+ let least = Infinity;
4951
+ for (let k = 0;;) {
4952
+ const fev = p.horizon(cur);
4953
+ while (k < p.order.length && p.hi[p.order[k]] >= fev)
4954
+ least = Math.min(least, p.from[p.order[k++]]);
4955
+ if (least >= cur)
4956
+ break;
4957
+ cur = least;
4958
+ }
4959
+ out = Math.min(out, cur);
4960
+ }
4961
+ return out;
4962
+ };
4963
+ }
4964
+ export function evidenceFrontierForTest(execs, rows, f) {
4965
+ const t = {
4966
+ starts: Int32Array.from(rows, (r) => r[0]),
4967
+ ends: Int32Array.from(rows, (r) => r[1]),
4968
+ family: new Uint8Array(rows.length),
4969
+ anchor: Int32Array.from(rows, (r) => r[0]),
4970
+ closeAt: Int32Array.from(rows, (r) => r[2]),
4971
+ };
4972
+ return createEvidenceFrontier([{ execs: execs.flat(), rows: t, at: (v) => v }])(f);
4454
4973
  }
4455
- function renderRange(text, spans, from, until) {
4974
+ export function forensicExecutionsForTest(text) {
4975
+ const out = [];
4976
+ for (const r of runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length }).evidence) {
4977
+ for (let k = 0; k < r.execs.length; k += 3)
4978
+ out.push([r.at(r.execs[k]), r.at(r.execs[k + 1]), r.at(r.execs[k + 2])]);
4979
+ }
4980
+ return out;
4981
+ }
4982
+ export function renderRedactionSpans(text, spans, from, until) {
4456
4983
  if (from >= until)
4457
4984
  return "";
4458
4985
  const parts = [];
@@ -4471,9 +4998,11 @@ function renderRange(text, spans, from, until) {
4471
4998
  parts.push(text.slice(at, until));
4472
4999
  return parts.join("");
4473
5000
  }
4474
- const STREAM_RESCAN_MIN = 64;
4475
- const streamRescanStep = (n) => Math.max(STREAM_RESCAN_MIN, n >>> 5);
5001
+ const streamRescanStep = (n) => Math.max(1, n >>> 5);
4476
5002
  const STREAM_WINDOW_HORIZON = 2;
5003
+ if (STREAM_WINDOW_HORIZON * MAX_DECISION_REACH > MAX_RECOGNITION_SPAN) {
5004
+ 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`);
5005
+ }
4477
5006
  const STREAM_WINDOW_MIN = 2048;
4478
5007
  function deriveStreamWindowAlphabets(priors) {
4479
5008
  const blank = new Uint8Array(0x10000);
@@ -4493,7 +5022,7 @@ function deriveStreamWindowAlphabets(priors) {
4493
5022
  throw new Error("redact: an anchored arm declares an empty anchor literal");
4494
5023
  for (let i = 0; i < anchor.length; i++) {
4495
5024
  const c = anchor.charCodeAt(i);
4496
- if (INVISIBLE_UNIT_CLASS[c] !== 0 || FORMAT_CODEPOINT_TABLE[c] === 1 || (c & 0xf800) === 0xd800 || c === CH_BACKSLASH) {
5025
+ if (INVISIBLE_UNIT_CLASS[c] !== 0 || FORMAT_CODEPOINT_TABLE[c] === 1 || isReadingIgnorable(c) || (c & 0xf800) === 0xd800 || c === CH_BACKSLASH) {
4497
5026
  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`);
4498
5027
  }
4499
5028
  }
@@ -4557,15 +5086,70 @@ function windowAdvancePoint(text, units, alphabets = STREAM_WINDOW_ALPHABETS) {
4557
5086
  return at <= 0 ? 0 : text.lastIndexOf("\n", at - 1) + 1;
4558
5087
  }
4559
5088
  function windowIndependentAt(text, q) {
4560
- const full = runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length }).spans;
5089
+ return windowVerdictAt(text, q) === "independent";
5090
+ }
5091
+ function windowVerdictAt(text, q) {
5092
+ const fullRun = runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length });
5093
+ const full = fullRun.spans;
4561
5094
  if (full.some((s) => s.start < q && s.end > q))
4562
- return false;
5095
+ return "span-straddles";
5096
+ if (fullRun.evidence.some((r) => footprintStraddles(r, q)))
5097
+ return "footprint-straddles";
5098
+ if (fullRun.evidence.some((r) => rowStraddles(r, q)))
5099
+ return "row-straddles";
4563
5100
  const after = full.filter((s) => s.start >= q);
4564
5101
  const tail = text.slice(q);
4565
- const alone = runRedaction(tail, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: tail.length }).spans;
5102
+ const aloneRun = runRedaction(tail, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: tail.length });
5103
+ const alone = aloneRun.spans;
4566
5104
  if (after.length !== alone.length)
5105
+ return "spans-differ";
5106
+ if (!after.every((a, i) => a.start - q === alone[i].start && a.end - q === alone[i].end && a.repl === alone[i].repl))
5107
+ return "spans-differ";
5108
+ if (!sameRowSet(rowKeysFrom(fullRun.evidence, q), rowKeysFrom(aloneRun.evidence, 0)))
5109
+ return "rows-differ";
5110
+ return fullRun.view < q ? "view-straddles" : "independent";
5111
+ }
5112
+ export function windowVerdictAtForTest(text, q) {
5113
+ return windowVerdictAt(text, q);
5114
+ }
5115
+ function footprintStraddles(r, q) {
5116
+ for (let k = 0; k < r.execs.length; k += 3)
5117
+ if (r.at(r.execs[k]) < q && r.at(r.execs[k + 1]) >= q)
5118
+ return true;
5119
+ return false;
5120
+ }
5121
+ function rowStraddles(r, q) {
5122
+ const t = r.rows;
5123
+ for (let k = 0; k < t.starts.length; k++) {
5124
+ if (r.at(t.starts[k]) >= q)
5125
+ continue;
5126
+ const c = t.closeAt[k];
5127
+ if (Math.max(r.at(t.ends[k] - 1) + 1, c >= 0 ? r.at(c) + 1 : 0) > q)
5128
+ return true;
5129
+ }
5130
+ return false;
5131
+ }
5132
+ function rowKeysFrom(readings, q) {
5133
+ const keys = new Set();
5134
+ for (const r of readings) {
5135
+ const t = r.rows;
5136
+ for (let k = 0; k < t.starts.length; k++) {
5137
+ const s = r.at(t.starts[k]);
5138
+ if (s < q)
5139
+ continue;
5140
+ const c = t.closeAt[k];
5141
+ 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}`);
5142
+ }
5143
+ }
5144
+ return keys;
5145
+ }
5146
+ function sameRowSet(a, b) {
5147
+ if (a.size !== b.size)
4567
5148
  return false;
4568
- return after.every((a, i) => a.start - q === alone[i].start && a.end - q === alone[i].end && a.repl === alone[i].repl);
5149
+ for (const k of a)
5150
+ if (!b.has(k))
5151
+ return false;
5152
+ return true;
4569
5153
  }
4570
5154
  export function windowAdvancePointForTest(text, units, extra = []) {
4571
5155
  return windowAdvancePoint(text, units, extra.length === 0 ? STREAM_WINDOW_ALPHABETS : deriveStreamWindowAlphabets([...armPool.priors, ...extra]));
@@ -4574,18 +5158,53 @@ export function windowIndependentAtForTest(text, q) {
4574
5158
  return windowIndependentAt(text, q);
4575
5159
  }
4576
5160
  export function createRedactionStream() {
4577
- return openRedactionStream(PENDING_TAIL_UNITS);
5161
+ return openRedactionStream();
4578
5162
  }
4579
5163
  export function createRedactionStreamWithoutWindowForTest() {
4580
- return openRedactionStream(PENDING_TAIL_UNITS, false);
5164
+ return openRedactionStream(false);
4581
5165
  }
4582
5166
  export function createRedactionStreamWithCertifierForTest(certify) {
4583
- return openRedactionStream(PENDING_TAIL_UNITS, true, certify);
4584
- }
4585
- export function createRedactionStreamWithTailForTest(units) {
4586
- return openRedactionStream(units);
4587
- }
4588
- function openRedactionStream(pendingTail, windowed = true, certify = windowIndependentAt) {
5167
+ return openRedactionStream(true, certify);
5168
+ }
5169
+ export function createRedactionStreamWithFrontierForTest(adjust) {
5170
+ return openRedactionStream(true, windowIndependentAt, adjust);
5171
+ }
5172
+ function settledCut(win, joints = NO_JOINTS) {
5173
+ const n = win.length;
5174
+ let rounds = 1;
5175
+ let r = runRedaction(win, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: n }, 0, joints);
5176
+ const lattice = r.lattice;
5177
+ const token = tokenTailOpen(win);
5178
+ let scanned = n;
5179
+ for (let tail = Math.min(lattice, token), back = 1; tail < scanned; back *= 2) {
5180
+ scanned = Math.max(0, Math.min(tail, scanned - back));
5181
+ const head = win.slice(0, scanned);
5182
+ r = runRedaction(head, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: scanned }, 0, joints);
5183
+ rounds += 1;
5184
+ tail = Math.min(r.lattice, tokenTailOpen(head));
5185
+ }
5186
+ const spans = r.spans;
5187
+ const inputs = { n, view: r.view, lattice, token, evidence: createEvidenceFrontier(r.evidence) };
5188
+ const frontier = settledFrontier(inputs);
5189
+ let cut = frontier;
5190
+ for (const s of spans)
5191
+ if (s.start < cut && (s.end >= cut || s.end >= scanned))
5192
+ cut = s.start;
5193
+ return { cut, spans, inputs, frontier, rounds };
5194
+ }
5195
+ export function settledCutsForTest(text) {
5196
+ const s = settledCut(text);
5197
+ const terms = Object.fromEntries(READ_PORTS.map((k) => [k, FRONTIER_TERMS[k](s.inputs, s.frontier)]));
5198
+ return { cut: s.cut, settled: renderRedactionSpans(text, s.spans, 0, s.cut), frontier: s.frontier, terms, rounds: s.rounds };
5199
+ }
5200
+ export function createScanViewForTest(text) {
5201
+ const r = new ScanReader(text);
5202
+ return { view: r, open: () => r.open, unattributed: () => r.unattributed };
5203
+ }
5204
+ export function expandHeadUnitsForTest(src, flags = "g") {
5205
+ return expandHeadUnits(src, flags).length;
5206
+ }
5207
+ function openRedactionStream(windowed = true, certify = windowIndependentAt, adjust = 0) {
4589
5208
  let past = [];
4590
5209
  let total = 0;
4591
5210
  let win = "";
@@ -4596,6 +5215,8 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4596
5215
  let sent = "";
4597
5216
  let scannedAt = 0;
4598
5217
  let evaluatedAt = -1;
5218
+ const commits = [];
5219
+ const jointsInWindow = () => (commits.length === 0 ? NO_JOINTS : commits.filter((c) => c > base).map((c) => c - base));
4599
5220
  let retryAt = 0;
4600
5221
  const whole = () => (past.length === 0 ? win : past.join("") + win);
4601
5222
  const advanceWindow = () => {
@@ -4604,7 +5225,7 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4604
5225
  evaluatedAt = total;
4605
5226
  retryAt = win.length + (win.length >> 1);
4606
5227
  windowEvaluations += 1;
4607
- const q = windowAdvancePoint(win, STREAM_WINDOW_HORIZON * pendingTail);
5228
+ const q = windowAdvancePoint(win, STREAM_WINDOW_HORIZON * MAX_DECISION_REACH);
4608
5229
  if (q < 0) {
4609
5230
  retryAt = 0;
4610
5231
  return;
@@ -4627,25 +5248,18 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4627
5248
  total += chunk.length;
4628
5249
  },
4629
5250
  takeSettled() {
4630
- if (total - released <= pendingTail)
4631
- return "";
4632
- if (total - scannedAt < streamRescanStep(total))
5251
+ if (released === total || total - scannedAt < streamRescanStep(total))
4633
5252
  return "";
4634
5253
  scannedAt = total;
4635
- const emitEnd = pendingTailStart(win, pendingTail);
4636
5254
  const from = released - base;
4637
- if (emitEnd <= from)
4638
- return "";
4639
- const spans = runRedaction(win, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd }).spans;
4640
- let cut = emitEnd;
4641
- for (const s of spans)
4642
- if (s.start < cut && s.end > cut)
4643
- cut = s.start;
5255
+ const settled = settledCut(win, jointsInWindow());
5256
+ const spans = settled.spans;
5257
+ let cut = Math.min(win.length, settled.cut + adjust);
4644
5258
  if (cut > 0 && cut < win.length && (win.charCodeAt(cut - 1) & 0xfc00) === 0xd800 && (win.charCodeAt(cut) & 0xfc00) === 0xdc00)
4645
5259
  cut -= 1;
4646
5260
  if (cut <= from)
4647
5261
  return "";
4648
- const out = renderRange(win, spans, from, cut);
5262
+ const out = renderRedactionSpans(win, spans, from, cut);
4649
5263
  released = base + cut;
4650
5264
  sent += out;
4651
5265
  return out;
@@ -4653,9 +5267,10 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4653
5267
  takeAll() {
4654
5268
  if (released === total)
4655
5269
  return "";
4656
- const spans = runRedaction(win, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans;
4657
- const out = renderRange(win, spans, released - base, win.length);
5270
+ const spans = runRedaction(win, false, false, false, "free-text", "multi-line", WHOLE_EXTENT, 0, jointsInWindow()).spans;
5271
+ const out = renderRedactionSpans(win, spans, released - base, win.length);
4658
5272
  released = scannedAt = total;
5273
+ commits.push(total);
4659
5274
  sent += out;
4660
5275
  return out;
4661
5276
  },
@@ -4663,12 +5278,13 @@ function openRedactionStream(pendingTail, windowed = true, certify = windowIndep
4663
5278
  if (deliveredFrom === total)
4664
5279
  return { rest: "", diverged: false };
4665
5280
  const buf = whole();
4666
- const spans = runRedaction(buf, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans;
4667
- const split = released > deliveredFrom && spans.some((s) => s.start < released && s.end > released);
4668
- return { rest: renderRange(buf, spans, released, buf.length), diverged: split || renderRange(buf, spans, deliveredFrom, released) !== sent };
5281
+ const { spans, crossed } = runRedaction(buf, false, false, false, "free-text", "multi-line", WHOLE_EXTENT, 0, commits);
5282
+ const split = released > deliveredFrom && (spans.some((s) => s.start < released && s.end > released) || crossed.some((j) => j > deliveredFrom));
5283
+ return { rest: renderRedactionSpans(buf, spans, released, buf.length), diverged: split || renderRedactionSpans(buf, spans, deliveredFrom, released) !== sent };
4669
5284
  },
4670
5285
  skip() {
4671
5286
  released = deliveredFrom = scannedAt = total;
5287
+ commits.push(total);
4672
5288
  sent = "";
4673
5289
  },
4674
5290
  endBlock() {
@@ -4836,10 +5452,6 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
4836
5452
  }
4837
5453
  return false;
4838
5454
  }
4839
- let displaySafeFailClosedWarned = false;
4840
- export function resetDisplaySafeFailClosedWarnForTest() {
4841
- displaySafeFailClosedWarned = false;
4842
- }
4843
5455
  export function buildDisplaySafeUrl(raw, positionalLaw = displayUrlOf) {
4844
5456
  return buildDisplaySafeUrlChecked(raw, positionalLaw).text;
4845
5457
  }
@@ -4875,10 +5487,6 @@ export function buildDisplaySafeRefusal(text, sourceUrls) {
4875
5487
  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. " +
4876
5488
  "The machine-readable verdict is on the pairing field beside this one; fix the configuration so the credential is not in the URL.]";
4877
5489
  function noteDisplaySafeFailClosed(level) {
4878
- if (displaySafeFailClosedWarned)
4879
- return;
4880
- displaySafeFailClosedWarned = true;
4881
- 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. ` +
4882
- `The product is safe, but this deployment has an input hitting the composition seam — report it upstream. No original bytes are logged.`, "SemaRedactionFailClosed");
5490
+ recordFailOpen("server.redact.display-safe-fail-closed", `level=${level}`);
4883
5491
  }
4884
5492
  //# sourceMappingURL=redact.js.map