@sema-agent/server 7.127.2 → 7.129.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 +130 -0
- package/USAGE.md +137 -20
- package/dist/approval-card.d.ts +53 -26
- package/dist/approval-card.js +40 -11
- package/dist/approval-crash-convergence.js +4 -4
- package/dist/audit.js +4 -1
- package/dist/bake-runner/main.js +2 -1
- package/dist/boot/execution-lane.d.ts +11 -0
- package/dist/boot/execution-lane.js +6 -0
- package/dist/boot/memory-consolidation.d.ts +46 -16
- package/dist/boot/memory-consolidation.js +15 -8
- package/dist/boot/resolve-spec.js +3 -1
- package/dist/boot/side-query-lane.d.ts +7 -20
- package/dist/boot/side-query-lane.js +0 -24
- package/dist/boot/stage-01-config.js +3 -0
- package/dist/boot/stage-06-runners.js +1 -1
- package/dist/boot/stores.js +2 -2
- package/dist/boot/web-admin.js +31 -20
- package/dist/boot/webfetch-summarize-lane.d.ts +10 -7
- package/dist/boot/webfetch-summarize-lane.js +2 -3
- package/dist/brain.js +6 -9
- package/dist/capabilities/builtin-tools.d.ts +2 -2
- package/dist/capabilities/engine-capabilities.d.ts +14 -0
- package/dist/capabilities/engine-capabilities.js +12 -0
- package/dist/capabilities/mcp-probe.js +11 -5
- package/dist/capability-facts.d.ts +3 -1
- package/dist/capability-facts.js +1 -0
- package/dist/config-catalog.js +1 -1
- package/dist/config-provider.d.ts +17 -4
- package/dist/config-provider.js +8 -6
- package/dist/config-types.d.ts +6 -5
- package/dist/egress-redirect.d.ts +12 -0
- package/dist/egress-redirect.js +7 -0
- package/dist/entry-mode-clamp-notice.d.ts +30 -0
- package/dist/entry-mode-clamp-notice.js +13 -0
- package/dist/fleet/fleet-bus.d.ts +7 -0
- package/dist/fleet/fleet-bus.js +1 -0
- package/dist/hooks/hook-llm.js +6 -2
- package/dist/hooks/hook-runner.d.ts +1 -0
- package/dist/hooks/hook-runner.js +10 -4
- package/dist/host-lsp-manager.d.ts +3 -3
- package/dist/http/admission.js +5 -2
- package/dist/http/resume-legs.js +46 -27
- package/dist/http/route-ctx.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +6 -4
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/session-sync.js +3 -4
- package/dist/http/routes/sessions.js +4 -17
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +4 -3
- package/dist/http/wire-types.d.ts +14 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/main.js +2 -0
- package/dist/mcp-assertion-header.d.ts +89 -0
- package/dist/mcp-assertion-header.js +49 -0
- package/dist/observability/fail-open.d.ts +7 -3
- package/dist/observability/fail-open.js +7 -3
- package/dist/observability/secret-env-scrub.d.ts +37 -3
- package/dist/observability/secret-env-scrub.js +2 -2
- package/dist/parked-decide.js +5 -2
- package/dist/plugins/host-platform.d.ts +43 -13
- package/dist/plugins/host-platform.js +67 -18
- package/dist/plugins/memory-embedder.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +180 -9
- package/dist/plugins/one-time-migrations.js +315 -3
- package/dist/plugins/pg-pool.js +3 -1
- package/dist/plugins/remote-env-host.d.ts +17 -20
- package/dist/plugins/remote-env-host.js +16 -28
- package/dist/plugins/session-policy-store-sql.d.ts +16 -4
- package/dist/plugins/session-policy-store-sql.js +17 -20
- package/dist/plugins/session-rules-column.d.ts +16 -0
- package/dist/plugins/session-rules-column.js +6 -0
- package/dist/plugins/tidb-pool.js +4 -1
- package/dist/read-face-posture.d.ts +2 -1
- package/dist/request-value-closure.js +1 -1
- package/dist/retired-card-words.d.ts +17 -0
- package/dist/retired-card-words.js +49 -0
- package/dist/rule-entry-list.js +2 -0
- package/dist/run-local.js +15 -6
- package/dist/server-secret-env.d.ts +77 -85
- package/dist/server-secret-env.js +40 -26
- package/dist/session-policy-wire.d.ts +61 -124
- package/dist/session-policy-wire.js +59 -121
- package/dist/session-sync.d.ts +8 -6
- package/dist/session-sync.js +6 -5
- package/dist/spec-fields.d.ts +3 -2
- package/dist/standing-answer-grant.d.ts +17 -9
- package/dist/standing-answer-grant.js +11 -8
- package/dist/task-mcp.d.ts +31 -3
- package/dist/task-mcp.js +42 -4
- package/dist/task-settings.d.ts +6 -1
- package/dist/task-settings.js +1 -1
- package/dist/tool-approval.d.ts +14 -7
- package/dist/tool-approval.js +23 -18
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/project.d.ts +7 -4
- package/dist/trace/project.js +7 -76
- package/dist/trace/tool-output-media.d.ts +10 -4
- package/dist/trace/tool-output-media.js +7 -29
- package/dist/web-admin-client/apply-effective.js +28 -11
- package/dist/web-admin-client/facade.d.ts +2 -2
- package/dist/web-admin-client/facade.js +2 -2
- package/dist/web-admin-client/http-client.d.ts +2 -2
- package/dist/web-admin-client/read-face.d.ts +73 -20
- package/dist/web-admin-client/read-face.js +76 -36
- package/dist/web-admin-client/skills-mcp.d.ts +20 -17
- package/dist/web-admin-client/skills-mcp.js +27 -8
- package/dist/web-admin-client/types.d.ts +18 -0
- package/package.json +3 -3
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export interface
|
|
3
|
-
readonly toolName?: unknown;
|
|
1
|
+
/** 卡载体:引擎铸的问 / 停驻行 / 它们的投影行(活的 `AskRequest`、停驻行的 `tool_approval` 臂、运维队列行都结构上满足)。 */
|
|
2
|
+
export interface StandingAnswerCard {
|
|
4
3
|
readonly boundGrantDigest?: unknown;
|
|
4
|
+
readonly boundGrantAbsence?: unknown;
|
|
5
|
+
}
|
|
6
|
+
/** 调用载体:策略层的 `ToolCallRequest`、豁免店的行(只有工具名)。 */
|
|
7
|
+
export interface StandingAnswerCall {
|
|
8
|
+
readonly toolName?: unknown;
|
|
5
9
|
}
|
|
6
|
-
/** 一问的授予绑定(闭集):`none` = 应答只覆盖这一次调用;`bound` = 授予、绑定可读;`unreadable` =
|
|
10
|
+
/** 一问的授予绑定(闭集):`none` = 应答只覆盖这一次调用;`bound` = 授予、绑定可读;`unreadable` = 授予或说不清、绑定读不出。 */
|
|
7
11
|
export type GrantBinding = {
|
|
8
12
|
readonly kind: "none";
|
|
9
13
|
} | {
|
|
@@ -12,9 +16,13 @@ export type GrantBinding = {
|
|
|
12
16
|
} | {
|
|
13
17
|
readonly kind: "unreadable";
|
|
14
18
|
};
|
|
15
|
-
/**
|
|
16
|
-
export declare function grantBindingOf(
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
/** 卡上的授予绑定 —— 只读引擎的词(读法见文件头注):摘要是串 ⇒ bound;摘要不在且词是 `not_bindable` ⇒ none;其余 ⇒ unreadable。 */
|
|
20
|
+
export declare function grantBindingOf(card: StandingAnswerCard): GrantBinding;
|
|
21
|
+
/** 调用上的授予绑定 —— 调用上没有引擎的词,读引擎绑定工厂的那一条规则(见文件头注;🚧 引擎在调用上盖词之后退役)。 */
|
|
22
|
+
export declare function callGrantBindingOf(call: StandingAnswerCall): GrantBinding;
|
|
23
|
+
/**
|
|
24
|
+
* 一问的授予绑定的**唯一折点**:读哪个载体由**问的属主**定 —— 名单自己的问(`deploymentListQuestion`,即 `standingAnswerHome` 的 `server_store` 家)读调用口,
|
|
25
|
+
* 引擎铸的问读卡口(载体与理由见文件头注)。所有把「一张卡」折成 {@link StandingAnswerFacts} 的读者(活的问、待决条目、停驻行、运维队列行)经它取绑定,不各自选口。
|
|
26
|
+
*/
|
|
27
|
+
export declare function questionGrantBindingOf(carrier: StandingAnswerCard & StandingAnswerCall, deploymentListQuestion: boolean): GrantBinding;
|
|
20
28
|
//# sourceMappingURL=standing-answer-grant.d.ts.map
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { SKILL_TOOL_NAME } from "@sema-agent/core";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return
|
|
2
|
+
const NOT_BINDABLE = "not_bindable";
|
|
3
|
+
export function grantBindingOf(card) {
|
|
4
|
+
const digest = card.boundGrantDigest;
|
|
5
|
+
if (typeof digest === "string")
|
|
6
|
+
return { kind: "bound", digest };
|
|
7
|
+
return digest === undefined && card.boundGrantAbsence === NOT_BINDABLE ? { kind: "none" } : { kind: "unreadable" };
|
|
8
8
|
}
|
|
9
|
-
export function
|
|
10
|
-
return
|
|
9
|
+
export function callGrantBindingOf(call) {
|
|
10
|
+
return call.toolName === SKILL_TOOL_NAME ? { kind: "unreadable" } : { kind: "none" };
|
|
11
|
+
}
|
|
12
|
+
export function questionGrantBindingOf(carrier, deploymentListQuestion) {
|
|
13
|
+
return deploymentListQuestion ? callGrantBindingOf(carrier) : grantBindingOf(carrier);
|
|
11
14
|
}
|
|
12
15
|
//# sourceMappingURL=standing-answer-grant.js.map
|
package/dist/task-mcp.d.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* When the gate is closed, a sent `mcpServers` is IGNORED (advertised via `capabilities.mcpInjection`).
|
|
24
24
|
*/
|
|
25
25
|
import { type CompliancePosture, type EngineNotice, type LockedKey, type McpInjectionDropReason, type McpServerSpec } from "@sema-agent/core";
|
|
26
|
+
import { type McpIdentityHeadersIssue, type RequestMcpServerSpec } from "./mcp-assertion-header.js";
|
|
26
27
|
/** Bound on caller-supplied servers (anti-DoS; a real local `.mcp.json` has a handful). */
|
|
27
28
|
export declare const MAX_REQUEST_MCP_SERVERS = 32;
|
|
28
29
|
/**
|
|
@@ -53,6 +54,8 @@ export declare const MAX_REQUEST_MCP_DROP_NAMES = 32;
|
|
|
53
54
|
* ## 🔴 两向钉在 core 的 `McpServerSpec` 上(不是手抄)
|
|
54
55
|
* 下面六道编译期门:型上多一个键而表没跟 ⇒ 红;表里写一个型上没有的词(改名 / 拼错 / 删键后的残条目)
|
|
55
56
|
* ⇒ 也红。于是这三张表是从 core 的型**派生**出来的数据,而不是誰记得去同步的清单。
|
|
57
|
+
* (唯一一格不在 core 型上的键 = http 传输的 `assertionHeader`:引擎尚无座,本仓承载 —— 那一支的门钉在
|
|
58
|
+
* `mcp-assertion-header.ts` 的 {@link RequestMcpServerSpec} 上,引擎一旦长出同名座,那里的退役门当场红。)
|
|
56
59
|
*
|
|
57
60
|
* ## 两个消费点的处置**刻意不同**(同一张表,不同的门)
|
|
58
61
|
* · 提交腿({@link normalizeServer})对词表外的键**宽容**(读不认识的键就当没有)—— 那条腿要能安全地
|
|
@@ -64,8 +67,9 @@ export declare const MAX_REQUEST_MCP_DROP_NAMES = 32;
|
|
|
64
67
|
export declare const MCP_SERVER_ENTRY_KEYS: readonly ["allowTools", "contentOrigin", "elicitation", "name", "source", "toolAxes", "toolFaces", "transport"];
|
|
65
68
|
/** `transport.kind:"stdio"` 的键闭集(两向钉在 core 那一支上)。 */
|
|
66
69
|
export declare const STDIO_MCP_TRANSPORT_KEYS: readonly ["args", "command", "env", "kind"];
|
|
67
|
-
/** `transport.kind:"http"` 的键闭集(同上)
|
|
68
|
-
|
|
70
|
+
/** `transport.kind:"http"` 的键闭集(同上)。`assertionHeader` 是本仓承载、引擎尚无座的一格(见 {@link RequestMcpServerSpec}):
|
|
71
|
+
* 表两向钉在**请求面的 http 传输型**上 = 引擎的键 ∪ 这一格;引擎一旦长出同名座,`mcp-assertion-header.ts` 的退役门当场红。 */
|
|
72
|
+
export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ["assertionHeader", "headers", "kind", "principalHeader", "url"];
|
|
69
73
|
/** 条目层:`{}` 与缺席等价的可选 Record 键。 */
|
|
70
74
|
export declare const MCP_ENTRY_EMPTY_RECORD_KEYS: readonly ["toolAxes", "toolFaces"];
|
|
71
75
|
/** 条目层:`[]` 与缺席等价的可选数组键。 */
|
|
@@ -113,7 +117,7 @@ export interface McpInjectionDropSeat {
|
|
|
113
117
|
*/
|
|
114
118
|
export declare function validateRequestMcp(raw: unknown): ValidatedRequestMcp | null;
|
|
115
119
|
type ValidatedRequestMcp = {
|
|
116
|
-
ok:
|
|
120
|
+
ok: RequestMcpServerSpec[];
|
|
117
121
|
dropped: McpInjectionDrop[];
|
|
118
122
|
droppedTotal: number;
|
|
119
123
|
badContentOrigin: Array<{
|
|
@@ -180,6 +184,30 @@ export declare function assertRequestMcpUnlocked(bodyMcp: unknown, lockedKeys: R
|
|
|
180
184
|
* 形不合(非数组 / 条目非对象)**不归本门**——那是 `validateRequestMcp` 的既有宽容形,本门不抢裁决。
|
|
181
185
|
*/
|
|
182
186
|
export declare function assertRequestMcpContentOrigin(bodyMcp: unknown): void;
|
|
187
|
+
/**
|
|
188
|
+
* S-1515 —— `mcpServers[].transport`(http)上两个身份头的**值域门**(fresh 腿限定,同步 400):`principalHeader` 与 `assertionHeader`
|
|
189
|
+
* **同设**(`request.body_shape`),或 `assertionHeader` 在场而**不是字符串**(`request.field_invalid`)。
|
|
190
|
+
*
|
|
191
|
+
* 前者:两个键回答同一个问题(「这台 http 服务器的请求上用哪个头送身份」),同设 = 两个答案。settings 面由契约包在解析期判拒;
|
|
192
|
+
* 请求面在这里判拒 —— **同一条规则、两处执行者**,判据与拒句单点在 `mcp-assertion-header.ts`(对契约包原句逐字,差分钉守)。
|
|
193
|
+
* 后者:契约包的 zod 类型拒它;请求面若把它读成"没写过",一条鉴权声明就无声消失、server 却照常挂上去连。
|
|
194
|
+
*
|
|
195
|
+
* ## 位置与 leg 分流(与 {@link assertRequestMcpContentOrigin} 同款理由,不发明第二套)
|
|
196
|
+
* 排在腿首、任何副作用之前;**只在 fresh 腿**:`checkpoint_ctx` 存的是原始 `TaskRequestBody`,耐久 resume 逐字重放它,
|
|
197
|
+
* 从这里逃出去的 `HttpError` 会被折成 `409 resume_blocked_by_policy`,把一条 park 任务永久砖死。resume 腿走读器
|
|
198
|
+
* ({@link normalizeServer}):**降级到升级前的读法**(server 照挂、`principalHeader` 胜、`assertionHeader` 不生效)+ `classifyHonored` 一行 warn ——
|
|
199
|
+
* 不拒、也不摘 server。判据是纯值域、与租户车道无关(多租户腿本来就忽略整只数组,告诉调用方「你这条写错了」只多不少)。
|
|
200
|
+
*/
|
|
201
|
+
export declare function assertRequestMcpIdentityHeaders(bodyMcp: unknown): void;
|
|
202
|
+
/** 一条**原始**请求条目上两个身份头的问题类(没有问题 / 不是对象 / 不是 http 传输 ⇒ `undefined`)—— 提交腿的门、探测口、重放腿的披露三处共读,
|
|
203
|
+
* 判据本体在 `mcp-assertion-header.ts`;这里只负责把「条目 → 传输对象」那一步读出来。 */
|
|
204
|
+
export declare function requestEntryIdentityIssue(entry: unknown): McpIdentityHeadersIssue | undefined;
|
|
205
|
+
/** 问题类 → wire 拒面(码 + 句子):两扇拒绝门(提交腿 / 探测口)同一份。条目名进句子,过唯一的回显清洗(剥控制 / 双向字符 + 截长);
|
|
206
|
+
* 读不出名字的条目用与丢弃点同一个占位。 */
|
|
207
|
+
export declare function requestEntryIdentityRefusal(entry: unknown): {
|
|
208
|
+
errorCode: "request.body_shape" | "request.field_invalid";
|
|
209
|
+
message: string;
|
|
210
|
+
} | undefined;
|
|
183
211
|
/**
|
|
184
212
|
* The ONE predicate for "this MCP server name belongs to the deployment": the scenario baseline (what mounted for
|
|
185
213
|
* this scenario) ∪ every name the deployment DECLARED (`CenterMcp.reservedNames` — mounted or not, any scenario).
|
package/dist/task-mcp.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { mcpInjectionDroppedNotice, resolveComplianceDenies } from "@sema-agent/core";
|
|
2
2
|
import { isUsableWireName } from "./wire-name-bound.js";
|
|
3
|
-
import { HttpError } from "./security.js";
|
|
3
|
+
import { HttpError, createCodedHttpError } from "./security.js";
|
|
4
4
|
import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
|
|
5
5
|
import { CONTENT_ORIGIN_WORDS, describeBadContentOrigin, isContentOriginWord } from "./mcp-content-origin.js";
|
|
6
6
|
import { readMcpSourceLabel } from "./mcp-source-label.js";
|
|
7
|
+
import { declaresMcpAssertionHeader, describeMcpAssertionHeaderNotAString, describeMcpIdentityHeadersConflict, mcpIdentityHeadersIssue, noteMcpAssertionHeaderIgnored, noteMcpAssertionHeaderNotSent, readMcpIdentityHeaders, toCoreMcpServerSpec } from "./mcp-assertion-header.js";
|
|
8
|
+
import { echoKeyName } from "./request-key-closure.js";
|
|
7
9
|
import { readMcpToolFaces } from "./mcp-tool-faces.js";
|
|
8
10
|
import { isToolAxisIrreversibility, isToolEffect } from "./tool-axis-words.js";
|
|
9
11
|
export const MAX_REQUEST_MCP_SERVERS = 32;
|
|
10
12
|
export const MAX_REQUEST_MCP_DROP_NAMES = MAX_REQUEST_MCP_SERVERS;
|
|
11
13
|
export const MCP_SERVER_ENTRY_KEYS = ["allowTools", "contentOrigin", "elicitation", "name", "source", "toolAxes", "toolFaces", "transport"];
|
|
12
14
|
export const STDIO_MCP_TRANSPORT_KEYS = ["args", "command", "env", "kind"];
|
|
13
|
-
export const HTTP_MCP_TRANSPORT_KEYS = ["headers", "kind", "principalHeader", "url"];
|
|
15
|
+
export const HTTP_MCP_TRANSPORT_KEYS = ["assertionHeader", "headers", "kind", "principalHeader", "url"];
|
|
14
16
|
export const MCP_ENTRY_EMPTY_RECORD_KEYS = ["toolAxes", "toolFaces"];
|
|
15
17
|
export const MCP_ENTRY_EMPTY_ARRAY_KEYS = ["allowTools"];
|
|
16
18
|
export const MCP_TRANSPORT_EMPTY_RECORD_KEYS = ["env", "headers"];
|
|
@@ -60,7 +62,7 @@ function normalizeServer(e, noteBadOrigin) {
|
|
|
60
62
|
kind: "http",
|
|
61
63
|
url: tr.url,
|
|
62
64
|
...(isStringRecord(tr.headers) ? { headers: tr.headers } : {}),
|
|
63
|
-
...(
|
|
65
|
+
...readMcpIdentityHeaders(tr),
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
68
|
else {
|
|
@@ -151,6 +153,33 @@ export function assertRequestMcpContentOrigin(bodyMcp) {
|
|
|
151
153
|
` (omit the key entirely for the default: this server's tools classify as external content)`, { code: "request.field_invalid" });
|
|
152
154
|
}
|
|
153
155
|
}
|
|
156
|
+
export function assertRequestMcpIdentityHeaders(bodyMcp) {
|
|
157
|
+
if (!Array.isArray(bodyMcp))
|
|
158
|
+
return;
|
|
159
|
+
for (const e of bodyMcp) {
|
|
160
|
+
const refusal = requestEntryIdentityRefusal(e);
|
|
161
|
+
if (refusal !== undefined)
|
|
162
|
+
throw createCodedHttpError(400, refusal.errorCode, refusal.message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export function requestEntryIdentityIssue(entry) {
|
|
166
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
167
|
+
return undefined;
|
|
168
|
+
const t = entry.transport;
|
|
169
|
+
if (!t || typeof t !== "object" || Array.isArray(t))
|
|
170
|
+
return undefined;
|
|
171
|
+
return mcpIdentityHeadersIssue(t);
|
|
172
|
+
}
|
|
173
|
+
export function requestEntryIdentityRefusal(entry) {
|
|
174
|
+
const issue = requestEntryIdentityIssue(entry);
|
|
175
|
+
if (issue === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
const name = echoKeyName("", declaredName(entry));
|
|
178
|
+
if (issue === "both_set")
|
|
179
|
+
return { errorCode: "request.body_shape", message: describeMcpIdentityHeadersConflict(name) };
|
|
180
|
+
const value = (entry.transport).assertionHeader;
|
|
181
|
+
return { errorCode: "request.field_invalid", message: describeMcpAssertionHeaderNotAString(name, value) };
|
|
182
|
+
}
|
|
154
183
|
export function deploymentClosedMcpNames(baseline, reservedNames) {
|
|
155
184
|
return new Set([...(baseline ?? []).map((s) => s.name), ...reservedNames]);
|
|
156
185
|
}
|
|
@@ -198,8 +227,17 @@ function classifyHonored(baseline, entries, config, logger) {
|
|
|
198
227
|
const reservedNames = config.mcpServers?.reservedNames ?? [];
|
|
199
228
|
const closed = deploymentClosedMcpNames(baseline, reservedNames);
|
|
200
229
|
const blocked = v.ok.filter((s) => closed.has(s.name)).map((s) => s.name);
|
|
230
|
+
for (const s of v.ok)
|
|
231
|
+
if (declaresMcpAssertionHeader(s) && !closed.has(s.name))
|
|
232
|
+
noteMcpAssertionHeaderNotSent(logger, s.name);
|
|
233
|
+
const mounted = new Set(v.ok.filter((s) => !closed.has(s.name)).map((s) => s.name));
|
|
234
|
+
for (const e of entries.slice(0, MAX_REQUEST_MCP_SERVERS)) {
|
|
235
|
+
const issue = requestEntryIdentityIssue(e);
|
|
236
|
+
if (issue !== undefined && mounted.has(declaredName(e)))
|
|
237
|
+
noteMcpAssertionHeaderIgnored(logger, declaredName(e), issue);
|
|
238
|
+
}
|
|
201
239
|
return {
|
|
202
|
-
mcp: mergeRequestMcp(baseline, v.ok, reservedNames),
|
|
240
|
+
mcp: mergeRequestMcp(baseline, v.ok.map(toCoreMcpServerSpec), reservedNames),
|
|
203
241
|
drops: [...v.dropped, ...blocked.map((server) => ({ server, reason: "name_reserved_by_deployment" }))],
|
|
204
242
|
droppedTotal: v.droppedTotal + blocked.length,
|
|
205
243
|
};
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
import { type ExecutionEnv, type TaskSpec, type ThinkingLevel, type ToolPolicy, type PromptProvider, type PermissionMode } from "@sema-agent/core";
|
|
51
51
|
import type { HooksConfig } from "@sema-agent/settings-schema/hooks";
|
|
52
52
|
import { type KeyClosureIssue } from "./request-key-closure.js";
|
|
53
|
+
import { type EntryPermissionMode } from "./runtime-governance.js";
|
|
53
54
|
/**
|
|
54
55
|
* 请求面 `permissionMode` 的闭集 = core `PERMISSION_MODES` **全集**(顶层 `permissionMode` / `settings.permissions.defaultMode` /
|
|
55
56
|
* `?permissionMode=` 查询串三处同读)。本仓不持有第二份词表,也不持有「收 / 不收」的表态:判据 = core 的 `isPermissionMode`,型 = core 的
|
|
@@ -111,6 +112,7 @@ export declare function planReviewDecisionExitsPlan(decision: "approve" | "edit"
|
|
|
111
112
|
* 入口词 = `runtime-governance.ts` `entryPermissionMode(体的生效词, 地板).effective`(「词在地板下变成什么」的唯一一只函数;秩是 core 的)。本函数只加
|
|
112
113
|
* 「写进体」这一半:地板缺席 ⇒ 体**原物**交回(零字节变化);地板在场 ⇒ 新体 `{ ...body, permissionMode: 入口词 }`,调用方的对象不改;bundle 的
|
|
113
114
|
* `settings.permissions.defaultMode` 不动 —— 顶层赢过它({@link effectivePermissionMode} 的优先序),再改它就是同一语义的第二个写者。
|
|
115
|
+
* 同时交回**这一次求值的读数**(`entry`:请求词 / 地板 / 生效词 / 折没折)—— 披露这一折的通告(`entry-mode-clamp-notice.ts`)读它,不另判一遍。
|
|
114
116
|
*
|
|
115
117
|
* 调用点一个:提交入口(`http/admission.ts` 的 `prepareSpec`:值形门全部判完之后、`resolveSpec` 之前)。那一份体就是首跑装配读的体,也是
|
|
116
118
|
* `buildCheckpointCtx` 持久化的体 ⇒ 一处盖章同时覆盖首跑与每一次续跑重建;装配腿与续跑腿都不读地板(两处读 = 两个源)。
|
|
@@ -120,7 +122,10 @@ export declare function planReviewDecisionExitsPlan(decision: "approve" | "edit"
|
|
|
120
122
|
export declare function stampEntryPermissionMode<B extends {
|
|
121
123
|
permissionMode?: unknown;
|
|
122
124
|
settings?: unknown;
|
|
123
|
-
}>(body: B, floor: PermissionMode | undefined):
|
|
125
|
+
}>(body: B, floor: PermissionMode | undefined): {
|
|
126
|
+
readonly body: B;
|
|
127
|
+
readonly entry: EntryPermissionMode;
|
|
128
|
+
};
|
|
124
129
|
/**
|
|
125
130
|
* S-433 —— **一条已持久化的提交体的生效 permission mode**,单点。
|
|
126
131
|
*
|
package/dist/task-settings.js
CHANGED
|
@@ -35,7 +35,7 @@ export function planReviewDecisionExitsPlan(decision) {
|
|
|
35
35
|
}
|
|
36
36
|
export function stampEntryPermissionMode(body, floor) {
|
|
37
37
|
const entry = entryPermissionMode(storedBodyPermissionMode(body), floor);
|
|
38
|
-
return entry.floor === undefined ? body : { ...body, permissionMode: entry.effective };
|
|
38
|
+
return { body: entry.floor === undefined ? body : { ...body, permissionMode: entry.effective }, entry };
|
|
39
39
|
}
|
|
40
40
|
export function storedBodyPermissionMode(body) {
|
|
41
41
|
return effectivePermissionMode(body, parseTaskSettings(body.settings).settings);
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
import { type ParkedGateFace } from "./approval-content-kind.js";
|
|
59
59
|
import { type ApprovalDecidedBy, type HostSettledBy, type RetiredDecisionKeyFacts } from "./host-decision.js";
|
|
60
60
|
import type { IncomingMessage } from "node:http";
|
|
61
|
-
import { type AskClass, type AskRequest, type AskOutcome, type PermissionResult, type CheckpointGate, type PendingAction, type PersistedRuleMandate, type ReadRootGrantCandidate, type RuleOffer, type AutoModeFallbackCause, type BoundSkillGrants, type ToolApprovalCard } from "@sema-agent/core";
|
|
61
|
+
import { type AskClass, type AskRequest, type AskOutcome, type PermissionResult, type CheckpointGate, type PendingAction, type PersistedRuleMandate, type ReadRootGrantCandidate, type RuleOffer, type AutoModeFallbackCause, type BoundGrantAbsence, type BoundSkillGrants, type ToolApprovalCard } from "@sema-agent/core";
|
|
62
62
|
import { type CardRulePersisted, type RuleConsentLane } from "./rules-consent.js";
|
|
63
63
|
import { type AllowRuleShadowProjection, type DenialLimitFallback, type RuleOffersAbsence, type ApprovalRequestFrame, type ApprovalRevokeFrame, type RuleEvidenceProjection } from "./approval-card.js";
|
|
64
64
|
import { type ApprovalAskStore, type AskRow } from "./plugins/approval-ask-store-sql.js";
|
|
@@ -67,7 +67,7 @@ import { type AskRowEndingCause, type SettleCause, type SettleOutcome } from "./
|
|
|
67
67
|
import type { ParkCommitWatch } from "./plugins/park-commit-watch.js";
|
|
68
68
|
import { type GovernanceAskMarks } from "./governance-ask-marks.js";
|
|
69
69
|
import { type GovernancePosture } from "./runtime-governance.js";
|
|
70
|
-
import { type GrantBinding, type
|
|
70
|
+
import { type GrantBinding, type StandingAnswerCall } from "./standing-answer-grant.js";
|
|
71
71
|
import { type ApprovalExemptionStore } from "./plugins/approval-exemption-store.js";
|
|
72
72
|
/** [ref]:审批 gate kind 闭集(core gateMatch 的 `human`/`irreversible_ask` ↔ outcome.gate "policy_ask")。
|
|
73
73
|
* 此前 7 份手写副本散在两只 checkpoint store 的数组/SQL 字面与 /decide 守卫——core 加审批味 kind 时
|
|
@@ -192,9 +192,10 @@ export declare function carriesDeploymentListQuestion(deploymentListAsks: boolea
|
|
|
192
192
|
* (`task-settings.ts`,`false`)—— 它们在交出这一问之前先读按工具名记的耐久豁免店(「本会话不再询问」在本层兑现,继承链上的同一问才够得着;审批席依约不退役
|
|
193
193
|
* 继承问),而**能不能读**与审批席 / 回决写侧是同一只判据({@link standingAnswerDurablyRecordable}):记得下的才代答,记不下的(带授予绑定的问 ……)不代答,
|
|
194
194
|
* 存量行也不例外 —— 一事一读。事实取自**这一问本身**:类集经 core 包根唯一的类派生 `classifyAsk`({@link NO_GATE_FACTS});`requiresRealApproval` /
|
|
195
|
-
* `mandated` 读这一问自报的成员;治理位恒假(本层不是治理层)
|
|
195
|
+
* `mandated` 读这一问自报的成员;治理位恒假(本层不是治理层);授予绑定读**调用**(`callGrantBindingOf` —— 本层手里还没有引擎铸的卡,core 7.51.0 不在
|
|
196
|
+
* `ToolCallRequest` 上盖绑定词,S-1493 载体问题见 `standing-answer-grant.ts` 头注)。
|
|
196
197
|
*/
|
|
197
|
-
export declare function policyAskStandingAnswerFacts(req:
|
|
198
|
+
export declare function policyAskStandingAnswerFacts(req: StandingAnswerCall, ask: Extract<PermissionResult, {
|
|
198
199
|
action: "ask";
|
|
199
200
|
}>, deploymentListQuestion: boolean): StandingAnswerFacts;
|
|
200
201
|
/**
|
|
@@ -216,7 +217,8 @@ export interface StandingAnswerFacts {
|
|
|
216
217
|
readonly mandated: boolean;
|
|
217
218
|
/** 这一问的应答**授不授予**这一次调用之外的东西、授予的话绑定是什么(批准引擎的 `Skill` = 激活技能 + 本 run 内清单免问)。
|
|
218
219
|
* `bound` ⇒ 站着的答案按「工具 + 绑定」记、只代答绑定相同的问(core 7.43.0 #1348 / #1357:活的问与停驻行同带 `boundGrantDigest`);
|
|
219
|
-
* `unreadable` ⇒ 不可记、不被代答(fail-closed)。读者一律经 `standing-answer-grant.ts`
|
|
220
|
+
* `unreadable` ⇒ 不可记、不被代答(fail-closed)。读者一律经 `standing-answer-grant.ts` 折这一位:载体由问的属主定(`questionGrantBindingOf`)——
|
|
221
|
+
* 引擎铸的问读卡上引擎的词(`grantBindingOf`),部署名单自己的问与策略层的调用读 `callGrantBindingOf`(S-1493)。 */
|
|
220
222
|
readonly grantBinding: GrantBinding;
|
|
221
223
|
/** 引擎盖在这一问上的**类集**(`AskRequest.classes` / 停驻行孪生座;经 core 的屏 `screenAskClassMembers` 读)。`undefined` = 卡上没有类
|
|
222
224
|
* (老引擎 / 不盖类的站)或屏拒 —— 读不出的类不是「无类」。 */
|
|
@@ -275,7 +277,7 @@ export declare function standingAnswerHome(facts: StandingAnswerFacts): Standing
|
|
|
275
277
|
* `seat` 还得没有授予绑定 —— 耐久店按 `(会话, 工具名)` 记,没有绑定这一维,记下一张带绑定的答案就会代答同名工具的每一种绑定
|
|
276
278
|
* (`plugins/approval-exemption-store.ts`);`none` 记不住。读者:耐久 `/decide` 腿的 remember 可记性(`http/resume-legs.ts`)与运维队列行的
|
|
277
279
|
* `rememberRefused`(`http/routes/approvals-assistant.ts`)—— 两处同答;策略层的豁免读面(部署名单门、workflow 问句门)经本函数 + {@link policyAskStandingAnswerFacts}
|
|
278
|
-
* 决定读不读店。读侧同理:耐久店不代答带绑定的问(`standingGrantSettles`
|
|
280
|
+
* 决定读不读店。读侧同理:耐久店不代答带绑定的问(`standingGrantSettles` 读这一问的 `grantBinding`,不是 `none` 就不读店)。
|
|
279
281
|
*/
|
|
280
282
|
export declare function standingAnswerDurablyRecordable(facts: StandingAnswerFacts): boolean;
|
|
281
283
|
/**
|
|
@@ -306,7 +308,7 @@ export declare function standingAnswerDurablyRecordable(facts: StandingAnswerFac
|
|
|
306
308
|
* (`SENSITIVE_WRITE_PATTERNS`)、`commandPolicy`、`AUTONOMY` 内层策略产的门与 shellGate `classify` 档,park 行上没有取证格
|
|
307
309
|
* (live 腿靠进程内标记表,跨副本 / 重启后够不着),本折法读不到它们的治理出身。
|
|
308
310
|
*/
|
|
309
|
-
export declare function parkRowStandingAnswerFacts(
|
|
311
|
+
export declare function parkRowStandingAnswerFacts(stored: Extract<PendingAction, {
|
|
310
312
|
kind: "tool_approval";
|
|
311
313
|
}>, gate: CheckpointGate, governance: GovernancePosture | undefined,
|
|
312
314
|
/** 部署名单门对这一行的调用出不出它自己的问(`approval.ts` 的 `deploymentListAsks`,调用方现读活配置;停驻行上只有 wire 名)。 */
|
|
@@ -467,6 +469,11 @@ export interface ToolApprovalFrame {
|
|
|
467
469
|
/** 上一位哈希的那一份值:`{ skill, allowedTools, disallowedTools }`(`skill: null` = 调用没点名任何已载技能)。与摘要同在同缺;**部署文本**,
|
|
468
470
|
* 壳按上下文转义渲染(core:display metadata)。逐字过境。 */
|
|
469
471
|
boundGrants?: BoundSkillGrants;
|
|
472
|
+
/** S-1493(core 7.51.0 #1688,**ADDITIVE**,`"tool_approval"` only):这张卡为什么没有 {@link boundGrantDigest} —— 引擎铸的每张卡恰带两者之一:
|
|
473
|
+
* `not_bindable`(这一问不是引擎 `Skill` 工具的激活,不绑任何授予 ⇒ 应答可按工具记)/ `unreadable`(绑着却读不出;活卡上引擎恒给摘要,这一词
|
|
474
|
+
* 只出现在读存量行的面上)。闭集经 `readClosedWordSeat(req, "boundGrantAbsence")` 窄读(筛 = core `BOUND_GRANT_ABSENCES`),集外 ⇒ 缺席 + 计一次。
|
|
475
|
+
* 与停驻行 / 运维队列行 / inbox 行的同名位同值;站着的答案的授予绑定(引擎铸的问那一家;部署名单自己的问读调用口)读它(`standing-answer-grant.ts` `grantBindingOf`)。display。 */
|
|
476
|
+
boundGrantAbsence?: BoundGrantAbsence;
|
|
470
477
|
/** core 7.43.0 #1353:分类器没给判词的成因词(`error` / `timeout` / `parse_error`;只在类 `classifier_no_ruling` 的卡上)。闭集经 core 的
|
|
471
478
|
* `readClosedWordSeat(req, "classifierNoRuling")` 窄读(筛 = core `isAutoModeFallbackCause`),集外 ⇒ 缺席 + 计一次。display:本仓不按它裁决。 */
|
|
472
479
|
classifierNoRuling?: AutoModeFallbackCause;
|
package/dist/tool-approval.js
CHANGED
|
@@ -9,13 +9,13 @@ import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerpri
|
|
|
9
9
|
import { createLogger } from "./observability/logger.js";
|
|
10
10
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
11
11
|
import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
12
|
-
import {
|
|
12
|
+
import { currentParkArm, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readProbeReason, readReadRootCandidate, readStoredCardEnvelope, readRuleEvidence, readAllowRuleShadowed, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, readAskClassPair, readClosedWordSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
|
|
13
13
|
import { POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
|
|
14
14
|
import { APPROVAL_ENDING_CAUSES, SETTLE_OUTCOME, causeOfAskRow } from "./approval-deny-reasons.js";
|
|
15
15
|
import { settleByPolicy } from "./approval-policy-settlement.js";
|
|
16
16
|
import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
|
|
17
17
|
import { governanceOriginOf } from "./runtime-governance.js";
|
|
18
|
-
import {
|
|
18
|
+
import { callGrantBindingOf, questionGrantBindingOf } from "./standing-answer-grant.js";
|
|
19
19
|
import { operatorAsk } from "./operator-ask.js";
|
|
20
20
|
import { APPROVAL_EXEMPTED_EVENT } from "./plugins/approval-exemption-store.js";
|
|
21
21
|
const defaultLogger = createLogger();
|
|
@@ -99,13 +99,14 @@ function sessionAllowKey(owner, sessionId, category, binding) {
|
|
|
99
99
|
}
|
|
100
100
|
function askStandingAnswerFacts(req, governanceForced, deploymentListAsks) {
|
|
101
101
|
const classes = screenAskClassMembers(req.askClass, req.classes)?.classes;
|
|
102
|
+
const deploymentListQuestion = carriesDeploymentListQuestion(deploymentListAsks, classes);
|
|
102
103
|
return {
|
|
103
104
|
governanceForced,
|
|
104
105
|
requiresRealApproval: req.requiresRealApproval === true,
|
|
105
106
|
mandated: req.mandated === true,
|
|
106
|
-
grantBinding:
|
|
107
|
+
grantBinding: questionGrantBindingOf(req, deploymentListQuestion),
|
|
107
108
|
classes,
|
|
108
|
-
deploymentListQuestion
|
|
109
|
+
deploymentListQuestion,
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
112
|
const NO_GATE_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} };
|
|
@@ -118,7 +119,7 @@ export function policyAskStandingAnswerFacts(req, ask, deploymentListQuestion) {
|
|
|
118
119
|
governanceForced: false,
|
|
119
120
|
requiresRealApproval: ask.requiresRealApproval === true,
|
|
120
121
|
mandated: ask.mandated === true,
|
|
121
|
-
grantBinding:
|
|
122
|
+
grantBinding: callGrantBindingOf(req),
|
|
122
123
|
classes: [...classifyAsk(ask, NO_GATE_FACTS).classes],
|
|
123
124
|
deploymentListQuestion,
|
|
124
125
|
};
|
|
@@ -146,16 +147,18 @@ export function standingAnswerDurablyRecordable(facts) {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
|
-
export function parkRowStandingAnswerFacts(
|
|
150
|
+
export function parkRowStandingAnswerFacts(stored, gate, governance, deploymentListAsks) {
|
|
150
151
|
const riskDescriptor = "riskDescriptor" in gate ? gate.riskDescriptor : undefined;
|
|
152
|
+
const pending = currentParkArm(stored);
|
|
151
153
|
const classes = screenAskClassMembers(pending.askClass, pending.classes)?.classes;
|
|
154
|
+
const deploymentListQuestion = carriesDeploymentListQuestion(deploymentListAsks, classes);
|
|
152
155
|
return {
|
|
153
156
|
governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
|
|
154
157
|
requiresRealApproval: readParkRowTrueSeat(pending, "requiresRealApproval"),
|
|
155
158
|
mandated: readParkRowTrueSeat(pending, "mandated") || mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
|
|
156
|
-
grantBinding:
|
|
159
|
+
grantBinding: questionGrantBindingOf(pending, deploymentListQuestion),
|
|
157
160
|
classes,
|
|
158
|
-
deploymentListQuestion
|
|
161
|
+
deploymentListQuestion,
|
|
159
162
|
};
|
|
160
163
|
}
|
|
161
164
|
function pendingEntryStandingAnswerFacts(entry) {
|
|
@@ -831,7 +834,7 @@ export class ToolApprovalCoordinator {
|
|
|
831
834
|
if (!sessionId)
|
|
832
835
|
return false;
|
|
833
836
|
const live = this.allowAllSessions.has(sessionAllowKey(origin.owner, sessionId, sessionAllowCategory(req.toolName), facts.grantBinding));
|
|
834
|
-
const durable = !live &&
|
|
837
|
+
const durable = !live && facts.grantBinding.kind === "none" && (await this.durableGrantHas(sessionId, req.toolName));
|
|
835
838
|
if (!live && !durable)
|
|
836
839
|
return false;
|
|
837
840
|
if (!(await this.sessionGrantUncontestedByRow(origin, req)))
|
|
@@ -1154,6 +1157,7 @@ export class ToolApprovalCoordinator {
|
|
|
1154
1157
|
let ruleLaneMaterial = this.buildRuleLaneMaterial(req, origin.owner, governanceForced, origin.sessionId);
|
|
1155
1158
|
const mandateWord = readClosedWordSeat(req, "mandate");
|
|
1156
1159
|
const noRulingCause = readClosedWordSeat(req, "classifierNoRuling");
|
|
1160
|
+
const grantAbsence = readClosedWordSeat(req, "boundGrantAbsence");
|
|
1157
1161
|
const cardWord = readApprovalCard(req.approvalCard);
|
|
1158
1162
|
const originWord = typeof req.origin === "string" && req.origin.length > 0 ? req.origin : undefined;
|
|
1159
1163
|
const askClassPair = readAskClassPair(req);
|
|
@@ -1185,6 +1189,7 @@ export class ToolApprovalCoordinator {
|
|
|
1185
1189
|
...(standingAnswerHome(askStandingAnswerFacts(req, governanceForced, deploymentListAsks)) === "none" ? { rememberRefused: true } : {}),
|
|
1186
1190
|
...(typeof req.boundGrantDigest === "string" ? { boundGrantDigest: req.boundGrantDigest, ...(req.boundGrants !== undefined ? { boundGrants: req.boundGrants } : {}) } : {}),
|
|
1187
1191
|
...(noRulingCause !== undefined ? { classifierNoRuling: noRulingCause } : {}),
|
|
1192
|
+
...(grantAbsence !== undefined ? { boundGrantAbsence: grantAbsence } : {}),
|
|
1188
1193
|
...(cardWord !== undefined ? { approvalCard: cardWord } : {}),
|
|
1189
1194
|
...(mandateWord !== undefined ? { mandate: mandateWord } : {}),
|
|
1190
1195
|
...(originWord !== undefined ? { origin: originWord } : {}),
|
|
@@ -1273,20 +1278,20 @@ export class ToolApprovalCoordinator {
|
|
|
1273
1278
|
releaseAdmission();
|
|
1274
1279
|
return replayTerminalRow(existingOrCreated.row);
|
|
1275
1280
|
}
|
|
1276
|
-
const persistedEnvelope =
|
|
1281
|
+
const persistedEnvelope = readStoredCardEnvelope(existingOrCreated.row.cardJson);
|
|
1277
1282
|
const maxMintableWindowMs = Math.max(effectiveWindowMs, this.unreachedTtlMs);
|
|
1278
1283
|
const sanePersistedDeadline = Number.isFinite(existingOrCreated.row.expiresAtMs) && existingOrCreated.row.expiresAtMs <= Date.now() + maxMintableWindowMs + DURABLE_CALL_TIMEOUT_MS;
|
|
1279
|
-
if (
|
|
1280
|
-
this.noteStoreError(new Error(`persisted approval row is unusable (envelopeOk=${persistedEnvelope
|
|
1284
|
+
if (persistedEnvelope === undefined || !sanePersistedDeadline) {
|
|
1285
|
+
this.noteStoreError(new Error(`persisted approval row is unusable (envelopeOk=${persistedEnvelope !== undefined}, deadlineOk=${sanePersistedDeadline}, ` +
|
|
1281
1286
|
`origin=${existingOrCreated.inserted ? "row-this-call-just-inserted" : "row-hit-idempotently"})`), "ensureAsk(persisted-row-unusable)");
|
|
1282
1287
|
releaseAdmission();
|
|
1283
1288
|
const unattended = this.unattendedOutcome({ arm: "persisted_row_unusable", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
|
|
1284
1289
|
return existingOrCreated.inserted ? this.landUnattendedOutcome(unattended, existingOrCreated.row) : unattended;
|
|
1285
1290
|
}
|
|
1286
1291
|
persistedExpiresAtMs = existingOrCreated.row.expiresAtMs;
|
|
1287
|
-
wireApprovalId = persistedEnvelope.
|
|
1292
|
+
wireApprovalId = persistedEnvelope.approvalId;
|
|
1288
1293
|
frame.approvalId = wireApprovalId;
|
|
1289
|
-
cardForFrame = persistedEnvelope.
|
|
1294
|
+
cardForFrame = persistedEnvelope.card;
|
|
1290
1295
|
if (cardForFrame.governanceForced === true)
|
|
1291
1296
|
frame.governanceForced = true;
|
|
1292
1297
|
else
|
|
@@ -1726,9 +1731,9 @@ export class ToolApprovalCoordinator {
|
|
|
1726
1731
|
return shapeOutcome(await allowedP);
|
|
1727
1732
|
}
|
|
1728
1733
|
}
|
|
1729
|
-
const entryGrantBinding = grantBindingOf(req);
|
|
1730
1734
|
const entryClasses = askClassPair?.classes;
|
|
1731
1735
|
const entryDeploymentListQuestion = carriesDeploymentListQuestion(deploymentListAsks, entryClasses);
|
|
1736
|
+
const entryGrantBinding = questionGrantBindingOf(req, entryDeploymentListQuestion);
|
|
1732
1737
|
const rememberRefused = standingAnswerHome(pendingEntryStandingAnswerFacts({
|
|
1733
1738
|
...(effectiveGovernanceForced ? { governanceForced: true } : {}),
|
|
1734
1739
|
...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
@@ -2145,10 +2150,10 @@ export class ToolApprovalCoordinator {
|
|
|
2145
2150
|
if (lane === undefined || row.owner === null)
|
|
2146
2151
|
return flag(false, "rule_lane_unavailable");
|
|
2147
2152
|
const owner = row.owner;
|
|
2148
|
-
const envelope =
|
|
2149
|
-
if (
|
|
2153
|
+
const envelope = readStoredCardEnvelope(row.cardJson);
|
|
2154
|
+
if (envelope === undefined)
|
|
2150
2155
|
return flag(false, "rule_material_absent");
|
|
2151
|
-
const card = envelope.
|
|
2156
|
+
const card = envelope.card;
|
|
2152
2157
|
if (card.governanceForced === true)
|
|
2153
2158
|
return flag(false, "rule_governance_forced");
|
|
2154
2159
|
const command = row.ruleCommand;
|
|
@@ -36,14 +36,14 @@ type _GuardProvenanceReverse = AssertAllKeysHandled<Exclude<ProvenanceProjected
|
|
|
36
36
|
type AgentMessageProjected = "from" | "body";
|
|
37
37
|
type _GuardAgentMessage = AssertAllKeysHandled<Exclude<keyof NonNullable<TaskNotificationPayload["agentMessage"]>, AgentMessageProjected>>;
|
|
38
38
|
type _GuardAgentMessageReverse = AssertAllKeysHandled<Exclude<AgentMessageProjected, keyof NonNullable<TaskNotificationPayload["agentMessage"]>>>;
|
|
39
|
-
type BgNotifProjected = "taskId" | "sessionId" | "seq" | "status" | "summary" | "stoppedBy" | "resumable" | "recentSteps" | "editedFiles" | "usage" | "transcriptId" | "rootSessionId" | "parentTaskId" | "parentToolCallId" | "completionId" | "spawnName";
|
|
39
|
+
type BgNotifProjected = "taskId" | "sessionId" | "seq" | "status" | "summary" | "stoppedBy" | "resumable" | "recentSteps" | "editedFiles" | "usage" | "transcriptId" | "rootSessionId" | "parentTaskId" | "parentToolCallId" | "completionId" | "spawnName" | "result";
|
|
40
40
|
type BgNotifExcluded = "kind" | "sessionScoped" | "owner" | "scope" | "description" | "agentType" | "name" | "currentAction" | "currentTool" | "parentSessionId" | "startedAt" | "workflowRunId" | "progressTaskId" | "progressParentTaskId" | "model";
|
|
41
41
|
type _GuardBgNotif = AssertAllKeysHandled<Exclude<keyof BackgroundChildEvent, BgNotifProjected | BgNotifExcluded>>;
|
|
42
42
|
type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNotifExcluded, keyof BackgroundChildEvent>>;
|
|
43
43
|
type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
|
|
44
44
|
type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
|
|
45
45
|
type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
|
|
46
|
-
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "approvalCard";
|
|
46
|
+
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "allowRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "approvalCard" | "boundGrantAbsence";
|
|
47
47
|
type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy";
|
|
48
48
|
type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
|
|
49
49
|
type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
|
|
@@ -136,7 +136,7 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
|
|
|
136
136
|
/** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
|
|
137
137
|
type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
|
|
138
138
|
type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
|
|
139
|
-
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
|
|
139
|
+
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling" | "boundGrantAbsence";
|
|
140
140
|
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "approvalCard" | "restoreMode" | "checkpointId" | "previewWithheld" | "authoredBy";
|
|
141
141
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
142
142
|
type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
|
|
17
17
|
* 刚立,立完就在下一次加码时自己咬住了)。
|
|
18
18
|
*/
|
|
19
|
-
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "task.tool_turn_incomplete", "route.finish_reason_unknown", "route.stream_end_ignored"];
|
|
19
|
+
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "permission.entry_mode_clamped", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "permission.rule_row_dropped", "permission.allowed_tool_not_granted", "task.tool_turn_incomplete", "route.finish_reason_unknown", "route.stream_end_ignored"];
|
|
20
20
|
export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
|
|
21
21
|
/** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
|
|
22
22
|
export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
|
|
@@ -29,6 +29,7 @@ export const ENGINE_NOTICE_WIRE_CODES = [
|
|
|
29
29
|
"instructions.source_changed",
|
|
30
30
|
"session.records_skipped",
|
|
31
31
|
"server.model_unreachable",
|
|
32
|
+
"permission.entry_mode_clamped",
|
|
32
33
|
"classifier.unavailable",
|
|
33
34
|
"config.permission_mode_unavailable",
|
|
34
35
|
"permission.ask_released_by_mode",
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -167,7 +167,8 @@ export declare function toolStartEventData(ev: {
|
|
|
167
167
|
* `done` 永远到不了、重连恒复现同一处),而 `gate-outcome-defective` 那一格还不涨。
|
|
168
168
|
*
|
|
169
169
|
* ⇒ 兑现方式从「枚举」换成**结构**:任何逃出投影的异常同走既有的那一口 report + withhold。规则因此变少
|
|
170
|
-
* (不是「守住已知的 N 种坏形」而是「坏字节一律 withhold」)
|
|
170
|
+
* (不是「守住已知的 N 种坏形」而是「坏字节一律 withhold」)。core 7.51.0 起筛本身对任意 JSON 形不抛(KL-1731-a:只覆盖 JSON ——
|
|
171
|
+
* 访问器 / Proxy 这类非 JSON 值仍可能抛),本仓此前叠在筛前的具名形门与成员门(第二读法)随之退役,诊断串就是筛给的缺陷句。
|
|
171
172
|
* ⛔ **刻意不动**「先脱敏后截断」的次序:截断在前会把跨界的密钥切成半截明文留在输出里,而且它只治
|
|
172
173
|
* `note` 一条叶(`approver` 同病)—— 那是给一个类打两个特判,正是本仓要消掉的形。
|
|
173
174
|
*/
|
|
@@ -188,11 +189,13 @@ export declare function screenedGate(v: unknown): GateOutcome | undefined;
|
|
|
188
189
|
* 一律过 {@link screenedGate},零例外」。它按**型**分派而不是按调用点,所以将来第二个携带
|
|
189
190
|
* `GateOutcome` 的账本型只需把 {@link GATE_BEARING_LEDGER_EVENT_TYPE} 扩成词表,读腿一行都不用改。
|
|
190
191
|
*
|
|
191
|
-
* ## 读腿与写腿现在是**同一只**筛(
|
|
192
|
+
* ## 读腿与写腿现在是**同一只**筛(零双读)
|
|
192
193
|
* 7.72.0–7.73.0 期间读腿多一步「认本服务上一版写下的退役词旧拼法」(`unresolvable` → `ancestor_marked`),
|
|
193
194
|
* 那是一条**长期双读臂**:同一份字节在写腿与读腿上有两种理解,而且没有到期日。7.74.0 把它换成**一次性
|
|
194
|
-
* 迁移**(启动期把存量字节原地改名;窗关后已删码)
|
|
195
|
-
*
|
|
195
|
+
* 迁移**(启动期把存量字节原地改名;窗关后已删码)。S-1542(core 7.51.0 #1731)起这只筛是 core `screenGateOutcome(g)` 缺省的**档案读法**
|
|
196
|
+
* (每个闭集 = 今日成员 ∪ core `RETIRED_MEMBERS`),读腿与写腿同走它:残留旧拼法的字节(local 车道引擎 File 店里不可改写的已决行)
|
|
197
|
+
* **整条读回、原词上帧、计数 0**(档案词是当年合法铸下的事实,不是缺陷,不改写成今天的词);整条 withhold + 计数的只有真缺陷
|
|
198
|
+
* (集外且不在退役表的词、破不变量、形坏)。读法只有一种。
|
|
196
199
|
*
|
|
197
200
|
* ## 与写腿脱敏汇点的关系(别再走回头路)
|
|
198
201
|
* 写腿的对偶是 `redactLedgerEventData`(三孪生 store 的 `appendEvent` 落库前),它管的是脱敏、不是判形;
|