@sema-agent/settings-schema 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/config-fns.d.ts +2 -2
- package/dist/config-fns.js +3 -3
- package/dist/file-store.d.ts +2 -2
- package/dist/file-store.js +4 -4
- package/dist/safety-merge-spec.d.ts +3 -3
- package/dist/safety-merge-spec.js +3 -3
- package/dist/types.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
> `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
|
|
7
7
|
> 包名是**历史记录**,如实保留不追改。
|
|
8
8
|
|
|
9
|
+
## 1.4.1 — 2026-09-02 — 文案 patch(cli [6037]/[6038] 请件)
|
|
10
|
+
|
|
11
|
+
- 复审出处注词族全清:src 12 处+d.ts 同族(`codex …`→`对抗复审 …`,语义零损)——1.4.0 件⑦「52→1」的三族 regex 未含此词族=漏计根因;npm 包注释=对外文案面,壳 bundle hygiene 禁表命中即发布腿红。
|
|
12
|
+
|
|
9
13
|
## 1.4.0 — 2026-09-02 — settings-schema 批八件(DEBTS-server S-59;新键=minor 按本批成文政策)
|
|
10
14
|
|
|
11
15
|
七件 = 两把新可选键(①⑥,additive)+ 三件文案/论证面(②③④)+ 一件文档重写(⑤)+ 一件发布物中性化(⑦)。
|
package/dist/config-fns.d.ts
CHANGED
|
@@ -203,7 +203,7 @@ export type EffectiveReadWarning = {
|
|
|
203
203
|
* (新 center 的新键不该炸老消费方),所以修法不是收紧成 `.strict()`,而是让它**响亮**:
|
|
204
204
|
* {@link buildEffective} 每域解析成功后调用本函数,非空即发 `"unknown-keys-dropped"` 警告。
|
|
205
205
|
*
|
|
206
|
-
* **嵌套也扫**(
|
|
206
|
+
* **嵌套也扫**(对抗复审 复审 F2 真 finding):`{infraCostRates:{toolCalMicroUsd:10}}` 这种**拼错的嵌套
|
|
207
207
|
* 键**在顶层看不见(`infraCostRates` 键还在),只看顶层等于对计费面的错键完全失明。故按 raw↔parsed
|
|
208
208
|
* 的键集逐层对比(对象逐键、数组同长时逐下标),不依赖任何 zod 内省。
|
|
209
209
|
*
|
|
@@ -227,7 +227,7 @@ export declare function carriedUnknownKeys(domain: DomainName, parsed: unknown):
|
|
|
227
227
|
/**
|
|
228
228
|
* 一域的 parse + **静默剥键/静默承运两条告警**(0.19.0)。`buildEffective` 与 `FileConfigStore.getDomain`
|
|
229
229
|
* 共用这一个判据 —— 此前只有 effective 那条路径有门,而 `getDomain` 是 `exportBundle` 的读法
|
|
230
|
-
* (
|
|
230
|
+
* (对抗复审 复审 round2 F3:导出一份被剥了键的域再导入 = 持久的配置丢失,而且全程无声)。
|
|
231
231
|
*/
|
|
232
232
|
export declare function parseDomainLoud<K extends DomainName>(domain: K, raw: unknown, onWarning?: (w: EffectiveReadWarning) => void): DomainConfig[K];
|
|
233
233
|
/** Per-domain READ-corruption policy (0.8.1) — what buildEffective does when a stored domain no longer parses.
|
package/dist/config-fns.js
CHANGED
|
@@ -182,7 +182,7 @@ function collectDropped(raw, parsed, prefix, depth, spared, out) {
|
|
|
182
182
|
* (新 center 的新键不该炸老消费方),所以修法不是收紧成 `.strict()`,而是让它**响亮**:
|
|
183
183
|
* {@link buildEffective} 每域解析成功后调用本函数,非空即发 `"unknown-keys-dropped"` 警告。
|
|
184
184
|
*
|
|
185
|
-
* **嵌套也扫**(
|
|
185
|
+
* **嵌套也扫**(对抗复审 复审 F2 真 finding):`{infraCostRates:{toolCalMicroUsd:10}}` 这种**拼错的嵌套
|
|
186
186
|
* 键**在顶层看不见(`infraCostRates` 键还在),只看顶层等于对计费面的错键完全失明。故按 raw↔parsed
|
|
187
187
|
* 的键集逐层对比(对象逐键、数组同长时逐下标),不依赖任何 zod 内省。
|
|
188
188
|
*
|
|
@@ -208,7 +208,7 @@ export function droppedKeys(domain, raw, parsed) {
|
|
|
208
208
|
* open-world 域(`.passthrough()`)的**已知键表**:路径 → 该层已知键(`""` = 顶层)。键集从 schema 的
|
|
209
209
|
* `shape` **单源派生**(手抄一份必然漂),路径表本身是显式的——嵌套只在真有 open-world 子对象的地方登记。
|
|
210
210
|
*
|
|
211
|
-
* 为什么需要它(
|
|
211
|
+
* 为什么需要它(对抗复审 复审 round2 F2):承运解决了「值到不了」,但没解决「键名拼错了没人说」——
|
|
212
212
|
* `toolResultsTtlSec`(多一个 s)会完好穿过本包,而消费方只遍历自己的键表,于是那条天花板悄悄没生效。
|
|
213
213
|
* 承运 + 点名两件一起做:值照送(消费方有权认新键),名照报(拼错的有人说)。
|
|
214
214
|
*
|
|
@@ -247,7 +247,7 @@ export function carriedUnknownKeys(domain, parsed) {
|
|
|
247
247
|
/**
|
|
248
248
|
* 一域的 parse + **静默剥键/静默承运两条告警**(0.19.0)。`buildEffective` 与 `FileConfigStore.getDomain`
|
|
249
249
|
* 共用这一个判据 —— 此前只有 effective 那条路径有门,而 `getDomain` 是 `exportBundle` 的读法
|
|
250
|
-
* (
|
|
250
|
+
* (对抗复审 复审 round2 F3:导出一份被剥了键的域再导入 = 持久的配置丢失,而且全程无声)。
|
|
251
251
|
*/
|
|
252
252
|
export function parseDomainLoud(domain, raw, onWarning) {
|
|
253
253
|
const parsed = parseDomain(domain, raw);
|
package/dist/file-store.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type DomainConfig, type DomainName, type EffectiveConfig } from "./type
|
|
|
5
5
|
* **default**域集 of the bundle helpers (`exportBundle`/`readBundle`). 每个被排除的域必须在
|
|
6
6
|
* {@link NON_PORTABLE_DOMAIN_REASONS} 里有一条成文理由(禁裸排除,#282 件5 附带裁定)。
|
|
7
7
|
*
|
|
8
|
-
* ⚠️ 口径诚实(
|
|
8
|
+
* ⚠️ 口径诚实(对抗复审 复审 round1 F4):这是**默认集与本地读取集,不是 API 级硬边界** —— `exportBundle`
|
|
9
9
|
* / `readBundle` 的 `domains` 参数可显式传任意 `DomainName`(bundle.ts 不拒非便携域)。把非便携域搬进
|
|
10
10
|
* bundle 的那条口子归调用方纪律(以及各自域的导出面),不是本常量能挡住的;要不要把 bundle API 收窄到
|
|
11
11
|
* `PortableDomain` 是一个独立的行为面裁定(#282 未决项,需三问),本批不擅自改。
|
|
@@ -22,7 +22,7 @@ export type PortableDomain = (typeof PORTABLE_DOMAINS)[number];
|
|
|
22
22
|
*
|
|
23
23
|
* `satisfies Record<Exclude<DomainName, PortableDomain>, string>` 让键集在**编译期**双向咬死:新开一个
|
|
24
24
|
* 域却不放进 PORTABLE_DOMAINS ⇒ 这里缺行 ⇒ 编译错;把某域改成便携却忘了删行 ⇒ 多余键 ⇒ 编译错。
|
|
25
|
-
* (
|
|
25
|
+
* (此前是「理由写在注释里」,机器只能做子串匹配——域名出现在文件任何角落都算数,等于没门;对抗复审
|
|
26
26
|
* 复审 round1 F3。)理由要写**为什么它不该被单机/跨部署搬运**,带出处。
|
|
27
27
|
*/
|
|
28
28
|
export declare const NON_PORTABLE_DOMAIN_REASONS: {
|
package/dist/file-store.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* LOCAL FILE LAYOUT (the TOC owns the root, e.g. `~/.ai-agent/` or a project-local `.ai-agent/`):
|
|
8
8
|
* <root>/config.d/<domain>.json for every domain in {@link PORTABLE_DOMAINS} — that constant is the
|
|
9
9
|
* AUTHORITY; do NOT hand-copy the list here (the hand-written enumeration that used to sit
|
|
10
|
-
* on this line silently missed `prompts` for three releases —
|
|
10
|
+
* on this line silently missed `prompts` for three releases — 对抗复审 复审 round2 F5).
|
|
11
11
|
* <root>/.env secret VALUES (KEY=value) — the ONLY file holding secrets; gitignored, never read here.
|
|
12
12
|
*
|
|
13
13
|
* Rules:
|
|
@@ -30,7 +30,7 @@ import { isDomainName } from "./types.js";
|
|
|
30
30
|
* **default**域集 of the bundle helpers (`exportBundle`/`readBundle`). 每个被排除的域必须在
|
|
31
31
|
* {@link NON_PORTABLE_DOMAIN_REASONS} 里有一条成文理由(禁裸排除,#282 件5 附带裁定)。
|
|
32
32
|
*
|
|
33
|
-
* ⚠️ 口径诚实(
|
|
33
|
+
* ⚠️ 口径诚实(对抗复审 复审 round1 F4):这是**默认集与本地读取集,不是 API 级硬边界** —— `exportBundle`
|
|
34
34
|
* / `readBundle` 的 `domains` 参数可显式传任意 `DomainName`(bundle.ts 不拒非便携域)。把非便携域搬进
|
|
35
35
|
* bundle 的那条口子归调用方纪律(以及各自域的导出面),不是本常量能挡住的;要不要把 bundle API 收窄到
|
|
36
36
|
* `PortableDomain` 是一个独立的行为面裁定(#282 未决项,需三问),本批不擅自改。
|
|
@@ -49,7 +49,7 @@ const NON_DOMAIN_CONFIG_FILES = new Set(["remote-exec.json"]);
|
|
|
49
49
|
*
|
|
50
50
|
* `satisfies Record<Exclude<DomainName, PortableDomain>, string>` 让键集在**编译期**双向咬死:新开一个
|
|
51
51
|
* 域却不放进 PORTABLE_DOMAINS ⇒ 这里缺行 ⇒ 编译错;把某域改成便携却忘了删行 ⇒ 多余键 ⇒ 编译错。
|
|
52
|
-
* (
|
|
52
|
+
* (此前是「理由写在注释里」,机器只能做子串匹配——域名出现在文件任何角落都算数,等于没门;对抗复审
|
|
53
53
|
* 复审 round1 F3。)理由要写**为什么它不该被单机/跨部署搬运**,带出处。
|
|
54
54
|
*/
|
|
55
55
|
export const NON_PORTABLE_DOMAIN_REASONS = {
|
|
@@ -185,7 +185,7 @@ export class FileConfigStore {
|
|
|
185
185
|
const raw = await this.#readDomainRaw(domain);
|
|
186
186
|
if (raw === undefined)
|
|
187
187
|
return undefined; // missing/empty file → domain absent (not schema defaults)
|
|
188
|
-
// 0.19.0(
|
|
188
|
+
// 0.19.0(对抗复审 复审 round2 F3):与 buildEffective **同一条** parse-and-warn 判据。此前本方法直接
|
|
189
189
|
// parseDomain,于是 `exportBundle`(它就是逐域走 getDomain 读的)会把一份携带未知键的域**静默剥**
|
|
190
190
|
// 成一份"干净"的 bundle —— 再 import 回去就是一次持久的配置丢失,全程零告警。
|
|
191
191
|
return parseDomainLoud(domain, raw, this.opts.onWarning);
|
|
@@ -85,7 +85,7 @@ export type MemberDirection = z.infer<typeof MemberDirection>;
|
|
|
85
85
|
/** One member's admission row. `minTrust` ABSENT ⇒ the category's `minTrust` applies: a row can refine the floor
|
|
86
86
|
* only by SAYING so; omission never lowers it (fail-closed by construction).
|
|
87
87
|
*
|
|
88
|
-
* `boolPole` (S-59 件八,
|
|
88
|
+
* `boolPole` (S-59 件八, 对抗复审 r3 [high]) — VALUE-DIRECTIONAL admission for a boolean member whose two poles
|
|
89
89
|
* point opposite ways (`enabled:true` narrows the sandbox, `enabled:false` opens it): a write EQUAL to
|
|
90
90
|
* `boolPole.strict` (strict equality, never truthiness — a string "true" is not the pole) is the tightening
|
|
91
91
|
* write and is admitted at `boolPole.minTrust`; ANY other write — the loose pole, a non-boolean, or a
|
|
@@ -591,7 +591,7 @@ export type SafetyMergeCategory = keyof typeof SAFETY_MERGE_SPEC;
|
|
|
591
591
|
* DISTRIBUTIVE over `C` on purpose: a resolver holds a WIDENED `SafetyMergeCategory` (out of its key→category
|
|
592
592
|
* map), and a non-distributive form would index the whole union at once, fail the `members` check on the
|
|
593
593
|
* whole-key categories and collapse to `never` — typing `membersOf(category)` as `readonly never[]` at exactly
|
|
594
|
-
* the seam these helpers exist for (
|
|
594
|
+
* the seam these helpers exist for (对抗复审 R2-F1). */
|
|
595
595
|
export type SafetyMergeMember<C extends SafetyMergeCategory> = C extends SafetyMergeCategory ? (typeof SAFETY_MERGE_SPEC)[C] extends {
|
|
596
596
|
readonly members: infer M;
|
|
597
597
|
} ? Extract<keyof M, string> : never : never;
|
|
@@ -604,7 +604,7 @@ export declare function membersOf<C extends SafetyMergeCategory>(category: C): r
|
|
|
604
604
|
* Fail-CLOSED and loud on both misuse arms — a category without `members` has no member to name, and a member
|
|
605
605
|
* outside the declared set is a caller bug, not "use the category floor" (that silent arm is exactly what would
|
|
606
606
|
* let a typo admit a bucket under the wrong rule).
|
|
607
|
-
* Value-aware (
|
|
607
|
+
* Value-aware (对抗复审 r3): pass the CANDIDATE VALUE being written for a value-directional (`boolPole`) member —
|
|
608
608
|
* the strict pole resolves to `boolPole.minTrust`, everything else (loose pole / non-boolean / no value) to the
|
|
609
609
|
* row floor. Rows without `boolPole` ignore the value entirely.
|
|
610
610
|
* @param categoryLabel only decorates the error message (the spec value carries no name of its own).
|
|
@@ -97,7 +97,7 @@ export const MemberDirection = z.enum(["loosen", "tighten"]);
|
|
|
97
97
|
/** One member's admission row. `minTrust` ABSENT ⇒ the category's `minTrust` applies: a row can refine the floor
|
|
98
98
|
* only by SAYING so; omission never lowers it (fail-closed by construction).
|
|
99
99
|
*
|
|
100
|
-
* `boolPole` (S-59 件八,
|
|
100
|
+
* `boolPole` (S-59 件八, 对抗复审 r3 [high]) — VALUE-DIRECTIONAL admission for a boolean member whose two poles
|
|
101
101
|
* point opposite ways (`enabled:true` narrows the sandbox, `enabled:false` opens it): a write EQUAL to
|
|
102
102
|
* `boolPole.strict` (strict equality, never truthiness — a string "true" is not the pole) is the tightening
|
|
103
103
|
* write and is admitted at `boolPole.minTrust`; ANY other write — the loose pole, a non-boolean, or a
|
|
@@ -183,7 +183,7 @@ export const SAFETY_MERGE_SPEC = {
|
|
|
183
183
|
// · network.allowAllUnixSockets / network.allowLocalBinding `false` = the opening stays shut ⇒ tighten;
|
|
184
184
|
// · filesystem.allowManagedReadPathsOnly `true` ⇒ tighten (same managed-only consumer caveat);
|
|
185
185
|
// · filesystem.denyWrite / filesystem.denyRead — deny arrays only ever narrow ⇒ tighten in every write.
|
|
186
|
-
// A boolean member's admission is VALUE-directional and the pole is MODELED (
|
|
186
|
+
// A boolean member's admission is VALUE-directional and the pole is MODELED (对抗复审 r3 [high]): each boolean
|
|
187
187
|
// row carries `boolPole {strict, minTrust:"local"}` and keeps its ROW floor at the loosen floor "global" —
|
|
188
188
|
// value-aware consumers admit the strict pole at "local"; pole-blind consumers (whose schema strips boolPole)
|
|
189
189
|
// read tighten@global and withhold BOTH poles from low-trust layers (fail-closed: convenience lost, never a
|
|
@@ -251,7 +251,7 @@ export function membersOf(category) {
|
|
|
251
251
|
* Fail-CLOSED and loud on both misuse arms — a category without `members` has no member to name, and a member
|
|
252
252
|
* outside the declared set is a caller bug, not "use the category floor" (that silent arm is exactly what would
|
|
253
253
|
* let a typo admit a bucket under the wrong rule).
|
|
254
|
-
* Value-aware (
|
|
254
|
+
* Value-aware (对抗复审 r3): pass the CANDIDATE VALUE being written for a value-directional (`boolPole`) member —
|
|
255
255
|
* the strict pole resolves to `boolPole.minTrust`, everything else (loose pole / non-boolean / no value) to the
|
|
256
256
|
* row floor. Rows without `boolPole` ignore the value entirely.
|
|
257
257
|
* @param categoryLabel only decorates the error message (the spec value carries no name of its own).
|
package/dist/types.js
CHANGED
|
@@ -746,7 +746,7 @@ export const LimitsConfig = z.object({
|
|
|
746
746
|
// 在上一份好配置);未知键原样承运,由消费方(键表在它手里)决定认或不认。
|
|
747
747
|
// · 附带收益:老编辑器(TOC)写回新 center 的 limits 文档时不会把自己不认识的键写没了(strip 语义下
|
|
748
748
|
// writeDomainFile 会真的丢键)。
|
|
749
|
-
// · **承运 ≠ 沉默**(
|
|
749
|
+
// · **承运 ≠ 沉默**(对抗复审 复审 round2 F2):未知成员会被 `"unknown-keys-carried"` 警告逐个点名
|
|
750
750
|
// (config-fns 的已知键表由本 schema 的 shape 单源派生)——拼错的键名于是有人说,而值仍然到得了
|
|
751
751
|
// 消费方手里,两头都不吃亏。
|
|
752
752
|
// · 失效条件(如实):若限额面将来收敛为「契约先行、消费方只读已声明键」,本论证不再成立;届时改回
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/settings-schema",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Sema settings schema — pure config contract (zod domains + effective-config + roster resolution + ref-integrity + remoteExec) shared by sema-registry, sema-server, and the TOC desktop/CLI. One source of truth. (Renamed at 1.0.0 from @sema-agent/registry-core — name and docs only, no behavior change; note the range moves 0.19.0 -> 1.0.0, so a dependency must become ^1.0.0. The old names @sema-agent/registry-core <=0.19.0 and @sema-ai/registry-core remain published, frozen, for migration.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|