@sema-agent/settings-schema 14.2.0 → 14.4.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 CHANGED
@@ -8,7 +8,48 @@
8
8
 
9
9
  ## Unreleased
10
10
 
11
- (空)
11
+ ## 14.4.0 — 2026-10-21 — minor:技能条目加两只可选工具名单 `SkillSpec.allowedTools?` / `disallowedTools?`(与引擎本地清单同名同形、逐条按作者所写承运;manifest 自动随到;读腿与兄弟列表同一条逐元素抢救律)
12
+
13
+ > 档位(按发版纪律):**minor** —— 加性:`SkillSpec` 多两个可选键 `allowedTools` / `disallowedTools`;既有清单(不带这两个键)的解析结果、`/effective` 的 `skills[]`、一致性语料的读数逐字节不变。
14
+
15
+ - **技能条目可以自带两只工具名单:`SkillSpec.allowedTools?` / `SkillSpec.disallowedTools?`(`string[]`,元素非空串)**。与引擎本地 `.sema/skills` 清单里同名两键同名同形(元素形与键名;读法见下) ——
16
+ 此前中心分发的技能没有这两个位,引擎本地目录里写好的 `allowed-tools` / `disallowed-tools` 搬到中心就只剩正文,名单在 zod 的默认剥键下无声消失。
17
+ 现在两只名单是 `SkillSpec` 的成员,**逐条按作者所写承运**:不去重、不排序、不规范化拼写,也不在这里判每一条是不是合法的规则拼写
18
+ (一条工具名、一段命名空间、一条带括号的规则各是什么意思,由消费方的授权判官在它那一处读;本包判的只有「非空字符串」)。
19
+ **消费方读法一句话**:**缺席 ⇒ 键不出现(不会被填成 `[]`);在场 ⇒ 整只数组原样交给消费方的判官**。`[]` 与缺席是两种承运形,读成什么由判官定 ——
20
+ 引擎本地目录把空名单读作「未声明」,所以别把 `allowedTools: []` 实现成「一个工具都不许」。同一段名单文本在本地目录与中心分发两处被接受的范围也可能不同:
21
+ 本地目录的装载器对不是规则拼写的条目会拒掉那条技能(不会以缩短的名单装上),本包照原样承运这样的条目、交给判官。
22
+ 坏形(不是数组 / 元素不是字符串 / 元素是空串 / `null`)⇒ 写腿拒,拒句坐标点名到字段(`skills[i].allowedTools`),错在元素上时点名到元素(`skills[i].allowedTools[j]`);
23
+ `/effective` 的 manifest(`SkillsManifestSchema`、`toSkillsManifest`)本来就由 `SkillSpec` 派生(去掉 `content`、加 `contentHash`),所以两只名单自动随到 `skills[]`,没有第二份键表。
24
+ **读腿(`buildEffective` / `readEffectiveWire` 的逐条容错)里,两只名单是普通的嵌套集合,与 `scenarios` / `rosters` / MCP `allowTools` 走同一条既有律,没有为它们另立的读法**:
25
+ 坏元素逐元素丢,告警 `entry-dropped` 的坐标到元素(`skills[i].disallowedTools[j]`),技能带着剩下的好元素存活;整只名单一个好元素都没有 ⇒ 不凭空造出 `[]`,
26
+ 这条技能整条丢并告警 `entry-dropped`(坐标 `skills[i]`);若域里因此没有任何技能幸存(含它是唯一一条)⇒ 既有的零幸存者律:不发 `entry-dropped`,只发 `domain-defaulted`(`skills` 退成空清单)。
27
+ **如实披露的限度**:这条律落在拒绝名单上,意思是读腿上一只带坏元素的 `disallowedTools` 会被截短、技能照装(比作者所写更松),告警是响的;坏元素只可能来自写腿本会拒掉的文档
28
+ (手改的存量文件、别的写者),消费方可以把坐标落在 `…allowedTools[j]` / `…disallowedTools[j]` 的 `entry-dropped` 当硬停信号处理。纯值列表的抢救律(整只原子 vs 逐元素)在下一个 major 统一收口。
29
+ 三问 —— **谁需要**:把引擎本地技能目录里的 `allowed-tools` / `disallowed-tools` 搬到中心分发的人(管理面写、服务端经 `/effective` 分发、worker 读);
30
+ **谁受伤**:① 不升级的**读取方** —— 旧读者(≤14.3.0 的 manifest 读者)不认这两个键,zod 默认剥掉,manifest 路径上没有剥键告警,于是名单在旧读者那里静默丢未知位
31
+ (`disallowedTools` 丢了 = 比作者所写更松);② **升了包的服务端与 client-core 的云端腿也还拿不到名单** —— 两处都是从 manifest 条目按手写键(name / description / content)建技能,没有这两个键;两位过境随各自的后续版本到货,
32
+ 到货前,走这两条路的 worker 上两位无效;③ 存量文档里**已经有同名键但不是数组形**的技能(手写 `config.d/skills.json` 照搬引擎 frontmatter 的逗号串形
33
+ `allowedTools: "Read, Grep"`):此前这个键是未知键,技能存活并发 `unknown-keys-dropped`;现在这个键有了形状,不是数组 ⇒ 这条技能读腿上整条丢(`entry-dropped`,域里没有任何技能幸存则 `domain-defaulted`),写腿拒;
34
+ **补偿**:管理面写入名单之前,先确认读这条链的每一跳都已升级并接入这两个键 —— 升级顺序就是旋钮,本包不另设开关;存量文档先把同名键改成数组形或去掉。
35
+ 钉:`test/skill-tool-lists-14.4.0.test.ts`(域形与 manifest 往返含空数组与缺席两形 · 重复 / 乱序 / 不配对拼写原样 · 缺席不物化键 · 域读与 `parseDomainLoud` 同一份零告警 ·
36
+ `buildEffective` → `resolveEffectiveForWorker` → `readEffectiveWire` 端到端 · 七种坏形 × 域形 / manifest 两条判据的坐标 · 读腿两条逐元素 ⇒ 告警到元素 · 与 `scenarios` 同一条律 ·
37
+ 整只名单全坏 ⇒ 整条丢、域里无幸存者(唯一一条或每条都坏)⇒ `domain-defaulted` · 三份导出 schema 转 JSON Schema 两只名单零非标关键字)。
38
+
39
+ ## 14.3.0 — 2026-10-21 — minor:信任阶梯词表做纯常量 `MIN_TRUST_WORDS`(`MinTrust` 由它派生;`trustRank` / `isAtLeast` 闭包零 zod,整函数抄走的消费方只登记一只全局)
40
+
41
+ > 档位(按发版纪律):**minor** —— 加性:新导出 `MIN_TRUST_WORDS`(包根与 `./safety-merge-spec`);型 `MinTrust`、枚举的词与声明序、`trustRank` 的数值与全部答案、未知词的 RangeError 句逐字节不变。
42
+
43
+ - **信任阶梯的词表是一只纯常量 `MIN_TRUST_WORDS`;`trustRank` / `isAtLeast` 的闭包只引这一只纯数据,不碰 zod**。此前 `trustRank` 的 `switch` 本身零 zod,
44
+ 唯一的 zod 引用是 `default` 臂 RangeError 句里的 `MinTrust.options.join(", ")` —— 于是按「闭包零 import、只登记全局」整函数抄走判官的消费方抄不了
45
+ `isAtLeast → trustRank` 这条链(`layerAdmission` 经它判层档);手写一份层表就是第二份层表。现在词表来源是数据:
46
+ `export const MIN_TRUST_WORDS = ["local", "session", "project", "global", "managed", "task"] as const`(声明序 = 既有枚举序,逐字节不变;声明序不是阶梯,阶梯只经 `trustRank`),
47
+ `MinTrust = z.enum(MIN_TRUST_WORDS)` 由它派生(与 `AUTONOMY_MODES` → `AutonomyMode` 同形),`trustRank` 的默认臂句读 `MIN_TRUST_WORDS.join(", ")`(句字节不变)。
48
+ 结果:`trustRank` 的自由标识符 = `MIN_TRUST_WORDS` + JS 内建 `RangeError` / `String`,`isAtLeast` 的 = `trustRank`;抄走这一对的消费方把 `MIN_TRUST_WORDS` 登记成全局就够了。
49
+ 不另出第二只函数、不出生成物(一只判官一份源)。
50
+ 三问 —— **谁需要**:按闭包抄判官的消费方(构建期生成器),以及任何抄 `layerAdmission` 链的包;**谁受伤**:无(新导出,既有导出对既有输入的答案逐格不变);**补偿**:无需。
51
+ 钉:`test/trust-ladder-zod-free.test.ts`(① 常量逐位 ≡ `MinTrust.options` 且 = 既有声明序 · ② 两只函数的自由标识符与代码标识符 · ③ 裸 `node:vm` 上下文只注入 `MIN_TRUST_WORDS`,六词 rank / 6×6 真值表 / 未知词的 RangeError 句与包内直接调用逐字节同);
52
+ `test/safety-merge-spec.test.ts` 的声明序文本钉改读新形(词表字面量恰一处、`MinTrust` 由它派生恰一处)。
12
53
 
