@sema-agent/server 7.62.0 → 7.64.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.
- package/MIGRATION.md +8 -0
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/USAGE.md +48 -15
- package/deploy/sema-up/smoke.sh +2 -2
- package/dist/approval-ask-audit-store.d.ts +149 -3
- package/dist/approval-ask-audit-store.js +11 -4
- package/dist/bench/s1/arms.d.ts +2 -2
- package/dist/bench/s1/arms.js +20 -15
- package/dist/bench/s1/live-deps.js +3 -1
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/runner-deps.d.ts +2 -2
- package/dist/boot/runner-deps.js +1 -0
- package/dist/config-catalog.js +2 -0
- package/dist/config-types.d.ts +27 -2
- package/dist/config.js +83 -2
- package/dist/degenerate-instrument.d.ts +0 -33
- package/dist/degenerate-instrument.js +2 -1
- package/dist/fleet/fleet-bus.d.ts +9 -4
- package/dist/fleet/fleet-bus.js +2 -1
- package/dist/host-decision.d.ts +39 -0
- package/dist/host-decision.js +6 -0
- package/dist/http/principal-gate.d.ts +9 -0
- package/dist/http/principal-gate.js +12 -0
- package/dist/http/route-ctx.d.ts +8 -5
- package/dist/http/routes/a2a-serve.js +4 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -13
- package/dist/http/routes/approvals-assistant.js +2 -1
- package/dist/http/routes/capabilities.js +2 -0
- package/dist/http/routes/diagnostics.js +7 -4
- package/dist/http/routes/runs.js +3 -2
- package/dist/http/routes/tasks.js +33 -29
- package/dist/http/routes/workflows.d.ts +0 -11
- package/dist/http/routes/workflows.js +18 -14
- package/dist/http/server.d.ts +7 -0
- package/dist/http/server.js +50 -46
- package/dist/leader/fanout.d.ts +6 -23
- package/dist/leader/fanout.js +8 -19
- package/dist/leader/leader.d.ts +13 -10
- package/dist/leader/leader.js +30 -22
- package/dist/leader/repair-wire.d.ts +4 -1
- package/dist/leader/repair-wire.js +7 -5
- package/dist/leader/wire.js +5 -4
- package/dist/main.js +7 -3
- package/dist/observability/fail-open.d.ts +8 -0
- package/dist/observability/fail-open.js +8 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.d.ts +25 -7
- package/dist/observability/run-terminal-log.js +15 -4
- package/dist/observability/tool-trace.d.ts +13 -12
- package/dist/observability/tool-trace.js +8 -13
- package/dist/parked-decide.d.ts +8 -4
- package/dist/parked-decide.js +1 -1
- package/dist/plugins/file-run-store.js +12 -21
- package/dist/plugins/memory-run-store.js +3 -2
- package/dist/plugins/run-store-sql.js +3 -2
- package/dist/run-local.js +6 -4
- package/dist/runs.d.ts +4 -19
- package/dist/runs.js +49 -47
- package/dist/terminal.d.ts +100 -0
- package/dist/terminal.js +55 -0
- package/dist/trace/core-keyset-guard.d.ts +20 -4
- package/dist/trace/ledger-sink.d.ts +2 -1
- package/dist/trace/ledger-sink.js +14 -7
- package/dist/trace/project.d.ts +9 -12
- package/dist/trace/project.js +54 -9
- package/dist/write-protection.d.ts +80 -0
- package/dist/write-protection.js +30 -0
- package/package.json +4 -4
package/dist/trace/project.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { redactErrorMessage } from "../observability/run-terminal-log.js";
|
|
2
|
-
import {
|
|
2
|
+
import { MCP_DELIVERY_VERDICTS, screenGateOutcome } from "@sema-agent/core";
|
|
3
|
+
import { recordFailOpen } from "../observability/fail-open.js";
|
|
3
4
|
import { redactSecrets, redactDeep } from "./redact.js";
|
|
4
5
|
import { injectionTierOrAbsent } from "./injection-tier.js";
|
|
5
6
|
import { semaProvenanceOrAbsent } from "./sema-provenance.js";
|
|
@@ -83,7 +84,52 @@ function identityFields(src) {
|
|
|
83
84
|
export function toolStartEventData(ev) {
|
|
84
85
|
return { toolCallId: ev.toolCallId, toolName: ev.toolName, ...(ev.label !== undefined ? { label: ev.label } : {}), args: redactDeep(ev.args), ...identityFields(ev) };
|
|
85
86
|
}
|
|
87
|
+
const SETTLEMENT_NOTE_MAX = 500;
|
|
88
|
+
export function screenedGate(v) {
|
|
89
|
+
if (v === null || typeof v !== "object")
|
|
90
|
+
return undefined;
|
|
91
|
+
const g = v;
|
|
92
|
+
const d = g.disposition;
|
|
93
|
+
if (d === null || typeof d !== "object" || (d.kind !== "allowed" && d.kind !== "denied")) {
|
|
94
|
+
recordFailOpen("server.trace.gate-outcome-defective", "disposition is not one of the two arms");
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const defects = screenGateOutcome(g);
|
|
98
|
+
if (defects.length > 0) {
|
|
99
|
+
recordFailOpen("server.trace.gate-outcome-defective", defects.join("; "));
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const settlement = g.settlement === undefined ? undefined : projectSettlement(g.settlement);
|
|
103
|
+
if (g.settlement !== undefined && settlement === undefined) {
|
|
104
|
+
recordFailOpen("server.trace.gate-outcome-defective", "settlement.who is not an object with a party in the closed set");
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
disposition: d.kind === "denied" ? { kind: "denied", deniedBy: d.deniedBy } : { kind: "allowed" },
|
|
109
|
+
...(settlement === undefined ? {} : { settlement }),
|
|
110
|
+
...(g.origin === undefined ? {} : { origin: g.origin }),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const SETTLEMENT_PARTIES = new Set(["person", "engine", "host", "none"]);
|
|
114
|
+
function projectSettlement(st) {
|
|
115
|
+
const whoV = st.who;
|
|
116
|
+
const partyV = whoV === null || typeof whoV !== "object" ? undefined : whoV.party;
|
|
117
|
+
if (typeof partyV !== "string" || !SETTLEMENT_PARTIES.has(partyV))
|
|
118
|
+
return undefined;
|
|
119
|
+
const who = whoV;
|
|
120
|
+
return {
|
|
121
|
+
kind: st.kind,
|
|
122
|
+
when: st.when,
|
|
123
|
+
who: {
|
|
124
|
+
party: who.party,
|
|
125
|
+
...(typeof who.approver === "string" && who.approver !== "" ? { approver: redactSecrets(who.approver) } : {}),
|
|
126
|
+
...(typeof who.window === "string" ? { window: who.window } : {}),
|
|
127
|
+
},
|
|
128
|
+
...(typeof st.note === "string" ? { note: redactSecrets(st.note).slice(0, SETTLEMENT_NOTE_MAX) } : {}),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
86
131
|
export function toolEndEventData(ev) {
|
|
132
|
+
const gate = ev.gate === undefined ? undefined : screenedGate(ev.gate);
|
|
87
133
|
return {
|
|
88
134
|
toolCallId: ev.toolCallId,
|
|
89
135
|
toolName: ev.toolName,
|
|
@@ -94,10 +140,8 @@ export function toolEndEventData(ev) {
|
|
|
94
140
|
...(Number.isFinite(ev.totalChars) ? { totalChars: ev.totalChars } : {}),
|
|
95
141
|
...(ev.structured !== undefined ? { structured: redactDeep(ev.structured) } : {}),
|
|
96
142
|
...(typeof ev.errorCode === "string" && ev.errorCode.length <= 128 ? { errorCode: ev.errorCode } : {}),
|
|
97
|
-
...(
|
|
98
|
-
...(
|
|
99
|
-
...(ev.autoDenied === true ? { autoDenied: true } : {}),
|
|
100
|
-
...(typeof ev.approver === "string" && ev.approver !== "" ? { approver: redactSecrets(ev.approver) } : {}),
|
|
143
|
+
...(typeof ev.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(ev.delivered) ? { delivered: ev.delivered } : {}),
|
|
144
|
+
...(gate !== undefined ? { gate } : {}),
|
|
101
145
|
...(typeof ev.gatedCallId === "string" && ev.gatedCallId !== "" ? { gatedCallId: ev.gatedCallId } : {}),
|
|
102
146
|
...identityFields(ev),
|
|
103
147
|
};
|
|
@@ -289,6 +333,8 @@ export function wiringManifestEventData(ev) {
|
|
|
289
333
|
...(num(e.toolCount) !== undefined ? { toolCount: e.toolCount } : {}),
|
|
290
334
|
...(str(e.source) !== undefined ? { source: e.source } : {}),
|
|
291
335
|
...(str(e.errorCode) !== undefined ? { errorCode: e.errorCode } : {}),
|
|
336
|
+
...(typeof e.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(e.delivered) ? { delivered: e.delivered } : {}),
|
|
337
|
+
...(num(e.httpStatus) !== undefined ? { httpStatus: e.httpStatus } : {}),
|
|
292
338
|
});
|
|
293
339
|
}
|
|
294
340
|
return rows;
|
|
@@ -447,6 +493,7 @@ export function toolCallFieldsOf(d) {
|
|
|
447
493
|
};
|
|
448
494
|
}
|
|
449
495
|
export function toolResultFieldsOf(d) {
|
|
496
|
+
const gate = d.gate === undefined ? undefined : screenedGate(d.gate);
|
|
450
497
|
return {
|
|
451
498
|
callId: String(d.toolCallId ?? ""),
|
|
452
499
|
...(typeof d.label === "string" ? { label: d.label } : {}),
|
|
@@ -454,10 +501,8 @@ export function toolResultFieldsOf(d) {
|
|
|
454
501
|
...(Number.isFinite(d.totalChars) ? { totalChars: d.totalChars } : {}),
|
|
455
502
|
...(d.structured !== undefined ? { structured: d.structured } : {}),
|
|
456
503
|
...(typeof d.errorCode === "string" ? { errorCode: d.errorCode } : {}),
|
|
457
|
-
...(
|
|
458
|
-
...(
|
|
459
|
-
...(isAskDenyResolution(d.resolution) ? { resolution: d.resolution } : {}),
|
|
460
|
-
...(d.autoDenied === true ? { autoDenied: true } : {}),
|
|
504
|
+
...(typeof d.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(d.delivered) ? { delivered: d.delivered } : {}),
|
|
505
|
+
...(gate !== undefined ? { gate } : {}),
|
|
461
506
|
...(typeof d.gatedCallId === "string" && d.gatedCallId !== "" ? { gatedCallId: d.gatedCallId } : {}),
|
|
462
507
|
...identityFields(d),
|
|
463
508
|
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-138 —— **写保护表**(core `RunnerDeps.writeProtectedPaths` 座)的姿态推导点(形制照
|
|
3
|
+
* `memory-posture.ts` / `sql-engine-posture.ts`:一次推导,两个读面)。
|
|
4
|
+
*
|
|
5
|
+
* ## 病(B-023 F2 的根)
|
|
6
|
+
*
|
|
7
|
+
* core 的写保护表是**字面名表**(CC `DANGEROUS_FILES` / `DANGEROUS_DIRECTORIES` /
|
|
8
|
+
* `DANGEROUS_DIRECTORY_PATHS` 三形对位):一次落在表行上的可定路径写(Write/Edit/NotebookEdit)会把
|
|
9
|
+
* 幸存的 `allow` 降级成 `ask`。座**缺席 = 缺省表在岗**(不是「没装」)。可是本仓此前没有任何读面能
|
|
10
|
+
* 回答「这台部署的写保护表在不在、有几行」—— 邻仓因此只能用「`writeProtectedPaths` 键名 grep 零命中」
|
|
11
|
+
* 去推断「表未装配」,推错了(座缺席正是缺省表在岗的姿势),而**读面缺席让这个错误无法被证否**。
|
|
12
|
+
*
|
|
13
|
+
* ## 药
|
|
14
|
+
*
|
|
15
|
+
* 一次推导,两个读面:
|
|
16
|
+
* · operator 面(`GET /v1/diagnostics/wiring` 的 `writeProtection` 段)—— **逐行**表 + 来源 + 被丢的缺省行名;
|
|
17
|
+
* · 租户面(`GET /v1/capabilities` 的 `writeProtection` 位)—— `{armed, rows, replaced}` 三项,**不含行内容**。
|
|
18
|
+
*
|
|
19
|
+
* ## 三条诚实纪律
|
|
20
|
+
*
|
|
21
|
+
* ① **表由 core 解析,本仓零重算**:`rows` 逐字来自 `compileWriteProtection().rows`(core 自己的
|
|
22
|
+
* 「disclosure face」),连行的**身份折叠**(大小写 + ı/ſ 两个易混字)都借 core 的
|
|
23
|
+
* `resolveWriteProtectedTable` 去判(见 {@link defaultRowsMissingFrom})—— 本仓不铸第二套判定语义,
|
|
24
|
+
* 也**不复制缺省 51 行**(单一属主在 core;上游改表之日起本仓不会成为第二份真源)。
|
|
25
|
+
* ② **两项刻意不合成一个布尔**(`permissionModeAuto` 同判据):`armed`(表非空)与 `replaced`(整表
|
|
26
|
+
* 替换座被写)回答的是两件事 —— 一个「引擎会不会对这类路径弹卡」,一个「运维有没有动过那张表」。
|
|
27
|
+
* 合成之后,「缺省表在岗」与「运维自定义了一张同样非空的表」得到同一个 true,而壳/运维对这两种
|
|
28
|
+
* 要做的事不同。
|
|
29
|
+
* ③ **行内容只上 operator 面**:缺省 51 行是 CC 公开表(零增量信息),但**部署自定义行**可能含内部
|
|
30
|
+
* 路径名(`.corp-deploy-key` 之类)⇒ 逐行披露给租户面等于告诉想绕过的人「哪些名字不受保护」。
|
|
31
|
+
*
|
|
32
|
+
* ## 与 `SENSITIVE_WRITE_PATTERNS` 的关系(**并列,不合流**)
|
|
33
|
+
*
|
|
34
|
+
* 本仓另有一套**模式型**(正则/glob)写 deny 集(`createSensitivePathPolicy`,旋钮
|
|
35
|
+
* `SENSITIVE_WRITE_PATTERNS`,经 deployment-governance 无条件施加)。两套按 core 的 d.ts 是并列机制、
|
|
36
|
+
* 互不覆盖,所以两个读面**分开报**:合成一个 `writeProtected: true` 会让壳答不出「这条路径是被哪一套
|
|
37
|
+
* 拦的」,而两套的解法完全不同(一套改模式,一套改名表)。
|
|
38
|
+
*/
|
|
39
|
+
import { type WriteProtectedRow } from "@sema-agent/core";
|
|
40
|
+
import type { ServiceConfig } from "./config-types.js";
|
|
41
|
+
/** 表的来源(闭集;`switch` 穷尽 —— 新增词 = 编译红,[ref] 词表纪律)。 */
|
|
42
|
+
export declare const WRITE_PROTECTION_SOURCES: readonly ["default", "extra", "replace", "off"];
|
|
43
|
+
export type WriteProtectionSource = (typeof WRITE_PROTECTION_SOURCES)[number];
|
|
44
|
+
/** operator 面(`GET /v1/diagnostics/wiring` 的 `writeProtection` 段)。 */
|
|
45
|
+
export interface WriteProtectionPosture {
|
|
46
|
+
/** 生效表的**逐行**内容,逐字来自 core `compileWriteProtection().rows`(空 = 显式无表)。 */
|
|
47
|
+
readonly rows: readonly WriteProtectedRow[];
|
|
48
|
+
/** `default` = 两根旋钮都没写(引擎缺省表在岗);`extra` = 缺省表 + 增量;`replace` = 整表替换;
|
|
49
|
+
* `off` = 整表替换成空表(合法但响亮的姿势)。 */
|
|
50
|
+
readonly source: WriteProtectionSource;
|
|
51
|
+
/** 整表替换**丢掉**的缺省行名(替换族才在场;`extra`/`default` 结构上丢不掉任何行)。 */
|
|
52
|
+
readonly droppedDefaultRows?: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
/** 租户面窄投影(`GET /v1/capabilities` 的 `writeProtection` 位)。**不含行内容**(见顶注纪律③)。 */
|
|
55
|
+
export interface WriteProtectionCapability {
|
|
56
|
+
/** 生效表非空 = 引擎真会对表行弹卡。 */
|
|
57
|
+
readonly armed: boolean;
|
|
58
|
+
/** 生效表的**行数**(内容不上租户面)。 */
|
|
59
|
+
readonly rows: number;
|
|
60
|
+
/** 整表替换座被写(缺省表不再原样在岗)。与 `armed` 刻意不合成(顶注纪律②)。 */
|
|
61
|
+
readonly replaced: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** 本模块读的 config 面(取型而非重述 —— 键名/形永不与真配置漂)。 */
|
|
64
|
+
export type WriteProtectionConfigView = Pick<ServiceConfig, "writeProtectedPaths">;
|
|
65
|
+
/**
|
|
66
|
+
* boot 期算一次的姿态(纯函数、零 I/O)。**同一只解析器**:`rows` 是 core 编译判子时用的那份表,不是
|
|
67
|
+
* 本仓照着 config 又算了一遍的复述 —— 「诊断页说 51 行、引擎按 2 行判」在结构上不可能。
|
|
68
|
+
*
|
|
69
|
+
* `compileWriteProtection` 对**空表**返回 `undefined`(core 契约:没有表就不挂 tighten)⇒ 本函数把它
|
|
70
|
+
* 读成 `rows: []`,那是 `off` 姿势的诚实形,不是「读不出来」。
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildWriteProtectionPosture(config: WriteProtectionConfigView): WriteProtectionPosture;
|
|
73
|
+
/**
|
|
74
|
+
* operator 面 → 租户面的窄投影。
|
|
75
|
+
*
|
|
76
|
+
* `null` = **这个进程说不出来**(不是 composition root 装起来的测试夹具形)—— 与 `sql` / `memoryPosture`
|
|
77
|
+
* 两位同姿势:不铸一个看起来像答案的空壳(那会让壳把夹具形读成「本部署无表」)。
|
|
78
|
+
*/
|
|
79
|
+
export declare function projectWriteProtectionCapability(posture: WriteProtectionPosture | undefined): WriteProtectionCapability | null;
|
|
80
|
+
//# sourceMappingURL=write-protection.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { WRITE_PROTECTED_DEFAULT_TABLE, compileWriteProtection, resolveWriteProtectedTable } from "@sema-agent/core";
|
|
2
|
+
export const WRITE_PROTECTION_SOURCES = ["default", "extra", "replace", "off"];
|
|
3
|
+
function defaultRowsMissingFrom(rows) {
|
|
4
|
+
return WRITE_PROTECTED_DEFAULT_TABLE.filter((row) => resolveWriteProtectedTable([...rows, row]).length !== rows.length).map((row) => row.name);
|
|
5
|
+
}
|
|
6
|
+
export function buildWriteProtectionPosture(config) {
|
|
7
|
+
const decision = config.writeProtectedPaths;
|
|
8
|
+
const rows = compileWriteProtection(decision?.entries)?.rows ?? [];
|
|
9
|
+
if (decision === undefined)
|
|
10
|
+
return { rows, source: "default" };
|
|
11
|
+
if (decision.knob === "extra")
|
|
12
|
+
return { rows, source: "extra" };
|
|
13
|
+
return { rows, source: rows.length === 0 ? "off" : "replace", droppedDefaultRows: defaultRowsMissingFrom(rows) };
|
|
14
|
+
}
|
|
15
|
+
export function projectWriteProtectionCapability(posture) {
|
|
16
|
+
if (posture === undefined)
|
|
17
|
+
return null;
|
|
18
|
+
return { armed: posture.rows.length > 0, rows: posture.rows.length, replaced: replacedOf(posture.source) };
|
|
19
|
+
}
|
|
20
|
+
function replacedOf(source) {
|
|
21
|
+
switch (source) {
|
|
22
|
+
case "default":
|
|
23
|
+
case "extra":
|
|
24
|
+
return false;
|
|
25
|
+
case "replace":
|
|
26
|
+
case "off":
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=write-protection.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.64.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,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.
|
|
58
|
-
"@sema-agent/settings-schema": "1.
|
|
57
|
+
"@sema-agent/core": "7.6.2",
|
|
58
|
+
"@sema-agent/settings-schema": "1.8.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": "8.
|
|
72
|
+
"@sema-agent/sdk": "8.4.0",
|
|
73
73
|
"@types/libsodium-wrappers": "^0.7.14",
|
|
74
74
|
"@types/node": "22.10.2",
|
|
75
75
|
"@types/pg": "^8.20.0",
|