@sema-agent/server 7.103.2 → 7.105.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 (204) hide show
  1. package/MIGRATION.md +378 -2
  2. package/README.md +6 -1
  3. package/README.zh-CN.md +5 -1
  4. package/USAGE.md +320 -16
  5. package/deploy/sema-up/README.md +57 -7
  6. package/deploy/sema-up/cluster-up.sh +18 -4
  7. package/deploy/sema-up/kube-up.sh +16 -0
  8. package/deploy/sema-up/lib/dataroot.sh +142 -0
  9. package/deploy/sema-up/lib/flagshape.sh +69 -0
  10. package/deploy/sema-up/lib/lanes.py +14 -4
  11. package/deploy/sema-up/lib/shell-access.sh +41 -0
  12. package/deploy/sema-up/preflight.sh +14 -1
  13. package/deploy/sema-up/sema-up.sh +134 -37
  14. package/deploy/sema-up/smoke.sh +5 -0
  15. package/dist/auto-mode-face.js +3 -4
  16. package/dist/boot/budget-tracing.js +1 -1
  17. package/dist/boot/config-center.d.ts +6 -2
  18. package/dist/boot/config-center.js +5 -59
  19. package/dist/boot/execution-env.d.ts +11 -0
  20. package/dist/boot/execution-env.js +14 -0
  21. package/dist/boot/execution-lane.d.ts +25 -0
  22. package/dist/boot/execution-lane.js +95 -0
  23. package/dist/boot/memory-consolidation.d.ts +185 -9
  24. package/dist/boot/memory-consolidation.js +192 -32
  25. package/dist/boot/resolve-spec.d.ts +2 -0
  26. package/dist/boot/resolve-spec.js +28 -24
  27. package/dist/boot/runner-deps.d.ts +6 -2
  28. package/dist/boot/runner-deps.js +6 -1
  29. package/dist/boot/session-faces.d.ts +5 -0
  30. package/dist/boot/session-faces.js +18 -2
  31. package/dist/boot/session-incarnation-seat.d.ts +41 -0
  32. package/dist/boot/session-incarnation-seat.js +69 -0
  33. package/dist/boot/stage-01-config.js +2 -2
  34. package/dist/boot/stage-02-registry-pull.js +3 -1
  35. package/dist/boot/stage-04-stores.d.ts +2 -0
  36. package/dist/boot/stage-04-stores.js +3 -1
  37. package/dist/boot/stage-05-execution-env.d.ts +4 -0
  38. package/dist/boot/stage-05-execution-env.js +2 -2
  39. package/dist/boot/stage-06-runners.d.ts +4 -1
  40. package/dist/boot/stage-06-runners.js +7 -14
  41. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  42. package/dist/boot/stage-08-reapers.d.ts +4 -1
  43. package/dist/boot/stage-09-leader.d.ts +6 -1
  44. package/dist/boot/stage-09-leader.js +2 -1
  45. package/dist/boot/stage-10-http-server.d.ts +6 -1
  46. package/dist/boot/stage-10-http-server.js +4 -1
  47. package/dist/brain.d.ts +1 -1
  48. package/dist/config-catalog.d.ts +16 -1
  49. package/dist/config-catalog.js +9 -9
  50. package/dist/config-center/restart-signal.d.ts +4 -4
  51. package/dist/config-invariants.d.ts +7 -1
  52. package/dist/config-invariants.js +37 -6
  53. package/dist/config-provider.d.ts +42 -10
  54. package/dist/config-provider.js +18 -16
  55. package/dist/config-types.d.ts +37 -16
  56. package/dist/config-types.js +1 -0
  57. package/dist/config.d.ts +28 -30
  58. package/dist/config.js +95 -77
  59. package/dist/env-facts.d.ts +13 -3
  60. package/dist/env-facts.js +15 -3
  61. package/dist/execution-lane-caps.d.ts +15 -0
  62. package/dist/execution-lane-caps.js +8 -0
  63. package/dist/file-history-capture.d.ts +42 -35
  64. package/dist/file-history-capture.js +15 -10
  65. package/dist/file-history-disclosure.d.ts +7 -31
  66. package/dist/file-history-disclosure.js +12 -0
  67. package/dist/fleet/fleet-bus.d.ts +5 -1
  68. package/dist/http/admission.d.ts +12 -5
  69. package/dist/http/admission.js +5 -1
  70. package/dist/http/dispatch.d.ts +18 -0
  71. package/dist/http/dispatch.js +19 -6
  72. package/dist/http/principal-gate.d.ts +22 -0
  73. package/dist/http/principal-gate.js +20 -0
  74. package/dist/http/resume-legs.d.ts +1 -1
  75. package/dist/http/resume-legs.js +9 -5
  76. package/dist/http/route-table.d.ts +15 -4
  77. package/dist/http/route-table.js +6 -5
  78. package/dist/http/routes/agents-roster.d.ts +7 -0
  79. package/dist/http/routes/agents-roster.js +6 -2
  80. package/dist/http/routes/approvals-assistant.d.ts +48 -19
  81. package/dist/http/routes/approvals-assistant.js +117 -50
  82. package/dist/http/routes/capabilities.js +3 -2
  83. package/dist/http/routes/config-catalog.js +1 -0
  84. package/dist/http/routes/fleet.d.ts +3 -2
  85. package/dist/http/routes/images.js +0 -1
  86. package/dist/http/routes/memory-consolidation.d.ts +3 -2
  87. package/dist/http/routes/memory-policy.js +3 -3
  88. package/dist/http/routes/rules.js +2 -2
  89. package/dist/http/routes/runs.js +29 -10
  90. package/dist/http/routes/session-sync.js +14 -15
  91. package/dist/http/routes/sessions-list.js +3 -2
  92. package/dist/http/routes/sessions.js +49 -25
  93. package/dist/http/routes/shared-memory.js +2 -2
  94. package/dist/http/routes/side-query.js +8 -1
  95. package/dist/http/routes/tasks.js +22 -19
  96. package/dist/http/routes/trace-usage.d.ts +2 -0
  97. package/dist/http/routes/trace-usage.js +8 -13
  98. package/dist/http/send.d.ts +12 -0
  99. package/dist/http/send.js +12 -1
  100. package/dist/http/server.d.ts +30 -8
  101. package/dist/http/server.js +11 -5
  102. package/dist/http/sse-lifecycle.d.ts +1 -1
  103. package/dist/http/sse-log.d.ts +12 -9
  104. package/dist/http/sse-log.js +1 -8
  105. package/dist/http/sync-submit-body.d.ts +43 -0
  106. package/dist/http/sync-submit-body.js +4 -0
  107. package/dist/http/wire-types.d.ts +23 -5
  108. package/dist/index.d.ts +1 -0
  109. package/dist/index.js +1 -0
  110. package/dist/leader/merge.d.ts +8 -3
  111. package/dist/memory-incident-sink.js +2 -1
  112. package/dist/model-select.d.ts +9 -0
  113. package/dist/model-select.js +5 -1
  114. package/dist/observability/corrupt-read-seat.d.ts +151 -16
  115. package/dist/observability/corrupt-read-seat.js +158 -15
  116. package/dist/observability/fail-open.d.ts +17 -1
  117. package/dist/observability/fail-open.js +17 -1
  118. package/dist/observability/metrics.js +1 -1
  119. package/dist/observability/run-terminal.d.ts +60 -10
  120. package/dist/observability/run-terminal.js +119 -9
  121. package/dist/parked-decide.d.ts +8 -3
  122. package/dist/parked-decide.js +56 -20
  123. package/dist/parked-revive-surface.d.ts +14 -7
  124. package/dist/parked-revive-surface.js +5 -1
  125. package/dist/plugins/approval-ask-store-file.d.ts +4 -1
  126. package/dist/plugins/approval-ask-store-file.js +9 -12
  127. package/dist/plugins/approval-ask-store-sql.d.ts +4 -0
  128. package/dist/plugins/approval-ask-store-sql.js +4 -1
  129. package/dist/plugins/approval-exemption-store.d.ts +21 -1
  130. package/dist/plugins/approval-exemption-store.js +36 -7
  131. package/dist/plugins/approval-nonce-store.d.ts +16 -2
  132. package/dist/plugins/approval-nonce-store.js +17 -7
  133. package/dist/plugins/caching-session-store.d.ts +5 -0
  134. package/dist/plugins/caching-session-store.js +3 -0
  135. package/dist/plugins/file-resume-anchor-store.d.ts +7 -2
  136. package/dist/plugins/file-resume-anchor-store.js +19 -5
  137. package/dist/plugins/file-run-store.d.ts +21 -8
  138. package/dist/plugins/file-run-store.js +60 -38
  139. package/dist/plugins/image-bake-store-sql.d.ts +0 -2
  140. package/dist/plugins/image-bake-store-sql.js +0 -5
  141. package/dist/plugins/local-session-store.d.ts +22 -0
  142. package/dist/plugins/local-session-store.js +74 -11
  143. package/dist/plugins/memory-run-store.d.ts +7 -5
  144. package/dist/plugins/memory-run-store.js +11 -4
  145. package/dist/plugins/one-time-migrations.d.ts +38 -1
  146. package/dist/plugins/one-time-migrations.js +143 -15
  147. package/dist/plugins/permission-rule-store-file.d.ts +9 -2
  148. package/dist/plugins/permission-rule-store-file.js +37 -51
  149. package/dist/plugins/pg-session-storage.d.ts +3 -0
  150. package/dist/plugins/pg-session-storage.js +19 -10
  151. package/dist/plugins/retention-store-sql.js +1 -0
  152. package/dist/plugins/run-store-sql.d.ts +15 -6
  153. package/dist/plugins/run-store-sql.js +15 -13
  154. package/dist/plugins/schema-indexes.js +1 -0
  155. package/dist/plugins/send-file-ledger.d.ts +5 -1
  156. package/dist/plugins/send-file-ledger.js +11 -6
  157. package/dist/plugins/session-event-payload.d.ts +14 -4
  158. package/dist/plugins/session-event-payload.js +5 -2
  159. package/dist/plugins/session-policy-store-sql.js +18 -5
  160. package/dist/plugins/store-backend.d.ts +13 -7
  161. package/dist/plugins/store-backend.js +10 -6
  162. package/dist/plugins/store-contracts.d.ts +7 -2
  163. package/dist/plugins/tidb-pool.js +12 -5
  164. package/dist/plugins/tidb-session-store.d.ts +6 -1
  165. package/dist/plugins/tidb-session-store.js +17 -10
  166. package/dist/project-identity.d.ts +4 -6
  167. package/dist/read-face-posture.d.ts +21 -0
  168. package/dist/read-face-posture.js +9 -0
  169. package/dist/request-key-closure.js +1 -1
  170. package/dist/request-value-closure.d.ts +72 -3
  171. package/dist/request-value-closure.js +12 -4
  172. package/dist/roster-bound-http-status.d.ts +6 -0
  173. package/dist/roster-bound-http-status.js +6 -0
  174. package/dist/rules-consent.js +0 -11
  175. package/dist/run-local.js +22 -27
  176. package/dist/runs.d.ts +22 -2
  177. package/dist/runs.js +29 -7
  178. package/dist/sealed-key.d.ts +31 -4
  179. package/dist/sealed-key.js +9 -0
  180. package/dist/security.d.ts +5 -1
  181. package/dist/session-children-retire.d.ts +92 -0
  182. package/dist/session-children-retire.js +85 -0
  183. package/dist/session-error-wire.d.ts +58 -0
  184. package/dist/session-error-wire.js +38 -0
  185. package/dist/session-incarnation.d.ts +61 -0
  186. package/dist/session-incarnation.js +43 -0
  187. package/dist/session-policy-wire.d.ts +98 -12
  188. package/dist/session-policy-wire.js +71 -8
  189. package/dist/session-records-skipped-notice.d.ts +20 -0
  190. package/dist/session-records-skipped-notice.js +27 -0
  191. package/dist/tool-approval.d.ts +65 -0
  192. package/dist/tool-approval.js +36 -9
  193. package/dist/trace/core-keyset-guard.d.ts +8 -7
  194. package/dist/trace/engine-notice-wire.d.ts +3 -1
  195. package/dist/trace/engine-notice-wire.js +2 -0
  196. package/dist/trace/project.d.ts +11 -6
  197. package/dist/trace/project.js +12 -3
  198. package/dist/trace/redact.d.ts +7 -0
  199. package/dist/trace/redact.js +153 -61
  200. package/dist/trace/wire-projection-faces.d.ts +10 -3
  201. package/dist/trace/wire-projection-faces.js +8 -2
  202. package/package.json +2 -2
  203. package/dist/plugins/memory-session-policy-store.d.ts +0 -46
  204. package/dist/plugins/memory-session-policy-store.js +0 -52
