@sema-agent/settings-schema 15.1.0 → 16.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +226 -0
  2. package/README.md +60 -46
  3. package/conformance/VERSION +1 -1
  4. package/conformance/cases/c16-deep-nesting-205/expected.json +2 -2
  5. package/conformance/cases/c16-deep-nesting-205/meta.json +1 -1
  6. package/conformance/cases/c50-s784-lane-host-legal/expected.json +6 -2
  7. package/conformance/cases/c50-s784-lane-host-legal/input.json +3 -1
  8. package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +6 -2
  9. package/conformance/cases/c51-s784-lane-e2b-legal/input.json +3 -1
  10. package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +6 -2
  11. package/conformance/cases/c52-s784-lane-k8s-legal/input.json +3 -1
  12. package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +6 -2
  13. package/conformance/cases/c53-s784-lane-ssh-legal/input.json +3 -1
  14. package/conformance/cases/c54-s784-lane-adb-legal/expected.json +6 -2
  15. package/conformance/cases/c54-s784-lane-adb-legal/input.json +3 -1
  16. package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +6 -2
  17. package/conformance/cases/c55-s784-lane-local-docker-legal/input.json +3 -1
  18. package/conformance/cases/c56-s784-lane-device-legal/expected.json +8 -2
  19. package/conformance/cases/c56-s784-lane-device-legal/input.json +5 -2
  20. package/conformance/cases/c57-s784-g1-enabled-no-lane/meta.json +1 -1
  21. package/conformance/cases/c58-s784-g2-multitenant-host/input.json +3 -1
  22. package/conformance/cases/c59-s784-g3-k8s-missing-image/input.json +3 -1
  23. package/conformance/cases/c60-s784-g3-e2b-missing-key/input.json +3 -1
  24. package/conformance/cases/c61-s784-g3-other-lane-key/input.json +3 -1
  25. package/conformance/cases/c62-s784-g7a-session-tidb/input.json +3 -1
  26. package/conformance/cases/c63-s784-host-two-replicas/input.json +3 -1
  27. package/conformance/cases/c64-s784-multitenant-local-store/input.json +3 -1
  28. package/conformance/cases/c65-s784-pg-without-db/input.json +3 -1
  29. package/conformance/cases/c66-s784-memory-backend-mismatch/input.json +3 -1
  30. package/conformance/cases/c66-s784-memory-backend-mismatch/meta.json +1 -1
  31. package/conformance/cases/c68-s784-device-local-store/input.json +6 -1
  32. package/conformance/cases/cli04-env-value-with-newline/expected.json +6 -2
  33. package/conformance/cases/cli04-env-value-with-newline/input.json +3 -1
  34. package/dist/cli.d.ts +1 -1
  35. package/dist/config-d-file.d.ts +41 -5
  36. package/dist/config-d-file.js +105 -22
  37. package/dist/config-document-refusal.d.ts +39 -0
  38. package/dist/config-document-refusal.js +2 -0
  39. package/dist/device-relay.d.ts +21 -0
  40. package/dist/device-relay.js +23 -0
  41. package/dist/env-model-table.d.ts +2 -9
  42. package/dist/env-model-table.js +6 -14
  43. package/dist/execution-lane-index.d.ts +21 -17
  44. package/dist/execution-lane-index.js +28 -20
  45. package/dist/execution-lanes.d.ts +20 -5
  46. package/dist/execution-lanes.js +31 -7
  47. package/dist/file-store.d.ts +30 -3
  48. package/dist/file-store.js +89 -32
  49. package/dist/fleet.d.ts +6 -8
  50. package/dist/fleet.js +5 -7
  51. package/dist/hooks.d.ts +1 -1
  52. package/dist/index.d.ts +2 -1
  53. package/dist/index.js +4 -3
  54. package/dist/migration-receipt.d.ts +1 -1
  55. package/dist/migration-receipt.js +1 -1
  56. package/dist/one-time-migrations.d.ts +1 -1
  57. package/dist/one-time-migrations.js +166 -45
  58. package/dist/remote-exec.js +11 -4
  59. package/dist/safety-merge-spec.d.ts +1 -1
  60. package/dist/safety-merge-spec.js +1 -1
  61. package/dist/types.d.ts +15 -11
  62. package/dist/types.js +100 -49
  63. package/dist/url-shape.d.ts +18 -0
  64. package/dist/url-shape.js +22 -0
  65. package/docs/API-SURFACE.md +18 -7
  66. package/docs/CONFORMANCE.md +1 -7
  67. package/docs/CONSUMER-INTEGRATION.md +4 -4
  68. package/llms-full.txt +27 -25
  69. package/llms.txt +10 -8
  70. package/package.json +1 -1
