@sema-agent/server 7.136.0 → 7.137.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 (214) hide show
  1. package/MIGRATION.md +173 -0
  2. package/USAGE.md +84 -16
  3. package/deploy/sema-up/README.md +14 -0
  4. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  5. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  6. package/dist/approval-content-kind.d.ts +6 -0
  7. package/dist/approval-content-kind.js +7 -0
  8. package/dist/approval-gate-currency.d.ts +48 -6
  9. package/dist/approval-gate-currency.js +16 -9
  10. package/dist/approval-hmac.d.ts +13 -1
  11. package/dist/approval-hmac.js +1 -1
  12. package/dist/approval.js +3 -3
  13. package/dist/auth-bridge.js +2 -2
  14. package/dist/boot/execution-env.d.ts +24 -1
  15. package/dist/boot/execution-env.js +69 -6
  16. package/dist/boot/reapers.d.ts +3 -0
  17. package/dist/boot/reapers.js +15 -1
  18. package/dist/boot/resolve-spec.d.ts +4 -0
  19. package/dist/boot/resolve-spec.js +16 -17
  20. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  21. package/dist/boot/stage-04-stores.d.ts +1 -1
  22. package/dist/boot/stage-05-execution-env.d.ts +3 -1
  23. package/dist/boot/stage-05-execution-env.js +11 -2
  24. package/dist/boot/stage-06-runners.d.ts +3 -1
  25. package/dist/boot/stage-06-runners.js +5 -1
  26. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  27. package/dist/boot/stage-07-capability-layer.js +1 -1
  28. package/dist/boot/stage-08-reapers.d.ts +4 -1
  29. package/dist/boot/stage-08-reapers.js +2 -1
  30. package/dist/boot/stage-09-leader.d.ts +4 -1
  31. package/dist/boot/stage-10-http-server.d.ts +4 -1
  32. package/dist/boot/stage-10-http-server.js +3 -2
  33. package/dist/boot/stores.d.ts +1 -1
  34. package/dist/broker/broker.d.ts +89 -0
  35. package/dist/broker/broker.js +287 -0
  36. package/dist/broker/constants.d.ts +39 -0
  37. package/dist/broker/constants.js +15 -0
  38. package/dist/broker/http1.d.ts +111 -0
  39. package/dist/broker/http1.js +256 -0
  40. package/dist/broker/lease-book.d.ts +87 -0
  41. package/dist/broker/lease-book.js +142 -0
  42. package/dist/broker/lease-file.d.ts +60 -0
  43. package/dist/broker/lease-file.js +127 -0
  44. package/dist/broker/main.d.ts +2 -0
  45. package/dist/broker/main.js +20 -0
  46. package/dist/capabilities/composite-members.d.ts +2 -1
  47. package/dist/capabilities/composite-members.js +2 -1
  48. package/dist/capabilities/engine-capabilities.js +1 -0
  49. package/dist/capabilities/scenarios.d.ts +23 -8
  50. package/dist/capabilities/scenarios.js +13 -8
  51. package/dist/capabilities/skills.d.ts +28 -5
  52. package/dist/capabilities/skills.js +8 -0
  53. package/dist/center-credential-renewer.js +5 -1
  54. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  55. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  56. package/dist/checkpoint-identity.d.ts +3 -3
  57. package/dist/config-catalog.js +9 -6
  58. package/dist/config-types.d.ts +9 -0
  59. package/dist/config.d.ts +1 -1
  60. package/dist/config.js +34 -8
  61. package/dist/deployment-governance.d.ts +2 -2
  62. package/dist/deployment-governance.js +7 -9
  63. package/dist/device-ws-protocol.d.ts +1 -0
  64. package/dist/device-ws-protocol.js +2 -1
  65. package/dist/file-history-disclosure.d.ts +4 -2
  66. package/dist/file-history-disclosure.js +2 -0
  67. package/dist/hooks/classic-stations.d.ts +11 -54
  68. package/dist/http/admission.js +10 -7
  69. package/dist/http/approval-sweeps.js +4 -3
  70. package/dist/http/caller-trust.d.ts +2 -1
  71. package/dist/http/caller-trust.js +1 -3
  72. package/dist/http/checkpoint-ctx.d.ts +48 -7
  73. package/dist/http/checkpoint-ctx.js +40 -0
  74. package/dist/http/dispatch.js +15 -10
  75. package/dist/http/durable-run-leg.d.ts +5 -1
  76. package/dist/http/durable-run-leg.js +1 -1
  77. package/dist/http/health-self-report.d.ts +2 -2
  78. package/dist/http/mcp-remote-text.d.ts +5 -0
  79. package/dist/http/mcp-remote-text.js +7 -0
  80. package/dist/http/principal-gate.d.ts +15 -10
  81. package/dist/http/principal-gate.js +48 -9
  82. package/dist/http/resume-legs.d.ts +2 -20
  83. package/dist/http/resume-legs.js +52 -38
  84. package/dist/http/route-ctx.d.ts +8 -2
  85. package/dist/http/routes/approvals-assistant.js +18 -4
  86. package/dist/http/routes/capabilities.js +11 -1
  87. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  88. package/dist/http/routes/mcp-reconnect.js +5 -7
  89. package/dist/http/routes/runs.js +2 -1
  90. package/dist/http/routes/session-sync.js +42 -6
  91. package/dist/http/routes/sessions.js +2 -1
  92. package/dist/http/routes/tasks.js +8 -6
  93. package/dist/http/server.d.ts +4 -0
  94. package/dist/http/stored-body-replay.d.ts +3 -3
  95. package/dist/http/stored-body-replay.js +6 -1
  96. package/dist/http/wire-types.d.ts +52 -27
  97. package/dist/identity-source.d.ts +65 -0
  98. package/dist/identity-source.js +45 -0
  99. package/dist/memory-store-refusal.d.ts +3 -3
  100. package/dist/model-entry-refusal.d.ts +2 -0
  101. package/dist/model-entry-refusal.js +11 -0
  102. package/dist/model-select.d.ts +6 -17
  103. package/dist/model-select.js +0 -3
  104. package/dist/namespaced-rule-name.d.ts +1 -1
  105. package/dist/observability/fail-open.d.ts +13 -1
  106. package/dist/observability/fail-open.js +13 -1
  107. package/dist/observability/metrics.js +1 -1
  108. package/dist/observability/run-terminal.js +1 -0
  109. package/dist/operator-ask.d.ts +2 -2
  110. package/dist/org-delivery-gate.js +1 -1
  111. package/dist/org-memory-admission.d.ts +2 -2
  112. package/dist/org-memory-admission.js +39 -2
  113. package/dist/parked-decide.js +3 -2
  114. package/dist/peer-directory.js +2 -0
  115. package/dist/permission-rule-list.d.ts +2 -2
  116. package/dist/plugins/checkpoint-store-sql.d.ts +19 -9
  117. package/dist/plugins/checkpoint-store-sql.js +6 -6
  118. package/dist/plugins/file-history-store-sql.js +7 -14
  119. package/dist/plugins/file-run-store.js +8 -2
  120. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  121. package/dist/plugins/k8s-exec-protocol.js +116 -28
  122. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  123. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  124. package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
  125. package/dist/plugins/k8s-pod-reaper.js +122 -0
  126. package/dist/plugins/local-checkpoint-store.d.ts +3 -6
  127. package/dist/plugins/local-checkpoint-store.js +3 -3
  128. package/dist/plugins/memory-run-store.js +2 -0
  129. package/dist/plugins/one-time-migrations.d.ts +28 -9
  130. package/dist/plugins/one-time-migrations.js +48 -5
  131. package/dist/plugins/permission-rule-store-file.js +2 -2
  132. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  133. package/dist/plugins/permission-rule-store-sql.js +16 -17
  134. package/dist/plugins/pg-pool.js +4 -0
  135. package/dist/plugins/pg-session-storage.js +2 -2
  136. package/dist/plugins/remote-env-adb.d.ts +3 -0
  137. package/dist/plugins/remote-env-adb.js +1 -0
  138. package/dist/plugins/remote-env-device.d.ts +3 -0
  139. package/dist/plugins/remote-env-device.js +4 -0
  140. package/dist/plugins/remote-env-e2b.d.ts +3 -0
  141. package/dist/plugins/remote-env-e2b.js +1 -0
  142. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  143. package/dist/plugins/remote-env-file-error.js +7 -1
  144. package/dist/plugins/remote-env-host.d.ts +7 -1
  145. package/dist/plugins/remote-env-host.js +7 -1
  146. package/dist/plugins/remote-env-k8s.d.ts +137 -10
  147. package/dist/plugins/remote-env-k8s.js +276 -14
  148. package/dist/plugins/remote-env-local-docker.d.ts +3 -0
  149. package/dist/plugins/remote-env-local-docker.js +1 -0
  150. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  151. package/dist/plugins/remote-env-ssh.js +1 -0
  152. package/dist/plugins/run-store-sql.js +10 -4
  153. package/dist/plugins/session-ownership.d.ts +17 -3
  154. package/dist/plugins/session-ownership.js +19 -5
  155. package/dist/plugins/sql-errors.d.ts +23 -2
  156. package/dist/plugins/sql-errors.js +15 -0
  157. package/dist/plugins/store-contracts.d.ts +10 -1
  158. package/dist/plugins/store-fault.d.ts +2 -2
  159. package/dist/plugins/store-fault.js +4 -2
  160. package/dist/plugins/tidb-pool.js +7 -0
  161. package/dist/plugins/tidb-session-store.js +2 -2
  162. package/dist/request-key-closure.js +1 -1
  163. package/dist/request-value-closure.d.ts +31 -2
  164. package/dist/request-value-closure.js +7 -1
  165. package/dist/rule-entry-list.d.ts +3 -2
  166. package/dist/rule-entry-list.js +2 -2
  167. package/dist/rules-consent.js +7 -4
  168. package/dist/run-local.d.ts +2 -2
  169. package/dist/runs.js +2 -1
  170. package/dist/runtime-governance.d.ts +5 -5
  171. package/dist/sandbox-git-lease.d.ts +49 -0
  172. package/dist/sandbox-git-lease.js +60 -0
  173. package/dist/sandbox-git-plane.d.ts +84 -0
  174. package/dist/sandbox-git-plane.js +105 -0
  175. package/dist/security.d.ts +16 -4
  176. package/dist/security.js +8 -3
  177. package/dist/server-secret-env.d.ts +6 -0
  178. package/dist/server-secret-env.js +14 -0
  179. package/dist/session-error-wire.d.ts +14 -6
  180. package/dist/session-error-wire.js +2 -1
  181. package/dist/session-policy-wire.d.ts +1 -1
  182. package/dist/session-policy-wire.js +2 -2
  183. package/dist/session-sync.d.ts +1 -1
  184. package/dist/spec-fields.d.ts +43 -13
  185. package/dist/spec-fields.js +33 -12
  186. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  187. package/dist/stored-request-prompt-profile-m30.js +25 -0
  188. package/dist/task-mcp.d.ts +1 -1
  189. package/dist/task-settings.d.ts +2 -2
  190. package/dist/tool-approval.d.ts +12 -2
  191. package/dist/tool-approval.js +8 -3
  192. package/dist/tool-rule-names.d.ts +5 -0
  193. package/dist/trace/core-keyset-guard.d.ts +4 -4
  194. package/dist/trace/engine-notice-wire.d.ts +1 -1
  195. package/dist/trace/engine-notice-wire.js +2 -0
  196. package/dist/trace/exit-seat.js +2 -2
  197. package/dist/trace/ledger-sink.d.ts +4 -5
  198. package/dist/trace/ledger-sink.js +3 -7
  199. package/dist/trace/project.d.ts +37 -3
  200. package/dist/trace/project.js +20 -6
  201. package/dist/trace/redact.d.ts +18 -6
  202. package/dist/trace/redact.js +118 -53
  203. package/dist/trace/self-minted-wire.d.ts +377 -3
  204. package/dist/trace/self-minted-wire.js +122 -0
  205. package/dist/trace/wire-projection-faces.d.ts +5 -3
  206. package/dist/trace/wire-projection-faces.js +7 -3
  207. package/dist/web-admin-client/apply-effective.js +0 -1
  208. package/dist/web-admin-client/center-request.d.ts +20 -2
  209. package/dist/web-admin-client/center-request.js +40 -5
  210. package/dist/web-admin-client/http-client.js +6 -6
  211. package/dist/web-admin-client/types.d.ts +5 -5
  212. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  213. package/package.json +2 -2
  214. package/wire-changes/server@7.137.0.json +825 -0