@@ -486,13 +486,12 @@ function deriveLiteralHead(re) {
486
486
  function scanResumeAfterHit(start, end, back) {
487
487
  return Math.max(start + 1, end - back);
488
488
  }
489
- const spanOffset = (a, m) => (a.outside === 1 ? m[1].length : 0);
490
489
  function collectRegexSpans(a, text, out) {
491
490
  const g = a.g;
492
491
  g.lastIndex = 0;
493
492
  for (let m = g.exec(text); m !== null; m = g.exec(text)) {
494
493
  const end = m.index + m[0].length;
495
- out.push({ start: m.index + spanOffset(a, m), end, repl: a.repl, rank: a.rank, order: a.order });
494
+ out.push({ start: m.index, end, repl: a.repl, rank: a.rank, order: a.order });
496
495
  g.lastIndex = scanResumeAfterHit(m.index, end, a.back);
497
496
  }
498
497
  }
@@ -585,7 +584,7 @@ function collectStickySpans(a, text, at, out) {
585
584
  if (m === null)
586
585
  continue;
587
586
  const end = p + m[0].length;
588
- out.push({ start: p + spanOffset(a, m), end, repl, rank, order });
587
+ out.push({ start: p, end, repl, rank, order });
589
588
  pos = scanResumeAfterHit(p, end, back);
590
589
  }
591
590
  }
