@sema-agent/settings-schema 13.0.0 → 13.0.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 CHANGED
@@ -6,6 +6,24 @@
6
6
  > `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
7
7
  > 包名是**历史记录**,如实保留不追改。
8
8
 
9
+ ## 13.0.1 — 2026-10-16 — patch:`classifyHooksConfig` 对「未知字段里藏守卫迹象」的拒绝项补 `field`(点名藏迹象的那个键)· 随包文字引用的 `docs/` 与 `llms.txt` / `llms-full.txt` 进 tarball
10
+
11
+ - **行为修复(拒绝项 `field`)**。13.0.0 写了「条目 / 匹配器的坏块另带坏字段名 `field`」,而实码只对「契约字段值写错」一形给了 `field`;「本会被剥掉的未知字段里藏着守卫迹象 ⇒ 拒」这一形
12
+ (`guard-matcher-invalid` / `guard-entry-invalid`)的拒绝项没有。现在一条律,两形相同:坏块是对象(匹配器 / 条目)⇒ `field` = 它上面出问题的那个键 —— 契约字段值错的那个字段,
13
+ 或装着迹象的那个成员(契约不认识的未知字段;装得下比字段类型更多东西的契约字段,如 http 条目的 `headers`;条目自己就是匹配器形时取 `hooks`);只有块本身不是对象才缺席。
14
+ 一块一条拒绝项、一个 `field`:迹象按文档序取第一个;同一块上迹象先于契约字段错判(它是「拒」而不是「剔」的缘由)。
15
+ 结局、码、坐标、剔点、装上的那一份逐字节不变(30 万份文档新旧对拍,判断面零差);变的只有这一形的拒绝项多一个 `field`,以及 zod 面对应 issue 的 `path` 多最后一段
16
+ (坐标 + `field`,与契约字段写错的 issue 同形)。型面不变(`HooksRejection.field` 本就是 `field?: string`);`HooksDroppedPiece` / 剔点读数不动。
17
+ 三问 —— **谁需要**:拿 `field` 告诉用户「改哪一个键」的消费方:藏迹象这一形此前只能指到匹配器 / 条目那一整块,用户要自己在块里找;**谁受伤**:断言「该形拒绝项没有 `field`」的消费方测试,
18
+ 或把 zod issue `path` 当定长坐标读的点,会多一段;**补偿**:`field` 缺席现在只有一种意思(块不是对象),读 `field` 前不必再区分是哪一形。
19
+ 钉:`test/hooks-classify-13.0.0.test.ts` ⑥(七形 + 两加试各自点名藏迹象的键 · 同族几形 · 多键取先出现的 · 文档序 · 正控 / 负控 · zod 面 issue path)。
20
+ - **随包文件补齐(`package.json` `files`)**。README / CHANGELOG / 源码注释(随 `dist/*.d.ts` 出包)里写着「见 `docs/…`」「见 `llms.txt`」的档此前不在 tarball 里,装了包的人顺着指路点不到。
21
+ `files` 加 `docs`(`API-SURFACE` · `CC-DIVERGENCE` · `CONFORMANCE` · `CONSUMER-INTEGRATION` · `openapi-auth` · `openapi-scopes` 六份)、`llms.txt`、`llms-full.txt`;
22
+ tarball 文件数 437 → 445,包体积约 +88 kB(851 kB → 938 kB)。
23
+ 三问 —— **谁需要**:读包内文字、顺着指路找契约档的人和 AI;**谁受伤**:无(只多文件,导出面与运行期零变化,下载多约 88 kB);**补偿**:无需。
24
+ 钉:`test/package-surface.test.ts`「随包文字引用的仓内文件都随包」—— 扫 README / CHANGELOG / `bin` / 源码注释里指到的、本仓真有的 `docs/…` 与 `llms` 档,逐个核落在 `files` 覆盖的路径里(今天恰 8 个);
25
+ 门有牙一格(去掉三项后同一扫描点名它们)。
26
+
9
27
  ## 13.0.0 — 2026-10-16 — major:hooks 坏形一只读者 `classifyHooksConfig` · 设置源表 + 层准入 `layerAdmission`(层档只有源表一个读者)· URL 名单判官两出口 · 新类目 `httpHookAllowlists` + http hook 两张名单跨层取交 · MCP 名单判决 `mcpServerPolicyVerdict` · 界面语言设置片段 · 模型能力位三态(major)
10
28
 
11
29
  > 档位:**major**。按「发版纪律」三处是破坏性(B1 `HooksConfig` 族受理面收紧且读数变 · B2 八只 hooks zod 面改形 · B3 `ModelEntry.reasoning` / `vision`
package/README.md CHANGED
@@ -431,7 +431,8 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
431
431
  the config surface (defined in `hooks-config.ts`, re-exported by `./hooks`): `HOOK_ENTRY_TYPES` (CC 2.1.296's five: command / prompt / agent / http /
432
432
  `mcp_tool`) and the ONE classifier `classifyHooksConfig(settingsDoc)` — which pieces of a settings document's `hooks` drop
433
433
  (`HOOKS_DROPPED_PIECE_CODES`), whether the whole document is rejected because a guard (`PreToolUse` / `PermissionRequest`) may be
434
- in a bad piece (`HOOKS_REJECTION_CODES`), and what loads; codes + `ConfigPath` coordinates, no sentences, self-contained (no zod;
434
+ in a bad piece (`HOOKS_REJECTION_CODES`), and what loads; codes + `ConfigPath` coordinates + the wrong key's name (`field`: for a matcher / entry, the contract
435
+ field whose value is wrong, or the member that holds a guard sign; absent only when the piece is not an object), no sentences, self-contained (no zod;
435
436
  its one free name besides JS built-ins is `HOOK_EVENTS` — a generator that copies the function must inject this package's runtime
436
437
  `HOOK_EVENTS` into the copy, never paste a literal event list).
437
438
  `HooksConfig` / `HookMatcherEntry` / `HookEntry` (the `settings.json` `hooks` shape — event names a CLOSED set) are its zod
@@ -1 +1 @@
1
- 13.0.0
1
+ 13.0.1
@@ -20,6 +20,12 @@
20
20
  * that merely spells `PreToolUse` is not a sign. A piece holding a sign is never dropped — dropping it could make a misplaced guard vanish —
21
21
  * the document is rejected instead, wherever the sign sits: outside `hooks`, under an unknown event, in a non-array event value, in a matcher's
22
22
  * or an entry's fields (including fields the contract does not know and would otherwise not carry).
23
+ * · `field` — ONE LAW for drops and rejections alike: when the bad piece is a matcher or an entry (an object), `field` is the KEY on it that makes it
24
+ * bad — a contract field whose value is wrong, or, for a piece rejected because it holds a guard sign, the member that holds the sign (an unknown
25
+ * field the contract would strip; a contract field carrying more than its kind, such as `headers` or `input`; `hooks` for an entry that is itself
26
+ * matcher-shaped). Absent only when the piece is not an object — and for the pieces above matchers, which have no fields to name. One piece, one
27
+ * `field`: the first wrong contract field, or the first sign in document order; a sign on the piece itself decides before a wrong contract field
28
+ * of the same piece does (it is why the piece is rejected rather than dropped).
23
29
  *
24
30
  * CC 2.1.296 DIVERGENCES (each in the stricter direction — a document CC would load may be rejected here, never the reverse): listed once,
25
31
  * in `docs/CC-DIVERGENCE.md` (section "hooks classifier", H1–H7) — the whole-subtree guard-sign scan is the first of them.
@@ -173,7 +179,9 @@ export interface HooksDroppedPiece {
173
179
  readonly path: ConfigPath;
174
180
  readonly field?: string;
175
181
  }
176
- /** One reason the document was rejected: `path` = the offending piece (for `guard-outside-hooks`: the guard sign itself); `field` as above. */
182
+ /** One reason the document was rejected: `path` = the offending piece (for `guard-outside-hooks`: the guard sign itself); `field` = for a matcher / entry,
183
+ * the key on it that makes it bad — the contract field whose value is wrong, or the member that holds a guard sign (see the file header's `field` law);
184
+ * absent when the piece is not an object, and for the codes above matchers. */
177
185
  export interface HooksRejection {
178
186
  readonly code: HooksRejectionCode;
179
187
  readonly path: ConfigPath;
@@ -20,6 +20,12 @@
20
20
  * that merely spells `PreToolUse` is not a sign. A piece holding a sign is never dropped — dropping it could make a misplaced guard vanish —
21
21
  * the document is rejected instead, wherever the sign sits: outside `hooks`, under an unknown event, in a non-array event value, in a matcher's
22
22
  * or an entry's fields (including fields the contract does not know and would otherwise not carry).
23
+ * · `field` — ONE LAW for drops and rejections alike: when the bad piece is a matcher or an entry (an object), `field` is the KEY on it that makes it
24
+ * bad — a contract field whose value is wrong, or, for a piece rejected because it holds a guard sign, the member that holds the sign (an unknown
25
+ * field the contract would strip; a contract field carrying more than its kind, such as `headers` or `input`; `hooks` for an entry that is itself
26
+ * matcher-shaped). Absent only when the piece is not an object — and for the pieces above matchers, which have no fields to name. One piece, one
27
+ * `field`: the first wrong contract field, or the first sign in document order; a sign on the piece itself decides before a wrong contract field
28
+ * of the same piece does (it is why the piece is rejected rather than dropped).
23
29
  *
24
30
  * CC 2.1.296 DIVERGENCES (each in the stricter direction — a document CC would load may be rejected here, never the reverse): listed once,
25
31
  * in `docs/CC-DIVERGENCE.md` (section "hooks classifier", H1–H7) — the whole-subtree guard-sign scan is the first of them.
@@ -214,6 +220,12 @@ function hookGuardSignIn(root, rootPath, place) {
214
220
  }
215
221
  return undefined;
216
222
  }