@@ -819,13 +819,57 @@ if (/[\]^-]/.test(SCHEME_DELIMITER_CHARS))
819
819
  const SCHEME_DELIMITER_CODES = new Set([...SCHEME_DELIMITER_CHARS].map((c) => c.charCodeAt(0)));
820
820
  const isSchemeDelimiterCode = (c) => SCHEME_DELIMITER_CODES.has(c);
821
821
  const SCHEME_SEPARATOR_CLASS = `[\\s${SCHEME_DELIMITER_CHARS.replace(/\\/g, "\\\\")}]`;
822
- const LEXICAL_KEYWORD_ARMS = new Set();
822
+ function expandKeywordForms(src) {
823
+ let forms = [""];
824
+ for (let i = 0; i < src.length;) {
825
+ let alts;
826
+ const pair = /^\[([A-Za-z])([A-Za-z])\]/.exec(src.slice(i));
827
+ if (pair !== null) {
828
+ if (pair[1].toLowerCase() !== pair[2].toLowerCase())
829
+ throw new Error(`redact: keyword source ${src} holds a class that is not a case pair`);
830
+ alts = [pair[1], pair[2]];
831
+ i += 4;
832
+ }
833
+ else if (/[A-Za-z0-9]/.test(src.charAt(i))) {
834
+ alts = [src.charAt(i)];
835
+ i += 1;
836
+ }
837
+ else {
838
+ throw new Error(`redact: keyword source ${src} holds an atom the head reading cannot enumerate (only letters, digits and [Xx] case pairs)`);
839
+ }
840
+ forms = forms.flatMap((f) => alts.map((a) => f + a));
841
+ }
842
+ const whole = new RegExp(`^(?:${src})$`);
843
+ for (const f of forms)
844
+ if (!whole.test(f))
845
+ throw new Error(`redact: keyword source ${src} does not match its own enumerated form ${f}`);
846
+ return forms;
847
+ }
848
+ const LEXICAL_KEYWORD_ARMS = new Map();
823
849
  const keywordArm = (keywordSrc, valueClass, min, tailSrc = "", separatorClass = "\\s") => {
824
850
  const re = new RegExp(`(?:${keywordSrc}${separatorClass}+${atLeast(valueClass, min)}${tailSrc}` +
825
851
  `|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${atLeast(valueClass, min)}${tailSrc})`, "g");
826
- LEXICAL_KEYWORD_ARMS.add(re);
852
+ const separator = new RegExp(`^${separatorClass}$`);
853
+ const value = new RegExp(`^${valueClass}$`);
854
+ LEXICAL_KEYWORD_ARMS.set(re, { forms: expandKeywordForms(keywordSrc), min, isSeparator: (u) => separator.test(u), isValue: (u) => value.test(u) });
827
855
  return re;
828
856
  };
