@sema-agent/settings-schema 14.0.0 → 14.1.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
@@ -6,6 +6,50 @@
6
6
  > `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
7
7
  > 包名是**历史记录**,如实保留不追改。
8
8
 
9
+ ## Unreleased
10
+
11
+ ## 14.1.0 — 2026-10-18 — minor:请求面 MCP 判官与服务端请求腿同严(申报名首尾空白 · `env` / `headers` 只收纯数据记录 · 两只回显描述器的回显口径)
12
+
13
+ > 档位:**minor**(型面加性:`McpEntryKeyIssue` 新增两个可选格 `member?` / `fault?`;三处两层两答的判官变严是 bug 修复);导出名单不变(请求面判官与描述器自 14.0.0 起都在主 barrel:`isUsableWireName` · `describeBadWireName` · `isContentOriginWord` ·
14
+ > `describeBadContentOrigin` · `readRequestMcpTransport` · `readMcpIdentityHeaders` · 值种类描述件与 `TOOL_WIRE_NAME_MAX_CHARS`,本版无增删,`docs/API-SURFACE.md` 不动)。
15
+ > 型面一处加性:`McpEntryKeyIssue` 多两个**可选**格 `member?` / `fault?`(不改既有的 `path` / `reason`;只有 `env` / `headers` 记录上的坏形带)。
16
+
17
+ - **行为修复(申报名首尾空白 = 坏名)**。`isUsableWireName` 对首尾带空白的名返回 `false`;此前它只判非串 / 空串 / 超 `TOOL_WIRE_NAME_MAX_CHARS`,所以 `" fs"` / `"fs\n"` 被收。
18
+ 「空白」就是 `String.prototype.trim` 剥的那一组(ECMAScript 的 WhiteSpace + LineTerminator 共 25 个码点:空格 / 制表 / 换行 / NBSP / BOM / 全角空格 / 行段分隔符等);不在这一组里的不可见字符(零宽空格 U+200B 等)不算空白,本判官不管。申报名是线上工具名 `mcp__<server>__<tool>` 的身份片:带着空白的那个名字不是
19
+ 另一个名字、是坏名 —— 所以**不 trim、不规范化**(与「不截断」同向:改写一个名字就是换一个身份,挂在原名上的 allowTools / toolFaces 收窄声明会一起对不上号)。内部空白(`"a b"`)照旧合法,本判官不扩判。
20
+ `describeBadWireName` 对这类名给一个种类词:`" a" (leading or trailing whitespace)`;又超界的一句说全:`… (129 chars > 128, leading or trailing whitespace)`;摘要的回显口径见下面「两只回显描述器」一条,
21
+ 既有的超界 / `empty` / 非串三形的句式不变。判与说共读同一个「首尾空白」定义,不会各按各的口径。
22
+ 三问 —— **谁需要**:两层同答。装配口(client-core)对首尾空白的申报名拒、而本判官收,同一个名字两层两答;判官在契约包,各层读同一份才不会再分岔。
23
+ **谁受伤**:申报名带首尾空白的配置 / 请求 —— 读本判官的服务端请求腿与声明腿(壳的发送前预检若读它亦然)此前放行(服务端受理后以带空白的名挂上),现在按坏名处理;重放存量提交体的消费方按各自对坏名的处置走
24
+ (服务端的重放腿:摘掉这条 server 并点名),所以存量里带空白名的 parked 任务续跑时会少这台 server。**补偿**:去掉空白 —— 拒句点名 `leading or trailing whitespace`(制表 / 换行这类控制空白在回显里被剥掉,看得出是空白靠这个种类词);
25
+ `"a b"` 这类内部空白无须改。
26
+ 钉:`test/mcp-request-entry-14.0.0.test.ts` ⑥(首尾空白各形,含 `trim` 那一组 25 个码点的前后位 · 组外不可见字符照旧合法 · 内部空白照旧 · 摘要既有三形不动 + 空白种类词 + 超界并存一句说全 · 判官与摘要按逐名事实表同一定义)·
27
+ ⑦(消费方切 import 要用的判官 / 描述器 / 常量与三个结果型逐名在主 barrel)。
28
+ - **行为修复(`env` / `headers` 只收纯数据的「名 → 串」记录)**。`readRequestMcpTransport` 读 `transport.env`(stdio)/ `transport.headers`(http)此前判「每个值都是串」(`Object.values` 全是串):
29
+ `new Map()`、`Set`、带自有串属性的类实例照收并原样交出(交出的就是那个 `Map` / 实例),JSON 文本铸出的自有 `__proto__` 键原样带出,交出的还是调用方的**同一个对象**,一个抛错的访问器会让读器抛出去。
30
+ 现在与服务端请求腿同严:记录交不交 = 原型是 `Object.prototype` 或 `null`(纯数据)且每个自有成员(不可枚举的也算,访问器调一次)的值都是串。
31
+ `Map` / `Set` / 类实例 / 建在原型上的记录 ⇒ 这个键整张不交,`lost` 记一处,原因句 `must be a plain object of string values — got an object that is not plain data (…)`;不是对象 / 数组 / `null` ⇒ 同一条(`got an array` 等);
32
+ 有成员值不是串(或访问器抛了)⇒ 整张不交,句子点名第一个坏成员(`its member "PORT" must be a string — got a number`);读原型与成员名时代理的 trap 抛了 ⇒ 同样整张不交(读器对递来的任何对象都不抛,这一条服务端读法是抛出去,本包更稳);
33
+ 名叫 `__proto__` 的成员(引擎的声明收据不收这个名)⇒ 记录照交、副本里没有它,`lost` 单记一处、路径 `transport.env.__proto__`,它的值也不是串时整张不交、一句说全两件事。
34
+ **交出的是副本**(原型 `Object.prototype`),改调用方的对象不影响读后的形。`isStringRecord` 与读器是同一个定义(包内没有第二把更宽的尺)。
35
+ 机读位:`McpEntryKeyIssue` 多两个可选格 —— `member`(成员名的回显形,可直接进日志与句子)与 `fault`(闭集词 `not_plain_record` / `member_not_string` / `member_name_reserved`),
36
+ 只有 `env` / `headers` 记录上的坏形带,调用方分流不必解析句子;句子 `reason` 仍是消费方拒句的来源(逐字同服务端请求腿,期望句取自服务端 7.132.0 读器的实测输出)。
37
+ - **行为修复(两只回显描述器用服务端的回显口径)**。`describeBadWireName` / `describeBadContentOrigin` 此前用 `JSON.stringify`(控制字符转义成 `\n` 字样、**双向控制字符原样输出**),名只截 32 字、内容出处值连引号截 64 字;
38
+ 现在:剥掉 C0 控制符、DEL 与双向控制字符(Unicode `Bidi_Control=Yes` 全集 12 个码点:U+061C · U+200E/200F · U+202A–202E · U+2066–2069;一个 RLO 能把一行字读反,一个换行能伪造一行日志)—— 是**删**,不转义;
39
+ 剥完再数,超 64 字截到 64 加 `…`(省略号在引号内);引号形 `"…"`,内部的引号与反斜杠逐字。清洗面不含 C1 与 U+2028 / U+2029(加宽是行为变更,要各自的判据)。`N chars > 128` 的 N 仍是送来的原长度。
40
+ 双向这一半的属主是引擎(`BIDI_CONTROL_CHARS`),本包运行期不依赖引擎,以镜像承载并由上游漂移门 ⑨ 逐码点全扫(U+0000–U+10FFFF)对拍。
41
+ 三问(这两条一起答)—— **谁需要**:服务端请求腿(与壳的发送前预检)。它们此前各留一份自家的传输读器与两只描述器,因为本包的读法比请求腿松;读法同严之后切到 import,请求面没有第二份判官。
42
+ **谁受伤**:① 直接调读器、把 `Map` / `Set` / 类实例当 `env` 或 `headers` 递进来的调用方 —— 此前本包放行(服务端请求腿已拒),现在整张不交并记 `lost`,受理新提交的门据它 400,两层同拒;
43
+ ② 带自有 `__proto__` 成员的 JSON 体 —— 现在 `lost` 点名它、副本里没有它;③ 读坏名 / 坏内容出处回显的运维 —— 换行不再显示成 `\n`、双向字符不再原样出现、名的回显从 32 字变 64 字、引号内不再转义。
44
+ **补偿**:把 `Map` / 类实例展开成纯对象再递(`Object.fromEntries(map)` / `{ ...instance }`);删掉 JSON 体里的 `__proto__` 键;分流用 `lost` 的 `path` / `member` / `fault`,不解析句子。
45
+ 钉:`test/mcp-request-entry-14.0.0.test.ts` ⑧(`Map` / `Set` / 类实例 / 原型上的记录 / `Date` · 非对象值 · 非串成员 · 自有 `__proto__` 成员与值也坏的并列 · 副本 · 隐藏成员与访问器 · 成员名回显清洗 · `lost` 读序 · 正控 · `isStringRecord` 与读器同答)·
46
+ ⑨(C0 / DEL / 12 个双向码点逐个剥,两只描述器各验 · C1 与 U+2028 / U+2029 不剥 · 先剥后数 · 64 字界 · 引号形 · 空白种类词与清洗同在)· `test/upstream-drift.test.ts` ⑨(回显清洗类对引擎 `BIDI_CONTROL_CHARS` 逐码点全扫,两只描述器各扫一遍 + 门有牙)。
47
+ - **下游(表态制:认领 + 排期 / 显式拒绝 + 理由 / 申请裁定)**:
48
+ **server**:申报名判官已读本包,抬到本版随之收严,零代码改动;请求腿的传输读器(`env` / `headers` 半场)与两只回显描述器的读法与服务端 7.132.0 同严,可整个换成 import、删自家三份
49
+ (描述器切过来时空白名多一个种类词);A2A 对端 `headers` 的记录读法(自家的 `plain-string-record.ts`)仍在服务端,本包不导出记录读器。
50
+ **cli**:可直接 import(14.0.0 起);读它即与服务端受理同答,本版无导出变化。**client-core**:对 14.0.0 档里「经包转出判官要加运行期依赖,请选」的答 = 选**构建期抄本**、不加运行期依赖;抄本的对拍钉请对本版
51
+ `isUsableWireName` 的真值表(首尾空白 = `trim` 那一组 ⇒ 坏名,内部空白合法,界 128);读器与描述器若也抄,请跟本版的读法(纯数据记录、回显口径)。**sdk**:零引用。
52
+
9
53
  ## 14.0.0 — 2026-10-17 — major:prompts 域单轴(中心 artifact v3 的结构镜像;声明轴与 v1 catalog 轴退役,一次性迁移 SS-M10)· 请求面 MCP 条目判官入包
10
54
 
11
55
  > 档位:**major**。两件事,一条律各一句:
package/README.md CHANGED
@@ -56,7 +56,8 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
56
56
  - **存量**:一次性迁移 SS-M10 删掉空的 `bindings: []`;非空 `bindings` 与 `catalog` 被拒、原样留着 —— 从中心编辑器重新发布(本地 `config.d` 的 `bindings`
57
57
  引擎从未读过,删键即可)。
58
58
  - **请求面 MCP 条目**:`isUsableWireName` / `readRequestMcpTransport` / `readMcpIdentityHeaders` / `isContentOriginWord` 住本包(判据与原因句与服务端请求腿逐字同),
59
- 壳的发送前预检与服务端受理同答。
59
+ 壳的发送前预检与服务端受理同答。申报名判官 `isUsableWireName` 另判首尾空白(坏名;不 trim、不规范化,内部空白照旧合法);`readRequestMcpTransport` 的 `env` / `headers`
60
+ 只收纯数据的「名 → 串」记录(`Map` / `Set` / 类实例 / 自有 `__proto__` 成员不收,交出的是副本,`lost` 条目带 `member` / `fault` 机读位);两只回显描述器剥控制与双向字符、截 64 字,与服务端回显同口径。
60
61
  - **部署序**:中心(发布方)与服务端同一维护窗升级,中心先;窗内新旧两端互拒整份候选、停在上一份好配置。细节与逐条三问见 CHANGELOG 14.0.0。
61
62
 
62
63
  ## 升级指引:10.x → 11.0.0 · 持久面:一次性迁移(`./one-time-migrations`)
@@ -462,8 +463,8 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
462
463
  written, sources}` — judged on the expanded target, echoed only as written; the managed layer (whose `allowManagedMcpServersOnly` counts)
