@sema-agent/settings-schema 1.10.0 → 2.0.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/dist/types.js CHANGED
@@ -6,10 +6,10 @@
6
6
  *
7
7
  * Detail level is deliberate (the user asked for "详略设计"): `models` is the MVP — fully specified —
8
8
  * while `scenarios` / `runtime` carry a real-but-skeletal schema to be fleshed out in later phases
9
- * (see docs/ROADMAP.md; `integrations`/`access` were deleted in 0.6.0, EXPERT-REDESIGN §9). Adding a
9
+ * (see CHANGELOG.md; `integrations`/`access` were deleted in 0.6.0, EXPERT-REDESIGN §9). Adding a
10
10
  * field is: extend the zod schema here → the form + the effective-config API pick it up.
11
11
  *
12
- * ⚠️ SECURITY BOUNDARY (see docs/SERVICE-INTEGRATION.md): the config center stores **non-secret**
12
+ * ⚠️ SECURITY BOUNDARY (see the service-integration design note): the config center stores **non-secret**
13
13
  * config only. A model's API key / a backend token is NEVER stored here as a value — we store the NAME
14
14
  * of the env var that holds it (`apiKeyEnv`, `tokenEnv`), and the agent-service reads the real secret
15
15
  * from its own environment. So a leak of this DB never leaks a credential. This is **enforced** by the
