@sema-agent/server 7.75.4 → 7.76.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.
- package/USAGE.md +29 -13
- package/dist/boot/reapers.d.ts +5 -1
- package/dist/boot/reapers.js +6 -1
- package/dist/boot/shutdown.js +5 -1
- package/dist/config-catalog.js +1 -1
- package/dist/config-types.d.ts +9 -7
- package/dist/http/routes/approvals-assistant.js +42 -2
- package/dist/http/server.d.ts +5 -1
- package/dist/main.js +3 -1
- package/dist/parent-watch.d.ts +55 -23
- package/dist/parent-watch.js +38 -23
- package/dist/plugins/approval-nonce-store.d.ts +94 -0
- package/dist/plugins/approval-nonce-store.js +126 -0
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -30
- package/dist/plugins/permission-rule-store-sql.js +2 -10
- package/dist/plugins/pg-pool.js +8 -0
- package/dist/plugins/pg-session-storage.js +1 -0
- package/dist/plugins/sql-driver.d.ts +27 -0
- package/dist/plugins/sql-driver.js +12 -0
- package/dist/plugins/store-backend.d.ts +6 -0
- package/dist/plugins/store-backend.js +7 -0
- package/dist/plugins/tidb-pool.js +26 -1
- package/dist/plugins/tidb-session-storage.d.ts +4 -0
- package/dist/plugins/tidb-session-storage.js +7 -2
- package/dist/principal-jwt.d.ts +72 -12
- package/dist/principal-jwt.js +24 -8
- package/dist/tool-approval.d.ts +2 -1
- package/dist/tool-approval.js +8 -1
- package/dist/trace/redact.d.ts +7 -4
- package/dist/trace/redact.js +64 -33
- package/package.json +2 -2
package/dist/tool-approval.js
CHANGED
|
@@ -31,10 +31,13 @@ const DEFAULT_PARK_TOMBSTONE_MAX = 2000;
|
|
|
31
31
|
const DEFAULT_PARK_TOMBSTONE_TTL_MS = 60 * 60_000;
|
|
32
32
|
const DEFAULT_PARK_TOMBSTONE_PER_PRINCIPAL_MAX = 128;
|
|
33
33
|
const PARKING_RETRY_AFTER_MS = 250;
|
|
34
|
+
function ruleMaterialCarriesCredentialShape(command) {
|
|
35
|
+
return redactSecrets(command, { observe: false }) !== normalizeForRedaction(command);
|
|
36
|
+
}
|
|
34
37
|
function durableRuleMaterial(material) {
|
|
35
38
|
if (material === undefined)
|
|
36
39
|
return { ruleCommand: null, ruleScopeRoot: null };
|
|
37
|
-
if (
|
|
40
|
+
if (ruleMaterialCarriesCredentialShape(material.command)) {
|
|
38
41
|
defaultLogger.warn("approval rule material withheld: the adjudicated command carries credential-shaped bytes", {
|
|
39
42
|
commandChars: material.command.length,
|
|
40
43
|
});
|
|
@@ -1799,6 +1802,10 @@ export class ToolApprovalCoordinator {
|
|
|
1799
1802
|
return withFlag(false, "rule_store_error");
|
|
1800
1803
|
if (inputWasEdited)
|
|
1801
1804
|
return withFlag(false, "rule_input_edited");
|
|
1805
|
+
if (ruleMaterialCarriesCredentialShape(material.command)) {
|
|
1806
|
+
defaultLogger.warn("approval rule material withheld on the live leg: the adjudicated command carries credential-shaped bytes", { commandChars: material.command.length });
|
|
1807
|
+
return withFlag(false, "rule_material_absent");
|
|
1808
|
+
}
|
|
1802
1809
|
if (persistRule.kind === "batch") {
|
|
1803
1810
|
const offer = material.offers[persistRule.batchOfferIndex];
|
|
1804
1811
|
if (offer === undefined || offer.kind !== "batch")
|
package/dist/trace/redact.d.ts
CHANGED
|
@@ -28,10 +28,13 @@
|
|
|
28
28
|
* `redactModelText`(四座同调:结果两叶经 `redactTerminalResult`,账本两行经 `redactLedgerEventData`);
|
|
29
29
|
* 它定的是**写回**判据(认出凭据形才改字节、抛则占位),不是第二张规则表。
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
* (
|
|
33
|
-
* ⚠️
|
|
34
|
-
* (
|
|
31
|
+
* 臂分**三段 + 一个候选池**,段之间的先后是契约(上下文越多越靠前):结构段(PEM/URL/JSON + 三条体扫描器)
|
|
32
|
+
* → 词法关键字段(`Bearer`/`Basic`/`token`)→ **候选池** → URL 收尾归一化。
|
|
33
|
+
* ⚠️ **池内没有优先序,自 7.76.0**:{@link SHAPE_SECRET_PATTERNS}(裸形八条)与 {@link FRAGMENT_SECRET_PATTERNS}
|
|
34
|
+
* ([ref] 前缀残片八条,上游先截后脱的产物 ⇒ 带 `:partial` 后缀的 token)并进**同一个**候选池
|
|
35
|
+
* ({@link armPoolRule}):全臂看同一份未改写文本收候选跨度 → 不动点 → 并集律解重叠 → 一次替换,
|
|
36
|
+
* **池内臂序对输出无语义**(7.75.2 只做到裸形一段,残片段仍逐臂改写 ⇒ S-282 的两条漏形)。
|
|
37
|
+
* 左边界律因此有**三种**边界:非词内字符 ∨ 转义分隔单元尾 ∨ **一枚已收候选跨度的尾**(= 凭据尾)。
|
|
35
38
|
*/
|
|
36
39
|
/** 本模块替换 token 的**闭形**正则源(`«redacted»` / `«redacted:aws-key»` / `«redacted:gh-token:partial»` …):
|
|
37
40
|
* URL-userinfo 臂用它把「用户名位上已经是本模块 token」整个当一个用户名单元吃掉。模块加载时对
|
package/dist/trace/redact.js
CHANGED
|
@@ -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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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]
|
|
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
|
|
761
|
-
if (
|
|
762
|
-
throw new Error(`redact:
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
1001
|
-
{ run: (text, _wantCount, _json, observe) => singleHit(labelOfToken(PPK_CONTAINER_TOKEN),
|
|
1002
|
-
{ run: (text, _wantCount, json) => singleHit(labelOfToken(PEM_FRAGMENT_TOKEN),
|
|
1003
|
-
{ run: (text) => singleHit(labelOfToken(HEADLESS_PPK_TOKEN),
|
|
1004
|
-
...KEYWORD_SECRET_PATTERNS.map((p) => regexRule(p
|
|
1005
|
-
|
|
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.
|
|
3
|
+
"version": "7.76.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.
|
|
57
|
+
"@sema-agent/core": "7.17.7",
|
|
58
58
|
"@sema-agent/settings-schema": "1.10.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|