@@ -14,6 +14,8 @@
14
14
  * - A MISSING file (ENOENT) → the domain is absent → schema defaults fill in (parseDomain tolerates undefined).
15
15
  * A present-but-EMPTY/whitespace file is a half-write → THROW (fail-closed, like remote-exec); to mean
16
16
  * "absent/defaults" the file must be missing, not empty. A valid `{}` object IS a set domain (→ defaults).
17
+ * - `read()` (16.0.0) is the ONE read that answers both ways: the effective config + its read warnings as data when every
18
+ * document reads, or the typed refusal list of the whole directory + its version when one does not — every document read once.
17
19
  * - Non-portable domains are OMITTED on a single machine and resolve to their schema defaults (`{workers:[]}`
18
20
  * etc.) — no stubbing needed. 逐域为什么被排除见 {@link NON_PORTABLE_DOMAIN_REASONS}(禁裸排除)。
19
21
  * - A RETIRED domain's file (`config.d/runtime.json`, see `RETIRED_DOMAINS`) is read only for its tombstone: carrying facts ⇒
@@ -24,16 +26,13 @@
24
26
  * "skip re-apply if version unchanged" works locally. `getEffective().updatedAt` = the max file mtime,
25
27
  * passed EXPLICITLY into buildEffective (the determinism refactor) so identical files ⇒ identical bytes.
26
28
  */
27
- import { readdir, readFile, stat } from "node:fs/promises";
29
+ import { readdir, stat } from "node:fs/promises";
28
30
  import path from "node:path";
29
- import { buildEffective, DOMAIN_READ_FALLBACK, parseDomainLoud } from "./config-fns.js";
30
- import { configDPath, readConfigDFile, refuseRetiredDomainFiles } from "./config-d-file.js";
31
- import { stableHash } from "./hash.js";
31
+ import { RetiredDomainError, buildEffective, DOMAIN_READ_FALLBACK, parseDomainLoud, refuseRetiredDomain } from "./config-fns.js";
32
+ import { RETIRED_DOMAIN_NAMES, configDPath, configDVersionOf, corruptDocumentRaw, readConfigDFile, readConfigDFileReading, refuseRetiredDomainFiles } from "./config-d-file.js";
32
33
  import { PORTABLE_DOMAINS } from "./portable-domains.js";
33
34
  import { REMOTE_EXEC_DOCUMENT } from "./remote-exec.js";
34
- import { RETIRED_DOMAINS, isDomainName, isRetiredDomainName } from "./types.js";
35
- /** The retired domain names (`RETIRED_DOMAINS` keys) — read from `config.d/` for their tombstone only. */
36
- const RETIRED_DOMAIN_NAMES = Object.keys(RETIRED_DOMAINS);
35
+ import { isDomainName, isRetiredDomainName } from "./types.js";
37
36
  // 便携域表的定义住在纯叶 ./portable-domains.ts(14.6.0;零 node 依赖,主桶模块可 import);这里原名转出口,`./node` 子路径逐名不变。
38
37
  export { NON_PORTABLE_DOMAIN_REASONS, PORTABLE_DOMAINS } from "./portable-domains.js";
39
38
  /** `config.d/` 里**合法存在但不是域文档**的文件名(不进 `"unread-config-file"` 告警)。`remote-exec`
@@ -87,6 +86,11 @@ export class FileConfigStore {
87
86
  const onWarning = this.opts.onWarning;
88
87
  if (!onWarning)
89
88
  return;
89
+ for (const w of await this.#unreadFiles())
90
+ onWarning(w);
91
+ }
92
+ /** {@link #warnUnreadFiles} 的扫描本体:没人读的 `*.json` 逐个一条告警(单读口 `read()` 把它们交成数据,所以它总扫)。 */
93
+ async #unreadFiles() {
90
94
  let names;
