@sema-agent/server 7.75.3 → 7.76.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.
@@ -206,23 +206,28 @@ function forgetEscapedSeparatorTails() {
206
206
  escapedTailsValue = undefined;
207
207
  }
208
208
  const VALUE_ARM_STICKY = new Map();
209
+ const VALUE_ARMS = new Set();
209
210
  const VALUE_ARM_BODIES = [];
210
211
  const valueArm = (prefixSrc, valueClass, min, evidence) => {
211
212
  const body = `${prefixSrc}${twoStrongAhead(valueClass)}${atLeast(valueClass, min)}`;
212
213
  VALUE_ARM_BODIES.push(body);
213
- switch (evidence) {
214
- case "self-evident":
215
- return new RegExp(body, "g");
216
- case "needs-left-boundary": {
217
- const g = new RegExp(`(?<!${WORD_INTERIOR_CLASS})${body}`, "g");
218
- VALUE_ARM_STICKY.set(g, new RegExp(body, "y"));
219
- return g;
220
- }
221
- default: {
222
- const unhandled = evidence;
223
- throw new Error(`redact: unhandled value-arm evidence ${String(unhandled)}`);
214
+ const g = (() => {
215
+ switch (evidence) {
216
+ case "self-evident":
217
+ return new RegExp(body, "g");
218
+ case "needs-left-boundary": {
219
+ const re = new RegExp(`(?<!${WORD_INTERIOR_CLASS})${body}`, "g");
220
+ VALUE_ARM_STICKY.set(re, new RegExp(body, "y"));
221
+ return re;
222
+ }
223
+ default: {
224
+ const unhandled = evidence;
225
+ throw new Error(`redact: unhandled value-arm evidence ${String(unhandled)}`);
226
+ }
224
227
  }
225
- }
228
+ })();
229
+ VALUE_ARMS.add(g);
230
+ return g;
226
231
  };
227
232
  let anyValueArm;
228
233
  function anyValueArmRe() {
@@ -312,10 +317,19 @@ function collectValueArmSpans(g, y, repl, order, text, out) {
312
317
  pos = end;
313
318
  }
314
319
  }
315
- function runValueArm(g, y, repl, text) {
316
- const spans = [];
317
- collectValueArmSpans(g, y, repl, 0, text, spans);
318
- return applySpans(text, spans);
320
+ function collectStickySpans(y, repl, order, text, at, out) {
321
+ let pos = 0;
322
+ for (const p of at) {
323
+ if (p < pos)
324
+ continue;
325
+ y.lastIndex = p;
326
+ const m = y.exec(text);
327
+ if (m === null)
328
+ continue;
329
+ const end = p + m[0].length;
330
+ out.push({ start: p, end, repl, order });
331
+ pos = end;
332
+ }
319
333
  }
320
334
  const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
321
335
  const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${atLeast(valueClass, min)}${tailSrc}` +
@@ -747,9 +761,10 @@ function redactionTokenEndAt(s, p, limit) {
747
761
  return 0;
748
762
  }
749
763
  const labelOfToken = (token) => token.replace(/[«»]/g, "").replace(/^redacted:/, "");
764
+ const armOfToken = (repl) => (repl.match(/«[^»]*»/g) ?? [repl]).some((t) => t.endsWith(":partial»")) ? "fragment" : "full";
750
765
  const NO_HITS = [];
751
766
  const singleHit = (label, arm, r) => r.count > 0 ? { out: r.out, hits: [{ label, count: r.count, arm }] } : { out: r.out, hits: NO_HITS };
752
- const regexRule = ([re, repl], arm) => {
767
+ const regexRule = ([re, repl]) => {
753
768
  const groupCount = (new RegExp(`${re.source}|`).exec("")?.length ?? 1) - 1;
754
769
  const refs = [...repl.matchAll(/\$(.)/g)].map(([, d]) => d ?? "");
755
770
  for (const d of refs) {
@@ -757,13 +772,11 @@ const regexRule = ([re, repl], arm) => {
757
772
  throw new Error(`redact: replacement ${JSON.stringify(repl)} uses unsupported ref $${d}`);
758
773
  }
759
774
  const expand = refs.length === 0 ? undefined : (args) => repl.replace(/\$([1-9])/g, (_, d) => args[Number(d)] ?? "");
760
- const sticky = VALUE_ARM_STICKY.get(re);
761
- if (sticky !== undefined && refs.length > 0)
762
- throw new Error(`redact: value arm replacement ${JSON.stringify(repl)} must not use $N refs`);
775
+ const arm = armOfToken(repl);
776
+ if (VALUE_ARMS.has(re) || arm === "fragment")
777
+ throw new Error(`redact: rule ${JSON.stringify(repl)} belongs in the arm pool, not a per-arm section`);
763
778
  const label = labelOfToken(repl);
764
779
  const applyOnce = (text, wantCount) => {
765
- if (sticky !== undefined)
766
- return runValueArm(re, sticky, repl, text);
767
780
  if (!wantCount)
768
781
  return { out: text.replace(re, repl), count: 0 };
769
782
  let n = 0;
@@ -970,12 +983,13 @@ function redactPpkContainers(text, record) {
970
983
  }
971
984
  const URL_USERINFO_LABEL = labelOfToken(URL_USERINFO_PATTERN[1]);
972
985
  const urlUserinfoRule = { run: (text, _wantCount, json) => singleHit(URL_USERINFO_LABEL, "full", redactUrlUserinfo(text, json)) };
973
- const shapeSectionRule = (() => {
974
- const arms = SHAPE_SECRET_PATTERNS.map(([re, repl], order) => {
986
+ const armPoolRule = (() => {
987
+ const arms = [...SHAPE_SECRET_PATTERNS, ...FRAGMENT_SECRET_PATTERNS].map(([re, repl], order) => {
975
988
  if (/\$./.test(repl))
976
- throw new Error(`redact: shape-section replacement ${JSON.stringify(repl)} must not use $N refs`);
989
+ throw new Error(`redact: arm-pool replacement ${JSON.stringify(repl)} must not use $N refs`);
977
990
  return { g: re, y: VALUE_ARM_STICKY.get(re), repl, order };
978
991
  });
992
+ const boundaryArms = arms.flatMap((a) => (a.y === undefined ? [] : [{ y: a.y, repl: a.repl, order: a.order }]));
979
993
  return {
980
994
  run: (text, wantCount) => {
981
995
  const spans = [];
@@ -985,6 +999,24 @@ const shapeSectionRule = (() => {
985
999
  else
986
1000
  collectValueArmSpans(a.g, a.y, a.repl, a.order, text, spans);
987
1001
  }
1002
+ const tried = new Set();
1003
+ const freshEnds = (from) => {
1004
+ const at = [];
1005
+ for (let k = from; k < spans.length; k++) {
1006
+ const e = spans[k].end;
1007
+ if (tried.has(e))
1008
+ continue;
1009
+ tried.add(e);
1010
+ at.push(e);
1011
+ }
1012
+ return at.sort((x, y) => x - y);
1013
+ };
1014
+ for (let at = freshEnds(0); at.length > 0;) {
1015
+ const before = spans.length;
1016
+ for (const a of boundaryArms)
1017
+ collectStickySpans(a.y, a.repl, a.order, text, at, spans);
1018
+ at = freshEnds(before);
1019
+ }
988
1020
  const kept = resolveSpanOverlaps(spans);
989
1021
  const out = applySpans(text, kept).out;
990
1022
  if (!wantCount || kept.length === 0)
@@ -992,18 +1024,17 @@ const shapeSectionRule = (() => {
992
1024
  const byToken = new Map();
993
1025
  for (const sp of kept)
994
1026
  byToken.set(sp.repl, (byToken.get(sp.repl) ?? 0) + 1);
995
- return { out, hits: [...byToken].map(([token, count]) => ({ label: labelOfToken(token), count, arm: "full" })) };
1027
+ return { out, hits: [...byToken].map(([token, count]) => ({ label: labelOfToken(token), count, arm: armOfToken(token) })) };
996
1028
  },
997
1029
  };
998
1030
  })();
999
1031
  const SECRET_RULES = [
1000
- ...STRUCTURAL_SECRET_PATTERNS.map((p) => (p === URL_USERINFO_PATTERN ? urlUserinfoRule : regexRule(p, "full"))),
1001
- { run: (text, _wantCount, _json, observe) => singleHit(labelOfToken(PPK_CONTAINER_TOKEN), "full", redactPpkContainers(text, observe)) },
1002
- { run: (text, _wantCount, json) => singleHit(labelOfToken(PEM_FRAGMENT_TOKEN), "fragment", redactPemFragments(text, json)) },
1003
- { run: (text) => singleHit(labelOfToken(HEADLESS_PPK_TOKEN), "fragment", redactHeadlessPpkBodies(text)) },
1004
- ...KEYWORD_SECRET_PATTERNS.map((p) => regexRule(p, "full")),
1005
- shapeSectionRule,
1006
- ...FRAGMENT_SECRET_PATTERNS.map((p) => regexRule(p, "fragment")),
1032
+ ...STRUCTURAL_SECRET_PATTERNS.map((p) => (p === URL_USERINFO_PATTERN ? urlUserinfoRule : regexRule(p))),
1033
+ { run: (text, _wantCount, _json, observe) => singleHit(labelOfToken(PPK_CONTAINER_TOKEN), armOfToken(PPK_CONTAINER_TOKEN), redactPpkContainers(text, observe)) },
1034
+ { run: (text, _wantCount, json) => singleHit(labelOfToken(PEM_FRAGMENT_TOKEN), armOfToken(PEM_FRAGMENT_TOKEN), redactPemFragments(text, json)) },
1035
+ { run: (text) => singleHit(labelOfToken(HEADLESS_PPK_TOKEN), armOfToken(HEADLESS_PPK_TOKEN), redactHeadlessPpkBodies(text)) },
1036
+ ...KEYWORD_SECRET_PATTERNS.map((p) => regexRule(p)),
1037
+ armPoolRule,
1007
1038
  { run: (text, _wantCount, json) => ({ out: redactUrlUserinfo(text, json).out, hits: NO_HITS }) },
1008
1039
  ];
1009
1040
  let onRedaction;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.75.3",
3
+ "version": "7.76.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",
@@ -54,7 +54,7 @@
54
54
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
55
55
  },
56
56
  "dependencies": {
57
- "@sema-agent/core": "7.17.5",
57
+ "@sema-agent/core": "7.17.6",
58
58
  "@sema-agent/settings-schema": "1.10.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",