857
+ function undecidedKeywordStart(text, t) {
858
+ const n = text.length;
859
+ let valueRun = 0;
860
+ while (valueRun < t.min && valueRun < n && t.isValue(text.charAt(n - 1 - valueRun)))
861
+ valueRun += 1;
862
+ if (valueRun >= t.min)
863
+ return -1;
864
+ let sepStart = n - valueRun;
865
+ while (sepStart > 0 && t.isSeparator(text.charAt(sepStart - 1)))
866
+ sepStart -= 1;
867
+ for (const f of t.forms) {
868
+ if (sepStart >= f.length && text.startsWith(f, sepStart - f.length))
869
+ return sepStart - f.length;
870
+ }
871
+ return -1;
872
+ }
829
873
  const CREDENTIAL_NAME_ANCHORS = {
830
874
  api_key: "structure-only", apikey: "structure-only", "api-key": "structure-only", key: "structure-only",
831
875
  token: "structure-only", access_token: "structure-only", refresh_token: "structure-only", id_token: "structure-only",
@@ -1166,7 +1210,7 @@ function collectSsh2FenceSpans(scan, text, span, isHead) {
1166
1210
  }
1167
1211
  const bodyFrom = fence + SSH2_FENCE_OPEN.length;
1168
1212
  if (isHead && unitCountWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
1169
- span(fence, text.length);
1213
+ span(fence, text.length, true);
1170
1214
  return;
1171
1215
  }
1172
1216
  const close = nextClose(bodyFrom);
@@ -1224,7 +1268,7 @@ function collectPemFenceSpans(text, span, isHead) {
1224
1268
  earliestEndFence(bodyFrom);
1225
1269
  if (bodyFrom === PEM_LINE_OPEN || endAt < 0) {
1226
1270
  if (isHead)
1227
- span(fence, text.length);
1271
+ span(fence, text.length, true);
1228
1272
  return;
1229
1273
  }
1230
1274
  if (endAt - bodyFrom > PEM_FENCE_BODY_MAX) {
@@ -1382,20 +1426,23 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
1382
1426
  continue;
1383
1427
  }
1384
1428
  let end = -1;
1429
+ let open = false;
1385
1430
  for (let from = p;;) {
1386
1431
  const r = bodyLineAt(from);
1387
1432
  if (r === LINE_NONE)
1388
1433
  break;
1389
1434
  end = r === LINE_OPEN ? n : r;
1390
- if (r === LINE_OPEN)
1435
+ if (r === LINE_OPEN) {
1436
+ open = true;
1391
1437
  break;
1438
+ }
1392
1439
  from = r;
1393
1440
  }
1394
1441
  if (end < 0) {
1395
1442
  search = p;
1396
1443
  continue;
1397
1444
  }
1398
- span(at, end);
1445
+ span(at, end, open);
1399
1446
  search = end;
1400
1447
  }
1401
1448
  }