@@ -220,8 +220,7 @@ export function extraBodyUnsafe(v) {
220
220
  * serialization and persist another (the pre-R10 superRefine+transform stringified TWICE).
221
221
  *
222
222
  * `field` only NAMES the field in the message — it never changes the judgment. What this does NOT do is validate
223
- * SHAPE: a field's shape/vocabulary is the consumer's single point (1.7.2 / 1.9.0 rule), and this domain parses
224
- * all-or-nothing, so a shape rejection here would blank the whole catalog. */
223
+ * SHAPE: whose job that is, per key, is {@link PARSE_TRANSPARENT_KEYS}. */
225
224
  function vetFreeFormJson(v, field) {
226
225
  let rawUnsafe;
227
226
  try {
@@ -294,6 +293,56 @@ export const SealedApiKey = z.object({
294
293
  * 解密不依赖它,非 center 写面(本地 TOC 手写配置)缺省合法。 */
295
294
  setAt: z.string().min(1).optional(),
296
295
  });
296
+ /**
297
+ * **解析透明键登记表** —— 本包收下但**不校验其形**的键,一处列全,连同「谁校验」。
298
+ *
299
+ * 成案:1.7.0 / 1.7.1 连着两次,在一个展示用的键上设形判据,让一条坏值抹掉了**整域**(彼时整域
300
+ * all-or-nothing);此后的缓解是逐键改成 `z.unknown()` 并在各自的 JSDoc 里**各复述一遍**同一段理由 ——
301
+ * 五行同义散文 ×3,没有任何一处能回答「一共有几个这样的键、分别归谁校验」。那正是「修法给每个新键加一条
302
+ * 特判」的形。这张表把那段理由收成一处:键在这里登记,JSDoc 只留一行指过来。
303
+ *
304
+ * 1.11.0 起整域 all-or-nothing 已按类改成逐条容错(坏条目逐条丢、同域其余条目存活),所以「设形判据
305
+ * 会抹掉整域」这条**理由本身已经弱化** —— 但登记表要回答的问题没变:这些键的形归谁校验。新键要不要
306
+ * 解析透明,按「本包是不是那张形表的属主」判,不再按「怕连坐」判。
307
+ *
308
+ * 「解析透明」按**行为**定义(不按 schema 的形名):喂一组有代表性的值全收 = 本包对它的形没有任何主张。
309
+ * 所以 `ModelEntry.extraBody` **不在**本表里 —— 它有自己的形门(必须是纯 JSON 对象,要摊进请求体);
310
+ * 它开放的只是对象里的键名词表(各家 provider 的请求参数表),那是另一回事,写在它自己的 JSDoc 里。
311
+ *
312
+ * 门:`test/parse-transparent-keys.test.ts` 从 `DOMAIN_SCHEMAS` **派生**出所有解析透明的叶子,与本表
313
+ * 双向对拍 —— 多登记一行(键已经不透明了)红,少登记一行(新键静默溜进来)也红;派生遇到不认识的
314
+ * schema 形 fail-loud,不「没认出来就当没有」。
315
+ */
316
+ export const PARSE_TRANSPARENT_KEYS = [
317
+ {
318
+ domain: "models",
319
+ path: "models[].compat",
320
+ since: "1.10.0",
321
+ validatedBy: "engine-ingest",
322
+ why: "镜像引擎的 per-model wire-compat 声明(thinkingFormat 等五键);那张词表随引擎走,本包复述一份必漂。",
323
+ },
324
+ {
325
+ domain: "mcp",
326
+ path: "servers[].source",
327
+ since: "1.7.2",
328
+ validatedBy: "consumer",
329
+ why: "声明面的分组标签,开放集(部署方自己的词同样合法);标签规则(非空、≤80 字)只在消费方的投影处一处,它丢坏标签的键、保留服务器本身。",
330
+ },
331
+ {
332
+ domain: "mcp",
333
+ path: "servers[].toolFaces",
334
+ since: "1.9.0",
335
+ validatedBy: "engine-ingest",
336
+ why: "按远端工具名 key 的 face 覆盖层,形归引擎摄入侧(不合 schema 的工具不挂并通告一次);本包复述那张表必漂。",
337
+ },
338
+ {
339
+ domain: "entitlement",
340
+ path: "subjects[].overrides[].value",
341
+ since: "0.6.x",
342
+ validatedBy: "consumer",
343
+ why: "它的形由同一条 override 的 `key` 决定(一个 key 一种形),执法点在读那个 key 的消费方;在这里写死形等于把所有可覆盖键的形表搬进本包。",
344
+ },
345
+ ];
297
346
  export const ModelEntry = z.object({
298
347
  /** Catalog key + `@name` mention handle (stable, url/word-safe), e.g. "model-a". */
299
348
  name: z.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i, "letters/digits/._- , no spaces"),
@@ -343,7 +392,9 @@ export const ModelEntry = z.object({
343
392
  // before our check). The getter-free descriptor walk (extraBodyUnsafe) validates the RAW value without
344
393
  // invoking any getter; a Proxy that fakes its descriptors is caught by re-scanning the JSON clone; and the
345
394
  // .transform persists that CLONE (concrete plain JSON) so a non-idempotent toJSON can't differ at persist.
346
- // Round 2-5 hardening.
395
+ // Round 2-5 hardening. NOT in {@link PARSE_TRANSPARENT_KEYS}: this key DOES have a shape gate here (it must
396
+ // be a plain JSON object — it is spread into a request body). What is open is the KEY VOCABULARY inside that
397
+ // object: those are each provider's request params, and this package is not their owner.
347
398
  extraBody: z
348
399
  .unknown()
349
400
  .optional()
@@ -371,17 +422,13 @@ export const ModelEntry = z.object({
371
422
  * gateway that thinks by DEFAULT can only be turned off with `chat_template_kwargs.enable_thinking=false`,
372
423
  * which `extraBody` cannot carry (the engine reserves those request keys) — and without this key a catalog
373
424
  * entry that declares it is stripped at parse, so the declaration never reaches the engine at all.
425
+ * ABSENT = key not minted = today's behaviour, zero migration.
374
426
  *
375
- * **Parse-transparent** (`z.unknown()`), same rule as `McpServerSpec.source` (1.7.2) and `.toolFaces` (1.9.0):
376
- * the SHAPE and the VOCABULARY (which `thinkingFormat` words exist) are the consuming side's single point —
377
- * this package deliberately does NOT restate that table. This domain parses all-or-nothing, so a shape/word
378
- * rejection HERE would blank the entire model catalog over one bad key (the 1.7.1 upgrade regression). ABSENT
379
- * = key not minted = today's behaviour, zero migration.
380
- *
381
- * The ONE rejection that remains is the free-form SAFETY judgment shared with `extraBody`
382
- * ({@link vetFreeFormJson}): a secret-shaped key/value, an accessor, a cycle, non-JSON, or too deep to hash
383
- * later. That is not shape validation — it is the boundary every free-form value must clear to enter the
384
- * persisted config and `stableHash` (R18). */
427
+ * **Parse-transparent** — who validates the shape, and why not here: {@link PARSE_TRANSPARENT_KEYS}
428
+ * (`models.models[].compat`). The ONE rejection that remains is the free-form SAFETY judgment shared with
429
+ * `extraBody` ({@link vetFreeFormJson}): a secret-shaped key/value, an accessor, a cycle, non-JSON, or too
430
+ * deep to hash later. That is not shape validation — it is the boundary every free-form value must clear to
431
+ * enter the persisted config and `stableHash` (R18). */
385
432
  compat: z
386
433
  .unknown()
387
434
  .optional()
@@ -510,11 +557,11 @@ export const ModelsConfig = z
510
557
  ctx.addIssue({ code: z.ZodIssueCode.custom, message: `activeTierGroup "${cfg.activeTierGroup}" is not a tier group name`, path: ["activeTierGroup"] });
511
558
  }
512
559
  });
513
- // ── Domain: rosters (per-worker model roster — docs/MULTI-ROSTER.md) ───────────
560
+ // ── Domain: rosters (per-worker model roster — the multi-roster design note) ───────────
514
561
  // A named VIEW over the global `models` catalog: which models a worker exposes + its role map + @-allowlist.
515
562
  // A worker references one by name (WorkerSpec.roster); GET /effective?worker=<name> resolves it. Models stay
516
563
  // single-sourced in the `models` domain — a roster only references them by name (cross-domain ref checked at
517
- // resolution/PUT, see docs/MULTI-ROSTER.md §6). This is what lets one sema-registry serve heterogeneous
564
+ // resolution/PUT, see the multi-roster design note §6). This is what lets one sema-registry serve heterogeneous
518
565
  // instances (OA vs review 各持异构模型) instead of a single global roster.
519
566
  export const RosterEntry = z.object({
520
567
  name: z.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i, "letters/digits/._- , no spaces"),
@@ -545,7 +592,7 @@ export const RostersConfig = z
545
592
  seen.add(r.name);
546
593
  // intra-roster consistency: a role target / an @-allowlist entry pinned to a model NAME must be one
547
594
  // this roster exposes (when it's a subset). Cross-domain (the name exists in the catalog) is checked at
548
- // PUT time — see MULTI-ROSTER.md. (Both layers matter: this catches subset-internal inconsistency even
595
+ // PUT time — see the multi-roster design note. (Both layers matter: this catches subset-internal inconsistency even
549
596
  // when models is the full catalog is left to the PUT check.)
550
597
  if (r.models.length) {
551
598
  const allowed = new Set(r.models);
@@ -587,6 +634,9 @@ export const ScenarioEntry = z.object({
587
634
  // registry field would become an ambiguous precedence channel」):配置域 prompt 单入口=center
588
635
  // prompts binding(scenario 维度);引擎 API 层 TaskSpec.systemPrompt 保留(不同层,勿混)。
589
636
  // 现网无用户直删;z.object 非 strict ⇒ 存量数据带 prompt 被 strip,parse 不炸。
637
+ // 落日(1.11.0):本条是**纯墓碑**(零代码,靠 strip 生效),没有要删的兼容码;它留在这里的唯一作用
638
+ // 是让下一个读到这段 schema 的人知道 `prompt` 这个名字曾经在这里、别再铸回来。0.19.0 起存量文档带
639
+ // 它会被 `unknown-keys-dropped` 点名,所以「还有人在写它」是可观察的,不靠记忆。
590
640
  enabled: z.boolean().default(true),
591
641
  notes: z.string().optional(),
592
642
  });
@@ -1026,6 +1076,8 @@ export const EntitlementOverride = z
1026
1076
  .object({
1027
1077
  principal: z.string().min(1),
1028
1078
  key: z.string().min(1),
1079
+ /** The override's value — its SHAPE follows its `key`, so it is parse-transparent here and validated by
1080
+ * whoever enforces that key: {@link PARSE_TRANSPARENT_KEYS} (`entitlement.subjects[].overrides[].value`). */
1029
1081
  value: z.unknown(),
1030
1082
  effectiveFrom: z.string().optional(),
1031
1083
  effectiveUntil: z.string().optional(),
@@ -1282,10 +1334,11 @@ export const ExecutionPolicy = z.object({
1282
1334
  /** projectId 形状:lowercase 规范 UUID。**宽读严写**(core 口径):读面/claim 收 generic
1283
1335
  * UUID(外部/历史铸造的 v4 也认——marker 是身份不是格式声明);铸造侧(core formatProjectMarker /
1284
1336
  * center mint 端点)统一产 **uuidv7**。36 字符,天然满足 design/142 §1.2 段编码 64 上限。
1285
- * SINGLE-SOURCE 纪律:core 1.268.0 尚未公开导出其内部 UUID_RE——本常量为自持镜像,与 core 的对拍
1286
- * 走 dev-only conformance(never a runtime import,EffectiveKey/coreTypes 先例)。
1287
- * TODO(0.10.x): @sema-agent/core 下一班车公开导出 PROJECT_ID_REGEX 后(core 已承诺),
1288
- * 加 dev-only drift-lock 测试与其对拍(坐标:test/projects-0.10.test.ts「conformance」节)。 */
1337
+ * SINGLE-SOURCE 纪律:本常量是引擎那条正则的**自持镜像**(本仓不带引擎运行时依赖轴),对拍走 dev-only
1338
+ * conformance(never a runtime import,EffectiveKey/coreTypes 先例)。
1339
+ * 1.11.0:drift-lock 已建(`test/upstream-drift.test.ts` ③)—— pattern 必须与引擎逐字相同,唯一成文的差是
1340
+ * flags:本常量刻意**不带** `i`,因为它是登记簿 Record 的**键**,大小写不同的同一个 UUID 会变成两条登记行
1341
+ * (同一 project 双注册);引擎侧是识别语义(读 marker / 找回身份),放宽合理。 */
1289
1342
  export const PROJECT_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1290
1343
  export const ProjectRegistration = z.object({
1291
1344
  /** 人读名(展示/管理面用;不是身份,改名自由——身份=Record 键 projectId)。 */
@@ -1424,7 +1477,7 @@ export const CollabConfig = z.object({ templates: z.array(CollabTemplate).defaul
1424
1477
  seen.add(t.id);
1425
1478
  });
1426
1479
  });
1427
- // ── Domain: workers (声明式编排 — service design/22, docs/ORCHESTRATOR.md) ──────
1480
+ // ── Domain: workers (声明式编排 — service design/22, the orchestrator design note) ──────
1428
1481
  // 每个 worker = 一个 worker 服务实例的声明(一份 env profile)。sema-registry 存 DESIRED(逻辑参数
1429
1482
  // + secret 的 env-NAME 引用,**绝不存 secret 值**);一个 per-host reconciler 拉取后在宿主机本地解析
1430
1483
  // secret → 渲染 docker compose → up/down 收敛。worker spec ↔ service env 的精确映射见 worker-env.ts。
@@ -1653,12 +1706,12 @@ export const WorkerSpec = z.object({
1653
1706
  auto: z.boolean().default(false),
1654
1707
  requires: z.array(z.string().min(1)).default([]),
1655
1708
  /** SOFT anti-affinity group: auto workers sharing this key are spread across hosts when possible
1656
- * (preferred, never blocks placement — design SCHEDULING.md). 不填 = 无 spread 约束。 */
1709
+ * (preferred, never blocks placement — the scheduling design note). 不填 = 无 spread 约束。 */
1657
1710
  spread: z.string().min(1).optional(),
1658
1711
  })
1659
1712
  .optional(),
1660
1713
  /**
1661
- * per-worker roster 引用 (docs/MULTI-ROSTER.md)。指向 `rosters` 域里一个命名 roster;该 worker 的 service 拉
1714
+ * per-worker roster 引用 (the multi-roster design note)。指向 `rosters` 域里一个命名 roster;该 worker 的 service 拉
1662
1715
  * `GET /api/config/effective?worker=<name>` 时,sema-registry 用这个 roster 解析它专属的 models/roles/@白名单。
1663
1716
  * 不填 → 该 worker 拿全局 models(向后兼容)。**不进 `workerSpecHash`**(改 roster 只让 service 重拉、不重建容器)。
1664
1717
  */
@@ -1801,13 +1854,13 @@ export const HostStatus = z.object({
1801
1854
  /** 探测时刻(ISO)——机器视图 hover 注明数据口径与此时间(§2-5:是分配额口径的底数,非实时负载)。 */
1802
1855
  ts: z.string().min(1),
1803
1856
  });
1804
- // ── Domain: skills (中心化 skill 分发 — docs/MCP-SKILLS.md, Roadmap B1) ─────────
1857
+ // ── Domain: skills (中心化 skill 分发 — the mcp/skills design note, Roadmap B1) ─────────
1805
1858
  // A skill is a prompt-level capability: frontmatter (name/description/scenarios) + markdown `content`
1806
1859
  // the service feeds into the agent's model context when the skill is invoked (core progressive disclosure
1807
1860
  // — NOT the base system prompt). Still centrally-delivered agent instructions. The center stores the FULL skill
1808
1861
  // (incl. content, admin-edited); /effective ships a MANIFEST (content → contentHash) and the service
1809
1862
  // lazily pulls the body by hash (slice 2). Targeting is by `scenarios[]`, filtered service-side exactly
1810
- // like the in-image `skillsForScenario` (MCP-SKILLS.md §3.1, §6 Q2). Applied at service boot/reconcile,
1863
+ // like the in-image `skillsForScenario` (the mcp/skills design note §3.1, §6 Q2). Applied at service boot/reconcile,
1811
1864
  // NOT hot (restart-to-apply, same as models/roles). ⚠️ content is centrally-delivered agent instructions
1812
1865
  // (injected into the model on invoke) → writing this domain is gated to `publisher` + the publish gate
1813
1866
  // (§3.3, DOMAIN_WRITE_ROLE in store.ts).
@@ -1818,7 +1871,7 @@ export const SkillSpec = z.object({
1818
1871
  description: z.string().default(""),
1819
1872
  /** Scenarios this skill applies to; empty = all scenarios (service-side filter, same as skillsForScenario). */
1820
1873
  scenarios: z.array(z.string().min(1)).default([]),
1821
- /** Per-roster targeting (v2, docs/MCP-SKILLS.md §6 Q2): roster NAMEs this skill is for. Empty = all rosters.
1874
+ /** Per-roster targeting (v2, the mcp/skills design note §6 Q2): roster NAMEs this skill is for. Empty = all rosters.
1822
1875
  * Filtered at GET /effective?worker=<name> by the worker's roster; the unscoped global pull ships all. */
1823
1876
  rosters: z.array(z.string().min(1)).default([]),
1824
1877
  /** The markdown body injected into the model context on invoke (progressive disclosure). Stored in full here; shipped as a hash in /effective. */
@@ -1845,7 +1898,7 @@ export const SkillsConfig = z
1845
1898
  export const SkillsManifestSchema = z.object({
1846
1899
  skills: z.array(SkillSpec.omit({ content: true }).extend({ contentHash: z.string().min(1) })).default([]),
1847
1900
  });
1848
- // ── Domain: mcp (MCP server 注册表 — docs/MCP-SKILLS.md, Roadmap B1) ────────────
1901
+ // ── Domain: mcp (MCP server 注册表 — the mcp/skills design note, Roadmap B1) ────────────
1849
1902
  // sema-registry = an MCP-server REGISTRY: a scenario mounts a subset by `scenarios[]`. Maps onto core's
1850
1903
  // `McpServerSpec = { name, transport: {kind:"stdio",command,args?,env?} | {kind:"http",url,headers?}, allowTools?, contentOrigin?, source? }`.
1851
1904
  // SECRET BOUNDARY (§3.1, design/15): a transport's env/header VALUES may be secret → the center stores only
@@ -1942,23 +1995,19 @@ export const McpServerSpec = z.object({
1942
1995
  * consumer can group servers by where they were declared). An OPEN set: CC's `plugin` / `user` /
1943
1996
  * `project` / `local` are conventions, not a vocabulary — a deployment's own word is equally legal, so
1944
1997
  * no closed table here (a closed table would reject the operator's grouping word at parse). ABSENT =
1945
- * no label (`.optional()`, no default — a label has no default tier). 🔴 ZERO rules here (`z.unknown()`):
1946
- * the label RULE (string, non-empty, ≤80 chars) lives in ONE place — the consumer's projection helper,
1947
- * which drops a malformed label's KEY, keeps the server and records the drop. Nothing may live here:
1948
- * this domain parses all-or-nothing, so ANY rejection at parse (1.7.0 `min/max`, 1.7.1 `z.string()`
1949
- * against a `null`) lets one bad label default the ENTIRE mcp domain — every server gone, silently when
1950
- * no onWarning is wired — a blast radius a display label must never have. 1.7.2 makes the key
1951
- * parse-transparent: whatever JSON value the file carries reaches the consumer verbatim. Display-only:
1952
- * no gate / policy / trust
1953
- * effect (the trust axis is `contentOrigin` above, which stays a closed table). Before this key existed
1954
- * both legs stripped it (default-strip object); this is the declaration half of the consumer-side fix
1955
- * (ticket B-036 / S-143), the projection half lives in the consumer. */
1998
+ * no label (`.optional()`, no default — a label has no default tier). Display-only: no gate / policy /
1999
+ * trust effect (the trust axis is `contentOrigin` above, which stays a closed table). Before this key
2000
+ * existed both legs stripped it (default-strip object); this is the declaration half of the
2001
+ * consumer-side fix, the projection half lives in the consumer.
2002
+ *
2003
+ * **Parse-transparent** — who validates the shape, and why not here: {@link PARSE_TRANSPARENT_KEYS}
2004
+ * (`mcp.servers[].source`). */
1956
2005
  source: z.unknown().optional(),
1957
- /** 1.9.0 —— core 7.8.0(design/388 片 1 F7)`toolFaces` 覆盖层:按远端工具名 key 的 face 声明
2006
+ /** 1.9.0 —— 引擎的 `toolFaces` 覆盖层:按远端工具名 key 的 face 声明
1958
2007
  * (`{ family?, pathTarget?: {param, aliases?, access, skillScopeEligible?}, renderHints?, ruleFace? }`)。
1959
- * 与 `source` 同一条规矩:**解析透明**(`z.unknown()`),形的校验属 core 摄入侧(不合 schema 的工具**不挂**并
1960
- * 通告一次 `config.tool_face_invalid`);本仓不复述那张表 —— 这一域 all-or-nothing,任一字段解析拒都会
1961
- * 让整套 mcp 服务器静默消失。缺席 = 不铸键。 */
2008
+ * 缺席 = 不铸键。
2009
+ *
2010
+ * **解析透明** —— 形归谁校验、为什么不放这里:{@link PARSE_TRANSPARENT_KEYS}(`mcp.servers[].toolFaces`)。 */
1962
2011
  toolFaces: z.unknown().optional(),
1963
2012
  notes: z.string().optional(),
1964
2013
  });
@@ -2196,7 +2245,7 @@ export const DOMAIN_SCHEMAS = {
2196
2245
  };
2197
2246
  // ── Effective-config PROVENANCE (the unified shape TOC `sema config --effective` and TOB `/effective?principal=`
2198
2247
  // step-5 trace both emit, so "why is this value what it is" renders identically across both seats). Settled
2199
- // via the center control-plane design gate③ (docs/design/CENTER-CONTROL-PLANE-DESIGN.md §R2-T/§10). A pure
2248
+ // via the center control-plane design gate③ (see the center control-plane design note §R2-T/§10). A pure
2200
2249
  // CONTRACT type — no runtime dep on the engine; the axis VALUES (enforcement/lockBehavior) mirror @sema-agent/
2201
2250
  // core's safety vocabulary, validated by a dev-only conformance test (never a runtime import).
2202
2251
  /** One provenance source in a key's resolution trail. Two orthogonal axes: the TOC file LAYER, or the TOB
@@ -2226,7 +2275,7 @@ export const EffectiveKey = z.object({
2226
2275
  * rollback-able). Lets user changes share the one audit stream the Audit page already renders. */
2227
2276
  export const RBAC_AUDIT = "rbac";
2228
2277
  /** Audit category for secret write-through (value injected into the orchestrator secret source) — name + action
2229
- * ONLY, never the value (config_change.value=null), same discipline as RBAC_AUDIT. docs/SELF-SERVICE-FLEET.md §3. */
2278
+ * ONLY, never the value (config_change.value=null), same discipline as RBAC_AUDIT. See the self-service-fleet design note §3. */
2230
2279
  export const SECRETS_AUDIT = "secrets";
2231
2280
  // Single source: derived from DOMAIN_SCHEMAS (insertion order = menu order) so the two can't drift —
2232
2281
  // adding a domain to DOMAIN_SCHEMAS is enough. Object.keys preserves declaration order.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/settings-schema",
3
- "version": "1.10.0",
3
+ "version": "2.0.0",
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",
@@ -118,6 +118,7 @@
118
118
  "test": "vitest run",
119
119
  "test:watch": "vitest",
120
120
  "e2e": "node examples/toc-e2e.mjs",
121
+ "drift:cc": "node scripts/extract-cc-hook-events.mjs",
121
122
  "prepare": "npm run build",
122
123
  "prepublishOnly": "npm run build && npm run test",
123
124
  "publish:npm": "npm publish --access public --@sema-agent:registry=https://registry.npmjs.org/ --\"//registry.npmjs.org/:_authToken=${NPM_TOKEN}\""