@sema-agent/server 7.77.0 → 7.78.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.
Files changed (43) hide show
  1. package/dist/approval-card.d.ts +24 -1
  2. package/dist/approval-card.js +13 -0
  3. package/dist/boot/resolve-spec.js +3 -0
  4. package/dist/boot/runner-deps.js +3 -1
  5. package/dist/config-catalog.js +1 -0
  6. package/dist/config-center/read-warnings.d.ts +10 -1
  7. package/dist/config-center/read-warnings.js +5 -1
  8. package/dist/config-provider.js +2 -2
  9. package/dist/config-types.d.ts +13 -1
  10. package/dist/config.js +2 -1
  11. package/dist/http/routes/approvals-assistant.d.ts +22 -0
  12. package/dist/http/routes/approvals-assistant.js +4 -1
  13. package/dist/http/routes/tasks.js +14 -9
  14. package/dist/http/routes/workflows.js +7 -1
  15. package/dist/http/server.d.ts +28 -4
  16. package/dist/http/server.js +47 -13
  17. package/dist/observability/fail-open.d.ts +4 -0
  18. package/dist/observability/fail-open.js +4 -0
  19. package/dist/observability/run-terminal-log.js +1 -0
  20. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -3
  21. package/dist/parked-decide.d.ts +27 -12
  22. package/dist/parked-decide.js +1 -1
  23. package/dist/plugins/workflow-run-store-sql.d.ts +7 -3
  24. package/dist/request-key-closure.d.ts +30 -0
  25. package/dist/request-key-closure.js +56 -0
  26. package/dist/runs.js +3 -1
  27. package/dist/runtime-governance.d.ts +18 -4
  28. package/dist/runtime-governance.js +2 -2
  29. package/dist/spec-fields.d.ts +3 -1
  30. package/dist/spec-fields.js +4 -5
  31. package/dist/task-settings.d.ts +18 -16
  32. package/dist/task-settings.js +17 -29
  33. package/dist/tool-approval.d.ts +35 -3
  34. package/dist/tool-approval.js +41 -19
  35. package/dist/trace/core-keyset-guard.d.ts +8 -8
  36. package/dist/trace/ledger-events.d.ts +7 -1
  37. package/dist/trace/ledger-sink.js +19 -6
  38. package/dist/trace/model-segment.d.ts +32 -2
  39. package/dist/trace/model-segment.js +16 -6
  40. package/dist/trace/project.d.ts +95 -3
  41. package/dist/trace/project.js +128 -12
  42. package/dist/trace/redact.js +159 -13
  43. package/package.json +4 -4
@@ -78,10 +78,13 @@ export function aggregateModelUsage(events) {
78
78
  }
79
79
  function identityFields(src) {
80
80
  return {
81
- ...(src.eventId ? { eventId: String(src.eventId) } : {}),
82
- ...(src.parentToolCallId ? { parentToolCallId: String(src.parentToolCallId) } : {}),
81
+ ...(identityKey(src.eventId) !== undefined ? { eventId: identityKey(src.eventId) } : {}),
82
+ ...(identityKey(src.parentToolCallId) !== undefined ? { parentToolCallId: identityKey(src.parentToolCallId) } : {}),
83
83
  };
84
84
  }
85
+ function identityKey(v) {
86
+ return typeof v === "string" && v !== "" ? v : undefined;
87
+ }
85
88
  export function modelTextEventData(ev) {
86
89
  return { text: ev.text, ...(ev.eventId ? { eventId: ev.eventId } : {}) };
87
90
  }
@@ -110,8 +113,12 @@ function screenedGateOrThrow(v) {
110
113
  recordFailOpen("server.trace.gate-outcome-defective", "disposition is not one of the two arms");
111
114
  return undefined;
112
115
  }
116
+ const classifierFallbackRaw = d.classifier !== null && typeof d.classifier === "object" ? d.classifier.fallback : undefined;
113
117
  const shapeDefect = (d.kind === "denied" && typeof d.deniedBy !== "string" && "disposition.deniedBy is not a string") ||
114
118
  (d.kind === "denied" && d.cause !== undefined && typeof d.cause !== "string" && "disposition.cause is not a string") ||