91
95
  try {
92
96
  names = await readdir(path.join(this.root, "config.d"));
@@ -94,8 +98,9 @@ export class FileConfigStore {
94
98
  catch {
95
99
  // 目录读不了(不存在 / 只有 x 没有 r 的目录权限)⇒ 本次扫不了,**不抛**:这条是尽力而为的审计线,
96
100
  // 绝不能把一次本来能成功的 getEffective 拖成失败(域文件本身仍按名逐个打开,该失败的自然会失败)。
97
- return;
101
+ return [];
98
102
  }
103
+ const out = [];
99
104
  for (const name of names) {
100
105
  if (name.startsWith(".") || !name.endsWith(".json") || NON_DOMAIN_CONFIG_FILES.has(name))
101
106
  continue;
@@ -104,10 +109,11 @@ export class FileConfigStore {
104
109
  continue; // 真被读的域
105
110
  if (isRetiredDomainName(base))
106
111
  continue; // retired 域:由墓碑判(带事实 ⇒ getEffective 抛),不是「没人读的文件」
107
- onWarning(isDomainName(base)
112
+ out.push(isDomainName(base)
108
113
  ? { kind: "unread-config-file", file: name, domain: base, why: "non-portable-domain" }
109
114
  : { kind: "unread-config-file", file: name, why: "not-a-domain" });
110
115
  }
116
+ return out;
111
117
  }
112
118
  /** Raw (unparsed) values for every PORTABLE domain and every retired domain (the latter for the tombstone), keyed by
113
119
  * name (absent → undefined). Both feed the version hash: a retired file appearing / changing flips `getEffective`
@@ -136,22 +142,24 @@ export class FileConfigStore {
136
142
  }
137
143
  /** Read+parse one domain (validated, defaults filled), or `undefined` if the domain is ABSENT — matching
138
144
  * the {@link ConfigReader} contract (reader.ts: absent domain → undefined) and the server stores'
139
- * `has(domain) ? parse : undefined`. A present-but-empty file (`{}` / whitespace) IS a set domain → parsed
140
- * with defaults; a missing file is absence → undefined. (getEffective still fills defaults for absent
141
- * domains; getDomain is the per-domain "was this ever set?" probe.) */
145
+ * `has(domain) ? parse : undefined`. Only a MISSING file (ENOENT) is absence → undefined. A present `{}` IS a set
146
+ * domain → parsed with defaults. A present file that is empty or whitespace-only (a half-write) throws as a corrupt file,
147
+ * as do bad JSON, a non-object, an unreadable file and one nested too deep (the same reading as getEffective / readBundle).
148
+ * (getEffective still fills defaults for absent domains; getDomain is the per-domain "was this ever set?" probe.) */
142
149
  async getDomain(domain) {
143
150
  await refuseRetiredDomainFiles(this.root); // every read entry asks the tombstone first (exportBundle reads through here)
144
151
  const raw = await this.#readDomainRaw(domain);
145
152
  if (raw === undefined)
146
- return undefined; // missing/empty file → domain absent (not schema defaults)
153
+ return undefined; // missing file (ENOENT) → domain absent (not schema defaults); a corrupt file threw above
147
154
  // 0.19.0(对抗复审 复审 round2 F3):与 buildEffective **同一条** parse-and-warn 判据。此前本方法直接
148
155
  // parseDomain,于是 `exportBundle`(它就是逐域走 getDomain 读的)会把一份携带未知键的域**静默剥**
149
156
  // 成一份"干净"的 bundle —— 再 import 回去就是一次持久的配置丢失,全程零告警。
150
157
  return parseDomainLoud(domain, raw, this.opts.onWarning);
151
158
  }
152
- /** Content hash of a raws snapshot → a stable u53-ish version number (≤ Number.MAX_SAFE_INTEGER). */
159
+ /** Content hash of a raws snapshot → a stable u53-ish version number (≤ Number.MAX_SAFE_INTEGER). The one reading is `configDVersionOf`
160
+ * (`./config-d-file`) — `read()` asks the same function, so a clean tree has the same version from either. */
153
161
  #versionOf(raws) {
154
- return parseInt(stableHash(raws).slice(0, 13), 16); // 13 hex digits ≤ 2^52, safely within u53
162
+ return configDVersionOf(raws);
155
163
  }
156
164
  async getEffective(opts) {
157
165
  // Every read entry asks the tombstone first (the same check getDomain runs); what it read (absent / `{}`) joins the version hash.
@@ -170,26 +178,19 @@ export class FileConfigStore {
170
178
  const hashRaws = { ...retired };
171
179
  const domainErrors = [];
172
180
  await Promise.all(PORTABLE_DOMAINS.map(async (d) => {
173
- try {
174
- const v = await this.#readDomainRaw(d);
181
+ // 与严读同一只文件读法(读数):被拒的读数交 `thrown`(gate 域原样抛)或 `error` 句(其余域单列进 domainErrors)—— 坏文件的指纹就是这一次读到的字节,不是事后的第二次读。
182
+ const reading = await readConfigDFileReading(this.root, d);
183
+ if (reading.status !== "refused") {
184
+ const v = reading.status === "present" ? reading.value : undefined;
175
185
  raws[d] = v;
176
186
  hashRaws[d] = v;
187
+ return;
177
188
  }
178
- catch (e) {
179
- if (DOMAIN_READ_FALLBACK[d] === "throw")
180
- throw e; // gate 域:READ 容错也不放宽 fail-loud
181
- domainErrors.push({ domain: d, error: e instanceof Error ? e.message : String(e) });
182
- raws[d] = undefined; // buildEffective 按 absent → schema default 落(fail-closed)
183
- // 哨兵进 hash:用文件文本 hash 区分不同坏内容;读不回文本(竞态删除等)退回错误文案。
184
- let textHash;
185
- try {
186
- textHash = stableHash((await readFile(this.domainPath(d), "utf8")).trim());
187
- }
188
- catch {
189
- textHash = stableHash(e instanceof Error ? e.message : String(e));
190
- }
191
- hashRaws[d] = { __corruptDomainFile: textHash };
192
- }
189
+ if (DOMAIN_READ_FALLBACK[d] === "throw")
190
+ throw reading.thrown; // gate 域:READ 容错也不放宽 fail-loud
191
+ domainErrors.push({ domain: d, error: reading.error });
192
+ raws[d] = undefined; // buildEffective 按 absent → schema default 落(fail-closed)
193
+ hashRaws[d] = corruptDocumentRaw(reading); // 哨兵进 hash:不同坏内容不同数(见 corruptDocumentRaw)
193
194
  }));
194
195
  const updatedAt = await this.#maxMtimeIso();
195
196
  const effective = buildEffective(this.#versionOf(hashRaws), (d) => raws[d], updatedAt, this.opts.onWarning);
@@ -200,4 +201,60 @@ export class FileConfigStore {
200
201
  async getVersion() {
201
202
  return this.#versionOf(await this.#readAllRaw());
202
203
  }
204
+ /**
205
+ * **单读口**(16.0.0):一次读整棵 `config.d`(全部便携域 + retired 域的墓碑文档,每份只读一次),交一个结局({@link FileConfigRead})——
206
+ * · 每份文档在文件层都读得进 ⇒ `effective`:与严读 `getEffective()` 逐位同;读侧告警交成数据(没人读的文件 + 装配告警,不经 `onWarning`);
207
+ * · 有文档在文件层被拒(空 / 坏 JSON / 不是对象 / 读不出 / 过深;带事实的 retired 域文档是墓碑拒,带迁移编号)⇒ `refused`:整棵目录的拒单与这一次读到的内容版本。
208
+ * 严读与 `readBundle` 读的是同一只文件读法(`./config-d-file` 的读数)—— 同一种坏形同一个词、同一句话;区别只在渲染:严读遇到第一份坏文档就抛,本方法把整个目录读完、逐份交出。
209
+ * 文件层之外的拒(读得出、过不了门控域的 schema)照严读抛 —— 那是判形,不在拒单里。拒单、版本、effective、告警都出自这一次读到的字节:没有「先抛、再重探」的第二次读。
210
+ */
211
+ async read() {
212
+ const documents = [...RETIRED_DOMAIN_NAMES, ...PORTABLE_DOMAINS];
213
+ const [readings, unread] = await Promise.all([Promise.all(documents.map((d) => readConfigDFileReading(this.root, d))), this.#unreadFiles()]);
214
+ const raws = {};
215
+ const refusals = [];
216
+ documents.forEach((document, i) => {
217
+ const reading = readings[i];
218
+ switch (reading.status) {
219
+ case "absent":
220
+ raws[document] = undefined;
221
+ return;
222
+ case "refused":
223
+ raws[document] = corruptDocumentRaw(reading);
224
+ refusals.push({ document, reason: reading.reason, error: reading.error });
225
+ return;
226
+ case "present":
227
+ raws[document] = reading.value;
228
+ if (isRetiredDomainName(document)) {
229
+ const tombstone = tombstoneOf(document, reading.value);
230
+ if (tombstone !== undefined)
231
+ refusals.push(tombstone);
232
+ }
233
+ return;
234
+ default: {
235
+ const unhandled = reading;
236
+ throw new Error(`FileConfigStore.read: unhandled reading ${String(unhandled)}`);
237
+ }
238
+ }
239
+ });
240
+ const version = this.#versionOf(raws);
241
+ if (refusals.length > 0)
242
+ return { kind: "refused", refusals, version, warnings: unread };
243
+ const warnings = [...unread];
244
+ const updatedAt = await this.#maxMtimeIso();
245
+ const effective = buildEffective(version, (d) => raws[d], updatedAt, (w) => warnings.push(w));
246
+ return { kind: "effective", effective, warnings };
247
+ }
248
+ }
249
+ /** 墓碑判一次(与严读同一只判官 `refuseRetiredDomain`、同一句):retired 域文档带事实 ⇒ 一条 `retired_domain` 拒;缺席 / `{}` ⇒ `undefined`。 */
250
+ function tombstoneOf(domain, raw) {
251
+ try {
252
+ refuseRetiredDomain(domain, raw);
253
+ return undefined;
254
+ }
255
+ catch (e) {
256
+ if (e instanceof RetiredDomainError)
257
+ return { document: e.domain, reason: "retired_domain", error: e.message, migration: e.migration };
258
+ throw e;
259
+ }
203
260
  }
package/dist/fleet.d.ts CHANGED
@@ -166,12 +166,13 @@ export type RestartSignal = z.infer<typeof RestartSignal>;
166
166
  * `config.gateway_fallback_without_primary` — fallback gateways are declared without a primary;
167
167
  * `config.catalog_replaces_env_models` ({@link CATALOG_REPLACES_ENV_MODELS_CODE}) — on the LOCAL config leg, a candidate that
168
168
  * manages the model plane would replace a declared env model table (`MODEL_ID` …) while the config root's decision document
169
- * (`config.env-models.json`, read by `readEnvModelsDecision`) does not say `replaced` — the env models keep serving;
170
- * `config.summarize_model_missing` ({@link SUMMARIZE_MODEL_MISSING_CODE}) — the summarize role, which every task resolves,
171
- * would land on no servable entry (every task would be refused after adoption).
169
+ * (`config.env-models.json`, read by `readEnvModelsDecision`) does not say `replaced` — the env models keep serving.
172
170
  * Source (engine): the first two in `src/model-entry-refusal.ts`, the next two in `src/model-route-endpoint.ts:40,42`
173
171
  * (invariant `model-route-endpoint`, judged by the model-plane stage in `src/config-center/apply-effective.ts:664-665`); the
174
- * last two are named HERE first (the code is a contract word: this package names it and exports the two constants for the engine to read; the engine does not mint them today).
172
+ * last one is named HERE first (the code is a contract word: this package names it and exports the constant for the engine to read; the engine does not mint it today).
173
+ * 16.0.0 retired `config.summarize_model_missing` (and its constant) from this set: a role seat naming an entry the catalog does not serve
174
+ * reads as unset (it falls back down the role chain and the engine says so once) — no reading refuses a model plane for it, so it was never
175
+ * an engine group refusal. A reader still holding it gets it dropped by {@link readModelRefusals} like any code outside the set.
175
176
  */
176
177
  export declare const MODEL_ENTRY_REFUSAL_CODES: readonly ["config.model_no_brain", "config.model_entry_invalid"];
177
178
  export declare const ModelEntryRefusalCode: z.ZodEnum<{
@@ -181,12 +182,9 @@ export declare const ModelEntryRefusalCode: z.ZodEnum<{
181
182
  export type ModelEntryRefusalCode = z.infer<typeof ModelEntryRefusalCode>;
182
183
  /** The env-model-table confirmation gate's group-refusal code (see {@link MODEL_PLANE_REFUSAL_CODES}). */
183
184
  export declare const CATALOG_REPLACES_ENV_MODELS_CODE: "config.catalog_replaces_env_models";
184
- /** The summarize invariant's group-refusal code (see {@link MODEL_PLANE_REFUSAL_CODES}); a writer's plan answers `fix_first` with it. */
185
- export declare const SUMMARIZE_MODEL_MISSING_CODE: "config.summarize_model_missing";
186
- export declare const MODEL_PLANE_REFUSAL_CODES: readonly ["config.default_model_missing", "config.model_refused_name_minted", "config.model_route_endpoint_missing", "config.gateway_fallback_without_primary", "config.catalog_replaces_env_models", "config.summarize_model_missing"];
185
+ export declare const MODEL_PLANE_REFUSAL_CODES: readonly ["config.default_model_missing", "config.model_refused_name_minted", "config.model_route_endpoint_missing", "config.gateway_fallback_without_primary", "config.catalog_replaces_env_models"];
187
186
  export declare const ModelPlaneRefusalCode: z.ZodEnum<{
188
187
  "config.catalog_replaces_env_models": "config.catalog_replaces_env_models";
189
- "config.summarize_model_missing": "config.summarize_model_missing";
190
188
  "config.default_model_missing": "config.default_model_missing";
191
189
  "config.model_refused_name_minted": "config.model_refused_name_minted";
192
190
  "config.model_route_endpoint_missing": "config.model_route_endpoint_missing";
package/dist/fleet.js CHANGED
@@ -234,26 +234,24 @@ function readSignalBody(body, pick, schema) {
234
234
  * `config.gateway_fallback_without_primary` — fallback gateways are declared without a primary;
235
235
  * `config.catalog_replaces_env_models` ({@link CATALOG_REPLACES_ENV_MODELS_CODE}) — on the LOCAL config leg, a candidate that
236
236
  * manages the model plane would replace a declared env model table (`MODEL_ID` …) while the config root's decision document
237
- * (`config.env-models.json`, read by `readEnvModelsDecision`) does not say `replaced` — the env models keep serving;
238
- * `config.summarize_model_missing` ({@link SUMMARIZE_MODEL_MISSING_CODE}) — the summarize role, which every task resolves,
239
- * would land on no servable entry (every task would be refused after adoption).
237
+ * (`config.env-models.json`, read by `readEnvModelsDecision`) does not say `replaced` — the env models keep serving.
240
238
  * Source (engine): the first two in `src/model-entry-refusal.ts`, the next two in `src/model-route-endpoint.ts:40,42`
241
239
  * (invariant `model-route-endpoint`, judged by the model-plane stage in `src/config-center/apply-effective.ts:664-665`); the
242
- * last two are named HERE first (the code is a contract word: this package names it and exports the two constants for the engine to read; the engine does not mint them today).
240
+ * last one is named HERE first (the code is a contract word: this package names it and exports the constant for the engine to read; the engine does not mint it today).
241
+ * 16.0.0 retired `config.summarize_model_missing` (and its constant) from this set: a role seat naming an entry the catalog does not serve
242
+ * reads as unset (it falls back down the role chain and the engine says so once) — no reading refuses a model plane for it, so it was never
243
+ * an engine group refusal. A reader still holding it gets it dropped by {@link readModelRefusals} like any code outside the set.
243
244
  */
244
245
  export const MODEL_ENTRY_REFUSAL_CODES = ["config.model_no_brain", "config.model_entry_invalid"];
245
246
  export const ModelEntryRefusalCode = z.enum(MODEL_ENTRY_REFUSAL_CODES);
246
247
  /** The env-model-table confirmation gate's group-refusal code (see {@link MODEL_PLANE_REFUSAL_CODES}). */
247
248
  export const CATALOG_REPLACES_ENV_MODELS_CODE = "config.catalog_replaces_env_models";
248
- /** The summarize invariant's group-refusal code (see {@link MODEL_PLANE_REFUSAL_CODES}); a writer's plan answers `fix_first` with it. */
249
- export const SUMMARIZE_MODEL_MISSING_CODE = "config.summarize_model_missing";
250
249
  export const MODEL_PLANE_REFUSAL_CODES = [
251
250
  "config.default_model_missing",
252
251
  "config.model_refused_name_minted",
253
252
  "config.model_route_endpoint_missing",
254
253
  "config.gateway_fallback_without_primary",
255
254
  CATALOG_REPLACES_ENV_MODELS_CODE,
256
- SUMMARIZE_MODEL_MISSING_CODE,
257
255
  ];
258
256
  export const ModelPlaneRefusalCode = z.enum(MODEL_PLANE_REFUSAL_CODES);
259
257
  /**
package/dist/hooks.d.ts CHANGED
@@ -78,8 +78,8 @@ export declare const HookEventName: z.ZodEnum<{
78
78
  }>;
79
79
  export type HookEventName = z.infer<typeof HookEventName>;
80
80
  export declare const HookOwner: z.ZodEnum<{
81
- engine: "engine";
82
81
  shell: "shell";
82
+ engine: "engine";
83
83
  }>;
84
84
  export type HookOwner = z.infer<typeof HookOwner>;
85
85
  /**
package/dist/index.d.ts CHANGED
@@ -4,7 +4,9 @@
4
4
  */
5
5
  export * from "./types.js";
6
6
  export * from "./execution-lanes.js";
7
+ export * from "./config-document-refusal.js";
7
8
  export * from "./url-shape.js";
9
+ export * from "./device-relay.js";
8
10
  export * from "./url-credential.js";
9
11
  export * from "./default-model.js";
10
12
  export { catalogManagesModelPlane, enabledEntryCount } from "./catalog-manages.js";
@@ -47,4 +49,3 @@ export * from "./control-and-bidi.js";
47
49
  export * from "./mcp-request-entry.js";
48
50
  export { REMOTE_MCP_SERVER_NAME_PREFIX } from "./mcp-server-name.js";
49
51
  export * from "./center-refs.js";
50
- export { isBlankCoordinate } from "./execution-lane-index.js";
package/dist/index.js CHANGED
@@ -3,8 +3,10 @@
3
3
  * web-admin, the worker service, and the TOC desktop/CLI.
4
4
  */
5
5
  export * from "./types.js";
6
- export * from "./execution-lanes.js"; // 7.0.0 M-R2: the ONE lane / store vocabulary (EXECUTION_LANE_WORDS / EXECUTION_LANES / STORE_BACKENDS / SESSION_BACKENDS / MEMORY_ENGINE_BACKENDS) — pure data, browser-safe
7
- export * from "./url-shape.js"; // 11.0.0: THE URL shape judge (httpUrlFlaws / httpUrlRule / isHierarchicalUrl), pure/browser-safe
6
+ export * from "./execution-lanes.js"; // 7.0.0 M-R2: the ONE lane / store vocabulary (EXECUTION_LANE_WORDS / EXECUTION_LANES / STORE_BACKENDS / SESSION_BACKENDS / MEMORY_ENGINE_BACKENDS) + THE lane-coordinate blank judge isBlankCoordinate (empty or whitespace-only = absent; the write gate and every consumer ask this one function; whether a key is set, and whether an optional key may be empty, stays the caller's call; the same function object as on the `./execution-lanes` subpath) — a zero-import leaf, pure/browser-safe
7
+ export * from "./config-document-refusal.js"; // 15.2.0: the typed refusal of one config.d document (CONFIG_DOCUMENT_REFUSAL_REASONS = empty / invalid_json / not_object / io / retired_domain; ConfigDocumentRefusal { document, reason, error } + the tombstone arm's migration) — the reading is `inspectBundle` in ./node; pure/browser-safe
8
+ export * from "./url-shape.js"; // 11.0.0: THE URL shape judge (httpUrlFlaws / httpUrlRule / isHierarchicalUrl; 16.0.0 + wsUrlFlaws), pure/browser-safe
9
+ export * from "./device-relay.js"; // 16.0.0: THE value grammar of the device lane's DEVICE_RELAY_URL (DEVICE_RELAY_NONE / deviceRelayFlaws / DEVICE_RELAY_RULE), pure/browser-safe
8
10
  export * from "./url-credential.js"; // 14.5.0: THE "does this URL carry a credential" judge (findUrlCredential / findUrlUserinfo / URL_CREDENTIAL_REASONS / CREDENTIAL_PARAM_NAMES …), pure/browser-safe
9
11
  export * from "./default-model.js"; // 11.0.0: THE catalog-default resolver (roles.default → active tier ladder → fallback), pure/browser-safe
10
12
  export { catalogManagesModelPlane, enabledEntryCount } from "./catalog-manages.js"; // THE "does this models document manage the model plane" judge (raw half: at least one entry not explicitly disabled) + the log count it is derived from, pure/browser-safe
@@ -47,7 +49,6 @@ export * from "./control-and-bidi.js"; // the one-line echo strip set (C0 + DEL
47
49
  export * from "./mcp-request-entry.js"; // 14.0.0: the request-plane MCP entry judges (isUsableWireName / isContentOriginWord / readMcpIdentityHeaders / readRequestMcpTransport + the value-shape describers) — one reading for the service legs and the shell pre-check, pure/browser-safe
48
50
  export { REMOTE_MCP_SERVER_NAME_PREFIX } from "./mcp-server-name.js"; // the MCP server-name prefix reserved for the remote MCP servers a device executor offers (mount name = prefix + the executor's server name); the predicate and its sentence stay package-private — the request plane's MCP name judges (isUsableMcpServerName / describeBadMcpServerName) read them; the config plane's McpServerSpec.name accepts the prefix (refusing it would be a validation tightening — a major), pure/browser-safe
49
51
  export * from "./center-refs.js"; // resolveCenterRefs(refs, lookup) — THE center-roster reference resolver: one verdict word per member (missing_name / not_a_string / read_threw, CENTER_REF_FAULTS), one whole verdict (resolved / unresolved / refs_unreadable, CENTER_REFS_VERDICTS); the refs record is read by the public readPlainStringRecord, the lookup is the caller's, it reads no process.env; pure/browser-safe
50
- export { isBlankCoordinate } from "./execution-lane-index.js"; // THE lane-coordinate blank judge (empty or whitespace-only = absent; the write gate and every consumer ask this one function; whether a key is set, and whether an optional key may be empty, stays the caller's call); named: the lane tables' derived indexes stay package-private, pure/browser-safe
51
52
  // NOTE: the Node-fs-backed helpers (FileConfigStore + the file editor) are NOT in this barrel — they pull
52
53
  // `node:fs`/`node:path` and would break a browser/client bundle that only wants the contract (e.g.
53
54
  // web-admin's client components import the zod types through here). Import them from the Node-only
@@ -7,7 +7,7 @@ import type { REMOTE_EXEC_DOCUMENT } from "./remote-exec.js";
7
7
  import type { DomainName, RetiredDomainName } from "./types.js";
8
8
  /** 本版登记的一次性**数据**迁移 id(闭集;每个 id 在 `ONE_TIME_MIGRATIONS` 登记表里恰一行)。收据只许这些 id —— 同表登记的 wire 删码位
9
9
  * (`WIRE_RETIREMENT_IDS`,`./one-time-migrations`)不改任何存储形,铸不出收据,不在本集。 */
10
- 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", "SS-M11"];
10
+ 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", "SS-M11", "SS-M12"];
11
11
  export type OneTimeMigrationId = (typeof ONE_TIME_MIGRATION_IDS)[number];
12
12
  /**
13
13
  * 一条收据说迁移**对一个坐标做了什么**(闭集,每词一义):
@@ -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", "SS-M10", "SS-M11"];
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", "SS-M11", "SS-M12"];
4
4
  /**
5
5
  * 一条收据说迁移**对一个坐标做了什么**(闭集,每词一义):
6
6
  * - `moved` — 值搬到新位(`to`),义不变;
@@ -41,7 +41,7 @@ export type StoredDocuments = Readonly<Partial<Record<DomainName | RetiredDomain
41
41
  export interface StoredDocumentsMigration {
42
42
  /** 迁移后的文档集:没改的文档是**同一引用**;被移除的(retired 域)不在;新建的(例:迁出的 `governance` / `limits`)在。 */
43
43
  readonly docs: Readonly<Record<string, unknown>>;
44
- /** 逐条收据(步序 SS-M1 → SS-M2 → SS-M5 → SS-M6 → SS-M8 → SS-M9 → SS-M10 → SS-M7 → SS-M4:纯改写步在前,用新 schema 终判的 SS-M4 最后;步内按坐标)。
44
+ /** 逐条收据(步序 SS-M1 → SS-M2 → SS-M5 → SS-M6 → SS-M8 → SS-M9 → SS-M10 → SS-M7 → workers 行步〔SS-M12 改写 → SS-M4 改写 → 一次终判〕:纯改写步在前,用新 schema 终判的 workers 行步最后;步内按坐标)。
45
45
  * 只有 `refused` 的收据 = 什么都没改。 */
46
46
  readonly receipts: readonly MigrationReceipt[];
47
47
  }