13
54
  ## 14.2.0 — 2026-10-18 — minor:守卫迹象报告序改句 · 关停位任一层 tighten-only(删 managed-only 告知)· 项目层 `.claude/settings*.json` 只经 layerAdmission 读 tighten 方向并点名未读部分 · 控制 / 双向字符剥除集根导出(server / cli 同读,漂移门 ⑨ 退役)
14
55
 
package/README.md CHANGED
@@ -297,6 +297,10 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
297
297
  query sits at the row floor `global`, and the merge is boolStricter (one layer's ON beats every layer's OFF). The table has no managed-only floor
298
298
  for them (`permissionMode`'s seam no longer says so): a consumer that stamped one locally retires it onto this row, so its judge and its
299
299
  resolver read the same row.
300
+ The ladder's words are a runtime value, the pure-data table `MIN_TRUST_WORDS` (`["local", "session", "project", "global", "managed", "task"]`): `MinTrust` (the
301
+ zod enum) is derived from it, and `trustRank` / `isAtLeast` read nothing else — apart from the JavaScript built-ins `RangeError` / `String`, the body of
302
+ `trustRank` names `MIN_TRUST_WORDS` and `isAtLeast` names `trustRank`, so a consumer that copies the pair whole (a closure with no import, only globals
303
+ registered) registers that one table instead of writing a ladder of its own. The table's order is the enum's declaration order, not the ladder.
300
304
  `MinTrust` gains the `task` word (the runtime task overlay, the loosest floor; appended, declaration order
301
305
  otherwise unchanged — the declaration order is not the ladder, `trustRank` is). The published table does not