@@ -935,6 +934,47 @@ function collectSsh2FenceSpans(text, span) {
935
934
  from = scanResumeAfterHit(fence, end, SSH2_FENCE_OPEN.length);
936
935
  }
937
936
  }
937
+ const PEM_END_OPEN = "-----END";
938
+ const PEM_FENCE_BODY_MAX = 8192;
939
+ function pemFenceLineEnd(text, labelFrom) {
940
+ const close = text.indexOf(PEM_FENCE_CLOSE, labelFrom);
941
+ return close >= 0 && isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : -1;
942
+ }
943
+ function collectPemFenceSpans(text, span) {
944
+ let endFrom = -1;
945
+ let endAt = -1;
946
+ let endAfter = -1;
947
+ const earliestEndFence = (from) => {
948
+ if (endFrom >= 0 && from >= endFrom && (endAt < 0 || endAt >= from))
949
+ return;
950
+ let b = text.indexOf(PEM_END_OPEN, from);
951
+ let after = -1;
952
+ while (b >= 0 && (after = pemFenceLineEnd(text, b + PEM_END_OPEN.length)) < 0)
953
+ b = text.indexOf(PEM_END_OPEN, b + 1);
954
+ endFrom = from;
955
+ endAt = b;
956
+ endAfter = after;
957
+ };
958
+ for (let from = 0;;) {
959
+ const fence = text.indexOf(PEM_FENCE_OPEN, from);
960
+ if (fence < 0)
961
+ return;
962
+ const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
963
+ if (bodyFrom < 0) {
964
+ from = fence + 1;
965
+ continue;
966
+ }
967
+ earliestEndFence(bodyFrom);
968
+ if (endAt < 0)
969
+ return;
970
+ if (endAt - bodyFrom > PEM_FENCE_BODY_MAX) {
971
+ from = fence + 1;
972
+ continue;
973
+ }
974
+ span(fence, endAfter);
975
+ from = scanResumeAfterHit(fence, endAfter, PEM_FENCE_OPEN.length);
976
+ }
977
+ }
938
978
  function collectPemFragmentSpans(text, json, span) {
939
979
  let cur = findPemFence(text, 0);
940
980
  let inherited = false;
@@ -1145,6 +1185,18 @@ const SSH2_FENCE_PATTERN = (() => {
1145
1185
  throw new Error("redact: SSH2 fence rule missing from FULL_ARM_PATTERNS");
1146
1186
  return row;
1147
1187
  })();
1188
+ const PEM_FENCE_PATTERN = (() => {
1189
+ const row = FULL_ARM_PATTERNS.find(([re]) => re.source.startsWith(PEM_FENCE_OPEN));
1190
+ if (row === undefined)
1191
+ throw new Error("redact: PEM fence rule missing from FULL_ARM_PATTERNS");
1192
+ return row;
1193
+ })();
1194
+ const JSON_SECRET_FIELD_PATTERN = (() => {
1195
+ const rows = FULL_ARM_PATTERNS.filter(([, repl]) => repl === '$1"«redacted»"');
1196
+ if (rows.length !== 1)
1197
+ throw new Error(`redact: expected exactly one JSON secret-field rule (replacement '$1"«redacted»"') in FULL_ARM_PATTERNS, found ${rows.length}`);
1198
+ return rows[0];
1199
+ })();
1148
1200
  const isRegexSpace = (c) => c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c === 0x1680 || (c >= 0x2000 && c <= 0x200a) || c === 0x2028 || c === 0x2029 || c === 0x202f || c === 0x205f || c === 0x3000 || c === 0xfeff;
1149
1201
  const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
1150
1202
  const isControlCode = (c) => c < 0x20 || c === 0x7f;
@@ -1360,6 +1412,78 @@ function collectUrlCredentialSpans(text, json, span) {
1360
1412
  i = valueEnd - 1;
1361
1413
  }
1362
1414
  }
