@sema-agent/server 7.134.0 → 7.136.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 (550) hide show
  1. package/MIGRATION.md +197 -1
  2. package/USAGE.md +147 -42
  3. package/deploy/sema-up/README.md +1 -1
  4. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  5. package/deploy/sema-up/chart/values.yaml +1 -1
  6. package/dist/adoption/plan.d.ts +1 -1
  7. package/dist/approval-ask-audit-store.d.ts +24 -24
  8. package/dist/approval-ask-machine.d.ts +2 -2
  9. package/dist/approval-card.d.ts +122 -154
  10. package/dist/approval-card.js +50 -51
  11. package/dist/approval-content-kind.d.ts +23 -5
  12. package/dist/approval-content-kind.js +8 -1
  13. package/dist/approval-deny-reasons.d.ts +1 -1
  14. package/dist/approval-gate-currency.d.ts +22 -3
  15. package/dist/approval-gate-currency.js +44 -18
  16. package/dist/approval-hmac.d.ts +7 -7
  17. package/dist/approval-policy-settlement.d.ts +2 -2
  18. package/dist/approval-reconciler.d.ts +3 -3
  19. package/dist/approval.d.ts +8 -8
  20. package/dist/approval.js +4 -2
  21. package/dist/auth-bridge.d.ts +35 -6
  22. package/dist/auth-bridge.js +20 -3
  23. package/dist/auto-mode-face.d.ts +4 -4
  24. package/dist/background-session-reap.d.ts +2 -2
  25. package/dist/bake-runner/main.d.ts +2 -2
  26. package/dist/bake-runner/runner.d.ts +4 -4
  27. package/dist/bench/s1/live-deps.d.ts +1 -1
  28. package/dist/bench/s1/run-firm.d.ts +1 -1
  29. package/dist/boot/budget-tracing.d.ts +1 -2
  30. package/dist/boot/budget-tracing.js +2 -2
  31. package/dist/boot/coordinators.d.ts +2 -2
  32. package/dist/boot/coordinators.js +2 -2
  33. package/dist/boot/deferred-sandbox-path-env.d.ts +0 -6
  34. package/dist/boot/deferred-sandbox-path-env.js +0 -5
  35. package/dist/boot/engine-lease.d.ts +5 -5
  36. package/dist/boot/execution-env.d.ts +16 -15
  37. package/dist/boot/execution-env.js +19 -21
  38. package/dist/boot/fatal-exit.d.ts +2 -2
  39. package/dist/boot/late-binds.d.ts +1 -1
  40. package/dist/boot/leader.d.ts +1 -1
  41. package/dist/boot/memory-boundary.d.ts +2 -2
  42. package/dist/boot/memory-consolidation.d.ts +15 -15
  43. package/dist/boot/org-memory.js +1 -1
  44. package/dist/boot/parked-revive-gate.d.ts +3 -3
  45. package/dist/boot/parked-revive-gate.js +3 -2
  46. package/dist/boot/reapers.d.ts +4 -4
  47. package/dist/boot/resolve-spec.d.ts +1 -1
  48. package/dist/boot/resolve-spec.js +36 -29
  49. package/dist/boot/runner-deps.d.ts +11 -11
  50. package/dist/boot/runner-deps.js +7 -4
  51. package/dist/boot/session-faces.d.ts +3 -3
  52. package/dist/boot/session-faces.js +11 -30
  53. package/dist/boot/shutdown.d.ts +5 -5
  54. package/dist/boot/side-query-lane.d.ts +2 -2
  55. package/dist/boot/stage-04-stores.d.ts +1 -1
  56. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  57. package/dist/boot/stage-05-execution-env.js +4 -4
  58. package/dist/boot/stage-06-runners.d.ts +1 -1
  59. package/dist/boot/stage-06-runners.js +0 -2
  60. package/dist/boot/stage-07-capability-layer.d.ts +1 -1
  61. package/dist/boot/stage-08-reapers.d.ts +1 -1
  62. package/dist/boot/stage-09-leader.d.ts +1 -1
  63. package/dist/boot/stage-10-http-server.d.ts +1 -1
  64. package/dist/boot/stores.js +4 -4
  65. package/dist/boot/web-admin.d.ts +17 -17
  66. package/dist/boot/web-admin.js +15 -27
  67. package/dist/boot/workflow-orchestration.js +2 -9
  68. package/dist/brain.d.ts +11 -11
  69. package/dist/brain.js +12 -3
  70. package/dist/budget.d.ts +7 -7
  71. package/dist/budget.js +4 -0
  72. package/dist/capabilities/approvals-push.d.ts +4 -4
  73. package/dist/capabilities/builtin-tools.d.ts +5 -5
  74. package/dist/capabilities/center-plugins.d.ts +5 -5
  75. package/dist/capabilities/center-plugins.js +1 -1
  76. package/dist/capabilities/center-prompts.d.ts +1 -1
  77. package/dist/capabilities/center-wiring.d.ts +1 -1
  78. package/dist/capabilities/composite-members.d.ts +1 -1
  79. package/dist/capabilities/execution-lane.d.ts +5 -5
  80. package/dist/capabilities/execution-lane.js +2 -2
  81. package/dist/capabilities/hands-lane.d.ts +5 -5
  82. package/dist/capabilities/mcp-probe.d.ts +11 -7
  83. package/dist/capabilities/sandbox-file-send.d.ts +1 -18
  84. package/dist/capabilities/sandbox-file-send.js +0 -7
  85. package/dist/capabilities/scenarios.d.ts +28 -10
  86. package/dist/capabilities/scenarios.js +15 -5
  87. package/dist/capabilities/send-user-file-tool.d.ts +1 -1
  88. package/dist/capabilities/skills.d.ts +31 -12
  89. package/dist/capabilities/skills.js +23 -17
  90. package/dist/capabilities/tool-defer.d.ts +1 -1
  91. package/dist/capability-facts.d.ts +29 -32
  92. package/dist/capability-facts.js +17 -21
  93. package/dist/center-config-tier.d.ts +1 -1
  94. package/dist/center-credential.d.ts +22 -8
  95. package/dist/center-credential.js +18 -3
  96. package/dist/checkpoint-identity.d.ts +5 -0
  97. package/dist/checkpoint-identity.js +14 -0
  98. package/dist/config-catalog.d.ts +12 -12
  99. package/dist/config-catalog.js +12 -10
  100. package/dist/config-d-migration.js +1 -1
  101. package/dist/config-invariants.d.ts +5 -5
  102. package/dist/config-invariants.js +11 -2
  103. package/dist/config-lkg.d.ts +1 -39
  104. package/dist/config-lkg.js +1 -83
  105. package/dist/config-provider.d.ts +13 -13
  106. package/dist/config-provider.js +2 -1
  107. package/dist/config-types.d.ts +95 -87
  108. package/dist/config.d.ts +26 -24
  109. package/dist/config.js +38 -18
  110. package/dist/cross-session-settings.d.ts +1 -1
  111. package/dist/delegated-session-owner.d.ts +16 -13
  112. package/dist/delegated-session-owner.js +9 -10
  113. package/dist/deployment-governance.d.ts +16 -16
  114. package/dist/deployment-shape.d.ts +127 -0
  115. package/dist/deployment-shape.js +72 -0
  116. package/dist/device-store.d.ts +1 -1
  117. package/dist/device-ws-hub.d.ts +1 -1
  118. package/dist/egress-redirect.d.ts +1 -1
  119. package/dist/elicitation.d.ts +8 -7
  120. package/dist/elicitation.js +9 -8
  121. package/dist/entry-mode-clamp-notice.d.ts +2 -2
  122. package/dist/env-facts.d.ts +10 -9
  123. package/dist/env-facts.js +1 -1
  124. package/dist/env-name-allowlist-knobs.d.ts +5 -5
  125. package/dist/exec-bounds.d.ts +2 -2
  126. package/dist/execution-lane-caps.d.ts +31 -43
  127. package/dist/execution-lane-caps.js +0 -13
  128. package/dist/file-history-capture.d.ts +9 -9
  129. package/dist/file-history-disclosure.d.ts +25 -11
  130. package/dist/file-history-disclosure.js +25 -14
  131. package/dist/fleet/drain-entry.d.ts +2 -2
  132. package/dist/fleet/fleet-bus.d.ts +22 -14
  133. package/dist/fleet/fleet-bus.js +5 -5
  134. package/dist/fleet/fleet-reconciler.d.ts +9 -9
  135. package/dist/fleet/subagent-tail-bus.d.ts +4 -4
  136. package/dist/fleet-client.d.ts +45 -14
  137. package/dist/fleet-client.js +24 -4
  138. package/dist/fleet-lease.d.ts +23 -19
  139. package/dist/fleet-lease.js +31 -23
  140. package/dist/governance-http-status.d.ts +2 -2
  141. package/dist/hooks/hook-llm.d.ts +7 -7
  142. package/dist/hooks/hook-llm.js +14 -8
  143. package/dist/hooks/hook-runner.d.ts +58 -31
  144. package/dist/hooks/hook-runner.js +37 -19
  145. package/dist/host-decision.d.ts +5 -5
  146. package/dist/host-lsp-manager.d.ts +5 -5
  147. package/dist/hosted-posture.d.ts +23 -13
  148. package/dist/hosted-posture.js +4 -9
  149. package/dist/http/admission.d.ts +2 -2
  150. package/dist/http/admission.js +20 -5
  151. package/dist/http/approval-sweeps.d.ts +1 -1
  152. package/dist/http/approval-sweeps.js +2 -1
  153. package/dist/http/checkpoint-ctx.d.ts +62 -17
  154. package/dist/http/checkpoint-ctx.js +78 -35
  155. package/dist/http/dispatch.d.ts +3 -3
  156. package/dist/http/dispatch.js +9 -9
  157. package/dist/http/durable-run-leg.js +4 -3
  158. package/dist/http/idempotency.d.ts +2 -2
  159. package/dist/http/mcp-dial-deadline.d.ts +7 -5
  160. package/dist/http/memory-compliance-failure-reply.d.ts +4 -4
  161. package/dist/http/owner-access.d.ts +4 -6
  162. package/dist/http/owner-access.js +1 -1
  163. package/dist/http/principal-gate.d.ts +7 -7
  164. package/dist/http/principal-gate.js +3 -2
  165. package/dist/http/readiness.d.ts +1 -1
  166. package/dist/http/request-guards.d.ts +4 -4
  167. package/dist/http/request-guards.js +2 -1
  168. package/dist/http/resume-ctx-read.d.ts +9 -23
  169. package/dist/http/resume-ctx-read.js +2 -3
  170. package/dist/http/resume-legs.d.ts +3 -3
  171. package/dist/http/resume-legs.js +11 -8
  172. package/dist/http/route-ctx.d.ts +29 -29
  173. package/dist/http/route-table.d.ts +9 -9
  174. package/dist/http/routes/agents-roster.d.ts +1 -1
  175. package/dist/http/routes/approvals-assistant.d.ts +5 -7
  176. package/dist/http/routes/approvals-assistant.js +101 -26
  177. package/dist/http/routes/background.d.ts +3 -3
  178. package/dist/http/routes/background.js +2 -2
  179. package/dist/http/routes/capabilities.d.ts +1 -1
  180. package/dist/http/routes/capabilities.js +16 -8
  181. package/dist/http/routes/config-catalog.d.ts +1 -1
  182. package/dist/http/routes/devices.d.ts +1 -1
  183. package/dist/http/routes/diagnostics.d.ts +3 -3
  184. package/dist/http/routes/fleet.d.ts +1 -1
  185. package/dist/http/routes/fleet.js +2 -1
  186. package/dist/http/routes/mcp-reconnect.d.ts +4 -4
  187. package/dist/http/routes/mcp-reconnect.js +3 -3
  188. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  189. package/dist/http/routes/notify-wake.d.ts +1 -1
  190. package/dist/http/routes/notify-wake.js +2 -2
  191. package/dist/http/routes/rules.d.ts +1 -1
  192. package/dist/http/routes/runs.js +23 -11
  193. package/dist/http/routes/session-sync.d.ts +1 -1
  194. package/dist/http/routes/session-sync.js +97 -46
  195. package/dist/http/routes/sessions-list.js +1 -1
  196. package/dist/http/routes/sessions.js +43 -53
  197. package/dist/http/routes/tasks.d.ts +1 -1
  198. package/dist/http/routes/trace-usage.d.ts +1 -1
  199. package/dist/http/routes/workflows.d.ts +3 -0
  200. package/dist/http/routes/workflows.js +31 -28
  201. package/dist/http/send.d.ts +5 -5
  202. package/dist/http/server.d.ts +34 -34
  203. package/dist/http/session-inherit-env.d.ts +3 -3
  204. package/dist/http/sse-lifecycle.d.ts +1 -1
  205. package/dist/http/sse-log.d.ts +2 -2
  206. package/dist/http/submit-ctx.js +1 -1
  207. package/dist/http/sync-submit-body.d.ts +1 -1
  208. package/dist/http/verify-rounds.d.ts +1 -1
  209. package/dist/http/wire-types.d.ts +33 -24
  210. package/dist/index.d.ts +1 -0
  211. package/dist/index.js +1 -0
  212. package/dist/instruction-source-notice.d.ts +2 -2
  213. package/dist/key-resolver.d.ts +3 -3
  214. package/dist/lane-home-probe.d.ts +1 -1
  215. package/dist/leader/diffout.d.ts +1 -1
  216. package/dist/leader/diffup.d.ts +2 -2
  217. package/dist/leader/endpoint.d.ts +1 -1
  218. package/dist/leader/fanout.d.ts +1 -1
  219. package/dist/leader/leader.d.ts +4 -4
  220. package/dist/leader/merge.d.ts +3 -3
  221. package/dist/leader/repair-wire.d.ts +1 -1
  222. package/dist/leader/wire.d.ts +6 -6
  223. package/dist/list-entry-display.d.ts +1 -1
  224. package/dist/lsp-evict.d.ts +1 -1
  225. package/dist/mcp-assertion-header.d.ts +1 -1
  226. package/dist/memory-layer-legacy-lock.d.ts +1 -1
  227. package/dist/memory-operator-faces.d.ts +1 -1
  228. package/dist/memory-posture.js +4 -2
  229. package/dist/memory-project-layer.d.ts +1 -1
  230. package/dist/memory-scope.d.ts +19 -19
  231. package/dist/memory-store-refusal.d.ts +4 -4
  232. package/dist/model-entry-headers.d.ts +2 -2
  233. package/dist/model-entry-refusal.d.ts +17 -17
  234. package/dist/model-provider.d.ts +10 -10
  235. package/dist/model-reach-scope.d.ts +1 -1
  236. package/dist/model-route-endpoint.d.ts +6 -6
  237. package/dist/model-route-gap.d.ts +49 -4
  238. package/dist/model-route-gap.js +34 -1
  239. package/dist/model-route-url.d.ts +5 -5
  240. package/dist/model-select.d.ts +15 -6
  241. package/dist/model-select.js +5 -0
  242. package/dist/model-unreachable-notice.d.ts +3 -3
  243. package/dist/namespaced-rule-name.d.ts +15 -5
  244. package/dist/namespaced-rule-name.js +3 -2
  245. package/dist/object-store-buckets.d.ts +13 -0
  246. package/dist/object-store-buckets.js +3 -0
  247. package/dist/observability/corrupt-read-seat.d.ts +9 -9
  248. package/dist/observability/cost-taxonomy.d.ts +1 -1
  249. package/dist/observability/err-text.d.ts +1 -1
  250. package/dist/observability/fail-open.d.ts +17 -13
  251. package/dist/observability/fail-open.js +17 -13
  252. package/dist/observability/metrics.d.ts +3 -3
  253. package/dist/observability/metrics.js +1 -1
  254. package/dist/observability/principal-context.d.ts +4 -1
  255. package/dist/observability/principal-context.js +6 -1
  256. package/dist/observability/run-terminal.d.ts +36 -36
  257. package/dist/observability/secret-env-scrub.d.ts +3 -3
  258. package/dist/observability/tool-trace.d.ts +1 -1
  259. package/dist/operator-ask.d.ts +4 -4
  260. package/dist/orchestration/workflow-agent-session-index.d.ts +3 -3
  261. package/dist/orchestration/workflow-completion-inbox.d.ts +20 -5
  262. package/dist/orchestration/workflow-completion-inbox.js +29 -7
  263. package/dist/orchestration/workflow-notify-journal.d.ts +43 -19
  264. package/dist/orchestration/workflow-notify-journal.js +17 -0
  265. package/dist/outbound-dispatcher.d.ts +4 -4
  266. package/dist/parent-watch.d.ts +4 -4
  267. package/dist/parked-decide.d.ts +20 -18
  268. package/dist/parked-decide.js +31 -11
  269. package/dist/parked-revive-surface.d.ts +7 -7
  270. package/dist/parked-revive-surface.js +2 -3
  271. package/dist/peer-directory.d.ts +5 -4
  272. package/dist/peer-directory.js +6 -6
  273. package/dist/permission-rule-list.d.ts +10 -1
  274. package/dist/permission-rule-list.js +7 -5
  275. package/dist/permission-rule-vocab.d.ts +2 -2
  276. package/dist/plugins/adoption-log-sql.d.ts +1 -1
  277. package/dist/plugins/approval-ask-store-file.d.ts +8 -1
  278. package/dist/plugins/approval-ask-store-file.js +6 -3
  279. package/dist/plugins/approval-ask-store-memory.d.ts +11 -7
  280. package/dist/plugins/approval-ask-store-memory.js +11 -7
  281. package/dist/plugins/approval-ask-store-sql.d.ts +42 -15
  282. package/dist/plugins/approval-ask-store-sql.js +21 -5
  283. package/dist/plugins/approval-exemption-store.d.ts +3 -3
  284. package/dist/plugins/approval-nonce-store.d.ts +3 -3
  285. package/dist/plugins/background-agent-store-sql.d.ts +1 -1
  286. package/dist/plugins/background-shell-support.d.ts +2 -2
  287. package/dist/plugins/blob-backend.d.ts +1 -1
  288. package/dist/plugins/blob-backend.js +2 -1
  289. package/dist/plugins/caching-session-store.d.ts +23 -32
  290. package/dist/plugins/caching-session-store.js +5 -17
  291. package/dist/plugins/checkpoint-index-columns.d.ts +130 -0
  292. package/dist/plugins/checkpoint-index-columns.js +90 -0
  293. package/dist/plugins/checkpoint-store-sql.d.ts +71 -69
  294. package/dist/plugins/checkpoint-store-sql.js +109 -127
  295. package/dist/plugins/device-store-sql.d.ts +7 -6
  296. package/dist/plugins/e2b-orphan-reclaim.d.ts +1 -1
  297. package/dist/plugins/ensure-schema-lock-policy.d.ts +1 -1
  298. package/dist/plugins/file-history-store-sql.d.ts +15 -7
  299. package/dist/plugins/file-history-store-sql.js +55 -36
  300. package/dist/plugins/file-run-store.d.ts +8 -8
  301. package/dist/plugins/fork-routing-session-store.d.ts +2 -2
  302. package/dist/plugins/fork-routing-session-store.js +1 -1
  303. package/dist/plugins/host-platform.d.ts +10 -10
  304. package/dist/plugins/image-bake-store-sql.d.ts +1 -1
  305. package/dist/plugins/image-index-sql.d.ts +1 -1
  306. package/dist/plugins/leader-run-store-sql.d.ts +1 -1
  307. package/dist/plugins/local-checkpoint-store.d.ts +10 -10
  308. package/dist/plugins/local-checkpoint-store.js +7 -7
  309. package/dist/plugins/local-session-store.d.ts +34 -38
  310. package/dist/plugins/local-session-store.js +20 -39
  311. package/dist/plugins/local-session-window.d.ts +1 -1
  312. package/dist/plugins/mailbox-store-sql.d.ts +2 -2
  313. package/dist/plugins/memory-engine-pg.d.ts +1 -1
  314. package/dist/plugins/memory-run-store.d.ts +4 -4
  315. package/dist/plugins/memory-sync-store-pg.d.ts +3 -3
  316. package/dist/plugins/one-time-migrations.d.ts +73 -54
  317. package/dist/plugins/one-time-migrations.js +131 -134
  318. package/dist/plugins/outcome-ledger-sql.d.ts +1 -1
  319. package/dist/plugins/park-commit-watch.d.ts +3 -3
  320. package/dist/plugins/permission-rule-store-file.d.ts +5 -5
  321. package/dist/plugins/permission-rule-store-file.js +12 -19
  322. package/dist/plugins/permission-rule-store-sql.d.ts +38 -4
  323. package/dist/plugins/permission-rule-store-sql.js +67 -24
  324. package/dist/plugins/pg-cost-quota.d.ts +1 -1
  325. package/dist/plugins/pg-pool.d.ts +2 -2
  326. package/dist/plugins/pg-pool.js +5 -2
  327. package/dist/plugins/pg-rate-limiter.d.ts +1 -1
  328. package/dist/plugins/pg-session-storage.d.ts +8 -20
  329. package/dist/plugins/pg-session-storage.js +14 -27
  330. package/dist/plugins/remote-env-adb.d.ts +10 -10
  331. package/dist/plugins/remote-env-device.d.ts +10 -9
  332. package/dist/plugins/remote-env-device.js +6 -2
  333. package/dist/plugins/remote-env-e2b.d.ts +3 -3
  334. package/dist/plugins/remote-env-host.d.ts +16 -16
  335. package/dist/plugins/remote-env-k8s.d.ts +26 -13
  336. package/dist/plugins/remote-env-k8s.js +24 -7
  337. package/dist/plugins/remote-env-local-docker.d.ts +2 -2
  338. package/dist/plugins/remote-env-ssh.d.ts +4 -4
  339. package/dist/plugins/remote-scratchpad.d.ts +2 -3
  340. package/dist/plugins/remote-scratchpad.js +0 -4
  341. package/dist/plugins/remote-shell.d.ts +6 -6
  342. package/dist/plugins/retention-lane-store-sql.d.ts +4 -4
  343. package/dist/plugins/retention-store-sql.d.ts +4 -4
  344. package/dist/plugins/roster-store-sql.d.ts +1 -1
  345. package/dist/plugins/run-store-sql.d.ts +8 -8
  346. package/dist/plugins/s3-answer.d.ts +15 -0
  347. package/dist/plugins/s3-answer.js +5 -0
  348. package/dist/plugins/schema-consistency.d.ts +2 -2
  349. package/dist/plugins/schema-ensure-legs.d.ts +3 -3
  350. package/dist/plugins/schema-indexes.d.ts +1 -1
  351. package/dist/plugins/schema-live-columns.d.ts +1 -1
  352. package/dist/plugins/session-event-payload.d.ts +4 -4
  353. package/dist/plugins/session-fork.d.ts +34 -0
  354. package/dist/plugins/session-fork.js +29 -0
  355. package/dist/plugins/session-ownership.js +1 -1
  356. package/dist/plugins/session-placement.d.ts +1 -1
  357. package/dist/plugins/session-policy-store-sql.d.ts +12 -12
  358. package/dist/plugins/session-store.d.ts +2 -2
  359. package/dist/plugins/session-store.js +2 -2
  360. package/dist/plugins/shared-memory-store-sql.d.ts +2 -2
  361. package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
  362. package/dist/plugins/snapshot-bucket-presence.js +35 -7
  363. package/dist/plugins/sql-driver.d.ts +8 -8
  364. package/dist/plugins/sql-errors.d.ts +40 -1
  365. package/dist/plugins/sql-errors.js +49 -1
  366. package/dist/plugins/sql-json-column.d.ts +21 -21
  367. package/dist/plugins/sql-row-helpers.d.ts +2 -2
  368. package/dist/plugins/store-backend.d.ts +17 -16
  369. package/dist/plugins/store-backend.js +3 -3
  370. package/dist/plugins/store-contracts.d.ts +21 -21
  371. package/dist/plugins/store-fault.d.ts +4 -0
  372. package/dist/plugins/store-fault.js +8 -0
  373. package/dist/plugins/task-attachment-store.d.ts +2 -2
  374. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  375. package/dist/plugins/tidb-pool.d.ts +2 -2
  376. package/dist/plugins/tidb-pool.js +10 -3
  377. package/dist/plugins/tidb-session-store.d.ts +16 -26
  378. package/dist/plugins/tidb-session-store.js +15 -27
  379. package/dist/plugins/tool-result-store-sql.d.ts +1 -1
  380. package/dist/plugins/usage-window-store-sql.d.ts +3 -3
  381. package/dist/plugins/user-input-ledger-file.d.ts +1 -1
  382. package/dist/plugins/user-input-ledger-sql.d.ts +3 -3
  383. package/dist/plugins/web-search.d.ts +19 -5
  384. package/dist/plugins/web-search.js +20 -5
  385. package/dist/{orchestration/workflow-journal-entry.d.ts → plugins/workflow-journal-payload.d.ts} +23 -32
  386. package/dist/plugins/workflow-journal-payload.js +32 -0
  387. package/dist/plugins/workflow-journal-store-sql.d.ts +28 -14
  388. package/dist/plugins/workflow-journal-store-sql.js +34 -15
  389. package/dist/plugins/workflow-run-store-sql.d.ts +4 -4
  390. package/dist/posture-source.d.ts +11 -11
  391. package/dist/principal-jwt.d.ts +6 -6
  392. package/dist/project-identity.d.ts +3 -3
  393. package/dist/project-memory.d.ts +4 -4
  394. package/dist/read-face-posture.d.ts +7 -7
  395. package/dist/request-declaration-refusal.d.ts +94 -0
  396. package/dist/request-declaration-refusal.js +78 -0
  397. package/dist/request-image-urls.d.ts +6 -0
  398. package/dist/request-image-urls.js +27 -0
  399. package/dist/request-key-closure.d.ts +6 -1
  400. package/dist/request-key-closure.js +5 -2
  401. package/dist/request-value-closure.d.ts +15 -8
  402. package/dist/request-value-closure.js +2 -0
  403. package/dist/router/route-orchestration.d.ts +11 -16
  404. package/dist/router/route-orchestration.js +2 -12
  405. package/dist/rule-entry-list.d.ts +8 -7
  406. package/dist/rule-entry-list.js +2 -9
  407. package/dist/rules-consent.d.ts +6 -6
  408. package/dist/rules-consent.js +2 -0
  409. package/dist/run-cancel-context.d.ts +5 -5
  410. package/dist/run-local.d.ts +10 -10
  411. package/dist/runs.d.ts +13 -11
  412. package/dist/runs.js +15 -1
  413. package/dist/runtime-caps-resolver.d.ts +6 -6
  414. package/dist/runtime-caps-resolver.js +1 -1
  415. package/dist/runtime-governance.d.ts +9 -9
  416. package/dist/sandbox-pkg-source.d.ts +17 -12
  417. package/dist/sandbox-pkg-source.js +14 -9
  418. package/dist/sealed-key.d.ts +22 -3
  419. package/dist/sealed-key.js +47 -4
  420. package/dist/security.d.ts +23 -26
  421. package/dist/security.js +12 -9
  422. package/dist/server-secret-env.d.ts +10 -10
  423. package/dist/session-children-retire.d.ts +1 -1
  424. package/dist/session-error-wire.d.ts +7 -7
  425. package/dist/session-incarnation.d.ts +65 -37
  426. package/dist/session-incarnation.js +30 -12
  427. package/dist/session-policy-wire.d.ts +27 -5
  428. package/dist/session-policy-wire.js +15 -1
  429. package/dist/session-records-skipped-notice.d.ts +1 -1
  430. package/dist/session-row.d.ts +3 -17
  431. package/dist/session-row.js +4 -0
  432. package/dist/session-sync-kernel.d.ts +4 -4
  433. package/dist/session-sync.d.ts +61 -6
  434. package/dist/session-sync.js +55 -10
  435. package/dist/session-titler.d.ts +7 -6
  436. package/dist/session-titler.js +8 -13
  437. package/dist/session-watch.d.ts +3 -3
  438. package/dist/session-watch.js +4 -4
  439. package/dist/sighup-idle.d.ts +2 -2
  440. package/dist/spec-fields.d.ts +4 -4
  441. package/dist/sql-engine-posture.d.ts +3 -3
  442. package/dist/store-posture.d.ts +2 -2
  443. package/dist/stored-request-tool-axes-m19.d.ts +1 -1
  444. package/dist/task-a2a.d.ts +52 -79
  445. package/dist/task-a2a.js +224 -62
  446. package/dist/task-cwd.d.ts +7 -88
  447. package/dist/task-cwd.js +2 -15
  448. package/dist/task-mcp.d.ts +13 -17
  449. package/dist/task-mcp.js +29 -49
  450. package/dist/task-settings.d.ts +30 -18
  451. package/dist/task-settings.js +33 -3
  452. package/dist/task-workflow.d.ts +0 -24
  453. package/dist/task-workflow.js +2 -1
  454. package/dist/terminal.d.ts +5 -5
  455. package/dist/tighten-only-seats.d.ts +7 -6
  456. package/dist/tighten-only-seats.js +1 -1
  457. package/dist/tool-approval.d.ts +166 -207
  458. package/dist/tool-approval.js +61 -61
  459. package/dist/tool-rule-names.d.ts +2 -2
  460. package/dist/trace/core-keyset-guard.d.ts +13 -8
  461. package/dist/trace/engine-notice-wire.d.ts +5 -5
  462. package/dist/trace/engine-notice-wire.js +1 -0
  463. package/dist/trace/exit-seat.d.ts +31 -4
  464. package/dist/trace/exit-seat.js +69 -67
  465. package/dist/trace/ledger-events.d.ts +3 -3
  466. package/dist/trace/ledger-sink.d.ts +1 -1
  467. package/dist/trace/model-segment.d.ts +3 -3
  468. package/dist/trace/project.d.ts +47 -45
  469. package/dist/trace/project.js +33 -1
  470. package/dist/trace/redact.d.ts +61 -31
  471. package/dist/trace/redact.js +394 -153
  472. package/dist/trace/self-minted-wire.d.ts +302 -14
  473. package/dist/trace/self-minted-wire.js +87 -0
  474. package/dist/trace/sema-provenance.d.ts +1 -1
  475. package/dist/trace/task-notification-facets.d.ts +4 -4
  476. package/dist/trace/wire-projection-faces.d.ts +3 -3
  477. package/dist/trace/wire-projection-faces.js +1 -1
  478. package/dist/unattended-permission-mode.d.ts +5 -5
  479. package/dist/url-credential-refusal.d.ts +52 -0
  480. package/dist/url-credential-refusal.js +14 -0
  481. package/dist/url-refusal.d.ts +16 -3
  482. package/dist/url-refusal.js +15 -2
  483. package/dist/usage-analytics.d.ts +2 -2
  484. package/dist/user-input-ledger.d.ts +23 -23
  485. package/dist/web-admin-client/apply-effective.d.ts +5 -5
  486. package/dist/web-admin-client/apply-effective.js +2 -1
  487. package/dist/web-admin-client/apply-ledger.d.ts +2 -2
  488. package/dist/web-admin-client/center-request.d.ts +2 -2
  489. package/dist/web-admin-client/facade.d.ts +5 -5
  490. package/dist/web-admin-client/facade.js +1 -1
  491. package/dist/web-admin-client/hot-keys-registry.d.ts +2 -2
  492. package/dist/web-admin-client/http-client.d.ts +4 -4
  493. package/dist/web-admin-client/mcp-revocation.d.ts +1 -1
  494. package/dist/web-admin-client/pull-state.d.ts +1 -1
  495. package/dist/web-admin-client/read-face.d.ts +2 -2
  496. package/dist/web-admin-client/read-warnings.d.ts +5 -5
  497. package/dist/web-admin-client/restart-signal.d.ts +6 -6
  498. package/dist/web-admin-client/skills-mcp.d.ts +28 -3
  499. package/dist/web-admin-client/skills-mcp.js +29 -1
  500. package/dist/web-admin-client/types.d.ts +16 -9
  501. package/dist/workspace-space.d.ts +32 -3
  502. package/dist/workspace-space.js +26 -0
  503. package/dist/write-protection.d.ts +2 -2
  504. package/package.json +5 -3
  505. package/scripts/drain-check-local.mjs +4 -2
  506. package/scripts/drain-check.mysql.sql +6 -2
  507. package/scripts/drain-check.pg.sql +6 -2
  508. package/skills/loop.md +1 -1
  509. package/skills/schedule.md +1 -1
  510. package/wire-changes/server@7.104.0.json +134 -0
  511. package/wire-changes/server@7.105.0.json +53 -0
  512. package/wire-changes/server@7.106.0.json +475 -0
  513. package/wire-changes/server@7.107.0.json +64 -0
  514. package/wire-changes/server@7.108.0.json +153 -0
  515. package/wire-changes/server@7.109.0.json +46 -0
  516. package/wire-changes/server@7.110.0.json +90 -0
  517. package/wire-changes/server@7.111.0.json +86 -0
  518. package/wire-changes/server@7.112.0.json +60 -0
  519. package/wire-changes/server@7.113.0.json +26 -0
  520. package/wire-changes/server@7.114.0.json +26 -0
  521. package/wire-changes/server@7.115.0.json +97 -0
  522. package/wire-changes/server@7.116.0.json +218 -0
  523. package/wire-changes/server@7.116.1.json +26 -0
  524. package/wire-changes/server@7.117.0.json +65 -0
  525. package/wire-changes/server@7.118.0.json +129 -0
  526. package/wire-changes/server@7.119.0.json +65 -0
  527. package/wire-changes/server@7.120.0.json +89 -0
  528. package/wire-changes/server@7.121.0.json +219 -0
  529. package/wire-changes/server@7.122.0.json +54 -0
  530. package/wire-changes/server@7.123.0.json +26 -0
  531. package/wire-changes/server@7.124.0.json +336 -0
  532. package/wire-changes/server@7.124.1.json +26 -0
  533. package/wire-changes/server@7.125.0.json +111 -0
  534. package/wire-changes/server@7.126.0.json +285 -0
  535. package/wire-changes/server@7.127.0.json +77 -0
  536. package/wire-changes/server@7.127.1.json +26 -0
  537. package/wire-changes/server@7.127.2.json +53 -0
  538. package/wire-changes/server@7.128.0.json +26 -0
  539. package/wire-changes/server@7.129.0.json +106 -0
  540. package/wire-changes/server@7.129.1.json +26 -0
  541. package/wire-changes/server@7.130.0.json +26 -0
  542. package/wire-changes/server@7.131.0.json +118 -0
  543. package/wire-changes/server@7.132.0.json +226 -0
  544. package/wire-changes/server@7.133.0.json +1155 -0
  545. package/wire-changes/server@7.134.0.json +107 -0
  546. package/wire-changes/server@7.135.0.json +174 -0
  547. package/wire-changes/server@7.136.0.json +448 -0
  548. package/dist/http/run-declared-families.d.ts +0 -16
  549. package/dist/http/run-declared-families.js +0 -12
  550. package/dist/orchestration/workflow-journal-entry.js +0 -27