302
306
  use `task`; the widened vocabulary is visible to consumers that hold a `Record<MinTrust, …>` / exhaustive
@@ -1 +1 @@
1
- 14.2.0
1
+ 14.4.0
@@ -60,10 +60,19 @@ export declare const MergeShape: z.ZodEnum<{
60
60
  overrideByName: "overrideByName";
61
61
  }>;
62
62
  export type MergeShape = z.infer<typeof MergeShape>;
63
+ /** The trust-tier VOCABULARY as a RUNTIME VALUE — the ONE source of the ladder's words, in the same form as
64
+ * `AUTONOMY_MODES`: {@link MinTrust} (the zod enum) and its type are DERIVED from this table, and {@link trustRank} /
65
+ * {@link isAtLeast} read the words from it (their RangeError sentence lists them). Pure data, no zod: a consumer that
66
+ * copies the ladder's judge functions whole — a closure with no import, only globals registered — registers this
67
+ * table as the one global the closure needs, instead of writing a ladder of its own.
68
+ * ⚠️ The DECLARATION ORDER is NOT the rank (it is the 1.1.0 order with `task` appended — kept byte-stable because
69
+ * reordering is breaking); never index it as a ladder — use {@link trustRank}. */
70
+ export declare const MIN_TRUST_WORDS: readonly ["local", "session", "project", "global", "managed", "task"];
63
71
  /** The trust-tier VOCABULARY a category — or, since 1.1.0, one of its members — names as its admission floor
64
72
  * (the MINIMUM trust a layer/principal must have to set it). The RANK is v2-design §1:29,
65
73
  * `managed 6 > global 5 > project 4 > local 3 > session 2 > task 1`, published as {@link trustRank}; compare with
66
74
  * {@link isAtLeast}. `task` (1.2.0) is the runtime task overlay — the lowest trust, the loosest floor.
75
+ * Derived from {@link MIN_TRUST_WORDS} (the words and their declaration order are unchanged).
67
76
  * ⚠️ This enum's DECLARATION ORDER is NOT the rank (it is the 1.1.0 order with `task` appended — kept byte-stable
68
77
  * because reordering is breaking); never index it as a ladder — use {@link trustRank}. */