1415
+ const JSON_FIELD_ANCHOR = '"';
1416
+ const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_NAME_WORDS.map((w) => w.length));
1417
+ function jsonSecretFieldValueQuoteAt(text, p) {
1418
+ const n = text.length;
1419
+ let name = "";
1420
+ let i = p + 1;
1421
+ for (; i < n; i++) {
1422
+ let c = text.charCodeAt(i);
1423
+ if (c === CH_QUOTE)
1424
+ break;
1425
+ if (i - p > CREDENTIAL_NAME_MAX)
1426
+ return -1;
1427
+ if (c >= CH_A && c <= CH_Z)
1428
+ c += 32;
1429
+ if (!((c >= CH_a && c <= CH_z) || (c >= CH_0 && c <= CH_9) || c === CH_UNDERSCORE || c === CH_MINUS))
1430
+ return -1;
1431
+ name += String.fromCharCode(c);
1432
+ }
1433
+ if (i >= n || !CREDENTIAL_NAME_SET.has(name))
1434
+ return -1;
1435
+ i += 1;
1436
+ while (i < n && isRegexSpace(text.charCodeAt(i)))
1437
+ i += 1;
1438
+ if (text.charCodeAt(i) !== CH_COLON)
1439
+ return -1;
1440
+ i += 1;
1441
+ while (i < n && isRegexSpace(text.charCodeAt(i)))
1442
+ i += 1;
1443
+ return text.charCodeAt(i) === CH_QUOTE ? i : -1;
1444
+ }
1445
+ function jsonStringEndAt(text, q) {
1446
+ const n = text.length;
1447
+ for (let i = q + 1; i < n;) {
1448
+ const c = text.charCodeAt(i);
1449
+ if (c === CH_QUOTE)
1450
+ return i + 1;
1451
+ if (c === CH_BACKSLASH) {
1452
+ if (i + 1 < n && !isLineTerminator(text.charCodeAt(i + 1))) {
1453
+ i += 2;
1454
+ continue;
1455
+ }
1456
+ return -1;
1457
+ }
1458
+ i += 1;
1459
+ }
1460
+ return -1;
1461
+ }
1462
+ function collectJsonSecretFieldSpans(text, span) {
1463
+ for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
1464
+ const q = jsonSecretFieldValueQuoteAt(text, p);
1465
+ const end = q < 0 ? -1 : jsonStringEndAt(text, q);
1466
+ if (end < 0) {
1467
+ p = text.indexOf(JSON_FIELD_ANCHOR, p + 1);
1468
+ continue;
1469
+ }
1470
+ span(q, end);
1471
+ p = text.indexOf(JSON_FIELD_ANCHOR, scanResumeAfterHit(p, end, JSON_FIELD_ANCHOR.length));
1472
+ }
1473
+ }
1474
+ export function structuralScannerForTest(arm) {
1475
+ const collect = (scan) => (text) => {
1476
+ const out = [];
1477
+ scan(text, (start, end) => out.push([start, end]));
1478
+ return out;
1479
+ };
1480
+ switch (arm) {
1481
+ case "json-secret-field":
1482
+ return { declared: new RegExp(JSON_SECRET_FIELD_PATTERN[0].source, JSON_SECRET_FIELD_PATTERN[0].flags), spans: collect(collectJsonSecretFieldSpans) };
1483
+ case "pem-fence":
1484
+ return { declared: new RegExp(PEM_FENCE_PATTERN[0].source, PEM_FENCE_PATTERN[0].flags), spans: collect(collectPemFenceSpans) };
1485
+ }
1486
+ }
1363
1487
  let ppkScanBytes = 0;