@@ -1855,7 +1902,7 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
1855
1902
  hi = scan.footHi;
1856
1903
  urlSegmentRuns += 1;
1857
1904
  if (at >= 0 && at !== (known ? ph[pc + 1] : -2))
1858
- span(start, at);
1905
+ span(start, at, isHead && at >= text.length);
1859
1906
  }
1860
1907
  if (hw.n + URL_SEG_STRIDE <= hw.a.length || growUrlSegments(hw)) {
1861
1908
  const w = hw.a;
@@ -1951,6 +1998,7 @@ function jsonSecretFieldValueQuoteAt(text, p) {
1951
1998
  i += 1;
1952
1999
  return text.charCodeAt(i) === CH_QUOTE ? i : -1;
1953
2000
  }
2001
+ const STRING_OPEN = -2;
1954
2002
  function jsonStringEndAt(text, q, isHead) {
1955
2003
  const n = text.length;
1956
2004
  for (let i = q + 1; i < n;) {
@@ -1962,21 +2010,23 @@ function jsonStringEndAt(text, q, isHead) {
1962
2010
  i += 2;
1963
2011
  continue;
1964
2012
  }
1965
- return isHead && i + 1 >= n ? n : -1;
2013
+ return isHead && i + 1 >= n ? STRING_OPEN : -1;
1966
2014
  }
1967
2015
  i += 1;
1968
2016
  }
1969
- return isHead ? n : -1;
2017
+ return isHead ? STRING_OPEN : -1;
1970
2018
  }
