@sema-agent/server 7.100.0 → 7.101.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/MIGRATION.md +22 -1
- package/README.md +12 -1
- package/README.zh-CN.md +9 -1
- package/USAGE.md +80 -9
- package/dist/approval-card.d.ts +24 -12
- package/dist/approval-card.js +4 -3
- package/dist/approval-policy-settlement.d.ts +31 -0
- package/dist/approval-policy-settlement.js +16 -0
- package/dist/approval-reconciler.d.ts +8 -1
- package/dist/approval-reconciler.js +5 -4
- package/dist/boot/config-center.js +86 -20
- package/dist/boot/execution-env.js +20 -2
- package/dist/boot/parked-revive-gate.js +1 -0
- package/dist/boot/reapers.js +1 -0
- package/dist/boot/resolve-spec.js +25 -8
- package/dist/boot/runner-deps.js +9 -3
- package/dist/boot/stage-02-registry-pull.js +9 -0
- package/dist/boot/stage-10-http-server.js +2 -0
- package/dist/boot/stores.js +4 -0
- package/dist/config-catalog.js +5 -3
- package/dist/config-center/apply-effective.d.ts +25 -0
- package/dist/config-center/apply-effective.js +144 -69
- package/dist/config-center/apply-ledger.d.ts +12 -2
- package/dist/config-center/apply-ledger.js +4 -1
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/http-client.d.ts +4 -1
- package/dist/config-center/http-client.js +15 -10
- package/dist/config-center/read-warnings.d.ts +115 -6
- package/dist/config-center/read-warnings.js +115 -5
- package/dist/config-center/types.d.ts +6 -0
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +15 -0
- package/dist/config-provider.d.ts +4 -0
- package/dist/config-provider.js +64 -9
- package/dist/config-types.d.ts +37 -8
- package/dist/config.js +87 -22
- package/dist/deployment-governance.d.ts +45 -12
- package/dist/deployment-governance.js +37 -9
- package/dist/env-name-allowlist-knobs.d.ts +37 -0
- package/dist/env-name-allowlist-knobs.js +24 -0
- package/dist/fleet/fleet-bus.d.ts +30 -4
- package/dist/fleet/fleet-bus.js +20 -4
- package/dist/fleet/fleet-reconciler.d.ts +29 -6
- package/dist/fleet/fleet-reconciler.js +109 -79
- package/dist/fleet-client.d.ts +2 -1
- package/dist/hooks/hook-llm.js +13 -4
- package/dist/host-lsp-manager.d.ts +14 -2
- package/dist/host-lsp-manager.js +7 -6
- package/dist/http/admission.js +22 -7
- package/dist/http/memory-store-refusal-reply.d.ts +24 -0
- package/dist/http/memory-store-refusal-reply.js +10 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +3 -3
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +3 -5
- package/dist/http/routes/memory-origin.js +3 -5
- package/dist/http/routes/sessions.js +7 -6
- package/dist/http/routes/side-query.js +10 -4
- package/dist/instruction-source-notice.d.ts +97 -0
- package/dist/instruction-source-notice.js +78 -0
- package/dist/memory-layer-legacy-lock.d.ts +14 -0
- package/dist/memory-layer-legacy-lock.js +74 -0
- package/dist/memory-operator-faces.js +1 -17
- package/dist/memory-store-refusal.d.ts +37 -0
- package/dist/memory-store-refusal.js +14 -0
- package/dist/model-entry-refusal.d.ts +130 -0
- package/dist/model-entry-refusal.js +157 -0
- package/dist/model-provider.d.ts +9 -3
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-select.d.ts +49 -4
- package/dist/model-select.js +48 -17
- package/dist/observability/fail-open.d.ts +13 -1
- package/dist/observability/fail-open.js +13 -1
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +27 -4
- package/dist/observability/secret-env-scrub.js +22 -7
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/local-session-store.d.ts +20 -25
- package/dist/plugins/local-session-store.js +111 -64
- package/dist/plugins/pg-session-storage.d.ts +7 -0
- package/dist/plugins/pg-session-storage.js +19 -0
- package/dist/plugins/remote-env-host.d.ts +3 -1
- package/dist/plugins/remote-env-host.js +3 -6
- package/dist/plugins/tidb-pool.js +5 -0
- package/dist/plugins/tidb-session-store.d.ts +13 -0
- package/dist/plugins/tidb-session-store.js +14 -0
- package/dist/plugins/web-search.d.ts +2 -1
- package/dist/plugins/web-search.js +43 -24
- package/dist/project-memory.d.ts +10 -0
- package/dist/project-memory.js +28 -14
- package/dist/run-local.d.ts +17 -15
- package/dist/run-local.js +115 -15
- package/dist/sealed-key.d.ts +5 -2
- package/dist/server-secret-env.d.ts +23 -1
- package/dist/server-secret-env.js +9 -0
- package/dist/tool-approval.d.ts +74 -31
- package/dist/tool-approval.js +102 -49
- package/dist/trace/core-keyset-guard.d.ts +27 -5
- package/dist/trace/engine-notice-wire.d.ts +27 -1
- package/dist/trace/engine-notice-wire.js +8 -0
- package/dist/trace/wire-projection-faces.d.ts +2 -2
- package/dist/trace/wire-projection-faces.js +2 -2
- package/package.json +2 -2
- package/dist/memory-layer-preflight.d.ts +0 -46
- package/dist/memory-layer-preflight.js +0 -121
|
@@ -15,4 +15,13 @@ export function scrubServerSecrets(env, findings) {
|
|
|
15
15
|
}
|
|
16
16
|
return out;
|
|
17
17
|
}
|
|
18
|
+
export function allowlistedEnv(names, env) {
|
|
19
|
+
const out = {};
|
|
20
|
+
for (const name of names) {
|
|
21
|
+
const value = env[name];
|
|
22
|
+
if (value !== undefined)
|
|
23
|
+
out[name] = value;
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
18
27
|
//# sourceMappingURL=server-secret-env.js.map
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -124,7 +124,8 @@ export interface StandingAnswerFacts {
|
|
|
124
124
|
* **自己**的位 —— core 每一次过闸(含 failover / 重启后的重入)都由 `runGateLanes` 里的 `stampAskOrigin`
|
|
125
125
|
* 重新盖、经同一处的 `carryOf` 带上 `AskRequest`,读侧手里的 `req` 恒是这一次过闸的真值。`governanceForced`
|
|
126
126
|
* 需要行读,是因为它来自本仓的**进程内**治理标表:failover / 重启后幂等命中既有行时本地表恒空,行上的卡才
|
|
127
|
-
*
|
|
127
|
+
* 存着落库那一刻的判定。(那张「行」是本仓的 ask 账本行,不是 core 的 park 行;core 7.31.0 #1079 起 park 行上的
|
|
128
|
+
* `mandated` 孪生座由**耐久** `/decide` 腿读,见 {@link parkRowStandingAnswerFacts}。)
|
|
128
129
|
*/
|
|
129
130
|
export declare function standingAnswerMayRetire(facts: StandingAnswerFacts): boolean;
|
|
130
131
|
/**
|
|
@@ -138,19 +139,19 @@ export declare function standingAnswerMayRetire(facts: StandingAnswerFacts): boo
|
|
|
138
139
|
* 本读者这里只**收窄**(失真两个方向的代价登记在 `governanceMandatesShellGateAlways` 顶注)。⚠️ 它**只覆盖 shell 门的
|
|
139
140
|
* `always` 档** —— live 腿的治理标来自进程内标记表(敏感写路径 / `commandPolicy` / `AUTONOMY` 内层策略 / shellGate 各档),
|
|
140
141
|
* park 行上唯一的取证格只有 `riskDescriptor.shellGateDoctrine`(见下方 🚧);
|
|
141
|
-
* · `requiresRealApproval` —— 行上 core 铸的那一位,经 `approval-card.ts` 的 {@link
|
|
142
|
+
* · `requiresRealApproval` —— 行上 core 铸的那一位,经 `approval-card.ts` 的 {@link readParkRowTrueSeat}(与运维
|
|
142
143
|
* 队列 `parkRowFacts` 同一只读法,`=== true`)。刻意**不**经 `parkRowFacts` 本身:那只展示投影顺手对 `ruleOffersAbsence`
|
|
143
144
|
* 的集外词计展示面 F 类 tag,而本读者对同一格是 fail-closed(S-663 codex r1 钉「权限面不计展示面 tag」);
|
|
144
|
-
* · `mandated` ——
|
|
145
|
-
*
|
|
146
|
-
*
|
|
145
|
+
* · `mandated` —— **S-663 二段(core #1079,7.31.0 / S-723)**:行上 core 铸的孪生座 `PendingAction.tool_approval.mandated`,经
|
|
146
|
+
* **同一只** {@link readParkRowTrueSeat}(`=== true`);**或**强制子集 `ruleOffersAbsence` 经三态窄读
|
|
147
|
+
* {@link readRuleOffersAbsenceSeat} 读成 `"mandated"` / 读不出(集外词 / 非串值,世代差)⇒ `true`。两臂是**两个事实**的析取,
|
|
148
|
+
* 不是同一个事实的新旧两种读法:core 对 `"mandated"` 这一词的定义就是「no persisted rule could ever clear this ask」(强制的
|
|
149
|
+
* 充分条件),而 #1079 之前铸的行(7.30.x 引擎 park、升级后才被 `/decide`)没有孪生座 —— 只读孪生座会让这类在飞行照旧落豁免,
|
|
150
|
+
* 是对上一版的回归;读不出按强制是 fail-closed(一个本仓不认识的新词可能正是一种强制因由)。
|
|
147
151
|
*
|
|
148
|
-
* 🚧
|
|
149
|
-
* ①
|
|
150
|
-
* (
|
|
151
|
-
* `ruleClearanceOf` 的词序让 `persistedRuleShadowed` / `matchedAskRule` 先得 `"shadowed"`),本折法读不到它的强制。#1079 到货后
|
|
152
|
-
* 改的**只是 `mandated` 这一格**(改读行上那一位),谓词与调用点零改(`test/approval-exemption-http.test.ts` 的 🚧 格按 #1079
|
|
153
|
-
* 行形钉着);
|
|
152
|
+
* 🚧 **剩余不对称一处**(耐久腿读得到的事实比 live 腿少,读不到的一律照旧可记):
|
|
153
|
+
* ① (已关,S-663 二段)`mandated` 孪生位 —— core #1079 到货,行上词为 `shadowed` / 缺席的强制卡由孪生座读到
|
|
154
|
+
* (`test/approval-exemption-http.test.ts` 原 🚧 格已按钉翻成「false + 零行」);
|
|
154
155
|
* ② **非 shell 治理(候 S-707)**:治理位经 `governanceOriginOf` 只覆盖 shell 门 `always` 档;敏感写路径
|
|
155
156
|
* (`SENSITIVE_WRITE_PATTERNS`)、`commandPolicy`、`AUTONOMY` 内层策略产的门与 shellGate `classify` 档,park 行上没有取证格
|
|
156
157
|
* (live 腿靠进程内标记表,跨副本 / 重启后够不着),本折法读不到它们的治理出身。
|
|
@@ -224,16 +225,19 @@ export interface ToolApprovalFrame {
|
|
|
224
225
|
* 车道**有什么可给,而那条车道对它不说话的工具、**没接规则店的部署**(本仓的 live-only 形)、装不下规则的
|
|
225
226
|
* 任务都是结构性沉默 —— 它的缺席**不是**断言。本键是这一问**自己**的属性,凡强制皆在场。读错的代价 core
|
|
226
227
|
* 实测过:同一条根外读命令在 shell 工具上是强制问、在 monitor 工具上看起来是普通问。
|
|
227
|
-
* (
|
|
228
|
-
*
|
|
228
|
+
* (耐久 `/decide` 腿手里只有 park 行:core 7.31.0 #1079 起行上有本键的孪生座 `PendingAction.tool_approval.mandated`,
|
|
229
|
+
* S-663 二段读它;#1079 之前铸的行没有孪生座,那条腿仍读那一词的**在场**(`"mandated"` ⇒ 强制)、从不读它的缺席 —— 见
|
|
230
|
+
* {@link parkRowStandingAnswerFacts}。live 腿恒读本键。)
|
|
229
231
|
*
|
|
230
232
|
* 🔴 **它不是 `requiresRealApproval`**:强制只说「没有规则能清掉」,**不**说「没有应答者可以答」——
|
|
231
233
|
* 一个人、一只 hook、一张一揽子 `onAsk:"allow"` 席照常答得了强制卡。两键别互推。
|
|
232
234
|
*
|
|
233
|
-
* 🔴 **真才带,缺席绝不编 `false`**(`governanceForced` / `requiresRealApproval` 同形)
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
235
|
+
* 🔴 **真才带,缺席绝不编 `false`**(`governanceForced` / `requiresRealApproval` 同形)。缺席覆盖「不是强制」「老引擎」
|
|
236
|
+
* 两形。**本仓从不自铸本键**:活卡帧(与 `livePending` 行顶层,T4①)只读 `AskRequest.mandated`;耐久面(运维队列行 / inbox 行)
|
|
237
|
+
* 自 core 7.31.0 #1079 起读 core 在 park 行上铸的孪生座(`PendingAction.tool_approval.mandated` / `CheckpointSummary.mandated`,
|
|
238
|
+
* S-663 二段),同样只投 core 给的那一位 —— 自铸一个就是本仓成了这个事实的第二个源。⚠️ **`card_json` 卡面不带本键**
|
|
239
|
+
* (`ApprovalCardSchema` 没有这一位,`.strict()`)⇒ 断连重放的 `approval_request` 帧(`buildReplayFrame` 只回放卡)上它恒缺席,
|
|
240
|
+
* 消费方读队列行 / 活卡帧 / `livePending` 行(8D-6:此前这里写的「`card_json` 只读 `AskRequest.mandated`」是错句,T4② 车OC 改真)。
|
|
237
241
|
* 🔴 **server 不判「是不是强制」**(判定属主在 core 的闸站),只**读**闸站盖好的这一位 —— 读者恰一个:
|
|
238
242
|
* S-660 起的会话级放行判据 {@link standingAnswerMayRetire}(会话授权是**上一次**应答记下的站着的答案,
|
|
239
243
|
* 不是对这一次调用的应答 ⇒ 强制卡上既不被既有 grant 代答、也不铸 grant)。上一段「一揽子 `onAsk:"allow"`
|
|
@@ -522,6 +526,10 @@ export interface LivePendingRow {
|
|
|
522
526
|
sessionId?: string;
|
|
523
527
|
requiresRealApproval?: true;
|
|
524
528
|
governanceForced?: true;
|
|
529
|
+
/** T4①(八病审计 8D-5,车OC / 7.101.0):强制位([ref],`AskRequest.mandated`)—— 与上两位**同层同形**(真才带,缺席不编 false),
|
|
530
|
+
* 值 = 条目上那一位(`askBroadcast` 铸条目时取自这一次过闸的 `req.mandated`,与 {@link frame} 上的同名键同源)。此前只在
|
|
531
|
+
* `frame` 里:同一个 `GET /v1/approvals` 响应的 durable `pending[]` 行顶层带它(S-663 二段),live 行却要进帧里读。 */
|
|
532
|
+
mandated?: true;
|
|
525
533
|
/** 发起者为委派子代(originTaskId 在场)。 */
|
|
526
534
|
fromSubagent?: true;
|
|
527
535
|
/** [ref] C2(additive):委派子代的 **canonical taskId**。值域精确成文(codex R1-F3 问询后按 core dist
|
|
@@ -1086,19 +1094,23 @@ export declare class ToolApprovalCoordinator {
|
|
|
1086
1094
|
* 的终局会取决于「谁赢了那把 CAS」「重试没重试」—— 比残余更坏的不可解释性。人的**真决议**(回放出
|
|
1087
1095
|
* `true`/`false`)当然不过这个口:那不是「无人可答」。
|
|
1088
1096
|
*
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1097
|
+
* **`ensureAsk` 的两条兜底臂也归它管**(S-702,7.100.0 合并复审 opus 腿 E1;此前两臂直接交裸 `"unavailable"`,是这句
|
|
1098
|
+
* 「唯一成形口」的例外):行不可用(`persisted_row_unusable`:信封形不合 / deadline 越界)与身份不定(`ensure_indeterminate`:
|
|
1099
|
+
* ensure 失败且复核读全失败)。两臂的**处置**不变 —— 不编造 wire 身份、不在身份未定时呈卡收人决(fail-closed)—— 变的只是
|
|
1100
|
+
* 「不呈卡之后交什么」:它们同样是「这只 ask 没有人能答」,于是同样过这个口(park ⇒ `"unavailable"` 逐字不变;deny ⇒
|
|
1101
|
+
* 部署政策拒对象),交出去的是**结算**时再经 {@link landUnattendedOutcome} 把同一个结算落在行上。修前 deny 部署在这两形下
|
|
1102
|
+
* 仍 durable park(与「不积压挂起 run」的自声明相反)。
|
|
1092
1103
|
*
|
|
1093
1104
|
* 🔴 **`deny` 部署上政策结算当场落行**(NP-2,7.100.0 合并复审 [ref] 交叉表 #4;codex R1-F1 当年**不采纳**的那一半
|
|
1094
1105
|
* 「给 deny 造一条原子 deny 终态转移」在这里收了):窗到期 / emit 全灭两臂在 deny 政策下的终局 claim 是
|
|
1095
|
-
* `refuse` 意图(
|
|
1106
|
+
* `refuse` 意图(政策→意图的唯一映射在 `approval-policy-settlement.ts` 的 `settleByPolicy`)—— STREAM_PENDING→DECIDED(deny) + `settled_by = "policy"`,**按 ask
|
|
1096
1107
|
* 作用域**(不碰批、不连坐兄弟)。落的来源词与本口交给 core 的是**同一个常量**(`POLICY_REFUSAL_DECISION`),于是
|
|
1097
1108
|
* 同 ask 重入 / 收敛后重入由 `outcomeOfDecidedRow` 读回同一个对象。当年「危害为零」的论证(PARKING 行绑不上、
|
|
1098
1109
|
* 自行收敛成 DENIED/VOID)在 S-689 之后不再成立:首判交 `{allow:false, settledBy:"policy"}`,行停 PARKING 的回放
|
|
1099
1110
|
* 却交裸 `false`(收敛后 DENIED 无来源)—— core 当成人拒、可停批,两次判决第一次可被观测地分家。
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1111
|
+
* S-702 / S-706 把同一只 claim 接到了另外三处:两条兜底臂({@link landUnattendedOutcome})与崩溃恢复腿(收敛器孤儿段,
|
|
1112
|
+
* 它是窗到期竞争者在崩溃后的补位)—— 四处调的是**一只** `settleByPolicy`,谁也不自己挑意图。deny 部署上仍按 PARKING
|
|
1113
|
+
* 记账的只剩一形:别的写者(park 政策副本 / 切换政策之前)推进过的行,重入走本口(`replayed_parked`)。
|
|
1102
1114
|
* deny 政策下批仍是**单 ask 批**(见 {@link batchIdFor}):`refuse` 本来就不碰批,单 ask 批让新行与存量整腿批行
|
|
1103
1115
|
* 的差别只剩「批 id 是谁」,结算语义一致。
|
|
1104
1116
|
*/
|
|
@@ -1117,14 +1129,6 @@ export declare class ToolApprovalCoordinator {
|
|
|
1117
1129
|
* 读的是政策**读**口(不落遥测)。
|
|
1118
1130
|
*/
|
|
1119
1131
|
private batchIdFor;
|
|
1120
|
-
/**
|
|
1121
|
-
* 窗到期 / emit 全灭两臂的**终局 claim 意图**(NP-2)—— 与 {@link batchIdFor} 读同一个政策读口:
|
|
1122
|
-
* · park 路由 ⇒ `expire`(行进投递面、批转 ROUTING_UNBOUND、兄弟连坐,core 随后 park);
|
|
1123
|
-
* · deny 政策 ⇒ `refuse`(按 ask 作用域当场落 DECIDED(deny) + `settled_by="policy"`,与 {@link unattendedOutcome}
|
|
1124
|
-
* 交给 core 的那一只同一个来源词 —— 回放 / 收敛后重入读回同一个对象;不碰批、不连坐兄弟,存量整腿批行同判)。
|
|
1125
|
-
* 两臂之外的终局 claim(取消臂 `cancel`)与政策无关,不读本口。
|
|
1126
|
-
*/
|
|
1127
|
-
private get unattendedClaimIntent();
|
|
1128
1132
|
/** {@link unattendedOutcome} 的**公开**口:`windowZero` 的 immediate-unavailable 闭包长在装配层
|
|
1129
1133
|
* (`http/routes/tasks.ts` 的 sync 腿),它必须与协调器内五臂读**同一个**旋钮值,而不是各算一遍。
|
|
1130
1134
|
* 出处随调用方给(那条腿知道自己是哪一只 ask)。 */
|
|
@@ -1133,6 +1137,29 @@ export declare class ToolApprovalCoordinator {
|
|
|
1133
1137
|
owner?: string | null;
|
|
1134
1138
|
toolName?: string;
|
|
1135
1139
|
}): UnattendedAskOutcome;
|
|
1140
|
+
/**
|
|
1141
|
+
* S-702 —— 两条兜底臂的「无人可答」终局的**落行**半场。交给 core 的值已由 {@link unattendedOutcome} 一处铸好,这里只回答
|
|
1142
|
+
* 「这条持久行上落什么」,判据是「交出去的是不是**结算**」(行 = 真源:交出去的结算必须同时是行上的结算,否则同 ask 重入 /
|
|
1143
|
+
* 收敛后重入回放出来的是另一个答案 —— NP-2 在 live 腿上收掉的同一种分家):
|
|
1144
|
+
* · park 路由(`"unavailable"`)不是结算 ⇒ 行**不动**:它留在 STREAM_PENDING,投递面由既有机器接(崩溃恢复腿按政策代打),
|
|
1145
|
+
* 与两臂修前逐字同形 —— park 部署行为不变;
|
|
1146
|
+
* · 部署政策拒(deny)是结算 ⇒ 经 {@link settleByPolicy}(live 腿 / 崩溃恢复腿同一只政策感知终态 claim)把同一个结算落在行上。
|
|
1147
|
+
*
|
|
1148
|
+
* 🔴 **只对「本次调用插下的」行调用**(调用方的义务;codex 对抗复审 F1 + R2-2 / [ref] 件2 同一条纪律「放弃收尾只许动本次插入的
|
|
1149
|
+
* 行」):兜底臂发生在**窗开之前** —— 一条幂等命中的 STREAM_PENDING 行此刻可能正被别的登记(另一副本 / 另一版本 / 重入)呈卡、
|
|
1150
|
+
* 有人正要按;当场 refuse 等于把一张活卡在它自己的窗走完之前作废。所以归属证不出(幂等命中、ensure 报错、迟到结果
|
|
1151
|
+
* `inserted:false`)⇒ 本方法不被调用、行不动:它的属主按自己的窗收场,属主不在了由收敛器孤儿段(同一只 claim,S-706)在
|
|
1152
|
+
* 窗过 + 宽限之后落同一个政策结算。`row.batchId` 恒取**行上**的批(claim 的 scope;本地派生的批可能与存量行不同,
|
|
1153
|
+
* 错 scope 的 claim 答 absent,不证明「行上没有决议」—— codex F1)。
|
|
1154
|
+
*
|
|
1155
|
+
* 答案处置(律 1 答案自足,与 live 腿同一个分类器 {@link classifyClaimLoss} + 同一个同 askId 分发口 {@link dispatchClaimTruth}):
|
|
1156
|
+
* - 赢 ⇒ 交政策拒;同 askId 的其余本地登记随行收尾(与 live 腿赢臂同形:park 路由口,deny 下它们同样成形为政策拒);
|
|
1157
|
+
* - 输 ⇒ 行真相:真决议 ⇒ 交它并分发给同 askId 的其余登记(codex F2:此前只交本调用方,另一条登记可按自己的店错落成相反终局);
|
|
1158
|
+
* 行已进投递面(别的写者推进的 PARKING/PARKED)⇒ 分发 park 路由、本调用交政策拒本身(与 `replayed_parked` 在 deny 下同答);
|
|
1159
|
+
* - absent / 编辑在飞 / 店抛错 ⇒ 交政策拒本身(fail-closed 方向不变);迟到回来的 claim 答案同样分发(赢 ⇒ park 路由口;输 ⇒ 真相)。
|
|
1160
|
+
* 本方法从不抛错(店错只进 `noteStoreError`)—— 迟到落盘行的收尾对它 fire-and-forget。
|
|
1161
|
+
*/
|
|
1162
|
+
private landUnattendedOutcome;
|
|
1136
1163
|
/**
|
|
1137
1164
|
* [ref] 车3 刀 3b —— [ref] §3.3 / 设计稿 §0 X-2 的**写侧准入门**。
|
|
1138
1165
|
*
|
|
@@ -1307,6 +1334,13 @@ export declare class ToolApprovalCoordinator {
|
|
|
1307
1334
|
*/
|
|
1308
1335
|
private hasOtherSuspendedOwner;
|
|
1309
1336
|
private settleSameAskIdParkRoute;
|
|
1337
|
+
/**
|
|
1338
|
+
* claim 败方真相的**同 askId 分发口**(codex [ref] R6-2 / R7-1 立在 live 腿的闭包里;S-702 codex F2 起两条兜底臂的落行半场
|
|
1339
|
+
* {@link landUnattendedOutcome} 也调它 —— 一个分发口,不写第二份):真相是**行**的,不是某一条登记的 ⇒ 真决议经
|
|
1340
|
+
* {@link notifyExternalDecision}、投递面经 {@link settleSameAskIdParkRoute} 交给同 askId 的全部本地登记;各登记的 settle 自带
|
|
1341
|
+
* 单赢者卫兵,对已落的恒无操作。absent / 编辑在飞没有可分发的真相。
|
|
1342
|
+
*/
|
|
1343
|
+
private dispatchClaimTruth;
|
|
1310
1344
|
/** [ref]:落一条 park 墓碑(容量帽按**插入序**逐出最旧,与 `allowAllSessions` 同款有界纪律)。
|
|
1311
1345
|
* 调用点唯一 = `settle` 里的 park 路由支(见那处注);真终局绝不调它。
|
|
1312
1346
|
*
|
|
@@ -1554,6 +1588,15 @@ export declare class ToolApprovalCoordinator {
|
|
|
1554
1588
|
* 宁少列不超范围;`"__none__"` 未鉴权哨兵自然恒空)。行的 `approvalId` 就是 `respond()` 收的 id
|
|
1555
1589
|
* (同源可达,[ref] 判据);`expiresAtMs` 照抄登记时单点铸定的窗死线([ref] 三键同一个数)。 */
|
|
1556
1590
|
listLivePending(scope: string | undefined): LivePendingRow[];
|
|
1591
|
+
/**
|
|
1592
|
+
* S-751:某只委派子代此刻有没有**待决**的活卡 —— fleet 对账腿「子代活性由属主说了算」的属主②读口(另一只是
|
|
1593
|
+
* bg registry 账)。子代卡在一张没人答的卡上时当然不出帧,只看「静默」就会把它当孤儿扫下面板(报案的真形)。
|
|
1594
|
+
* 只答是非、不交出 Map(与 `isRunLiveOnThisReplica` 同姿势);纯读,零锁零结算语义。
|
|
1595
|
+
* 键 = ask 发起者域(`originTaskId` = core `AskRequest.sourceTaskId` = 子代自己的 core session id),**不是**
|
|
1596
|
+
* fleet 行 id —— 换算在 `FleetEventBus.childOwnerKeys`。只看本副本的内存登记:子代跑在本副本,它的活卡就登记在
|
|
1597
|
+
* 本副本;耐久 park 的卡由 bg registry 的 `parked` 态作保(见 fleet-reconciler.ts `bgAgentVouches`)。
|
|
1598
|
+
*/
|
|
1599
|
+
hasLivePendingFrom(originTaskId: string): boolean;
|
|
1557
1600
|
/**
|
|
1558
1601
|
* 🔴 [ref](案A)追记 F4 —— **跨副本决议的执行通知**(悬挂行的一次终态轮询)。
|
|
1559
1602
|
*
|
package/dist/tool-approval.js
CHANGED
|
@@ -8,8 +8,9 @@ import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerpri
|
|
|
8
8
|
import { createLogger } from "./observability/logger.js";
|
|
9
9
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
10
10
|
import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
11
|
-
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat,
|
|
11
|
+
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
|
|
12
12
|
import { POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
|
|
13
|
+
import { settleByPolicy } from "./approval-policy-settlement.js";
|
|
13
14
|
import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
|
|
14
15
|
import { governanceOriginOf } from "./runtime-governance.js";
|
|
15
16
|
const defaultLogger = createLogger();
|
|
@@ -87,8 +88,8 @@ export function parkRowStandingAnswerFacts(pending, gate, governance) {
|
|
|
87
88
|
const riskDescriptor = "riskDescriptor" in gate ? gate.riskDescriptor : undefined;
|
|
88
89
|
return {
|
|
89
90
|
governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
|
|
90
|
-
requiresRealApproval:
|
|
91
|
-
mandated: mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
|
|
91
|
+
requiresRealApproval: readParkRowTrueSeat(pending, "requiresRealApproval"),
|
|
92
|
+
mandated: readParkRowTrueSeat(pending, "mandated") || mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
95
|
function mandatedFromSeat(seat) {
|
|
@@ -377,9 +378,6 @@ export class ToolApprovalCoordinator {
|
|
|
377
378
|
batchIdFor(askId, sourceTaskId, runId, legKey) {
|
|
378
379
|
return this.unattendedRoutesToPark ? deriveBatchId(sourceTaskId, runId, legKey) : deriveAskScopedBatchId(askId);
|
|
379
380
|
}
|
|
380
|
-
get unattendedClaimIntent() {
|
|
381
|
-
return this.unattendedRoutesToPark ? "expire" : "refuse";
|
|
382
|
-
}
|
|
383
381
|
unattendedAskOutcome(site) {
|
|
384
382
|
return this.unattendedOutcome({
|
|
385
383
|
arm: "window_zero",
|
|
@@ -388,6 +386,32 @@ export class ToolApprovalCoordinator {
|
|
|
388
386
|
...(site.toolName !== undefined ? { toolName: site.toolName } : {}),
|
|
389
387
|
});
|
|
390
388
|
}
|
|
389
|
+
async landUnattendedOutcome(outcome, row) {
|
|
390
|
+
const store = this.askStore;
|
|
391
|
+
if (outcome === "unavailable" || !store)
|
|
392
|
+
return outcome;
|
|
393
|
+
const dispatch = (res, where) => {
|
|
394
|
+
if (res.claimed) {
|
|
395
|
+
this.settleSameAskIdParkRoute(row.askId);
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
const read = this.classifyClaimLoss(row.askId, res.current, where);
|
|
399
|
+
this.dispatchClaimTruth(row.askId, read);
|
|
400
|
+
return read;
|
|
401
|
+
};
|
|
402
|
+
let res;
|
|
403
|
+
try {
|
|
404
|
+
res = await this.withStoreDeadline(settleByPolicy(store, row, this.unattendedPolicy), "claimTerminal(unattended-fallback)", DURABLE_CALL_TIMEOUT_MS, (late) => {
|
|
405
|
+
dispatch(late, "late:unattended-fallback");
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
this.noteStoreError(err, "claimTerminal(unattended-fallback)");
|
|
410
|
+
return outcome;
|
|
411
|
+
}
|
|
412
|
+
const read = dispatch(res, "claim:unattended-fallback");
|
|
413
|
+
return read?.kind === "decided" ? read.outcome : outcome;
|
|
414
|
+
}
|
|
391
415
|
admit(taskId, owner) {
|
|
392
416
|
if (!this.askStore)
|
|
393
417
|
return () => undefined;
|
|
@@ -701,6 +725,23 @@ export class ToolApprovalCoordinator {
|
|
|
701
725
|
for (const entry of [...entries])
|
|
702
726
|
entry.settleParkRoute();
|
|
703
727
|
}
|
|
728
|
+
dispatchClaimTruth(askId, read) {
|
|
729
|
+
switch (read.kind) {
|
|
730
|
+
case "decided":
|
|
731
|
+
this.notifyExternalDecision(askId, read.outcome);
|
|
732
|
+
return;
|
|
733
|
+
case "parked":
|
|
734
|
+
this.settleSameAskIdParkRoute(askId);
|
|
735
|
+
return;
|
|
736
|
+
case "absent":
|
|
737
|
+
case "edit-in-flight":
|
|
738
|
+
return;
|
|
739
|
+
default: {
|
|
740
|
+
const unreachable = read;
|
|
741
|
+
throw new Error(`dispatchClaimTruth: unknown recovery read ${String(unreachable)}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
704
745
|
recordParkTombstone(approvalId, tomb) {
|
|
705
746
|
const existing = this.parkTombstones.get(approvalId);
|
|
706
747
|
if (existing !== undefined) {
|
|
@@ -945,16 +986,31 @@ export class ToolApprovalCoordinator {
|
|
|
945
986
|
let wireApprovalId = id;
|
|
946
987
|
let cardForFrame = card;
|
|
947
988
|
let persistedExpiresAtMs = expiresAtMs;
|
|
948
|
-
let
|
|
949
|
-
let
|
|
989
|
+
let lateEnsuredRow;
|
|
990
|
+
let lateTerminalRow;
|
|
991
|
+
let abandonedAs;
|
|
950
992
|
const attemptCreatedAtMs = Date.now();
|
|
951
|
-
const
|
|
993
|
+
const finishAbandonedRow = (orphan) => {
|
|
952
994
|
const store = this.askStore;
|
|
953
|
-
if (!store)
|
|
954
|
-
return;
|
|
955
|
-
void
|
|
956
|
-
|
|
957
|
-
|
|
995
|
+
if (!store || abandonedAs === undefined)
|
|
996
|
+
return undefined;
|
|
997
|
+
if (abandonedAs === "void") {
|
|
998
|
+
void store
|
|
999
|
+
.transitionAsk(orphan.askId, "STREAM_PENDING", "VOID", { updatedAtMs: Date.now() })
|
|
1000
|
+
.catch((err) => this.noteStoreError(err, "transitionAsk(abandon-late-ensure)"));
|
|
1001
|
+
return undefined;
|
|
1002
|
+
}
|
|
1003
|
+
return this.landUnattendedOutcome(abandonedAs, orphan);
|
|
1004
|
+
};
|
|
1005
|
+
const abandonRow = (handed) => {
|
|
1006
|
+
abandonedAs = handed === undefined || handed === "unavailable" ? "void" : handed;
|
|
1007
|
+
return lateEnsuredRow !== undefined ? finishAbandonedRow(lateEnsuredRow) : undefined;
|
|
1008
|
+
};
|
|
1009
|
+
const replayTerminalRow = (row) => {
|
|
1010
|
+
const replayed = replayTerminalAskRow(row);
|
|
1011
|
+
return replayed === "unavailable"
|
|
1012
|
+
? this.unattendedOutcome({ arm: "replayed_parked", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability })
|
|
1013
|
+
: replayed;
|
|
958
1014
|
};
|
|
959
1015
|
if (this.askStore && card) {
|
|
960
1016
|
const sourceTaskId = req.sourceTaskId ?? origin.taskId;
|
|
@@ -980,17 +1036,19 @@ export class ToolApprovalCoordinator {
|
|
|
980
1036
|
expiresAtMs,
|
|
981
1037
|
createdAtMs: attemptCreatedAtMs,
|
|
982
1038
|
}), "ensureAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1039
|
+
if (late.row.state !== "STREAM_PENDING") {
|
|
1040
|
+
lateTerminalRow = late.row;
|
|
1041
|
+
this.dispatchClaimTruth(late.row.askId, this.classifyClaimLoss(late.row.askId, projectAskTerminalClaimTruth(late.row), "late:ensureAsk"));
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
lateEnsuredRow = late.inserted ? { askId: late.row.askId, batchId: late.row.batchId } : undefined;
|
|
1045
|
+
if (lateEnsuredRow !== undefined)
|
|
1046
|
+
void finishAbandonedRow(lateEnsuredRow);
|
|
986
1047
|
});
|
|
987
1048
|
batchId = existingOrCreated.row.batchId;
|
|
988
1049
|
if (existingOrCreated.row.state !== "STREAM_PENDING") {
|
|
989
1050
|
releaseAdmission();
|
|
990
|
-
|
|
991
|
-
return replayed === "unavailable"
|
|
992
|
-
? this.unattendedOutcome({ arm: "replayed_parked", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability })
|
|
993
|
-
: replayed;
|
|
1051
|
+
return replayTerminalRow(existingOrCreated.row);
|
|
994
1052
|
}
|
|
995
1053
|
const persistedEnvelope = ApprovalCardEnvelopeSchema.safeParse(existingOrCreated.row.cardJson);
|
|
996
1054
|
const maxMintableWindowMs = Math.max(effectiveWindowMs, this.unreachedTtlMs);
|
|
@@ -999,7 +1057,8 @@ export class ToolApprovalCoordinator {
|
|
|
999
1057
|
this.noteStoreError(new Error(`persisted approval row is unusable (envelopeOk=${persistedEnvelope.success}, deadlineOk=${sanePersistedDeadline}, ` +
|
|
1000
1058
|
`origin=${existingOrCreated.inserted ? "row-this-call-just-inserted" : "row-hit-idempotently"})`), "ensureAsk(persisted-row-unusable)");
|
|
1001
1059
|
releaseAdmission();
|
|
1002
|
-
|
|
1060
|
+
const unattended = this.unattendedOutcome({ arm: "persisted_row_unusable", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
|
|
1061
|
+
return existingOrCreated.inserted ? this.landUnattendedOutcome(unattended, existingOrCreated.row) : unattended;
|
|
1003
1062
|
}
|
|
1004
1063
|
persistedExpiresAtMs = existingOrCreated.row.expiresAtMs;
|
|
1005
1064
|
wireApprovalId = persistedEnvelope.data.approvalId;
|
|
@@ -1059,23 +1118,26 @@ export class ToolApprovalCoordinator {
|
|
|
1059
1118
|
await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
|
|
1060
1119
|
}
|
|
1061
1120
|
}
|
|
1121
|
+
if (lateTerminalRow !== undefined) {
|
|
1122
|
+
releaseAdmission();
|
|
1123
|
+
return replayTerminalRow(lateTerminalRow);
|
|
1124
|
+
}
|
|
1062
1125
|
if (indeterminate) {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
voidAbandonedRow(lateEnsuredAskId);
|
|
1126
|
+
const unattended = this.unattendedOutcome({ arm: "ensure_indeterminate", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
|
|
1127
|
+
const landed = abandonRow(unattended);
|
|
1066
1128
|
releaseAdmission();
|
|
1067
|
-
return
|
|
1129
|
+
return landed ?? unattended;
|
|
1068
1130
|
}
|
|
1069
1131
|
if (!confirmedRowExists) {
|
|
1070
1132
|
askId = undefined;
|
|
1071
1133
|
batchId = undefined;
|
|
1072
|
-
abandonedEnsure = true;
|
|
1073
|
-
if (lateEnsuredAskId !== undefined)
|
|
1074
|
-
voidAbandonedRow(lateEnsuredAskId);
|
|
1075
1134
|
if (reachability === "unreached") {
|
|
1135
|
+
const unattended = this.unattendedOutcome({ arm: "unreached_no_row", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
|
|
1136
|
+
const landed = abandonRow(unattended);
|
|
1076
1137
|
releaseAdmission();
|
|
1077
|
-
return
|
|
1138
|
+
return landed ?? unattended;
|
|
1078
1139
|
}
|
|
1140
|
+
void abandonRow(undefined);
|
|
1079
1141
|
}
|
|
1080
1142
|
}
|
|
1081
1143
|
}
|
|
@@ -1151,21 +1213,7 @@ export class ToolApprovalCoordinator {
|
|
|
1151
1213
|
const broadcastClaimTruth = (read) => {
|
|
1152
1214
|
if (askId === undefined)
|
|
1153
1215
|
return;
|
|
1154
|
-
|
|
1155
|
-
case "decided":
|
|
1156
|
-
this.notifyExternalDecision(askId, read.outcome);
|
|
1157
|
-
return;
|
|
1158
|
-
case "parked":
|
|
1159
|
-
this.settleSameAskIdParkRoute(askId);
|
|
1160
|
-
return;
|
|
1161
|
-
case "absent":
|
|
1162
|
-
case "edit-in-flight":
|
|
1163
|
-
return;
|
|
1164
|
-
default: {
|
|
1165
|
-
const unreachable = read;
|
|
1166
|
-
throw new Error(`broadcastClaimTruth: unknown recovery read ${String(unreachable)}`);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1216
|
+
this.dispatchClaimTruth(askId, read);
|
|
1169
1217
|
};
|
|
1170
1218
|
const settleFromClaimLoss = (read) => {
|
|
1171
1219
|
switch (read.kind) {
|
|
@@ -1315,11 +1363,10 @@ export class ToolApprovalCoordinator {
|
|
|
1315
1363
|
const store = this.askStore;
|
|
1316
1364
|
const pinnedAskId = askId;
|
|
1317
1365
|
const pinnedBatchId = batchId;
|
|
1318
|
-
const claimIntent = this.unattendedClaimIntent;
|
|
1319
1366
|
await resolveLocalOutcome({
|
|
1320
1367
|
intent: "expire",
|
|
1321
1368
|
storeLabel: "claimTerminal(window)",
|
|
1322
|
-
claim: (timeoutMs) => boundedClaim(() => store
|
|
1369
|
+
claim: (timeoutMs) => boundedClaim(() => settleByPolicy(store, { askId: pinnedAskId, batchId: pinnedBatchId }, this.unattendedPolicy), "claimTerminal(window)", timeoutMs, (late) => {
|
|
1323
1370
|
if (!late.claimed) {
|
|
1324
1371
|
lateClaimLossSettle(late.current, "expire");
|
|
1325
1372
|
return;
|
|
@@ -1498,7 +1545,6 @@ export class ToolApprovalCoordinator {
|
|
|
1498
1545
|
const store = this.askStore;
|
|
1499
1546
|
const pinnedAskId = askId;
|
|
1500
1547
|
const pinnedBatchId = batchId;
|
|
1501
|
-
const claimIntent = this.unattendedClaimIntent;
|
|
1502
1548
|
const settleUnreachable = () => {
|
|
1503
1549
|
if (done)
|
|
1504
1550
|
return;
|
|
@@ -1508,7 +1554,7 @@ export class ToolApprovalCoordinator {
|
|
|
1508
1554
|
await resolveLocalOutcome({
|
|
1509
1555
|
intent: "unreachable",
|
|
1510
1556
|
storeLabel: "claimTerminal(emit-failed)",
|
|
1511
|
-
claim: (timeoutMs) => boundedClaim(() => store
|
|
1557
|
+
claim: (timeoutMs) => boundedClaim(() => settleByPolicy(store, { askId: pinnedAskId, batchId: pinnedBatchId }, this.unattendedPolicy), "claimTerminal(emit-failed)", timeoutMs, (late) => {
|
|
1512
1558
|
if (!late.claimed) {
|
|
1513
1559
|
lateClaimLossSettle(late.current, "unreachable");
|
|
1514
1560
|
return;
|
|
@@ -2060,6 +2106,7 @@ export class ToolApprovalCoordinator {
|
|
|
2060
2106
|
...(e.sessionId !== undefined ? { sessionId: e.sessionId } : {}),
|
|
2061
2107
|
...(e.requiresRealApproval ? { requiresRealApproval: true } : {}),
|
|
2062
2108
|
...(e.governanceForced ? { governanceForced: true } : {}),
|
|
2109
|
+
...(e.mandated ? { mandated: true } : {}),
|
|
2063
2110
|
...(e.originTaskId !== undefined ? { fromSubagent: true, originTaskId: e.originTaskId } : {}),
|
|
2064
2111
|
...(contentKind !== undefined ? { contentKind } : {}),
|
|
2065
2112
|
frame: e.frame.approvalId === id ? e.frame : { ...e.frame, approvalId: id },
|
|
@@ -2067,6 +2114,12 @@ export class ToolApprovalCoordinator {
|
|
|
2067
2114
|
}
|
|
2068
2115
|
return rows;
|
|
2069
2116
|
}
|
|
2117
|
+
hasLivePendingFrom(originTaskId) {
|
|
2118
|
+
for (const e of this.pending.values())
|
|
2119
|
+
if (e.originTaskId === originTaskId)
|
|
2120
|
+
return true;
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2070
2123
|
async pollSuspendedDecisions() {
|
|
2071
2124
|
const store = this.askStore;
|
|
2072
2125
|
if (!store)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
|
|
18
18
|
* TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
|
|
19
19
|
*/
|
|
20
|
-
import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus } from "@sema-agent/core";
|
|
20
|
+
import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, PendingAction, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus, NodeLspManagerOptions, RunnerDeps } from "@sema-agent/core";
|
|
21
21
|
import type { ToolApprovalFrame } from "../tool-approval.js";
|
|
22
22
|
/** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。
|
|
23
23
|
* 🔴 **导出**([ref] U1/U2):本仓「词表闭集,未处置成员=编译红」的钉法只许有**一个**形 ——
|
|
@@ -60,7 +60,7 @@ type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peer
|
|
|
60
60
|
type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
|
|
61
61
|
type _GuardMailboxAppendReverse = AssertAllKeysHandled<Exclude<MailboxAppendProjected, keyof MailboxAppendMessage>>;
|
|
62
62
|
type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection" | "teamRole";
|
|
63
|
-
type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny" | "peerPresence";
|
|
63
|
+
type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny" | "peerPresence" | "fileHistory";
|
|
64
64
|
type _GuardWiringSections = AssertAllKeysHandled<Exclude<keyof WiringManifest, WiringSectionProjected | WiringSectionExcluded>>;
|
|
65
65
|
type _GuardWiringSectionsReverse = AssertAllKeysHandled<Exclude<WiringSectionProjected | WiringSectionExcluded, keyof WiringManifest>>;
|
|
66
66
|
type WiringSection<K extends keyof WiringManifest> = NonNullable<WiringManifest[K]>;
|
|
@@ -130,12 +130,20 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
|
|
|
130
130
|
/** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
|
|
131
131
|
type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
|
|
132
132
|
type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
|
|
133
|
-
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
|
|
133
|
+
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable" | "mandated";
|
|
134
134
|
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
|
|
135
135
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
136
136
|
type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
|
|
137
|
+
type ParkArmKeys = keyof Extract<PendingAction, {
|
|
138
|
+
kind: "tool_approval";
|
|
139
|
+
}>;
|
|
140
|
+
type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
|
|
141
|
+
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
|
|
142
|
+
type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
|
|
143
|
+
type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
|
|
144
|
+
type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
|
|
137
145
|
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "approverPosture" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "autoModeRequested" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools";
|
|
138
|
-
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
|
|
146
|
+
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "fileHistory";
|
|
139
147
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
140
148
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
141
149
|
type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel" | "compaction";
|
|
@@ -197,6 +205,13 @@ type McpListingIncompleteKeys = keyof NonNullable<McpReconnectResult["listingInc
|
|
|
197
205
|
type McpListingIncompleteProjected = "reason" | "pages" | "budgetMs" | "error";
|
|
198
206
|
type _GuardMcpListingIncomplete = AssertAllKeysHandled<Exclude<McpListingIncompleteKeys, McpListingIncompleteProjected>>;
|
|
199
207
|
type _GuardMcpListingIncompleteReverse = AssertAllKeysHandled<Exclude<McpListingIncompleteProjected, McpListingIncompleteKeys>>;
|
|
208
|
+
type LspOptionsWired = "env" | "log";
|
|
209
|
+
type LspOptionsUnwired = "onNotice" | "servers" | "spawn" | "readText" | "resolveRoot" | "warmup" | "maxSessions" | "idleTtlMs" | "diagnostics";
|
|
210
|
+
type _GuardLspOptions = AssertAllKeysHandled<Exclude<keyof NodeLspManagerOptions, LspOptionsWired | LspOptionsUnwired>>;
|
|
211
|
+
type _GuardLspOptionsReverse = AssertAllKeysHandled<Exclude<LspOptionsWired | LspOptionsUnwired, keyof NodeLspManagerOptions>>;
|
|
212
|
+
type _NoDoubleBookLspOptions = AssertAllKeysHandled<Extract<LspOptionsWired, LspOptionsUnwired>>;
|
|
213
|
+
type RunnerDepsDroppedPendingS724 = "fileHistoryCapture";
|
|
214
|
+
type _GuardFacelessRunnerDepsReverse = AssertAllKeysHandled<Exclude<RunnerDepsDroppedPendingS724, keyof RunnerDeps>>;
|
|
200
215
|
export type CoreKeysetGuards = [
|
|
201
216
|
_GuardNotification,
|
|
202
217
|
_GuardNotificationReverse,
|
|
@@ -259,6 +274,9 @@ export type CoreKeysetGuards = [
|
|
|
259
274
|
_GuardPermissionTraceNonEmpty,
|
|
260
275
|
_GuardInboxSummary,
|
|
261
276
|
_GuardInboxSummaryReverse,
|
|
277
|
+
_GuardParkArmQueue,
|
|
278
|
+
_NoDoubleBookParkArmQueue,
|
|
279
|
+
_GuardParkArmQueueReverse,
|
|
262
280
|
_GuardTaskSpec,
|
|
263
281
|
_GuardTaskSpecReverse,
|
|
264
282
|
_GuardTaskResult,
|
|
@@ -294,7 +312,11 @@ export type CoreKeysetGuards = [
|
|
|
294
312
|
_GuardMcpListingIncomplete,
|
|
295
313
|
_GuardMcpListingIncompleteReverse,
|
|
296
314
|
_GuardWiringMcpLiveness,
|
|
297
|
-
_GuardWiringMcpLivenessReverse
|
|
315
|
+
_GuardWiringMcpLivenessReverse,
|
|
316
|
+
_GuardLspOptions,
|
|
317
|
+
_NoDoubleBookLspOptions,
|
|
318
|
+
_GuardLspOptionsReverse,
|
|
319
|
+
_GuardFacelessRunnerDepsReverse
|
|
298
320
|
];
|
|
299
321
|
export {};
|
|
300
322
|
//# sourceMappingURL=core-keyset-guard.d.ts.map
|
|
@@ -16,8 +16,34 @@
|
|
|
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"];
|
|
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", "instructions.source_changed"];
|
|
20
20
|
export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
|
|
21
|
+
/**
|
|
22
|
+
* S-690 —— **本仓自铸**的通告码与它们的受众(码 / 铸点 / 受众三者的属主都是本仓)。
|
|
23
|
+
*
|
|
24
|
+
* 为什么可以有这一族:core 的 `EngineNotice.code` 刻意是开放 `string`,顶注逐字「a host forwarding its own notices
|
|
25
|
+
* through the same sink is a supported shape」—— 部署经同一只 `onNotice` 转发体投自己的通告,白名单 / 归属 / 消毒 /
|
|
26
|
+
* 隔离四件判据一个不绕。为什么受众写在这里:core 的 `NOTICE_AUDIENCE` 只收录 core 自己铸的码,对本仓的码
|
|
27
|
+
* `noticeAudienceOf` 只会读保守默认 `"operator"` —— 那是「不认识」,不是判决。顶注的「本仓不许自造第二份受众语义」
|
|
28
|
+
* 说的是 **core 的码**;本表只收 core 码册里**没有**的码,这里是它们唯一的一份受众语义。
|
|
29
|
+
*
|
|
30
|
+
* 🔴 机器门(`test/engine-notice-wire.test.ts` 的 server 自铸码属主门):① 本表的码与 core 运行期码册 `ENGINE_NOTICE_CODES`
|
|
31
|
+
* **零交集**(core 哪天铸了同名码 ⇒ 属主变成两个,当场红:销本表的行、改走 core 的受众与工厂);② 判 `"user"` 的码必须
|
|
32
|
+
* 入白名单;③ 白名单里的码,受众一律由「本表 ∪ core 注册表」给,判出来必须是 `"user"`。
|
|
33
|
+
*/
|
|
34
|
+
export declare const SERVER_NOTICE_AUDIENCE: {
|
|
35
|
+
readonly "instructions.source_changed": "user";
|
|
36
|
+
/**
|
|
37
|
+
* O2(7.101.0 合并复审)—— boot 期探测到 ≤7.30.x 引擎留下的锁住的记忆层(唯一铸点 `src/memory-layer-legacy-lock.ts`,
|
|
38
|
+
* 调用点 = `boot/stores.ts` 的 file 记忆引擎装配段)。受众 **operator**:恢复动词是运维在数据根上执行一次 `chmod -R u+w`,
|
|
39
|
+
* 而且铸在 boot 期、没有任何会话 ⇒ 不上 wire、不进白名单,只走转发体的日志终点(`engine_notice` warn)。
|
|
40
|
+
* `detail: { layers, remedy }` —— 层名(挂载面的直接子目录名)与那一句补救;条目名 / 内容不进。
|
|
41
|
+
*/
|
|
42
|
+
readonly "memory.layer_locked_legacy": "operator";
|
|
43
|
+
};
|
|
44
|
+
export type ServerNoticeCode = keyof typeof SERVER_NOTICE_AUDIENCE;
|
|
45
|
+
/** 该码是否本仓自铸(属主判据单点:门与投递面都读这一个)。 */
|
|
46
|
+
export declare function isServerNoticeCode(code: string): code is ServerNoticeCode;
|
|
21
47
|
/** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
|
|
22
48
|
export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
|
|
23
49
|
/**
|
|
@@ -23,7 +23,15 @@ export const ENGINE_NOTICE_WIRE_CODES = [
|
|
|
23
23
|
"mcp.injection_dropped",
|
|
24
24
|
"delegation.ask_unresolvable",
|
|
25
25
|
"task.interrupt_unconsumed",
|
|
26
|
+
"instructions.source_changed",
|
|
26
27
|
];
|
|
28
|
+
export const SERVER_NOTICE_AUDIENCE = {
|
|
29
|
+
"instructions.source_changed": "user",
|
|
30
|
+
"memory.layer_locked_legacy": "operator",
|
|
31
|
+
};
|
|
32
|
+
export function isServerNoticeCode(code) {
|
|
33
|
+
return Object.hasOwn(SERVER_NOTICE_AUDIENCE, code);
|
|
34
|
+
}
|
|
27
35
|
const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
|
|
28
36
|
export function isEngineNoticeWireCode(code) {
|
|
29
37
|
return WIRE_CODES.has(code);
|