@sema-agent/server 7.75.1 → 7.75.3
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { redactSecrets } from "../trace/redact.js";
|
|
2
|
-
import { taskProgressEventData, toolStartEventData, toolEndEventData } from "../trace/project.js";
|
|
2
|
+
import { taskProgressEventData, toolStartEventData, toolEndEventData, textEndEventData } from "../trace/project.js";
|
|
3
3
|
const TAIL_QUEUE_CAP = 512;
|
|
4
4
|
export function projectTailFrame(e) {
|
|
5
5
|
const t = e.type;
|
|
@@ -19,14 +19,8 @@ export function projectTailFrame(e) {
|
|
|
19
19
|
return { type: "text_delta", delta: td.delta, ...(td.eventId ? { eventId: td.eventId } : {}), ...(td.parentToolCallId ? { parentToolCallId: td.parentToolCallId } : {}) };
|
|
20
20
|
}
|
|
21
21
|
if (t === "text_end") {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
type: "text_end",
|
|
26
|
-
content: e.content,
|
|
27
|
-
...(typeof e.eventId === "string" ? { eventId: e.eventId } : {}),
|
|
28
|
-
...(typeof e.parentToolCallId === "string" ? { parentToolCallId: e.parentToolCallId } : {}),
|
|
29
|
-
};
|
|
22
|
+
const f = textEndEventData(e);
|
|
23
|
+
return f === null ? null : { type: "text_end", ...f };
|
|
30
24
|
}
|
|
31
25
|
if (t === "reasoning_delta") {
|
|
32
26
|
const rd = e;
|
|
@@ -12,7 +12,7 @@ import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../..
|
|
|
12
12
|
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
13
13
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
14
14
|
import { redactSecrets } from "../../trace/redact.js";
|
|
15
|
-
import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage } from "../../trace/project.js";
|
|
15
|
+
import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData } from "../../trace/project.js";
|
|
16
16
|
import { cascadeConfig, runMeta } from "../run-meta.js";
|
|
17
17
|
import { scopedIdempotencyKey } from "../idempotency.js";
|
|
18
18
|
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
@@ -347,8 +347,9 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
347
347
|
sseData(res, { type: "text_delta", delta: td.delta, ...(td.eventId ? { eventId: td.eventId } : {}), ...(td.parentToolCallId ? { parentToolCallId: td.parentToolCallId } : {}) });
|
|
348
348
|
}
|
|
349
349
|
else if (t === "text_end") {
|
|
350
|
-
const tend = e;
|
|
351
|
-
|
|
350
|
+
const tend = textEndEventData(e);
|
|
351
|
+
if (tend !== null)
|
|
352
|
+
sseData(res, { type: "text_end", ...tend });
|
|
352
353
|
}
|
|
353
354
|
else if (t === "reasoning_delta") {
|
|
354
355
|
const rd = e;
|
|
@@ -493,16 +494,19 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
493
494
|
...(e.sourceTaskId !== undefined ? { sourceTaskId: e.sourceTaskId } : {}),
|
|
494
495
|
...(e.bgAgentId !== undefined ? { bgAgentId: e.bgAgentId } : {}),
|
|
495
496
|
};
|
|
497
|
+
const textEnd = ev.type === "text_end" ? textEndEventData(e) : undefined;
|
|
496
498
|
const arm = ev.type === "text_delta"
|
|
497
499
|
? { type: "text_delta", delta: e.delta, ...ident }
|
|
498
500
|
: ev.type === "text_end"
|
|
499
|
-
?
|
|
501
|
+
?
|
|
502
|
+
textEnd === null || textEnd === undefined ? null : { type: "text_end", ...textEnd }
|
|
500
503
|
: ev.type === "turn_end"
|
|
501
504
|
? { type: "turn_end", ...(e.usage !== undefined ? { usage: e.usage } : {}), ...(e.usageMissing !== undefined ? { usageMissing: e.usageMissing } : {}), ...(e.stopReason !== undefined ? { stopReason: e.stopReason } : {}), ...ident }
|
|
502
505
|
: ev.type === "message_committed"
|
|
503
506
|
? { type: "message_committed", entryId: e.entryId, role: e.role, ...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}), ...ident }
|
|
504
507
|
: { type: "context_usage", ...contextUsageEventData(ev), ...ident };
|
|
505
|
-
|
|
508
|
+
if (arm !== null)
|
|
509
|
+
sseData(res, arm);
|
|
506
510
|
}
|
|
507
511
|
else {
|
|
508
512
|
sseData(res, ev);
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -187,6 +187,13 @@ export declare function screenedGate(v: unknown): GateOutcome | undefined;
|
|
|
187
187
|
* 不改原对象(调用方与账本共享引用):有变更时换一只信封,原记录一个字节不动。
|
|
188
188
|
*/
|
|
189
189
|
export declare function screenedStoredEventData(type: string, data: unknown): unknown;
|
|
190
|
+
/** `text_end` 段末帧的**唯一**铸点(三腿共用:top stream / 转发子代腿 / 子代 tail 面)。content = 段权威全文 ⇒ 与
|
|
191
|
+
* durable 面(`result` / 账本 `text` 行)同一只 {@link redactModelText}(认出才改写、抛则占位、不设上限)。
|
|
192
|
+
* 此前三处各自逐字透传 —— 同一份文本毫秒后在 `done.result.result` 上已脱,live 面却在凭据生成瞬间明文(B-114)。
|
|
193
|
+
* `text_delta` 仍逐字:跨 chunk 的凭据无法就地判,流式脱敏是独立设计件(S-270)。
|
|
194
|
+
* 坏形(content 非串/空串)⇒ null(丢帧,不铸「权威空段」:core 契约明言空段不发本帧,消费端收到空段会清掉已攒 delta)。
|
|
195
|
+
* 身份四键零字节变换(live 现状带全四键,消费端可能已依赖)。 */
|
|
196
|
+
export declare function textEndEventData(ev: Record<string, unknown>): Record<string, unknown> | null;
|
|
190
197
|
/** The durable `tool_end` payload (shared by both append sites). E1: the model-facing `output` is UNTRUSTED RAW
|
|
191
198
|
* from core (file/shell/API bodies) — core only SIZE-bounds it (`truncated`), REDACTION is ours (same contract as
|
|
192
199
|
* `tool_start.args`); never re-fed to a model. E2: identity. Absent `output` ⇒ the tool produced no content body.
|
package/dist/trace/project.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { redactErrorMessage } from "../observability/run-terminal-log.js";
|
|
1
|
+
import { redactErrorMessage, redactModelText } from "../observability/run-terminal-log.js";
|
|
2
2
|
import { MCP_DELIVERY_VERDICTS, screenGateOutcome, ToolRoster, ToolRosterDelta } from "@sema-agent/core";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
4
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
@@ -173,6 +173,17 @@ function projectSettlement(st) {
|
|
|
173
173
|
...(typeof st.note === "string" ? { note: redactSecrets(st.note).slice(0, SETTLEMENT_NOTE_MAX) } : {}),
|
|
174
174
|
};
|
|
175
175
|
}
|
|
176
|
+
export function textEndEventData(ev) {
|
|
177
|
+
if (typeof ev.content !== "string" || ev.content.length === 0)
|
|
178
|
+
return null;
|
|
179
|
+
return {
|
|
180
|
+
content: redactModelText(ev.content),
|
|
181
|
+
...(ev.eventId !== undefined ? { eventId: ev.eventId } : {}),
|
|
182
|
+
...(ev.parentToolCallId !== undefined ? { parentToolCallId: ev.parentToolCallId } : {}),
|
|
183
|
+
...(ev.sourceTaskId !== undefined ? { sourceTaskId: ev.sourceTaskId } : {}),
|
|
184
|
+
...(ev.bgAgentId !== undefined ? { bgAgentId: ev.bgAgentId } : {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
176
187
|
export function toolEndEventData(ev) {
|
|
177
188
|
const gate = ev.gate === undefined ? undefined : screenedGate(ev.gate);
|
|
178
189
|
return {
|
package/dist/trace/redact.d.ts
CHANGED
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
*
|
|
31
31
|
* 两组臂,顺序即优先序:{@link FULL_SECRET_PATTERNS}(完整形)在前,{@link FRAGMENT_SECRET_PATTERNS}
|
|
32
32
|
* ([ref] 前缀残片,上游先截后脱的产物 ⇒ 带 `:partial` 后缀的 token)在后。
|
|
33
|
+
* ⚠️ **一个例外,自 7.75.2**:完整臂里的**裸形段**内部没有优先序 —— 该段全臂一次收候选跨度、解重叠、一次替换
|
|
34
|
+
* ({@link shapeSectionRule}),段内臂序对输出无语义。段**之间**的先后照旧是契约。
|
|
33
35
|
*/
|
|
34
36
|
/** 本模块替换 token 的**闭形**正则源(`«redacted»` / `«redacted:aws-key»` / `«redacted:gh-token:partial»` …):
|
|
35
37
|
* URL-userinfo 臂用它把「用户名位上已经是本模块 token」整个当一个用户名单元吃掉。模块加载时对
|
package/dist/trace/redact.js
CHANGED
|
@@ -207,32 +207,65 @@ function forgetEscapedSeparatorTails() {
|
|
|
207
207
|
}
|
|
208
208
|
const VALUE_ARM_STICKY = new Map();
|
|
209
209
|
const VALUE_ARM_BODIES = [];
|
|
210
|
-
const valueArm = (prefixSrc, valueClass, min) => {
|
|
210
|
+
const valueArm = (prefixSrc, valueClass, min, evidence) => {
|
|
211
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
212
|
VALUE_ARM_BODIES.push(body);
|
|
215
|
-
|
|
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)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
216
226
|
};
|
|
217
227
|
let anyValueArm;
|
|
218
228
|
function anyValueArmRe() {
|
|
219
229
|
return (anyValueArm ??= new RegExp(`(?:${VALUE_ARM_BODIES.join("|")})`, "y"));
|
|
220
230
|
}
|
|
221
|
-
function
|
|
231
|
+
function collectRegexSpans(g, repl, order, text, out) {
|
|
232
|
+
g.lastIndex = 0;
|
|
233
|
+
for (let m = g.exec(text); m !== null; m = g.exec(text)) {
|
|
234
|
+
const end = m.index + m[0].length;
|
|
235
|
+
out.push({ start: m.index, end, repl, order });
|
|
236
|
+
g.lastIndex = end > m.index ? end : m.index + 1;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function resolveSpanOverlaps(spans) {
|
|
240
|
+
spans.sort((a, b) => a.start - b.start || b.end - a.end || a.order - b.order);
|
|
241
|
+
const kept = [];
|
|
242
|
+
let covered = 0;
|
|
243
|
+
for (const s of spans) {
|
|
244
|
+
if (s.end <= covered)
|
|
245
|
+
continue;
|
|
246
|
+
kept.push(s.start >= covered ? s : { ...s, start: covered });
|
|
247
|
+
covered = s.end;
|
|
248
|
+
}
|
|
249
|
+
return kept;
|
|
250
|
+
}
|
|
251
|
+
function applySpans(text, spans) {
|
|
252
|
+
if (spans.length === 0)
|
|
253
|
+
return { out: text, count: 0 };
|
|
254
|
+
const parts = [];
|
|
255
|
+
let copied = 0;
|
|
256
|
+
for (const s of spans) {
|
|
257
|
+
parts.push(text.slice(copied, s.start), s.repl);
|
|
258
|
+
copied = s.end;
|
|
259
|
+
}
|
|
260
|
+
parts.push(text.slice(copied));
|
|
261
|
+
return { out: parts.join(""), count: spans.length };
|
|
262
|
+
}
|
|
263
|
+
function collectValueArmSpans(g, y, repl, order, text, out) {
|
|
222
264
|
const tails = escapedSeparatorTails(text);
|
|
223
265
|
if (tails === undefined) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
let hits = 0;
|
|
227
|
-
const replaced = text.replace(g, () => {
|
|
228
|
-
hits += 1;
|
|
229
|
-
return repl;
|
|
230
|
-
});
|
|
231
|
-
return { out: replaced, count: hits };
|
|
266
|
+
collectRegexSpans(g, repl, order, text, out);
|
|
267
|
+
return;
|
|
232
268
|
}
|
|
233
|
-
let out;
|
|
234
|
-
let copied = 0;
|
|
235
|
-
let count = 0;
|
|
236
269
|
let pos = 0;
|
|
237
270
|
let ti = 0;
|
|
238
271
|
let bIdx = -1;
|
|
@@ -275,15 +308,14 @@ function runValueArm(g, y, repl, text, wantCount) {
|
|
|
275
308
|
}
|
|
276
309
|
else
|
|
277
310
|
break;
|
|
278
|
-
|
|
279
|
-
copied = end;
|
|
280
|
-
count += 1;
|
|
311
|
+
out.push({ start: idx, end, repl, order });
|
|
281
312
|
pos = end;
|
|
282
313
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
314
|
+
}
|
|
315
|
+
function runValueArm(g, y, repl, text) {
|
|
316
|
+
const spans = [];
|
|
317
|
+
collectValueArmSpans(g, y, repl, 0, text, spans);
|
|
318
|
+
return applySpans(text, spans);
|
|
287
319
|
}
|
|
288
320
|
const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
|
|
289
321
|
const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${atLeast(valueClass, min)}${tailSrc}` +
|
|
@@ -303,7 +335,6 @@ const KEYWORD_SECRET_PATTERNS = [
|
|
|
303
335
|
[keywordArm("[Tt]oken", "[A-Za-z0-9._~+/-]", 20), "token «redacted»"],
|
|
304
336
|
];
|
|
305
337
|
const SHAPE_SECRET_PATTERNS = [
|
|
306
|
-
[valueArm("sk-", "[A-Za-z0-9_-]", 16), "«redacted:key»"],
|
|
307
338
|
[new RegExp(String.raw `(?:ghp|gho|ghu|ghs|ghr)_${atLeast("[A-Za-z0-9]", 20)}`, "g"), "«redacted:gh-token»"],
|
|
308
339
|
[new RegExp(String.raw `github_pat_[A-Za-z0-9]{22}_${atLeast("[A-Za-z0-9]", 59)}`, "g"), "«redacted:gh-token»"],
|
|
309
340
|
[new RegExp(String.raw `xox[baprs]-${atLeast("[A-Za-z0-9-]", 10)}`, "g"), "«redacted:slack-token»"],
|
|
@@ -311,6 +342,7 @@ const SHAPE_SECRET_PATTERNS = [
|
|
|
311
342
|
[new RegExp(String.raw `ASIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
|
|
312
343
|
[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
344
|
[new RegExp(String.raw `xapp-\d-${atLeast("[A-Za-z0-9-]", 20)}`, "g"), "«redacted:slack-token»"],
|
|
345
|
+
[valueArm("sk-", "[A-Za-z0-9_-]", 16, "self-evident"), "«redacted:key»"],
|
|
314
346
|
];
|
|
315
347
|
const FULL_SECRET_PATTERNS = [...STRUCTURAL_SECRET_PATTERNS, ...KEYWORD_SECRET_PATTERNS, ...SHAPE_SECRET_PATTERNS];
|
|
316
348
|
const PEM_HEADER_IDENT_MAX = 31;
|
|
@@ -688,14 +720,14 @@ function redactHeadlessPpkBodies(text) {
|
|
|
688
720
|
return count === 0 ? { out: text, count: 0 } : { out: out + text.slice(last), count };
|
|
689
721
|
}
|
|
690
722
|
const FRAGMENT_SECRET_PATTERNS = [
|
|
691
|
-
[valueArm("sk-", "[A-Za-z0-9_-]", 6), "«redacted:key:partial»"],
|
|
692
|
-
[valueArm("(?:ghp|gho|ghu|ghs|ghr)_", "[A-Za-z0-9]", 8), "«redacted:gh-token:partial»"],
|
|
723
|
+
[valueArm("sk-", "[A-Za-z0-9_-]", 6, "needs-left-boundary"), "«redacted:key:partial»"],
|
|
724
|
+
[valueArm("(?:ghp|gho|ghu|ghs|ghr)_", "[A-Za-z0-9]", 8, "needs-left-boundary"), "«redacted:gh-token:partial»"],
|
|
693
725
|
[new RegExp(String.raw `github_pat_${atLeast("[A-Za-z0-9_]", 8)}`, "g"), "«redacted:gh-token:partial»"],
|
|
694
|
-
[valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4), "«redacted:slack-token:partial»"],
|
|
695
|
-
[valueArm("AKIA", "[0-9A-Z]", 6), "«redacted:aws-key:partial»"],
|
|
696
|
-
[valueArm("ASIA", "[0-9A-Z]", 8), "«redacted:aws-key:partial»"],
|
|
697
|
-
[valueArm("(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-|(?:sk|rk)_(?:live|test|org)_|pypi-)", "[A-Za-z0-9_-]", 8), "«redacted:key:partial»"],
|
|
698
|
-
[valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4), "«redacted:slack-token:partial»"],
|
|
726
|
+
[valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
|
|
727
|
+
[valueArm("AKIA", "[0-9A-Z]", 6, "needs-left-boundary"), "«redacted:aws-key:partial»"],
|
|
728
|
+
[valueArm("ASIA", "[0-9A-Z]", 8, "needs-left-boundary"), "«redacted:aws-key:partial»"],
|
|
729
|
+
[valueArm("(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-|(?:sk|rk)_(?:live|test|org)_|pypi-)", "[A-Za-z0-9_-]", 8, "needs-left-boundary"), "«redacted:key:partial»"],
|
|
730
|
+
[valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
|
|
699
731
|
];
|
|
700
732
|
const REDACTION_TOKENS = new Set([...FULL_SECRET_PATTERNS.map(([, r]) => r), PEM_FRAGMENT_TOKEN, ...FRAGMENT_SECRET_PATTERNS.map(([, r]) => r)]
|
|
701
733
|
.flatMap((r) => r.match(/«[^»]*»/g) ?? []));
|
|
@@ -715,6 +747,8 @@ function redactionTokenEndAt(s, p, limit) {
|
|
|
715
747
|
return 0;
|
|
716
748
|
}
|
|
717
749
|
const labelOfToken = (token) => token.replace(/[«»]/g, "").replace(/^redacted:/, "");
|
|
750
|
+
const NO_HITS = [];
|
|
751
|
+
const singleHit = (label, arm, r) => r.count > 0 ? { out: r.out, hits: [{ label, count: r.count, arm }] } : { out: r.out, hits: NO_HITS };
|
|
718
752
|
const regexRule = ([re, repl], arm) => {
|
|
719
753
|
const groupCount = (new RegExp(`${re.source}|`).exec("")?.length ?? 1) - 1;
|
|
720
754
|
const refs = [...repl.matchAll(/\$(.)/g)].map(([, d]) => d ?? "");
|
|
@@ -726,23 +760,20 @@ const regexRule = ([re, repl], arm) => {
|
|
|
726
760
|
const sticky = VALUE_ARM_STICKY.get(re);
|
|
727
761
|
if (sticky !== undefined && refs.length > 0)
|
|
728
762
|
throw new Error(`redact: value arm replacement ${JSON.stringify(repl)} must not use $N refs`);
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
return expand === undefined ? repl : expand(args);
|
|
742
|
-
});
|
|
743
|
-
return { out, count: n };
|
|
744
|
-
},
|
|
763
|
+
const label = labelOfToken(repl);
|
|
764
|
+
const applyOnce = (text, wantCount) => {
|
|
765
|
+
if (sticky !== undefined)
|
|
766
|
+
return runValueArm(re, sticky, repl, text);
|
|
767
|
+
if (!wantCount)
|
|
768
|
+
return { out: text.replace(re, repl), count: 0 };
|
|
769
|
+
let n = 0;
|
|
770
|
+
const out = text.replace(re, (...args) => {
|
|
771
|
+
n += 1;
|
|
772
|
+
return expand === undefined ? repl : expand(args);
|
|
773
|
+
});
|
|
774
|
+
return { out, count: n };
|
|
745
775
|
};
|
|
776
|
+
return { run: (text, wantCount) => singleHit(label, arm, applyOnce(text, wantCount)) };
|
|
746
777
|
};
|
|
747
778
|
const URL_USERINFO_PATTERN = (() => {
|
|
748
779
|
const row = FULL_SECRET_PATTERNS.find(([, repl]) => repl.endsWith("»@"));
|
|
@@ -937,16 +968,43 @@ function redactPpkContainers(text, record) {
|
|
|
937
968
|
}
|
|
938
969
|
return count === 0 ? { out: text, count: 0 } : { out: out + text.slice(last), count };
|
|
939
970
|
}
|
|
940
|
-
const
|
|
971
|
+
const URL_USERINFO_LABEL = labelOfToken(URL_USERINFO_PATTERN[1]);
|
|
972
|
+
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) => {
|
|
975
|
+
if (/\$./.test(repl))
|
|
976
|
+
throw new Error(`redact: shape-section replacement ${JSON.stringify(repl)} must not use $N refs`);
|
|
977
|
+
return { g: re, y: VALUE_ARM_STICKY.get(re), repl, order };
|
|
978
|
+
});
|
|
979
|
+
return {
|
|
980
|
+
run: (text, wantCount) => {
|
|
981
|
+
const spans = [];
|
|
982
|
+
for (const a of arms) {
|
|
983
|
+
if (a.y === undefined)
|
|
984
|
+
collectRegexSpans(a.g, a.repl, a.order, text, spans);
|
|
985
|
+
else
|
|
986
|
+
collectValueArmSpans(a.g, a.y, a.repl, a.order, text, spans);
|
|
987
|
+
}
|
|
988
|
+
const kept = resolveSpanOverlaps(spans);
|
|
989
|
+
const out = applySpans(text, kept).out;
|
|
990
|
+
if (!wantCount || kept.length === 0)
|
|
991
|
+
return { out, hits: NO_HITS };
|
|
992
|
+
const byToken = new Map();
|
|
993
|
+
for (const sp of kept)
|
|
994
|
+
byToken.set(sp.repl, (byToken.get(sp.repl) ?? 0) + 1);
|
|
995
|
+
return { out, hits: [...byToken].map(([token, count]) => ({ label: labelOfToken(token), count, arm: "full" })) };
|
|
996
|
+
},
|
|
997
|
+
};
|
|
998
|
+
})();
|
|
941
999
|
const SECRET_RULES = [
|
|
942
1000
|
...STRUCTURAL_SECRET_PATTERNS.map((p) => (p === URL_USERINFO_PATTERN ? urlUserinfoRule : regexRule(p, "full"))),
|
|
943
|
-
{
|
|
944
|
-
{
|
|
945
|
-
{
|
|
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)) },
|
|
946
1004
|
...KEYWORD_SECRET_PATTERNS.map((p) => regexRule(p, "full")),
|
|
947
|
-
|
|
1005
|
+
shapeSectionRule,
|
|
948
1006
|
...FRAGMENT_SECRET_PATTERNS.map((p) => regexRule(p, "fragment")),
|
|
949
|
-
{
|
|
1007
|
+
{ run: (text, _wantCount, json) => ({ out: redactUrlUserinfo(text, json).out, hits: NO_HITS }) },
|
|
950
1008
|
];
|
|
951
1009
|
let onRedaction;
|
|
952
1010
|
export function setRedactionObserver(fn) {
|
|
@@ -958,8 +1016,8 @@ function applyRules(text, wantCount, json, observe) {
|
|
|
958
1016
|
for (const rule of SECRET_RULES) {
|
|
959
1017
|
const r = rule.run(out, wantCount, json, observe);
|
|
960
1018
|
out = r.out;
|
|
961
|
-
if (wantCount && r.
|
|
962
|
-
hits.push(
|
|
1019
|
+
if (wantCount && r.hits.length > 0)
|
|
1020
|
+
hits.push(...r.hits);
|
|
963
1021
|
}
|
|
964
1022
|
return { out, hits };
|
|
965
1023
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.75.
|
|
3
|
+
"version": "7.75.3",
|
|
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.
|
|
57
|
+
"@sema-agent/core": "7.17.5",
|
|
58
58
|
"@sema-agent/settings-schema": "1.10.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|