119
+ (d.classifier !== undefined && (d.classifier === null || typeof d.classifier !== "object") && "disposition.classifier is not an object") ||
120
+ (classifierFallbackRaw !== undefined && (classifierFallbackRaw === null || typeof classifierFallbackRaw !== "object") && "disposition.classifier.fallback is not an object") ||
121
+ (classifierFallbackRaw !== undefined && classifierFallbackRaw !== null && typeof classifierFallbackRaw === "object" && typeof classifierFallbackRaw.cause !== "string" && "disposition.classifier.fallback.cause is not a string") ||
115
122
  (g.settlement !== undefined && (g.settlement === null || typeof g.settlement !== "object") && "settlement is not an object") ||
116
123
  (g.settlement !== undefined && g.settlement !== null && typeof g.settlement.kind !== "string" && "settlement.kind is not a string") ||
117
124
  (g.origin !== undefined && typeof g.origin !== "string" && "origin is not a string") ||
@@ -125,6 +132,16 @@ function screenedGateOrThrow(v) {
125
132
  recordFailOpen("server.trace.gate-outcome-defective", defects.join("; "));
126
133
  return undefined;
127
134
  }
135
+ const attribution = d.classifier;
136
+ const classifier = attribution === undefined
137
+ ? undefined
138
+ : {
139
+ modelRequested: attribution.modelRequested,
140
+ modelUsed: attribution.modelUsed,
141
+ ...(attribution.fallback === undefined
142
+ ? {}
143
+ : { fallback: { from: attribution.fallback.from, to: attribution.fallback.to, cause: attribution.fallback.cause } }),
144
+ };
128
145
  const settlement = g.settlement === undefined ? undefined : projectSettlement(g.settlement);