463
464
  is the source whose tier in `SETTINGS_SOURCE_CONTRACT` is `managed`.
464
465
  - **Request-plane MCP entry judges** (`mcp-request-entry.ts`, 14.0.0): `isUsableWireName` / `describeBadWireName` (the engine's wire-name bound
465
- `TOOL_WIRE_NAME_MAX_CHARS`, mirrored), `isContentOriginWord` / `describeBadContentOrigin`, `readMcpIdentityHeaders`, `readRequestMcpTransport`
466
- (hard faults `ok:false` naming the key path; soft faults dropped by the reader and listed in `lost`) and the value-shape describers — one reading for
466
+ `TOOL_WIRE_NAME_MAX_CHARS`, mirrored; a name with leading or trailing whitespace is a bad name too — never trimmed, never normalised; inner whitespace stays legal), `isContentOriginWord` / `describeBadContentOrigin`, `readMcpIdentityHeaders`, `readRequestMcpTransport`
467
+ (hard faults `ok:false` naming the key path; soft faults dropped by the reader and listed in `lost`; `env` / `headers` are read as plain-data string records — a `Map`, a class instance or an own `__proto__` member is not one, the delivered record is a copy, and a `lost` entry for a record carries machine-readable `member` / `fault`; the two describers strip control and bidi characters and cut at 64 characters, the service's echo form) and the value-shape describers — one reading for
467
468
  the service's request legs and the shell's pre-send check. A `toolFaces` declaration stays parse-transparent here: its shape is the engine ingest side's to judge, and none of the tool-face vocabularies is mirrored in this package.
468
469
  - **UI language** (`ui-language.ts`, 13.0.0): `semaUiLanguage` (`en` / `zh`, category `ui`, user settings) + `readUiLanguage`.
469
470
  - **Cross-session settings** (`cross-session.ts`): the two `settings.json` keys `crossSessionInbound`
@@ -1 +1 @@
1
- 14.0.0
1
+ 14.1.0
@@ -7,13 +7,16 @@
7
7
  * 或者自己抄一份 —— 两份判据就是将来漂开的那一份。现在判官在契约包,服务端与壳读同一份,同一条条目在两处同受理 / 同拒、同一个键路径、同一句原因。
8
8
  *
9
9
  * ## 语义是搬来的,不是重写的
10
- * 判据与原因句逐字等于消费方 7.131.x 的那一份(钉:`test/mcp-request-entry-14.0.0.test.ts` 逐例搬了消费方既有钉的夹具与期望)。
10
+ * 判据与原因句逐字等于消费方请求腿的那一份(钉:`test/mcp-request-entry-14.0.0.test.ts` 逐例搬了消费方既有钉的夹具与期望)。
11
11
  * 本模块只答「这一格的形」;拒绝的整句(条目下标、条目名的回显清洗、收尾句)与「拒还是降级」的后果仍是消费方按自己的腿定的 —— 判据一份,后果按腿分。
12
+ * 与最初搬来时相比有三处收严,都是收到与消费方请求腿同严、不是新语义,消费方改 import 这一份之后不再各留一份旧判据:申报名判官另判**首尾空白**;
13
+ * `env` / `headers` 只收**纯数据**的「名 → 串」记录(Map / 类实例 / 自有 `__proto__` 成员不收,交出的是副本);两只回显描述器用消费方的回显口径(剥控制与双向字符、截 64 字、引号形)。
12
14
  *
13
15
  * ## 依赖的引擎事实(镜像 + 漂移门)
14
16
  * · 申报名的线长界 {@link TOOL_WIRE_NAME_MAX_CHARS} = 引擎同名常量(值级对拍:上游漂移门 ⑧);
15
17
  * · 请求面传输形 {@link RequestMcpTransport} = 引擎 MCP 条目的传输形,http 形多一个本包承载的 `assertionHeader`(编译期双向对拍:门 ⑧);
16
18
  * · 内容出处词表 = {@link MCP_CONTENT_ORIGINS}(已有的镜像,门 ⑥);
19
+ * · 回显清洗类的双向半场 = 引擎 `BIDI_CONTROL_CHARS`(镜像,逐码点全扫对拍:门 ⑨;本包运行期不依赖引擎);
17
20
  * · url 前缀守卫 = `./url-shape` 的 `startsWithHttpScheme`(本包判 URL 的正则只在那一个模块)。
18
21
  *
19
22
  * ## 与配置面的关系(如实)
@@ -30,23 +33,31 @@ export declare function describeNotStringArray(v: unknown): string;
30
33
  export declare function describeNotStringRecord(v: unknown): string;
31
34
  /** 字符串数组(空数组合法)。 */
32
35
  export declare function isStringArray(v: unknown): v is string[];
33
- /** 字符串值的对象(空对象合法;数组不是)。 */
36
+ /**
37
+ * 纯数据的「名 → 串」记录(空对象合法;数组、`Map`、类实例、自有 `__proto__` 成员、有非串成员的对象都不是)。与传输读器读 `env` / `headers` 是**同一个**定义
38
+ * ({@link readPlainStringRecord}):包内没有第二把更宽的尺。
39
+ */
34
40
  export declare function isStringRecord(v: unknown): v is Record<string, string>;
35
41
  /** 引擎 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像:一台 MCP server(或 A2A 对端)的申报名会成为线上工具名的命名空间段,引擎对超界名是**拒**(不截断)。 */
36
42
  export declare const TOOL_WIRE_NAME_MAX_CHARS = 128;
37
43
  /**
38
- * 这个申报名能用吗。严判:非串、空串、超 {@link TOOL_WIRE_NAME_MAX_CHARS} 一律不可用。不截断、不规范化 —— 截短一个名字就是**换一个身份**,
39
- * 而被换掉身份的那台 server 上挂的 allowTools / toolFaces 收窄声明会一起对不上号。
44
+ * 这个申报名能用吗。严判:非串、空串、超 {@link TOOL_WIRE_NAME_MAX_CHARS}、**首尾带空白**一律不可用。不截断、不 trim、不规范化 —— 截短或剥掉一个名字就是**换一个身份**,
45
+ * 而被换掉身份的那台 server 上挂的 allowTools / toolFaces 收窄声明会一起对不上号。首尾空白 = 坏名(它不是另一个名字:名字是 `mcp__<server>__<tool>` 的身份片,
46
+ * 带着空白的一端无从与不带的那个对上号);内部空白(`a b`)照旧是合法名,本判官不扩判。
40
47
  */
41
48
  export declare function isUsableWireName(v: unknown): v is string;
42
- /** 坏名的**可打印**摘要(进拒句 / 告警元数据):种类、`empty`,或截到 32 字加长度事实 —— 绝不把任意长度的申报串原样回显。 */
49
+ /**
50
+ * 坏名的**可打印**摘要(进拒句 / 告警元数据):种类、`empty`,或引号形的回显加原因 —— 绝不把任意长度的申报串原样回显。回显口径见 {@link quotedCallerText}
51
+ * (剥控制与双向字符、截 64 字)。串的原因是:超界 ⇒ `N chars > 128`(N 是送来的原长度,不是剥完的);首尾带空白 ⇒ `leading or trailing whitespace`
52
+ * (制表 / 换行这类控制空白在回显里被剥掉,所以看得出是空白靠这个种类词,不靠引号里的样子);两件事并存 ⇒ 一句说全(`N chars > 128, leading or trailing whitespace`)。
53
+ */
43
54
  export declare function describeBadWireName(v: unknown): string;
44
55
  /**
45
56
  * 在内容出处词表({@link MCP_CONTENT_ORIGINS})内吗。严判:非串与大小写不符(`"LOCAL"`)一律不在 —— 引擎的词是字面量联合、不做规范化;
46
57
  * 替它「宽容一下」等于自造第二份值域语义,而这条轴上宽容的方向恰好是**放宽信任**(把一个写坏的声明读成 `"local"`)。
47
58
  */
48
59
  export declare function isContentOriginWord(v: unknown): v is McpContentOrigin;
49
- /** 坏内容出处值的**可打印**摘要:串 JSON 化、64 字截断加 `…`;非串只说种类。 */
60
+ /** 坏内容出处值的**可打印**摘要:串 ⇒ 引号形的回显({@link quotedCallerText}:剥控制与双向字符、截 64 字);非串只说种类。 */
50
61
  export declare function describeBadContentOrigin(v: unknown): string;
51
62
  /**
52
63
  * 一份 http 传输上两个身份头(`principalHeader` / `assertionHeader`)的读法:字符串才收;**`principalHeader` 胜** —— 它在场(不论值形)时
@@ -69,10 +80,21 @@ export type RequestMcpTransport = {
69
80
  principalHeader?: string;
70
81
  assertionHeader?: string;
71
82
  };
72
- /** 条目内一处坏形:`path` = 条目内的键路径(`transport.url`),`reason` = 判官的原因句(只说形与词表,不回显值)。 */
83
+ /**
84
+ * 条目内一处坏形:`path` = 条目内的键路径(`transport.url`),`reason` = 判官的原因句(只说形与词表,不回显值)。
85
+ *
86
+ * `env` / `headers` 这两张「名 → 串」记录上的坏形另带两格机读位,调用方分流不必解析句子:`fault` = 闭集词 ——
87
+ * · `not_plain_record`:这个键的值不是纯数据记录(不是对象 / 数组 / `null`,或原型不是 `Object.prototype` 与 `null`:`Map`、`Set`、类实例);整张记录不交;
88
+ * · `member_not_string`:有一个成员的值不是串(或访问器抛了);整张记录不交,`member` 点名它;
89
+ * · `member_name_reserved`:有一个成员叫 `__proto__`(引擎的声明收据拒这个名)。成员的值是串 ⇒ 只剔这个成员、其余照交,`path` 是成员级(`transport.env.__proto__`);
90
+ * 成员的值也不是串 ⇒ 整张记录不交,`path` 是键级,句子一次说全两件事 —— 名坏优先、值坏并列,所以词仍是 `member_name_reserved`。
91
+ * `member` 是成员名的**回显形**(剥控制与双向字符、截 64 字加 `…`),可直接进日志与句子,不是回查原对象的键。其余的坏形(`args` / `url` / `principalHeader` / ……)两格都不带。
92
+ */
73
93
  export interface McpEntryKeyIssue {
74
94
  readonly path: string;
75
95
  readonly reason: string;
96
+ readonly member?: string;
97
+ readonly fault?: "not_plain_record" | "member_not_string" | "member_name_reserved";
76
98
  }
77
99
  /** {@link readRequestMcpTransport} 的结果:`ok:false` = 硬坏形(这条 server 无从挂载);`ok:true` 的 `lost` = 读器**丢掉**的可选子键(server 照挂、少一个声明)。 */
78
100
  export type RequestMcpTransportRead = {
@@ -86,8 +108,11 @@ export type RequestMcpTransportRead = {
86
108
  /**
87
109
  * 请求面 `transport` 的**唯一读器**:读器投影 transport 用它,发送前 / 受理时的判形读它报的 `lost` —— 同一只函数两个消费点,「读器丢了什么」与「门拒什么」不可能分岔。
88
110
  * · 硬坏形(`transport` 不是对象 / `kind` 出闭集 / stdio 缺 `command` / http 的 `url` 不是 http(s))⇒ `ok:false`,`path` 是读器点名的键路径;
89
- * · 软坏形(`args` 不是串数组 / `env` `headers` 含非串值 / `principalHeader` 非串)⇒ 读器**丢这个键**(重放存量提交体时的升级前读法),并把它记进 `lost`
111
+ * · 软坏形(`args` 不是串数组 / `env` `headers` 不是纯数据的串记录 / `principalHeader` 非串)⇒ 读器**丢这个键**(重放存量提交体时的升级前读法),并把它记进 `lost`
90
112
  * (受理新提交的门据它响亮拒)。`assertionHeader` 不在 `lost` 里:两个身份头的键组合与 `assertionHeader` 的值形由调用方在读之前对原始传输判。
113
+ * · `env` / `headers` 只收**纯数据**的记录({@link readPlainStringRecord}):原型必须是 `Object.prototype` 或 `null`(`Map`、`Set`、类实例整张不收),每个自有成员的值都是串;
114
+ * 交出的是**副本**(不是调用方的对象,改原对象不影响读后的形)。名叫 `__proto__` 的成员(JSON 文本铸得出这个自有键,对象字面量写不出)不进副本、单独记进 `lost`。
115
+ * 坏形的 `lost` 条目带机读位(见 {@link McpEntryKeyIssue})。
91
116
  * url 的拒句不回显值:它常规地带凭据(userinfo)。
92
117
  */
93
118
  export declare function readRequestMcpTransport(t: unknown): RequestMcpTransportRead;
@@ -7,13 +7,16 @@
7
7
  * 或者自己抄一份 —— 两份判据就是将来漂开的那一份。现在判官在契约包,服务端与壳读同一份,同一条条目在两处同受理 / 同拒、同一个键路径、同一句原因。
8
8
  *
9
9
  * ## 语义是搬来的,不是重写的
10
- * 判据与原因句逐字等于消费方 7.131.x 的那一份(钉:`test/mcp-request-entry-14.0.0.test.ts` 逐例搬了消费方既有钉的夹具与期望)。
10
+ * 判据与原因句逐字等于消费方请求腿的那一份(钉:`test/mcp-request-entry-14.0.0.test.ts` 逐例搬了消费方既有钉的夹具与期望)。
11
11
  * 本模块只答「这一格的形」;拒绝的整句(条目下标、条目名的回显清洗、收尾句)与「拒还是降级」的后果仍是消费方按自己的腿定的 —— 判据一份,后果按腿分。
12
+ * 与最初搬来时相比有三处收严,都是收到与消费方请求腿同严、不是新语义,消费方改 import 这一份之后不再各留一份旧判据:申报名判官另判**首尾空白**;
13
+ * `env` / `headers` 只收**纯数据**的「名 → 串」记录(Map / 类实例 / 自有 `__proto__` 成员不收,交出的是副本);两只回显描述器用消费方的回显口径(剥控制与双向字符、截 64 字、引号形)。
12
14
  *
13
15
  * ## 依赖的引擎事实(镜像 + 漂移门)
14
16
  * · 申报名的线长界 {@link TOOL_WIRE_NAME_MAX_CHARS} = 引擎同名常量(值级对拍:上游漂移门 ⑧);
15
17
  * · 请求面传输形 {@link RequestMcpTransport} = 引擎 MCP 条目的传输形,http 形多一个本包承载的 `assertionHeader`(编译期双向对拍:门 ⑧);
16
18
  * · 内容出处词表 = {@link MCP_CONTENT_ORIGINS}(已有的镜像,门 ⑥);
19
+ * · 回显清洗类的双向半场 = 引擎 `BIDI_CONTROL_CHARS`(镜像,逐码点全扫对拍:门 ⑨;本包运行期不依赖引擎);
17
20
  * · url 前缀守卫 = `./url-shape` 的 `startsWithHttpScheme`(本包判 URL 的正则只在那一个模块)。
18
21
  *
19
22
  * ## 与配置面的关系(如实)
@@ -50,31 +53,65 @@ export function describeNotStringRecord(v) {
50
53
  export function isStringArray(v) {
51
54
  return Array.isArray(v) && v.every((a) => typeof a === "string");
52
55
  }
53
- /** 字符串值的对象(空对象合法;数组不是)。 */
56
+ /**
57
+ * 纯数据的「名 → 串」记录(空对象合法;数组、`Map`、类实例、自有 `__proto__` 成员、有非串成员的对象都不是)。与传输读器读 `env` / `headers` 是**同一个**定义
58
+ * ({@link readPlainStringRecord}):包内没有第二把更宽的尺。
59
+ */
54
60
  export function isStringRecord(v) {
55
- return !!v && typeof v === "object" && !Array.isArray(v) && Object.values(v).every((x) => typeof x === "string");
61
+ const read = readPlainStringRecord(v);
62
+ return read.ok && !read.reservedMember;
56
63
  }
57
64
  function isRecord(v) {
58
65
  return v !== null && typeof v === "object" && !Array.isArray(v);
59
66
  }
67
+ // ── 调用方写的串的回显口径 ──────────────────────────────────────────────────────────────────────────
68
+ /**
69
+ * 回显清洗类:C0 控制符 + DEL + 双向控制字符(Unicode `Bidi_Control=Yes` 全集 12 个码点 —— 一个 RLO 能把一行字读反,一个换行能伪造一行日志)。清洗面刻意不含 C1 与
70
+ * U+2028 / U+2029:加宽是行为变更,要各自的判据。双向这一半的属主是引擎(`BIDI_CONTROL_CHARS`),这里是镜像,由上游漂移门逐码点全扫对拍。
71
+ */
72
+ const CONTROL_AND_BIDI_STRIP_RE = /[\u0000-\u001f\u007f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
73
+ /** 回显的字符上限(剥完控制与双向字符之后数)。 */
74
+ const CALLER_TEXT_ECHO_MAX_CHARS = 64;
75
+ /** 调用方写的串的回显形(不带引号):剥控制与双向字符 → 超 {@link CALLER_TEXT_ECHO_MAX_CHARS} 字截断加 `…`。拒句 / 告警元数据里的有界回显,串可能任意长、可能带换行。 */
76
+ function echoCallerText(text) {
77
+ const clean = text.replace(CONTROL_AND_BIDI_STRIP_RE, "");
78
+ return clean.length > CALLER_TEXT_ECHO_MAX_CHARS ? `${clean.slice(0, CALLER_TEXT_ECHO_MAX_CHARS)}…` : clean;
79
+ }
80
+ /** 句子里点名一个值的写法:{@link echoCallerText} 外加一对双引号;内部的引号与反斜杠逐字(不转义)。 */
81
+ function quotedCallerText(text) {
82
+ return `"${echoCallerText(text)}"`;
83
+ }
60
84
  // ── 申报名的线长界 ──────────────────────────────────────────────────────────────────────────────────
61
85
  /** 引擎 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像:一台 MCP server(或 A2A 对端)的申报名会成为线上工具名的命名空间段,引擎对超界名是**拒**(不截断)。 */
62
86
  export const TOOL_WIRE_NAME_MAX_CHARS = 128;
63
87
  /**
64
- * 这个申报名能用吗。严判:非串、空串、超 {@link TOOL_WIRE_NAME_MAX_CHARS} 一律不可用。不截断、不规范化 —— 截短一个名字就是**换一个身份**,
65
- * 而被换掉身份的那台 server 上挂的 allowTools / toolFaces 收窄声明会一起对不上号。
88
+ * 首尾带空白吗 —— 「空白」就是 `String.prototype.trim` 剥的那一组(ECMAScript 的 WhiteSpace + LineTerminator:空格 / 制表 / 换行 / NBSP / BOM / 全角空格 / 行段分隔符等)。
89
+ * {@link isUsableWireName} 与 {@link describeBadWireName} 共读这一个定义,判与说不会各按各的口径。
90
+ */
91
+ const hasEdgeWhitespace = (v) => v.trim() !== v;
92
+ /**
93
+ * 这个申报名能用吗。严判:非串、空串、超 {@link TOOL_WIRE_NAME_MAX_CHARS}、**首尾带空白**一律不可用。不截断、不 trim、不规范化 —— 截短或剥掉一个名字就是**换一个身份**,
94
+ * 而被换掉身份的那台 server 上挂的 allowTools / toolFaces 收窄声明会一起对不上号。首尾空白 = 坏名(它不是另一个名字:名字是 `mcp__<server>__<tool>` 的身份片,
95
+ * 带着空白的一端无从与不带的那个对上号);内部空白(`a b`)照旧是合法名,本判官不扩判。
66
96
  */
67
97
  export function isUsableWireName(v) {
68
- return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS;
98
+ return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS && !hasEdgeWhitespace(v);
69
99
  }
70
- /** 坏名的**可打印**摘要(进拒句 / 告警元数据):种类、`empty`,或截到 32 字加长度事实 —— 绝不把任意长度的申报串原样回显。 */
100
+ /**
101
+ * 坏名的**可打印**摘要(进拒句 / 告警元数据):种类、`empty`,或引号形的回显加原因 —— 绝不把任意长度的申报串原样回显。回显口径见 {@link quotedCallerText}
102
+ * (剥控制与双向字符、截 64 字)。串的原因是:超界 ⇒ `N chars > 128`(N 是送来的原长度,不是剥完的);首尾带空白 ⇒ `leading or trailing whitespace`
103
+ * (制表 / 换行这类控制空白在回显里被剥掉,所以看得出是空白靠这个种类词,不靠引号里的样子);两件事并存 ⇒ 一句说全(`N chars > 128, leading or trailing whitespace`)。
104
+ */
71
105
  export function describeBadWireName(v) {
72
106
  if (typeof v !== "string")
73
107
  return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
74
108
  if (v.length === 0)
75
109
  return "empty";
76
- const head = v.length > 32 ? `${JSON.stringify(v.slice(0, 32))}…` : JSON.stringify(v);
77
- return `${head} (${v.length} chars > ${TOOL_WIRE_NAME_MAX_CHARS})`;
110
+ const head = quotedCallerText(v);
111
+ const tooLong = `${v.length} chars > ${TOOL_WIRE_NAME_MAX_CHARS}`;
112
+ if (!hasEdgeWhitespace(v))
113
+ return `${head} (${tooLong})`;
114
+ return `${head} (${v.length > TOOL_WIRE_NAME_MAX_CHARS ? `${tooLong}, ` : ""}leading or trailing whitespace)`;
78
115
  }
79
116
  // ── 内容出处词 ──────────────────────────────────────────────────────────────────────────────────────
80
117
  const CONTENT_ORIGIN_WORDS = new Set(MCP_CONTENT_ORIGINS);
@@ -85,10 +122,9 @@ const CONTENT_ORIGIN_WORDS = new Set(MCP_CONTENT_ORIGINS);
85
122
  export function isContentOriginWord(v) {
86
123
  return typeof v === "string" && CONTENT_ORIGIN_WORDS.has(v);
87
124
  }
88
- /** 坏内容出处值的**可打印**摘要:串 JSON 化、64 字截断加 `…`;非串只说种类。 */
125
+ /** 坏内容出处值的**可打印**摘要:串 ⇒ 引号形的回显({@link quotedCallerText}:剥控制与双向字符、截 64 字);非串只说种类。 */
89
126
  export function describeBadContentOrigin(v) {
90
- const raw = typeof v === "string" ? JSON.stringify(v) : v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
91
- return raw.length > 64 ? `${raw.slice(0, 64)}…` : raw;
127
+ return typeof v === "string" ? quotedCallerText(v) : v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
92
128
  }
93
129
  // ── 两个身份头 ──────────────────────────────────────────────────────────────────────────────────────
94
130
  /**
@@ -105,8 +141,11 @@ const transportBad = (path, reason) => ({ ok: false, issue: { path, reason } });
105
141
  /**
106
142
  * 请求面 `transport` 的**唯一读器**:读器投影 transport 用它,发送前 / 受理时的判形读它报的 `lost` —— 同一只函数两个消费点,「读器丢了什么」与「门拒什么」不可能分岔。
107
143
  * · 硬坏形(`transport` 不是对象 / `kind` 出闭集 / stdio 缺 `command` / http 的 `url` 不是 http(s))⇒ `ok:false`,`path` 是读器点名的键路径;
108
- * · 软坏形(`args` 不是串数组 / `env` `headers` 含非串值 / `principalHeader` 非串)⇒ 读器**丢这个键**(重放存量提交体时的升级前读法),并把它记进 `lost`
144
+ * · 软坏形(`args` 不是串数组 / `env` `headers` 不是纯数据的串记录 / `principalHeader` 非串)⇒ 读器**丢这个键**(重放存量提交体时的升级前读法),并把它记进 `lost`
109
145
  * (受理新提交的门据它响亮拒)。`assertionHeader` 不在 `lost` 里:两个身份头的键组合与 `assertionHeader` 的值形由调用方在读之前对原始传输判。
146
+ * · `env` / `headers` 只收**纯数据**的记录({@link readPlainStringRecord}):原型必须是 `Object.prototype` 或 `null`(`Map`、`Set`、类实例整张不收),每个自有成员的值都是串;
147
+ * 交出的是**副本**(不是调用方的对象,改原对象不影响读后的形)。名叫 `__proto__` 的成员(JSON 文本铸得出这个自有键,对象字面量写不出)不进副本、单独记进 `lost`。
148
+ * 坏形的 `lost` 条目带机读位(见 {@link McpEntryKeyIssue})。
110
149
  * url 的拒句不回显值:它常规地带凭据(userinfo)。
111
150
  */
112
151
  export function readRequestMcpTransport(t) {
@@ -119,15 +158,14 @@ export function readRequestMcpTransport(t) {
119
158
  return transportBad("transport.command", `must be a non-empty string (the command to run) — got ${describeValueShape(tr.command)}`);
120
159
  if (tr.args !== undefined && !isStringArray(tr.args))
121
160
  lost.push({ path: "transport.args", reason: `must be an array of strings — got ${describeNotStringArray(tr.args)}` });
122
- if (tr.env !== undefined && !isStringRecord(tr.env))
123
- lost.push({ path: "transport.env", reason: `must be an object of string values — got ${describeNotStringRecord(tr.env)}` });
161
+ const env = readTransportRecord("transport.env", tr.env, lost);
124
162
  return {
125
163
  ok: true,
126
164
  transport: {
127
165
  kind: "stdio",
128
166
  command: tr.command,
129
167
  ...(isStringArray(tr.args) ? { args: tr.args } : {}),
130
- ...(isStringRecord(tr.env) ? { env: tr.env } : {}),
168
+ ...(env !== undefined ? { env } : {}),
131
169
  },
132
170
  lost,
133
171
  };
@@ -137,8 +175,7 @@ export function readRequestMcpTransport(t) {
137
175
  if (typeof tr.url !== "string" || !startsWithHttpScheme(tr.url))
138
176
  return transportBad("transport.url", "must be an absolute http(s) URL (put credentials in transport.headers, never in the URL)");
139
177
  const ids = readMcpIdentityHeaders(tr);
140
- if (tr.headers !== undefined && !isStringRecord(tr.headers))
141
- lost.push({ path: "transport.headers", reason: `must be an object of string values — got ${describeNotStringRecord(tr.headers)}` });
178
+ const headers = readTransportRecord("transport.headers", tr.headers, lost);
142
179
  if (tr.principalHeader !== undefined && ids.principalHeader === undefined) {
143
180
  lost.push({ path: "transport.principalHeader", reason: `must be a string (the HTTP header NAME the end-user principal is injected into) — got ${describeValueShape(tr.principalHeader)}` });
144
181
  }
@@ -147,7 +184,7 @@ export function readRequestMcpTransport(t) {
147
184
  transport: {
148
185
  kind: "http",
149
186
  url: tr.url,
150
- ...(isStringRecord(tr.headers) ? { headers: tr.headers } : {}),
187
+ ...(headers !== undefined ? { headers } : {}),
151
188
  ...ids,
152
189
  },
153
190
  lost,
@@ -155,3 +192,75 @@ export function readRequestMcpTransport(t) {
155
192
  }
156
193
  return transportBad("transport.kind", `must be "stdio" or "http" — got ${describeValueShape(tr.kind)}`);
157
194
  }
195
+ // ── 「名 → 串」记录的读法(`env` / `headers`)────────────────────────────────────────────────────────
196
+ /** 引擎的声明收据不收的成员名(core `mcp-server-spec`:按赋值拷贝它会变成原型):JSON 文本铸得出这个**自有**键,对象字面量写不出。 */
197
+ const RESERVED_MEMBER_NAME = "__proto__";
198
+ const RESERVED_MEMBER_FAULT = `no member of a declaration may be named "${RESERVED_MEMBER_NAME}"`;
199
+ /**
200
+ * 读一份「名 → 串」记录。记录交不交:是对象(不是数组 / `null`),原型是 `Object.prototype` 或 `null`(纯数据:`Map`、`Set`、类实例、建在原型上的记录都不是),
201
+ * 且每个成员的值都是串。成员 = `Object.getOwnPropertyNames` 的每一个名字(不可枚举的也算 —— 引擎的收据按全部自有属性名读),值按属性描述读:数据属性取值,访问器调一次(访问器或描述 trap 抛了同判为坏)。
202
+ * 读原型与成员名时代理 trap 抛了 ⇒ 整张不交(读器对调用方递来的任何对象都不抛;`JSON` 存体里不会有代理,这条只落在直接调读器的嵌入方身上)。
203
+ * 交出去的是副本(原型 `Object.prototype`、成员全可枚举),调用方的对象不被交出去、也不被改;名叫 `__proto__` 的成员不进副本,`reservedMember` 记下它。
204
+ * 名叫 `__proto__` 而值也坏的成员,句子一次说全两件事(名坏在前、值坏并列):调用方照值改完再撞第二条是两步才改对。
205
+ */
206
+ function readPlainStringRecord(v) {
207
+ if (v === null || typeof v !== "object" || Array.isArray(v))
208
+ return { ok: false, fault: "not_plain_record", detail: v === null ? "got null" : Array.isArray(v) ? "got an array" : `got a ${typeof v}` };
209
+ let proto;
210
+ let names;
211
+ try {
212
+ proto = Object.getPrototypeOf(v);
213
+ names = Object.getOwnPropertyNames(v);
214
+ }
215
+ catch {
216
+ // 代理的 trap 抛了:读不出原型与成员名的对象不是纯数据。读器不抛出去 —— 重放存量提交体的腿被一次抛错砖死比整键不交糟,而「整键不交」是响亮的(进 `lost`)。
217
+ return { ok: false, fault: "not_plain_record", detail: "got an object that could not be inspected (a proxy trap threw)" };
218
+ }
219
+ if (proto !== Object.prototype && proto !== null) {
220
+ return { ok: false, fault: "not_plain_record", detail: "got an object that is not plain data (a class instance, a Map, process.env, or a record built on a prototype; copy it into a plain object)" };
221
+ }
222
+ const record = {};
223
+ let reservedMember = false;
224
+ for (const name of names) {
225
+ let value;
226
+ let unreadable = false;
227
+ try {
228
+ const d = Object.getOwnPropertyDescriptor(v, name);
229
+ value = d === undefined ? undefined : Object.hasOwn(d, "value") ? d.value : d.get === undefined ? undefined : Reflect.apply(d.get, v, []);
230
+ }
231
+ catch {
232
+ unreadable = true;
233
+ }
234
+ if (unreadable || typeof value !== "string") {
235
+ const valueFault = unreadable ? "could not be read (its getter threw)" : `must be a string — got ${describeValueShape(value)}`;
236
+ const reserved = name === RESERVED_MEMBER_NAME;
237
+ return {
238
+ ok: false,
239
+ fault: reserved ? "member_name_reserved" : "member_not_string",
240
+ member: echoCallerText(name),
241
+ detail: `its member "${echoCallerText(name)}" ${reserved ? `is not allowed (${RESERVED_MEMBER_FAULT}) and its value ${valueFault}` : valueFault}`,
242
+ };
243
+ }
244
+ if (name === RESERVED_MEMBER_NAME)
245
+ reservedMember = true;
246
+ else
247
+ record[name] = value;
248
+ }
249
+ return { ok: true, record, reservedMember };
250
+ }
251
+ /**
252
+ * `transport.env` / `transport.headers` 的读法(两键同一只):缺席 ⇒ 缺席;记录不交 ⇒ `lost` 一处点名**这个键**(不交的是整张记录;坏在某个成员时句子与 `member` 点名它);
253
+ * 交 ⇒ 纯数据副本,名叫 `__proto__` 的成员在 `lost` 里单记一处、路径 `<键>.__proto__`。
254
+ */
255
+ function readTransportRecord(path, raw, lost) {
256
+ if (raw === undefined)
257
+ return undefined;
258
+ const read = readPlainStringRecord(raw);
259
+ if (!read.ok) {
260
+ lost.push({ path, reason: `must be a plain object of string values — ${read.detail}`, ...(read.member !== undefined ? { member: read.member } : {}), fault: read.fault });
261
+ return undefined;
262
+ }
263
+ if (read.reservedMember)
264
+ lost.push({ path: `${path}.${RESERVED_MEMBER_NAME}`, reason: RESERVED_MEMBER_FAULT, member: RESERVED_MEMBER_NAME, fault: "member_name_reserved" });
265
+ return read.record;
266
+ }
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  llms-full.txt — 自包含全文 · 消费方(使用者)视角 · @sema-agent/settings-schema
3
- 重写日期: 2026-10-17 | 包版本: 14.0.0
3
+ 重写日期: 2026-10-18 | 包版本: 14.1.0
4
4
  生成方式: 无生成脚本——按当前树 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版。
5
5
  证据约定: 每条"它怎么工作"的断言附 (文件: 符号) 坐标,刻意不用行号(行号逐版腐烂;符号名由
6
6
  typecheck 与 test/package-surface.test.ts、test/docs-hygiene.test.ts 等常驻门钉住)。无证据推断显式标【推断】。
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
3
- 重写日期: 2026-10-17 | 包版本: 14.0.0
3
+ 重写日期: 2026-10-18 | 包版本: 14.1.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 相等、
@@ -109,8 +109,9 @@ llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
109
109
  (src/http-hook-allowlists.ts)
110
110
  - **MCP 名单判决**(13.0.0):`mcpServerPolicyVerdict(server, layers)` → `McpPolicyBlock{name, list, rule, target, written, sources}` 或 `undefined`;
111
111
  判按展开后目标、回显只按展开前写法(stdio 只给可执行名 + 参数个数);托管层 = 源表档 `managed` 的源(层只带源名)。(src/mcp-server-policy.ts)
112
- - **请求面 MCP 条目判官**(14.0.0,`src/mcp-request-entry.ts`):`isUsableWireName` / `describeBadWireName`(引擎线上名长界 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像)·
113
- `isContentOriginWord` / `describeBadContentOrigin` · `readMcpIdentityHeaders` · `readRequestMcpTransport`(硬坏形 `ok:false` 点名键路径;软坏形读器丢键并记进 `lost`)
112
+ - **请求面 MCP 条目判官**(14.0.0,`src/mcp-request-entry.ts`):`isUsableWireName` / `describeBadWireName`(引擎线上名长界 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像;首尾空白 = 坏名,不 trim 不规范化,内部空白合法)·
113
+ `isContentOriginWord` / `describeBadContentOrigin`(回显剥控制与双向字符、截 64 字、引号形)· `readMcpIdentityHeaders` · `readRequestMcpTransport`(硬坏形 `ok:false` 点名键路径;软坏形读器丢键并记进 `lost`;
114
+ `env` / `headers` 只收纯数据的「名 → 串」记录,`Map` / 类实例 / 自有 `__proto__` 成员不收、交副本,`lost` 条目带 `member` / `fault` 机读位)
114
115
  + 值种类描述件 —— 服务端请求腿与壳的发送前预检同读一份(判据与原因句逐字同)。`toolFaces` 声明在本包解析透明,形归引擎摄入侧判,本包不镜像工具面词表。
115
116
  - **prompts 域单轴**(14.0.0):只装中心 artifact(存储 `PromptsConfig = { artifact?, workers? }` / 下发 `EffectivePromptsWire = { schemaVersion: 2, artifact }` /
116
117
  唯一选择读法 `promptArtifactFor`);本包镜像 artifact v3 的结构与上界,语义交引擎 verify;13.x 两轴是墓碑,存量经一次性迁移 SS-M10。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/settings-schema",
3
- "version": "14.0.0",
3
+ "version": "14.1.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",