223
+ /** The key of the object piece at `at` that holds `sign` (a coordinate at or below `at`, as {@link hookGuardSignIn} reports it): the first segment
224
+ * below `at`; `hooks` when the sign IS the piece itself (a matcher-shaped object — its `hooks` member is what makes it one). */
225
+ function hookSignKeyOf(at, sign) {
226
+ const key = sign[at.length];
227
+ return key === undefined ? "hooks" : String(key);
228
+ }
217
229
  /** Does `value` fit the field kind `base` (the kind without its `?`)? */
218
230
  function hookFieldFits(base, value) {
219
231
  switch (base) {
@@ -279,27 +291,22 @@ function hookMatcherRead(matcher, at, guardEvent, reject, drop) {
279
291
  return undefined;
280
292
  }
281
293
  const entries = Object.hasOwn(matcher, "hooks") ? matcher.hooks : undefined;
282
- // a sign at the matcher level: a guard-event key on the matcher itself, or anywhere in its fields other than a well-formed entry list
283
- let matcherSign = hookGuardKeyOf(matcher) !== undefined;
284
- for (const key of Object.keys(matcher)) {
285
- if (matcherSign)
286
- break;
287
- if (key === "hooks" && Array.isArray(entries))
288
- continue;
289
- matcherSign = hookGuardSignIn(matcher[key], [...at, key], "other") !== undefined;
290
- }
291
- if (matcherSign) {
292
- reject("guard-matcher-invalid", at);
294
+ // a sign at the matcher level: a guard-event key on the matcher itself, or anywhere in its fields other than a well-formed entry list;
295
+ // the member that holds the first one (document order — the order hookGuardSignIn reports) is the rejection's `field`
296
+ const signField = hookGuardKeyOf(matcher) ??
297
+ Object.keys(matcher).find((key) => !(key === "hooks" && Array.isArray(entries)) && hookGuardSignIn(matcher[key], [...at, key], "other") !== undefined);
298
+ if (signField !== undefined) {
299
+ reject("guard-matcher-invalid", at, signField);
293
300
  return undefined;
294
301
  }
295
302
  const entrySigns = [];
296
303
  if (Array.isArray(entries))
297
304
  for (let j = 0; j < entries.length; j++)
298
- entrySigns.push(hookGuardSignIn(entries[j], [...at, "hooks", j], "other") !== undefined);
305
+ entrySigns.push(hookGuardSignIn(entries[j], [...at, "hooks", j], "other"));
299
306
  const name = Object.hasOwn(matcher, "matcher") ? matcher.matcher : undefined;
300
307
  const badField = name !== undefined && typeof name !== "string" ? "matcher" : !Array.isArray(entries) ? "hooks" : undefined;
301
308
  if (badField !== undefined || !Array.isArray(entries)) {
302
- if (guardEvent || entrySigns.includes(true))
309
+ if (guardEvent || entrySigns.some((sign) => sign !== undefined))
303
310
  reject("guard-matcher-invalid", at, badField);
304
311
  else
305
312
  drop("matcher-invalid", at, badField);
@@ -308,8 +315,9 @@ function hookMatcherRead(matcher, at, guardEvent, reject, drop) {
308
315
  const kept = [];
309
316
  for (let j = 0; j < entries.length; j++) {
310
317
  const where = [...at, "hooks", j];
311
- if (entrySigns[j]) {
312
- reject("guard-entry-invalid", where);
318
+ const sign = entrySigns[j];
319
+ if (sign !== undefined) {
320
+ reject("guard-entry-invalid", where, hookIsRecord(entries[j]) ? hookSignKeyOf(where, sign) : undefined);
313
321
  continue;
314
322
  }
315
323
  const read = hookEntryRead(entries[j]);