69
78
  export declare const MinTrust: z.ZodEnum<{
@@ -80,11 +89,14 @@ export type MinTrust = z.infer<typeof MinTrust>;
80
89
  * The numbers themselves are contract (a consumer may persist or log them), not merely their order.
81
90
  * Exhaustive over {@link MinTrust}: a word added to the enum without a rank is a COMPILE error here; a word that
82
91
  * is not in the enum at runtime (a JavaScript / deserialized caller) is a loud RangeError — never `undefined` or
83
- * `NaN`, which would turn every comparison into a silent `false`. */
92
+ * `NaN`, which would turn every comparison into a silent `false`.
93
+ * Self-contained: apart from the JavaScript built-ins `RangeError` / `String`, the body names exactly one
94
+ * free identifier, the pure-data {@link MIN_TRUST_WORDS} — no zod — so a consumer may copy it whole. */
84
95
  export declare function trustRank(layer: MinTrust): number;
85
96
  /** `trustRank(layer) ≥ trustRank(minTrust)` — "is a source at `layer` trusted enough to set a key / member whose
86
97
  * floor is `minTrust`?" (1.2.0). The comparison a resolver's admission check is; reflexive (a floor admits its
87
- * own tier). Both arguments are validated by {@link trustRank} (unknown word ⇒ RangeError). */
98
+ * own tier). Both arguments are validated by {@link trustRank} (unknown word ⇒ RangeError). Its body names one free
99
+ * identifier, {@link trustRank} — so the pair copies whole with {@link MIN_TRUST_WORDS} as the only registered global. */
88
100
  export declare function isAtLeast(layer: MinTrust, minTrust: MinTrust): boolean;
89
101
  /** Which way a MEMBER moves the engine's unattended surface (1.1.0). `loosen` = admits an action without a human
90
102
  * that would otherwise ask/deny — a privilege, so it needs trust; `tighten` = only ever narrows — restricting is
@@ -53,19 +53,30 @@
53
53
  import { z } from "zod";
54
54
  /** How values from different layers/principals combine for a category. */
55
55
  export const MergeShape = z.enum(["denyFirst", "replace", "runAll", "overrideByName", "concat"]);
56
+ /** The trust-tier VOCABULARY as a RUNTIME VALUE — the ONE source of the ladder's words, in the same form as
57
+ * `AUTONOMY_MODES`: {@link MinTrust} (the zod enum) and its type are DERIVED from this table, and {@link trustRank} /
58
+ * {@link isAtLeast} read the words from it (their RangeError sentence lists them). Pure data, no zod: a consumer that
59
+ * copies the ladder's judge functions whole — a closure with no import, only globals registered — registers this
60
+ * table as the one global the closure needs, instead of writing a ladder of its own.
61
+ * ⚠️ The DECLARATION ORDER is NOT the rank (it is the 1.1.0 order with `task` appended — kept byte-stable because
62
+ * reordering is breaking); never index it as a ladder — use {@link trustRank}. */
63
+ export const MIN_TRUST_WORDS = ["local", "session", "project", "global", "managed", "task"];
56
64
  /** The trust-tier VOCABULARY a category — or, since 1.1.0, one of its members — names as its admission floor
57
65
  * (the MINIMUM trust a layer/principal must have to set it). The RANK is v2-design §1:29,
58
66
  * `managed 6 > global 5 > project 4 > local 3 > session 2 > task 1`, published as {@link trustRank}; compare with
59
67
  * {@link isAtLeast}. `task` (1.2.0) is the runtime task overlay — the lowest trust, the loosest floor.
68
+ * Derived from {@link MIN_TRUST_WORDS} (the words and their declaration order are unchanged).
60
69
  * ⚠️ This enum's DECLARATION ORDER is NOT the rank (it is the 1.1.0 order with `task` appended — kept byte-stable
61
70
  * because reordering is breaking); never index it as a ladder — use {@link trustRank}. */
