@sema-agent/settings-schema 14.8.0 → 15.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/CHANGELOG.md +97 -0
- package/README.md +22 -1
- package/conformance/VERSION +1 -1
- package/conformance/cases/c59-s784-g3-k8s-missing-image/input.json +4 -1
- package/conformance/cases/c61-s784-g3-other-lane-key/input.json +1 -0
- package/conformance/cases/c64-s784-multitenant-local-store/input.json +2 -1
- package/dist/execution-lane-index.d.ts +6 -0
- package/dist/execution-lane-index.js +8 -0
- package/dist/execution-lanes.d.ts +2 -1
- package/dist/execution-lanes.js +6 -4
- package/dist/remote-exec.d.ts +1 -1
- package/dist/remote-exec.js +4 -2
- package/dist/types.js +5 -5
- package/llms-full.txt +5 -3
- package/llms.txt +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,103 @@
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 15.0.0 — 2026-10-28 — major:k8s 车道的沙箱命名空间 `K8S_NAMESPACE` 由可选挪进必需(车道表的 k8s 行 · 本地文件契约 `RemoteExecSpec` 的 k8s 臂)· 写闸对必需坐标的值:空串或只含空白一律按缺(校验收紧三处:此前受理的声明从受理变成拒绝)
|
|
12
|
+
|
|
13
|
+
> 档位(按发版纪律):**major** —— 校验收紧三处,每一处都让此前受理的输入被拒:**B1** 车道表把 `K8S_NAMESPACE` 挪进 k8s 行的必需栏(缺它的 k8s worker 被写闸拒;存下来的 workers 文档里有这样一行,
|
|
14
|
+
> 整个 workers 域读不出);**B2** 本地文件契约 `RemoteExecSpec` 的 k8s 臂 `namespace` 由可选改必填(缺它的 `config.d/remote-exec.json` 被拒);**B3** 写闸对必需坐标的值:空串或只含空白一律按缺(所有车道、所有必需坐标同一只判官)。
|
|
15
|
+
> 发版纪律按消费方在**运行期**的可见性判档,不看 diff 大小。
|
|
16
|
+
> 一件事,一条律:**k8s 车道的沙箱命名空间是部署自己声明的坐标 —— 车道表不能再把它放在「可选」栏**。消费方按本表行拒启:服务端的必需 env 门按本表行 `requiredEnv ∪ requiredSecretEnv` 拒启,部署面的车道清单读同一行;
|
|
17
|
+
> 表此前把它写在 `optionalEnv`,写闸与这道门因此放行不带它的 k8s 声明:同一件事,表、写闸、文件契约各说各话。现在只有一种:`EXECUTION_LANES` 的 k8s 行 `requiredEnv` = `["K8S_SANDBOX_IMAGE", "K8S_NAMESPACE"]`,`optionalEnv` 去掉它;其余六行逐字不动。
|
|
18
|
+
> 导出面:包根 / `./types` / `./node` / `./execution-lanes` 的名字零增删;答案变的是常量 `EXECUTION_LANES` 的 k8s 行、读它的写闸与读函数,以及 `RemoteExecSpec` 的 k8s 臂(见节末机器重算表);判官 `isBlankCoordinate` 在包内私有、不进任何导出面。
|
|
19
|
+
> 一致性语料:条数不变;c59 / c61 / c64 三条拒因语料的输入各补一格 `coords.K8S_NAMESPACE`(它们要打的格不是命名空间,不补则各多一条拒因、不再「只因这一格被拒」),三条的拒绝态答案与金样一字不动;其余语料逐字同。
|
|
20
|
+
|
|
21
|
+
### BREAKING 清单(逐条三问 + 下游动作)
|
|
22
|
+
|
|
23
|
+
**B1 · k8s 车道的 `K8S_NAMESPACE` 是必需坐标**(零别名、零过渡读法、不自动补写)。读车道表这一格的地方都从同一张车道表派生,没有第二份手抄的车道必需键清单(本地文件契约上的同一件事是 B2):
|
|
24
|
+
写闸 `WorkerSpec` —— 缺 ⇒ issue `lane "k8s" requires coords.K8S_NAMESPACE (the engine refuses to start without it)`,`path` 指 `runtime.execution.coords.K8S_NAMESPACE`;给空串或纯空白 ⇒ `required coordinate K8S_NAMESPACE of lane "k8s" is empty or whitespace-only — it counts as absent, and the engine refuses to start without it`(空串此前作为可选坐标照收;纯空白见 B3);
|
|
25
|
+
存下来的 workers 文档的两只读函数(`parseDomainLoud` / `buildEffective`);一次性迁移 SS-M4 对没写命名空间的 6.x k8s 行的终判。缺键拒句(`lane "k8s" requires coords.<键> (the engine refuses to start without it)`)的措辞本版不改(它本来就说「缺了就起不了服」,现在表与它一致);空值拒句本版改了措辞(B3:`is empty — the engine reads an empty value as unset and refuses to start` → `is empty or whitespace-only — it counts as absent, and the engine refuses to start without it`),点名同一格、`path` 不变。
|
|
26
|
+
**谁需要**:沙箱 pod 住哪只有部署知道,没有哪一方替它猜;表、写闸、中心表单、服务端的必需 env 门与部署面的车道清单读同一张表 —— 表说必需,保存时就拦下,而不是等服务起来才发现。
|
|
27
|
+
**谁受伤**:
|
|
28
|
+
① 存下来的 workers 文档里 `lane` 是 `k8s` 而 `coords` 没有 `K8S_NAMESPACE`(或为空串)的行:整域读的两只读函数 `parseDomainLoud` 与 `buildEffective` 遇到它就抛 —— workers 域不逐条抢救
|
|
29
|
+
(丢一行 = 对账器把这台 worker 当成不再声明、去退役它),一行不合 = 整个 workers 文档读不出,拒句点名 `runtime.execution.coords.K8S_NAMESPACE`(响亮,不静默丢行);
|
|
30
|
+
按行读的 `readWorkersRows`(web-admin 的 workers 读口)则把这一行扣下(`held`,带原文与拒因,`field` 指到同一格),其余行照常进 `doc`;
|
|
31
|
+
② 保存 k8s worker 的写入席(`WorkerSpec.safeParse`、中心表单):不带命名空间的写入被拒;
|
|
32
|
+
③ 一次性迁移 SS-M4:没写命名空间的 6.x k8s 行(此前靠内置缺省命名空间)⇒ `refused` 点名同一格、行原样留着 —— 迁移不替运维猜命名空间;
|
|
33
|
+
④ 按本包车道表算「缺了就起不了服的键」的消费方(服务端的必需 env 门按本表行 `requiredEnv ∪ requiredSecretEnv` 拒启、部署面车道清单读同一行、壳的执行车道预检):k8s 的必需键多一名,提货后随表重算。
|
|
34
|
+
不受伤:本机 `config.d`(workers 是非便携域,本机腿不读它);其余六条车道;带齐两键的 k8s 行;语料里的 `c52`(合法 k8s 行本来就带 `K8S_NAMESPACE`)。
|
|
35
|
+
**补偿**:给每个 k8s 行补一格 `K8S_NAMESPACE`,值 = 部署里沙箱 pod 所在的命名空间;**此前靠引擎内置缺省命名空间 `sandboxes` 的部署写 `sandboxes` 即与升级前同**。
|
|
36
|
+
落点看行的形:现行形的行写 `runtime.execution.coords.K8S_NAMESPACE`;还没过 SS-M4 的 6.x 行(仍带 `env.REMOTE_EXEC`)写它自己的 `env.K8S_NAMESPACE` 再跑迁移(迁移把它搬进 `coords`;往 6.x 行里加 `runtime.execution` 是新旧两形并存,迁移拒、不替运维挑赢家)。
|
|
37
|
+
**部署序:先补键、后升级** —— 14.8.x 上 `K8S_NAMESPACE` 本来就是合法的可选坐标,现行形的存量文档可以在旧版本下先补齐,升级时没有新旧两版同时在读的窗。
|
|
38
|
+
**下游**:中心(web-admin)的 k8s 写入路径要带命名空间,存量 workers 文档先补键;服务端提货后必需键门与车道清单随表重算(清单由同一张表生成);壳提货后 k8s 车道的半组判定多一名;三方各自按能力表态制回帖。
|
|
39
|
+
|
|
40
|
+
**B2 · 本地文件契约 `RemoteExecSpec` 的 k8s 臂:`namespace` 由可选字段改必填**(`config.d/remote-exec.json` 里 `provider` 为 `k8s` 的那一臂;零别名、零过渡读法)。B1 说的那件事在文件契约面的另一份说法 ——
|
|
41
|
+
v14.8.0 时两处都说可选;B1 把车道表收成必需之后,文件契约若仍说可选,同一个部署声明沙箱 pod 住哪,读两个地方就得两个答案 —— 所以同版把文件契约收成同一个答案:也要求写。判据 = 非空串(与 `image` 同一只 `execStr`,含不许 NUL)。
|
|
42
|
+
入口同答:`RemoteExecSpec.safeParse` / `validateRemoteExec` / `parseRemoteExecDocument` 拒(issue 指到 `namespace`);`loadRemoteExec` 读到这样的文件抛(文件存在即赢、存在而坏即抛,不静默退回 host 默认);`writeRemoteExec` 不落盘。
|
|
43
|
+
**谁需要**:一件事一个答案 —— 文件契约与车道表对「命名空间要不要写」不再各说各话。
|
|
44
|
+
**谁受伤**:`config.d/remote-exec.json` 里 k8s 臂没写 `namespace` 的部署:读侧 `loadRemoteExec` 抛,写侧(配置编辑器经 `writeRemoteExec`)不带命名空间的 k8s 写入被拒。不受伤:其余五个 provider;带 `namespace` 的 k8s 文件。
|
|
45
|
+
**补偿**:补 `namespace`,值 = 部署里沙箱 pod 所在的命名空间;**此前靠引擎内置缺省命名空间 `sandboxes` 的部署写 `sandboxes` 即与升级前同**。14.8.x 下 `namespace` 本来就是合法的可选字段,先补后升级,没有新旧两版同时在读的窗。
|
|
46
|
+
**下游**:读写 `config.d/remote-exec.json` 的消费方(读 `loadRemoteExec`、写 `writeRemoteExec`),提货后按同一句话补。
|
|
47
|
+
|
|
48
|
+
**B3 · 写闸对必需坐标的值:空串或只含空白一律按缺**(所有车道、所有必需坐标同一只判官 `isBlankCoordinate`:值 `trim()` 之后为空;零别名、零过渡读法)。此前写闸只判 `=== ""`,`" "` 一类纯空白被当成「写了」放行 —— 保存成功、写了却等于没写。
|
|
49
|
+
拒句同 B1 的空串句(`required coordinate <键> of lane "<车道>" is empty or whitespace-only — …`),`path` 指到该坐标位。判官只判「是不是空白」,不 trim 值、不管值的格式(`" sandboxes "` 照过 —— 格式不在这一条的范围);可选坐标不走它(个别可选坐标上空串有语义,如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata)。
|
|
50
|
+
**谁需要**:一个必需坐标写成纯空白,效果与没写一样;写闸放行它,就是保存时不拦、到用的时候才失败。
|
|
51
|
+
**谁受伤**:必需坐标(任一车道:`K8S_SANDBOX_IMAGE` / `K8S_NAMESPACE` / `SSH_HOST` / `SSH_USER` / `ADB_SERIAL` / `DOCKER_IMAGE`)写成纯空白的声明(病态输入,正常写入不会有);存下来的 workers 文档里有这样的行,处置与 B1 的缺键行相同(整域读函数抛、`readWorkersRows` 扣下并指到该格)。
|
|
52
|
+
**补偿**:拒因点名那一格 —— 填真值(必需坐标缺席同样被拒,删掉那一键不是出路)。
|
|
53
|
+
**下游**:同 B1。
|
|
54
|
+
|
|
55
|
+
### 钉
|
|
56
|
+
|
|
57
|
+
`test/k8s-namespace-required-15.0.0.test.ts`(四面:表行〔必需栏恰为两名、次序镜像在前、可选栏不再有它、归属索引的 required 位、别的车道必需栏一格没动〕· 写闸〔只给镜像 ⇒ 恰一条 issue 指到该格、给空串 ⇒ 「必需坐标为空」一条、两键都给 ⇒ 过、一键不给 ⇒ 两条〕·
|
|
58
|
+
存下来的 workers 文档〔`parseDomainLoud` / `buildEffective` 抛并点名该键、`readWorkersRows` 扣下这一行并把 `field` 指到该格、补键后同一份文档读进、没有 k8s 行的文档不受影响〕· 随包文字〔升级指引与本节写明迁移动作,README / `llms` 里没有一句把这一键说成可选〕);
|
|
59
|
+
B3 的钉在同一文件:必需坐标〔`K8S_NAMESPACE` 取空格 / 制表 / 换行 / U+00A0 / U+FEFF / 混合 ⇒ 各恰一条 issue 指到该坐标位 · 正控:`"sandboxes"` / `" sandboxes "` / `"Sandboxes"` / `"a/b"` 照过 · 每条车道的每个必需坐标〔从表现算〕写成纯空白 ⇒ 都有 issue 指到自己的坐标位 · 可选坐标纯空白照收 · 结构:写闸读判官、源里没有 `value === ""` 一臂 · 存下来的文档同缺键行〕;
|
|
60
|
+
B2 的钉在同一文件:本地文件契约面〔k8s 臂必填字段恰为 `provider` / `image` / `namespace`(逐字段问 `undefined` 过不过,不手抄)· 只给镜像 ⇒ 恰一条 issue 指到 `namespace` · `validateRemoteExec` / `parseRemoteExecDocument` / `writeRemoteExec` / `loadRemoteExec` 同答 · 补键后照读 · 别的 provider 不受影响〕;
|
|
61
|
+
`test/remote-exec.test.ts` / `test/remote-exec-migration-11.3.0.test.ts` / `test/url-shape-11.0.0.test.ts` / `test/custom-issue-params-11.3.0.test.ts` / `test/file-edit.test.ts`(k8s 臂夹具补 `namespace`,各格仍只因自己要打的那一处被拒);
|
|
62
|
+
`test/execution-lanes-7.0.0.test.ts`(两处镜像钉改成现状:k8s 必需键并集含它、可选栏逐行快照去掉它;钉的注释不再说「引擎 `REQUIRED_ENVS_BY_LANE` 逐字」—— 服务端已直读本表);
|
|
63
|
+
`test/conformance-workers-7.0.0.test.ts`(三条拒因语料「issue 恰一条」原样绿);`test/one-time-migration-ss-m4-workers.test.ts`(没写命名空间的 6.x k8s 行 ⇒ refused 点名该格、文档同一引用;多租户格补命名空间);`test/file-edit.test.ts`(基准 worker 补命名空间)。
|
|
64
|
+
|
|
65
|
+
**自 v14.8.0 起运行期代码有改动的公开导出(机器重算;上界 —— 可达 ≠ 一定变,不可达 ⇒ 一定不变;本版新增的导出不入表)**
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
DOMAINS [., ./types]
|
|
69
|
+
DOMAIN_SCHEMAS [., ./types]
|
|
70
|
+
EXECUTION_LANES [., ./execution-lanes]
|
|
71
|
+
FileConfigStore [./node]
|
|
72
|
+
NON_PORTABLE_DOMAINS [./conformance]
|
|
73
|
+
RESERVED_WORKER_ENV [., ./types]
|
|
74
|
+
RemoteExecSpec [., ./remote-exec]
|
|
75
|
+
WorkerSpec [., ./types]
|
|
76
|
+
WorkersConfig [., ./types]
|
|
77
|
+
buildEffective [., ./config-fns]
|
|
78
|
+
deriveLocalExpectation [./conformance]
|
|
79
|
+
deriveWireExpectation [./conformance]
|
|
80
|
+
domainFilePath [./node]
|
|
81
|
+
emptyEffective [., ./config-fns]
|
|
82
|
+
foreignKeysOf [./conformance]
|
|
83
|
+
isDomainName [., ./types]
|
|
84
|
+
loadLocalConfig [./node]
|
|
85
|
+
loadRemoteExec [./node]
|
|
86
|
+
migrateConfigDirectory [./node]
|
|
87
|
+
migrateConfigDocuments [./node]
|
|
88
|
+
migrateEffectiveSnapshot [./one-time-migrations]
|
|
89
|
+
migrateEnvModelsDecision [./node]
|
|
90
|
+
migrateStoredDocuments [./one-time-migrations]
|
|
91
|
+
parseDomain [., ./config-fns]
|
|
92
|
+
parseDomainLoud [., ./config-fns]
|
|
93
|
+
parseDomainSalvaged [., ./config-fns]
|
|
94
|
+
planEnvModelTableWrite [.]
|
|
95
|
+
readBundle [./node]
|
|
96
|
+
readEffectiveWire [., ./config-fns]
|
|
97
|
+
readWorkersRows [.]
|
|
98
|
+
resolveLegExpectation [./conformance]
|
|
99
|
+
runConformance [./conformance]
|
|
100
|
+
urlSeatPatterns [./one-time-migrations]
|
|
101
|
+
validateDomain [./node]
|
|
102
|
+
validateRemoteExec [./node]
|
|
103
|
+
writeBundle [./node]
|
|
104
|
+
writeDomainFile [./node]
|
|
105
|
+
writeRemoteExec [./node]
|
|
106
|
+
```
|
|
107
|
+
|
|
11
108
|
## 14.8.0 — 2026-10-27 — minor:配置根上的两份根级文档(env 模型表被目录顶替的决定文档 `config.env-models.json` 与按 id 一份的迁移运行记录)· 两只读者 `readEnvModelsDecision` / `readMigrationMarker` 与决定文件的唯一写函数 `writeEnvModelsDecision`(`./node`)· 一次性迁移 SS-M11(`migrateEnvModelsDecision`)· 目录管不管模型面的原文判官 `catalogManagesModelPlane` 与计数 `enabledEntryCount` · env 模型表的一份读法与写者的计划助手(`readEnvModelSlots` · `buildEnvModelBaseline` · `planEnvModelTableWrite`)· 两只模型面组拒码 `CATALOG_REPLACES_ENV_MODELS_CODE` / `SUMMARIZE_MODEL_MISSING_CODE` · 中心引用解析口 `resolveCenterRefs` 与记录读器 `readPlainStringRecord` · MCP 条目的 `sessionMetaKey` / `startupTimeoutMs`(解析透明)· MCP 服务器名保留前缀 `REMOTE_MCP_SERVER_NAME_PREFIX` 与请求面判官一对 `isUsableMcpServerName` / `describeBadMcpServerName` · 引擎钉 7.58.0 与上游漂移门 ⑧ / ⑪
|
|
12
109
|
|
|
13
110
|
> 档位(按发版纪律):**minor** —— 加性:新导出(包根 +53 名,其中两只组拒码常量同时在 `./fleet` 上;`./node` +9 名。包根:根级文档的名字、闭集答案表与读数身份拼法、原文判官两只、env 模型表读法与计划助手族、两只组拒码常量、中心引用解析口 `resolveCenterRefs` 与它的两张词表和读数型、记录读器 `readPlainStringRecord` 与它的原因词表 / 保留成员名 / 读数型、MCP 服务器名的保留前缀 `REMOTE_MCP_SERVER_NAME_PREFIX` 与请求面 MCP 名判官一对 `isUsableMcpServerName` / `describeBadMcpServerName`;`./node`:决定文档与运行记录的两只读者、一只写函数、一次性迁移 SS-M11 的迁移体与它的 I/O 座;导出面登记表合计〔同名在多条子路径上各计一次〕1396 → 1460 名);
|
package/README.md
CHANGED
|
@@ -47,6 +47,27 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
47
47
|
> zod 3, nested inside every zod-4 host). The move to zod 4 did not change what any schema accepts or produces —
|
|
48
48
|
> the conformance corpus reads byte-identically on both — only the wording of validation errors (see CHANGELOG 11.0.0).
|
|
49
49
|
|
|
50
|
+
## 升级指引:14.8.x → 15.0.0(BREAKING:k8s 车道的 `K8S_NAMESPACE` 由可选变必需 —— 车道表与本地文件契约两处;必需坐标的纯空白值按缺)
|
|
51
|
+
|
|
52
|
+
- **改了什么**:车道表 `EXECUTION_LANES` 的 k8s 行把 `K8S_NAMESPACE`(沙箱 pod 所在的命名空间)从 `optionalEnv` 挪进 `requiredEnv`(现为 `["K8S_SANDBOX_IMAGE", "K8S_NAMESPACE"]`)。
|
|
53
|
+
其余六行逐字不动,导出面名字零增删。沙箱 pod 住哪是部署自己的声明,表不替它留缺省:服务端的必需 env 门按本表行 `requiredEnv ∪ requiredSecretEnv` 拒启,部署面的车道清单读同一行;
|
|
54
|
+
此前表把它写在可选栏、这道门放行不带它的 k8s 声明,现在车道表(写闸、归属索引与迁移终判也读它)是唯一来源。零别名、零过渡读法。
|
|
55
|
+
- **谁会红**:① 存下来的 workers 文档里,`runtime.execution.lane` 是 `k8s` 而 `coords` 没有 `K8S_NAMESPACE`(或为空串)的行 —— workers 域整域读、不逐条抢救,
|
|
56
|
+
一行不合 = 整个 workers 文档读不出(`buildEffective` / `parseDomainLoud` 抛,拒句点名 `runtime.execution.coords.K8S_NAMESPACE`),不是静默丢掉那一行;
|
|
57
|
+
按行读的 `readWorkersRows`(web-admin 的 workers 读口)把这一行扣下(`held`,带原文与拒因,`field` 指到同一格)、其余行照常进 `doc`;
|
|
58
|
+
② 保存新 k8s worker 的写入席(`WorkerSpec.safeParse` / 中心表单):不带命名空间的写入现在被拒,拒句 `lane "k8s" requires coords.K8S_NAMESPACE (the engine refuses to start without it)`;
|
|
59
|
+
③ 一次性迁移 SS-M4 遇到没写命名空间的 6.x k8s 行 ⇒ `refused` 点名同一格、行原样留着(迁移不替运维猜命名空间)。本机 `config.d` 不读 workers 文档,本机腿不受影响。
|
|
60
|
+
- **要做的**:给每个 k8s 行补一格 `K8S_NAMESPACE`,值 = 部署里沙箱 pod 所在的命名空间。**此前靠引擎内置缺省命名空间 `sandboxes` 的部署,写 `sandboxes` 即与升级前同。**
|
|
61
|
+
落点看行的形:现行形的行(有 `runtime.execution`)写在 `runtime.execution.coords.K8S_NAMESPACE`;还没过一次性迁移 SS-M4 的 6.x 行(仍带 `env.REMOTE_EXEC`)写在它自己的 `env.K8S_NAMESPACE`,再跑迁移,迁移把它搬进 `coords`
|
|
62
|
+
(往 6.x 行里加 `runtime.execution` 会造成新旧两形并存,迁移拒、不替运维挑赢家)。
|
|
63
|
+
- **文件契约面的同一件事**:本地文件契约 `RemoteExecSpec` 的 k8s 臂(`config.d/remote-exec.json`)的 `namespace` 由可选字段改必填 —— 没写的文件 `loadRemoteExec` 抛(文件存在即赢、存在而坏即抛)、`validateRemoteExec` / `parseRemoteExecDocument` 拒、`writeRemoteExec` 不落盘。
|
|
64
|
+
补 `namespace`(值 = 部署里沙箱 pod 所在的命名空间;此前靠内置缺省的写 `sandboxes`)。14.8.x 下该字段本就合法,先补后升级。
|
|
65
|
+
- **必需坐标的值(B3)**:写闸对所有车道、所有必需坐标同判一只判官 —— 空串或只含空白一律按缺(此前只判空串),拒因点名那一格(`required coordinate <键> of lane "<车道>" is empty or whitespace-only`)。
|
|
66
|
+
只影响把必需坐标写成纯空白的声明(病态输入);补偿 = 填真值(必需坐标缺席同样被拒,删掉那一键不是出路)。判官只判「是不是空白」,不 trim 值、不管值的格式。
|
|
67
|
+
- **部署序**:**先补键、后升级** —— 14.8.x 上 `K8S_NAMESPACE` 本来就是合法的可选坐标,现行形的存量文档可以在旧版本下先补齐,升级时不存在新旧两版同时在读的窗。
|
|
68
|
+
找存量:列出 `runtime.execution.lane` 为 `k8s` 且 `coords.K8S_NAMESPACE` 缺席或为空的 worker 行,以及带 `env.REMOTE_EXEC` 为 `k8s` 而 `env.K8S_NAMESPACE` 缺席或为空的 6.x 行。
|
|
69
|
+
- 逐条三问与钉见 CHANGELOG 15.0.0。
|
|
70
|
+
|
|
50
71
|
## 升级指引:14.7.x → 14.8.0(加性:配置根上的两份根级文档 · env 模型表的一份读法与写者的计划助手 · 一次性迁移 SS-M11)
|
|
51
72
|
|
|
52
73
|
- **新面**:配置根(持有 `config.d` 的那个目录)上多两份**根级文档** —— `config.env-models.json`(`{"envModels":"replaced"|"kept"}`:一份管模型面的
|
|
@@ -264,7 +285,7 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
264
285
|
`MEMORY_ENGINE_BACKENDS`. Web-admin's write gate (this package's `WorkerSpec.safeParse`), web-admin's env
|
|
265
286
|
renderer and the engine's boot checks all read that one table, so they cannot drift. A worker declares
|
|
266
287
|
`runtime.execution = { lane, coords, secretRefs }` (non-secret coordinates as literals, secrets as host env-NAMEs,
|
|
267
|
-
keys ⊆ that lane's columns, required ones present and
|
|
288
|
+
keys ⊆ that lane's columns, required ones present and not blank — an empty or whitespace-only value counts as absent) and `runtime.store = { backend, session, memory,
|
|
268
289
|
db? }` (+ `secretRefs.dbPasswordEnv`); an enabled worker must declare both. Cross-field refusals live in the
|
|
269
290
|
schema as zod issues whose `path` names the field to change and whose message carries the way out: a
|
|
270
291
|
multi-tenant worker (`requirePrincipal`) on a lane that does not separate tenants (outlets computed from the
|
package/conformance/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
15.0.0
|
|
@@ -22,6 +22,12 @@ export interface LaneEnvOwner {
|
|
|
22
22
|
}
|
|
23
23
|
/** 车道 env 名 → 归属。四栏两两不交、跨车道不交(`test/execution-lanes-7.0.0.test.ts` 钉),所以每个名恰好一个归属。 */
|
|
24
24
|
export declare const LANE_ENV_OWNER: ReadonlyMap<string, LaneEnvOwner>;
|
|
25
|
+
/**
|
|
26
|
+
* 必需坐标的值算不算「缺」的**唯一判官**:空串,或只含空白(`String.prototype.trim` 认的空白:空格 / 制表 / 换行 / 不换行空格 / 字节序标记 …),都按缺席论。
|
|
27
|
+
* 写闸对所有车道、所有必需坐标同判这一只;它只判「是不是空白」,不 trim 值、不管值的格式(格式不在这一条的范围)。可选坐标不走它 ——
|
|
28
|
+
* 个别可选坐标上空串有语义(如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata)。
|
|
29
|
+
*/
|
|
30
|
+
export declare function isBlankCoordinate(value: string): boolean;
|
|
25
31
|
/** 一个车道 env 名在 WorkerSpec 里的**唯一写位**(保留键的拒因指路用它)。 */
|
|
26
32
|
export declare function laneEnvSeat(name: string, owner: LaneEnvOwner): string;
|
|
27
33
|
/** 车道块的两个特殊键(不在车道表里,但归车道块:迁移把 `REMOTE_EXEC` 搬成 `runtime.execution.lane`、`REQUIRE_EXECUTION_LANE` 丢弃)。 */
|
|
@@ -24,6 +24,14 @@ export const LANE_ENV_OWNER = new Map(EXECUTION_LANES.flatMap((r) => {
|
|
|
24
24
|
const own = (names, secret, required) => names.map((k) => [k, { lane: r.lane, secret, required }]);
|
|
25
25
|
return [...own(r.requiredEnv, false, true), ...own(r.optionalEnv, false, false), ...own(r.requiredSecretEnv, true, true), ...own(r.optionalSecretEnv, true, false)];
|
|
26
26
|
}));
|
|
27
|
+
/**
|
|
28
|
+
* 必需坐标的值算不算「缺」的**唯一判官**:空串,或只含空白(`String.prototype.trim` 认的空白:空格 / 制表 / 换行 / 不换行空格 / 字节序标记 …),都按缺席论。
|
|
29
|
+
* 写闸对所有车道、所有必需坐标同判这一只;它只判「是不是空白」,不 trim 值、不管值的格式(格式不在这一条的范围)。可选坐标不走它 ——
|
|
30
|
+
* 个别可选坐标上空串有语义(如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata)。
|
|
31
|
+
*/
|
|
32
|
+
export function isBlankCoordinate(value) {
|
|
33
|
+
return value.trim() === "";
|
|
34
|
+
}
|
|
27
35
|
/** 一个车道 env 名在 WorkerSpec 里的**唯一写位**(保留键的拒因指路用它)。 */
|
|
28
36
|
export function laneEnvSeat(name, owner) {
|
|
29
37
|
return `runtime.execution.${owner.secret ? "secretRefs" : "coords"}.${name}`;
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* · `tenantSeparation`:同文件 `executionLaneCaps` 每臂的 `tenantSeparation` 位(:209 host / :230 e2b / :249 k8s /
|
|
18
18
|
* :269 ssh / :288 adb / :308 local-docker / :327 device)。`tenantSeparationBasis`(11.0.0):分租户的那几臂凭什么分 ——
|
|
19
19
|
* e2b / k8s / local-docker 每任务一只隔离目标(`isolation`),device 是发起者自己的设备(`principal-binding`)。
|
|
20
|
-
* · `requiredEnv ∪ requiredSecretEnv
|
|
20
|
+
* · `requiredEnv ∪ requiredSecretEnv`:这条车道缺了就起不了服的键。最初逐字抄自 `src/config.ts` 的手写表 `REQUIRED_ENVS_BY_LANE`(:1054),
|
|
21
|
+
* 那张手写表已退役 —— 服务端的必需 env 门按本表行的这两栏拒启,部署面的车道清单读同一行,所以今天**本表就是**必需键的唯一来源。按 `src/config-catalog.ts`
|
|
21
22
|
* 该键那一行的值类型分栏 —— 类型 `secret` / `opaque`(目录自己「绝不回显值」的两类)进 `*SecretEnv`,其余进 `*Env`。
|
|
22
23
|
* · `optionalEnv ∪ optionalSecretEnv`:`src/config.ts` `parseOrchestrationDomain` 的 `REMOTE_EXEC` 三元链里**该车道那一臂**
|
|
23
24
|
* 读的 env 名(:3925 e2b / :3957 k8s / :3991 ssh / :4006 adb / :4014 host / :4026 local-docker / :4055 device;
|
package/dist/execution-lanes.js
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* · `tenantSeparation`:同文件 `executionLaneCaps` 每臂的 `tenantSeparation` 位(:209 host / :230 e2b / :249 k8s /
|
|
18
18
|
* :269 ssh / :288 adb / :308 local-docker / :327 device)。`tenantSeparationBasis`(11.0.0):分租户的那几臂凭什么分 ——
|
|
19
19
|
* e2b / k8s / local-docker 每任务一只隔离目标(`isolation`),device 是发起者自己的设备(`principal-binding`)。
|
|
20
|
-
* · `requiredEnv ∪ requiredSecretEnv
|
|
20
|
+
* · `requiredEnv ∪ requiredSecretEnv`:这条车道缺了就起不了服的键。最初逐字抄自 `src/config.ts` 的手写表 `REQUIRED_ENVS_BY_LANE`(:1054),
|
|
21
|
+
* 那张手写表已退役 —— 服务端的必需 env 门按本表行的这两栏拒启,部署面的车道清单读同一行,所以今天**本表就是**必需键的唯一来源。按 `src/config-catalog.ts`
|
|
21
22
|
* 该键那一行的值类型分栏 —— 类型 `secret` / `opaque`(目录自己「绝不回显值」的两类)进 `*SecretEnv`,其余进 `*Env`。
|
|
22
23
|
* · `optionalEnv ∪ optionalSecretEnv`:`src/config.ts` `parseOrchestrationDomain` 的 `REMOTE_EXEC` 三元链里**该车道那一臂**
|
|
23
24
|
* 读的 env 名(:3925 e2b / :3957 k8s / :3991 ssh / :4006 adb / :4014 host / :4026 local-docker / :4055 device;
|
|
@@ -71,16 +72,17 @@ const LANE_ROWS = {
|
|
|
71
72
|
optionalSecretEnv: ["E2B_SANDBOX_ENV"],
|
|
72
73
|
},
|
|
73
74
|
/** server `config.ts` :3957 臂;`K8S_TOKEN` 目录 :412 类型 `secret`、`K8S_CA_CERT_B64` :403 类型 `opaque`;
|
|
74
|
-
* `execution-lane-caps.ts` :249 `supported`(每任务一只 pod)。`MINIO_*` 刻意不收(见文件头)。
|
|
75
|
+
* `execution-lane-caps.ts` :249 `supported`(每任务一只 pod)。`MINIO_*` 刻意不收(见文件头)。
|
|
76
|
+
* `K8S_NAMESPACE`(沙箱 pod 所在的命名空间)是**必需**坐标:pod 住哪是部署自己的声明,表不替它留缺省;服务端的必需 env 门按本行 `requiredEnv` 拒启,
|
|
77
|
+
* 所以这里没有「可选」一说。 */
|
|
75
78
|
k8s: {
|
|
76
79
|
tenantSeparation: "supported",
|
|
77
80
|
tenantSeparationBasis: "isolation",
|
|
78
|
-
requiredEnv: ["K8S_SANDBOX_IMAGE"],
|
|
81
|
+
requiredEnv: ["K8S_SANDBOX_IMAGE", "K8S_NAMESPACE"],
|
|
79
82
|
requiredSecretEnv: [],
|
|
80
83
|
optionalEnv: [
|
|
81
84
|
"K8S_API_URL",
|
|
82
85
|
"K8S_INSECURE_TLS",
|
|
83
|
-
"K8S_NAMESPACE",
|
|
84
86
|
"K8S_RUNTIME_CLASS",
|
|
85
87
|
"K8S_MOUNT_PATH",
|
|
86
88
|
"K8S_TIMEOUT_MS",
|
package/dist/remote-exec.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export declare const RemoteExecSpec: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
63
63
|
tokenEnv: z.ZodOptional<z.ZodString>;
|
|
64
64
|
caCertEnv: z.ZodOptional<z.ZodString>;
|
|
65
65
|
insecureTls: z.ZodOptional<z.ZodBoolean>;
|
|
66
|
-
namespace: z.
|
|
66
|
+
namespace: z.ZodString;
|
|
67
67
|
runtimeClass: z.ZodOptional<z.ZodString>;
|
|
68
68
|
mountPath: z.ZodOptional<z.ZodString>;
|
|
69
69
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
package/dist/remote-exec.js
CHANGED
|
@@ -84,7 +84,7 @@ export const RemoteExecSpec = z.discriminatedUnion("provider", [
|
|
|
84
84
|
/** Which of THIS process's env-NAMEs to forward into the sandbox (service: E2B_SANDBOX_ENV, a CSV of NAMEs). */
|
|
85
85
|
sandboxEnv: z.array(ENV_NAME).optional(),
|
|
86
86
|
}),
|
|
87
|
-
// ── k8s: per-task Kata pod (service provider "k8s"; REMOTE_EXEC=k8s + K8S_SANDBOX_IMAGE).
|
|
87
|
+
// ── k8s: per-task Kata pod (service provider "k8s"; REMOTE_EXEC=k8s + K8S_SANDBOX_IMAGE + K8S_NAMESPACE).
|
|
88
88
|
z.object({
|
|
89
89
|
provider: z.literal("k8s"),
|
|
90
90
|
/** Sandbox container image (service: K8S_SANDBOX_IMAGE). Required so a run is reproducible. */
|
|
@@ -97,7 +97,9 @@ export const RemoteExecSpec = z.discriminatedUnion("provider", [
|
|
|
97
97
|
/** env-NAME holding the base64 CA cert (service: K8S_CA_CERT_B64). NEVER the cert bytes. */
|
|
98
98
|
caCertEnv: ENV_NAME.optional(),
|
|
99
99
|
insecureTls: z.boolean().optional(),
|
|
100
|
-
namespace:
|
|
100
|
+
/** The namespace the sandbox pods live in (service: K8S_NAMESPACE). Required — the deployment declares where its pods run and nothing here guesses one;
|
|
101
|
+
* the execution-lane table lists K8S_NAMESPACE as a required coordinate of the k8s lane, and this file contract says the same. */
|
|
102
|
+
namespace: execStr,
|
|
101
103
|
runtimeClass: execStr.optional(),
|
|
102
104
|
mountPath: execStr.optional(),
|
|
103
105
|
timeoutMs: INTEGER.positive().optional(),
|
package/dist/types.js
CHANGED
|
@@ -30,7 +30,7 @@ import { PREVIOUS_WEB_ADMIN_ENV } from "./previous-web-admin-env.js"; // the pre
|
|
|
30
30
|
import { retiredEnvRefusal } from "./retired-envs.js"; // the worker env write gate asks the retired-env tombstone table too (one table, one refusal sentence)
|
|
31
31
|
import { Sha256Digest } from "./sha256.js"; // the one `sha256:<hex>` digest form
|
|
32
32
|
import { EXECUTION_LANES, EXECUTION_LANE_WORDS, MEMORY_ENGINE_BACKENDS, SESSION_BACKENDS, STORE_BACKENDS, retiredLaneWordRefusal } from "./execution-lanes.js"; // 7.0.0 M-R2: the lane / store vocabulary (one exported table)
|
|
33
|
-
import { LANE_ENV_OWNER, isMigratableEnvKey, laneEnvSeat, laneRowOf } from "./execution-lane-index.js"; // package-private indexes derived from that table (not in the barrel)
|
|
33
|
+
import { LANE_ENV_OWNER, isBlankCoordinate, isMigratableEnvKey, laneEnvSeat, laneRowOf } from "./execution-lane-index.js"; // package-private indexes derived from that table (not in the barrel)
|
|
34
34
|
import { migrationSignpost, retiredFormSentence, retiredKey } from "./retired-form.js"; // 11.0.0: the ONE retired-form sentence + key tombstone (package-private)
|
|
35
35
|
import { retiredRoleWordsRefusal } from "./retired-role-words.js"; // 12.0.0: the retired role-word tombstone (package-private; SS-M8)
|
|
36
36
|
import { buildQuotedIssue, fixed } from "./quoted-issue.js"; // THE minting point of custom issues (package-private)
|
|
@@ -1889,8 +1889,8 @@ const nonSecretLiteral = (minLength) => z
|
|
|
1889
1889
|
/**
|
|
1890
1890
|
* `runtime.execution` —— 这台 worker 的执行车道(渲染:`REMOTE_EXEC=<lane>` + `REQUIRE_EXECUTION_LANE=true` + coords / secretRefs 逐键)。
|
|
1891
1891
|
* - `lane`:{@link EXECUTION_LANE_WORDS} 之一。没有「无手」词。
|
|
1892
|
-
* - `coords`:该车道的**非密坐标**(服务变量名 → 字面值),键 ⊆ 表[lane] 的 `requiredEnv ∪ optionalEnv
|
|
1893
|
-
* (
|
|
1892
|
+
* - `coords`:该车道的**非密坐标**(服务变量名 → 字面值),键 ⊆ 表[lane] 的 `requiredEnv ∪ optionalEnv`;必需键在场且不是空白
|
|
1893
|
+
* (空串或只含空白按缺 —— 唯一判官 `isBlankCoordinate`)。可选键的空串照收(例:`K8S_RUNTIME_CLASS=""` 在引擎里是「不用 Kata」)。
|
|
1894
1894
|
* - `secretRefs`:该车道的**密钥**(服务变量名 → 宿主 env-NAME),键 ⊆ 表[lane] 的 `requiredSecretEnv ∪ optionalSecretEnv`,必需键在场。
|
|
1895
1895
|
* 别车道的键 ⇒ 拒并点名它属于哪条车道;本车道的密钥写进 coords / 坐标写进 secretRefs ⇒ 拒并指路另一栏。
|
|
1896
1896
|
*/
|
|
@@ -1925,8 +1925,8 @@ const ExecutionBlock = z
|
|
|
1925
1925
|
return true;
|
|
1926
1926
|
};
|
|
1927
1927
|
for (const [key, value] of Object.entries(x.coords)) {
|
|
1928
|
-
if (judgeKey("coords", key) && value
|
|
1929
|
-
issue("coords", key, buildQuotedIssue `required coordinate ${key} of lane "${x.lane}" is empty
|
|
1928
|
+
if (judgeKey("coords", key) && isBlankCoordinate(value) && LANE_ENV_OWNER.get(key)?.required === true) {
|
|
1929
|
+
issue("coords", key, buildQuotedIssue `required coordinate ${key} of lane "${x.lane}" is empty or whitespace-only — it counts as absent, and the engine refuses to start without it`);
|
|
1930
1930
|
}
|
|
1931
1931
|
}
|
|
1932
1932
|
for (const key of Object.keys(x.secretRefs))
|
package/llms-full.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
llms-full.txt — 自包含全文 · 消费方(使用者)视角 · @sema-agent/settings-schema
|
|
3
|
-
重写日期: 2026-10-
|
|
3
|
+
重写日期: 2026-10-28 | 包版本: 15.0.0
|
|
4
4
|
生成方式: 无生成脚本——按当前树 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版。
|
|
5
5
|
证据约定: 每条"它怎么工作"的断言附 (文件: 符号) 坐标,刻意不用行号(行号逐版腐烂;符号名由
|
|
6
6
|
typecheck 与 test/package-surface.test.ts、test/docs-hygiene.test.ts 等常驻门钉住)。无证据推断显式标【推断】。
|
|
@@ -251,7 +251,7 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
|
|
|
251
251
|
- `host`:{ workdir?, allowedPaths?, shell?, commandTimeoutMs? } —— 无 secret。
|
|
252
252
|
- `local-docker`:{ image!, memory?, cpus?, network?("none"|"bridge"|"host"), workdir?, mountPath? }。
|
|
253
253
|
- `e2b`:{ apiKeyEnv!, template?, timeoutMs?, livenessMs?, allowInternetAccess?, sandboxEnv? }。
|
|
254
|
-
- `k8s`:{ image!, apiUrl?, tokenEnv?, caCertEnv?, insecureTls?, namespace
|
|
254
|
+
- `k8s`:{ image!, apiUrl?, tokenEnv?, caCertEnv?, insecureTls?, namespace!, runtimeClass?, mountPath?,
|
|
255
255
|
timeoutMs?, memory?, cpu?, s3Snapshot?{ endpoint, bucket?, accessKeyEnv, secretKeyEnv, … } }。
|
|
256
256
|
- `ssh`:{ host!, port?, username!, privateKeyEnv!, mountPath? }。
|
|
257
257
|
- `adb`:{ serial!, adbPath?, mountPath? }。
|
|
@@ -266,9 +266,11 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
|
|
|
266
266
|
- `EXECUTION_LANES: readonly ExecutionLaneRow[]`:每行 `{ lane, tenantSeparation: "supported"|"single-user-only", requiredEnv,
|
|
267
267
|
requiredSecretEnv, optionalEnv, optionalSecretEnv }`(深冻结;四栏 env 名两两不交、跨车道不交;行内容逐格抄自引擎,
|
|
268
268
|
坐标在每行 JSDoc)。`TENANT_SEPARATION_WORDS`。
|
|
269
|
+
k8s 行的非密必需栏 = `K8S_SANDBOX_IMAGE` + `K8S_NAMESPACE`(沙箱 pod 所在的命名空间,部署自己声明;服务端的必需 env 门按本行 `requiredEnv ∪ requiredSecretEnv` 拒启)——
|
|
270
|
+
写 worker 的 k8s 行时 `runtime.execution.coords` 两键都要给,缺一键 ⇒ 写闸 issue、存下来的 workers 文档整域读不出。
|
|
269
271
|
- `STORE_BACKENDS`(mysql/pg/local/memory)、`SESSION_BACKENDS`(memory/mysql/auto)、`MEMORY_ENGINE_BACKENDS`(file/pg/tidb)。
|
|
270
272
|
- 判据(都在 `WorkerSpec` schema 里,zod issue 的 `path` 指字段、message 带出路):enabled ⇒ 两块必填;coords / secretRefs 键 ⊆
|
|
271
|
-
表[lane]
|
|
273
|
+
表[lane] 对应栏、必需键在场且非空白(空串或只含空白按缺);别车道键点名车道;`requirePrincipal` ∧ single-user-only 车道拒(出路按表现算);host ∧ 副本 > 1
|
|
272
274
|
拒;device ⇒ SQL 店且 session ≠ memory;memory 店 ∧ session mysql 拒;记忆引擎 pg/tidb ∧ session memory 拒;SQL 坐标 ⇔ SQL 店;
|
|
273
275
|
记忆后端配店;多租户 ⇒ SQL 店;6.x 删除项(`sessionBackend` / `memoryBackend` / `runtime.db` /
|
|
274
276
|
`coords.tidb*` / `tidbPasswordEnv`)读到即拒并指路迁移。`WorkerSpec` 是 ZodEffects(无 `.shape`):名字判据单独导出 `WORKER_NAME`(src/types.ts)。
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
|
|
3
|
-
重写日期: 2026-10-
|
|
3
|
+
重写日期: 2026-10-28 | 包版本: 15.0.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 相等、
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/settings-schema",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.0.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",
|