@sema-agent/settings-schema 6.1.0 → 6.3.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/CHANGELOG.md +265 -0
- package/README.md +40 -9
- package/conformance/VERSION +1 -1
- package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +166 -0
- package/conformance/cases/c41-s792-encoded-pem-identity/input.json +62 -0
- package/conformance/cases/c41-s792-encoded-pem-identity/meta.json +12 -0
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +120 -0
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/input.json +25 -0
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/meta.json +11 -0
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +137 -0
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/input.json +44 -0
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/meta.json +12 -0
- package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +115 -0
- package/conformance/cases/c44-s792-utf8-escape-glued-token/input.json +30 -0
- package/conformance/cases/c44-s792-utf8-escape-glued-token/meta.json +10 -0
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +185 -0
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/input.json +78 -0
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/meta.json +11 -0
- package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/expected.json +88 -0
- package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/input.json +18 -0
- package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/meta.json +6 -0
- package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/expected.json +98 -0
- package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/input.json +26 -0
- package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/meta.json +6 -0
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +0 -8
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/meta.json +2 -1
- package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +0 -8
- package/conformance/cases/cli02-control-and-zero-width-chars/meta.json +2 -1
- package/dist/config-fns.d.ts +62 -14
- package/dist/config-fns.js +105 -30
- package/dist/cross-session.d.ts +8 -5
- package/dist/cross-session.js +8 -5
- package/dist/fleet.d.ts +163 -0
- package/dist/fleet.js +126 -5
- package/dist/secret-scan.d.ts +39 -0
- package/dist/secret-scan.js +110 -0
- package/dist/types.d.ts +79 -72
- package/dist/types.js +17 -31
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,271 @@
|
|
|
6
6
|
> `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
|
|
7
7
|
> 包名是**历史记录**,如实保留不追改。
|
|
8
8
|
|
|
9
|
+
## Unreleased
|
|
10
|
+
|
|
11
|
+
## 6.3.0 — 2026-09-27 — 安全修(codex gpt-6-astra 对 v6.1.0 复审 [high],回填 D-4):可披露身份改白名单闭形 `DISCLOSABLE_IDENTITY_RE` · `redactSecrets` 整串判套在一处归一化上 · 告警坐标出门一道
|
|
12
|
+
|
|
13
|
+
> **判档 = minor**(发版方裁,2026-09-27;原排 6.2.1 当日补丁):按 README「发版纪律」,新导出 `DISCLOSABLE_IDENTITY_RE` = minor
|
|
14
|
+
> (归一化 `secretScanForms` 在合并复审 opus 腿 L7 / M1 之后收进包内私有模块 `src/secret-scan.ts`,不是导出)。另:`isDisclosableIdentity` / `isReportableRefusalName` 对「不是标识符」的串由答 true 收成答 false,
|
|
15
|
+
> `entry-dropped.identity` 与引擎 `modelRefusals.entries` 随之少带这类名(告警面 / 诊断面收紧,effective 与校验结果不动)。
|
|
16
|
+
> 不改任何 wire 形、类型面、默认值与校验结果。
|
|
17
|
+
|
|
18
|
+
### 病(6.1.0 起;5153d2a 之前没有 `entry-dropped.identity` 这个面)
|
|
19
|
+
|
|
20
|
+
一条被丢条目的 `id = "key=" + encodeURIComponent(<PEM 私钥>)` ⇒ 四个出门面原样带出整段编码私钥,解码即可用:
|
|
21
|
+
`entry-dropped.identity.id`(与 `projectWarning` 投影)、同串作 Record 坏键时的坐标段、zod error 的 `received` / `message`、
|
|
22
|
+
`isReportableRefusalName` 答 true(引擎会把它当 `modelRefusals.entries` 的键送上心跳)。机制:`PRIVATE_KEY_PEM_HEADER` 只测
|
|
23
|
+
**原文**(`-----BEGIN%20PRIVATE%20KEY` 的 `%20` 不在 `[A-Z0-9 ]`);令牌那一遍把整串当一个 run 交 `containsSecretToken`,它解码后
|
|
24
|
+
按**行首锚**判,`key=` 前缀让锚失败。
|
|
25
|
+
|
|
26
|
+
### 修(以点改面,不加 PEM 检测臂)
|
|
27
|
+
|
|
28
|
+
- **身份面 = 标识符白名单**(`src/config-fns.ts`):新导出 `DISCLOSABLE_IDENTITY_RE = /^[A-Za-z0-9._:/@+-]{1,256}$/`(无旗标;上限 256 与 web-admin 读侧名长上限对齐,发版方裁)。
|
|
29
|
+
`isDisclosableIdentity(v)` = 字符串 ∧ 落在该闭形里 ∧ `redactSecrets(v) === v`。「不像密钥就可披露」不再是**充分**条件
|
|
30
|
+
(6.1.0 / 6.2.0 的判据,放行了上面的形);密钥否决留作**合取项**:集内照样有 `sk-…` / `ghp_…` / `AKIA…` / 三段 JWT(实测:
|
|
31
|
+
只留白名单 ⇒ S-712「秘密形字面」、S-712 对抗轮 #2、S-768 B3 逐格判、S-768 N1 四格钉红),且否决必须是坐标段 / error 文案
|
|
32
|
+
用的**同一只**脱敏器(对抗轮 #2 律;集内残例 = `@` 之后的单点 `eyJ…` 串,裸探测器判阴、脱敏器会改,语料 c41 压着)。
|
|
33
|
+
`isReportableRefusalName` 经它,同收。不可披露 ⇒ 按既有的「无名」处置(键缺席;没有新造占位词)。「无密钥形」这一项里另有一只
|
|
34
|
+
URL userinfo 判定(合并复审 opus 腿 L1):`https://admin:hunter2@db…` / `postgres://svc:pw@h` 的字符全在集内,6.2.0 起两只身份谓词都答
|
|
35
|
+
可披露;复用校验侧 `NON_SECRET_URL` 拒写的**同一只**判定(抽成包内私有 `carriesUserinfo`,`NON_SECRET_URL` 与 https 插件源的 refine
|
|
36
|
+
改调它,校验结果逐字不变),不加新检测臂。路径里的 `@`(`https://host/@scope/pkg`)照旧可披露。
|
|
37
|
+
- **自由文本面 = 一处归一化**(包内私有模块 `src/secret-scan.ts`,不进主 barrel、不进 `exports`:展示侧读法不做公开契约 —— opus 腿 L7):
|
|
38
|
+
`percentLayers(text)` 给校验侧探测器,`secretScanForms(text)` 回纯数据记录给展示侧脱敏器 ——
|
|
39
|
+
`percentLayers` = 原文 + 至多三层宽容百分号解码(到不动点即停),正是 `containsSecretToken` 一直在读的那几层,抽成一处定义;
|
|
40
|
+
`forms` = 这些层 + 每层的**边界读法**(连续的 `%HH` 按 UTF-8 整段解,解不开的转义读作空格 —— 转义的十六进制位永远不粘到后面那个词上;
|
|
41
|
+
codex r2 F-r2-1:`key=%C2%A0sk-live-…` 逐个解码解不开 NBSP,`A0sk-…` 让令牌边界消失)+ 以上每个形把 `+` 读作空格(一层
|
|
42
|
+
application/x-www-form-urlencoded 读法),去重、先见先排;`unresolved` = 三层百分号层走完仍能再解(归一化没走完)。`containsSecretToken` 只读 `percentLayers`,**判定逐格不变**(冻结 6.2.0 实现作 oracle:4000 条确定性伪随机串,
|
|
43
|
+
字母表含 `+` / `Bearer+` / `basic+`,+ 既有样例零分歧;头注的 major 理由不变,不放宽也不收紧)。`redactSecrets` 的整串判是一条规则:
|
|
44
|
+
`unresolved` ⇒ 整串扣(**归一化不完的东西不出门**,codex r1 F1 四层编码),PEM header 在原文的每一个 `forms` 上测(codex r1 F1:
|
|
45
|
+
`-----BEGIN+PRIVATE+KEY` 形式编码头),`containsSecretToken` 在替换结果的每一个 `forms` 上测。没有长度截断臂:每层一遍线性替换,
|
|
46
|
+
不存在「超长就只看原文」的 fail-open 口。「解码失败」在宽容解码下不存在:单个解不开的转义(`%E4` 这类不完整 UTF-8)是不动点,
|
|
47
|
+
不算「没走完」—— 所以编码的非 ASCII 文本(`%E4%B8%AD`)照旧只做令牌级替换(任务书字面是「仍有 `%xx` 即扣」,这里取「还能再解一层」,
|
|
48
|
+
差别只在这一类合法文本上,候发版方复核)。
|
|
49
|
+
- **读路径不放大**(合并复审 opus 腿 M1 [medium,对 6.2.0 回归],同轮修):① 两处解码都**零异常、单遍**:单个转义只有 ASCII 的
|
|
50
|
+
`%00`–`%7F` 单独解得开,写成字符集替换;转义串的 UTF-8 判定写成良构判定(Unicode Table 3-7),不再对每个非 ASCII `%HH` 抛一次
|
|
51
|
+
异常 —— 结果与原 try/catch 逐字相同(冻结 a33e7fd 实现作 oracle:6000 条伪随机 + 边界样例零分歧);② `containsSecretToken` 只算
|
|
52
|
+
`percentLayers`,展示形只有 `redactSecrets` 算;未替换过的串复用同一组形;③ 出门坐标的段有长度上界(与身份白名单同一只 256,
|
|
53
|
+
私有常量 `DISCLOSABLE_IDENTITY_MAX` 两处复用):超长段不扫描、整段 `[redacted-secret]`(O(1),fail-closed)。读数(opus `perf2/3/4.mjs`
|
|
54
|
+
同输入,本机;冷 = 原脚本直跑,暖 = 先热身再三次取中位数):
|
|
55
|
+
|
|
56
|
+
| 输入 | 6.2.0 | 修前(a33e7fd) | 修后 |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| perf2 64 × 16 KB `%C2` 未知键名 | 冷 4–9 ms / 暖 0.5 ms | 冷 17.8–19.3 s | 冷 7–21 ms / 暖 0.3–0.4 ms |
|
|
59
|
+
| perf2 16 × 64 KB `%C2` 未知键名 | 冷 0–1 ms / 暖 0.3 ms | 冷 24.0 s | 冷 0 ms / 暖 0.2 ms |
|
|
60
|
+
| perf3 64 × 16 KB plain / `%20` / `%C2` / 编码中文 | 暖 0.2–0.3 ms | 23 ms / 308 ms / 17.1 s / 10.6 s | 冷 4–13 / 1 / 0 / 0 ms,暖 0.1–0.3 ms |
|
|
61
|
+
| perf4 16 × 16 KB 编码中文 roles 坏键(6.2.0 已脱敏的路径)| 6.1–14.4 s | 41.8 s | 146–526 ms |
|
|
62
|
+
|
|
63
|
+
冷读数里修后比 6.2.0 多出的十几 ms 是本模块首调的编译热身(同一脚本先跑 6.2.0 那份);暖读数与 6.2.0 同量级,perf2 / perf3 / perf4 三组满足「≤ 6.2.0 的 2×」(发版前 opus 验修腿在负载 40–123 下复跑同结论)。
|
|
64
|
+
`redactSecrets` 对**单个大块良性文本**(1 MB,`perf.mjs`)仍比 6.2.0 慢 1.9–2.9 ×(ASCII 转义密集又夹 `+` 的良性文本 2.5–4.4 ×,20,000 个 250 字坏键 18 s → 54 s;它要多判边界读法与 `+` 读法那几个形;最坏形反向:1 MB `%C2` 从 6.2.0 的 ≈190 s 降到 1–3 s,编码中文 / 非法 UTF-8 快 20–100 倍);读路径上
|
|
65
|
+
能进它的长串只剩 zod 回显的坏值(坐标段已被 256 上界截住),对编码中文 / `%C2` 这类转义密集文本修后比 6.2.0 快 20–60 ×。
|
|
66
|
+
- **铸点不动**:`buildEffective` 的 `entry-dropped.identity` 经 `readEntryIdentity` → `isDisclosableIdentity`,投影随之。
|
|
67
|
+
- **坐标出门一道**(codex r1 F2,发版方裁并入;opus 腿 M2 同轮补臂):告警里用户写的串 —— 被丢条目的 `path`、`unknown-keys-dropped` /
|
|
68
|
+
`unknown-keys-carried` 的 `keys`、`hosts-grandfathered` 的 `droppedNames` —— 出门前每段过同一道私有 `outgoingSegment`:长过 256
|
|
69
|
+
(与身份白名单同一只上界)整段 `[redacted-secret]`,否则过 `redactSecrets`(同一只脱敏器,不另设判据;数字段原样)。`droppedNames`
|
|
70
|
+
判在夹紧成 63 字**之前**(截断会把秘密形截成认不出的半截);此前它以 63 字原文前缀出门,`sk-live-…` 与编码 PEM 的 header 加私钥体
|
|
71
|
+
前段都在里面(6.2.0 同形)。`legacy-governance-lifted.keys` 不经它:键只来自闭集 `GOVERNANCE_MIRROR_KEYS`,按构造没有用户写的串。
|
|
72
|
+
**先钳后扫**(codex 验修腿 S-813 [medium],对 a33e7fd / 6.2.0 回归,同轮修):`droppedNames` 只给前 64 个(与未知键同一只 `DROP_SCAN_MAX_KEYS`)
|
|
73
|
+
铸标签,其余只计数 —— `grandfatherHostsRead` 的结果加 `droppedTotal`(真实条数,与 `UnknownKeyScan` 的 `{ keys, total }` 同形),
|
|
74
|
+
`hosts-grandfathered` 告警加可选 `droppedTotal?`(在场 ⇔ 标签被钳,缺席 = 列表是穷举;additive,投影随带)。修前 M2 让每个超长主机名
|
|
75
|
+
都先归一化、条数无上限:1 个合法 host + 5000 个 `'%41'.repeat(85)` 名(≈ 1.3 MB)每次读 620–786 ms(本机;codex 读 1.5–1.8 s,
|
|
76
|
+
vitest 负载下 2.2 s),无 `onWarning` 也付;修后暖读 6.8–17 ms(6.2.0 0.8–2.7 ms,a33e7fd 0.5–1.3 ms)。
|
|
77
|
+
此前只有被丢 Record 条目的键段过脱敏器,未知键告警的键名 0.19.0 起从来不过:`sk-…` / `ghp_…` / 编码 PEM 形的未知键名原样进告警与投影。
|
|
78
|
+
导出的 `droppedKeys` / `carriedUnknownKeys` 给的 `keys` 同为出门形(`total` 照数真实条数);schema 已知字段名脱敏器原样放行。
|
|
79
|
+
|
|
80
|
+
### 谁受影响(行为面)
|
|
81
|
+
|
|
82
|
+
- `entry-dropped.identity.name` / `.id` 与引擎的 refusal 名:含 `%` / `=` / 空白 / 引号 / 反斜杠 / 控制与零宽字符 / 非 ASCII /
|
|
83
|
+
超 256 字的串,6.2.0 带,6.3.0 起不带。**普查**(6.2.0 基线树全部 fixture / 测试 / 语料 / 样例 / 文档里的 `name` / `id` 字面
|
|
84
|
+
1261 个 + 6.2.0 refusal 名样例):新拒 19 个,**真实形的模型 id / 名字 0 个** —— 对抗语料 10 个(cli01 NFC/NFD `café`、cli02 BEL /
|
|
85
|
+
零宽、cli05 300 / 303 字 —— cli05 两条是**合法**条目,在 effective 里,不上身份面;作 refusal 名时不再上报)、按自身 schema 就
|
|
86
|
+
非法的负样例 5 个(`BAD NAME` 类)、空串 1 个、自由展示名 3 个(`Team A` / `Global again` = scopes wire、BudgetGroup 在 gate 域,
|
|
87
|
+
不上身份面;`评审团` = CollabTemplate.name —— collab 域被丢的模板身份里 6.3.0 起只剩 `id`(一条规则,展示名不另设谓词 —— 发版方裁))。新放行 0 个。
|
|
88
|
+
- **刻意的代价(合并复审 opus 腿 L3 / L4,一条规则的一致推广,丢的是诊断文本不是泄露)**:良性英文里的 `basic+…` / `bearer+…`
|
|
89
|
+
(`https://x.com/search?q=basic+authentication`、身份 `gpt-4o+basic+reasoning-v2`)在 `+` 读法上判阳,自由文本整串扣、身份不可披露
|
|
90
|
+
(6.2.0 对原文空格形 `q=basic authentication` 本就整串扣);名字 / id 含空格、中文、括号的被丢条目(`{name:"GPT 4o", id:"gpt-4o (2024-08-06)"}`)
|
|
91
|
+
身份整体缺席,运维 UI 上只剩坐标。
|
|
92
|
+
- 语料:`cli01` / `cli02` 的 `expected.json` 重 bless,diff 只少两处 identity(meta 加 note);**消费方自己入库的语料**若金样里
|
|
93
|
+
identity 带了集外字符,提货时要重新加冕(`docs/CONFORMANCE.md` 同口径)。
|
|
94
|
+
- 自由文本面(过 `redactSecrets` 的那几处 = 告警坐标的键段、zod error 文案):含编码私钥 header(百分号编码、`+` 代空格)的串、
|
|
95
|
+
以及三层解码走完仍能再解的串,整串变 `[redacted-secret]`(6.2.0 原样出门);替换结果的 `+` 读法 / 边界读法上判阳的
|
|
96
|
+
(`auth=Bearer+<令牌>`、`key=%C2%A0sk-…`、`%E4sk-…` 这类转义粘令牌)同样整串扣。编码的非 ASCII、一层就解完的 `%` 文本照旧只做令牌级替换。
|
|
97
|
+
- 未知键告警(`unknown-keys-dropped` / `-carried`)与 `droppedKeys` / `carriedUnknownKeys` 的 `keys`、`hosts-grandfathered.droppedNames`:
|
|
98
|
+
秘密形段与超过 256 字的段读 `[redacted-secret]`(6.2.0 原样;超长良性键名 / 主机名因此也不再逐字点名 —— fail-closed 的代价);两条不同的秘密键会读成同一段,告警比对本就是多重集(c10 同律)。良性拼错键照旧逐字点名;承运进 effective 的键与值不动。
|
|
99
|
+
|
|
100
|
+
### 残留(如实登记;6.2.0 同形,非本版回归)
|
|
101
|
+
|
|
102
|
+
- 裸 base64 私钥体(无 header):任何一面的检测器都认不出 —— 粗粒度启发式的固有边界(真边界仍是结构化字段上的 env-NAME);身份面只在它
|
|
103
|
+
带了集外字符(`=` 填充、`key=` 前缀、编码)时由白名单挡下,纯 `[A-Za-z0-9+/]` 的一段体照样可披露。
|
|
104
|
+
- 形式编码与多层百分号编码之外的变形(base64 包一层、HTML 实体、`\u002D` 转义等):不在归一化里,见上一条同一边界;非 `%HH` 的坏转义粘令牌
|
|
105
|
+
(`%ZZsk-…`)照旧是既有登记的粗粒度缺口。
|
|
106
|
+
- **codex r2 F-r2-2 [medium](未修,6.2.0 同形)**:短的 Basic / Bearer 凭据在 `+` 读法上 —— `note:Basic+dTpw`(`dTpw` 解出 `u:p`)——
|
|
107
|
+
脱敏器的定点 Bearer 一遍只跑原文,整串判用的探测器那支要求 ≥ 8 个令牌字符,所以它原样出门,且落在身份白名单字符集内,身份 / refusal 名
|
|
108
|
+
两谓词都答可披露。同族的 `%20` 形(`note:Basic%20dTpw` / `x-Bearer%20abc123`,合并复审 opus 腿 L2)在自由文本面同样原样出门
|
|
109
|
+
(身份面由白名单挡住 `%`)。发版方令本车第二轮只收敛 high,登 DEBTS。
|
|
110
|
+
- URL userinfo 否决只判得了能解析成 URL 的串:无 scheme 的 `svc:pw@host` 解析成不透明路径,不算带 userinfo。
|
|
111
|
+
- **S-811 [high,既有非回归,本版不修]**:自由文本面(`hosts-grandfathered.droppedNames`、坏 roles 键的坐标段与 zod 错误句)不查 URL userinfo
|
|
112
|
+
—— `https://admin:hunter2@<host>` 原样出门(本版只在身份面加这只判定;设计随 S-809 三表归一)。现状由语料 🚧 特征格钉住:
|
|
113
|
+
`c46-s811-host-name-url-userinfo-characterization` / `c47-s811-roles-key-url-userinfo-characterization`,修后两条金样翻成 `[redacted-secret]`、
|
|
114
|
+
meta.secrets 补上 `hunter2`。
|
|
115
|
+
- AWS secret key 形(40 位 base64 类)、只有 footer 没有 header 的扁平 PEM(`MC4C…+-----END+PRIVATE+KEY-----`)等粗粒度启发式认不出的形:
|
|
116
|
+
与上面「裸 base64 私钥体」同一边界(opus 腿 L1 附注)。
|
|
117
|
+
- codex r1 三条(F1 `+` 形式编码 / 扁平 PEM、URLSearchParams 形、四层编码;F2 未知键坐标;F3 长名上限)本版均已处置,见「修」。
|
|
118
|
+
- codex r2(`--base 1965aa9 --scope branch`,本车第二轮也是最后一轮):F-r2-1 [high](转义的 UTF-8 字节粘令牌)红先成立并修(边界读法,见「修」);
|
|
119
|
+
F-r2-2 [medium] 未修(见上)。
|
|
120
|
+
- 以上 codex gpt-6-astra r1 复核(车PE 收车档逐条复现:修后树与 6.2.0 源两侧同跑)。另:codex r1 F3 —— 首稿上限 200 让
|
|
121
|
+
schema 合法的 201–256 字名不再上报 / 不进身份;发版方裁上限改 256(与 web-admin 读侧对齐),超 256 字的合法名(`ModelEntry.name`
|
|
122
|
+
无长度上限)仍按无名处置 —— 对 6.2.0 是诊断面收窄,见「谁受影响」。
|
|
123
|
+
|
|
124
|
+
### 测试
|
|
125
|
+
|
|
126
|
+
- 新 `test/disclosable-identity-6.3.0.test.ts`:R1 codex 复现形(现场生成 Ed25519,身份 / 投影 / 坐标 / error / refusal 名四面 +
|
|
127
|
+
四重编码)· R2 白名单正负矩阵(常量源串逐字、无旗标;集内密钥形否决;经 `buildEffective` 同律扫)· R3 `redactSecrets` 归一化
|
|
128
|
+
(单 / 双重编码整串扣、出门不变量、6.2.0 读数逐字的控制格、`secretScanForms` 形 —— 测试直接 import 私有模块)· `containsSecretToken` 6.2.0 oracle。
|
|
129
|
+
红先提交(ef13bef)上 6.2.0 读数:11 红 / 5 绿(绿 = 正控 / 同律扫 / 控制 / 前提 / oracle);修提交加的 R1 四重编码格在 6.2.0
|
|
130
|
+
源上同红(终版文件对 6.2.0:12 红 / 5 绿)。
|
|
131
|
+
- 语料 `c41-s792-encoded-pem-identity`(contract):编码 PEM 作 id 与 roles 坏键、四重编码 id、集内 `sk-` 形与 `ops@eyJ…` 形 name、
|
|
132
|
+
真实形 id 正控;6.2.0 上三腿告警差 + 秘密扫描命中 2 条。
|
|
133
|
+
- B 组(codex r1 F1 部分修):`+` 扁平形四面、URLSearchParams 形作 Record 坏键、四层编码作 Record 坏键 / 未知键名、出门不变量、
|
|
134
|
+
正控(含 `%` 路径 / 编码非 ASCII / 三层恰解完 / `+` 在真实 id 里原样)、边界(恰四层的非秘密串按没走完整串扣);`secretScanForms` 形钉
|
|
135
|
+
改纯数据记录;原 🚧 四层前提格翻钉;语料 `c43-s792-form-and-deep-encoded-pem`。变异门加两靶:`+` 读法去掉、`unresolved` 去掉
|
|
136
|
+
(都由 c43 杀);三只 PEM 靶 needle 随改。
|
|
137
|
+
- M1 组(opus 腿 M1):坐标段 256 上界(256 逐字 / 257 整段扣)、宽容解码零异常(`decodeURIComponent` 抛出计数 = 0,修前 4001)、
|
|
138
|
+
opus perf2 同输入宽上界验收格(< 5 s,修前 19.3 s)、a33e7fd 归一化冻结 oracle(6000 条)。
|
|
139
|
+
- B2 组(codex r2 F-r2-1):三形前提(探测器判阴、标准解码后判阳)、自由文本整串扣、Record 坏键 / 未知键名两面;五层编码只剩 `unresolved`
|
|
140
|
+
扣得住的格;语料 `c44-s792-utf8-escape-glued-token`(`path%2Fto%E4%B8%AD` 正控逐字留)、c43 追加五层一条。变异门加「边界读法去掉」一靶(c44 杀),
|
|
141
|
+
`+` 读法靶 needle 随改。
|
|
142
|
+
- A 组(codex r1 F2):strip 域顶层 / 嵌套、open-world 承运、编码 PEM 键名、导出扫描函数四格;语料 `c42-s792-unknown-key-secret-redacted`
|
|
143
|
+
(6.3.0 修前三腿告警差 + 秘密扫描命中 3 条)。变异门加一靶坐标出门去脱敏,两只嵌套坐标靶 needle 随改。
|
|
144
|
+
- S-813 组:语义格(70 个超长名 ⇒ 标签 64 个、`droppedTotal` 70、告警同带)、控制格(不超界时告警不铸 `droppedTotal`)、
|
|
145
|
+
性能格(codex 同输入,暖读中位数 ≤ 50 ms;修前 vitest 内 2.2 s)。
|
|
146
|
+
- 🚧 S-811 特征语料 c46 / c47(见「残留」;不是红先复现,是现状钉)。
|
|
147
|
+
- 合并复审 opus 腿组:M2(`droppedNames` 两格)、L1(负正各一格)、M1(见上);语料 `c45-s792-identity-bounds-and-decode-depth`
|
|
148
|
+
(256 / 257 字名与未知键名、三重编码 mcp 参数、`postgres://svc:…@db…` id、`sk-live-…` 超长主机名)—— 杀手语料,不是红先复现。
|
|
149
|
+
- 变异门(`test/conformance-mutation.test.ts`)身份 / 脱敏 / 出门各靶的**实测**杀手(车PE 逐靶探针,十六靶无活口):
|
|
150
|
+
白名单闭形 → cli01 + cli02 + c45(**都不是秘密形**:编码 PEM 那一形在这个变异体下先被脱敏器否决扣下,c41 不再红 —— opus 腿 L5;
|
|
151
|
+
秘密轴上白名单独自承重的形没有语料钉);集内否决 → `true` / 退回裸探测器 → c41 + c43;整串兜底 → c40 + c41 + c43 + c44;
|
|
152
|
+
任意位置 PEM → c40 + c41 + c43;PEM 判在替换前 → c40;PEM 判在归一化形 → c41 + c43;`+` 读法 → c43;边界读法 → c44;
|
|
153
|
+
`unresolved` → c43;`outgoingSegment` 去脱敏 → c10 + c40–c45;`DISCLOSABLE_IDENTITY_MAX` 放大 / `outgoingSegment` 去上界 /
|
|
154
|
+
`SECRET_SCAN_DECODE_LAYERS` 3 → 2 / 去 userinfo 否决 / `droppedNames` 不过出门 → c45。
|
|
155
|
+
|
|
156
|
+
- **test-only(S-595,零运行期变化;随 6.3.0 折入)**:上游漂移门加 ④ —— `CROSS_SESSION_INBOUND_VALUES` / `DIALOG_EXPIRY_VALUES` / `DIALOG_EXPIRY_DEFAULT` 与引擎运行期导出 `CROSS_SESSION_INBOUND_SETTINGS` / `CROSS_SESSION_DIALOG_EXPIRY_VALUES` / `CROSS_SESSION_DIALOG_EXPIRY_DEFAULT` 逐字同序相等(登记镜像:登记表恒空、零豁免,顺序另钉,两只 zod 受理面 = 引擎词表 + `"default"`;负控一格 + 五发变异各红一格;对拍的是 devDependency 钉住的那版引擎,不证明与消费方自装的任意引擎版本相容),等值门从消费方挪回镜像持有者;`src/cross-session.ts` 头注「与引擎词表的锁步」段与 `llms-full.txt` 同句改口径,头注同一处 6.0.0 起陈旧的计数(`"default"` 是第四拼写 / 值域三个)订正为第五 / 四个(注释 / 文档)。
|
|
157
|
+
|
|
158
|
+
## 6.2.0 — 2026-09-26 — additive:`ModelRefusalsWire` / `ModelRefusalsSignal` 一处定义 + `readModelRefusals`(S-768;条目两码 / 组拒四码闭集是读方返回型,wire 开放串、读方收窄,心跳字段形坏降缺席 —— 诊断键不打路由)· `isDisclosableIdentity` / `isReportableRefusalName` 导出 · devDep core 2.1.0 → 7.33.1 精确(漂移门 ② 改读运行期 `MODEL_ROLES`,删陈旧豁免)· README / llms-full zod 两实例真话(server / CLI 已在 zod 4;归一 = 7.0.0,S-777)
|
|
159
|
+
|
|
160
|
+
> 本节全 additive(新导出 / 新可选键),零删零改名。判档口径见 README「发版纪律」:新键 / 新导出 = minor。心跳上的新键
|
|
161
|
+
> 形坏只降为缺席,6.1.0 收下的任何心跳体 6.2.0 照收(见「谁受伤」③)。
|
|
162
|
+
|
|
163
|
+
### S-768 · 模型拒因信号:闭集码真源 + wire 形 + 一只读口两出口(additive)
|
|
164
|
+
|
|
165
|
+
`src/fleet.ts`(与 `RestartSignal` 同节)新导出:
|
|
166
|
+
|
|
167
|
+
- **成因码闭集**(`as const` 元组 + `z.enum` + 型):`MODEL_ENTRY_REFUSAL_CODES = ["config.model_no_brain",
|
|
168
|
+
"config.model_entry_invalid"]` / `ModelEntryRefusalCode`;`MODEL_PLANE_REFUSAL_CODES = ["config.default_model_missing",
|
|
169
|
+
"config.model_refused_name_minted", "config.model_route_endpoint_missing", "config.gateway_fallback_without_primary"]` /
|
|
170
|
+
`ModelPlaneRefusalCode`。词**逐字**取自引擎(server @ 2aa56fe7:`src/model-entry-refusal.ts:45-56` —— 条目两码 = S-704 逐条判据
|
|
171
|
+
的两个成因,`config.default_model_missing` = S-705 模型面组拒,`config.model_refused_name_minted` = S-712 不变量组拒;
|
|
172
|
+
`src/model-route-endpoint.ts:40,42` —— 不变量 `model-route-endpoint` 的两码,模型面 stage 在 `src/config-center/apply-effective.ts:664-665`
|
|
173
|
+
判);红先文件里的期望字面是抄来的,不从实现派生。**组拒码镜像引擎组拒码表的整族**(发版方裁 1):不按「今天热路径上可达」
|
|
174
|
+
挑 —— 可达性会变,表不变(`config.gateway_fallback_without_primary` 今天在热路径上实际不可达,同一 env 下 boot 恒先拒,照收)。
|
|
175
|
+
**真源上移**:此前闭集只活在引擎一侧,中心要穷举就得手抄(C-R75);上移后两边 import 同一份。**谁在加码时编译红**(对抗腿
|
|
176
|
+
F8 订正首稿「双侧编译红」):按码穷举 `switch` 的读方(中心)必红;生产方只有持**以码为键**的表(对码穷举 `switch` /
|
|
177
|
+
`Record<码, …>`)才红 —— 只拿 `satisfies` 把自己的码值约束成本包闭集的子集,证明的是「子集」不是「相等」,加码照绿。引擎提货
|
|
178
|
+
时要留一张以码为键的表。
|
|
179
|
+
- **两个面,刻意分开**(发版方裁 3:失败方向 = 语义方向 —— 这是**诊断**键,集外词 / 形坏绝不能把一台健康实例打成不可路由):
|
|
180
|
+
· **`ModelRefusalsWire`**(zod)= wire 上走的、心跳存行里存的:`{ entries: record(非空名 → 非空串), plane?: 非空串 }` + 空信号
|
|
181
|
+
refine —— 只判**形律**,码是开放串;本包版本不认识的码照过(对这个读方是「没说」,存行照存,中心升级本包后同一行读出新码)。
|
|
182
|
+
· **`ModelRefusalsSignal`**(纯型)= `{ entries: Record<名, ModelEntryRefusalCode>, plane?: ModelPlaneRefusalCode }` =
|
|
183
|
+
`readModelRefusals` 的返回型;两只 `z.enum` 仍导出,仍是加码时读方的编译红源。
|
|
184
|
+
- **值只码**:引擎侧拒因事实里的上游 `id` / `provider` / 坏字段键 / 拒因句都不上 wire —— `entry_invalid` 行是没人验过的行,
|
|
185
|
+
`id` / `provider` 可能是坏行原样;句子是散文不是契约;中心要的就是「哪个名、什么码」。
|
|
186
|
+
- **`plane` 单码**(发版方裁 2):一个候选恰一个组拒判决 —— 引擎按固定序判模型面不变量、首个不成立即拒;Runner 换装步的拒
|
|
187
|
+
没有码,不在本信号射程。**`entries` 与 `plane` 描述同一份最新候选目录**(发版方裁 6):`plane` 在场时即那份被拒的候选
|
|
188
|
+
(逐条拒因通常就是它没有缺省的原因);组拒期间引擎自己的拒因表不换代、选择通道仍按上一代拒因执行,所以此时 `entries` 读作
|
|
189
|
+
「这份目录为什么落不了地」,不是「此刻点这些名会被拒」。
|
|
190
|
+
- **在场律同 `RestartSignal`**:在场 ⇔ 至少一条**可报**的拒(名能出门的条目拒,或一个组拒);空信号(`entries` 空且无 `plane`)
|
|
191
|
+
被形律拒 ——「没有」只有一种拼法(键缺席);键内「没有条目拒」拼作 `entries: {}`(键必在)—— 每层各一种拼法。
|
|
192
|
+
- **`readModelRefusals(body)`** = **一只读口两出口**:传 `/health` 体或已 parse 的公告都行(只读 `modelRefusals` 键)。先按
|
|
193
|
+
`ModelRefusalsWire` 判形(形不合 / 缺席 / 非对象 / 访问器抛 ⇒ `undefined`,不抛),再按两只闭集收窄:集外 entry 丢、集外
|
|
194
|
+
`plane` 丢,丢到空 ⇒ `undefined`(与「没说」同渲)。**只答键、不判版本**(发版方裁 4)。与 `readRestartSignal` 共用一只私有
|
|
195
|
+
读口 `readHealthSignal(health, key, schema)`(`readRestartSignal` 行为逐字不变,6.1.0 的 11 格照绿)。
|
|
196
|
+
- **`InstanceAnnouncement.modelRefusals = ModelRefusalsWire.optional().catch(undefined)`**(`InstanceRegistration` 随 extend 同带):
|
|
197
|
+
集外码照过;形坏(空信号 / 非串值 / `plane` 数组 …)**降为缺席**,不拒整条心跳(与 `restart` 不同 —— 那一键的形仍门住整行;
|
|
198
|
+
发版方采纳);键因此可能以 `undefined` 值在场。中心若要给形坏留痕,自己对请求原体的该字段 `ModelRefusalsWire.safeParse`
|
|
199
|
+
(parse 之前),读法口不替它记。每拍整行重报 ⇒ 不带键的一拍 = 不再有可报的拒,存行整替不合并。**缺席读法 = 读版本,不读
|
|
200
|
+
缺席**(发版方裁 4,不加能力位,与 server 读 core 新键的约定同形):更早的引擎从不发此键,缺席 = **不可知**;读方看公告上已有的
|
|
201
|
+
`version`,≥ 首发此键的引擎版本(server 7.103.0,以 server 发布帖为准)才把缺席读成「没有可报的拒」。
|
|
202
|
+
- `/health` 一侧 **`InstanceHealth` 形不动**(passthrough,`modelRefusals` 不入类型;类型化 = 宽读改严判 = major,与
|
|
203
|
+
`restart` 同一笔 7.0.0 欠账)—— `/health` 上的形律与收窄都由 `readModelRefusals` 执法。
|
|
204
|
+
|
|
205
|
+
### S-768 · 名筛两只谓词:`isDisclosableIdentity`(B3,原私有改名导出)+ `isReportableRefusalName`(entries 键)(additive)
|
|
206
|
+
|
|
207
|
+
`src/config-fns.ts` 私有 `isIdentityString(v): v is string` 改名导出为 `isDisclosableIdentity(v: string): boolean`(发版方裁 5:
|
|
208
|
+
留,server 阶段 ② import 它、不复制);判据不变(`redactSecrets(v) === v`;非字符串答 false、不抛 —— 验修轮 N5)。它是「用户写的
|
|
209
|
+
身份串(条目 `name` / `id`)能否原样出门」的**唯一**判据 —— S-712 被丢条目身份今天就由它执法(秘密形 ⇒ 按无名处置:不带,而不是
|
|
210
|
+
打码)。**此前引擎一侧没有这只谓词**(它读的是身份键的缺席;它自己的另一只脱敏器是另一套探测):不导出,引擎只能手抄一行判据或
|
|
211
|
+
改用另一只探测器 —— 同一条律两个判官。签名**刻意不是**类型谓词(对抗腿 F5:谓词形 `v is string` 对一些字符串答 false,调用方
|
|
212
|
+
否定分支里一个 `string` 被收窄成 `never`,tsc 实红);「是不是字符串」由调用点判(`readEntryIdentity` 两处 `typeof … === "string" &&`)。
|
|
213
|
+
|
|
214
|
+
- **`isReportableRefusalName(name: string): boolean`**(验修轮 N1)=「一个条目名能不能当 `entries` 的键」的**唯一**判据,
|
|
215
|
+
引擎铸键前只调这一只:① wire schema **自己载得住**这个键(就地拿 `ModelRefusalsWire` 判:空串不过键规则;`__proto__` 被 zod
|
|
216
|
+
的 record 静默丢掉 —— JSON 解析出的自有属性也丢 —— 载不住;不写名单)∧ ② `isDisclosableIdentity`。首稿把 `__proto__` 写成
|
|
217
|
+
文字里的第二条律、而 `isDisclosableIdentity("__proto__")` 答 true(验修轮实测)—— 收成一只。住在 `config-fns.ts`:`fleet.ts`
|
|
218
|
+
被 `types.ts` 引,它再引 `config-fns.ts` 会成环(三种加载序实测无环)。`__proto__` 的丢法另有 characterization 钉。
|
|
219
|
+
- **schema 刻意不复判秘密**:wire 的键只判非空,不跑本谓词 —— 秘密探测器是会变宽的启发式,执法在铸点(引擎);中心展示前可以
|
|
220
|
+
再过一次同一只谓词(丢,不拒)。
|
|
221
|
+
|
|
222
|
+
### 仓内两件(发版方追加,不影响发布物的契约面)
|
|
223
|
+
|
|
224
|
+
- **开发期引擎钉 `@sema-agent/core` ^2.1.0(实装 2.1.0)→ `7.33.1` 精确**(devDependencies,与 server 同钉):上游漂移门 ②③
|
|
225
|
+
(`test/upstream-drift.test.ts`)与安全合并表词表门(`test/safety-merge-spec.test.ts` 读 `SAFETY_AXIS_VOCABULARY`)此前在对 core 2.1.0
|
|
226
|
+
(npmjs 发布于 2026-07-29,其后 core 已走到 7.33.1)做「漂移」检查 —— 门是瞎的。**提货当时读数**:门 ③ `PROJECT_ID_REGEX` 三格与安全合并表两格在 7.33.1
|
|
227
|
+
上绿;**门 ② 两格红** —— 原文「ModelRole 声明里有不是字符串字面量的成分(`typeofMODEL_ROLES[number]`)」:core 7.33.1
|
|
228
|
+
`dist/core/model-seat.d.ts:42` 把 `ModelRole` 写成 `(typeof MODEL_ROLES)[number]`,门按设计 fail-loud(「上游改了导出形,修这条
|
|
229
|
+
查找,别删门」)。**发版方裁后落地(阶段 ①c)**:门 ② 的上游词表改读 core **运行期** `MODEL_ROLES`(dev-only import,与门 ③ /
|
|
230
|
+
安全合并表门同形),删 `.d.ts` 字面量解析腿及其专属变异格(读实现不读文本,C-R24);查找改后门按设计报陈旧豁免 ——
|
|
231
|
+
「引擎 ModelRole:登记表里有已经不存在的差 —— 陈旧豁免,删掉那几行: expected [ 'consolidate' ] to deeply equal []」(门 ② 两格
|
|
232
|
+
1 红 / 1 绿)—— 删 `consolidate: ours-only` 登记行(core 7.33.1 已含该词,原理由句自己写明「随开发期引擎升版复核本行」)后 2 / 2 绿;
|
|
233
|
+
`advisor: upstream-only` 留。core 7.33.1 `MODEL_ROLES` = default / summarize / subagent / team / synthesize / advisor / verifier /
|
|
234
|
+
classifier / consolidate。
|
|
235
|
+
- **README「zod 单实例」订正为两实例现状**:server / CLI 在 zod 4、config-center 产品仍在 zod 3,zod-4 宿主树里本包的 zod 3 是
|
|
236
|
+
嵌套的第二实例;跨缝传纯数据、不混两实例的 schema 对象。升 zod 4 归一是本包的一个 major(S-777);本版不动依赖与 peer 段。
|
|
237
|
+
|
|
238
|
+
### 三问 · 消费方 · 红先 · 复审
|
|
239
|
+
|
|
240
|
+
- **谁需要** = web-admin KL-35(实例页逐台列「这台拒了哪些条目、为什么」,[8445])+ server 7.103.0(S-768 阶段 ②:一只取值口
|
|
241
|
+
两出口 —— `/health.modelRefusals` 与心跳 `modelRefusals`;两张码表改 import 本包闭集(并留一张以码为键的表);铸键前调
|
|
242
|
+
`isReportableRefusalName`,日志 / 拒因句里要不要写出一个名字问 `isDisclosableIdentity`;组拒码当数据带出 —— 引擎的组拒记录
|
|
243
|
+
只存不变量 id,`model-route-endpoint` 一个 id 两个码,按判决那刻的码)。
|
|
244
|
+
- **谁受伤** = 无(新导出 + 新可选键;`InstanceHealth` 一字不动;`readRestartSignal` / 被丢条目身份行为不变;旧中心收到带
|
|
245
|
+
`modelRefusals` 的心跳:`InstanceAnnouncement` 是默认 strip 的 `z.object`,键被剥,行为同今)。**风险如实登记**:
|
|
246
|
+
① ~~心跳里 `modelRefusals` 形不合 ⇒ 整条心跳 400~~ —— 发版方裁 3 后**不再成立**:形坏降为缺席、集外码照过(对抗腿按 web-admin
|
|
247
|
+
心跳路由读法实测的「连拒两窗后实例掉出可路由集」这一形,本版已无从触发);② **版本错位**:引擎日后发一个新码(本包下一 minor
|
|
248
|
+
尾插),装着旧版本包的中心**照收心跳**,只是新码在它那里「没说」,直到中心升级本包;部署序仍建议中心先提货,但不再是补偿
|
|
249
|
+
手段;③ 判档:6.1.0 收下(剥掉)的任何带 `modelRefusals` 的心跳体,6.2.0 照收 —— 对抗腿 F4 的判档张力(strip 对象加类型化
|
|
250
|
+
可选键 = 宽读改严判)随之消解。
|
|
251
|
+
- **补偿** = 无旋钮(additive,无需补偿)。
|
|
252
|
+
- **消费方** = server(阶段 ②,如上);web-admin(收键、存 wire 形、经 `readModelRefusals` 渲;缺席按引擎 `version` 分「没有可报的
|
|
253
|
+
拒 / 不可知」;按码穷举 `switch`;能力显式表态制 @点名);cli n/a;core n/a。
|
|
254
|
+
- **红先**:`test/model-refusals-6.2.0.test.ts` 对 6.1.0 源 **18 红 / 4 绿**(绿 = additive 不带键控制格、restart / ready 负控、
|
|
255
|
+
`InstanceHealth` 形不动、类型面格(`expectTypeOf`,由 tsc 执法、运行期空跑)),tsc 36 错全在本文件;改源后 22 / 22、tsc 0。
|
|
256
|
+
B3 两格对 `0d4227d` **2 红** → 24 / 24。复审修复与阶段 ①b 裁定的红先见下(文件现 36 格)。
|
|
257
|
+
- **对抗复审**(独立 opus 腿一轮,只读、不喂结论、沙箱逐条复现;codex 当日额度墙未投):9 条。**真修**:F5(谓词签名,
|
|
258
|
+
tsc 红先 2 错 → 0)· F8(「双侧编译红」订正)· F2 的「秘密形名下的拒 ⇒ 整键缺席」(在场律改写为「可报的拒」)· F9(公开 d.ts
|
|
259
|
+
里指向私有函数的 JSDoc、B3 组标题里本仓验不了的断言)· F3 附带的 `__proto__` 键(登记边界 + characterization 钉)。**发版方裁
|
|
260
|
+
后落地**:F1(组拒码收整族,裁 1)· F3(wire 开放串 + 读方收窄 + 心跳上形坏降为缺席,裁 3)· F2 其余(读版本、不加能力位,
|
|
261
|
+
裁 4)· F4(随裁 3 消解)。**驳 / 不改**:F6(读口里按谓词丢秘密形键:执法留在铸点)· F7(两层「没有」各一种拼法;`ModelRefusalsWire`
|
|
262
|
+
是 `ZodEffects`、无 `.shape` / `.extend`,与 `RestartSignal` 的 `ZodObject` 形不同 —— 登记)· F9 命名(`readModelRefusals` 与
|
|
263
|
+
`readRestartSignal` 不对称:名按派车书;与引擎现有同名常量 / 型撞名:引擎提货时换成 import,阶段 ② 的事)。
|
|
264
|
+
- **验修轮**(同一 opus 腿,只核 `10f4d85..0450c94`,不开新检测面):F5 / F8 修复成立,相对 `10f4d85` 无运行期回归;新 6 条全处置:
|
|
265
|
+
**N1**(`__proto__` 成了文字里的第二条律 ⇒ 收成一只 `isReportableRefusalName`)· **N5**(`isDisclosableIdentity` 非字符串由答
|
|
266
|
+
false 变抛 TypeError ⇒ 回到答 false)· **N2 / N3**(组拒期间 `entries` 的读法;无码组拒 —— 裁 1 后已无此类)补进接入档 ·
|
|
267
|
+
**N4 / N6**(旧签名句、llms-full「= 无拒」、格数)订正。两格红先:对 `0450c94` **2 红 / 26 绿** → 28 / 28。
|
|
268
|
+
- **阶段 ①b · 发版方六裁落地**(每裁一格红先):裁 1 组拒码四码(`0d3a48d` 1 红 → 绿)· 裁 2 `plane` 单码 + 注(`4f924fb` 1 红 →
|
|
269
|
+
绿)· 裁 3 wire 开放串 + 读方收窄(`c36745c` 12 红 / tsc 3 错 → 绿;本体两格 = 集外码心跳 `success`、收窄读数,其余 10 格是
|
|
270
|
+
形律格改指 `ModelRefusalsWire`)· 裁 3 附 心跳上形坏降为缺席(`5289ad9` 3 红 → 绿)· 裁 4 读版本不读缺席(`1014997` 1 红 → 绿,
|
|
271
|
+
另一格「读口不判版本」是控制格,生来绿)· 裁 5 `isDisclosableIdentity` 留(无改动,既有格照绿)· 裁 6 同一份候选目录(`a53ab22`
|
|
272
|
+
1 红 → 绿)。阶段 ①b 的改动本身未再过复审(复审终止律:一轮 + 一次验修已用完)。
|
|
273
|
+
|
|
9
274
|
## 6.1.0 — 2026-09-25 — 四件 additive:`entry-dropped` 身份(S-712)· 中心凭证信封 + 三态判别器(S-698)· `RestartSignal` 一处定义(S-741)· 中心凭证两键名 + 二选一判官(S-742);`redactSecrets` 私钥 PEM 串内任意位置且判在替换前(6.0.0 起三面既有盲点顺带关)
|
|
10
275
|
|
|
11
276
|
> 本节四件全 additive(新可选键 / 新导出 / 新文件),零删零改名;版号与节标题由发版方写。判档口径见下方「发版纪律」:
|
package/README.md
CHANGED
|
@@ -35,9 +35,18 @@ server (the workers), and the client (desktop/CLI) seams. No two copies of the s
|
|
|
35
35
|
(≤`0.19.0` 发在旧名 `@sema-agent/registry-core` 下,冻结)。
|
|
36
36
|
|
|
37
37
|
The package is **pure**: zero `next`/`react`/server imports, zero DB drivers. Its only runtime dependency is
|
|
38
|
-
`zod` (a `dependency` pinned `^3.24.0
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
`zod` (a `dependency` pinned `^3.24.0`, also declared as a `peerDependency` with the same range). `npm ls --prod`
|
|
39
|
+
therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
40
|
+
|
|
41
|
+
> **zod instances today: two, not one** (corrected in 6.2.0 — this section used to promise a single shared instance).
|
|
42
|
+
> The range was chosen so every host would dedup to one zod, but the hosts have since moved apart: `sema-server` and the
|
|
43
|
+
> CLI depend on zod 4 (`^4.x`), the config-center product (`sema-web-admin`) is still on zod 3. In a zod-4 host the
|
|
44
|
+
> package manager installs this package's own zod 3 NESTED under it — the server tree today has zod 4 at the top level
|
|
45
|
+
> and zod 3 at `node_modules/@sema-agent/settings-schema/node_modules/zod`. There, this package's schemas and the host's
|
|
46
|
+
> are two zod instances: cross the seam with plain data (parse with the schemas / functions this package exports), and
|
|
47
|
+
> never mix schema objects of the two instances (`instanceof ZodError`, composing a host schema with one of ours). Only
|
|
48
|
+
> a zod-3 host (today the config-center product) still dedups to one instance. Moving this package to zod 4 is a major
|
|
49
|
+
> of its own (S-777); 6.2.0 does not touch the dependency or peer range.
|
|
41
50
|
|
|
42
51
|
## What's in it
|
|
43
52
|
|
|
@@ -131,19 +140,27 @@ sound). `npm ls --prod` therefore shows only `zod`. Node built-ins only (`node:c
|
|
|
131
140
|
limits/workers) stay fail-loud even in tolerant mode. 0.19.0 adds a FAIL-LOUD sweep on the same read:
|
|
132
141
|
a `config.d/*.json` the local set will never read (a name that is not a domain, or a non-portable one)
|
|
133
142
|
and any top-level key a domain schema strips are both NAMED through `onWarning`
|
|
134
|
-
(`unread-config-file` / `unknown-keys-dropped`) instead of vanishing silently
|
|
143
|
+
(`unread-config-file` / `unknown-keys-dropped`) instead of vanishing silently (**6.3.0**: every warning coordinate —
|
|
144
|
+
unknown keys included — leaves through `redactSecrets`, so a secret-shaped key segment reads `[redacted-secret]`;
|
|
145
|
+
`droppedKeys` / `carriedUnknownKeys` return the same outgoing form). **2.0.0** adds per-ENTRY
|
|
135
146
|
tolerance on the same read path: one malformed catalog entry is dropped on its own (named through
|
|
136
147
|
`onWarning` as `entry-dropped`) and its siblings survive, instead of the whole domain falling to
|
|
137
148
|
its schema default. It is the SAME `DOMAIN_READ_FALLBACK` table that decides where this applies — gate
|
|
138
149
|
domains keep failing loud, entry by entry included. **S-712** (additive) lets each `entry-dropped` carry an
|
|
139
150
|
optional `identity?: { name?, id?, enabled? }` read from the dropped entry's OWN text (`DroppedEntryIdentity`):
|
|
140
151
|
a string `name` / `id` and a boolean `enabled`, own data properties only, each key present only when it
|
|
141
|
-
reads with that exact type and `
|
|
142
|
-
|
|
143
|
-
|
|
152
|
+
reads with that exact type and `isDisclosableIdentity` says it may ride out (not guessed, not coerced, not
|
|
153
|
+
masked — a masked string identifies nothing). **6.3.0**: an identity is an IDENTIFIER, not free text — it must
|
|
154
|
+
match the closed allowlist `DISCLOSABLE_IDENTITY_RE` (`^[A-Za-z0-9._:/@+-]{1,256}$`; `%`, `=`, whitespace, quotes,
|
|
155
|
+
backslash, control / zero-width / non-ASCII characters and anything over 256 chars are out) AND be left unchanged
|
|
156
|
+
by `redactSecrets`, the SAME redactor the coordinates and the error text go through (the allowlist still admits
|
|
157
|
+
`sk-…` / `ghp_…` shapes) AND not be a URL carrying userinfo (`postgres://svc:pw@h` — the same judgement `NON_SECRET_URL`
|
|
158
|
+
refuses on). 6.1.0–6.2.0 judged by the redactor alone and let a `key=`-prefixed, percent-encoded
|
|
159
|
+
private-key PEM ride out whole (codex review [high]). Nothing readable ⇒ no `identity` key (absent = "not safely
|
|
160
|
+
readable", whichever the reason). A consumer learns WHICH entry was
|
|
144
161
|
dropped without re-reading the source document by coordinate (the document it holds may not be the one this
|
|
145
162
|
read saw). `identity` is part of the warning projection, so the conformance gate compares it (corpus
|
|
146
|
-
|
|
163
|
+
cases `c40` / `c41` pin the laws JSON can express; the historical normalizer strips it, since older versions cannot
|
|
147
164
|
mint it — `ConformanceNormalizer.warning?`). **3.0.0** re-casts every coordinate these warnings
|
|
148
165
|
carry (`entry-dropped.path`, `unknown-keys-*.keys`) from a dot-joined STRING to a structured
|
|
149
166
|
`ConfigPath = readonly (string | number)[]` — a string segment is an object key (verbatim, `.` included),
|
|
@@ -176,7 +193,21 @@ sound). `npm ls --prod` therefore shows only `zod`. Node built-ins only (`node:c
|
|
|
176
193
|
read stays lenient) and `readRestartSignal(health)` reads `health.restart` against the one shape (`undefined` on
|
|
177
194
|
any mismatch, never throws). Presence is the signal (present ⇔ restart required; no `required` boolean inside).
|
|
178
195
|
A heartbeat re-announces the full row, so one without `restart` means "no longer required". Typing
|
|
179
|
-
`InstanceHealth.restart` strictly is left for the next major.
|
|
196
|
+
`InstanceHealth.restart` strictly is left for the next major. **S-768** adds the model-refusals signal the same
|
|
197
|
+
way (one mint point, two exits, ONE reader): the wire face `ModelRefusalsWire` (`{ entries: { <catalog entry name>:
|
|
198
|
+
<code> }, plane?: <code> }` with codes as open strings — name → code is the whole wire, no upstream id / provider /
|
|
199
|
+
field keys / prose) rides the heartbeat as `InstanceAnnouncement.modelRefusals?` and /health (`InstanceHealth` again
|
|
200
|
+
unchanged); `readModelRefusals(body)` reads either exit and narrows to `ModelRefusalsSignal` — the codes are CLOSED
|
|
201
|
+
sets owned here, `MODEL_ENTRY_REFUSAL_CODES` (`config.model_no_brain`, `config.model_entry_invalid`) and
|
|
202
|
+
`MODEL_PLANE_REFUSAL_CODES` (`config.default_model_missing`, `config.model_refused_name_minted`,
|
|
203
|
+
`config.model_route_endpoint_missing`, `config.gateway_fallback_without_primary`), and a code this package version
|
|
204
|
+
does not know is dropped ("not said"). It is a diagnostic key, so its failure direction is "not said": an unknown
|
|
205
|
+
code or a shape violation on the heartbeat never fails the announcement (the key drops to absent). Present ⇔ at
|
|
206
|
+
least one reportable refusal; absence is read by the announced `version` — an engine that predates the key never
|
|
207
|
+
sends it (UNKNOWN, not "none"). A name that cannot ride out is left out by the engine, judged by ONE predicate,
|
|
208
|
+
`isReportableRefusalName` (the wire can carry the key — `__proto__` it cannot — and `isDisclosableIdentity`, the
|
|
209
|
+
rule the dropped-entry identity on read warnings already uses: the `DISCLOSABLE_IDENTITY_RE` allowlist ∧ not
|
|
210
|
+
secret-shaped, 6.3.0).
|
|
180
211
|
- **Declared-but-unvalidated keys** (`PARSE_TRANSPARENT_KEYS`, 2.0.0): the ONE registry of keys this package
|
|
181
212
|
carries verbatim WITHOUT checking their shape, each row naming who does check it (the engine's ingest side,
|
|
182
213
|
a consumer's projection, or the key's own guard). Today: `models[].extraBody`, `models[].compat` (1.10.0),
|
package/conformance/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.3.0
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
{
|
|
2
|
+
"domains": {
|
|
3
|
+
"outcome": "effective",
|
|
4
|
+
"effective": {
|
|
5
|
+
"collab": {
|
|
6
|
+
"templates": []
|
|
7
|
+
},
|
|
8
|
+
"entitlement": {
|
|
9
|
+
"budgetGroups": [],
|
|
10
|
+
"groupBindings": [],
|
|
11
|
+
"subjects": [],
|
|
12
|
+
"tiers": []
|
|
13
|
+
},
|
|
14
|
+
"execution": {
|
|
15
|
+
"allowedLanes": [
|
|
16
|
+
"host"
|
|
17
|
+
],
|
|
18
|
+
"required": false
|
|
19
|
+
},
|
|
20
|
+
"governance": {},
|
|
21
|
+
"hosts": {
|
|
22
|
+
"hosts": []
|
|
23
|
+
},
|
|
24
|
+
"limits": {},
|
|
25
|
+
"mcp": {
|
|
26
|
+
"servers": []
|
|
27
|
+
},
|
|
28
|
+
"models": {
|
|
29
|
+
"models": [
|
|
30
|
+
{
|
|
31
|
+
"api": "openai-completions",
|
|
32
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
33
|
+
"enabled": true,
|
|
34
|
+
"id": "keep-id",
|
|
35
|
+
"name": "keep",
|
|
36
|
+
"provider": "openai",
|
|
37
|
+
"quotaWeight": 1,
|
|
38
|
+
"reasoning": false,
|
|
39
|
+
"tier": "standard",
|
|
40
|
+
"vision": false
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"roles": {
|
|
44
|
+
"summarize": {
|
|
45
|
+
"model": "keep"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"tierGroups": []
|
|
49
|
+
},
|
|
50
|
+
"plugins": {
|
|
51
|
+
"plugins": []
|
|
52
|
+
},
|
|
53
|
+
"projects": {
|
|
54
|
+
"projects": {}
|
|
55
|
+
},
|
|
56
|
+
"prompts": {
|
|
57
|
+
"bindings": []
|
|
58
|
+
},
|
|
59
|
+
"rosters": {
|
|
60
|
+
"rosters": []
|
|
61
|
+
},
|
|
62
|
+
"runtime": {},
|
|
63
|
+
"scenarios": {
|
|
64
|
+
"scenarios": []
|
|
65
|
+
},
|
|
66
|
+
"skills": {
|
|
67
|
+
"skills": []
|
|
68
|
+
},
|
|
69
|
+
"systems": {
|
|
70
|
+
"systems": []
|
|
71
|
+
},
|
|
72
|
+
"workers": {
|
|
73
|
+
"workers": []
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"warnings": [
|
|
77
|
+
{
|
|
78
|
+
"domain": "models",
|
|
79
|
+
"identity": {
|
|
80
|
+
"name": "encoded-pem"
|
|
81
|
+
},
|
|
82
|
+
"kind": "entry-dropped",
|
|
83
|
+
"path": [
|
|
84
|
+
"models",
|
|
85
|
+
1
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"domain": "models",
|
|
90
|
+
"identity": {
|
|
91
|
+
"id": "in-charset-secret-name-id"
|
|
92
|
+
},
|
|
93
|
+
"kind": "entry-dropped",
|
|
94
|
+
"path": [
|
|
95
|
+
"models",
|
|
96
|
+
2
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"domain": "models",
|
|
101
|
+
"identity": {
|
|
102
|
+
"id": "one-dot-jwt-name-id"
|
|
103
|
+
},
|
|
104
|
+
"kind": "entry-dropped",
|
|
105
|
+
"path": [
|
|
106
|
+
"models",
|
|
107
|
+
3
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"domain": "models",
|
|
112
|
+
"identity": {
|
|
113
|
+
"id": "accounts/fireworks/models/llama-v3p1-8b-instruct",
|
|
114
|
+
"name": "gpt-4o-2024-08-06"
|
|
115
|
+
},
|
|
116
|
+
"kind": "entry-dropped",
|
|
117
|
+
"path": [
|
|
118
|
+
"models",
|
|
119
|
+
4
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"domain": "models",
|
|
124
|
+
"identity": {
|
|
125
|
+
"id": "claude-3-5-sonnet@20240620:free+high",
|
|
126
|
+
"name": "o3-mini"
|
|
127
|
+
},
|
|
128
|
+
"kind": "entry-dropped",
|
|
129
|
+
"path": [
|
|
130
|
+
"models",
|
|
131
|
+
5
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"domain": "models",
|
|
136
|
+
"identity": {
|
|
137
|
+
"name": "deep-encoded-pem"
|
|
138
|
+
},
|
|
139
|
+
"kind": "entry-dropped",
|
|
140
|
+
"path": [
|
|
141
|
+
"models",
|
|
142
|
+
6
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"domain": "models",
|
|
147
|
+
"kind": "entry-dropped",
|
|
148
|
+
"path": [
|
|
149
|
+
"roles",
|
|
150
|
+
"[redacted-secret]"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"metadata": {
|
|
155
|
+
"version": 12,
|
|
156
|
+
"updatedAt": "2026-09-27T00:00:00.000Z"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"wire": {
|
|
160
|
+
"derive": true
|
|
161
|
+
},
|
|
162
|
+
"localRoot": {
|
|
163
|
+
"derive": true,
|
|
164
|
+
"localVersion": 1537928059550602
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"version": 12,
|
|
4
|
+
"updatedAt": "2026-09-27T00:00:00.000Z"
|
|
5
|
+
},
|
|
6
|
+
"domains": {
|
|
7
|
+
"models": {
|
|
8
|
+
"models": [
|
|
9
|
+
{
|
|
10
|
+
"name": "keep",
|
|
11
|
+
"provider": "openai",
|
|
12
|
+
"id": "keep-id",
|
|
13
|
+
"apiKeyEnv": "OPENAI_API_KEY"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "encoded-pem",
|
|
17
|
+
"provider": "openai",
|
|
18
|
+
"id": "key=-----BEGIN%20PRIVATE%20KEY-----%0AMC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000%0A-----END%20PRIVATE%20KEY-----%0A",
|
|
19
|
+
"apiKeyEnv": "not an env name"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "sk-FAKE-S621-CORPUS-0000000000",
|
|
23
|
+
"provider": "openai",
|
|
24
|
+
"id": "in-charset-secret-name-id",
|
|
25
|
+
"apiKeyEnv": "not an env name"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "ops@eyJFAKES621CORPUSHDR0.tail",
|
|
29
|
+
"provider": "openai",
|
|
30
|
+
"id": "one-dot-jwt-name-id",
|
|
31
|
+
"apiKeyEnv": "not an env name"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "gpt-4o-2024-08-06",
|
|
35
|
+
"provider": "openai",
|
|
36
|
+
"id": "accounts/fireworks/models/llama-v3p1-8b-instruct",
|
|
37
|
+
"apiKeyEnv": "not an env name"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "o3-mini",
|
|
41
|
+
"provider": "openai",
|
|
42
|
+
"id": "claude-3-5-sonnet@20240620:free+high",
|
|
43
|
+
"apiKeyEnv": "not an env name"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "deep-encoded-pem",
|
|
47
|
+
"provider": "openai",
|
|
48
|
+
"id": "key=-----BEGIN%25252520PRIVATE%25252520KEY-----%2525250AMC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000%2525250A-----END%25252520PRIVATE%25252520KEY-----%2525250A",
|
|
49
|
+
"apiKeyEnv": "not an env name"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"roles": {
|
|
53
|
+
"summarize": {
|
|
54
|
+
"model": "keep"
|
|
55
|
+
},
|
|
56
|
+
"key=-----BEGIN%20PRIVATE%20KEY-----%0AMC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000%0A-----END%20PRIVATE%20KEY-----%0A": {
|
|
57
|
+
"model": "keep"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contributor": "contract",
|
|
3
|
+
"claim": "6.3.0(codex gpt-6-astra 对 v6.1.0 复审 [high] 回填 D-4):`key=` + 百分号编码的 PEM 私钥 —— 原文上 PEM header 带 `%20` 看不见、`containsSecretToken` 的行首锚被 `key=` 前缀打败 —— 同时作被丢条目的 `id`(身份面)与 `roles` 的坏键(坐标面 + zod error 的 received / message 面)。身份面走白名单闭形 `DISCLOSABLE_IDENTITY_RE`(`%` / `=` 不在集内)⇒ 只剩 name(四重编码的同一把私钥同样只剩 name —— 身份面靠白名单闭形,与编码深度无关;6.3.0 起自由文本面也在解码界上 fail-closed);坐标 / error 走 `redactSecrets`,整串判套在归一化形(原文 + 探测器同界的解码层)上 ⇒ 坐标段整段扣留、error 不带私钥。另压白名单的两道边:字符集**之内**的密钥形 name(`sk-` 形;`ops@` 后接单点 `eyJ…` 形 —— 探测器判阴、坐标脱敏器会改)⇒ 不带 name,身份的否决判据与坐标脱敏同一只;正控:带 `/` `@` `:` `+` 的真实形上游 id 与合法 name 逐字带出。",
|
|
4
|
+
"firstRedOn": [],
|
|
5
|
+
"secrets": [
|
|
6
|
+
"MC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000",
|
|
7
|
+
"BEGIN%20PRIVATE%20KEY",
|
|
8
|
+
"sk-FAKE-S621-CORPUS-0000000000",
|
|
9
|
+
"eyJFAKES621CORPUSHDR0"
|
|
10
|
+
],
|
|
11
|
+
"note": "车PE(2026-09-27)红先:加本条时 6.2.0 读腿在 domains 腿上报告警差(identity.id 与 roles 坐标段原样带出编码私钥),秘密扫描同时命中私钥体行。变异门 `test/conformance-mutation.test.ts` 为 isDisclosableIdentity / redactSecrets 登记的靶(逐靶实测,车PE 收车档):去掉白名单(退回「脱敏器原样放行即可披露」)⇒ cli01 / cli02 红(本条四重编码那条原先也红,借的是「自由文本面只读三层解码」那个缺口;6.3.0 B 在解码界上 fail-closed 后,一重 / 四重编码两条都由修后的 redactSecrets 否决,身份面两道闸各自扣下,本条对该靶不再红 —— 已按原 🚧 注翻过);白名单之内的否决去掉(`redactSecrets(v) === v` → `true`)⇒ `sk-` 形 name 带出红;否决退回裸探测器(→ `!containsSecretToken(v)`)⇒ `ops@eyJ…` 形 name 带出红;归一化形退回只有原文(`secretScanForms(text)` → `[text]`)⇒ roles 坐标段与 error 带出编码私钥红。firstRedOn 为空:`conformance/historical/` 的阶梯证据(2.1.0 及更早)真跑于本条入库之前,没有本条的读数,那一栏只收 historical 真跑;本条对 6.2.0 的红由红先提交实测(见车PE 收车档),不手填进 firstRedOn。"
|
|
12
|
+
}
|