@sema-agent/server 7.99.0 → 7.101.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 +35 -1
- package/README.md +12 -1
- package/README.zh-CN.md +9 -1
- package/USAGE.md +150 -56
- package/dist/approval-ask-machine.d.ts +10 -0
- package/dist/approval-ask-machine.js +3 -0
- package/dist/approval-card.d.ts +86 -1
- package/dist/approval-card.js +42 -8
- 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/approval.d.ts +44 -20
- package/dist/approval.js +14 -6
- package/dist/boot/config-center.d.ts +16 -8
- package/dist/boot/config-center.js +114 -90
- package/dist/boot/execution-env.d.ts +1 -2
- package/dist/boot/execution-env.js +21 -3
- package/dist/boot/leader.js +12 -9
- package/dist/boot/memory-consolidation.d.ts +38 -0
- package/dist/boot/memory-consolidation.js +35 -0
- package/dist/boot/parked-revive-gate.js +1 -0
- package/dist/boot/reapers.js +1 -0
- package/dist/boot/resolve-spec.d.ts +0 -1
- package/dist/boot/resolve-spec.js +28 -10
- package/dist/boot/runner-deps.d.ts +2 -2
- package/dist/boot/runner-deps.js +9 -3
- package/dist/boot/stage-02-registry-pull.js +9 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +3 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -3
- package/dist/boot/stage-07-capability-layer.js +6 -6
- package/dist/boot/stage-08-reapers.d.ts +3 -4
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -3
- package/dist/boot/stage-10-http-server.d.ts +2 -3
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/boot/stores.js +4 -0
- package/dist/capabilities/center-plugins.d.ts +14 -2
- package/dist/capabilities/center-plugins.js +13 -5
- package/dist/capabilities/hands-lane.js +2 -1
- package/dist/capabilities/scenarios.js +18 -5
- package/dist/capabilities/tool-defer.d.ts +2 -2
- package/dist/config-catalog.d.ts +2 -2
- package/dist/config-catalog.js +16 -15
- package/dist/config-center/apply-effective.d.ts +59 -39
- package/dist/config-center/apply-effective.js +231 -120
- package/dist/config-center/apply-ledger.d.ts +45 -16
- package/dist/config-center/apply-ledger.js +28 -18
- package/dist/config-center/facade.d.ts +12 -6
- package/dist/config-center/facade.js +2 -2
- package/dist/config-center/hot-keys-registry.d.ts +11 -4
- package/dist/config-center/hot-keys-registry.js +11 -7
- 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/restart-signal.d.ts +21 -14
- package/dist/config-center/restart-signal.js +16 -22
- package/dist/config-center/types.d.ts +9 -3
- 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 +57 -13
- package/dist/config.js +87 -22
- package/dist/cross-session-settings.js +1 -1
- 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-client.d.ts +2 -1
- package/dist/hooks/hook-llm.js +13 -4
- package/dist/host-lsp-manager.d.ts +41 -0
- package/dist/host-lsp-manager.js +15 -0
- package/dist/http/active-run-conflict.d.ts +2 -12
- package/dist/http/active-run-conflict.js +1 -6
- package/dist/http/admission.js +30 -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/resume-legs.d.ts +1 -1
- package/dist/http/resume-legs.js +14 -10
- package/dist/http/route-ctx.d.ts +2 -2
- package/dist/http/routes/approvals-assistant.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +3 -2
- 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 -0
- package/dist/http/routes/memory-origin.js +3 -0
- package/dist/http/routes/sessions.js +7 -0
- package/dist/http/routes/side-query.js +10 -4
- package/dist/http/routes/tasks.js +7 -6
- package/dist/http/server.d.ts +6 -5
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/instruction-source-notice.d.ts +97 -0
- package/dist/instruction-source-notice.js +78 -0
- package/dist/leader/wire.d.ts +20 -12
- package/dist/leader/wire.js +7 -6
- package/dist/memory-layer-legacy-lock.d.ts +14 -0
- package/dist/memory-layer-legacy-lock.js +74 -0
- 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/corrupt-read-seat.d.ts +21 -4
- package/dist/observability/corrupt-read-seat.js +31 -0
- package/dist/observability/fail-open.d.ts +16 -4
- package/dist/observability/fail-open.js +16 -4
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +27 -3
- package/dist/observability/secret-env-scrub.js +22 -6
- package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
- package/dist/orchestration/workflow-completion-inbox.js +8 -3
- package/dist/plugins/approval-ask-store-file.d.ts +20 -2
- package/dist/plugins/approval-ask-store-file.js +21 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
- package/dist/plugins/approval-ask-store-memory.js +39 -13
- package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
- package/dist/plugins/approval-ask-store-sql.js +27 -3
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
- package/dist/plugins/checkpoint-store-sql.js +19 -2
- package/dist/plugins/local-checkpoint-store.js +2 -0
- package/dist/plugins/local-session-store.d.ts +20 -25
- package/dist/plugins/local-session-store.js +111 -64
- package/dist/plugins/mailbox-store-sql.d.ts +7 -5
- package/dist/plugins/mailbox-store-sql.js +6 -4
- package/dist/plugins/permission-rule-store-file.js +3 -2
- 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/sql-json-column.d.ts +16 -3
- package/dist/plugins/sql-json-column.js +15 -4
- 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 +44 -23
- package/dist/plugins/web-search.js +161 -50
- 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 +119 -18
- package/dist/runs.js +6 -5
- package/dist/runtime-governance.d.ts +50 -3
- package/dist/runtime-governance.js +5 -0
- package/dist/sealed-key.d.ts +5 -2
- package/dist/server-secret-env.d.ts +26 -2
- package/dist/server-secret-env.js +9 -0
- package/dist/task-settings.d.ts +1 -1
- package/dist/tool-approval.d.ts +150 -28
- package/dist/tool-approval.js +150 -54
- package/dist/trace/core-keyset-guard.d.ts +30 -8
- package/dist/trace/engine-notice-wire.d.ts +27 -1
- package/dist/trace/engine-notice-wire.js +8 -0
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +22 -5
- package/dist/trace/projection-drop.d.ts +2 -0
- package/dist/trace/projection-drop.js +6 -0
- package/dist/trace/sema-provenance.d.ts +4 -1
- package/dist/trace/sema-provenance.js +1 -1
- package/dist/trace/task-notification-facets.d.ts +65 -0
- package/dist/trace/task-notification-facets.js +31 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +3 -3
- package/package.json +3 -3
package/dist/model-select.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ModelEntryRefusal } from "./model-entry-refusal.js";
|
|
1
2
|
/**
|
|
2
3
|
* Resolve the model ref for a task spec from the request (CLI `/model` picker + `run --model`).
|
|
3
4
|
*
|
|
@@ -27,12 +28,53 @@
|
|
|
27
28
|
* `resolveModel("default", catalog)` 解它)。**单点**:读面要说「这台部署的主模型是谁」时读同一个词
|
|
28
29
|
* (`auto-mode-face.ts` 的席解析器入参),两处各写一个 `"default"` 字面串正是漂移的成因。 */
|
|
29
30
|
export declare const DEFAULT_MODEL_REF = "default";
|
|
30
|
-
export declare function resolveTaskModel(bodyModel: unknown, objective: string, catalog: Record<string, unknown>, allowlist?: readonly string[]): {
|
|
31
|
+
export declare function resolveTaskModel(bodyModel: unknown, objective: string, catalog: Record<string, unknown>, allowlist?: readonly string[], refusals?: Readonly<Record<string, ModelEntryRefusal>>): {
|
|
31
32
|
model: string;
|
|
32
33
|
cleanedObjective: string;
|
|
33
34
|
unknownExplicit?: string;
|
|
34
35
|
notAllowed?: ModelNotAllowed;
|
|
36
|
+
unavailable?: ModelUnavailable;
|
|
35
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* S-704 判别式拒因:这次**用户选择**点中的是一条已发布、但本部署不可用的目录条目(`config.modelRefusals`)。
|
|
40
|
+
* 与 {@link ModelNotAllowed} 同一种处置口:本函数不 throw,调用点按 leg 分流(fresh 400 `request.model_unavailable` /
|
|
41
|
+
* resume 留声降级)。拒因句由 `model-entry-refusal.ts` 的 `modelUnavailableMessage` 铸(一处)。
|
|
42
|
+
*/
|
|
43
|
+
export interface ModelUnavailable {
|
|
44
|
+
/** 调用方发来的原始 ref(name / id / `@name`),已做过日志安全的 repr 归一。 */
|
|
45
|
+
requested: string;
|
|
46
|
+
/** 命中的不可用条目的 catalog name。 */
|
|
47
|
+
name: string;
|
|
48
|
+
refusal: ModelEntryRefusal;
|
|
49
|
+
/** 走的是哪条用户通道:`explicit`=body.model/settings.model,`mention`=objective 内联 `@name`。 */
|
|
50
|
+
source: "explicit" | "mention";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* S-704 —— **用户可选目录**:服务的目录经 core `expandTiers` 增广(档位词 / CC 别名 / `best`),再把**不可用条目的名字**
|
|
54
|
+
* 从增广出来的别名键里**让出**。全部用户**显式**选择通道(body.model / `@name` / settings.model / compactionModel /
|
|
55
|
+
* agents[].model / side-query 的 body.model / 名单判定 / `/v1/models` 的可点性)都在这一只上解析 —— 一个优先序,不各写一份;
|
|
56
|
+
* 与之配套的另一半是 {@link matchCatalogModel} 的「保留名不走 id 回退」。
|
|
57
|
+
* ⚠️ 射程只到显式选择:引擎自己的隐式选路(角色的档位缺省、档位梯、workflow 名单在 core 里的解析)用的是 core 的增广目录,
|
|
58
|
+
* 本函数**不**进那里(codex 验修轮 V4:让出名字会改变只带 `modelRole` 的隐式选路)。不可用条目名撞上别名词时引擎侧的改绑
|
|
59
|
+
* (运维配置里点名它的角色绑定 / workflow 名单)自 S-712 起由不变量 `refused-names-not-minted`(`model-entry-refusal.ts`)在
|
|
60
|
+
* 源头收口:增广会把被拒名铸到别的条目上的候选,模型面整组被拒、进不了引擎 —— 于是本函数的「让出」在已采纳的配置上恒是
|
|
61
|
+
* 空操作,保留作 HTTP 选择面的第二道(绕过 stage 的写者若造出这种表,选择面仍不改绑)。
|
|
62
|
+
*
|
|
63
|
+
* 为什么要让出(codex r1 [high],真复现):一条已发布的条目占着它的名字;它可用时,core 的别名展开**让它**(`shadowed`,
|
|
64
|
+
* 同名别名不生成)。它因不可用被摘出目录之后,展开会把同名的别名(条目恰好叫 `sonnet` / `pro` …)重新指到另一只模型 ——
|
|
65
|
+
* 一次点它的选择被静默改绑到另一只模型(及其端点、账号),一张写着它的 @-名单也随之准了那一只。让出之后,它的名字在可选
|
|
66
|
+
* 目录里不存在,选择走 {@link refusedCatalogEntry} 拿到拒因码,名单里的它解析不到任何条目(准不了任何东西)。
|
|
67
|
+
* 可用条目的真名永远不会被让出:目录里 name 唯一,不可用条目的名字只可能撞上**展开生成**的别名键。
|
|
68
|
+
*/
|
|
69
|
+
export declare function selectableCatalog<M>(models: Record<string, M>, tiers: Record<string, string> | undefined, refusals: Readonly<Record<string, ModelEntryRefusal>> | undefined): Record<string, M>;
|
|
70
|
+
/**
|
|
71
|
+
* S-704 —— ref(name 或上游 id)→ 它命中的**不可用条目**;未命中 ⇒ undefined。与 {@link matchCatalogModel} 同一种双键
|
|
72
|
+
* 索引(name 用 `Object.hasOwn`,id 扫值)。调用方**先**查可用目录、再查这里(一个 id 被可用与不可用条目共享时,可用那条胜)。
|
|
73
|
+
*/
|
|
74
|
+
export declare function refusedCatalogEntry(ref: string, refusals: Readonly<Record<string, ModelEntryRefusal>> | undefined): {
|
|
75
|
+
name: string;
|
|
76
|
+
refusal: ModelEntryRefusal;
|
|
77
|
+
} | undefined;
|
|
36
78
|
/** [ref] 判别式拒因:名单在场且这次**用户选择**落在名单外。`resolved` 已是目录 name 形(归一后)。 */
|
|
37
79
|
export interface ModelNotAllowed {
|
|
38
80
|
/** 调用方发来的原始 ref(name / 档位词 / 别名 / id / `@name`),已做过日志安全的 repr 归一。 */
|
|
@@ -64,7 +106,7 @@ export interface ModelNotAllowed {
|
|
|
64
106
|
* ⚠️ 射程:本门是**提交时**门。core 运行时路由(degrade 降级链 / cascade 梯 / roles 解析)按 operator
|
|
65
107
|
* 配置自己走,**不经本门**——那不是一次用户选择,如实声明,不假装是运行时执法。
|
|
66
108
|
*/
|
|
67
|
-
export declare function isModelAllowlisted(ref: string, catalog: Record<string, unknown>, allowlist: readonly string[] | undefined): boolean;
|
|
109
|
+
export declare function isModelAllowlisted(ref: string, catalog: Record<string, unknown>, allowlist: readonly string[] | undefined, refusals?: Readonly<Record<string, ModelEntryRefusal>>): boolean;
|
|
68
110
|
/**
|
|
69
111
|
* [ref] —— ref → **终端 catalog name**(拒因/日志的可读形)。
|
|
70
112
|
*
|
|
@@ -73,15 +115,18 @@ export declare function isModelAllowlisted(ref: string, catalog: Record<string,
|
|
|
73
115
|
* 取条目自己的 `Model.name`(applyEffective 建目录时 `models[m.name].name === m.name`,env lane 同)
|
|
74
116
|
* 才是终端名;拿不到时如实退回匹配键(诚实缺席,不猜)。
|
|
75
117
|
*/
|
|
76
|
-
export declare function terminalCatalogName(ref: string, catalog: Record<string, unknown
|
|
118
|
+
export declare function terminalCatalogName(ref: string, catalog: Record<string, unknown>, refusals?: Readonly<Record<string, ModelEntryRefusal>>): string | undefined;
|
|
77
119
|
/**
|
|
78
120
|
* [ref]② name+id 双键目录索引:返回 ref 命中的目录 **name 键**(spec.model 的规范形),未知 → undefined。
|
|
79
121
|
* - name 命中用 Object.hasOwn(🔴 不用真值索引:Object.prototype 继承键 "constructor"/"__proto__" 等在裸
|
|
80
122
|
* 索引下是真值,会跳过 allow-list——沿袭原实现的纪律)。
|
|
81
123
|
* - id 命中扫目录值(`Model.id`),跳过 "default" 别名键(它与其指向的真身共享 id,解析到别名会让 /model
|
|
82
124
|
* 面板与实际路由再度撕裂);同 id 多 name 时取目录序第一个(与 /v1/models 列表序一致,确定性)。
|
|
125
|
+
* - 🔴 S-704(codex 验修轮 V1):**名字先于 id** 这条序对不可用条目同样成立 —— `refusals` 里的名字是一条已发布
|
|
126
|
+
* 条目的名字,它不在可用目录里时**不许**被 id 回退解析成另一条可用条目(v7.100.0 那条条目在目录里,name 命中先返回
|
|
127
|
+
* 它自己)。命中保留名 ⇒ undefined,调用方接着问 {@link refusedCatalogEntry} 拿拒因码。
|
|
83
128
|
*/
|
|
84
|
-
export declare function matchCatalogModel(ref: string, catalog: Record<string, unknown
|
|
129
|
+
export declare function matchCatalogModel(ref: string, catalog: Record<string, unknown>, refusals?: Readonly<Record<string, ModelEntryRefusal>>): string | undefined;
|
|
85
130
|
/**
|
|
86
131
|
* vision precheck: a catalog model can read images iff its `input` modality list
|
|
87
132
|
* includes `"image"` (the SAME test as the vision capability on GET /v1/models — `server.ts`). FALSE-SAFE on
|
package/dist/model-select.js
CHANGED
|
@@ -1,31 +1,60 @@
|
|
|
1
|
-
import { parseModelMention } from "@sema-agent/core";
|
|
1
|
+
import { expandTiers, parseModelMention } from "@sema-agent/core";
|
|
2
2
|
export const DEFAULT_MODEL_REF = "default";
|
|
3
|
-
export function resolveTaskModel(bodyModel, objective, catalog, allowlist) {
|
|
4
|
-
const mention = parseModelMention(objective, Object.keys(catalog));
|
|
5
|
-
const
|
|
3
|
+
export function resolveTaskModel(bodyModel, objective, catalog, allowlist, refusals) {
|
|
4
|
+
const mention = parseModelMention(objective, [...Object.keys(refusals ?? {}), ...Object.keys(catalog)]);
|
|
5
|
+
const mentionUsable = mention.model !== undefined && Object.hasOwn(catalog, mention.model) ? mention.model : undefined;
|
|
6
|
+
const explicit = typeof bodyModel === "string" ? matchCatalogModel(bodyModel, catalog, refusals) : undefined;
|
|
6
7
|
const present = bodyModel !== undefined && bodyModel !== null;
|
|
7
|
-
const unknown = present && explicit === undefined;
|
|
8
8
|
const repr = typeof bodyModel !== "string" ? `<non-string:${typeof bodyModel}>` : bodyModel === "" ? "<empty-string>" : bodyModel;
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
? { requested:
|
|
9
|
+
const refusedExplicit = present && explicit === undefined && typeof bodyModel === "string" ? refusedCatalogEntry(bodyModel, refusals) : undefined;
|
|
10
|
+
const refusedMention = explicit === undefined && refusedExplicit === undefined && mention.model !== undefined && mentionUsable === undefined ? refusedCatalogEntry(mention.model, refusals) : undefined;
|
|
11
|
+
const unavailable = refusedExplicit !== undefined
|
|
12
|
+
? { requested: repr, name: refusedExplicit.name, refusal: refusedExplicit.refusal, source: "explicit" }
|
|
13
|
+
: refusedMention !== undefined
|
|
14
|
+
? { requested: mention.model, name: refusedMention.name, refusal: refusedMention.refusal, source: "mention" }
|
|
15
|
+
: undefined;
|
|
16
|
+
const unknown = present && explicit === undefined && refusedExplicit === undefined;
|
|
17
|
+
const model = explicit ?? mentionUsable ?? DEFAULT_MODEL_REF;
|
|
18
|
+
const picked = explicit !== undefined ? { ref: explicit, source: "explicit", requested: repr } : mentionUsable !== undefined ? { ref: mentionUsable, source: "mention", requested: mentionUsable } : undefined;
|
|
19
|
+
const notAllowed = picked !== undefined && !isModelAllowlisted(picked.ref, catalog, allowlist, refusals)
|
|
20
|
+
? { requested: picked.requested, resolved: terminalCatalogName(picked.ref, catalog, refusals) ?? picked.ref, source: picked.source }
|
|
13
21
|
: undefined;
|
|
14
22
|
return {
|
|
15
23
|
model,
|
|
16
24
|
cleanedObjective: mention.cleanedText || objective,
|
|
17
25
|
...(unknown ? { unknownExplicit: repr } : {}),
|
|
18
26
|
...(notAllowed ? { notAllowed } : {}),
|
|
27
|
+
...(unavailable ? { unavailable } : {}),
|
|
19
28
|
};
|
|
20
29
|
}
|
|
21
|
-
export function
|
|
30
|
+
export function selectableCatalog(models, tiers, refusals) {
|
|
31
|
+
const expanded = (expandTiers(models, tiers ?? {}) ?? models);
|
|
32
|
+
const reserved = Object.keys(refusals ?? {}).filter((n) => Object.hasOwn(expanded, n) && !Object.hasOwn(models, n));
|
|
33
|
+
if (reserved.length === 0)
|
|
34
|
+
return expanded;
|
|
35
|
+
const out = { ...expanded };
|
|
36
|
+
for (const n of reserved)
|
|
37
|
+
delete out[n];
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
export function refusedCatalogEntry(ref, refusals) {
|
|
41
|
+
if (refusals === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
if (Object.hasOwn(refusals, ref))
|
|
44
|
+
return { name: ref, refusal: refusals[ref] };
|
|
45
|
+
for (const [name, r] of Object.entries(refusals))
|
|
46
|
+
if (r.id === ref)
|
|
47
|
+
return { name, refusal: r };
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
export function isModelAllowlisted(ref, catalog, allowlist, refusals) {
|
|
22
51
|
if (allowlist === undefined || allowlist.length === 0)
|
|
23
52
|
return true;
|
|
24
|
-
const entry = catalogEntryOf(ref, catalog);
|
|
53
|
+
const entry = catalogEntryOf(ref, catalog, refusals);
|
|
25
54
|
if (entry === undefined)
|
|
26
55
|
return true;
|
|
27
56
|
for (const name of allowlist) {
|
|
28
|
-
const allowed = catalogEntryOf(name, catalog);
|
|
57
|
+
const allowed = catalogEntryOf(name, catalog, refusals);
|
|
29
58
|
if (allowed === undefined)
|
|
30
59
|
continue;
|
|
31
60
|
if (allowed === entry)
|
|
@@ -33,20 +62,22 @@ export function isModelAllowlisted(ref, catalog, allowlist) {
|
|
|
33
62
|
}
|
|
34
63
|
return false;
|
|
35
64
|
}
|
|
36
|
-
function catalogEntryOf(ref, catalog) {
|
|
37
|
-
const name = matchCatalogModel(ref, catalog);
|
|
65
|
+
function catalogEntryOf(ref, catalog, refusals) {
|
|
66
|
+
const name = matchCatalogModel(ref, catalog, refusals);
|
|
38
67
|
return name === undefined ? undefined : catalog[name];
|
|
39
68
|
}
|
|
40
|
-
export function terminalCatalogName(ref, catalog) {
|
|
41
|
-
const key = matchCatalogModel(ref, catalog);
|
|
69
|
+
export function terminalCatalogName(ref, catalog, refusals) {
|
|
70
|
+
const key = matchCatalogModel(ref, catalog, refusals);
|
|
42
71
|
if (key === undefined)
|
|
43
72
|
return undefined;
|
|
44
73
|
const name = catalog[key]?.name;
|
|
45
74
|
return typeof name === "string" && name.length > 0 ? name : key;
|
|
46
75
|
}
|
|
47
|
-
export function matchCatalogModel(ref, catalog) {
|
|
76
|
+
export function matchCatalogModel(ref, catalog, refusals) {
|
|
48
77
|
if (Object.hasOwn(catalog, ref))
|
|
49
78
|
return ref;
|
|
79
|
+
if (refusals !== undefined && Object.hasOwn(refusals, ref))
|
|
80
|
+
return undefined;
|
|
50
81
|
for (const [name, m] of Object.entries(catalog)) {
|
|
51
82
|
if (name === "default")
|
|
52
83
|
continue;
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
*
|
|
19
19
|
* 载荷形不是本仓铸的:`FileStorageCorruptReadInfo` 是 **core 的导出**,并且是 core 亲口说的**最宽**那一只
|
|
20
20
|
* ——"the widest shape any store this backend constructs can deliver, so ONE sink reaches all of them…
|
|
21
|
-
* the two-field store seats are satisfied by it"
|
|
22
|
-
*
|
|
23
|
-
* (`{path,reason}`
|
|
21
|
+
* the two-field store seats are satisfied by it"。于是本属主就按那只最宽形收参 —— 窄座的载荷都可赋给它
|
|
22
|
+
* ⇒ 吃最宽形的函数可赋给窄座。**本仓因此零镜像类型**。
|
|
23
|
+
* ⚠️ core 7.30.0(#1041 三态,S-616 提货)起 `CorruptReadInfo` 不再是 `{path, reason}` 两键形,而是按 `kind`
|
|
24
|
+
* 分三级的 union(`record` / `replay` / `file`,`path` + `reason` 每级都在);逐级读法见 {@link corruptReadLevelFields}。
|
|
24
25
|
*
|
|
25
26
|
* ## 刻意不做的两件事
|
|
26
27
|
* · **不在这里下统一结论**:后果按脸各不相同(少了会话规则这一层 / 列表静默少行 / rewind 边界读成缺席 /
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
* 闭包,天然满足;但**不要**把某个对象的裸方法引用直接塞进座 —— 读 `this` 会抛,而那个抛会被 core 的
|
|
36
37
|
* guard 吞掉,于是披露**静默丢失**(比没接座更难查)。
|
|
37
38
|
*/
|
|
38
|
-
import type { FileStorageCorruptReadInfo } from "@sema-agent/core";
|
|
39
|
+
import type { FileStorageCorruptReadInfo, readJsonlRecords } from "@sema-agent/core";
|
|
39
40
|
import type { Logger } from "./logger.js";
|
|
40
41
|
/**
|
|
41
42
|
* 铸一只腐读披露座。
|
|
@@ -53,4 +54,20 @@ export declare function createCorruptReadSeat(warn: Logger["warn"], event: strin
|
|
|
53
54
|
* 这一句从前在两处各抄一份、只差一个词;现在词从参数来,句子只有一份。
|
|
54
55
|
*/
|
|
55
56
|
export declare function buildDurableAbsenceNote(thirdFace: string): string;
|
|
57
|
+
/** core `readJsonlRecords` 的坏行回调入参(core 不从包根导出这两级的型名 ⇒ 从那只函数的签名派生,不手抄)。 */
|
|
58
|
+
export type JsonlCorruptInfo = Parameters<NonNullable<Parameters<typeof readJsonlRecords>[1]>>[0];
|
|
59
|
+
/** 其中「一条被跳过的记录」那一级。 */
|
|
60
|
+
export type JsonlSkippedRecord = Extract<JsonlCorruptInfo, {
|
|
61
|
+
kind: "record";
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* S-616(core 7.30.0 #1041 提货)—— 本仓直接调 `readJsonlRecords` 的店(approval-ask 日志、permission-rule 两本日志)
|
|
65
|
+
* 的坏行回调**唯一**形。core 自这一版起对一次坏读报两级:每条被跳过的记录一发 `record`,这次重放的计数一发
|
|
66
|
+
* `replay`(`dropped` = 同一批 `record` 的条数)。这些店按**记录**计数、按记录告警 / 跳闸 —— 手写回调不分级就把同一条
|
|
67
|
+
* 坏行记两次(k3s8 实测:approval-ask 的「1 条解析不出」读成 2 条,permission-rule 的 onError 一行变两行)。
|
|
68
|
+
* 规则一句:**只按记录计,计数级是同一事实的汇总,不再计一次**;穷举 switch,core 加第三级 ⇒ 编译红。
|
|
69
|
+
* 与 {@link createCorruptReadSeat} 的分工:那只是 core 自带 File 店的**披露座**(两级都转发,日志按 `kind` 分桶);
|
|
70
|
+
* 本函数是本仓自有店**消费**坏行事实的口子(计数 / 跳闸只要记录级)。
|
|
71
|
+
*/
|
|
72
|
+
export declare function createSkippedRecordSink(onRecord: (info: JsonlSkippedRecord) => void): (info: JsonlCorruptInfo) => void;
|
|
56
73
|
//# sourceMappingURL=corrupt-read-seat.d.ts.map
|
|
@@ -1,16 +1,47 @@
|
|
|
1
1
|
export function createCorruptReadSeat(warn, event, note) {
|
|
2
2
|
return (info) => warn(event, {
|
|
3
|
+
kind: info.kind,
|
|
3
4
|
path: info.path,
|
|
4
5
|
reason: info.reason,
|
|
6
|
+
...corruptReadLevelFields(info),
|
|
5
7
|
...(info.sessionId !== undefined ? { sessionId: info.sessionId } : {}),
|
|
6
8
|
...(info.principal !== undefined ? { principal: info.principal } : {}),
|
|
7
9
|
...(note !== undefined ? { note } : {}),
|
|
8
10
|
});
|
|
9
11
|
}
|
|
12
|
+
function corruptReadLevelFields(info) {
|
|
13
|
+
switch (info.kind) {
|
|
14
|
+
case "record":
|
|
15
|
+
return { line: info.line, form: info.form };
|
|
16
|
+
case "replay":
|
|
17
|
+
return { kept: info.kept, dropped: info.dropped, total: info.total };
|
|
18
|
+
case "file":
|
|
19
|
+
return {};
|
|
20
|
+
default: {
|
|
21
|
+
const unreachable = info;
|
|
22
|
+
return unreachable;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
10
26
|
export function buildDurableAbsenceNote(thirdFace) {
|
|
11
27
|
return ("a durable read was treated as ABSENT because the bytes were unreadable (never a plain ENOENT). " +
|
|
12
28
|
"Consequence depends on which store read it: session-policy = that run lost its SUBTRACTIVE session-rule " +
|
|
13
29
|
"layer (deployment policy / hooks / shell gate still applied); session repo = a listing silently omitted " +
|
|
14
30
|
`rows; ${thirdFace}. Inspect the named path before deciding.`);
|
|
15
31
|
}
|
|
32
|
+
export function createSkippedRecordSink(onRecord) {
|
|
33
|
+
return (info) => {
|
|
34
|
+
switch (info.kind) {
|
|
35
|
+
case "record":
|
|
36
|
+
onRecord(info);
|
|
37
|
+
return;
|
|
38
|
+
case "replay":
|
|
39
|
+
return;
|
|
40
|
+
default: {
|
|
41
|
+
const unreachable = info;
|
|
42
|
+
return unreachable;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
16
47
|
//# sourceMappingURL=corrupt-read-seat.js.map
|
|
@@ -39,7 +39,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
39
39
|
};
|
|
40
40
|
readonly "server.trace.projection-input-malformed": {
|
|
41
41
|
readonly cls: "F";
|
|
42
|
-
readonly note: "7.78.0(core 7.18.0 #741/#786/#789/#790,`trace/project.ts`
|
|
42
|
+
readonly note: "7.78.0(core 7.18.0 #741/#786/#789/#790,`trace/project.ts` 的五张新投影脸共用一条规则;之后逐版入族 —— NP-5 / 7.100.0 起含 `trace/task-notification-facets.ts` 的 S-616 委派终态 facet / 重供两键与 `compacted.summarizedCrossSessionMessages`,助手住叶模块 `trace/projection-drop.ts`,全表见普查表第 73 行):`tool_progress` / `tool_disclosure` 两帧的必需位读不出 ⇒ **整帧不发**;`wiring_manifest.hooks[]` / `wiring_manifest.lsp` / `context_usage.sections[]` 三段的容器形或成员形读不出 ⇒ 那一行/那一段被丢、全丢光即键缺席。规则一句话:**在场但读不出 ⇒ 计一次并丢弃;缺席从不计数**(缺席是这几张脸上的一等状态,契约把它定义成一个正面事实 —— `hooks` 缺席 = 「本条腿没有自己的钩子记录」、`sections` 缺席 = 「这条腿没装配出段 IR」、`lsp` 缺席 = 「本部署没接 LSP」)。放行的最坏后果=这条腿的消费端少一张观测/渲染面(壳退回上一份已知状态或通用卡片),**不影响任何裁决**——门在引擎里早已判完。反方向(半张脸照发)严格更坏:消费端会把读不出的行当成那个正面事实。计数是**唯一**能把「坏世代 / 第三方 producer 一直在发坏载荷」与那个正面事实分开的位。";
|
|
43
43
|
};
|
|
44
44
|
readonly "server.task-mcp.source-label-dropped": {
|
|
45
45
|
readonly cls: "F";
|
|
@@ -121,6 +121,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
121
121
|
readonly cls: "F";
|
|
122
122
|
readonly note: "运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint.risk_descriptor` 这一格**不是合法 JSON 文本**(手改过的行 / 回滚残留 / 列语义漂;与同一 `rows.map` 里的 `rule_suggestions` 是同族坏法,两方言都是 TEXT 列)。放行的最坏后果 = **这一行**的分诊 descriptor 落 null:severity 折 0 排到有 descriptor 的行之后、`shadowedRule` 无可脱敏、读面派生的 `governanceForced` 徽章不亮 —— 三者都是**展示/分诊**,不是执法判据(门早已 park,真按 `shellGateDoctrine` 判的 `active-run-conflict.ts` 读的是 checkpoint blob 的 `get()`,不经本读面),且 `governanceForced` 的成文语义本就是「缺席 = 没有治理来源的**证据**」。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 cell 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),连同其余所有行一起消失。故 F 类;tag 与候选那格**分开计**,并计会让「哪一格在坏」在遥测里读不出来。";
|
|
123
123
|
};
|
|
124
|
+
readonly "server.approvals.park-row-blob-unreadable": {
|
|
125
|
+
readonly cls: "F";
|
|
126
|
+
readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowFactsCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。";
|
|
127
|
+
};
|
|
124
128
|
readonly "server.sql-json.checkpoint.pending-steer-queue-lossy": {
|
|
125
129
|
readonly cls: "F";
|
|
126
130
|
readonly note: "S-550(`plugins/checkpoint-store-sql.ts` 的 `get()`,core 5.14.0 #147 的耐久转向队列):一条挂起 run 的 `checkpoint.pending_steer_queue` 这一格**没能完整读出来**(不是合法 JSON 文本 / 不是数组 / 某一条目的四个必备键不合形)⇒ 读不出的那部分**丢掉**,run 照常 resume。放行的最坏后果 = 一条已经 park 的操作员转向指令**没有**在这次 resume 上注入(人以为说过的话没送到);不放行的代价是这条挂起的 run **永远不可 resume** —— resume 是它唯一的出路,为一格读不出的转向把整条 run 变成砖是错误的失败方向(写侧刻意相反:`setPendingSteer` 往一份读不全的队列上追加会把读不出的条目永久抹掉,那里 fail-loud)。故 F 类。留痕从一条 `logger.info` 升成登记 tag 的理由:日志行在**遥测轴**上是零信号,「这台机器的转向队列一直读不出来」与「这台机器本来就没人 steer」在指标上完全同形。计数非零 = 去按日志里的 token 指纹查那一行。";
|
|
@@ -189,6 +193,14 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
189
193
|
readonly cls: "F";
|
|
190
194
|
readonly note: "#310(codex 对抗复审 R1-[medium],验真后修):三条 run 腿把一条白名单通告写进 durable 账本时,那次 `appendEvent` **异步 reject**(store 抖动 / 连接断 / 行冲突)。结构上不能 await —— `onNotice` 是 core 的同步回调,拿一次账本写去阻塞引擎是更坏的交易 —— 所以写口只能 fire-and-forget,`route` 的同步 try 结构性观察不到这次失败 —— 「投出去了」与「真落盘了」不是同一件事。放行的最坏后果按腿分档,如实登记:**sync stream 腿**还有 live SSE 那一份(在线的人看得见,丢的是断连补看);**bg / resume 两腿账本是唯一的用户可见终点** ⇒ 那条通告对用户永久消失。仍判 F 而不是 P:通告是**披露**面不是执法面(不参与任何门/裁决),且运维面那一份(结构化日志 `engine_notice`)在分流之前就已经落定、一条不丢。此前是裸 `.catch(() => undefined)` —— 同文件 resume 腿的子代 forward 写口早在 C2/C5 批2 就因为「裸吞 = 事件从 durable log 永久消失且零信号」补过 `warnAppend`,本 tag 是同一条纪律在通告口的落实(warn 留细节 + 计数让「系统性在漏」显形)。不做重试队列是刻意的:账本里没有任何一族行做有界重试,单给通告开一条会造出第二套持久化语义。";
|
|
191
195
|
};
|
|
196
|
+
readonly "server.memory.locked-layer-probe-partial": {
|
|
197
|
+
readonly cls: "F";
|
|
198
|
+
readonly note: "O2(7.101.0 合并复审,`memory-layer-legacy-lock.ts announceLockedLegacyMemoryLayers`,boot 期锁住记忆层探测;detail 只报 `unreadable=<条数>`):挂载面或某个层里的目录**读不出**(EACCES 之类 —— 不是 ≤7.30.x 引擎的锁形,那种 0555 目录可读)⇒ 那棵子树看不全,照常起服。放行的最坏后果 = 一个锁住的层在 boot 日志里没被点名 —— 探测是**披露**面,不参与任何门 / 裁决;core 在真正失败的那一次删除上仍给出自己的 `chmod -R u+w` 补救句。不放行的代价严重不对称:为一条 warn 打死起服(混跑窗里能服务的副本)。计数让「这台部署的记忆树有读不出的目录」显形。";
|
|
199
|
+
};
|
|
200
|
+
readonly "server.instruction-source.announce-degraded": {
|
|
201
|
+
readonly cls: "F";
|
|
202
|
+
readonly note: "S-690(`instruction-source-notice.ts announceInstructionSource`,项目指令换源通告的唯一铸点;detail 的 `op` 点名哪一步):基线**读**失败或超期(会话店抖动 / 列形坏 / 2s 期限到,detail 带 `timeout=`)⇒ 本 run 不比也不写;基线**写**失败或超期 ⇒ 本 run 的换源帧照发(它对本 run 为真),下一 run 可能再说一遍同一次换源(超期后才落地的写由 CAS 挡住,盖不掉更新的基线);通告**出口**抛 ⇒ 本 run 缺这一帧。放行的最坏后果 = 一次换源没被说出来或被多说一次 —— 通告是**披露**面,不参与任何门 / 裁决,模型读到的指令本身不受影响。不放行的代价严重不对称:铸点跑在 core 的 `loadProjectMemory` 席内,抛回去 core 会把这次 prepare 的项目记忆整块丢掉(不注入),为一条通告赔上指令本身。计数让「某台部署的会话店一直读不出基线」与「这台部署从来没换过源」在遥测上分得开。";
|
|
203
|
+
};
|
|
192
204
|
readonly "server.engine-notice.wire-sink-threw": {
|
|
193
205
|
readonly cls: "F";
|
|
194
206
|
readonly note: "#310:`engine_notice` 分流器把一条白名单通告投给某条 run 腿的登记口时,那只口抛了(live 口写向已断/已撕裂的 SSE socket,或 durable 口的账本写同步抛)。放行的最坏后果 = **这一条通告的这一个终点**缺席:①日志终点在分流之前已经逐字打过(事实一条不丢);②两个终点注册成两只独立 sink ⇒ live 抛不牵连 durable(断连后仍看得见的那半保住);③同会话其余口照投。故 F 类。不放行的代价是把异常回抛给 core 的 `deliverEngineNotice` —— 它会吞掉,于是同一次失败**既没有留痕也没有第二只口**,正是本 tag 要根除的形。留痕是承重的:静默吞掉之后「wire 腿为什么总有几条通告不到」在遥测里与「core 本来就没发」同形。";
|
|
@@ -227,7 +239,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
227
239
|
};
|
|
228
240
|
readonly "server.approval.ambiguous-cas.expire": {
|
|
229
241
|
readonly cls: "P-DEBT";
|
|
230
|
-
readonly note: "S-25(设计件 2026-08-31 §3 步骤 3;DEBTS S-25 / #290)→ S-69 收窄(S-25-R1 终局,core design/384 §4.3 七律 / §4.4 键形对表,[5934]):流内 ask 的**窗到期臂**打原子终局 claim(`claimTerminal(expire)`,S-69 前 `expireAsk` CAS)被 reject(抛错 / 超墙钟,什么也证明不了 —— 律 3 K2)之后,有界退避**重发同一 claim**(6 格 ⇒ 至多 7 把,在飞帽 2 按持久 askId 记、撞帽拍不计 attempt、让路探针读同帽同界、帽满或同 askId 尚有在飞操作时探针不计 rung、真相广播同 askId 全部登记(本登记已 done 亦广播)—— codex S-69 R2-1/R3-1/R3-2/R4-1/R5-1/R6-1/R6-2/R7-1;末格睡后仍再发 —— codex R1-F1 同律;总墙钟硬上界 TERMINAL_LADDER_WALL_MS ≈10.15s —— R2-2 修正算术 + R3-3 硬封顶)**全部 reject** ⇒ 按本臂意图投影结算 = park 路由(`\"unavailable\"`;`deny` 政策下 `{allow:false,settledBy:\"timeout\"}`)并留 park 墓碑。方向 fail-closed(无 DECIDED=approve 行绝不执行工具),但行**可能**已被另一副本判成 DECIDED(approve) 而本腿没兑现 —— 人批了、工具不跑、run 再挂起要人再批一次 = 丢一次人类决议,故记债不当合法兜底。补偿:core 侧仍 pending 的 checkpoint(人再批一次就走)+ 跨副本收敛器兜底(A-075.116②:其兜底覆盖本形经确认后本 tag 降 F)。**不计**的形(S-69 起命中条件收窄到「店对整条阶梯持续不可用」):claim 输 ⇒ 败方答案**携真相**(DECIDED ⇒ 按人的决议结算,零补读;PARKING/PARKED ⇒ park 路由;absent = 行不在/错 scope ⇒ K1 零退避投影),迟到回来的 claim 同样自足(迟到赢 ⇒ 副作用补做,迟到输 ⇒ 按真决议结算 —— 此前迟到回调只认 won 的那条残余窗即 S-25-R1,本批闭合);编辑在飞标记在场 ⇒ 有界让路(不重打 claim)、用尽走 park 路由(标记的事不是店的债,codex R1-F2/R2-F2;负终态与带载荷 approve 不受标记压制,R2-F1/R3-F1);阶梯里曾拿到真相而标记已放开 ⇒ 按那份真相结算。detail 带 askId 与 claim 把数。";
|
|
242
|
+
readonly note: "S-25(设计件 2026-08-31 §3 步骤 3;DEBTS S-25 / #290)→ S-69 收窄(S-25-R1 终局,core design/384 §4.3 七律 / §4.4 键形对表,[5934]):流内 ask 的**窗到期臂**打原子终局 claim(`claimTerminal(expire)`,S-69 前 `expireAsk` CAS;NP-2 / 7.100.0 起 `deny` 政策下意图是 `refuse` = 当场落 DECIDED(deny)+`settled_by=policy`,同一架阶梯、同一个 tag)被 reject(抛错 / 超墙钟,什么也证明不了 —— 律 3 K2)之后,有界退避**重发同一 claim**(6 格 ⇒ 至多 7 把,在飞帽 2 按持久 askId 记、撞帽拍不计 attempt、让路探针读同帽同界、帽满或同 askId 尚有在飞操作时探针不计 rung、真相广播同 askId 全部登记(本登记已 done 亦广播)—— codex S-69 R2-1/R3-1/R3-2/R4-1/R5-1/R6-1/R6-2/R7-1;末格睡后仍再发 —— codex R1-F1 同律;总墙钟硬上界 TERMINAL_LADDER_WALL_MS ≈10.15s —— R2-2 修正算术 + R3-3 硬封顶)**全部 reject** ⇒ 按本臂意图投影结算 = park 路由(`\"unavailable\"`;`deny` 政策下 `{allow:false,settledBy:\"timeout\"}`)并留 park 墓碑。方向 fail-closed(无 DECIDED=approve 行绝不执行工具),但行**可能**已被另一副本判成 DECIDED(approve) 而本腿没兑现 —— 人批了、工具不跑、run 再挂起要人再批一次 = 丢一次人类决议,故记债不当合法兜底。补偿:core 侧仍 pending 的 checkpoint(人再批一次就走)+ 跨副本收敛器兜底(A-075.116②:其兜底覆盖本形经确认后本 tag 降 F)。**不计**的形(S-69 起命中条件收窄到「店对整条阶梯持续不可用」):claim 输 ⇒ 败方答案**携真相**(DECIDED ⇒ 按人的决议结算,零补读;PARKING/PARKED ⇒ park 路由;absent = 行不在/错 scope ⇒ K1 零退避投影),迟到回来的 claim 同样自足(迟到赢 ⇒ 副作用补做,迟到输 ⇒ 按真决议结算 —— 此前迟到回调只认 won 的那条残余窗即 S-25-R1,本批闭合);编辑在飞标记在场 ⇒ 有界让路(不重打 claim)、用尽走 park 路由(标记的事不是店的债,codex R1-F2/R2-F2;负终态与带载荷 approve 不受标记压制,R2-F1/R3-F1);阶梯里曾拿到真相而标记已放开 ⇒ 按那份真相结算。detail 带 askId 与 claim 把数。";
|
|
231
243
|
};
|
|
232
244
|
readonly "server.approval.ambiguous-cas.cancel": {
|
|
233
245
|
readonly cls: "P-DEBT";
|
|
@@ -235,7 +247,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
235
247
|
};
|
|
236
248
|
readonly "server.approval.ambiguous-cas.unreachable": {
|
|
237
249
|
readonly cls: "P-DEBT";
|
|
238
|
-
readonly note: "S-25 同族第三员 → S-69 收窄:**emit 全灭臂**(开卡帧两族都没送到任何连接)打 `claimTerminal(expire)`(S-69 前 `expireAsk` CAS)被 reject 之后,有界重发至多 7 把 claim(在飞帽 2,墙钟上界同 expire 员)全部 reject ⇒ 按本臂意图投影结算 = `emitFailed` ⇒ `\"unavailable\"`(park 路由;`deny` 政策下 deny),不留墓碑(卡没到任何人手上,无人持有这把 approvalId)。S-25 之前这条 catch 不读行直接强改 —— 正是 round2 finding A「durable 行已批准、活终局被 emit 全灭强改成 unavailable」的 catch 同形缺口;S-25 起行读出 DECIDED ⇒ 按人的决议结算;S-69 起真相随败方答案回来、迟到答案自足,只有店对整条阶梯持续 reject 才计数。";
|
|
250
|
+
readonly note: "S-25 同族第三员 → S-69 收窄:**emit 全灭臂**(开卡帧两族都没送到任何连接)打 `claimTerminal(expire)`(S-69 前 `expireAsk` CAS;NP-2 起 `deny` 政策下是 `refuse`,同窗到期员)被 reject 之后,有界重发至多 7 把 claim(在飞帽 2,墙钟上界同 expire 员)全部 reject ⇒ 按本臂意图投影结算 = `emitFailed` ⇒ `\"unavailable\"`(park 路由;`deny` 政策下 deny),不留墓碑(卡没到任何人手上,无人持有这把 approvalId)。S-25 之前这条 catch 不读行直接强改 —— 正是 round2 finding A「durable 行已批准、活终局被 emit 全灭强改成 unavailable」的 catch 同形缺口;S-25 起行读出 DECIDED ⇒ 按人的决议结算;S-69 起真相随败方答案回来、迟到答案自足,只有店对整条阶梯持续 reject 才计数。";
|
|
239
251
|
};
|
|
240
252
|
readonly "server.approval-ask-audit.append-failed": {
|
|
241
253
|
readonly cls: "F";
|
|
@@ -271,7 +283,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
271
283
|
};
|
|
272
284
|
readonly "server.approval-card.closed-word-out-of-set": {
|
|
273
285
|
readonly cls: "F";
|
|
274
|
-
readonly note: "S-249(`approval-card.ts` 的边界窄读族;今天的唯一成员是 `readRuleOffersAbsence`):活卡帧 / `card_json` 的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40)。";
|
|
286
|
+
readonly note: "S-249(`approval-card.ts` 的边界窄读族;今天的唯一成员是 `readRuleOffersAbsence`):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。";
|
|
275
287
|
};
|
|
276
288
|
readonly "server.pg-pool.idle-client-error": {
|
|
277
289
|
readonly cls: "F";
|
|
@@ -30,7 +30,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
30
30
|
},
|
|
31
31
|
"server.trace.projection-input-malformed": {
|
|
32
32
|
cls: "F",
|
|
33
|
-
note: "7.78.0(core 7.18.0 #741/#786/#789/#790,`trace/project.ts`
|
|
33
|
+
note: "7.78.0(core 7.18.0 #741/#786/#789/#790,`trace/project.ts` 的五张新投影脸共用一条规则;之后逐版入族 —— NP-5 / 7.100.0 起含 `trace/task-notification-facets.ts` 的 S-616 委派终态 facet / 重供两键与 `compacted.summarizedCrossSessionMessages`,助手住叶模块 `trace/projection-drop.ts`,全表见普查表第 73 行):`tool_progress` / `tool_disclosure` 两帧的必需位读不出 ⇒ **整帧不发**;`wiring_manifest.hooks[]` / `wiring_manifest.lsp` / `context_usage.sections[]` 三段的容器形或成员形读不出 ⇒ 那一行/那一段被丢、全丢光即键缺席。规则一句话:**在场但读不出 ⇒ 计一次并丢弃;缺席从不计数**(缺席是这几张脸上的一等状态,契约把它定义成一个正面事实 —— `hooks` 缺席 = 「本条腿没有自己的钩子记录」、`sections` 缺席 = 「这条腿没装配出段 IR」、`lsp` 缺席 = 「本部署没接 LSP」)。放行的最坏后果=这条腿的消费端少一张观测/渲染面(壳退回上一份已知状态或通用卡片),**不影响任何裁决**——门在引擎里早已判完。反方向(半张脸照发)严格更坏:消费端会把读不出的行当成那个正面事实。计数是**唯一**能把「坏世代 / 第三方 producer 一直在发坏载荷」与那个正面事实分开的位。",
|
|
34
34
|
},
|
|
35
35
|
"server.task-mcp.source-label-dropped": {
|
|
36
36
|
cls: "F",
|
|
@@ -112,6 +112,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
112
112
|
cls: "F",
|
|
113
113
|
note: "运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint.risk_descriptor` 这一格**不是合法 JSON 文本**(手改过的行 / 回滚残留 / 列语义漂;与同一 `rows.map` 里的 `rule_suggestions` 是同族坏法,两方言都是 TEXT 列)。放行的最坏后果 = **这一行**的分诊 descriptor 落 null:severity 折 0 排到有 descriptor 的行之后、`shadowedRule` 无可脱敏、读面派生的 `governanceForced` 徽章不亮 —— 三者都是**展示/分诊**,不是执法判据(门早已 park,真按 `shellGateDoctrine` 判的 `active-run-conflict.ts` 读的是 checkpoint blob 的 `get()`,不经本读面),且 `governanceForced` 的成文语义本就是「缺席 = 没有治理来源的**证据**」。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 cell 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),连同其余所有行一起消失。故 F 类;tag 与候选那格**分开计**,并计会让「哪一格在坏」在遥测里读不出来。",
|
|
114
114
|
},
|
|
115
|
+
"server.approvals.park-row-blob-unreadable": {
|
|
116
|
+
cls: "F",
|
|
117
|
+
note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowFactsCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。",
|
|
118
|
+
},
|
|
115
119
|
"server.sql-json.checkpoint.pending-steer-queue-lossy": {
|
|
116
120
|
cls: "F",
|
|
117
121
|
note: "S-550(`plugins/checkpoint-store-sql.ts` 的 `get()`,core 5.14.0 #147 的耐久转向队列):一条挂起 run 的 `checkpoint.pending_steer_queue` 这一格**没能完整读出来**(不是合法 JSON 文本 / 不是数组 / 某一条目的四个必备键不合形)⇒ 读不出的那部分**丢掉**,run 照常 resume。放行的最坏后果 = 一条已经 park 的操作员转向指令**没有**在这次 resume 上注入(人以为说过的话没送到);不放行的代价是这条挂起的 run **永远不可 resume** —— resume 是它唯一的出路,为一格读不出的转向把整条 run 变成砖是错误的失败方向(写侧刻意相反:`setPendingSteer` 往一份读不全的队列上追加会把读不出的条目永久抹掉,那里 fail-loud)。故 F 类。留痕从一条 `logger.info` 升成登记 tag 的理由:日志行在**遥测轴**上是零信号,「这台机器的转向队列一直读不出来」与「这台机器本来就没人 steer」在指标上完全同形。计数非零 = 去按日志里的 token 指纹查那一行。",
|
|
@@ -180,6 +184,14 @@ export const FAIL_OPEN_TAGS = {
|
|
|
180
184
|
cls: "F",
|
|
181
185
|
note: "#310(codex 对抗复审 R1-[medium],验真后修):三条 run 腿把一条白名单通告写进 durable 账本时,那次 `appendEvent` **异步 reject**(store 抖动 / 连接断 / 行冲突)。结构上不能 await —— `onNotice` 是 core 的同步回调,拿一次账本写去阻塞引擎是更坏的交易 —— 所以写口只能 fire-and-forget,`route` 的同步 try 结构性观察不到这次失败 —— 「投出去了」与「真落盘了」不是同一件事。放行的最坏后果按腿分档,如实登记:**sync stream 腿**还有 live SSE 那一份(在线的人看得见,丢的是断连补看);**bg / resume 两腿账本是唯一的用户可见终点** ⇒ 那条通告对用户永久消失。仍判 F 而不是 P:通告是**披露**面不是执法面(不参与任何门/裁决),且运维面那一份(结构化日志 `engine_notice`)在分流之前就已经落定、一条不丢。此前是裸 `.catch(() => undefined)` —— 同文件 resume 腿的子代 forward 写口早在 C2/C5 批2 就因为「裸吞 = 事件从 durable log 永久消失且零信号」补过 `warnAppend`,本 tag 是同一条纪律在通告口的落实(warn 留细节 + 计数让「系统性在漏」显形)。不做重试队列是刻意的:账本里没有任何一族行做有界重试,单给通告开一条会造出第二套持久化语义。",
|
|
182
186
|
},
|
|
187
|
+
"server.memory.locked-layer-probe-partial": {
|
|
188
|
+
cls: "F",
|
|
189
|
+
note: "O2(7.101.0 合并复审,`memory-layer-legacy-lock.ts announceLockedLegacyMemoryLayers`,boot 期锁住记忆层探测;detail 只报 `unreadable=<条数>`):挂载面或某个层里的目录**读不出**(EACCES 之类 —— 不是 ≤7.30.x 引擎的锁形,那种 0555 目录可读)⇒ 那棵子树看不全,照常起服。放行的最坏后果 = 一个锁住的层在 boot 日志里没被点名 —— 探测是**披露**面,不参与任何门 / 裁决;core 在真正失败的那一次删除上仍给出自己的 `chmod -R u+w` 补救句。不放行的代价严重不对称:为一条 warn 打死起服(混跑窗里能服务的副本)。计数让「这台部署的记忆树有读不出的目录」显形。",
|
|
190
|
+
},
|
|
191
|
+
"server.instruction-source.announce-degraded": {
|
|
192
|
+
cls: "F",
|
|
193
|
+
note: "S-690(`instruction-source-notice.ts announceInstructionSource`,项目指令换源通告的唯一铸点;detail 的 `op` 点名哪一步):基线**读**失败或超期(会话店抖动 / 列形坏 / 2s 期限到,detail 带 `timeout=`)⇒ 本 run 不比也不写;基线**写**失败或超期 ⇒ 本 run 的换源帧照发(它对本 run 为真),下一 run 可能再说一遍同一次换源(超期后才落地的写由 CAS 挡住,盖不掉更新的基线);通告**出口**抛 ⇒ 本 run 缺这一帧。放行的最坏后果 = 一次换源没被说出来或被多说一次 —— 通告是**披露**面,不参与任何门 / 裁决,模型读到的指令本身不受影响。不放行的代价严重不对称:铸点跑在 core 的 `loadProjectMemory` 席内,抛回去 core 会把这次 prepare 的项目记忆整块丢掉(不注入),为一条通告赔上指令本身。计数让「某台部署的会话店一直读不出基线」与「这台部署从来没换过源」在遥测上分得开。",
|
|
194
|
+
},
|
|
183
195
|
"server.engine-notice.wire-sink-threw": {
|
|
184
196
|
cls: "F",
|
|
185
197
|
note: "#310:`engine_notice` 分流器把一条白名单通告投给某条 run 腿的登记口时,那只口抛了(live 口写向已断/已撕裂的 SSE socket,或 durable 口的账本写同步抛)。放行的最坏后果 = **这一条通告的这一个终点**缺席:①日志终点在分流之前已经逐字打过(事实一条不丢);②两个终点注册成两只独立 sink ⇒ live 抛不牵连 durable(断连后仍看得见的那半保住);③同会话其余口照投。故 F 类。不放行的代价是把异常回抛给 core 的 `deliverEngineNotice` —— 它会吞掉,于是同一次失败**既没有留痕也没有第二只口**,正是本 tag 要根除的形。留痕是承重的:静默吞掉之后「wire 腿为什么总有几条通告不到」在遥测里与「core 本来就没发」同形。",
|
|
@@ -218,7 +230,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
218
230
|
},
|
|
219
231
|
"server.approval.ambiguous-cas.expire": {
|
|
220
232
|
cls: "P-DEBT",
|
|
221
|
-
note: "S-25(设计件 2026-08-31 §3 步骤 3;DEBTS S-25 / #290)→ S-69 收窄(S-25-R1 终局,core design/384 §4.3 七律 / §4.4 键形对表,[5934]):流内 ask 的**窗到期臂**打原子终局 claim(`claimTerminal(expire)`,S-69 前 `expireAsk` CAS)被 reject(抛错 / 超墙钟,什么也证明不了 —— 律 3 K2)之后,有界退避**重发同一 claim**(6 格 ⇒ 至多 7 把,在飞帽 2 按持久 askId 记、撞帽拍不计 attempt、让路探针读同帽同界、帽满或同 askId 尚有在飞操作时探针不计 rung、真相广播同 askId 全部登记(本登记已 done 亦广播)—— codex S-69 R2-1/R3-1/R3-2/R4-1/R5-1/R6-1/R6-2/R7-1;末格睡后仍再发 —— codex R1-F1 同律;总墙钟硬上界 TERMINAL_LADDER_WALL_MS ≈10.15s —— R2-2 修正算术 + R3-3 硬封顶)**全部 reject** ⇒ 按本臂意图投影结算 = park 路由(`\"unavailable\"`;`deny` 政策下 `{allow:false,settledBy:\"timeout\"}`)并留 park 墓碑。方向 fail-closed(无 DECIDED=approve 行绝不执行工具),但行**可能**已被另一副本判成 DECIDED(approve) 而本腿没兑现 —— 人批了、工具不跑、run 再挂起要人再批一次 = 丢一次人类决议,故记债不当合法兜底。补偿:core 侧仍 pending 的 checkpoint(人再批一次就走)+ 跨副本收敛器兜底(A-075.116②:其兜底覆盖本形经确认后本 tag 降 F)。**不计**的形(S-69 起命中条件收窄到「店对整条阶梯持续不可用」):claim 输 ⇒ 败方答案**携真相**(DECIDED ⇒ 按人的决议结算,零补读;PARKING/PARKED ⇒ park 路由;absent = 行不在/错 scope ⇒ K1 零退避投影),迟到回来的 claim 同样自足(迟到赢 ⇒ 副作用补做,迟到输 ⇒ 按真决议结算 —— 此前迟到回调只认 won 的那条残余窗即 S-25-R1,本批闭合);编辑在飞标记在场 ⇒ 有界让路(不重打 claim)、用尽走 park 路由(标记的事不是店的债,codex R1-F2/R2-F2;负终态与带载荷 approve 不受标记压制,R2-F1/R3-F1);阶梯里曾拿到真相而标记已放开 ⇒ 按那份真相结算。detail 带 askId 与 claim 把数。",
|
|
233
|
+
note: "S-25(设计件 2026-08-31 §3 步骤 3;DEBTS S-25 / #290)→ S-69 收窄(S-25-R1 终局,core design/384 §4.3 七律 / §4.4 键形对表,[5934]):流内 ask 的**窗到期臂**打原子终局 claim(`claimTerminal(expire)`,S-69 前 `expireAsk` CAS;NP-2 / 7.100.0 起 `deny` 政策下意图是 `refuse` = 当场落 DECIDED(deny)+`settled_by=policy`,同一架阶梯、同一个 tag)被 reject(抛错 / 超墙钟,什么也证明不了 —— 律 3 K2)之后,有界退避**重发同一 claim**(6 格 ⇒ 至多 7 把,在飞帽 2 按持久 askId 记、撞帽拍不计 attempt、让路探针读同帽同界、帽满或同 askId 尚有在飞操作时探针不计 rung、真相广播同 askId 全部登记(本登记已 done 亦广播)—— codex S-69 R2-1/R3-1/R3-2/R4-1/R5-1/R6-1/R6-2/R7-1;末格睡后仍再发 —— codex R1-F1 同律;总墙钟硬上界 TERMINAL_LADDER_WALL_MS ≈10.15s —— R2-2 修正算术 + R3-3 硬封顶)**全部 reject** ⇒ 按本臂意图投影结算 = park 路由(`\"unavailable\"`;`deny` 政策下 `{allow:false,settledBy:\"timeout\"}`)并留 park 墓碑。方向 fail-closed(无 DECIDED=approve 行绝不执行工具),但行**可能**已被另一副本判成 DECIDED(approve) 而本腿没兑现 —— 人批了、工具不跑、run 再挂起要人再批一次 = 丢一次人类决议,故记债不当合法兜底。补偿:core 侧仍 pending 的 checkpoint(人再批一次就走)+ 跨副本收敛器兜底(A-075.116②:其兜底覆盖本形经确认后本 tag 降 F)。**不计**的形(S-69 起命中条件收窄到「店对整条阶梯持续不可用」):claim 输 ⇒ 败方答案**携真相**(DECIDED ⇒ 按人的决议结算,零补读;PARKING/PARKED ⇒ park 路由;absent = 行不在/错 scope ⇒ K1 零退避投影),迟到回来的 claim 同样自足(迟到赢 ⇒ 副作用补做,迟到输 ⇒ 按真决议结算 —— 此前迟到回调只认 won 的那条残余窗即 S-25-R1,本批闭合);编辑在飞标记在场 ⇒ 有界让路(不重打 claim)、用尽走 park 路由(标记的事不是店的债,codex R1-F2/R2-F2;负终态与带载荷 approve 不受标记压制,R2-F1/R3-F1);阶梯里曾拿到真相而标记已放开 ⇒ 按那份真相结算。detail 带 askId 与 claim 把数。",
|
|
222
234
|
},
|
|
223
235
|
"server.approval.ambiguous-cas.cancel": {
|
|
224
236
|
cls: "P-DEBT",
|
|
@@ -226,7 +238,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
226
238
|
},
|
|
227
239
|
"server.approval.ambiguous-cas.unreachable": {
|
|
228
240
|
cls: "P-DEBT",
|
|
229
|
-
note: "S-25 同族第三员 → S-69 收窄:**emit 全灭臂**(开卡帧两族都没送到任何连接)打 `claimTerminal(expire)`(S-69 前 `expireAsk` CAS)被 reject 之后,有界重发至多 7 把 claim(在飞帽 2,墙钟上界同 expire 员)全部 reject ⇒ 按本臂意图投影结算 = `emitFailed` ⇒ `\"unavailable\"`(park 路由;`deny` 政策下 deny),不留墓碑(卡没到任何人手上,无人持有这把 approvalId)。S-25 之前这条 catch 不读行直接强改 —— 正是 round2 finding A「durable 行已批准、活终局被 emit 全灭强改成 unavailable」的 catch 同形缺口;S-25 起行读出 DECIDED ⇒ 按人的决议结算;S-69 起真相随败方答案回来、迟到答案自足,只有店对整条阶梯持续 reject 才计数。",
|
|
241
|
+
note: "S-25 同族第三员 → S-69 收窄:**emit 全灭臂**(开卡帧两族都没送到任何连接)打 `claimTerminal(expire)`(S-69 前 `expireAsk` CAS;NP-2 起 `deny` 政策下是 `refuse`,同窗到期员)被 reject 之后,有界重发至多 7 把 claim(在飞帽 2,墙钟上界同 expire 员)全部 reject ⇒ 按本臂意图投影结算 = `emitFailed` ⇒ `\"unavailable\"`(park 路由;`deny` 政策下 deny),不留墓碑(卡没到任何人手上,无人持有这把 approvalId)。S-25 之前这条 catch 不读行直接强改 —— 正是 round2 finding A「durable 行已批准、活终局被 emit 全灭强改成 unavailable」的 catch 同形缺口;S-25 起行读出 DECIDED ⇒ 按人的决议结算;S-69 起真相随败方答案回来、迟到答案自足,只有店对整条阶梯持续 reject 才计数。",
|
|
230
242
|
},
|
|
231
243
|
"server.approval-ask-audit.append-failed": {
|
|
232
244
|
cls: "F",
|
|
@@ -244,7 +256,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
244
256
|
"server.redact.ppk-eat-to-end": { cls: "F", note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;计数=多少次整段吃尾。" },
|
|
245
257
|
"server.approval-card.closed-word-out-of-set": {
|
|
246
258
|
cls: "F",
|
|
247
|
-
note: "S-249(`approval-card.ts` 的边界窄读族;今天的唯一成员是 `readRuleOffersAbsence`):活卡帧 / `card_json` 的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40)。",
|
|
259
|
+
note: "S-249(`approval-card.ts` 的边界窄读族;今天的唯一成员是 `readRuleOffersAbsence`):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。",
|
|
248
260
|
},
|
|
249
261
|
"server.pg-pool.idle-client-error": {
|
|
250
262
|
cls: "F",
|
|
@@ -239,6 +239,7 @@ export function createMetrics() {
|
|
|
239
239
|
m.counter("cost_quota_rejected_total", "Tasks refused because the principal is over its cumulative cost quota");
|
|
240
240
|
m.counter("degraded_total", "Tasks degraded to a cheaper model (1.40), by reason (budget/rate_limit/breaker_open)");
|
|
241
241
|
m.counter("task_model_not_allowlisted_resume_total", "Resumed legs whose model/compactionModel is outside the @-mention allowlist (allowed to keep running by design — the allowlist gates fresh picks only)");
|
|
242
|
+
m.counter("task_model_unavailable_fallback_total", "Resumed legs whose explicit model pick names a catalog entry that is unavailable on this deployment (no brain mounted for its provider / entry fails its schema) — the leg runs on the default model; fresh submits get 400 request.model_unavailable");
|
|
242
243
|
m.counter("prompt_constitution_total", "Prompt assembly constitution mode per task (core 1.243 提示词主权): steady state = core; provider-assembled/replaced/legacy warrant eyes");
|
|
243
244
|
m.counter("rewind_snapshot_failed_total", "Per-turn rewind file snapshots that failed, by the engine's FileHistoryResult.error.code (snapshot_failed/aborted/…; `unknown` = the disclosure text carried no readable code). COARSE on purpose — whether a failure is permanent (the same file will fail every turn until the deployment changes) is counted by file_history_permanent_failure_total instead, from the typed error object rather than from prose");
|
|
244
245
|
m.counter("verifications_total", "Tasks run through the verification gate (1.44), by final verdict");
|
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import type { ServerSecretEnvFinding } from "../server-secret-env.js";
|
|
2
2
|
import type { Logger } from "./logger.js";
|
|
3
3
|
import type { Metrics } from "./metrics.js";
|
|
4
|
+
/**
|
|
5
|
+
* 一个剥除现场的登记:`note` = 这条腿上泄漏意味着什么;`remedy` = 部署方能不能自己把某个被剥的键放回来。
|
|
6
|
+
*
|
|
7
|
+
* 🔴 S-699:`remedy` 在场与否决定披露行的**级别**,规则一句 —— **有出路的现场用 warn,并把出路句与当前放行名单写进同一行**;
|
|
8
|
+
* 没有出路的现场是纯保护留痕,照旧 info。理由:没有出路的剥除(模型可驱动的 shell、git 钩子)是本服务替部署方做的决定,
|
|
9
|
+
* 读者只需要知道它生效了;有出路的剥除(语言服务器)可能让一件功能静默变差(补全 / 诊断缺了私有 registry 的依赖),
|
|
10
|
+
* 那是部署方要自己决定的事 —— 看不见 = 无从归因。出路句不带任何值。
|
|
11
|
+
*/
|
|
12
|
+
export interface SecretEnvScrubSiteSpec {
|
|
13
|
+
readonly note: string;
|
|
14
|
+
readonly remedy: string | null;
|
|
15
|
+
}
|
|
4
16
|
/** 登记过的剥除现场。闭集:未登记的 site 传不进 {@link recordSecretEnvScrub}(编译期拒,同 FAIL_OPEN_TAGS 纪律)。 */
|
|
5
17
|
export declare const SECRET_ENV_SCRUB_SITES: {
|
|
6
|
-
readonly "server.remote-env-host.inherit-scrub":
|
|
7
|
-
|
|
18
|
+
readonly "server.remote-env-host.inherit-scrub": {
|
|
19
|
+
readonly note: "模型可驱动的 host shell 继承 env(`inheritEnv:\"scrub\"` 默认腿)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令";
|
|
20
|
+
readonly remedy: null;
|
|
21
|
+
};
|
|
22
|
+
readonly "server.project-memory.git-subprocess": {
|
|
23
|
+
readonly note: "project-memory 读仓叙事时的 git 子进程 env —— 仓内的钩子/凭证助手会以本进程身份运行";
|
|
24
|
+
readonly remedy: null;
|
|
25
|
+
};
|
|
26
|
+
readonly "server.lsp.spawn-env": {
|
|
27
|
+
readonly note: "宿主车道本地语言服务器子进程 env(core 7.30.1 #1073 的 `NodeLspManagerOptions.env`)—— 语言服务器会执行仓里的代码(构建脚本/插件/工作区配置),一次泄漏等于把本进程的凭据交给仓";
|
|
28
|
+
readonly remedy: `LSP_ENV_ALLOW=NAME,NAME \u70B9\u540D\u653E\u884C(\u539F\u6837\u4EA4\u7ED9\u8BED\u8A00\u670D\u52A1\u5668,\u90E8\u7F72\u65B9\u663E\u5F0F\u627F\u62C5;${string});\u503C\u4E0D\u8FDB\u65E5\u5FD7`;
|
|
29
|
+
};
|
|
8
30
|
};
|
|
9
31
|
/** 词表键推导的闭集类型。 */
|
|
10
32
|
export type SecretEnvScrubSite = keyof typeof SECRET_ENV_SCRUB_SITES;
|
|
@@ -15,8 +37,10 @@ interface Sink {
|
|
|
15
37
|
/**
|
|
16
38
|
* 记一次剥除。**总不抛** —— 观测本身绝不能变成故障(同 `recordFailOpen` / reapers 的判据)。
|
|
17
39
|
* `findings` 为空(这一次没丢任何 key)时什么也不做:那不是事件,是常态。
|
|
40
|
+
* `findings` 只许装**最终没进子进程**的键 —— 点名放回的键不是剥除(不计数、不披露)。
|
|
41
|
+
* `allowed` = 这一次生效的点名放行名单(S-699),只在有出路的现场(`remedy` 在场)写进披露行;没有放行面的腿传 `[]`。
|
|
18
42
|
*/
|
|
19
|
-
export declare function recordSecretEnvScrub(site: SecretEnvScrubSite, findings: readonly ServerSecretEnvFinding[]): void;
|
|
43
|
+
export declare function recordSecretEnvScrub(site: SecretEnvScrubSite, findings: readonly ServerSecretEnvFinding[], allowed: readonly string[]): void;
|
|
20
44
|
/** 装配层把 logger/metrics 接上(main.ts,与 `installFailOpenRecorder` 同一处)。装配前累计的次数在此补进 metrics。 */
|
|
21
45
|
export declare function installSecretEnvScrubRecorder(deps: Sink): void;
|
|
22
46
|
/** 已留驻的「现场×键」条数(测试/诊断读面)。恒 ≤ {@link DISCLOSED_CAP}。 */
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { summarizeRedactions } from "@sema-agent/core";
|
|
2
|
+
import { describeAllowlistPostures } from "../env-name-allowlist-knobs.js";
|
|
2
3
|
export const SECRET_ENV_SCRUB_SITES = {
|
|
3
|
-
"server.remote-env-host.inherit-scrub":
|
|
4
|
-
|
|
4
|
+
"server.remote-env-host.inherit-scrub": {
|
|
5
|
+
note: "模型可驱动的 host shell 继承 env(`inheritEnv:\"scrub\"` 默认腿)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令",
|
|
6
|
+
remedy: null,
|
|
7
|
+
},
|
|
8
|
+
"server.project-memory.git-subprocess": {
|
|
9
|
+
note: "project-memory 读仓叙事时的 git 子进程 env —— 仓内的钩子/凭证助手会以本进程身份运行",
|
|
10
|
+
remedy: null,
|
|
11
|
+
},
|
|
12
|
+
"server.lsp.spawn-env": {
|
|
13
|
+
note: "宿主车道本地语言服务器子进程 env(core 7.30.1 #1073 的 `NodeLspManagerOptions.env`)—— 语言服务器会执行仓里的代码(构建脚本/插件/工作区配置),一次泄漏等于把本进程的凭据交给仓",
|
|
14
|
+
remedy: `LSP_ENV_ALLOW=NAME,NAME 点名放行(原样交给语言服务器,部署方显式承担;${describeAllowlistPostures()});值不进日志`,
|
|
15
|
+
},
|
|
5
16
|
};
|
|
6
17
|
const SCRUB_COUNTER = "secret_env_scrubbed_total";
|
|
7
18
|
const DISCLOSED_CAP = 512;
|
|
@@ -30,7 +41,7 @@ function drainToMetrics() {
|
|
|
30
41
|
}
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
|
-
export function recordSecretEnvScrub(site, findings) {
|
|
44
|
+
export function recordSecretEnvScrub(site, findings, allowed) {
|
|
34
45
|
if (findings.length === 0)
|
|
35
46
|
return;
|
|
36
47
|
try {
|
|
@@ -48,12 +59,17 @@ export function recordSecretEnvScrub(site, findings) {
|
|
|
48
59
|
const fresh = findings.filter((f) => !disclosed.has(`${site}${SEP}${f.key}`)).slice(0, room);
|
|
49
60
|
if (fresh.length === 0)
|
|
50
61
|
return;
|
|
51
|
-
|
|
62
|
+
const spec = SECRET_ENV_SCRUB_SITES[site];
|
|
63
|
+
const fields = {
|
|
52
64
|
site,
|
|
53
|
-
note:
|
|
65
|
+
note: spec.note,
|
|
54
66
|
keys: fresh.map((f) => f.key),
|
|
55
67
|
summary: summarizeRedactions(fresh),
|
|
56
|
-
}
|
|
68
|
+
};
|
|
69
|
+
if (spec.remedy === null)
|
|
70
|
+
logger.info("secret_env_scrubbed", fields);
|
|
71
|
+
else
|
|
72
|
+
logger.warn("secret_env_scrubbed", { ...fields, allowed: [...allowed], hint: spec.remedy });
|
|
57
73
|
for (const f of fresh)
|
|
58
74
|
disclosed.add(`${site}${SEP}${f.key}`);
|
|
59
75
|
}
|