1364
1488
  export function ppkScanBytesForTest() { return ppkScanBytes; }
1365
1489
  export function resetPpkScanBytesForTest() { ppkScanBytes = 0; }
@@ -1462,56 +1586,19 @@ const armPool = (() => {
1462
1586
  throw new Error(`redact: arm-pool replacement ${JSON.stringify(token)} may only use a leading $1 (the run kept outside the span)`);
1463
1587
  return body;
1464
1588
  };
1465
- const literalAnchorHead = (re) => {
1466
- const src = re.source;
1467
- let i = src.startsWith("(") && !src.startsWith("(?") ? 1 : 0;
1468
- let out = "";
1469
- for (; i < src.length; i++) {
1470
- const c = src[i];
1471
- if (c === "\\") {
1472
- const e = src[i + 1];
1473
- if (e === undefined || !HEAD_LITERAL_ESCAPES.has(e))
1474
- break;
1475
- if ("*+?{".includes(src[i + 2] ?? ""))
1476
- break;
1477
- out += src.slice(i, i + 2);
1478
- i += 1;
1479
- continue;
1480
- }
1481
- if ("([{|).^$*+?".includes(c))
1482
- break;
1483
- if ("*+?{".includes(src[i + 1] ?? ""))
1484
- break;
1485
- out += c;
1486
- }
1487
- if (out.length === 0)
1488
- throw new Error(`redact: structural arm /${src}/ has no literal anchor head — its same-arm rewind amount cannot be derived`);
1489
- return out;
1490
- };
1491
- const regexMember = ([re, token], rank, order, structural) => {
1589
+ const regexMember = ([re, token], rank, order) => {
1590
+ if (token.startsWith("$1"))
1591
+ throw new Error(`redact: regex arm /${re.source}/ cannot carry an outside-span $1 prefix — that form belongs to the structural scanners`);
1492
1592
  const repl = spanRepl(token);
1493
- const outside = token.startsWith("$1") ? 1 : 0;
1494
1593
  const y = VALUE_ARM_STICKY.get(re);
1495
- let back;
1496
- let headSrc;
1497
- let headFirst;
1498
- if (structural) {
1499
- back = literalAnchorHead(re).length;
1500
- headSrc = undefined;
1501
- headFirst = undefined;
1502
- }
1503
- else {
1504
- const d = deriveLiteralHead(re);
1505
- back = Math.max(d.len - 1, 0);
1506
- headSrc = d.src;
1507
- headFirst = d.first;
1508
- if (y !== undefined && (d.len === 0 || headSrc === undefined || headFirst === undefined)) {
1509
- throw new Error(`redact: boundary arm /${re.source}/ has a zero-length structural head — the bounded head screen would admit every candidate position`);
1510
- }
1594
+ const d = deriveLiteralHead(re);
1595
+ const back = Math.max(d.len - 1, 0);
1596
+ const headSrc = d.src;
1597
+ const headFirst = d.first;
1598
+ if (y !== undefined && (d.len === 0 || headSrc === undefined || headFirst === undefined)) {
1599
+ throw new Error(`redact: boundary arm /${re.source}/ has a zero-length structural head — the bounded head screen would admit every candidate position`);
1511
1600
  }
1512
- if (y !== undefined && outside === 1)
1513
- throw new Error(`redact: value arm /${re.source}/ cannot carry an outside-span $1 prefix`);
1514
- const arm = { g: re, y, back, headSrc, headFirst, repl, rank, order, outside };
1601
+ const arm = { g: re, y, back, headSrc, headFirst, repl, rank, order };
1515
1602
  const boundary = y === undefined || headSrc === undefined || headFirst === undefined ? undefined : { ...arm, y, headSrc, headFirst };
1516
1603
  return {
1517
1604
  token,
@@ -1541,24 +1628,29 @@ const armPool = (() => {
1541
1628
  headFirst: undefined,
1542
1629
  };
1543
1630
  };
1631
+ const structuralScannerMember = (row, order) => {
1632
+ if (row === PEM_FENCE_PATTERN)
1633
+ return scannerMember(row[1], order, 1, (text, _json, _observe, span) => collectPemFenceSpans(text, span));
1634
+ if (row === SSH2_FENCE_PATTERN)
1635
+ return scannerMember(row[1], order, 2, (text, _json, _observe, span) => collectSsh2FenceSpans(text, span));
1636
+ if (row === URL_CREDENTIAL_PATTERN)
1637
+ return scannerMember(row[1], order, 2, (text, json, _observe, span) => collectUrlCredentialSpans(text, json, span));
1638
+ if (row === JSON_SECRET_FIELD_PATTERN)
1639
+ return scannerMember(row[1], order, 1, (text, _json, _observe, span) => collectJsonSecretFieldSpans(text, span));
1640
+ 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)`);
1641
+ };
1544
1642
  const members = [];
1545
- for (const row of STRUCTURAL_SECRET_PATTERNS) {
1546
- const order = members.length;
1547
- members.push(row === URL_CREDENTIAL_PATTERN
1548
- ? scannerMember(row[1], order, 2, (text, json, _observe, span) => collectUrlCredentialSpans(text, json, span))
1549
- : row === SSH2_FENCE_PATTERN
1550
- ? scannerMember(row[1], order, 2, (text, _json, _observe, span) => collectSsh2FenceSpans(text, span))
1551
- : regexMember(row, ARM_RANK.structural, order, true));
1552
- }
1643
+ for (const row of STRUCTURAL_SECRET_PATTERNS)
1644
+ members.push(structuralScannerMember(row, members.length));
1553
1645
  members.push(scannerMember(PPK_CONTAINER_TOKEN, members.length, 2, (text, _json, observe, span) => collectPpkContainerSpans(text, observe, span)));
1554
1646
  members.push(scannerMember(PEM_FRAGMENT_TOKEN, members.length, 2, (text, json, _observe, span) => collectPemFragmentSpans(text, json, span)));
1555
1647
  members.push(scannerMember(HEADLESS_PPK_TOKEN, members.length, 2, (text, _json, _observe, span) => collectHeadlessPpkSpans(text, span)));
1556
1648
  for (const row of KEYWORD_SECRET_PATTERNS)
1557
- members.push(regexMember(row, ARM_RANK.keyword, members.length, false));
1649
+ members.push(regexMember(row, ARM_RANK.keyword, members.length));
1558
1650
  for (const row of SHAPE_SECRET_PATTERNS)
1559
- members.push(regexMember(row, ARM_RANK.shape, members.length, false));
1651
+ members.push(regexMember(row, ARM_RANK.shape, members.length));
1560
1652
  for (const row of FRAGMENT_SECRET_PATTERNS)
1561
- members.push(regexMember(row, ARM_RANK.fragment, members.length, false));
1653
+ members.push(regexMember(row, ARM_RANK.fragment, members.length));
1562
1654
  for (const [re] of KEYWORD_SECRET_PATTERNS) {
1563
1655
  if (!LEXICAL_KEYWORD_ARMS.has(re))
1564
1656
  throw new Error(`redact: keyword arm /${re.source}/ is not registered as lexical — build it with keywordArm() so the bounded-unit scope can exclude it`);
@@ -16,7 +16,8 @@
16
16
  * `NotificationExcluded` 直接读成本表这一行的 `excluded`,于是「哪几个键有意不上 wire」全仓**只有一个写者**。
17
17
  * 旧形是两个:守卫文件一簿、`test/wire-whitelist-exhaustiveness.test.ts` 的 `KnownExcluded` 一簿,那份门注
18
18
  * 自己写着「两处必须同时改」—— 而「必须同时改」正是 [ref] 要下线的东西(对账门不算替代,单写者才算)。
19
- * 本表是纯数据、无行为,被守卫文件以 `typeof import(...)` 的**类型查询**读取(编译期擦除,零运行期边)。
19
+ * 本表是纯数据、无行为(唯一的派生是 `wiring_manifest_operator` 行的 `excluded` 取值器 = 租户行减具名常量,S-724),
20
+ * 被守卫文件以 `typeof import(...)` 的**类型查询**读取(编译期擦除,零运行期边)。
20
21
  *
21
22
  * ## 门在哪里
22
23
  * `test/wire-whitelist-exhaustiveness.test.ts`:对每一行喂一枚 core 型全键 fixture,真的跑一遍构造器,
@@ -54,6 +55,12 @@ export interface WireProjectionFace {
54
55
  export declare const FRAME_ENVELOPE_KEYS: readonly ["type", "sourceTaskId", "bgAgentId"];
55
56
  /** 探针文件要从 `@sema-agent/core` 具名导入的类型(`coreType` 表达式里用到的全部名字)。 */
56
57
  export declare const WIRE_PROJECTION_CORE_IMPORTS: readonly ["TaskEvent", "TaskNotificationPayload", "WiringManifest", "EngineNotice", "MemoryImportReport", "OriginClearanceRow", "OriginClearanceEvent", "MemoryEntryOrigin"];
58
+ /**
59
+ * `wiring_manifest` 的 **operator 面多投的那几段**(`wiringManifestOperatorEventData` 在租户形之上加的段)。
60
+ * operator 行的 `excluded` = 租户行减本常量(派生);四段各自的受众裁定在 `core-keyset-guard.ts` ⑥b。
61
+ * operator 构造器新投一段 ⇒ 同批加进本常量(否则 operator 行会把它登记成「不投」,键集等式门当场红)。
62
+ */
63
+ export declare const WIRING_MANIFEST_OPERATOR_ONLY_SECTIONS: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny"];
57
64
  /**
58
65
  * 全部帧投影面。键 = 面名(门的报错文案与负控都按它称呼这一行)。
59
66
  *
@@ -161,14 +168,14 @@ export declare const WIRE_PROJECTION_FACES: {
161
168
  readonly wiring_manifest_tenant: {
162
169
  readonly projector: "wiringManifestEventData";
163
170
  readonly coreType: `WiringManifest & ${string}`;
164
- readonly excluded: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence", "fileHistory"];
171
+ readonly excluded: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence"];
165
172
  readonly unwrapped: readonly ["manifest"];
166
173
  readonly minted: readonly [];
167
174
  };
168
175
  readonly wiring_manifest_operator: {
169
176
  readonly projector: "wiringManifestOperatorEventData";
170
177
  readonly coreType: `WiringManifest & ${string}`;
171
- readonly excluded: readonly ["peerPresence", "fileHistory"];
178
+ readonly excluded: readonly string[];
172
179
  readonly unwrapped: readonly ["manifest"];
173
180
  readonly minted: readonly [];
174
181
  };
@@ -10,6 +10,10 @@ export const WIRE_PROJECTION_CORE_IMPORTS = [
10
10
  "MemoryEntryOrigin",
11
11
  ];
12
12
  const arm = (name) => `Extract<TaskEvent, { type: "${name}" }>`;
13
+ export const WIRING_MANIFEST_OPERATOR_ONLY_SECTIONS = ["governance", "configFingerprint", "autoConsolidation", "readDeny"];
14
+ function withoutSections(all, drop) {
15
+ return all.filter((k) => !drop.includes(k));
16
+ }
13
17
  export const WIRE_PROJECTION_FACES = {
14
18
  task_progress: { projector: "taskProgressEventData", coreType: arm("task_progress"), excluded: [], minted: [] },
15
19
  tool_start: { projector: "toolStartEventData", coreType: arm("tool_start"), excluded: [], minted: [] },
@@ -36,14 +40,16 @@ export const WIRE_PROJECTION_FACES = {
36
40
  wiring_manifest_tenant: {
37
41
  projector: "wiringManifestEventData",
38
42
  coreType: `WiringManifest & ${arm("wiring_manifest")}`,
39
- excluded: ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence", "fileHistory"],
43
+ excluded: ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence"],
40
44
  unwrapped: ["manifest"],
41
45
  minted: [],
42
46
  },
43
47
  wiring_manifest_operator: {
44
48
  projector: "wiringManifestOperatorEventData",
45
49
  coreType: `WiringManifest & ${arm("wiring_manifest")}`,
46
- excluded: ["peerPresence", "fileHistory"],
50
+ get excluded() {
51
+ return withoutSections(WIRE_PROJECTION_FACES.wiring_manifest_tenant.excluded, WIRING_MANIFEST_OPERATOR_ONLY_SECTIONS);
52
+ },
47
53
  unwrapped: ["manifest"],
48
54
  minted: [],
49
55
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.103.2",
3
+ "version": "7.105.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -77,7 +77,7 @@
77
77
  "sharp": "^0.35.3"
78
78
  },
79
79
  "devDependencies": {
80
- "@sema-agent/sdk": "11.3.0",
80
+ "@sema-agent/sdk": "12.0.1",
81
81
  "@types/libsodium-wrappers": "^0.7.14",
82
82
  "@types/node": "22.10.2",
83
83
  "@types/pg": "^8.20.0",
@@ -1,46 +0,0 @@
1
- /**
2
- * In-memory SessionPolicyStore + the service's `deleteBySession` extension (E21 purge / 2c overwrite-dst wipe).
3
- *
4
- * WHY THIS EXISTS (and not core's `InMemorySessionPolicyStore`): core's in-memory/file policy stores implement only
5
- * the core interface (getRules/putRules/listBySession) — they have NO deletion seam, so neither the E21 session purge
6
- * nor the 2c `overwrite-dst` subsystem wipe can remove policy rows on a backend that uses them (the SQL twins carry
7
- * `deleteBySession` as a service extension; see session-policy-store-sql.ts). This store is the delete-capable
8
- * in-memory twin. The seam itself is also requested upstream (core: optional `deleteBySession?` on the interface +
9
- * the File/InMemory impls) — if core lands it, this class can retire in favor of core's.
10
- *
11
- * BYTE-MATCH DISCIPLINE: getRules/putRules/listBySession replicate core's `InMemorySessionPolicyStore` semantics
12
- * exactly (same key shape, same CAS `conflict` on expectedRev mismatch, same tighten-only `loosen_forbidden` gate via
13
- * core's exported `normalizeRules`/`loosenReasons`/`stripRev`, same rev start/bump). Any divergence is a bug here.
14
- * S-529 起同样钉住 `@contract session_policy.rev_survives_delete`:`deleteBySession` 保留每把键的 rev 高水位
15
- * **并给那把键的世代(`gen`)+1**(见 {@link MemorySessionPolicyStore.deleteBySession});共用试剂盒
16
- * `test/helpers/session-policy-rev-line-contract.ts` 与 SQL 双生、以及 core 自己的 `InMemorySessionPolicyStore`
17
- * (7.25.0 起带这条契约,已作为第三只引擎接进同一只试剂盒当 oracle)跑同一段剧本。
18
- */
19
- import { type PutRulesOptions, type SessionPermissionRules, type SessionPolicyStore, type SessionRulesRecord, type StoredSessionRules } from "@sema-agent/core";
20
- export declare class MemorySessionPolicyStore implements SessionPolicyStore {
21
- private readonly map;
22
- /** S-529 —— 每把键的 rev **高水位 + 世代**(`@contract session_policy.rev_survives_delete`):`deleteBySession`
23
- * 抹掉规则行时把它那条 rev 线的终点留在这里,于是删后首写 stamp 的是 `max(当前行 rev, 记号.rev) + 1`,
24
- * 而不是从 1 重来;同时把 `gen` +1 —— **抹除终结的是那一行的血统**,下一次写开的是新世代,读侧因此能把
25
- * 「重建的行」与「它正在执行的那一行的延续」分开,无论新行的 rev 有多大(core 7.25.0 [ref] 判词:
26
- * 没有世代时读侧只能靠「亲眼看见行消失」那条弱守卫,于是两道门之间未被观察到的 erase+重建抓不住,
27
- * 而一次瞬时读 null 会把该腿永久闩在旧行上)。
28
- * 与 SQL 双生的 `session_policy_rev_mark` 表、core File 店的 `<stem>.hwm.json` 侧车是同一条律的三个实现体
29
- * (SQL 那边的表注写了为什么不留墓碑行)。本表只增不减、条目是两个整数 —— 与 `map` 同为进程内结构,
30
- * 随进程亡;跨副本的持久面在 SQL 双生那半。 */
31
- private readonly marks;
32
- private key;
33
- getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
34
- putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
35
- listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
36
- /** Purge ALL policy rows for a session, across every principal — KEEPING each key's rev high-water mark.
37
- * Scoped by session_id alone — the caller (E21 route / importSession overwrite-dst) already proved ownership.
38
- * Shape = core 1.423's optional `SessionPolicyStore.deleteBySession?` seam (`Promise<void>` — the count was
39
- * incidental; nothing consumed it).
40
- *
41
- * S-529:规则本身死,rev 线不死(`@contract session_policy.rev_survives_delete`)。记号只升不降(`Math.max`),
42
- * 所以反复抹除不会把线走回头;从没写过的键不留记号(没有线就不凭空抬)。**抹除同时终结那一行的血统**:
43
- * `gen` +1,于是下一次写开的是新世代 —— 数字继续爬,但读侧按世代判「这不是我在执行的那一行的延续」。 */
44
- deleteBySession(sessionId: string): Promise<void>;
45
- }
46
- //# sourceMappingURL=memory-session-policy-store.d.ts.map
@@ -1,52 +0,0 @@
1
- import { normalizeRules, loosenReasons, stripRev, SessionPolicyError, } from "@sema-agent/core";
2
- export class MemorySessionPolicyStore {
3
- map = new Map();
4
- marks = new Map();
5
- key(sessionId, principal) {
6
- return JSON.stringify([sessionId, principal ?? null]);
7
- }
8
- async getRules(sessionId, principal) {
9
- const r = this.map.get(this.key(sessionId, principal));
10
- return r ? structuredClone(r) : null;
11
- }
12
- async putRules(sessionId, principal, rules, opts) {
13
- const key = this.key(sessionId, principal);
14
- const prior = this.map.get(key);
15
- if (opts?.expectedRev !== undefined && (prior?.rev ?? 0) !== opts.expectedRev) {
16
- throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${prior?.rev ?? 0})`);
17
- }
18
- const clean = normalizeRules(stripRev(rules));
19
- if (prior && opts?.operator !== true) {
20
- const loosens = loosenReasons(prior, clean);
21
- if (loosens.length > 0) {
22
- throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
23
- }
24
- }
25
- const mark = this.marks.get(key) ?? { rev: 0, gen: 0 };
26
- const gen = prior?.gen ?? mark.gen;
27
- const next = { ...structuredClone(clean), rev: Math.max(prior?.rev ?? 0, mark.rev) + 1, gen };
28
- this.map.set(key, next);
29
- this.marks.set(key, { rev: next.rev, gen });
30
- return structuredClone(next);
31
- }
32
- async listBySession(sessionId) {
33
- const out = [];
34
- for (const [k, rules] of this.map) {
35
- const [sid, principal] = JSON.parse(k);
36
- if (sid === sessionId)
37
- out.push({ principal: principal ?? undefined, rules: structuredClone(rules) });
38
- }
39
- return out;
40
- }
41
- async deleteBySession(sessionId) {
42
- for (const [k, rules] of [...this.map]) {
43
- const [sid] = JSON.parse(k);
44
- if (sid !== sessionId)
45
- continue;
46
- const mark = this.marks.get(k) ?? { rev: 0, gen: 0 };
47
- this.marks.set(k, { rev: Math.max(mark.rev, rules.rev), gen: mark.gen + 1 });
48
- this.map.delete(k);
49
- }
50
- }
51
- }
52
- //# sourceMappingURL=memory-session-policy-store.js.map