62
- export const MinTrust = z.enum(["local", "session", "project", "global", "managed", "task"]);
71
+ export const MinTrust = z.enum(MIN_TRUST_WORDS);
63
72
  /** The trust RANK of a floor / layer word — v2-design §1:29 verbatim (`MANAGED 6 > GLOBAL 5 > PROJECT 4 > LOCAL 3 >
64
73
  * SESSION 2 > TASK 1`), the ONE published ladder every resolver compares against (1.2.0). Higher = more trusted.
65
74
  * The numbers themselves are contract (a consumer may persist or log them), not merely their order.
66
75
  * Exhaustive over {@link MinTrust}: a word added to the enum without a rank is a COMPILE error here; a word that
67
76
  * is not in the enum at runtime (a JavaScript / deserialized caller) is a loud RangeError — never `undefined` or
68
- * `NaN`, which would turn every comparison into a silent `false`. */
77
+ * `NaN`, which would turn every comparison into a silent `false`.
78
+ * Self-contained: apart from the JavaScript built-ins `RangeError` / `String`, the body names exactly one
79
+ * free identifier, the pure-data {@link MIN_TRUST_WORDS} — no zod — so a consumer may copy it whole. */
69
80
  export function trustRank(layer) {
70
81
  switch (layer) {
71
82
  case "managed": return 6;
@@ -76,13 +87,14 @@ export function trustRank(layer) {
76
87
  case "task": return 1;
77
88
  default: {
78
89
  const unknown = layer;
79
- throw new RangeError(`MinTrust: unknown trust word '${String(unknown)}' (words: ${MinTrust.options.join(", ")})`);
90
+ throw new RangeError(`MinTrust: unknown trust word '${String(unknown)}' (words: ${MIN_TRUST_WORDS.join(", ")})`);
80
91
  }
81
92
  }
82
93
  }
83
94
  /** `trustRank(layer) ≥ trustRank(minTrust)` — "is a source at `layer` trusted enough to set a key / member whose
84
95
  * floor is `minTrust`?" (1.2.0). The comparison a resolver's admission check is; reflexive (a floor admits its
85
- * own tier). Both arguments are validated by {@link trustRank} (unknown word ⇒ RangeError). */
96
+ * own tier). Both arguments are validated by {@link trustRank} (unknown word ⇒ RangeError). Its body names one free
97
+ * identifier, {@link trustRank} — so the pair copies whole with {@link MIN_TRUST_WORDS} as the only registered global. */
86
98
  export function isAtLeast(layer, minTrust) {
87
99
  return trustRank(layer) >= trustRank(minTrust);
88
100
  }
package/dist/types.d.ts CHANGED
@@ -1793,6 +1793,8 @@ export declare const SkillSpec: z.ZodObject<{
1793
1793
  rosters: z.ZodPrefault<z.ZodArray<z.ZodString>>;
1794
1794
  content: z.ZodDefault<z.ZodString>;
1795
1795
  enabled: z.ZodDefault<z.ZodBoolean>;
1796
+ allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
1797
+ disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
1796
1798
  notes: z.ZodOptional<z.ZodString>;
1797
1799
  }, z.core.$strip>;
1798
1800
  export type SkillSpec = z.infer<typeof SkillSpec>;
@@ -1804,6 +1806,8 @@ export declare const SkillsConfig: z.ZodObject<{
1804
1806
  rosters: z.ZodPrefault<z.ZodArray<z.ZodString>>;
1805
1807
  content: z.ZodDefault<z.ZodString>;
1806
1808
  enabled: z.ZodDefault<z.ZodBoolean>;
1809
+ allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
1810
+ disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
1807
1811
  notes: z.ZodOptional<z.ZodString>;
1808
1812
  }, z.core.$strip>>>;
1809
1813
  }, z.core.$strip>;
@@ -1828,6 +1832,8 @@ export declare const SkillsManifestSchema: z.ZodObject<{
1828
1832
  rosters: z.ZodPrefault<z.ZodArray<z.ZodString>>;
1829
1833
  description: z.ZodDefault<z.ZodString>;
1830
1834
  scenarios: z.ZodPrefault<z.ZodArray<z.ZodString>>;
1835
+ allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
1836
+ disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
1831
1837
  contentHash: z.ZodString;
1832
1838
  }, z.core.$strip>>>;
