@sema-agent/settings-schema 13.0.1 → 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 +134 -0
- package/README.md +19 -1
- package/conformance/VERSION +1 -1
- package/conformance/baseline.json +1 -3
- package/conformance/cases/c01-entry-salvage-then-typo/expected.json +1 -3
- package/conformance/cases/c02-hosts-clamp-stacked/expected.json +1 -3
- package/conformance/cases/c03-literal-dotted-top-key/expected.json +1 -3
- package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +1 -3
- package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +1 -3
- package/conformance/cases/c08-limits-unknown-carried-64/expected.json +1 -3
- package/conformance/cases/c09-limits-unknown-carried-65/expected.json +1 -3
- package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +1 -3
- package/conformance/cases/c11-unicode-unknown-keys/expected.json +1 -3
- package/conformance/cases/c12-prototype-name-top-keys/expected.json +1 -3
- package/conformance/cases/c13-models-field-null/expected.json +1 -3
- package/conformance/cases/c14-models-field-empty-array/expected.json +1 -3
- package/conformance/cases/c15-models-field-number/expected.json +1 -3
- package/conformance/cases/c16-deep-nesting-205/expected.json +1 -3
- package/conformance/cases/c17-empty-domain-file/expected.json +1 -3
- package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +1 -3
- package/conformance/cases/c19-wire-envelope-no-version/expected.json +1 -3
- package/conformance/cases/c21-governance-empty-object-wins/expected.json +1 -3
- package/conformance/cases/c23-mixed-validity-models/expected.json +1 -3
- package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +1 -3
- package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +1 -3
- package/conformance/cases/c26-hosts-array-1024/expected.json +1 -3
- package/conformance/cases/c27-hosts-array-1025/expected.json +1 -3
- package/conformance/cases/c28-large-document-over-1mb/expected.json +1 -3
- package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +1 -3
- package/conformance/cases/c30-version-triple-a-first/expected.json +1 -3
- package/conformance/cases/c31-version-triple-b-changed/expected.json +1 -3
- package/conformance/cases/c32-version-triple-a-restored/expected.json +1 -3
- package/conformance/cases/c33-limits-nested-carried-key/expected.json +1 -3
- package/conformance/cases/c34-skills-content-vs-manifest/expected.json +1 -3
- package/conformance/cases/c35-portable-domain-file-is-null/expected.json +1 -3
- package/conformance/cases/c36-skills-entry-without-content/expected.json +1 -3
- package/conformance/cases/c37-skills-field-null/expected.json +1 -3
- package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +1 -3
- package/conformance/cases/c39-hidden-file-in-config-d/expected.json +1 -3
- package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +1 -3
- package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +1 -3
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +1 -3
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +1 -3
- package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +1 -3
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +1 -3
- package/conformance/cases/c46-s811-host-name-url-userinfo-withheld/expected.json +1 -3
- package/conformance/cases/c47-s811-roles-key-url-userinfo-withheld/expected.json +1 -3
- package/conformance/cases/c48-s935-model-headers-transported/expected.json +1 -3
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +1 -3
- package/conformance/cases/c50-s784-lane-host-legal/expected.json +1 -3
- package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +1 -3
- package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +1 -3
- package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +1 -3
- package/conformance/cases/c54-s784-lane-adb-legal/expected.json +1 -3
- package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +1 -3
- package/conformance/cases/c56-s784-lane-device-legal/expected.json +1 -3
- package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +1 -3
- package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +1 -3
- package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +1 -3
- package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +1 -3
- package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +1 -3
- package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +1 -3
- package/conformance/cases/c63-s784-host-two-replicas/expected.json +1 -3
- package/conformance/cases/c64-s784-multitenant-local-store/expected.json +1 -3
- package/conformance/cases/c65-s784-pg-without-db/expected.json +1 -3
- package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +1 -3
- package/conformance/cases/c67-s784-retired-field-loud/expected.json +1 -3
- package/conformance/cases/c68-s784-device-local-store/expected.json +1 -3
- package/conformance/cases/c69-deep-encoded-token-past-every-reading/expected.json +1 -3
- package/conformance/cases/c70-rewritten-token-in-front-of-encoded-pem-header/expected.json +1 -3
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/expected.json +1 -3
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/expected.json +1 -3
- package/conformance/cases/c73-projects-extended-registration-reads/expected.json +1 -3
- package/conformance/cases/c74-projects-over-bound-refused/expected.json +1 -3
- package/conformance/cases/c75-projects-patch-remote-uniqueness/expected.json +1 -3
- package/conformance/cases/c76-projects-entry-salvage-nested/expected.json +1 -3
- package/conformance/cases/c77-prompts-center-artifact-carried/expected.json +252 -0
- package/conformance/cases/c77-prompts-center-artifact-carried/input.json +93 -0
- package/conformance/cases/c77-prompts-center-artifact-carried/meta.json +5 -0
- package/conformance/cases/c78-prompts-empty-bindings-refused/expected.json +82 -0
- package/conformance/cases/c78-prompts-empty-bindings-refused/input.json +11 -0
- package/conformance/cases/c78-prompts-empty-bindings-refused/meta.json +5 -0
- package/conformance/cases/c79-prompts-bad-worker-artifact-not-salvaged/expected.json +82 -0
- package/conformance/cases/c79-prompts-bad-worker-artifact-not-salvaged/input.json +129 -0
- package/conformance/cases/c79-prompts-bad-worker-artifact-not-salvaged/meta.json +5 -0
- package/conformance/cases/c80-prompts-wire-v2-verbatim/expected.json +195 -0
- package/conformance/cases/c80-prompts-wire-v2-verbatim/input.json +65 -0
- package/conformance/cases/c80-prompts-wire-v2-verbatim/meta.json +5 -0
- package/conformance/cases/c81-prompts-declaration-bindings-refused/expected.json +82 -0
- package/conformance/cases/c81-prompts-declaration-bindings-refused/input.json +23 -0
- package/conformance/cases/c81-prompts-declaration-bindings-refused/meta.json +5 -0
- package/conformance/cases/c82-prompts-v1-catalog-refused/expected.json +82 -0
- package/conformance/cases/c82-prompts-v1-catalog-refused/input.json +35 -0
- package/conformance/cases/c82-prompts-v1-catalog-refused/meta.json +5 -0
- package/conformance/cases/c83-prompts-bad-overlay-not-salvaged/expected.json +82 -0
- package/conformance/cases/c83-prompts-bad-overlay-not-salvaged/input.json +56 -0
- package/conformance/cases/c83-prompts-bad-overlay-not-salvaged/meta.json +5 -0
- package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +1 -3
- package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +1 -3
- package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +1 -3
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +1 -3
- package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +1 -3
- package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +1 -3
- package/conformance/cases/cli04-env-value-with-newline/expected.json +1 -3
- package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +1 -3
- package/conformance/cases/t01-absent-domain-key-not-null/expected.json +1 -3
- package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +1 -3
- package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +1 -3
- package/conformance/cases/z01-every-domain-empty-object/expected.json +2 -6
- package/conformance/cases/z02-every-domain-unknown-key/expected.json +2 -6
- package/dist/config-fns.d.ts +7 -3
- package/dist/config-fns.js +19 -16
- package/dist/conformance/derive.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp-request-entry.d.ts +118 -0
- package/dist/mcp-request-entry.js +266 -0
- package/dist/migration-receipt.d.ts +1 -1
- package/dist/migration-receipt.js +1 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/one-time-migrations.d.ts +1 -1
- package/dist/one-time-migrations.js +39 -0
- package/dist/safety-merge-spec.d.ts +6 -6
- package/dist/types.d.ts +438 -355
- package/dist/types.js +161 -136
- package/dist/url-shape.d.ts +8 -0
- package/dist/url-shape.js +10 -0
- package/docs/API-SURFACE.md +41 -27
- package/docs/CONFORMANCE.md +1 -1
- package/docs/CONSUMER-INTEGRATION.md +3 -2
- package/llms-full.txt +14 -11
- package/llms.txt +8 -2
- package/package.json +2 -2
- package/dist/prompts-digest.d.ts +0 -16
- package/dist/prompts-digest.js +0 -49
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 请求面 MCP 条目(一条任务提交体 `mcpServers[i]`)的形判官 —— 14.0.0 起住本包:一只读法,消费方的请求腿 / 中心腿 / 壳的发送前预检都 import 这一份。
|
|
3
|
+
* 纯函数、浏览器安全(唯一的值依赖是本包自己的内容出处词表)。
|
|
4
|
+
*
|
|
5
|
+
* ## 律(一条)
|
|
6
|
+
* **一条请求面 MCP 条目的每一格「能不能用」只有一只判官。** 此前这些判官是消费方私有的:壳拿不到,只能在发送前披露「判不准、交给服务端」,
|
|
7
|
+
* 或者自己抄一份 —— 两份判据就是将来漂开的那一份。现在判官在契约包,服务端与壳读同一份,同一条条目在两处同受理 / 同拒、同一个键路径、同一句原因。
|
|
8
|
+
*
|
|
9
|
+
* ## 语义是搬来的,不是重写的
|
|
10
|
+
* 判据与原因句逐字等于消费方请求腿的那一份(钉:`test/mcp-request-entry-14.0.0.test.ts` 逐例搬了消费方既有钉的夹具与期望)。
|
|
11
|
+
* 本模块只答「这一格的形」;拒绝的整句(条目下标、条目名的回显清洗、收尾句)与「拒还是降级」的后果仍是消费方按自己的腿定的 —— 判据一份,后果按腿分。
|
|
12
|
+
* 与最初搬来时相比有三处收严,都是收到与消费方请求腿同严、不是新语义,消费方改 import 这一份之后不再各留一份旧判据:申报名判官另判**首尾空白**;
|
|
13
|
+
* `env` / `headers` 只收**纯数据**的「名 → 串」记录(Map / 类实例 / 自有 `__proto__` 成员不收,交出的是副本);两只回显描述器用消费方的回显口径(剥控制与双向字符、截 64 字、引号形)。
|
|
14
|
+
*
|
|
15
|
+
* ## 依赖的引擎事实(镜像 + 漂移门)
|
|
16
|
+
* · 申报名的线长界 {@link TOOL_WIRE_NAME_MAX_CHARS} = 引擎同名常量(值级对拍:上游漂移门 ⑧);
|
|
17
|
+
* · 请求面传输形 {@link RequestMcpTransport} = 引擎 MCP 条目的传输形,http 形多一个本包承载的 `assertionHeader`(编译期双向对拍:门 ⑧);
|
|
18
|
+
* · 内容出处词表 = {@link MCP_CONTENT_ORIGINS}(已有的镜像,门 ⑥);
|
|
19
|
+
* · 回显清洗类的双向半场 = 引擎 `BIDI_CONTROL_CHARS`(镜像,逐码点全扫对拍:门 ⑨;本包运行期不依赖引擎);
|
|
20
|
+
* · url 前缀守卫 = `./url-shape` 的 `startsWithHttpScheme`(本包判 URL 的正则只在那一个模块)。
|
|
21
|
+
*
|
|
22
|
+
* ## 与配置面的关系(如实)
|
|
23
|
+
* 配置面的 MCP 条目(`McpServerSpec`,部署声明:凭据只许 env-NAME 引用)与请求面(提交体:可带字面凭据 `env` / `headers`)是两份契约,本模块只管后者。
|
|
24
|
+
* 两份契约对「名」与「url」今天给的判据不同(配置面:名按字符集、url 按 `NON_SECRET_URL`;请求面:名按长度界、url 按 http(s) 前缀),本版刻意不动
|
|
25
|
+
* (语义逐字搬),是一项已知待收的分歧。
|
|
26
|
+
*/
|
|
27
|
+
import { MCP_CONTENT_ORIGINS } from "./types.js";
|
|
28
|
+
import { startsWithHttpScheme } from "./url-shape.js";
|
|
29
|
+
// ── 值的种类描述(拒句只说「你写的是什么形」,不回显值:值可能带凭据、可能任意长)──────────────────────────────────────
|
|
30
|
+
/** 值的**种类**(不回显值本身)。 */
|
|
31
|
+
export function describeValueShape(v) {
|
|
32
|
+
if (v === undefined)
|
|
33
|
+
return "nothing (the key is absent)";
|
|
34
|
+
if (v === null)
|
|
35
|
+
return "null";
|
|
36
|
+
if (Array.isArray(v))
|
|
37
|
+
return "an array";
|
|
38
|
+
if (typeof v === "string")
|
|
39
|
+
return v.length === 0 ? "an empty string" : `a string of ${v.length} characters`;
|
|
40
|
+
if (typeof v === "object")
|
|
41
|
+
return "an object";
|
|
42
|
+
return `a ${typeof v}`;
|
|
43
|
+
}
|
|
44
|
+
/** 「不是字符串数组」的种类:数组里混了非串元素说那一句,否则回落到 {@link describeValueShape}。 */
|
|
45
|
+
export function describeNotStringArray(v) {
|
|
46
|
+
return Array.isArray(v) ? "an array with a non-string element" : describeValueShape(v);
|
|
47
|
+
}
|
|
48
|
+
/** 「不是字符串值的对象」的种类:对象里混了非串值说那一句,否则回落到 {@link describeValueShape}。 */
|
|
49
|
+
export function describeNotStringRecord(v) {
|
|
50
|
+
return isRecord(v) ? "an object with a non-string value" : describeValueShape(v);
|
|
51
|
+
}
|
|
52
|
+
/** 字符串数组(空数组合法)。 */
|
|
53
|
+
export function isStringArray(v) {
|
|
54
|
+
return Array.isArray(v) && v.every((a) => typeof a === "string");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 纯数据的「名 → 串」记录(空对象合法;数组、`Map`、类实例、自有 `__proto__` 成员、有非串成员的对象都不是)。与传输读器读 `env` / `headers` 是**同一个**定义
|
|
58
|
+
* ({@link readPlainStringRecord}):包内没有第二把更宽的尺。
|
|
59
|
+
*/
|
|
60
|
+
export function isStringRecord(v) {
|
|
61
|
+
const read = readPlainStringRecord(v);
|
|
62
|
+
return read.ok && !read.reservedMember;
|
|
63
|
+
}
|
|
64
|
+
function isRecord(v) {
|
|
65
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
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
|
+
}
|
|
84
|
+
// ── 申报名的线长界 ──────────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
/** 引擎 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像:一台 MCP server(或 A2A 对端)的申报名会成为线上工具名的命名空间段,引擎对超界名是**拒**(不截断)。 */
|
|
86
|
+
export const TOOL_WIRE_NAME_MAX_CHARS = 128;
|
|
87
|
+
/**
|
|
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`)照旧是合法名,本判官不扩判。
|
|
96
|
+
*/
|
|
97
|
+
export function isUsableWireName(v) {
|
|
98
|
+
return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS && !hasEdgeWhitespace(v);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 坏名的**可打印**摘要(进拒句 / 告警元数据):种类、`empty`,或引号形的回显加原因 —— 绝不把任意长度的申报串原样回显。回显口径见 {@link quotedCallerText}
|
|
102
|
+
* (剥控制与双向字符、截 64 字)。串的原因是:超界 ⇒ `N chars > 128`(N 是送来的原长度,不是剥完的);首尾带空白 ⇒ `leading or trailing whitespace`
|
|
103
|
+
* (制表 / 换行这类控制空白在回显里被剥掉,所以看得出是空白靠这个种类词,不靠引号里的样子);两件事并存 ⇒ 一句说全(`N chars > 128, leading or trailing whitespace`)。
|
|
104
|
+
*/
|
|
105
|
+
export function describeBadWireName(v) {
|
|
106
|
+
if (typeof v !== "string")
|
|
107
|
+
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
108
|
+
if (v.length === 0)
|
|
109
|
+
return "empty";
|
|
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)`;
|
|
115
|
+
}
|
|
116
|
+
// ── 内容出处词 ──────────────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
const CONTENT_ORIGIN_WORDS = new Set(MCP_CONTENT_ORIGINS);
|
|
118
|
+
/**
|
|
119
|
+
* 在内容出处词表({@link MCP_CONTENT_ORIGINS})内吗。严判:非串与大小写不符(`"LOCAL"`)一律不在 —— 引擎的词是字面量联合、不做规范化;
|
|
120
|
+
* 替它「宽容一下」等于自造第二份值域语义,而这条轴上宽容的方向恰好是**放宽信任**(把一个写坏的声明读成 `"local"`)。
|
|
121
|
+
*/
|
|
122
|
+
export function isContentOriginWord(v) {
|
|
123
|
+
return typeof v === "string" && CONTENT_ORIGIN_WORDS.has(v);
|
|
124
|
+
}
|
|
125
|
+
/** 坏内容出处值的**可打印**摘要:串 ⇒ 引号形的回显({@link quotedCallerText}:剥控制与双向字符、截 64 字);非串只说种类。 */
|
|
126
|
+
export function describeBadContentOrigin(v) {
|
|
127
|
+
return typeof v === "string" ? quotedCallerText(v) : v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
128
|
+
}
|
|
129
|
+
// ── 两个身份头 ──────────────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
/**
|
|
131
|
+
* 一份 http 传输上两个身份头(`principalHeader` / `assertionHeader`)的读法:字符串才收;**`principalHeader` 胜** —— 它在场(不论值形)时
|
|
132
|
+
* `assertionHeader` 不收。所以读后的形**结构上不可能同带两键**;同设(一份声明给出两个答案)由调用方在读之前对**原始**传输判(判的是写了什么,不是读后留下什么)。
|
|
133
|
+
*/
|
|
134
|
+
export function readMcpIdentityHeaders(transport) {
|
|
135
|
+
return {
|
|
136
|
+
...(typeof transport.principalHeader === "string" ? { principalHeader: transport.principalHeader } : {}),
|
|
137
|
+
...(transport.principalHeader === undefined && typeof transport.assertionHeader === "string" ? { assertionHeader: transport.assertionHeader } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const transportBad = (path, reason) => ({ ok: false, issue: { path, reason } });
|
|
141
|
+
/**
|
|
142
|
+
* 请求面 `transport` 的**唯一读器**:读器投影 transport 用它,发送前 / 受理时的判形读它报的 `lost` —— 同一只函数两个消费点,「读器丢了什么」与「门拒什么」不可能分岔。
|
|
143
|
+
* · 硬坏形(`transport` 不是对象 / `kind` 出闭集 / stdio 缺 `command` / http 的 `url` 不是 http(s))⇒ `ok:false`,`path` 是读器点名的键路径;
|
|
144
|
+
* · 软坏形(`args` 不是串数组 / `env` `headers` 不是纯数据的串记录 / `principalHeader` 非串)⇒ 读器**丢这个键**(重放存量提交体时的升级前读法),并把它记进 `lost`
|
|
145
|
+
* (受理新提交的门据它响亮拒)。`assertionHeader` 不在 `lost` 里:两个身份头的键组合与 `assertionHeader` 的值形由调用方在读之前对原始传输判。
|
|
146
|
+
* · `env` / `headers` 只收**纯数据**的记录({@link readPlainStringRecord}):原型必须是 `Object.prototype` 或 `null`(`Map`、`Set`、类实例整张不收),每个自有成员的值都是串;
|
|
147
|
+
* 交出的是**副本**(不是调用方的对象,改原对象不影响读后的形)。名叫 `__proto__` 的成员(JSON 文本铸得出这个自有键,对象字面量写不出)不进副本、单独记进 `lost`。
|
|
148
|
+
* 坏形的 `lost` 条目带机读位(见 {@link McpEntryKeyIssue})。
|
|
149
|
+
* url 的拒句不回显值:它常规地带凭据(userinfo)。
|
|
150
|
+
*/
|
|
151
|
+
export function readRequestMcpTransport(t) {
|
|
152
|
+
if (!t || typeof t !== "object")
|
|
153
|
+
return transportBad("transport", `must be an object with a "kind" of "stdio" or "http" — got ${describeValueShape(t)}`);
|
|
154
|
+
const tr = t;
|
|
155
|
+
const lost = [];
|
|
156
|
+
if (tr.kind === "stdio") {
|
|
157
|
+
if (typeof tr.command !== "string" || tr.command.length === 0)
|
|
158
|
+
return transportBad("transport.command", `must be a non-empty string (the command to run) — got ${describeValueShape(tr.command)}`);
|
|
159
|
+
if (tr.args !== undefined && !isStringArray(tr.args))
|
|
160
|
+
lost.push({ path: "transport.args", reason: `must be an array of strings — got ${describeNotStringArray(tr.args)}` });
|
|
161
|
+
const env = readTransportRecord("transport.env", tr.env, lost);
|
|
162
|
+
return {
|
|
163
|
+
ok: true,
|
|
164
|
+
transport: {
|
|
165
|
+
kind: "stdio",
|
|
166
|
+
command: tr.command,
|
|
167
|
+
...(isStringArray(tr.args) ? { args: tr.args } : {}),
|
|
168
|
+
...(env !== undefined ? { env } : {}),
|
|
169
|
+
},
|
|
170
|
+
lost,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (tr.kind === "http") {
|
|
174
|
+
// http(s) only — never file://, never a bare host (a syntactic guard, not the URL shape judge).
|
|
175
|
+
if (typeof tr.url !== "string" || !startsWithHttpScheme(tr.url))
|
|
176
|
+
return transportBad("transport.url", "must be an absolute http(s) URL (put credentials in transport.headers, never in the URL)");
|
|
177
|
+
const ids = readMcpIdentityHeaders(tr);
|
|
178
|
+
const headers = readTransportRecord("transport.headers", tr.headers, lost);
|
|
179
|
+
if (tr.principalHeader !== undefined && ids.principalHeader === undefined) {
|
|
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)}` });
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
ok: true,
|
|
184
|
+
transport: {
|
|
185
|
+
kind: "http",
|
|
186
|
+
url: tr.url,
|
|
187
|
+
...(headers !== undefined ? { headers } : {}),
|
|
188
|
+
...ids,
|
|
189
|
+
},
|
|
190
|
+
lost,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return transportBad("transport.kind", `must be "stdio" or "http" — got ${describeValueShape(tr.kind)}`);
|
|
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
|
+
}
|
|
@@ -6,7 +6,7 @@ import type { REMOTE_EXEC_DOCUMENT } from "./remote-exec.js";
|
|
|
6
6
|
import type { DomainName, RetiredDomainName } from "./types.js";
|
|
7
7
|
/** 本版登记的一次性**数据**迁移 id(闭集;每个 id 在 `ONE_TIME_MIGRATIONS` 登记表里恰一行)。收据只许这些 id —— 同表登记的 wire 删码位
|
|
8
8
|
* (`WIRE_RETIREMENT_IDS`,`./one-time-migrations`)不改任何存储形,铸不出收据,不在本集。 */
|
|
9
|
-
export declare const ONE_TIME_MIGRATION_IDS: readonly ["SS-M1", "SS-M2", "SS-M3", "SS-M4", "SS-M5", "SS-M6", "SS-M7", "SS-M8", "SS-M9"];
|
|
9
|
+
export declare const ONE_TIME_MIGRATION_IDS: readonly ["SS-M1", "SS-M2", "SS-M3", "SS-M4", "SS-M5", "SS-M6", "SS-M7", "SS-M8", "SS-M9", "SS-M10"];
|
|
10
10
|
export type OneTimeMigrationId = (typeof ONE_TIME_MIGRATION_IDS)[number];
|
|
11
11
|
/**
|
|
12
12
|
* 一条收据说迁移**对一个坐标做了什么**(闭集,每词一义):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** 本版登记的一次性**数据**迁移 id(闭集;每个 id 在 `ONE_TIME_MIGRATIONS` 登记表里恰一行)。收据只许这些 id —— 同表登记的 wire 删码位
|
|
2
2
|
* (`WIRE_RETIREMENT_IDS`,`./one-time-migrations`)不改任何存储形,铸不出收据,不在本集。 */
|
|
3
|
-
export const ONE_TIME_MIGRATION_IDS = ["SS-M1", "SS-M2", "SS-M3", "SS-M4", "SS-M5", "SS-M6", "SS-M7", "SS-M8", "SS-M9"];
|
|
3
|
+
export const ONE_TIME_MIGRATION_IDS = ["SS-M1", "SS-M2", "SS-M3", "SS-M4", "SS-M5", "SS-M6", "SS-M7", "SS-M8", "SS-M9", "SS-M10"];
|
|
4
4
|
/**
|
|
5
5
|
* 一条收据说迁移**对一个坐标做了什么**(闭集,每词一义):
|
|
6
6
|
* - `moved` — 值搬到新位(`to`),义不变;
|
package/dist/node.d.ts
CHANGED
|
@@ -9,6 +9,5 @@ export * from "./file-edit.js";
|
|
|
9
9
|
export * from "./bundle.js";
|
|
10
10
|
export * from "./local-load.js";
|
|
11
11
|
export * from "./scheduler-store-node.js";
|
|
12
|
-
export * from "./prompts-digest.js";
|
|
13
12
|
export { migrateConfigDirectory, type ConfigDirectoryDocument, type ConfigDirectoryIo, type ConfigDirectoryMigration, type ConfigDirectoryRefusal, type MigrateConfigDirectoryOptions } from "./config-dir-migration.js";
|
|
14
13
|
export { nodeConfigDirectoryIo } from "./config-dir-io.js";
|
package/dist/node.js
CHANGED
|
@@ -9,7 +9,6 @@ export * from "./file-edit.js";
|
|
|
9
9
|
export * from "./bundle.js";
|
|
10
10
|
export * from "./local-load.js";
|
|
11
11
|
export * from "./scheduler-store-node.js"; // R7 self-wake scheduler store — fs binding of the ./scheduler-store contract
|
|
12
|
-
export * from "./prompts-digest.js"; // prompts 下发包的内容摘要(4.0.0 从 scheduler-store-node 搬来 —— 与调度存储无关;出口路径不变)
|
|
13
12
|
// 目录形的一次性迁移(读目录 → 迁 → 整域终判 → 原子写 → 删 retired 域文件)—— 语义只此一处,命令行是它的渲染器、引擎起服调同一只。显式列名:该模块的包内私有件(出门形/拼路径)不外泄。
|
|
14
13
|
export { migrateConfigDirectory } from "./config-dir-migration.js";
|
|
15
14
|
export { nodeConfigDirectoryIo } from "./config-dir-io.js"; // 上面那只函数的真 I/O 绑定:node:fs + 本包唯一的原子写器(config-document 模式规则)
|
|
@@ -39,7 +39,7 @@ export type StoredDocuments = Readonly<Partial<Record<DomainName | RetiredDomain
|
|
|
39
39
|
export interface StoredDocumentsMigration {
|
|
40
40
|
/** 迁移后的文档集:没改的文档是**同一引用**;被移除的(retired 域)不在;新建的(例:迁出的 `governance` / `limits`)在。 */
|
|
41
41
|
readonly docs: Readonly<Record<string, unknown>>;
|
|
42
|
-
/** 逐条收据(步序 SS-M1 → SS-M2 → SS-M5 → SS-M6 → SS-M8 → SS-M9 → SS-M7 → SS-M4:纯改写步在前,用新 schema 终判的 SS-M4 最后;步内按坐标)。
|
|
42
|
+
/** 逐条收据(步序 SS-M1 → SS-M2 → SS-M5 → SS-M6 → SS-M8 → SS-M9 → SS-M10 → SS-M7 → SS-M4:纯改写步在前,用新 schema 终判的 SS-M4 最后;步内按坐标)。
|
|
43
43
|
* 只有 `refused` 的收据 = 什么都没改。 */
|
|
44
44
|
readonly receipts: readonly MigrationReceipt[];
|
|
45
45
|
}
|
|
@@ -161,6 +161,19 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
161
161
|
removal: "the next major after the window: delete the SS-M9 step; the models.default tombstone field stays — its SS-M9 clause goes",
|
|
162
162
|
afterRemoval: "the models.default tombstone field (permanent): a residual key is refused on read and write (the models domain degrades to its default)",
|
|
163
163
|
},
|
|
164
|
+
{
|
|
165
|
+
id: "SS-M10",
|
|
166
|
+
oldForm: "refused",
|
|
167
|
+
introducedIn: "14.0.0",
|
|
168
|
+
releasedOn: "2026-10-17",
|
|
169
|
+
removableAfter: "2026-11-16",
|
|
170
|
+
rewrites: "prompts: an empty `bindings` array (the default every document carried before 14.0.0 — it served nothing) is removed; a non-empty " +
|
|
171
|
+
"`bindings` and a `catalog` are refused and kept verbatim — declarations compile to center overlays only through the center's card " +
|
|
172
|
+
"compiler (republish), and a catalog's digest is the engine's (rebuild it with the engine's buildPromptArtifact)",
|
|
173
|
+
removal: "the next major after the window: delete the SS-M10 step; the prompts `bindings` / `catalog` tombstone fields stay — their SS-M10 clause goes",
|
|
174
|
+
afterRemoval: "the prompts `bindings` / `catalog` tombstone fields (permanent): a residual key is refused on read and write (the prompts domain degrades " +
|
|
175
|
+
"to its default — no center layer is served)",
|
|
176
|
+
},
|
|
164
177
|
{
|
|
165
178
|
// 一条 wire 位的删码登记,不是数据迁移(id 在 WIRE_RETIREMENT_IDS,不在收据闭集)。
|
|
166
179
|
id: "SS-W1",
|
|
@@ -205,6 +218,7 @@ export function migrateStoredDocuments(docs, opts) {
|
|
|
205
218
|
stepAtMention(out, receipts);
|
|
206
219
|
stepRetiredRoleWords(out, receipts);
|
|
207
220
|
stepModelsDefault(out, receipts);
|
|
221
|
+
stepPromptsAxes(out, receipts);
|
|
208
222
|
stepUrlFragments(out, receipts);
|
|
209
223
|
stepWorkers6x(out, receipts);
|
|
210
224
|
return { docs: out, receipts };
|
|
@@ -749,6 +763,31 @@ function stepModelsDefault(out, receipts) {
|
|
|
749
763
|
receipts.push(moved("SS-M9", "models", ["default"], { document: "models", path: ["roles", "default"] }));
|
|
750
764
|
out.models = { ...without(doc, "default"), roles: { ...roles, default: { model: name } } };
|
|
751
765
|
}
|
|
766
|
+
// ── SS-M10:prompts 两轴退役 ───────────────────────────────────────────────────────────────────────────
|
|
767
|
+
/** 14.0.0 的 prompts 域只装中心 artifact;13.x 的两轴键是墓碑。迁移只做只认形的那一件:空的 `bindings: []`(13.x 每份文档恒物化的默认值,
|
|
768
|
+
* 从不下发任何内容)删掉。非空 `bindings` / 不是数组的 `bindings` / `catalog` 一律 refused、原样留着 —— 声明要经中心的卡编译器才编得成
|
|
769
|
+
* overlay,catalog 的 digest 是引擎铸的,替运维机械转就是第二个编译器 / 第二个铸点。`prompts` 不是对象 ⇒ 不动(判形是读者的事)。 */
|
|
770
|
+
function stepPromptsAxes(out, receipts) {
|
|
771
|
+
const doc = out.prompts;
|
|
772
|
+
if (!isPlainObject(doc))
|
|
773
|
+
return;
|
|
774
|
+
let next = doc;
|
|
775
|
+
if (has(doc, "bindings") && doc.bindings !== undefined) {
|
|
776
|
+
if (Array.isArray(doc.bindings) && doc.bindings.length === 0) {
|
|
777
|
+
next = without(next, "bindings");
|
|
778
|
+
receipts.push(receipt("SS-M10", "prompts", ["bindings"], "dropped-inert"));
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
receipts.push(refused("SS-M10", "prompts", ["bindings"], "declaration bindings compile to center overlays only through the center's card compiler — republish the prompts from the web-admin editor " +
|
|
782
|
+
"(a local config.d copy was never read by the engine: delete the key)"));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (has(doc, "catalog") && doc.catalog !== undefined) {
|
|
786
|
+
receipts.push(refused("SS-M10", "prompts", ["catalog"], "a v1 catalog cannot be converted (its digest is the engine's) — republish from the web-admin, or rebuild with the engine's buildPromptArtifact"));
|
|
787
|
+
}
|
|
788
|
+
if (next !== doc)
|
|
789
|
+
out.prompts = next;
|
|
790
|
+
}
|
|
752
791
|
// ── SS-M7:URL fragment ────────────────────────────────────────────────────────────────────────────────
|
|
753
792
|
/** SS-M7 走查的文档 → 它的 schema(座位从 schema 派生的那一只来源):配置域按 `DOMAIN_SCHEMAS`,`remote-exec` 按 `RemoteExecSpec`;别的文档没有 URL 座位。 */
|
|
754
793
|
function urlSeatSchemaOf(document) {
|
|
@@ -69,8 +69,8 @@ export type MergeShape = z.infer<typeof MergeShape>;
|
|
|
69
69
|
export declare const MinTrust: z.ZodEnum<{
|
|
70
70
|
local: "local";
|
|
71
71
|
session: "session";
|
|
72
|
-
global: "global";
|
|
73
72
|
project: "project";
|
|
73
|
+
global: "global";
|
|
74
74
|
managed: "managed";
|
|
75
75
|
task: "task";
|
|
76
76
|
}>;
|
|
@@ -118,8 +118,8 @@ export declare const MemberAdmission: z.ZodObject<{
|
|
|
118
118
|
minTrust: z.ZodOptional<z.ZodEnum<{
|
|
119
119
|
local: "local";
|
|
120
120
|
session: "session";
|
|
121
|
-
global: "global";
|
|
122
121
|
project: "project";
|
|
122
|
+
global: "global";
|
|
123
123
|
managed: "managed";
|
|
124
124
|
task: "task";
|
|
125
125
|
}>>;
|
|
@@ -128,8 +128,8 @@ export declare const MemberAdmission: z.ZodObject<{
|
|
|
128
128
|
minTrust: z.ZodEnum<{
|
|
129
129
|
local: "local";
|
|
130
130
|
session: "session";
|
|
131
|
-
global: "global";
|
|
132
131
|
project: "project";
|
|
132
|
+
global: "global";
|
|
133
133
|
managed: "managed";
|
|
134
134
|
task: "task";
|
|
135
135
|
}>;
|
|
@@ -194,8 +194,8 @@ export declare const MergeCategorySpec: z.ZodObject<{
|
|
|
194
194
|
minTrust: z.ZodEnum<{
|
|
195
195
|
local: "local";
|
|
196
196
|
session: "session";
|
|
197
|
-
global: "global";
|
|
198
197
|
project: "project";
|
|
198
|
+
global: "global";
|
|
199
199
|
managed: "managed";
|
|
200
200
|
task: "task";
|
|
201
201
|
}>;
|
|
@@ -240,8 +240,8 @@ export declare const MergeCategorySpec: z.ZodObject<{
|
|
|
240
240
|
minTrust: z.ZodOptional<z.ZodEnum<{
|
|
241
241
|
local: "local";
|
|
242
242
|
session: "session";
|
|
243
|
-
global: "global";
|
|
244
243
|
project: "project";
|
|
244
|
+
global: "global";
|
|
245
245
|
managed: "managed";
|
|
246
246
|
task: "task";
|
|
247
247
|
}>>;
|
|
@@ -250,8 +250,8 @@ export declare const MergeCategorySpec: z.ZodObject<{
|
|
|
250
250
|
minTrust: z.ZodEnum<{
|
|
251
251
|
local: "local";
|
|
252
252
|
session: "session";
|
|
253
|
-
global: "global";
|
|
254
253
|
project: "project";
|
|
254
|
+
global: "global";
|
|
255
255
|
managed: "managed";
|
|
256
256
|
task: "task";
|
|
257
257
|
}>;
|