1971
2019
  function collectJsonSecretFieldSpans(text, span, isHead) {
1972
2020
  for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
1973
2021
  const q = jsonSecretFieldValueQuoteAt(text, p);
1974
- const end = q < 0 ? -1 : jsonStringEndAt(text, q, isHead);
1975
- if (end < 0) {
2022
+ const found = q < 0 ? -1 : jsonStringEndAt(text, q, isHead);
2023
+ if (found === -1) {
1976
2024
  p = text.indexOf(JSON_FIELD_ANCHOR, p + 1);
1977
2025
  continue;
1978
2026
  }
1979
- span(q, end);
2027
+ const open = found === STRING_OPEN;
2028
+ const end = open ? text.length : found;
2029
+ span(q, end, open);
1980
2030
  p = text.indexOf(JSON_FIELD_ANCHOR, scanResumeAfterHit(p, end, JSON_FIELD_ANCHOR.length));
1981
2031
  }
1982
2032
  }
@@ -2144,7 +2194,7 @@ function quotedValueEnd(text, cs, quote, json, isHead) {
2144
2194
  i += unitLenOf(escaped);
2145
2195
  }
2146
2196
  }
2147
- return isHead ? n : -1;
2197
+ return isHead ? STRING_OPEN : -1;
2148
2198
  }