1833
1839
  }, z.core.$strip>;
@@ -2480,6 +2486,8 @@ export declare const DOMAIN_SCHEMAS: {
2480
2486
  rosters: z.ZodPrefault<z.ZodArray<z.ZodString>>;
2481
2487
  content: z.ZodDefault<z.ZodString>;
2482
2488
  enabled: z.ZodDefault<z.ZodBoolean>;
2489
+ allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
2490
+ disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
2483
2491
  notes: z.ZodOptional<z.ZodString>;
2484
2492
  }, z.core.$strip>>>;
2485
2493
  }, z.core.$strip>;
package/dist/types.js CHANGED
@@ -2338,6 +2338,21 @@ export const SkillSpec = z.object({
2338
2338
  /** The markdown body injected into the model context on invoke (progressive disclosure). Stored in full here; shipped as a hash in /effective. */
2339
2339
  content: z.string().default(""),
2340
2340
  enabled: z.boolean().default(true),
2341
+ /** Tool allow list the skill declares — carried entry by entry as the author wrote it (same name and element shape as the
2342
+ * engine's local `.sema/skills` manifest `SkillSpec.allowedTools`). ABSENT = the key is not there (no list is carried);
2343
+ * PRESENT = the whole array, `[]` included, goes to the consumer's judge — two carried shapes, and what each reads as is the
2344
+ * judge's call (the engine's local skills directory reads an empty list as "not declared"). This contract only CARRIES: no
2345
+ * de-duplication, no sorting, no spelling check, no roster intersection — what an entry means (a tool, a namespace, a rule,
2346
+ * a path) is read in ONE place, the consumer's grant judge; the element shape judged here is "a non-empty string" and nothing
2347
+ * more. A malformed shape (not an array / a non-string or empty entry) is refused with a path naming the field
2348
+ * (`skills[i].allowedTools`) and, when the fault is an element, the element (`skills[i].allowedTools[j]`). Read leg: the list is an ordinary nested collection and follows the one salvage law that
2349
+ * `scenarios` / `rosters` / `mcp.servers[].allowTools` follow (a bad element is dropped with an `entry-dropped` warning whose
2350
+ * path reaches the element; a list with no good element makes the skill unreadable and the skill is dropped; when no skill of the domain survives, the domain falls back to its default with `domain-defaulted`). Travels on
2351
+ * `/effective` with the rest of the manifest ({@link SkillsManifestSchema} derives from this schema). */
2352
+ allowedTools: z.array(z.string().min(1)).optional(),
2353
+ /** Tool deny list the skill declares — same carrying and reading rules as {@link SkillSpec.allowedTools} (absent = no list
2354
+ * carried, present = the whole array goes to the consumer's judge, `[]` included; never de-duplicated, sorted or judged here). */
2355
+ disallowedTools: z.array(z.string().min(1)).optional(),
2341
2356
  notes: z.string().optional(),
2342
2357
  });
2343
2358
  export const SkillsConfig = z
@@ -4,9 +4,9 @@
4
4
  > 每次全量重算并逐名比对 —— 加 / 删 / 改形一个导出而不重生成本档 ⇒ 红。导出面的每次变动因此以本档的 diff 出现在评审里。
5
5
  > 形:`value` = 运行期拿得到的值;`type` = 只在类型层;`value+type` = 同名的值与类型(zod schema 与它推出的类型)。
6
6
 
7
- 子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1337 个(同名在多条子路径上各计一次)。
7
+ 子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1339 个(同名在多条子路径上各计一次)。
8
8
 
9
- ## `.`(661)
9
+ ## `.`(662)
10
10
 
11
11
  - `ABSOLUTE_HOST_PATH_MAX` — value
12
12
  - `ACCESS_TOKEN_TTL_SECONDS` — value
@@ -236,6 +236,7 @@
236
236
  - `MCP_POLICY_RULES` — value
237
237
  - `MCP_SERVER_LIST_KEYS` — value
238
238
  - `MEMORY_ENGINE_BACKENDS` — value
