@sema-agent/server 7.74.0 → 7.75.1

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.
@@ -2,18 +2,292 @@ import { stripFormatCharacters } from "@sema-agent/core";
2
2
  import { recordFailOpen } from "../observability/fail-open.js";
3
3
  export const normalizeForRedaction = stripFormatCharacters;
4
4
  const REDACTION_TOKEN_RE_SRC = String.raw `«redacted(?::[a-z-]{1,16}){0,2}»`;
5
+ const atLeast = (cls, min) => `${cls}{${min}}${cls}*`;
5
6
  const ENTROPY_SCAN_MAX = 96;
6
7
  const twoStrongAhead = (valueClass) => String.raw `(?=${valueClass}{0,${ENTROPY_SCAN_MAX}}[0-9A-Z]${valueClass}{0,${ENTROPY_SCAN_MAX}}[0-9A-Z])`;
7
8
  const IDENT_CONNECTORS = "[_./-]";
8
9
  const noConnectorAhead = (valueClass) => String.raw `(?!${valueClass}{0,${ENTROPY_SCAN_MAX}}${IDENT_CONNECTORS}${valueClass})`;
9
10
  const WORD_INTERIOR_CLASS = "[A-Za-z0-9]";
10
- const ANSI_CSI = String.raw `(?:\x1b\[|\\u001[bB]\[|\x9b|\\u009[bB])[0-?]*[ -/]*[@-~]`;
11
- const ESCAPED_SEPARATOR_TAIL = String.raw `(?<=\\(?:[bfnrt]|u[0-9a-fA-F]{4})|${ANSI_CSI})`;
12
- const VALUE_ARM_LEFT_BOUNDARY = String.raw `(?:(?<!${WORD_INTERIOR_CLASS})|${ESCAPED_SEPARATOR_TAIL})`;
13
- const valueArm = (prefixSrc, valueClass, min) => new RegExp(`${VALUE_ARM_LEFT_BOUNDARY}${prefixSrc}${twoStrongAhead(valueClass)}${valueClass}{${min},}`, "g");
11
+ const CH_TAB = 9, CH_LF = 10, CH_CR = 13, CH_ESC = 27, CH_SPACE = 32, CH_PLUS = 43, CH_MINUS = 45, CH_SLASH = 47;
12
+ const CH_0 = 48, CH_9 = 57, CH_COLON = 58, CH_EQ = 61, CH_AT = 64, CH_A = 65, CH_Z = 90;
13
+ const CH_LBRACKET = 91, CH_BACKSLASH = 92, CH_UNDERSCORE = 95;
14
+ const CH_a = 97, CH_b = 98, CH_f = 102, CH_n = 110, CH_r = 114, CH_t = 116, CH_u = 117, CH_z = 122, CH_TILDE = 126;
15
+ const CH_QUOTE = 34;
16
+ const CH_CSI8 = 155;
17
+ const CH_LAQUO = 171;
18
+ const ESC_CHAR = String.fromCharCode(CH_ESC);
19
+ const CSI8_CHAR = String.fromCharCode(CH_CSI8);
20
+ const WORD_INTERIOR_BYTES = (() => {
21
+ const one = new RegExp(`^${WORD_INTERIOR_CLASS}$`);
22
+ const table = new Uint8Array(256);
23
+ for (let c = 0; c < table.length; c++)
24
+ if (one.test(String.fromCharCode(c)))
25
+ table[c] = 1;
26
+ return table;
27
+ })();
28
+ function hexDigitValue(c) {
29
+ if (c >= CH_0 && c <= CH_9)
30
+ return c - CH_0;
31
+ if (c >= CH_A && c <= CH_A + 5)
32
+ return c - CH_A + 10;
33
+ if (c >= CH_a && c <= CH_a + 5)
34
+ return c - CH_a + 10;
35
+ return -1;
36
+ }
37
+ function c1LeadLen(s, i, code) {
38
+ if (s.charCodeAt(i) === code)
39
+ return 1;
40
+ if (s.charCodeAt(i) !== CH_BACKSLASH || s.charCodeAt(i + 1) !== CH_u)
41
+ return 0;
42
+ let v = 0;
43
+ for (let k = i + 2; k < i + 6; k++) {
44
+ const d = hexDigitValue(s.charCodeAt(k));
45
+ if (d < 0)
46
+ return 0;
47
+ v = v * 16 + d;
48
+ }
49
+ return v === code ? 6 : 0;
50
+ }
51
+ const CSI_PARAM_RUN = /[0-?]*/y;
52
+ const INTERMEDIATE_RUN = /[ -/]*/y;
53
+ function skipClassRun(re, s, p) {
54
+ if (p >= s.length)
55
+ return p;
56
+ re.lastIndex = p;
57
+ return re.exec(s) === null ? p : re.lastIndex;
58
+ }
59
+ function scanIntermediateChain(s, p, termLo, termHi, mark) {
60
+ const n = s.length;
61
+ for (;;) {
62
+ p = skipClassRun(INTERMEDIATE_RUN, s, p);
63
+ if (p >= n)
64
+ return p;
65
+ const c = s.charCodeAt(p);
66
+ if (c >= termLo && c <= termHi)
67
+ mark(p + 1);
68
+ if (c !== CH_BACKSLASH)
69
+ return p;
70
+ let k = p;
71
+ while (k < n && s.charCodeAt(k) === CH_BACKSLASH)
72
+ k += 1;
73
+ if (k >= n || s.charCodeAt(k) !== CH_QUOTE)
74
+ return p;
75
+ p = k + 1;
76
+ }
77
+ }
78
+ const ESCAPED_SEPARATOR_UNITS = [
79
+ {
80
+ leads: ["\\b", "\\f", "\\n", "\\r", "\\t", "\\u"],
81
+ scan: (s, i, mark) => {
82
+ if (s.charCodeAt(i) !== CH_BACKSLASH)
83
+ return i;
84
+ const c = s.charCodeAt(i + 1);
85
+ if (c === CH_b || c === CH_f || c === CH_n || c === CH_r || c === CH_t)
86
+ mark(i + 2);
87
+ else if (c === CH_u && hexDigitValue(s.charCodeAt(i + 2)) >= 0 && hexDigitValue(s.charCodeAt(i + 3)) >= 0
88
+ && hexDigitValue(s.charCodeAt(i + 4)) >= 0 && hexDigitValue(s.charCodeAt(i + 5)) >= 0)
89
+ mark(i + 6);
90
+ return i;
91
+ },
92
+ },
93
+ {
94
+ leads: [ESC_CHAR, CSI8_CHAR, "\\u"],
95
+ scan: (s, i, mark) => {
96
+ const esc = c1LeadLen(s, i, CH_ESC);
97
+ let p;
98
+ if (esc > 0) {
99
+ if (s.charCodeAt(i + esc) !== CH_LBRACKET)
100
+ return i;
101
+ p = i + esc + 1;
102
+ }
103
+ else {
104
+ const csi8 = c1LeadLen(s, i, CH_CSI8);
105
+ if (csi8 === 0)
106
+ return i;
107
+ p = i + csi8;
108
+ }
109
+ return scanIntermediateChain(s, skipClassRun(CSI_PARAM_RUN, s, p), CH_AT, CH_TILDE, mark);
110
+ },
111
+ },
112
+ {
113
+ leads: [ESC_CHAR, "\\u"],
114
+ scan: (s, i, mark) => {
115
+ const esc = c1LeadLen(s, i, CH_ESC);
116
+ return esc === 0 ? i : scanIntermediateChain(s, i + esc, CH_0, CH_TILDE, mark);
117
+ },
118
+ },
119
+ ];
120
+ const ESCAPED_SEPARATOR_NEEDLES = [...new Set(ESCAPED_SEPARATOR_UNITS.flatMap((u) => u.leads))];
121
+ const NEEDLE_FIRST_CHARS = [...new Set(ESCAPED_SEPARATOR_NEEDLES.map((n) => n.slice(0, 1)))];
122
+ const RUN_SKIPPABLE_BYTES = (() => {
123
+ const table = new Uint8Array(256);
124
+ for (const first of NEEDLE_FIRST_CHARS) {
125
+ const code = first.charCodeAt(0);
126
+ if (code >= table.length)
127
+ continue;
128
+ const single = ESCAPED_SEPARATOR_NEEDLES.some((n) => n.length === 1 && n.charCodeAt(0) === code);
129
+ const selfPair = ESCAPED_SEPARATOR_NEEDLES.some((n) => n.length > 1 && n.charCodeAt(0) === code && n.charCodeAt(1) === code);
130
+ if (!single && !selfPair)
131
+ table[code] = 1;
132
+ }
133
+ return table;
134
+ })();
135
+ function markEscapedSeparatorTails(s) {
136
+ const any = anyValueArmRe();
137
+ let ends;
138
+ let ascending = true;
139
+ const mark = (end) => {
140
+ if (end > s.length)
141
+ return;
142
+ const tail = s.charCodeAt(end - 1);
143
+ if (tail < WORD_INTERIOR_BYTES.length && WORD_INTERIOR_BYTES[tail] !== 1)
144
+ return;
145
+ any.lastIndex = end;
146
+ if (any.exec(s) === null)
147
+ return;
148
+ if (ends === undefined)
149
+ ends = [end];
150
+ else {
151
+ if (end < ends[ends.length - 1])
152
+ ascending = false;
153
+ ends.push(end);
154
+ }
155
+ };
156
+ const nextAt = new Array(NEEDLE_FIRST_CHARS.length).fill(-1);
157
+ const nextLead = (from) => {
158
+ let best = Number.POSITIVE_INFINITY;
159
+ for (let k = 0; k < NEEDLE_FIRST_CHARS.length; k++) {
160
+ if (nextAt[k] < from) {
161
+ const hit = s.indexOf(NEEDLE_FIRST_CHARS[k], from);
162
+ nextAt[k] = hit === -1 ? Number.POSITIVE_INFINITY : hit;
163
+ }
164
+ if (nextAt[k] < best)
165
+ best = nextAt[k];
166
+ }
167
+ return best === Number.POSITIVE_INFINITY ? -1 : best;
168
+ };
169
+ const n = s.length;
170
+ for (let i = nextLead(0); i >= 0;) {
171
+ let resume = i;
172
+ for (const unit of ESCAPED_SEPARATOR_UNITS) {
173
+ const consumed = unit.scan(s, i, mark);
174
+ if (consumed > resume)
175
+ resume = consumed;
176
+ }
177
+ if (resume === i) {
178
+ const code = s.charCodeAt(i);
179
+ if (code < RUN_SKIPPABLE_BYTES.length && RUN_SKIPPABLE_BYTES[code] === 1) {
180
+ let j = i + 1;
181
+ while (j < n && s.charCodeAt(j) === code)
182
+ j += 1;
183
+ if (j > i + 1)
184
+ resume = j - 1;
185
+ }
186
+ }
187
+ i = nextLead(resume > i ? resume : i + 1);
188
+ }
189
+ if (ends === undefined)
190
+ return undefined;
191
+ if (!ascending)
192
+ ends.sort((x, y) => x - y);
193
+ return ends;
194
+ }
195
+ let escapedTailsText;
196
+ let escapedTailsValue;
197
+ function escapedSeparatorTails(s) {
198
+ if (escapedTailsText !== s) {
199
+ escapedTailsValue = markEscapedSeparatorTails(s);
200
+ escapedTailsText = s;
201
+ }
202
+ return escapedTailsValue;
203
+ }
204
+ function forgetEscapedSeparatorTails() {
205
+ escapedTailsText = undefined;
206
+ escapedTailsValue = undefined;
207
+ }
208
+ const VALUE_ARM_STICKY = new Map();
209
+ const VALUE_ARM_BODIES = [];
210
+ const valueArm = (prefixSrc, valueClass, min) => {
211
+ const body = `${prefixSrc}${twoStrongAhead(valueClass)}${atLeast(valueClass, min)}`;
212
+ const g = new RegExp(`(?<!${WORD_INTERIOR_CLASS})${body}`, "g");
213
+ VALUE_ARM_STICKY.set(g, new RegExp(body, "y"));
214
+ VALUE_ARM_BODIES.push(body);
215
+ return g;
216
+ };
217
+ let anyValueArm;
218
+ function anyValueArmRe() {
219
+ return (anyValueArm ??= new RegExp(`(?:${VALUE_ARM_BODIES.join("|")})`, "y"));
220
+ }
221
+ function runValueArm(g, y, repl, text, wantCount) {
222
+ const tails = escapedSeparatorTails(text);
223
+ if (tails === undefined) {
224
+ if (!wantCount)
225
+ return { out: text.replace(g, repl), count: 0 };
226
+ let hits = 0;
227
+ const replaced = text.replace(g, () => {
228
+ hits += 1;
229
+ return repl;
230
+ });
231
+ return { out: replaced, count: hits };
232
+ }
233
+ let out;
234
+ let copied = 0;
235
+ let count = 0;
236
+ let pos = 0;
237
+ let ti = 0;
238
+ let bIdx = -1;
239
+ let bEnd = -1;
240
+ const nextB = (from) => {
241
+ while (ti < tails.length) {
242
+ const p = tails[ti];
243
+ ti += 1;
244
+ if (p < from)
245
+ continue;
246
+ y.lastIndex = p;
247
+ const m = y.exec(text);
248
+ if (m !== null) {
249
+ bIdx = p;
250
+ bEnd = p + m[0].length;
251
+ return;
252
+ }
253
+ }
254
+ bIdx = -1;
255
+ };
256
+ nextB(0);
257
+ g.lastIndex = 0;
258
+ let a = g.exec(text);
259
+ for (;;) {
260
+ if (a !== null && a.index < pos) {
261
+ g.lastIndex = pos;
262
+ a = g.exec(text);
263
+ }
264
+ if (bIdx >= 0 && bIdx < pos)
265
+ nextB(pos);
266
+ let idx;
267
+ let end;
268
+ if (a !== null && (bIdx < 0 || a.index <= bIdx)) {
269
+ idx = a.index;
270
+ end = a.index + a[0].length;
271
+ }
272
+ else if (bIdx >= 0) {
273
+ idx = bIdx;
274
+ end = bEnd;
275
+ }
276
+ else
277
+ break;
278
+ (out ??= []).push(text.slice(copied, idx), repl);
279
+ copied = end;
280
+ count += 1;
281
+ pos = end;
282
+ }
283
+ if (out === undefined)
284
+ return { out: text, count: 0 };
285
+ out.push(text.slice(copied));
286
+ return { out: out.join(""), count };
287
+ }
14
288
  const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