@@ -1,5 +1,5 @@
1
1
  import { displayUrlOf, stripFormatCharacters } from "@sema-agent/core";
2
- import { isHierarchicalUrl } from "@sema-agent/settings-schema";
2
+ import { CONTROL_AND_BIDI_CHARS, CREDENTIAL_PARAM_NAMES, 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) {
@@ -28,43 +28,91 @@ export function mapNormalizedToOriginal(text, normalized) {
28
28
  map[normalized.length] = text.length;
29
29
  return map;
30
30
  }
31
- function deleteSpelledFormatCharacters(s) {
31
+ const SPELLED_FORMAT = 1, SPELLED_CONTROL = 2, RAW_CONTROL = 4, RAW_WHITESPACE = 8;
32
+ const INVISIBLE_UNIT_CLASS = (() => {
33
+ const inSet = new RegExp(`[${CONTROL_AND_BIDI_CHARS}]`);
34
+ const table = new Uint8Array(0x10000);
35
+ for (let unit = 0; unit < table.length; unit++) {
36
+ const ch = String.fromCharCode(unit);
37
+ if (inSet.test(ch))
38
+ table[unit] = /\s/.test(ch) ? 2 : 1;
39
+ }
40
+ return table;
41
+ })();
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");
55
+ })();
56
+ function collectRawControlCuts(s, allowed) {
57
+ const cuts = [];
58
+ let present = 0;
59
+ if ((allowed & (RAW_CONTROL | RAW_WHITESPACE)) === 0)
60
+ 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;
68
+ }
69
+ return { cuts, present };
70
+ }
71
+ function collectSpelledCuts(s) {
72
+ const cuts = [];
73
+ let present = 0;
32
74
  let i = s.indexOf("\\");
33
75
  if (i < 0)
34
- return { text: s, at: undefined, cum: undefined };
76
+ return { cuts, present };
35
77
  const n = s.length;
36
78
  const read = { end: 0 };
37
- let cuts;
38
79
  while (i >= 0) {
39
80
  let body = i + 1;
40
81
  while (body < n && s.charCodeAt(body) === CH_BACKSLASH)
41
82
  body += 1;
42
83
  const cp = readSpelledCodePoint(s, body, read);
43
- if (cp >= 0 && isFormatCodePoint(cp)) {
44
- (cuts ??= []).push(i, read.end);
84
+ const cls = cp < 0 ? 0 : isFormatCodePoint(cp) ? SPELLED_FORMAT : cp < 0x10000 && INVISIBLE_UNIT_CLASS[cp] === 1 ? SPELLED_CONTROL : 0;
85
+ if (cls !== 0) {
86
+ cuts.push(i, read.end, cls);
87
+ present |= cls;
45
88
  body = read.end;
46
89
  }
47
90
  i = s.indexOf("\\", body);
48
91
  }
49
- if (cuts === undefined)
50
- return { text: s, at: undefined, cum: undefined };
51
- const count = cuts.length / 2;
52
- const at = new Int32Array(count);
53
- const cum = new Int32Array(count);
92
+ return { cuts, present };
93
+ }
94
+ function deleteInvisibleCharacters(s, all, mask) {
95
+ const atList = [];
96
+ const cumList = [];
54
97
  const parts = [];
55
98
  let from = 0;
56
99
  let removed = 0;
57
- for (let k = 0; k < count; k++) {
58
- const a = cuts[2 * k];
59
- const b = cuts[2 * k + 1];
100
+ const { cuts } = all;
101
+ for (let k = 0; k < cuts.length; k += 3) {
102
+ if ((cuts[k + 2] & mask) === 0)
103
+ continue;
104
+ const a = cuts[k];
105
+ const b = cuts[k + 1];
60
106
  parts.push(s.slice(from, a));
61
- at[k] = a - removed;
107
+ atList.push(a - removed);
62
108
  removed += b - a;
63
- cum[k] = removed;
109
+ cumList.push(removed);
64
110
  from = b;
65
111
  }
112
+ if (atList.length === 0)
113
+ return { text: s, at: undefined, cum: undefined };
66
114
  parts.push(s.slice(from));
67
- return { text: parts.join(""), at, cum };
115
+ return { text: parts.join(""), at: Int32Array.from(atList), cum: Int32Array.from(cumList) };
68
116
  }
69
117
  function viewIndexInInput(fold, v) {
70
118
  const at = fold.at;
@@ -413,8 +461,9 @@ let urlSegmentRuns = 0;
413
461
  let urlSegmentReplays = 0;
414
462
  let ssh2ScanBytes = 0;
415
463
  let mappedUnits = 0;
464
+ let readings = 0;
416
465
  export function armPoolBudgetForTest() {
417
- return { spanCount: poolSpanCount, coveredPositions, headScreenHits, fullYCalls, armCount: poolArmCount, boundaryArmCount: poolBoundaryArmCount, forensicLevels, forensicRuns, forensicSkips, urlSegmentRuns, urlSegmentReplays, ssh2ScanBytes, mappedUnits };
466
+ return { spanCount: poolSpanCount, coveredPositions, headScreenHits, fullYCalls, armCount: poolArmCount, boundaryArmCount: poolBoundaryArmCount, forensicLevels, forensicRuns, forensicSkips, urlSegmentRuns, urlSegmentReplays, ssh2ScanBytes, mappedUnits, readings };
418
467
  }
419
468
  export function resetArmPoolBudgetForTest() {
420
469
  poolSpanCount = 0;
@@ -428,6 +477,7 @@ export function resetArmPoolBudgetForTest() {
428
477
  urlSegmentReplays = 0;
429
478
  ssh2ScanBytes = 0;
430
479
  mappedUnits = 0;
480
+ readings = 0;
431
481
  }
432
482
  const unitBit = (t, c) => (t[c >>> 5] >>> (c & 31)) & 1;
433
483
  let codeUnitDomain;
@@ -786,14 +836,19 @@ const CREDENTIAL_NAME_ANCHORS = {
786
836
  "x-amz-signature": "structure-only", "x-amz-security-token": "structure-only", "x-amz-credential": "structure-only",
787
837
  sas: "structure-only",
788
838
  };
789
- const CREDENTIAL_NAME_WORDS = Object.keys(CREDENTIAL_NAME_ANCHORS);
790
839
  const CREDENTIAL_NAME_ALT = (() => {
791
- for (const w of CREDENTIAL_NAME_WORDS) {
840
+ for (const w of CREDENTIAL_PARAM_NAMES) {
792
841
  if (!/^[a-z][a-z0-9_-]*$/.test(w))
793
842
  throw new Error(`redact: credential name ${JSON.stringify(w)} must be lowercase [a-z0-9_-] (the alternation is built without escaping)`);
794
843
  }
795
- return [...CREDENTIAL_NAME_WORDS].sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).join("|");
844
+ return [...CREDENTIAL_PARAM_NAMES].sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).join("|");
796
845
  })();
846
+ const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_PARAM_NAMES.map((w) => w.length));
847
+ const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
848
+ function credentialNameAnchor(name) {
849
+ return CREDENTIAL_ANCHOR_OF.get(asciiLower(name));
850
+ }
851
+ const CREDENTIAL_ANCHOR_OF = new Map(Object.entries(CREDENTIAL_NAME_ANCHORS));
797
852
  const STRUCTURAL_SECRET_PATTERNS = [
798
853
  [/-----BEGIN(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
799
854
  [/---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----[\s\S]{0,8192}?---- END SSH2 ENCRYPTED PRIVATE KEY ----/g, "«redacted:private-key»"],
@@ -1029,14 +1084,17 @@ function isPemFenceLabel(s, from, to) {
1029
1084
  return false;
1030
1085
  if (!s.startsWith(PEM_KEY_WORD, end - PEM_KEY_WORD.length))
1031
1086
  return false;
1087
+ return !pemLabelDashRuleBroken(s, from, to);
1088
+ }
1089
+ function pemLabelDashRuleBroken(s, from, to) {
1032
1090
  if (s.charCodeAt(from) === 45)
1033
- return false;
1091
+ return true;
1034
1092
  if (s.charCodeAt(from + 1) === 45)
1035
- return false;
1093
+ return true;
1036
1094
  for (let i = from + 1; i < to; i++)
1037
1095
  if (s.charCodeAt(i) === 45 && s.charCodeAt(i + 1) === 45)
1038
- return false;
1039
- return true;
1096
+ return true;
1097
+ return false;
1040
1098
  }
1041
1099
  const PEM_FENCE_OPEN = "-----BEGIN";
1042
1100
  const PEM_FENCE_CLOSE = "-----";
@@ -1081,7 +1139,7 @@ function unitCountWithin(scan, from, to, max) {
1081
1139
  }
1082
1140
  return n <= max;
1083
1141
  }
1084
- function collectSsh2FenceSpans(scan, text, span) {
1142
+ function collectSsh2FenceSpans(scan, text, span, isHead) {
1085
1143
  let cachedFrom = 0;
1086
1144
  let cachedAt;
1087
1145
  const nextClose = (from) => {
@@ -1107,6 +1165,10 @@ function collectSsh2FenceSpans(scan, text, span) {
1107
1165
  continue;
1108
1166
  }
1109
1167
  const bodyFrom = fence + SSH2_FENCE_OPEN.length;
1168
+ if (isHead && unitCountWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
1169
+ span(fence, text.length);
1170
+ return;
1171
+ }
1110
1172
  const close = nextClose(bodyFrom);
1111
1173
  if (close < 0)
1112
1174
  return;
@@ -1121,11 +1183,20 @@ function collectSsh2FenceSpans(scan, text, span) {
1121
1183
  }
1122
1184
  const PEM_END_OPEN = "-----END";
1123
1185
  const PEM_FENCE_BODY_MAX = 8192;
1186
+ const PEM_LINE_INVALID = -1;
1187
+ const PEM_LINE_OPEN = -2;
1124
1188
  function pemFenceLineEnd(text, labelFrom) {
1125
1189
  const close = text.indexOf(PEM_FENCE_CLOSE, labelFrom);
1126
- return close >= 0 && isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : -1;
1190
+ if (close < 0) {
1191
+ let to = text.length;
1192
+ while (to > labelFrom && text.charCodeAt(to - 1) === 45)
1193
+ to -= 1;
1194
+ const stillPossible = text.length - to >= 2 ? isPemFenceLabel(text, labelFrom, to) : !pemLabelDashRuleBroken(text, labelFrom, to);
1195
+ return stillPossible ? PEM_LINE_OPEN : PEM_LINE_INVALID;
1196
+ }
1197
+ return isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : PEM_LINE_INVALID;
1127
1198
  }
1128
- function collectPemFenceSpans(text, span) {
1199
+ function collectPemFenceSpans(text, span, isHead) {
1129
1200
  let endFrom = -1;
1130
1201
  let endAt = -1;
1131
1202
  let endAfter = -1;
@@ -1145,13 +1216,17 @@ function collectPemFenceSpans(text, span) {
1145
1216
  if (fence < 0)
1146
1217
  return;
1147
1218
  const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
1148
- if (bodyFrom < 0) {
1219
+ if (bodyFrom === PEM_LINE_INVALID) {
1149
1220
  from = fence + 1;
1150
1221
  continue;
1151
1222
  }
1152
- earliestEndFence(bodyFrom);
1153
- if (endAt < 0)
1223
+ if (bodyFrom !== PEM_LINE_OPEN)
1224
+ earliestEndFence(bodyFrom);
1225
+ if (bodyFrom === PEM_LINE_OPEN || endAt < 0) {
1226
+ if (isHead)
1227
+ span(fence, text.length);
1154
1228
  return;
1229
+ }
1155
1230
  if (endAt - bodyFrom > PEM_FENCE_BODY_MAX) {
1156
1231
  from = fence + 1;
1157
1232
  continue;
@@ -1192,9 +1267,19 @@ function collectPemFragmentSpans(scan, text, json, span) {
1192
1267
  cur = next;
1193
1268
  }
1194
1269
  }
1195
- function collectHeadlessPpkSpans(scan, text, span) {
1270
+ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1196
1271
  const KEY = "Private-Lines: ";
1197
1272
  const n = text.length;
1273
+ const LINE_NONE = -1;
1274
+ const LINE_OPEN = -2;
1275
+ const openAt = (i) => {
1276
+ if (!isHead)
1277
+ return false;
1278
+ let k = i;
1279
+ while (k < n && opaqueUnitEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
1280
+ k += 1;
1281
+ return k >= n;
1282
+ };
1198
1283
  const isB64 = (c) => (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 43 || c === 47 || c === 61;
1199
1284
  const b64At = (i) => opaqueUnitEnd(scan, i) < 0 && isB64(text.charCodeAt(i));
1200
1285
  const skipHs = (i) => { while (i < n && opaqueUnitEnd(scan, i) < 0 && (text.charCodeAt(i) === 32 || text.charCodeAt(i) === 9))
@@ -1233,7 +1318,9 @@ function collectHeadlessPpkSpans(scan, text, span) {
1233
1318
  seen = true;
1234
1319
  q = skipHs(q);
1235
1320
  }
1236
- return seen ? q : -1;
1321
+ if (openAt(q))
1322
+ return LINE_OPEN;
1323
+ return seen ? q : LINE_NONE;
1237
1324
  };
1238
1325
  const escapedNewlineLen = (i) => {
1239
1326
  if (opaqueUnitEnd(scan, i) >= 0)
@@ -1248,13 +1335,35 @@ function collectHeadlessPpkSpans(scan, text, span) {
1248
1335
  };
1249
1336
  const atLineEnd = (i) => {
1250
1337
  const b = skipHs(i);
1251
- if (b >= n)
1338
+ if (b >= n || openAt(b))
1252
1339
  return true;
1253
1340
  if (opaqueUnitEnd(scan, b) >= 0)
1254
1341
  return false;
1255
1342
  const c = text.charCodeAt(b);
1256
1343
  return c === 10 || c === 13 || c === 34 || escapedNewlineLen(b) > 0;
1257
1344
  };
1345
+ const bodyLineAt = (from) => {
1346
+ const q0 = skipNewlines(from);
1347
+ if (q0 < 0)
1348
+ return q0;
1349
+ let q = q0;
1350
+ const mk = opaqueUnitEnd(scan, q) < 0 ? text.charCodeAt(q) : -1;
1351
+ if (mk === 43 || mk === 45)
1352
+ q = skipHs(q + 1);
1353
+ let r = q;
1354
+ while (r < n && b64At(r))
1355
+ r += 1;
1356
+ if (r - q < 4) {
1357
+ if (isHead && r >= n)
1358
+ return LINE_OPEN;
1359
+ r = q0;
1360
+ while (r < n && b64At(r))
1361
+ r += 1;
1362
+ if (r - q0 < 4)
1363
+ return LINE_NONE;
1364
+ }
1365
+ return atLineEnd(r) ? r : LINE_NONE;
1366
+ };
1258
1367
  let search = 0;
1259
1368
  for (;;) {
1260
1369
  const at = text.indexOf(KEY, search);
@@ -1268,36 +1377,21 @@ function collectHeadlessPpkSpans(scan, text, span) {
1268
1377
  const d0 = p;
1269
1378
  while (p < n && opaqueUnitEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
1270
1379
  p += 1;
1271
- if (p === d0) {
1380
+ if (p === d0 && p < n) {
1272
1381
  search = at + KEY.length;
1273
1382
  continue;
1274
1383
  }
1275
- let end = p;
1276
- let lines = 0;
1277
- for (;;) {
1278
- const q0 = skipNewlines(end);
1279
- if (q0 === -1)
1384
+ let end = -1;
1385
+ for (let from = p;;) {
1386
+ const r = bodyLineAt(from);
1387
+ if (r === LINE_NONE)
1280
1388
  break;
1281
- let q = q0;
1282
- const mk = opaqueUnitEnd(scan, q) < 0 ? text.charCodeAt(q) : -1;
1283
- if (mk === 43 || mk === 45)
1284
- q = skipHs(q + 1);
1285
- let r = q;
1286
- while (r < n && b64At(r))
1287
- r += 1;
1288
- if (r - q < 4) {
1289
- r = q0;
1290
- while (r < n && b64At(r))
1291
- r += 1;
1292
- if (r - q0 < 4)
1293
- break;
1294
- }
1295
- if (!atLineEnd(r))
1389
+ end = r === LINE_OPEN ? n : r;
1390
+ if (r === LINE_OPEN)
1296
1391
  break;
1297
- lines += 1;
1298
- end = r;
1392
+ from = r;
1299
1393
  }
1300
- if (lines === 0) {
1394
+ if (end < 0) {
1301
1395
  search = p;
1302
1396
  continue;
1303
1397
  }
@@ -1386,9 +1480,7 @@ const isRegexSpace = (c) => c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c =
1386
1480
  const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
1387
1481
  const isControlCode = (c) => c < 0x20 || c === 0x7f;
1388
1482
  const TOKEN_UNIT_Y = new RegExp(REDACTION_TOKEN_RE_SRC, "y");
1389
- const CREDENTIAL_PARAM_Y = new RegExp(`(?:${CREDENTIAL_NAME_ALT})=`, "iy");
1390
- const CREDENTIAL_NAME_SET = new Set(CREDENTIAL_NAME_WORDS);
1391
- const CREDENTIAL_NAME_SCAN_MAX = Math.max(...CREDENTIAL_NAME_WORDS.map((w) => w.length)) * 3 + 1;
1483
+ const CREDENTIAL_NAME_SCAN_MAX = CREDENTIAL_NAME_MAX * 3 + 1;
1392
1484
  function decodeAsciiName(name) {
1393
1485
  let out = "";
1394
1486
  for (let i = 0; i < name.length; i++) {
@@ -1409,9 +1501,6 @@ function decodeAsciiName(name) {
1409
1501
  return out;
1410
1502
  }
1411
1503
  function credentialParamValueStart(text, p) {
1412
- CREDENTIAL_PARAM_Y.lastIndex = p;
1413
- if (CREDENTIAL_PARAM_Y.test(text))
1414
- return CREDENTIAL_PARAM_Y.lastIndex;
1415
1504
  const limit = Math.min(text.length, p + CREDENTIAL_NAME_SCAN_MAX);
1416
1505
  let hasPct = false;
1417
1506
  for (let i = p; i < limit; i++) {
@@ -1420,12 +1509,14 @@ function credentialParamValueStart(text, p) {
1420
1509
  hasPct = true;
1421
1510
  continue;
1422
1511
  }
1423
- if (c !== CH_EQ)
1424
- continue;
1425
- if (!hasPct)
1512
+ if (c === CH_EQ) {
1513
+ if (i === p)
1514
+ return -1;
1515
+ const name = hasPct ? decodeAsciiName(text.slice(p, i)) : text.slice(p, i);
1516
+ return name !== undefined && credentialNameAnchor(name) !== undefined ? i + 1 : -1;
1517
+ }
1518
+ if (!(isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS))
1426
1519
  return -1;
1427
- const decoded = decodeAsciiName(text.slice(p, i));
1428
- return decoded !== undefined && CREDENTIAL_NAME_SET.has(decoded.toLowerCase()) ? i + 1 : -1;
1429
1520
  }
1430
1521
  return -1;
1431
1522
  }
@@ -1700,10 +1791,12 @@ function scanUrlRun(scan, text, i, kind, json) {
1700
1791
  noteEvidenceRead(scan, i);
1701
1792
  return i;
1702
1793
  }
1703
- function matchUserinfoAt(scan, text, p, json) {
1794
+ function matchUserinfoAt(scan, text, p, json, isHead) {
1704
1795
  const probeEnd = scanUrlRun(scan, text, p, "plain-user", json);
1705
1796
  const keyId = probeEnd > p && text.charCodeAt(probeEnd) === 58 && credentialSpansCover(scan, p, probeEnd);
1706
- scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
1797
+ const runEnd = scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
1798
+ if (isHead && runEnd >= text.length && runEnd > p)
1799
+ return runEnd;
1707
1800
  const at = scan.lastAt;
1708
1801
  if (at <= p)
1709
1802
  return null;
@@ -1721,7 +1814,7 @@ function growUrlSegments(list) {
1721
1814
  list.a = bigger;
1722
1815
  return true;
1723
1816
  }
1724
- function collectUrlCredentialSpans(scan, text, json, span) {
1817
+ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
1725
1818
  const dirty = scan.dirty;
1726
1819
  const hr = scan.headsRead;
1727
1820
  const hw = scan.headsWrite;
@@ -1757,7 +1850,7 @@ function collectUrlCredentialSpans(scan, text, json, span) {
1757
1850
  else {
1758
1851
  scan.footLo = FOOT_NONE;
1759
1852
  scan.footHi = -1;
1760
- at = matchUserinfoAt(scan, text, start, json) ?? -1;
1853
+ at = matchUserinfoAt(scan, text, start, json, isHead) ?? -1;
1761
1854
  lo = scan.footLo;
1762
1855
  hi = scan.footHi;
1763
1856
  urlSegmentRuns += 1;
@@ -1834,24 +1927,19 @@ function collectUrlCredentialSpans(scan, text, json, span) {
1834
1927
  scan.footHi = armHi;
1835
1928
  }
1836
1929
  const JSON_FIELD_ANCHOR = '"';
1837
- const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_NAME_WORDS.map((w) => w.length));
1838
1930
  function jsonSecretFieldValueQuoteAt(text, p) {
1839
1931
  const n = text.length;
1840
- let name = "";
1841
1932
  let i = p + 1;
1842
1933
  for (; i < n; i++) {
1843
- let c = text.charCodeAt(i);
1934
+ const c = text.charCodeAt(i);
1844
1935
  if (c === CH_QUOTE)
1845
1936
  break;
1846
1937
  if (i - p > CREDENTIAL_NAME_MAX)
1847
1938
  return -1;
1848
- if (c >= CH_A && c <= CH_Z)
1849
- c += 32;
1850
- if (!((c >= CH_a && c <= CH_z) || (c >= CH_0 && c <= CH_9) || c === CH_UNDERSCORE || c === CH_MINUS))
1939
+ if (!(isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS))
1851
1940
  return -1;
1852
- name += String.fromCharCode(c);
1853
1941
  }
1854
- if (i >= n || !CREDENTIAL_NAME_SET.has(name))
1942
+ if (i >= n || i === p + 1 || credentialNameAnchor(text.slice(p + 1, i)) === undefined)
1855
1943
  return -1;
1856
1944
  i += 1;
1857
1945
  while (i < n && isRegexSpace(text.charCodeAt(i)))
@@ -1863,7 +1951,7 @@ function jsonSecretFieldValueQuoteAt(text, p) {
1863
1951
  i += 1;
1864
1952
  return text.charCodeAt(i) === CH_QUOTE ? i : -1;
1865
1953
  }
1866
- function jsonStringEndAt(text, q) {
1954
+ function jsonStringEndAt(text, q, isHead) {
1867
1955
  const n = text.length;
1868
1956
  for (let i = q + 1; i < n;) {
1869
1957
  const c = text.charCodeAt(i);
@@ -1874,16 +1962,16 @@ function jsonStringEndAt(text, q) {
1874
1962
  i += 2;
1875
1963
  continue;
1876
1964
  }
1877
- return -1;
1965
+ return isHead && i + 1 >= n ? n : -1;
1878
1966
  }
1879
1967
  i += 1;
1880
1968
  }
1881
- return -1;
1969
+ return isHead ? n : -1;
1882
1970
  }
1883
- function collectJsonSecretFieldSpans(text, span) {
1971
+ function collectJsonSecretFieldSpans(text, span, isHead) {
1884
1972
  for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
1885
1973
  const q = jsonSecretFieldValueQuoteAt(text, p);
1886
- const end = q < 0 ? -1 : jsonStringEndAt(text, q);
1974
+ const end = q < 0 ? -1 : jsonStringEndAt(text, q, isHead);
1887
1975
  if (end < 0) {
1888
1976
  p = text.indexOf(JSON_FIELD_ANCHOR, p + 1);
1889
1977
  continue;
@@ -1896,7 +1984,6 @@ const STRONG_AUTH_SCHEMES = new Set(["basic", "bearer"]);
1896
1984
  const STRONG_AUTH_SCHEME_MAX = Math.max(...[...STRONG_AUTH_SCHEMES].map((w) => w.length));
1897
1985
  const ABSENT_OR_BOOLEAN_WORDS = new Set(["null", "nil", "none", "undefined", "true", "false", "yes", "no"]);
1898
1986
  const ABSENT_OR_BOOLEAN_MAX = Math.max(...[...ABSENT_OR_BOOLEAN_WORDS].map((w) => w.length));
1899
- const CREDENTIAL_ANCHOR_OF = new Map(Object.entries(CREDENTIAL_NAME_ANCHORS));
1900
1987
  const CH_GRAVE = 96, CH_APOS = 39, CH_DOT = 46, CH_COMMA = 44, CH_SEMI = 59, CH_DOLLAR = 36;
1901
1988
  const CH_LPAREN = 40, CH_RPAREN = 41, CH_RBRACKET = 93, CH_LBRACE = 123, CH_RBRACE = 125, CH_LT = 60, CH_GT = 62;
1902
1989
  const CH_QMARK = 63, CH_AMP = 38, CH_HASH = 35;
@@ -2021,7 +2108,7 @@ function anchorHeadAt(text, op, json) {
2021
2108
  return undefined;
2022
2109
  before = j - openLen;
2023
2110
  }
2024
- const strength = CREDENTIAL_ANCHOR_OF.get(text.slice(nameStart, nameEnd).toLowerCase());
2111
+ const strength = credentialNameAnchor(text.slice(nameStart, nameEnd));
2025
2112
  if (strength === undefined)
2026
2113
  return undefined;
2027
2114
  if (!afterEscape && runStart === j + 1 && quote === 0 && j >= 0 && text.charCodeAt(j) === CH_SLASH)
@@ -2037,7 +2124,7 @@ function anchorHeadAt(text, op, json) {
2037
2124
  }
2038
2125
  return { strength, quotedName: quote !== 0, spacedLeft, listSep };
2039
2126
  }
2040
- function quotedValueEnd(text, cs, quote, json) {
2127
+ function quotedValueEnd(text, cs, quote, json, isHead) {
2041
2128
  const n = text.length;
2042
2129
  let i = cs;
2043
2130
  while (i < n) {
@@ -2046,18 +2133,18 @@ function quotedValueEnd(text, cs, quote, json) {
2046
2133
  if (ch === quote)
2047
2134
  return i;
2048
2135
  if (ch === JSON_STRING_END)
2049
- break;
2136
+ return -1;
2050
2137
  i += unitLenOf(unit);
2051
2138
  if (ch === CH_BACKSLASH && quote !== JSON_STRING_END) {
2052
2139
  if (i >= n)
2053
2140
  break;
2054
2141
  const escaped = readAnchorUnit(text, i, json);
2055
2142
  if (unitChOf(escaped) === JSON_STRING_END)
2056
- break;
2143
+ return -1;
2057
2144
  i += unitLenOf(escaped);
2058
2145
  }
2059
2146
  }
2060
- return -1;
2147
+ return isHead ? n : -1;
2061
2148
  }
2062
2149
  function bareValueEnd(text, s, json, listSep) {
2063
2150
  const n = text.length;
@@ -2102,7 +2189,7 @@ function bareValueEnd(text, s, json, listSep) {
2102
2189
  }
2103
2190
  return e;
2104
2191
  }
2105
- function literalValueAt(text, s, json, declaredIfBare, listSep) {
2192
+ function literalValueAt(text, s, json, declaredIfBare, listSep, isHead) {
2106
2193
  const n = text.length;
2107
2194
  if (s >= n)
2108
2195
  return undefined;
@@ -2115,7 +2202,7 @@ function literalValueAt(text, s, json, declaredIfBare, listSep) {
2115
2202
  return undefined;
2116
2203
  if (!isAsciiAlnumCode(unitChOf(readAnchorUnit(text, vs, json))))
2117
2204
  return undefined;
2118
- const close = quotedValueEnd(text, vs, first, json);
2205
+ const close = quotedValueEnd(text, vs, first, json, isHead);
2119
2206
  if (close >= 0)
2120
2207
  return { start: vs, end: close, quoted: true };
2121
2208
  }
@@ -2183,7 +2270,7 @@ function schemeValueAt(text, s, json) {
2183
2270
  return undefined;
2184
2271
  return { start: vs, end: e, quoted: false };
2185
2272
  }
2186
- function collectAnchoredValueSpans(text, json, span) {
2273
+ function collectAnchoredValueSpans(text, json, span, isHead) {
2187
2274
  const n = text.length;
2188
2275
  let resume = 0;
2189
2276
  let nextEq = text.indexOf("=");
@@ -2224,10 +2311,10 @@ function collectAnchoredValueSpans(text, json, span) {
2224
2311
  v = schemeValueAt(text, s, json);
2225
2312
  break;
2226
2313
  case "declarative":
2227
- v = literalValueAt(text, s, json, head.quotedName || (!head.spacedLeft && !spacedRight), head.listSep);
2314
+ v = literalValueAt(text, s, json, head.quotedName || (!head.spacedLeft && !spacedRight), head.listSep, isHead);
2228
2315
  break;
2229
2316
  case "any-form":
2230
- v = literalValueAt(text, s, json, true, head.listSep);
2317
+ v = literalValueAt(text, s, json, true, head.listSep, isHead);
2231
2318
  break;
2232
2319
  default: {
2233
2320
  const unhandled = head.strength;
@@ -2243,7 +2330,7 @@ function collectAnchoredValueSpans(text, json, span) {
2243
2330
  export function structuralScannerForTest(arm) {
2244
2331
  const collect = (scan) => (text) => {
2245
2332
  const out = [];
2246
- scan(text, (start, end) => out.push([start, end]));
2333
+ scan(text, (start, end) => out.push([start, end]), false);
2247
2334
  return out;
2248
2335
  };
2249
2336
  switch (arm) {
@@ -2256,7 +2343,7 @@ export function structuralScannerForTest(arm) {
2256
2343
  let ppkScanBytes = 0;
2257
2344
  export function ppkScanBytesForTest() { return ppkScanBytes; }
2258
2345
  export function resetPpkScanBytesForTest() { ppkScanBytes = 0; }
2259
- function collectPpkContainerSpans(scan, text, record, span) {
2346
+ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
2260
2347
  const HEAD = "PuTTY-User-Key-File-";
2261
2348
  const MAC = "Private-MAC: ";
2262
2349
  const HASH = "Private-Hash: ";
@@ -2272,9 +2359,8 @@ function collectPpkContainerSpans(scan, text, record, span) {
2272
2359
  cache.set(needle, v);
2273
2360
  return v;
2274
2361
  };
2275
- const isVersionedHead = (at) => {
2362
+ const versionEnd = (at) => {
2276
2363
  let p = at + HEAD.length;
2277
- const start = p;
2278
2364
  while (p < text.length && opaqueUnitEnd(scan, p) < 0) {
2279
2365
  const c = text.charCodeAt(p);
2280
2366
  if (c >= 48 && c <= 57)
@@ -2282,7 +2368,15 @@ function collectPpkContainerSpans(scan, text, record, span) {
2282
2368
  else
2283
2369
  break;
2284
2370
  }
2285
- return p > start && text.startsWith(": ", p);
2371
+ return p;
2372
+ };
2373
+ const isVersionedHead = (at) => {
2374
+ const p = versionEnd(at);
2375
+ return p > at + HEAD.length && text.startsWith(": ", p);
2376
+ };
2377
+ const isOpenHead = (at) => {
2378
+ const p = versionEnd(at);
2379
+ return p >= text.length || (p === text.length - 1 && p > at + HEAD.length && text.charCodeAt(p) === 58);
2286
2380
  };
2287
2381
  const nextVersionedHead = (from) => {
2288
2382
  let p = find(HEAD, from);
@@ -2305,7 +2399,7 @@ function collectPpkContainerSpans(scan, text, record, span) {
2305
2399
  while (at !== Number.POSITIVE_INFINITY) {
2306
2400
  let pos = at + HEAD.length + 1;
2307
2401
  let end = text.length;
2308
- let ateToEnd = true;
2402
+ let ateToEnd = !isHead;
2309
2403
  for (;;) {
2310
2404
  const mac = find(MAC, pos);
2311
2405
  const hash = find(HASH, pos);
@@ -2338,15 +2432,21 @@ function collectPpkContainerSpans(scan, text, record, span) {
2338
2432
  }
2339
2433
  }
2340
2434
  }
2435
+ const undecided = isHead && limit === Number.POSITIVE_INFINITY;
2341
2436
  ateToEnd = straggler < limit || !gapBlank;
2342
- end = ateToEnd ? text.length : hexEnd;
2437
+ end = ateToEnd || undecided ? text.length : hexEnd;
2343
2438
  break;
2344
2439
  }
2345
- if (ateToEnd && record)
2346
- recordFailOpen("server.redact.ppk-eat-to-end", `from=${at} len=${text.length}`);
2440
+ if (ateToEnd && eat !== undefined && eat.from < 0) {
2441
+ eat.from = at;
2442
+ eat.len = text.length;
2443
+ }
2347
2444
  span(at, end);
2348
2445
  at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
2349
2446
  }
2447
+ const last = isHead ? text.lastIndexOf(HEAD) : -1;
2448
+ if (last >= 0 && !inOpaqueUnit(scan, last) && isOpenHead(last))
2449
+ span(last, text.length);
2350
2450
  }
2351
2451
  const armPool = (() => {
2352
2452
  const spanRepl = (token) => {
@@ -2376,8 +2476,8 @@ const armPool = (() => {
2376
2476
  phase: 1,
2377
2477
  lexical: LEXICAL_KEYWORD_ARMS.has(re),
2378
2478
  collect: boundary === undefined
2379
- ? (text, _json, _observe, out) => collectRegexSpans(arm, text, out)
2380
- : (text, _json, _observe, out) => collectValueArmSpans(boundary, text, out),
2479
+ ? (text, _json, _eat, out) => collectRegexSpans(arm, text, out)
2480
+ : (text, _json, _eat, out) => collectValueArmSpans(boundary, text, out),
2381
2481
  retry: boundary === undefined ? undefined : (text, at, out) => collectStickySpans(boundary, text, at, out),
2382
2482
  headSrc: boundary === undefined ? undefined : headSrc,
2383
2483
  headFirst: boundary === undefined ? undefined : headFirst,
@@ -2391,7 +2491,7 @@ const armPool = (() => {
2391
2491
  order,
2392
2492
  phase: 1,
2393
2493
  lexical,
2394
- collect: (text, json, observe, out) => arm(text, json, observe, (start, end) => out.push({ start, end, repl, rank, order })),
2494
+ collect: (text, json, eat, out, isHead) => arm(text, json, eat, (start, end) => out.push({ start, end, repl, rank, order }), isHead),
2395
2495
  retry: undefined,
2396
2496
  headSrc: undefined,
2397
2497
  headFirst: undefined,
@@ -2406,7 +2506,7 @@ const armPool = (() => {
2406
2506
  order,
2407
2507
  phase: 2,
2408
2508
  lexical: false,
2409
- collect: (scan, text, json, observe, out) => arm(scan, text, json, observe, (start, end) => out.push({ start, end, repl, rank, order })),
2509
+ collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end) => out.push({ start, end, repl, rank, order }), isHead),
2410
2510
  retry: undefined,
2411
2511
  headSrc: undefined,
2412
2512
  headFirst: undefined,
@@ -2414,22 +2514,22 @@ const armPool = (() => {
2414
2514
  };
2415
2515
  const structuralScannerMember = (row, order) => {
2416
2516
  if (row === PEM_FENCE_PATTERN)
2417
- return scannerMember(row[1], order, (text, _json, _observe, span) => collectPemFenceSpans(text, span));
2517
+ return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectPemFenceSpans(text, span, isHead));
2418
2518
  if (row === SSH2_FENCE_PATTERN)
2419
- return forensicMember(row[1], order, (scan, text, _json, _observe, span) => collectSsh2FenceSpans(scan, text, span));
2519
+ return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, span, isHead));
2420
2520
  if (row === URL_CREDENTIAL_PATTERN)
2421
- return forensicMember(row[1], order, (scan, text, json, _observe, span) => collectUrlCredentialSpans(scan, text, json, span));
2521
+ return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead));
2422
2522
  if (row === JSON_SECRET_FIELD_PATTERN)
2423
- return scannerMember(row[1], order, (text, _json, _observe, span) => collectJsonSecretFieldSpans(text, span));
2523
+ return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead));
2424
2524
  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)`);
2425
2525
  };
2426
2526
  const members = [];
2427
2527
  for (const row of STRUCTURAL_SECRET_PATTERNS)
2428
2528
  members.push(structuralScannerMember(row, members.length));
2429
- members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, observe, span) => collectPpkContainerSpans(scan, text, observe, span)));
2430
- members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _observe, span) => collectPemFragmentSpans(scan, text, json, span)));
2431
- members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _observe, span) => collectHeadlessPpkSpans(scan, text, span)));
2432
- members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _observe, span) => collectAnchoredValueSpans(text, json, span), ARM_RANK.keyword, true));
2529
+ members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead)));
2530
+ members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span) => collectPemFragmentSpans(scan, text, json, span)));
2531
+ members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead)));
2532
+ members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead) => collectAnchoredValueSpans(text, json, span, isHead), ARM_RANK.keyword, true));
2433
2533
  for (const row of KEYWORD_SECRET_PATTERNS)
2434
2534
  members.push(regexMember(row, ARM_RANK.keyword, members.length));
2435
2535
  for (const row of SHAPE_SECRET_PATTERNS)
@@ -2460,7 +2560,8 @@ const armPool = (() => {
2460
2560
  const headFirstUnit = unionMembers(retryMembers.map((m) => m.headFirst)) ?? new Uint32Array(0x10000 >>> 5).fill(0xffffffff);
2461
2561
  poolArmCount = members.length;
2462
2562
  poolBoundaryArmCount = retryMembers.length;
2463
- return (text, wantCount, json, observe, scope) => {
2563
+ return (text, wantCount, json, eat, scope, isHead) => {
2564
+ readings += 1;
2464
2565
  const lane = LANES[scope];
2465
2566
  const spans = [];
2466
2567
  let tried;
@@ -2496,7 +2597,7 @@ const armPool = (() => {
2496
2597
  }
2497
2598
  };
2498
2599
  for (const m of lane.phase1)
2499
- m.collect(text, json, observe, spans);
2600
+ m.collect(text, json, eat, spans, isHead);
2500
2601
  closeCoveragePositions(0);
2501
2602
  if (lane.phase2.length > 0) {
2502
2603
  const scan = buildForensicScan(credentialSpans(text), lane.phase2.length);
@@ -2513,7 +2614,7 @@ const armPool = (() => {
2513
2614
  }
2514
2615
  scan.footLo = FOOT_NONE;
2515
2616
  scan.footHi = -1;
2516
- lane.phase2[j].collect(scan, text, json, observe && level === 0, spans);
2617
+ lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans, isHead);
2517
2618
  foot[2 * j] = scan.footLo;
2518
2619
  foot[2 * j + 1] = scan.footHi;
2519
2620
  forensicRuns += 1;
@@ -2559,8 +2660,8 @@ let onRedaction;
2559
2660
  export function setRedactionObserver(fn) {
2560
2661
  onRedaction = fn;
2561
2662
  }
2562
- function applyRules(text, wantCount, json, observe, scope) {
2563
- return armPool(text, wantCount, json, observe, scope);
2663
+ function applyRules(text, wantCount, json, eat, scope, isHead) {
2664
+ return armPool(text, wantCount, json, eat, scope, isHead);
2564
2665
  }
2565
2666
  function reportHits(hits) {
2566
2667
  if (onRedaction === undefined)
@@ -2573,24 +2674,151 @@ function reportHits(hits) {
2573
2674
  }
2574
2675
  }
2575
2676
  }
2576
- function runRedaction(text, observe, json, wantHits, scope, muteCount = false) {
2577
- const wantCount = wantHits || (onRedaction !== undefined && observe && !muteCount);
2578
- const normalized = normalizeForRedaction(text);
2579
- const first = applyRules(normalized, wantCount, json, observe, scope);
2580
- forgetEscapedSeparatorTails();
2581
- let spans = spansInOriginal(text, normalized, undefined, first.kept);
2582
- let hits = first.hits;
2583
- const second = deleteSpelledFormatCharacters(normalized);
2584
- if (second.at !== undefined) {
2585
- const r2 = applyRules(second.text, wantCount, json, false, scope);
2677
+ function invisibleClassesOf(layout) {
2678
+ switch (layout) {
2679
+ case "multi-line":
2680
+ return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL;
2681
+ case "single-line":
2682
+ return SPELLED_FORMAT | SPELLED_CONTROL | RAW_CONTROL | RAW_WHITESPACE;
2683
+ default: {
2684
+ const unhandled = layout;
2685
+ throw new Error(`redact: unhandled layout ${String(unhandled)}`);
2686
+ }
2687
+ }
2688
+ }
2689
+ function trailingPartialEscapeStart(s) {
2690
+ const n = s.length;
2691
+ const runStart = (end) => {
2692
+ let k = end;
2693
+ while (k > 0 && s.charCodeAt(k - 1) === CH_BACKSLASH)
2694
+ k -= 1;
2695
+ return k;
2696
+ };
2697
+ const highSurrogateEscapeStartBefore = (end) => {
2698
+ let j = end;
2699
+ let value = -1;
2700
+ let bodyStart = -1;
2701
+ if (j >= 1 && s.charCodeAt(j - 1) === CH_RBRACE) {
2702
+ let k = j - 1;
2703
+ while (k > 0 && hexDigitValue(s.charCodeAt(k - 1)) >= 0)
2704
+ k -= 1;
2705
+ if (k >= 2 && s.charCodeAt(k - 1) === CH_LBRACE && s.charCodeAt(k - 2) === CH_u && j - 1 - k >= 1) {
2706
+ value = readHexRunValue(s, k, j - 1);
2707
+ bodyStart = k - 2;
2708
+ }
2709
+ }
2710
+ else if (j >= 5 && s.charCodeAt(j - 5) === CH_u) {
2711
+ value = readHexRun(s, j - 4, 4);
2712
+ bodyStart = j - 5;
2713
+ }
2714
+ if (value < 0xd800 || value > 0xdbff)
2715
+ return -1;
2716
+ const k = runStart(bodyStart);
2717
+ return k < bodyStart ? k : -1;
2718
+ };
2719
+ let j = n;
2720
+ while (j > 0 && hexDigitValue(s.charCodeAt(j - 1)) >= 0)
2721
+ j -= 1;
2722
+ const hexRun = n - j;
2723
+ let element = -1;
2724
+ if (j >= 2 && s.charCodeAt(j - 1) === CH_LBRACE && s.charCodeAt(j - 2) === CH_u)
2725
+ element = j - 2;
2726
+ else if (j >= 1 && s.charCodeAt(j - 1) === CH_u && hexRun <= 3)
2727
+ element = j - 1;
2728
+ else if (j >= 1 && s.charCodeAt(j - 1) === CH_x && hexRun <= 1)
2729
+ element = j - 1;
2730
+ else if (hexRun === 0 && n >= 1 && s.charCodeAt(n - 1) === CH_BACKSLASH)
2731
+ element = n;
2732
+ if (element >= 0) {
2733
+ const k = runStart(element);
2734
+ if (k >= element)
2735
+ return -1;
2736
+ const high = highSurrogateEscapeStartBefore(k);
2737
+ return high >= 0 ? high : k;
2738
+ }
2739
+ return highSurrogateEscapeStartBefore(n);
2740
+ }
2741
+ function readHexRunValue(s, from, to) {
2742
+ let v = 0;
2743
+ for (let k = from; k < to; k++) {
2744
+ const d = hexDigitValue(s.charCodeAt(k));
2745
+ if (d < 0)
2746
+ return -1;
2747
+ v = v * 16 + d;
2748
+ if (v > 0x10ffff)
2749
+ return -1;
2750
+ }
2751
+ return v;
2752
+ }
2753
+ function shortestReading(text, layout) {
2754
+ const raw = collectRawControlCuts(text, invisibleClassesOf(layout));
2755
+ const normalized = normalizeForRedaction(deleteInvisibleCharacters(text, raw, raw.present).text);
2756
+ const open = trailingPartialEscapeStart(normalized);
2757
+ const spelled = collectSpelledCuts(normalized);
2758
+ const length = deleteInvisibleCharacters(normalized, spelled, spelled.present).text.length;
2759
+ return open < 0 ? { visible: length, partial: false } : { visible: length - (normalized.length - open), partial: true };
2760
+ }
2761
+ export function readingHeadEnd(text, layout, units, from = 0) {
2762
+ const rest = text.length - from;
2763
+ let len = Math.min(rest, units);
2764
+ let first = true;
2765
+ let grow = 64;
2766
+ while (len < rest) {
2767
+ const { visible, partial } = shortestReading(text.slice(from, from + len), layout);
2768
+ if (visible >= units) {
2769
+ if (!partial)
2770
+ return from + len;
2771
+ len = Math.min(rest, len + grow);
2772
+ grow *= 2;
2773
+ continue;
2774
+ }
2775
+ const step = first && visible * 2 >= len ? Math.ceil((units - visible) / (visible / len)) + 1 : Math.max(units - visible, len);
2776
+ first = false;
2777
+ len = Math.min(rest, len + step);
2778
+ }
2779
+ return text.length;
2780
+ }
2781
+ function runRedaction(text, observe, json, wantHits, scope, layout, extent) {
2782
+ const wantCount = wantHits || (onRedaction !== undefined && observe);
2783
+ const eat = observe ? { from: -1, len: 0 } : undefined;
2784
+ let spans = [];
2785
+ let hits = NO_HITS;
2786
+ let readsSoFar = 0;
2787
+ let unioned = false;
2788
+ const read = (layers, view) => {
2789
+ const r = applyRules(view, wantCount, json, eat, scope, extent === "head");
2586
2790
  forgetEscapedSeparatorTails();
2587
- const extra = spansInOriginal(text, normalized, second, r2.kept);
2588
- if (extra.length > 0) {
2589
- spans = resolveSpanOverlaps([...spans, ...extra]);
2590
- hits = wantCount ? hitsOfSpans(spans) : NO_HITS;
2791
+ readsSoFar += 1;
2792
+ if (readsSoFar === 1) {
2793
+ spans = spansInOriginal(layers, r.kept);
2794
+ hits = r.hits;
2795
+ return;
2591
2796
  }
2797
+ const extra = spansInOriginal(layers, r.kept);
2798
+ if (extra.length === 0)
2799
+ return;
2800
+ spans = resolveSpanOverlaps([...spans, ...extra]);
2801
+ unioned = true;
2802
+ };
2803
+ const raw = collectRawControlCuts(text, invisibleClassesOf(layout));
2804
+ for (let rmask = 0;; rmask = (rmask - raw.present) & raw.present) {
2805
+ const rfold = deleteInvisibleCharacters(text, raw, rmask);
2806
+ const normalized = normalizeForRedaction(rfold.text);
2807
+ const spelled = collectSpelledCuts(normalized);
2808
+ for (let smask = 0;; smask = (smask - spelled.present) & spelled.present) {
2809
+ const sfold = deleteInvisibleCharacters(normalized, spelled, smask);
2810
+ read([{ fold: sfold }, { input: rfold.text, view: normalized }, { fold: rfold }], sfold.text);
2811
+ if (smask === spelled.present)
2812
+ break;
2813
+ }
2814
+ if (rmask === raw.present)
2815
+ break;
2592
2816
  }
2593
- if (observe && !muteCount)
2817
+ if (unioned && wantCount)
2818
+ hits = hitsOfSpans(spans);
2819
+ if (eat !== undefined && eat.from >= 0)
2820
+ recordFailOpen("server.redact.ppk-eat-to-end", `from=${eat.from} len=${eat.len}`);
2821
+ if (observe)
2594
2822
  reportHits(hits);
2595
2823
  return { text: applySpans(text, spans).out, hits, spans };
2596
2824
  }
@@ -2608,28 +2836,41 @@ function hitsOfSpans(spans) {
2608
2836
  }
2609
2837
  return [...by.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm }));
2610
2838
  }
2611
- function spansInOriginal(text, normalized, fold, kept) {
2612
- if (kept.length === 0)
2613
- return kept;
2614
- const inNormalized = fold?.at === undefined ? kept : kept.map((s) => ({ ...s, start: viewIndexInInput(fold, s.start), end: viewIndexInInput(fold, s.end - 1) + 1 }));
2615
- if (normalized === text)
2616
- return inNormalized;
2617
- const map = mapNormalizedToOriginal(text, normalized);
2618
- return inNormalized.map((s) => ({ ...s, start: map[s.start], end: map[s.end - 1] + 1 }));
2839
+ function spansInOriginal(layers, kept) {
2840
+ let out = kept;
2841
+ for (const layer of layers) {
2842
+ if (out.length === 0)
2843
+ return out;
2844
+ let at;
2845
+ if ("fold" in layer) {
2846
+ const fold = layer.fold;
2847
+ if (fold.at === undefined)
2848
+ continue;
2849
+ at = (v) => viewIndexInInput(fold, v);
2850
+ }
2851
+ else {
2852
+ if (layer.view === layer.input)
2853
+ continue;
2854
+ const map = mapNormalizedToOriginal(layer.input, layer.view);
2855
+ at = (v) => map[v];
2856
+ }
2857
+ out = out.map((s) => ({ ...s, start: at(s.start), end: at(s.end - 1) + 1 }));
2858
+ }
2859
+ return out;
2619
2860
  }
2620
2861
  export function redactSecrets(text, opts) {
2621
- return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text").text;
2862
+ return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", "whole").text;
2622
2863
  }
2623
2864
  export function redactSecretsWithArms(text, opts) {
2624
- const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text");
2865
+ const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", "whole");
2625
2866
  return { text: r.text, arms: new Set(r.hits.map((h) => h.arm)) };
2626
2867
  }
2627
2868
  export function redactionSpansForTest(text, opts) {
2628
- const r = runRedaction(text, false, opts?.json === true, false, "free-text");
2869
+ const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line", "whole");
2629
2870
  return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
2630
2871
  }
2631
- export function proposeRedactionSpans(text, scope, opts) {
2632
- return runRedaction(text, true, false, false, scope, opts?.countHits === false).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
2872
+ export function proposeRedactionSpans(text, scope, layout, extent) {
2873
+ return runRedaction(text, true, false, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
2633
2874
  }
2634
2875
  const REDACT_MAX_DEPTH = 64;
2635
2876
  export function redactDeep(value) {
@@ -2731,7 +2972,7 @@ function unitCarriesCredential(unit) {
2731
2972
  for (const v of armValueClassViews())
2732
2973
  views.add(boundedUnitView(spelling, v.inClass, v.filler));
2733
2974
  views.delete("");
2734
- const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit").text !== view);
2975
+ const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line", "whole").text !== view);
2735
2976
  return { hit, unreadable: decoded.residue };
2736
2977
  }
2737
2978
  function displaySafePathname(pathname) {
@@ -2761,7 +3002,7 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
2761
3002
  const boundary = canonical.indexOf("?");
2762
3003
  if (boundary < 0)
2763
3004
  return false;
2764
- if (runRedaction(canonical, false, false, false, "free-text").spans.some((sp) => sp.end > boundary))
3005
+ if (runRedaction(canonical, false, false, false, "free-text", "multi-line", "whole").spans.some((sp) => sp.end > boundary))
2765
3006
  return true;
2766
3007
  for (const unit of canonical.slice(boundary + 1).split("&").flatMap((sg) => {
2767
3008
  const eq = sg.indexOf("=");