239
+ - `MIN_TRUST_WORDS` — value
239
240
  - `MODEL_ENTRY_REFUSAL_CODES` — value
240
241
  - `MODEL_PLANE_REFUSAL_CODES` — value
241
242
  - `MODEL_SWITCH_SOURCES` — value
@@ -1037,9 +1038,10 @@
1037
1038
  - `REMOTE_EXEC_DOCUMENT` — value
1038
1039
  - `RemoteExecSpec` — value+type
1039
1040
 
1040
- ## `./safety-merge-spec`(18)
1041
+ ## `./safety-merge-spec`(19)
1041
1042
 
1042
1043
  - `FailMode` — value+type
1044
+ - `MIN_TRUST_WORDS` — value
1043
1045
  - `MemberAdmission` — value+type
1044
1046
  - `MemberDirection` — value+type
1045
1047
  - `MergeCategorySpec` — value+type
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  llms-full.txt — 自包含全文 · 消费方(使用者)视角 · @sema-agent/settings-schema
3
- 重写日期: 2026-10-18 | 包版本: 14.2.0
3
+ 重写日期: 2026-10-21 | 包版本: 14.4.0
4
4
  生成方式: 无生成脚本——按当前树 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版。
5
5
  证据约定: 每条"它怎么工作"的断言附 (文件: 符号) 坐标,刻意不用行号(行号逐版腐烂;符号名由
6
6
  typecheck 与 test/package-surface.test.ts、test/docs-hygiene.test.ts 等常驻门钉住)。无证据推断显式标【推断】。
@@ -105,6 +105,7 @@ registry:公网 npmjs,无需私有 registry 配置。
105
105
  --- 6.4.0:`ModelEntry.headers?`(`z.unknown().optional()` 解析透明,无默认;与 `compat` 同一只变换 —— 缺席不铸键,其余只过与 `compat`/`extraBody` 共用的 `vetFreeFormJson` 安全判据,拒句同形只换字段名;登记在 `PARSE_TRANSPARENT_KEYS`,`validatedBy: "consumer"`)——部署 ↔ 供应商的 per-model 附加请求头,例 `{"anthropic-beta":"context-1m-2025-08-07"}`(开 1M 上下文);头名闭集白名单 × 读它的线路 × 值形的判据**唯一属主是 sema-server**(坏声明 ⇒ 该条目整条不可用,不是丢头照常),本包刻意不做形校验(字符串 / 数组 / 大小写不规范的名 / 白名单外的名一律逐字过,由 server 判)。修前(≤6.3.0)每条读腿都剥它并报 `unknown-keys-dropped` ---
106
106
  · rosters:`RosterEntry[]`(`primaryModel`/`cheapModel` 标记角色;`ROSTER_PRIMARY_ROLES`/`ROSTER_CHEAP_ROLES`)。
107
107
  · skills:`SkillSpec[]`(content 全文;wire 转 manifest 见 §4.5)。