129
146
  if (g.settlement !== undefined && settlement === undefined) {
130
147
  recordFailOpen("server.trace.gate-outcome-defective", "settlement.who is not an object with a party in the closed set");
@@ -132,8 +149,8 @@ function screenedGateOrThrow(v) {
132
149
  }
133
150
  return {
134
151
  disposition: d.kind === "denied"
135
- ? { kind: "denied", deniedBy: d.deniedBy, ...(d.cause === undefined ? {} : { cause: d.cause }) }
136
- : { kind: "allowed" },
152
+ ? { kind: "denied", deniedBy: d.deniedBy, ...(d.cause === undefined ? {} : { cause: d.cause }), ...(classifier === undefined ? {} : { classifier }) }
153
+ : { kind: "allowed", ...(classifier === undefined ? {} : { classifier }) },
137
154
  ...(settlement === undefined ? {} : { settlement }),
138
155
  ...(g.origin === undefined ? {} : { origin: g.origin }),
139
156
  };
@@ -178,15 +195,17 @@ export function textEndEventData(ev) {
178
195
  return null;
179
196
  return segmentEndFields(ev.content, ev);
180
197
  }
181
- function segmentEndFields(content, ev) {
198
+ export function liveIdentityFields(ev) {
182
199
  return {
183
- content: redactModelText(content),
184
- ...(ev.eventId !== undefined ? { eventId: ev.eventId } : {}),
185
- ...(ev.parentToolCallId !== undefined ? { parentToolCallId: ev.parentToolCallId } : {}),
186
- ...(ev.sourceTaskId !== undefined ? { sourceTaskId: ev.sourceTaskId } : {}),
187
- ...(ev.bgAgentId !== undefined ? { bgAgentId: ev.bgAgentId } : {}),
200
+ ...(identityKey(ev.eventId) !== undefined ? { eventId: identityKey(ev.eventId) } : {}),
201
+ ...(identityKey(ev.parentToolCallId) !== undefined ? { parentToolCallId: identityKey(ev.parentToolCallId) } : {}),
202
+ ...(identityKey(ev.sourceTaskId) !== undefined ? { sourceTaskId: identityKey(ev.sourceTaskId) } : {}),
203
+ ...(identityKey(ev.bgAgentId) !== undefined ? { bgAgentId: identityKey(ev.bgAgentId) } : {}),
188
204
  };
189
205
  }
206
+ function segmentEndFields(content, ev) {
207
+ return { content: redactModelText(content), ...liveIdentityFields(ev) };
208
+ }
190
209
  export function reasoningEndEventData(ev) {
191
210
  if (typeof ev.content !== "string" || ev.content.trim().length === 0)
192
211
  return null;
@@ -210,6 +229,43 @@ export function toolEndEventData(ev) {
210
229
  ...identityFields(ev),
211
230
  };
212
231
  }
232
+ const dropProjectedFace = (what) => {
233
+ recordFailOpen("server.trace.projection-input-malformed", what);
234
+ return undefined;
235
+ };
236
+ export function toolProgressEventData(ev) {
237
+ const id = typeof ev.toolCallId === "string" && ev.toolCallId !== "" ? ev.toolCallId : undefined;
238
+ const name = typeof ev.toolName === "string" && ev.toolName !== "" ? ev.toolName : undefined;
239
+ if (id === undefined || name === undefined)
240
+ return dropProjectedFace("tool_progress is missing toolCallId/toolName");
241
+ const n = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
242
+ const hasOutput = typeof ev.output === "string";
243
+ return {
244
+ toolCallId: id,
245
+ toolName: name,
246
+ ...(n(ev.elapsedTimeSeconds) !== undefined ? { elapsedTimeSeconds: n(ev.elapsedTimeSeconds) } : {}),
247
+ ...(hasOutput ? { output: redactDeep(ev.output) } : {}),
248
+ ...(hasOutput && n(ev.totalLines) !== undefined ? { totalLines: n(ev.totalLines) } : {}),
249
+ ...(hasOutput && n(ev.totalBytes) !== undefined ? { totalBytes: n(ev.totalBytes) } : {}),
250
+ ...identityFields(ev),
251
+ };
252
+ }
253
+ export function toolDisclosureEventData(ev) {
254
+ const policy = typeof ev.policy === "string" && ev.policy !== "" ? ev.policy : undefined;
255
+ const names = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : undefined);
256
+ const deferred = names(ev.deferred);
257
+ const activated = names(ev.activated);
258
+ if (policy === undefined || deferred === undefined || activated === undefined)
259
+ return dropProjectedFace("tool_disclosure is missing policy/deferred/activated");
260
+ const threshold = typeof ev.thresholdPercent === "number" && Number.isFinite(ev.thresholdPercent) ? ev.thresholdPercent : undefined;
261
+ return {
262
+ policy,
263
+ ...(threshold !== undefined ? { thresholdPercent: threshold } : {}),
264
+ deferred,
265
+ activated,
266
+ ...identityFields(ev),
267
+ };
268
+ }
213
269
  export function taskProgressEventData(ev) {
214
270
  return {
215
271
  taskId: ev.taskId,
@@ -390,6 +446,41 @@ export function wiringManifestEventData(ev) {
390
446
  })();
391
447
  const toolsIn = m.tools;
392
448
  const tools = toolsIn === undefined ? undefined : Value.Check(ToolRoster, toolsIn) ? toolsIn : (recordFailOpen("server.trace.tool-roster-malformed", "wiring_manifest.tools did not fit core ToolRoster"), undefined);
449
+ const hooksIn = m.hooks;
450
+ const hooks = (() => {
451
+ if (hooksIn === undefined)
452
+ return undefined;
453
+ if (!Array.isArray(hooksIn))
454
+ return dropProjectedFace("wiring_manifest.hooks is not an array");
455
+ const rows = [];
456
+ for (const raw of hooksIn) {
457
+ const e = sec(raw);
458
+ if (e === undefined) {
459
+ dropProjectedFace("wiring_manifest.hooks[] row is not an object");
460
+ continue;
461
+ }
462
+ const family = str(e.family);
463
+ const owner = str(e.owner);
464
+ if (family === undefined || owner === undefined) {
465
+ dropProjectedFace("wiring_manifest.hooks[] row is missing family/owner");
466
+ continue;
467
+ }
468
+ rows.push({ family, owner, ...(e.observational === true ? { observational: true } : {}) });
469
+ }
470
+ return rows.length > 0 ? rows : undefined;
471
+ })();
472
+ const lspIn = sec(m.lsp);
473
+ const lsp = (() => {
474
+ if (m.lsp === undefined)
475
+ return undefined;
476
+ if (lspIn === undefined)
477
+ return dropProjectedFace("wiring_manifest.lsp is not an object");
478
+ const mounted = bool(lspIn.mounted);
479
+ if (mounted === undefined)
480
+ return dropProjectedFace("wiring_manifest.lsp.mounted is not a boolean");
481
+ const lane = str(lspIn.lane);
482
+ return { mounted, ...(lane === undefined ? {} : { lane }) };
483
+ })();
393
484
  const mcpIn = m.mcp;
394
485
  const mcp = (() => {
395
486
  if (!Array.isArray(mcpIn))
@@ -430,6 +521,8 @@ export function wiringManifestEventData(ev) {
430
521
  ...(autoMode !== undefined ? { autoMode } : {}),
431
522
  ...(tools !== undefined ? { tools } : {}),
432
523
  ...(mcp !== undefined ? { mcp } : {}),
524
+ ...(hooks !== undefined ? { hooks } : {}),
525
+ ...(lsp !== undefined ? { lsp } : {}),
433
526
  ...identityFields(ev),
434
527
  };
435
528
  }
@@ -505,8 +598,7 @@ export function compactedEventData(ev) {
505
598
  return Object.keys(out).length > 0 ? out : undefined;
506
599
  })();
507
600
  return {
508
- ...(ev.eventId !== undefined ? { eventId: ev.eventId } : {}),
509
- ...(ev.parentToolCallId !== undefined ? { parentToolCallId: ev.parentToolCallId } : {}),
601
+ ...identityFields(ev),
510
602
  ...(typeof ev.trigger === "string" ? { trigger: ev.trigger } : {}),
511
603
  ...(num(ev.tokensBefore) !== undefined ? { tokensBefore: num(ev.tokensBefore) } : {}),
512
604
  ...(ev.preserved_segment?.firstKeptEntryId ? { preserved_segment: { firstKeptEntryId: ev.preserved_segment.firstKeptEntryId } } : {}),
@@ -535,9 +627,32 @@ export function contextUsageEventData(ev) {
535
627
  ...(n(ev.usedTokens) !== undefined ? { usedTokens: n(ev.usedTokens) } : {}),
536
628
  ...(n(ev.windowTokens) !== undefined ? { windowTokens: n(ev.windowTokens) } : {}),
537
629
  ...(n(ev.compactAtTokens) !== undefined ? { compactAtTokens: n(ev.compactAtTokens) } : {}),
630
+ ...contextUsageSectionsField(ev.sections),
538
631
  ...identityFields(ev),
539
632
  };
540
633
  }
634
+ function contextUsageSectionsField(sections) {
635
+ if (sections === undefined)
636
+ return {};
637
+ if (!Array.isArray(sections)) {
638
+ dropProjectedFace("context_usage.sections is not an array");
639
+ return {};
640
+ }
641
+ const out = [];
642
+ for (const raw of sections) {
643
+ if (typeof raw !== "object" || raw === null) {
644
+ dropProjectedFace("context_usage.sections[] element is not an object");
645
+ continue;
646
+ }
647
+ const { id, tokens, kind } = raw;
648
+ if (typeof id !== "string" || id === "" || typeof tokens !== "number" || !Number.isFinite(tokens)) {
649
+ dropProjectedFace("context_usage.sections[] element is missing id/tokens");
650
+ continue;
651
+ }
652
+ out.push({ id, tokens, ...(typeof kind === "string" && kind !== "" ? { kind } : {}) });
653
+ }
654
+ return out.length > 0 ? { sections: out } : {};
655
+ }
541
656
  export function turnEndEventData(ev) {
542
657
  return {
543
658
  ...(ev.usage ? { usage: ev.usage } : {}),
@@ -711,6 +826,7 @@ function mapTraceEventInner(type, seq, data) {
711
826
  case "wiring_manifest":
712
827
  case "human_input":
713
828
  case "tool_roster_delta":
829
+ case "tool_disclosure":
714
830
  return null;
715
831
  default:
716
832
  return null;
@@ -233,12 +233,150 @@ let anyValueArm;
233
233
  function anyValueArmRe() {
234
234
  return (anyValueArm ??= new RegExp(`(?:${VALUE_ARM_BODIES.join("|")})`, "y"));
235
235
  }
236
- function collectRegexSpans(g, repl, order, text, out) {
236
+ const HEAD_CLASS_ESCAPES = new Set(["d", "D", "w", "W", "s", "S"]);
237
+ const HEAD_LITERAL_ESCAPES = new Set(["\\", "/", "-", ".", "+", "*", "?", "(", ")", "[", "]", "{", "}", "|", "^", "$"]);
238
+ function headOfAtoms(atoms, src) {
239
+ const tail = atoms[atoms.length - 1];
240
+ if (tail === undefined || !tail.unbounded || tail.cls === undefined) {
241
+ throw new Error(`redact: arm-pool pattern /${src}/ must end in a greedy unbounded single-class value run`);
242
+ }
243
+ let pos = 0;
244
+ let head = 0;
245
+ for (let k = 0; k < atoms.length - 1; k++) {
246
+ const a = atoms[k];
247
+ if (a.cls !== tail.cls)
248
+ head = pos + a.len;
249
+ pos += a.len;
250
+ }
251
+ return head;
252
+ }
253
+ const HEAD_GROUP_OPEN = /^\((\?:|\?=|\?!|\?<=|\?<!)/;
254
+ function parseHeadAlternative(src, from) {
255
+ let seqs = [[]];
256
+ let i = from;
257
+ let closed = false;
258
+ while (i < src.length) {
259
+ const c = src[i];
260
+ if (c === "|" || c === ")")
261
+ break;
262
+ if (closed)
263
+ throw new Error(`redact: arm-pool pattern /${src}/ has trailing syntax after its value run`);
264
+ let base;
265
+ if (c === "(") {
266
+ const open = HEAD_GROUP_OPEN.exec(src.slice(i));
267
+ if (open === null)
268
+ throw new Error(`redact: head derivation cannot bound group at ${i} in /${src}/`);
269
+ const mark = open[1];
270
+ const inner = parseHeadAlternatives(src, i + 1 + mark.length);
271
+ if (src[inner.next] !== ")")
272
+ throw new Error(`redact: unbalanced group at ${i} in /${src}/`);
273
+ i = inner.next + 1;
274
+ if (mark !== "?:")
275
+ continue;
276
+ if (inner.alts.some((alt) => alt.some((a) => a.unbounded))) {
277
+ seqs = inner.alts.flatMap((alt) => seqs.map((s) => [...s, ...alt]));
278
+ closed = true;
279
+ continue;
280
+ }
281
+ base = { len: Math.max(...inner.alts.map((alt) => alt.reduce((n, a) => n + a.len, 0))), cls: undefined };
282
+ }
283
+ else if (c === "[") {
284
+ let k = i + 1;
285
+ while (k < src.length && src[k] !== "]")
286
+ k += src[k] === "\\" ? 2 : 1;
287
+ if (k >= src.length)
288
+ throw new Error(`redact: unterminated character class at ${i} in /${src}/`);
289
+ base = { len: 1, cls: src.slice(i, k + 1) };
290
+ i = k + 1;
291
+ }
292
+ else if (c === "\\") {
293
+ const e = src[i + 1];
294
+ if (e === undefined)
295
+ throw new Error(`redact: dangling escape at ${i} in /${src}/`);
296
+ if (HEAD_CLASS_ESCAPES.has(e))
297
+ base = { len: 1, cls: `\\${e}` };
298
+ else if (HEAD_LITERAL_ESCAPES.has(e))
299
+ base = { len: 1, cls: undefined };
300
+ else
301
+ throw new Error(`redact: head derivation cannot bound escape \\${e} at ${i} in /${src}/`);
302
+ i += 2;
303
+ }
304
+ else if (c === "." || c === "^" || c === "$" || c === "*" || c === "+" || c === "?" || c === "{") {
305
+ throw new Error(`redact: head derivation cannot bound ${JSON.stringify(c)} at ${i} in /${src}/`);
306
+ }
307
+ else {
308
+ base = { len: 1, cls: undefined };
309
+ i += 1;
310
+ }
311
+ let atom = { ...base, unbounded: false };
312
+ const q = src[i];
313
+ if (q === "*" || q === "+") {
314
+ atom = { ...base, unbounded: true };
315
+ i += 1;
316
+ }
317
+ else if (q === "?") {
318
+ i += 1;
319
+ }
320
+ else if (q === "{") {
321
+ const m = /^\{(\d+)(,(\d*))?\}/.exec(src.slice(i));
322
+ if (m === null)
323
+ throw new Error(`redact: head derivation cannot bound quantifier at ${i} in /${src}/`);
324
+ const lo = Number(m[1]);
325
+ const hi = m[2] === undefined ? lo : m[3] === "" ? -1 : Number(m[3]);
326
+ atom = hi < 0 ? { ...base, unbounded: true } : { len: base.len * hi, cls: base.cls, unbounded: false };
327
+ i += m[0].length;
328
+ }
329
+ if (src[i] === "?")
330
+ throw new Error(`redact: head derivation cannot bound a lazy quantifier at ${i} in /${src}/`);
331
+ if (atom.unbounded)
332
+ closed = true;
333
+ for (const s of seqs)
334
+ s.push(atom);
335
+ }
336
+ return { seqs, next: i };
337
+ }
338
+ function parseHeadAlternatives(src, from) {
339
+ const alts = [];
340
+ let i = from;
341
+ for (;;) {
342
+ const one = parseHeadAlternative(src, i);
343
+ alts.push(...one.seqs);
344
+ i = one.next;
345
+ if (src[i] !== "|")
346
+ break;
347
+ i += 1;
348
+ }
349
+ return { alts, next: i };
350
+ }
351
+ function literalHeadLength(re) {
352
+ if (/[uv]/.test(re.flags))
353
+ throw new Error(`redact: head derivation does not support the u/v flags on /${re.source}/`);
354
+ const src = re.source;
355
+ const parsed = parseHeadAlternatives(src, 0);
356
+ if (parsed.next !== src.length)
357
+ throw new Error(`redact: head derivation stopped at ${parsed.next} in /${src}/`);
358
+ return Math.max(...parsed.alts.map((alt) => headOfAtoms(alt, src)));
359
+ }
360
+ function scanResumeAfterHit(start, end, head) {
361
+ return Math.max(start + 1, end - Math.max(head - 1, 0));
362
+ }
363
+ function headWindowCandidates(at, maxHead) {
364
+ const out = [];
365
+ let last = -1;
366
+ for (const e of at) {
367
+ for (let p = Math.max(e - Math.max(maxHead - 1, 0), last + 1, 0); p <= e; p++) {
368
+ out.push({ p, back: e - p });
369
+ last = p;
370
+ }
371
+ }
372
+ return out;
373
+ }
374
+ function collectRegexSpans(g, head, repl, order, text, out) {
237
375
  g.lastIndex = 0;
238
376
  for (let m = g.exec(text); m !== null; m = g.exec(text)) {
239
377
  const end = m.index + m[0].length;
240
378
  out.push({ start: m.index, end, repl, order });
241
- g.lastIndex = end > m.index ? end : m.index + 1;
379
+ g.lastIndex = scanResumeAfterHit(m.index, end, head);
242
380
  }
243
381
  }
244
382
  function resolveSpanOverlaps(spans) {
@@ -265,10 +403,10 @@ function applySpans(text, spans) {
265
403
  parts.push(text.slice(copied));
266
404
  return { out: parts.join(""), count: spans.length };
267
405
  }
268
- function collectValueArmSpans(g, y, repl, order, text, out) {
406
+ function collectValueArmSpans(g, y, head, repl, order, text, out) {
269
407
  const tails = escapedSeparatorTails(text);
270
408
  if (tails === undefined) {
271
- collectRegexSpans(g, repl, order, text, out);
409
+ collectRegexSpans(g, head, repl, order, text, out);
272
410
  return;
273
411
  }
274
412
  let pos = 0;
@@ -314,12 +452,14 @@ function collectValueArmSpans(g, y, repl, order, text, out) {
314
452
  else
315
453
  break;
316
454
  out.push({ start: idx, end, repl, order });
317
- pos = end;
455
+ pos = scanResumeAfterHit(idx, end, head);
318
456
  }
319
457
  }
320
- function collectStickySpans(y, repl, order, text, at, out) {
458
+ function collectStickySpans(y, head, repl, order, text, at, out) {
321
459
  let pos = 0;
322
- for (const p of at) {
460
+ for (const { p, back } of at) {
461
+ if (back >= head)
462
+ continue;
323
463
  if (p < pos)
324
464
  continue;
325
465
  y.lastIndex = p;
@@ -328,7 +468,7 @@ function collectStickySpans(y, repl, order, text, at, out) {
328
468
  continue;
329
469
  const end = p + m[0].length;
330
470
  out.push({ start: p, end, repl, order });
331
- pos = end;
471
+ pos = scanResumeAfterHit(p, end, head);
332
472
  }
333
473
  }
334
474
  const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
@@ -1000,17 +1140,22 @@ const armPoolRule = (() => {
1000
1140
  const arms = [...SHAPE_SECRET_PATTERNS, ...FRAGMENT_SECRET_PATTERNS].map(([re, repl], order) => {
1001
1141
  if (/\$./.test(repl))
1002
1142
  throw new Error(`redact: arm-pool replacement ${JSON.stringify(repl)} must not use $N refs`);
1003
- return { g: re, y: VALUE_ARM_STICKY.get(re), repl, order };
1143
+ const y = VALUE_ARM_STICKY.get(re);
1144
+ const head = literalHeadLength(re);
1145
+ if (y !== undefined && head === 0)
1146
+ throw new Error(`redact: boundary arm /${re.source}/ has a zero-length structural head — the head-window screen would skip every candidate position`);
1147
+ return { g: re, y, head, repl, order };
1004
1148
  });
1005
- const boundaryArms = arms.flatMap((a) => (a.y === undefined ? [] : [{ y: a.y, repl: a.repl, order: a.order }]));
1149
+ const boundaryArms = arms.flatMap((a) => (a.y === undefined ? [] : [{ y: a.y, head: a.head, repl: a.repl, order: a.order }]));
1150
+ const maxBoundaryHead = Math.max(1, ...boundaryArms.map((a) => a.head));
1006
1151
  return {
1007
1152
  run: (text, wantCount) => {
1008
1153
  const spans = [];
1009
1154
  for (const a of arms) {
1010
1155
  if (a.y === undefined)
1011
- collectRegexSpans(a.g, a.repl, a.order, text, spans);
1156
+ collectRegexSpans(a.g, a.head, a.repl, a.order, text, spans);
1012
1157
  else
1013
- collectValueArmSpans(a.g, a.y, a.repl, a.order, text, spans);
1158
+ collectValueArmSpans(a.g, a.y, a.head, a.repl, a.order, text, spans);
1014
1159
  }
1015
1160
  const tried = new Set();
1016
1161
  const freshEnds = (from) => {
@@ -1026,8 +1171,9 @@ const armPoolRule = (() => {
1026
1171
  };
1027
1172
  for (let at = freshEnds(0); at.length > 0;) {
1028
1173
  const before = spans.length;
1174
+ const cand = headWindowCandidates(at, maxBoundaryHead);
1029
1175
  for (const a of boundaryArms)
1030
- collectStickySpans(a.y, a.repl, a.order, text, at, spans);
1176
+ collectStickySpans(a.y, a.head, a.repl, a.order, text, cand, spans);
1031
1177
  at = freshEnds(before);
1032
1178
  }
1033
1179
  const kept = resolveSpanOverlaps(spans);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.77.0",
3
+ "version": "7.78.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,8 +54,8 @@
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.7",
58
- "@sema-agent/settings-schema": "1.10.0",
57
+ "@sema-agent/core": "7.19.0",
58
+ "@sema-agent/settings-schema": "2.1.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",
61
61
  "mysql2": "^3.22.4",
@@ -69,7 +69,7 @@
69
69
  "sharp": "^0.35.3"
70
70
  },
71
71
  "devDependencies": {
72
- "@sema-agent/sdk": "9.3.0",
72
+ "@sema-agent/sdk": "9.5.0",
73
73
  "@types/libsodium-wrappers": "^0.7.14",
74
74
  "@types/node": "22.10.2",
75
75
  "@types/pg": "^8.20.0",