2149
2199
  function bareValueEnd(text, s, json, listSep) {
2150
2200
  const n = text.length;
@@ -2203,6 +2253,8 @@ function literalValueAt(text, s, json, declaredIfBare, listSep, isHead) {
2203
2253
  if (!isAsciiAlnumCode(unitChOf(readAnchorUnit(text, vs, json))))
2204
2254
  return undefined;
2205
2255
  const close = quotedValueEnd(text, vs, first, json, isHead);
2256
+ if (close === STRING_OPEN)
2257
+ return { start: vs, end: n, quoted: true, open: true };
2206
2258
  if (close >= 0)
2207
2259
  return { start: vs, end: close, quoted: true };
2208
2260
  }
@@ -2323,7 +2375,7 @@ function collectAnchoredValueSpans(text, json, span, isHead) {
2323
2375
  }
2324
2376
  if (v === undefined)
2325
2377
  continue;
2326
- span(v.start, v.end);
2378
+ span(v.start, v.end, v.open === true);
2327
2379
  resume = v.quoted ? v.start : v.end;
2328
2380
  }
2329
2381
  }
@@ -2400,12 +2452,15 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
2400
2452
  let pos = at + HEAD.length + 1;
2401
2453
  let end = text.length;
2402
2454
  let ateToEnd = !isHead;
2455
+ let open = false;
2403
2456
  for (;;) {
2404
2457
  const mac = find(MAC, pos);
2405
2458
  const hash = find(HASH, pos);
2406
2459
  const term = Math.min(mac, hash);
2407
- if (term === Number.POSITIVE_INFINITY)
2460
+ if (term === Number.POSITIVE_INFINITY) {
2461
+ open = isHead;
2408
2462
  break;
2463
+ }
2409
2464
  const hexStart = term + (term === mac ? MAC.length : HASH.length);
2410
2465
  const hexEnd = hexRunEnd(hexStart);
2411
2466
  if (hexEnd - hexStart < 32) {
@@ -2435,18 +2490,19 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
2435
2490
  const undecided = isHead && limit === Number.POSITIVE_INFINITY;
2436
2491
  ateToEnd = straggler < limit || !gapBlank;
2437
2492
  end = ateToEnd || undecided ? text.length : hexEnd;
2493
+ open = undecided;
2438
2494
  break;
2439
2495
  }
2440
2496
  if (ateToEnd && eat !== undefined && eat.from < 0) {
2441
2497
  eat.from = at;
2442
2498
  eat.len = text.length;
2443
2499
  }
2444
- span(at, end);
2500
+ span(at, end, open);
2445
2501
  at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
2446
2502
  }
2447
2503
  const last = isHead ? text.lastIndexOf(HEAD) : -1;
2448
2504
  if (last >= 0 && !inOpaqueUnit(scan, last) && isOpenHead(last))
2449
- span(last, text.length);
2505
+ span(last, text.length, true);
2450
2506
  }