108
+ --- 14.4.0:`SkillSpec.allowedTools?` / `disallowedTools?`(`string[]`,元素非空串;与引擎本地 `.sema/skills` 清单同名同形)—— 逐条按作者所写承运:不去重 / 不排序 / 不判拼写(本包只判「非空字符串」);缺席 = 键不出现,在场 = 整只数组交给消费方的判官(`[]` 与缺席是两种承运形,读成什么由判官定;引擎本地目录把空名单读作未声明);manifest 由 `SkillSpec` 派生,两键自动随到 `/effective` 的 `skills[]`;读腿的逐条容错对它们与 `scenarios` / `rosters` / MCP `allowTools` 同一条律:坏元素逐元素丢并告警 `entry-dropped`(坐标到元素 `skills[i].<名单>[j]`),整只名单全坏 ⇒ 整条技能丢(告警 `skills[i]`;域里没有任何技能幸存 ⇒ 不发 `entry-dropped`、只发既有的 `domain-defaulted`);所以读腿上带坏元素的 `disallowedTools` 会被截短、技能照装(告警是响的),限度与消费方的处置见 CHANGELOG 该条。
108
109
  · mcp:`McpServerSpec[]` —— `transport` 判别 stdio(command/args/envRefs)| http(url/headerRefs/
109
110
  principalHeader/assertionHeader;后两者互斥);`allowTools?`、`scenarios`/`rosters` 过滤、`elicitation`(默认 false,inbound elicitation
110
111
  的 per-server opt-in)、`contentOrigin?`(信任出处声明,闭词表 `MCP_CONTENT_ORIGINS =
@@ -346,6 +347,8 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
346
347
  `memberMinTrust`(禁自铸表)。
347
348
  - 信任阶梯(1.2.0):`trustRank`(managed 6 > global 5 > project 4 > local 3 > session 2 > task 1)、
348
349
  `isAtLeast`;`MinTrust` 含 `task`(声明序非阶梯,阶梯唯一真源是 `trustRank`)。
350
+ 词表是一只纯常量 `MIN_TRUST_WORDS`(`local, session, project, global, managed, task`;`MinTrust` zod 枚举由它派生):`trustRank` 的闭包自由名 = `MIN_TRUST_WORDS` + JS 内建
351
+ `RangeError` / `String`,`isAtLeast` 的 = `trustRank` —— 整函数抄走这一对的生成器(client-core)把 `MIN_TRUST_WORDS` 登记成全局即可,不许手写层表 / 词表字面量。
349
352
  - 设置源表(src/settings-sources.ts,主 barrel):`SETTINGS_SOURCES`(合并序 user<project<local<flag<policy)、`SETTINGS_SOURCE_CONTRACT`
350
353
  (user global / project project 仓库可控 / local project 仓库可控 / flag global / policy managed)、`WORKSPACE_TRUST_STATES`
351
354
  (accepted/declined/no_trust_step)、`layerAdmission(source, workspace, category, member?, value?)` → admitted / trust_withheld / below_trust_floor
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
3
- 重写日期: 2026-10-18 | 包版本: 14.2.0
3
+ 重写日期: 2026-10-21 | 包版本: 14.4.0
4
4
  生成方式: 无生成脚本——按当前树的 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版;
5
5
  每条断言附证据坐标(文件:符号),刻意不用行号(行号随每版腐烂,符号名由 typecheck/test 钉住)。
6
6
  刷新触发: package.json 版号变更(test/docs-hygiene.test.ts D 组钉「包版本:」与 package.json 相等、
@@ -95,7 +95,7 @@ llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
95
95
  - **跨会话两键**(1.6.0 新增,`settings.json` 面;6.0.0 词表随 core 7.30.0 改):`crossSessionInbound`(wake/next-turn/hold/refuse + `default`=未设;`accept` 已退役,文件层响亮拒)
96
96
  与 `dialogExpiry`(60s/5m/10m/never,缺省 5m);另出两只层读取器,坏值 `ok:false` 不静默折缺省。
97
97
  (src/cross-session.ts: `CrossSessionSettings` / `readCrossSessionInbound` / `readDialogExpiry`)
98
- - **安全合并表** `SAFETY_MERGE_SPEC` + 信任阶梯 `trustRank` / `isAtLeast` + 成员级准入 `memberAdmission`;关停位 `disableBypassPermissionsMode` / `disableAutoMode`
98
+ - **安全合并表** `SAFETY_MERGE_SPEC` + 信任阶梯 `trustRank` / `isAtLeast`(词表来源 = 纯常量 `MIN_TRUST_WORDS`,闭包零 zod)+ 成员级准入 `memberAdmission`;关停位 `disableBypassPermissionsMode` / `disableAutoMode`
99
99
  = 类目 `killSwitches`(尾插):任一层可收紧、不放宽 —— 开 = tighten@local(任一设置文件层),关 / 非布尔 / 不带值 = 行地板 global,合并 boolStricter;没有「只许托管层」的地板。
100
100
  (src/safety-merge-spec.ts)
101
101
  - **设置源表 + 层准入**:`SETTINGS_SOURCES`(合并序)/ `SETTINGS_SOURCE_CONTRACT`(信任档 + 是否仓库可控;本地层与项目层同 `project` 档)/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/settings-schema",
3
- "version": "14.2.0",
3
+ "version": "14.4.0",
4
4
  "description": "Sema settings schema — pure config contract (zod domains + effective-config + roster resolution + ref-integrity + remoteExec) shared by sema-web-admin, 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",