15
- const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${valueClass}{${min},}${tailSrc}` +
16
- `|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${valueClass}{${min},}${tailSrc})`, "g");
289
+ const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${atLeast(valueClass, min)}${tailSrc}` +
290
+ `|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${atLeast(valueClass, min)}${tailSrc})`, "g");
17
291
  const STRUCTURAL_SECRET_PATTERNS = [
18
292
  [/-----BEGIN(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
19
293
  [/---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----[\s\S]{0,8192}?---- END SSH2 ENCRYPTED PRIVATE KEY ----/g, "«redacted:private-key»"],
@@ -30,22 +304,17 @@ const KEYWORD_SECRET_PATTERNS = [
30
304
  ];
31
305
  const SHAPE_SECRET_PATTERNS = [
32
306
  [valueArm("sk-", "[A-Za-z0-9_-]", 16), "«redacted:key»"],
33
- [/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g, "«redacted:gh-token»"],
34
- [/github_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59,}/g, "«redacted:gh-token»"],
35
- [/xox[baprs]-[A-Za-z0-9-]{10,}/g, "«redacted:slack-token»"],
36
- [/AKIA[0-9A-Z]{16,}/g, "«redacted:aws-key»"],
37
- [/ASIA[0-9A-Z]{16,}/g, "«redacted:aws-key»"],
38
- [/(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test|org)_[A-Za-z0-9]{24,}|pypi-[A-Za-z0-9_-]{85,})/g, "«redacted:key»"],
39
- [/xapp-\d-[A-Za-z0-9-]{20,}/g, "«redacted:slack-token»"],
307
+ [new RegExp(String.raw `(?:ghp|gho|ghu|ghs|ghr)_${atLeast("[A-Za-z0-9]", 20)}`, "g"), "«redacted:gh-token»"],
308
+ [new RegExp(String.raw `github_pat_[A-Za-z0-9]{22}_${atLeast("[A-Za-z0-9]", 59)}`, "g"), "«redacted:gh-token»"],
309
+ [new RegExp(String.raw `xox[baprs]-${atLeast("[A-Za-z0-9-]", 10)}`, "g"), "«redacted:slack-token»"],
310
+ [new RegExp(String.raw `AKIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
311
+ [new RegExp(String.raw `ASIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
312
+ [new RegExp(String.raw `(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-${atLeast("[A-Za-z0-9_-]", 20)}|(?:sk|rk)_(?:live|test|org)_${atLeast("[A-Za-z0-9]", 24)}|pypi-${atLeast("[A-Za-z0-9_-]", 85)})`, "g"), "«redacted:key»"],
313
+ [new RegExp(String.raw `xapp-\d-${atLeast("[A-Za-z0-9-]", 20)}`, "g"), "«redacted:slack-token»"],
40
314
  ];
41
315
  const FULL_SECRET_PATTERNS = [...STRUCTURAL_SECRET_PATTERNS, ...KEYWORD_SECRET_PATTERNS, ...SHAPE_SECRET_PATTERNS];
42
316
  const PEM_HEADER_IDENT_MAX = 31;
43
317
  const PEM_BODY_RUN_MIN = 8;
44
- const CH_TAB = 9, CH_LF = 10, CH_CR = 13, CH_SPACE = 32, CH_PLUS = 43, CH_MINUS = 45, CH_SLASH = 47;
45
- const CH_0 = 48, CH_9 = 57, CH_COLON = 58, CH_EQ = 61, CH_A = 65, CH_Z = 90, CH_BACKSLASH = 92, CH_UNDERSCORE = 95;
46
- const CH_a = 97, CH_n = 110, CH_r = 114, CH_t = 116, CH_z = 122;
47
- const CH_QUOTE = 34;
48
- const CH_LAQUO = 171;
49
318
  const isSepCode = (c) => c === CH_SPACE || c === CH_TAB || c === CH_CR || c === CH_LF;
50
319
  const isBase64Code = (c) => (c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= CH_z) || (c >= CH_0 && c <= CH_9) || c === CH_PLUS || c === CH_SLASH || c === CH_EQ;
51
320
  const isIdentStartCode = (c) => (c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= CH_z);
@@ -421,7 +690,7 @@ function redactHeadlessPpkBodies(text) {
421
690
  const FRAGMENT_SECRET_PATTERNS = [
422
691
  [valueArm("sk-", "[A-Za-z0-9_-]", 6), "«redacted:key:partial»"],
423
692
  [valueArm("(?:ghp|gho|ghu|ghs|ghr)_", "[A-Za-z0-9]", 8), "«redacted:gh-token:partial»"],
424
- [/github_pat_[A-Za-z0-9_]{8,}/g, "«redacted:gh-token:partial»"],
693
+ [new RegExp(String.raw `github_pat_${atLeast("[A-Za-z0-9_]", 8)}`, "g"), "«redacted:gh-token:partial»"],
425
694
  [valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4), "«redacted:slack-token:partial»"],
426
695
  [valueArm("AKIA", "[0-9A-Z]", 6), "«redacted:aws-key:partial»"],
427
696
  [valueArm("ASIA", "[0-9A-Z]", 8), "«redacted:aws-key:partial»"],
@@ -454,10 +723,15 @@ const regexRule = ([re, repl], arm) => {
454
723
  throw new Error(`redact: replacement ${JSON.stringify(repl)} uses unsupported ref $${d}`);
455
724
  }
456
725
  const expand = refs.length === 0 ? undefined : (args) => repl.replace(/\$([1-9])/g, (_, d) => args[Number(d)] ?? "");
726
+ const sticky = VALUE_ARM_STICKY.get(re);
727
+ if (sticky !== undefined && refs.length > 0)
728
+ throw new Error(`redact: value arm replacement ${JSON.stringify(repl)} must not use $N refs`);
457
729
  return {
458
730
  label: labelOfToken(repl),
459
731
  arm,
460
732
  run: (text, wantCount, json) => {
733
+ if (sticky !== undefined)
734
+ return runValueArm(re, sticky, repl, text, wantCount);
461
735
  const rx = re;
462
736
  if (!wantCount)
463
737
  return { out: text.replace(rx, repl), count: 0 };
@@ -704,6 +978,7 @@ export function redactSecrets(text, opts) {
704
978
  const wantCount = onRedaction !== undefined && (opts?.observe ?? true);
705
979
  const json = opts?.json === true;
706
980
  const r = applyRules(normalizeForRedaction(text), wantCount, json, opts?.observe ?? true);
981
+ forgetEscapedSeparatorTails();
707
982
  reportHits(r.hits);
708
983
  return r.out;
709
984
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.74.0",
3
+ "version": "7.75.1",
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.1",
57
+ "@sema-agent/core": "7.17.4",
58
58
  "@sema-agent/settings-schema": "1.10.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",
@@ -69,7 +69,7 @@
69
69
  "sharp": "^0.35.3"
70
70
  },
71
71
  "devDependencies": {
72
- "@sema-agent/sdk": "9.1.0",
72
+ "@sema-agent/sdk": "9.3.0",
73
73
  "@types/libsodium-wrappers": "^0.7.14",
74
74
  "@types/node": "22.10.2",
75
75
  "@types/pg": "^8.20.0",