2451
2507
  const armPool = (() => {
2452
2508
  const spanRepl = (token) => {
@@ -2468,6 +2524,7 @@ const armPool = (() => {
2468
2524
  throw new Error(`redact: boundary arm /${re.source}/ has a zero-length structural head — the bounded head screen would admit every candidate position`);
2469
2525
  }
2470
2526
  const arm = { g: re, y, back, headSrc, headFirst, repl, rank, order };
2527
+ const keywordTail = LEXICAL_KEYWORD_ARMS.get(re);
2471
2528
  const boundary = y === undefined || headSrc === undefined || headFirst === undefined ? undefined : { ...arm, y, headSrc, headFirst };
2472
2529
  return {
2473
2530
  token,
@@ -2475,9 +2532,17 @@ const armPool = (() => {
2475
2532
  order,
2476
2533
  phase: 1,
2477
2534
  lexical: LEXICAL_KEYWORD_ARMS.has(re),
2478
- collect: boundary === undefined
2479
- ? (text, _json, _eat, out) => collectRegexSpans(arm, text, out)
2480
- : (text, _json, _eat, out) => collectValueArmSpans(boundary, text, out),
2535
+ collect: (text, _json, _eat, out, isHead) => {
2536
+ if (boundary === undefined)
2537
+ collectRegexSpans(arm, text, out);
2538
+ else
2539
+ collectValueArmSpans(boundary, text, out);
2540
+ if (isHead && keywordTail !== undefined) {
2541
+ const at = undecidedKeywordStart(text, keywordTail);
2542
+ if (at >= 0)
2543
+ out.push({ start: at, end: text.length, repl, rank, order, undecided: true });
2544
+ }
2545
+ },
2481
2546
  retry: boundary === undefined ? undefined : (text, at, out) => collectStickySpans(boundary, text, at, out),
2482
2547
  headSrc: boundary === undefined ? undefined : headSrc,
2483
2548
  headFirst: boundary === undefined ? undefined : headFirst,
@@ -2491,7 +2556,7 @@ const armPool = (() => {
2491
2556
  order,
2492
2557
  phase: 1,
2493
2558
  lexical,
2494
- collect: (text, json, eat, out, isHead) => arm(text, json, eat, (start, end) => out.push({ start, end, repl, rank, order }), isHead),
2559
+ collect: (text, json, eat, out, isHead) => arm(text, json, eat, (start, end, undecided) => out.push(undecided === true ? { start, end, repl, rank, order, undecided } : { start, end, repl, rank, order }), isHead),
2495
2560
  retry: undefined,
2496
2561
  headSrc: undefined,
2497
2562
  headFirst: undefined,
@@ -2506,7 +2571,7 @@ const armPool = (() => {
2506
2571
  order,
2507
2572
  phase: 2,
2508
2573
  lexical: false,
2509
- collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end) => out.push({ start, end, repl, rank, order }), isHead),
2574
+ collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end, undecided) => out.push(undecided === true ? { start, end, repl, rank, order, undecided } : { start, end, repl, rank, order }), isHead),
2510
2575
  retry: undefined,
2511
2576
  headSrc: undefined,
2512
2577
  headFirst: undefined,
@@ -2560,8 +2625,9 @@ const armPool = (() => {
2560
2625
  const headFirstUnit = unionMembers(retryMembers.map((m) => m.headFirst)) ?? new Uint32Array(0x10000 >>> 5).fill(0xffffffff);
2561
2626
  poolArmCount = members.length;
2562
2627
  poolBoundaryArmCount = retryMembers.length;
2563
- return (text, wantCount, json, eat, scope, isHead) => {
2628
+ return (text, wantCount, json, eat, scope, reach) => {
2564
2629
  readings += 1;
2630
+ const isHead = reach !== undefined;
2565
2631
  const lane = LANES[scope];
2566
2632
  const spans = [];
2567
2633
  let tried;
@@ -2629,11 +2695,12 @@ const armPool = (() => {
2629
2695
  }
2630
2696
  }
2631
2697
  poolSpanCount += spans.length;
2632
- const kept = resolveSpanOverlaps(spans);
2698
+ const survives = (sp) => sp.undecided !== true || reach === undefined || reach(sp.start);
2699
+ const kept = resolveSpanOverlaps(reach === undefined ? spans : spans.filter(survives));
2633
2700
  if (!wantCount || kept.length === 0)
2634
2701
  return { hits: NO_HITS, kept };
2635
2702
  const tokenOf = (sp) => members[sp.order].token;
2636
- const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full").sort((a, b) => a.start - b.start);
2703
+ const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full" && survives(sp)).sort((a, b) => a.start - b.start);
2637
2704
  const byArmedToken = new Map();
2638
2705
  const stamped = [];
2639
2706
  let si = 0;
@@ -2660,8 +2727,8 @@ let onRedaction;
2660
2727
  export function setRedactionObserver(fn) {
2661
2728
  onRedaction = fn;
2662
2729
  }
2663
- function applyRules(text, wantCount, json, eat, scope, isHead) {
2664
- return armPool(text, wantCount, json, eat, scope, isHead);
2730
+ function applyRules(text, wantCount, json, eat, scope, reach) {
2731
+ return armPool(text, wantCount, json, eat, scope, reach);
2665
2732
  }
2666
2733
  function reportHits(hits) {
2667
2734
  if (onRedaction === undefined)
@@ -2674,6 +2741,7 @@ function reportHits(hits) {
2674
2741
  }
2675
2742
  }
2676
2743
  }
2744
+ export const WHOLE_EXTENT = { kind: "whole" };
2677
2745
  function invisibleClassesOf(layout) {
2678
2746
  switch (layout) {
2679
2747
  case "multi-line":
@@ -2786,15 +2854,17 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent) {
2786
2854
  let readsSoFar = 0;
2787
2855
  let unioned = false;
2788
2856
  const read = (layers, view) => {
2789
- const r = applyRules(view, wantCount, json, eat, scope, extent === "head");
2857
+ const toOriginal = createViewToOriginal(layers);
2858
+ const reach = extent.kind === "head" ? (viewStart) => toOriginal.start(viewStart) < extent.emitEnd : undefined;
2859
+ const r = applyRules(view, wantCount, json, eat, scope, reach);
2790
2860
  forgetEscapedSeparatorTails();
2791
2861
  readsSoFar += 1;
2792
2862
  if (readsSoFar === 1) {
2793
- spans = spansInOriginal(layers, r.kept);
2863
+ spans = toOriginal.spans(r.kept);
2794
2864
  hits = r.hits;
2795
2865
  return;
2796
2866
  }
2797
- const extra = spansInOriginal(layers, r.kept);
2867
+ const extra = toOriginal.spans(r.kept);
2798
2868
  if (extra.length === 0)
2799
2869
  return;
2800
2870
  spans = resolveSpanOverlaps([...spans, ...extra]);
@@ -2836,37 +2906,32 @@ function hitsOfSpans(spans) {
2836
2906
  }
2837
2907
  return [...by.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm }));
2838
2908
  }
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;
2909
+ function createViewToOriginal(layers) {
2910
+ let ats;
2911
+ const layerMaps = () => (ats ??= layers.flatMap((layer) => {
2845
2912
  if ("fold" in layer) {
2846
2913
  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];
2914
+ return fold.at === undefined ? [] : [(v) => viewIndexInInput(fold, v)];
2856
2915
  }
2857
- out = out.map((s) => ({ ...s, start: at(s.start), end: at(s.end - 1) + 1 }));
2858
- }
2859
- return out;
2916
+ if (layer.view === layer.input)
2917
+ return [];
2918
+ const map = mapNormalizedToOriginal(layer.input, layer.view);
2919
+ return [(v) => map[v]];
2920
+ }));
2921
+ return {
2922
+ start: (viewIndex) => layerMaps().reduce((p, at) => at(p), viewIndex),
2923
+ spans: (kept) => (kept.length === 0 ? kept : layerMaps().reduce((out, at) => out.map((s) => ({ ...s, start: at(s.start), end: at(s.end - 1) + 1 })), kept)),
2924
+ };
2860
2925
  }
2861
2926
  export function redactSecrets(text, opts) {
2862
- return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", "whole").text;
2927
+ return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT).text;
2863
2928
  }
2864
2929
  export function redactSecretsWithArms(text, opts) {
2865
- const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", "whole");
2930
+ const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", WHOLE_EXTENT);
2866
2931
  return { text: r.text, arms: new Set(r.hits.map((h) => h.arm)) };
2867
2932
  }
2868
2933
  export function redactionSpansForTest(text, opts) {
2869
- const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line", "whole");
2934
+ const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT);
2870
2935
  return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
2871
2936
  }
2872
2937
  export function proposeRedactionSpans(text, scope, layout, extent) {
@@ -2972,7 +3037,7 @@ function unitCarriesCredential(unit) {
2972
3037
  for (const v of armValueClassViews())
2973
3038
  views.add(boundedUnitView(spelling, v.inClass, v.filler));
2974
3039
  views.delete("");
2975
- const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line", "whole").text !== view);
3040
+ const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line", WHOLE_EXTENT).text !== view);
2976
3041
  return { hit, unreadable: decoded.residue };
2977
3042
  }
2978
3043
  function displaySafePathname(pathname) {
@@ -3002,7 +3067,7 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
3002
3067
  const boundary = canonical.indexOf("?");
3003
3068
  if (boundary < 0)
3004
3069
  return false;
3005
- if (runRedaction(canonical, false, false, false, "free-text", "multi-line", "whole").spans.some((sp) => sp.end > boundary))
3070
+ if (runRedaction(canonical, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans.some((sp) => sp.end > boundary))
3006
3071
  return true;
3007
3072
  for (const unit of canonical.slice(boundary + 1).split("&").flatMap((sg) => {
3008
3073
  const eq = sg.indexOf("=");