@sema-agent/settings-schema 17.2.0 → 19.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 (141) hide show
  1. package/CHANGELOG.md +216 -0
  2. package/README.md +53 -6
  3. package/conformance/VERSION +1 -1
  4. package/conformance/baseline.json +3 -0
  5. package/conformance/cases/c01-entry-salvage-then-typo/expected.json +4 -1
  6. package/conformance/cases/c02-hosts-clamp-stacked/expected.json +4 -1
  7. package/conformance/cases/c03-literal-dotted-top-key/expected.json +4 -1
  8. package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +4 -1
  9. package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +4 -1
  10. package/conformance/cases/c08-limits-unknown-carried-64/expected.json +4 -1
  11. package/conformance/cases/c09-limits-unknown-carried-65/expected.json +4 -1
  12. package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +4 -1
  13. package/conformance/cases/c11-unicode-unknown-keys/expected.json +4 -1
  14. package/conformance/cases/c12-prototype-name-top-keys/expected.json +4 -1
  15. package/conformance/cases/c13-models-field-null/expected.json +4 -1
  16. package/conformance/cases/c14-models-field-empty-array/expected.json +4 -1
  17. package/conformance/cases/c15-models-field-number/expected.json +4 -1
  18. package/conformance/cases/c16-deep-nesting-205/expected.json +3 -0
  19. package/conformance/cases/c17-empty-domain-file/expected.json +3 -0
  20. package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +4 -1
  21. package/conformance/cases/c19-wire-envelope-no-version/expected.json +4 -1
  22. package/conformance/cases/c21-governance-empty-object-wins/expected.json +4 -1
  23. package/conformance/cases/c23-mixed-validity-models/expected.json +4 -1
  24. package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +4 -1
  25. package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +4 -1
  26. package/conformance/cases/c26-hosts-array-1024/expected.json +4 -1
  27. package/conformance/cases/c27-hosts-array-1025/expected.json +4 -1
  28. package/conformance/cases/c28-large-document-over-1mb/expected.json +4 -1
  29. package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +4 -1
  30. package/conformance/cases/c30-version-triple-a-first/expected.json +4 -1
  31. package/conformance/cases/c31-version-triple-b-changed/expected.json +4 -1
  32. package/conformance/cases/c32-version-triple-a-restored/expected.json +4 -1
  33. package/conformance/cases/c33-limits-nested-carried-key/expected.json +4 -1
  34. package/conformance/cases/c34-skills-content-vs-manifest/expected.json +7 -1
  35. package/conformance/cases/c35-portable-domain-file-is-null/expected.json +3 -0
  36. package/conformance/cases/c36-skills-entry-without-content/expected.json +7 -1
  37. package/conformance/cases/c37-skills-field-null/expected.json +6 -0
  38. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +4 -1
  39. package/conformance/cases/c39-hidden-file-in-config-d/expected.json +4 -1
  40. package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +4 -1
  41. package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +4 -1
  42. package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +4 -1
  43. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +4 -1
  44. package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +4 -1
  45. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +4 -1
  46. package/conformance/cases/c46-s811-host-name-url-userinfo-withheld/expected.json +4 -1
  47. package/conformance/cases/c47-s811-roles-key-url-userinfo-withheld/expected.json +4 -1
  48. package/conformance/cases/c48-s935-model-headers-transported/expected.json +4 -1
  49. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +4 -1
  50. package/conformance/cases/c50-s784-lane-host-legal/expected.json +4 -1
  51. package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +4 -1
  52. package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +4 -1
  53. package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +4 -1
  54. package/conformance/cases/c54-s784-lane-adb-legal/expected.json +4 -1
  55. package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +4 -1
  56. package/conformance/cases/c56-s784-lane-device-legal/expected.json +4 -1
  57. package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +4 -1
  58. package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +4 -1
  59. package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +4 -1
  60. package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +4 -1
  61. package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +4 -1
  62. package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +4 -1
  63. package/conformance/cases/c63-s784-host-two-replicas/expected.json +4 -1
  64. package/conformance/cases/c64-s784-multitenant-local-store/expected.json +4 -1
  65. package/conformance/cases/c65-s784-pg-without-db/expected.json +4 -1
  66. package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +4 -1
  67. package/conformance/cases/c67-s784-retired-field-loud/expected.json +4 -1
  68. package/conformance/cases/c68-s784-device-local-store/expected.json +4 -1
  69. package/conformance/cases/c69-deep-encoded-token-past-every-reading/expected.json +4 -1
  70. package/conformance/cases/c70-rewritten-token-in-front-of-encoded-pem-header/expected.json +4 -1
  71. package/conformance/cases/c71-one-scanner-write-reads-display-forms/expected.json +4 -1
  72. package/conformance/cases/c72-dangling-reference-write-seat-refuses/expected.json +4 -1
  73. package/conformance/cases/c73-projects-extended-registration-reads/expected.json +4 -1
  74. package/conformance/cases/c74-projects-over-bound-refused/expected.json +4 -1
  75. package/conformance/cases/c75-projects-patch-remote-uniqueness/expected.json +4 -1
  76. package/conformance/cases/c76-projects-entry-salvage-nested/expected.json +4 -1
  77. package/conformance/cases/c77-prompts-center-artifact-carried/expected.json +4 -1
  78. package/conformance/cases/c78-prompts-empty-bindings-refused/expected.json +4 -1
  79. package/conformance/cases/c79-prompts-bad-worker-artifact-not-salvaged/expected.json +4 -1
  80. package/conformance/cases/c80-prompts-wire-v2-verbatim/expected.json +7 -1
  81. package/conformance/cases/c81-prompts-declaration-bindings-refused/expected.json +4 -1
  82. package/conformance/cases/c82-prompts-v1-catalog-refused/expected.json +4 -1
  83. package/conformance/cases/c83-prompts-bad-overlay-not-salvaged/expected.json +4 -1
  84. package/conformance/cases/c84-sandbox-git-block-carried-verbatim/expected.json +7 -1
  85. package/conformance/cases/c85-sandbox-git-refused-block-still-carried/expected.json +7 -1
  86. package/conformance/cases/c86-a2a-peers-legal/expected.json +135 -0
  87. package/conformance/cases/c86-a2a-peers-legal/input.json +32 -0
  88. package/conformance/cases/c86-a2a-peers-legal/meta.json +5 -0
  89. package/conformance/cases/c87-a2a-credential-refs-not-partially-salvaged/expected.json +123 -0
  90. package/conformance/cases/c87-a2a-credential-refs-not-partially-salvaged/input.json +39 -0
  91. package/conformance/cases/c87-a2a-credential-refs-not-partially-salvaged/meta.json +5 -0
  92. package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +4 -1
  93. package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +4 -1
  94. package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +4 -1
  95. package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +4 -1
  96. package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +4 -1
  97. package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +4 -1
  98. package/conformance/cases/cli04-env-value-with-newline/expected.json +4 -1
  99. package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +4 -1
  100. package/conformance/cases/t01-absent-domain-key-not-null/expected.json +4 -1
  101. package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +4 -1
  102. package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +4 -1
  103. package/conformance/cases/z01-every-domain-empty-object/expected.json +4 -1
  104. package/conformance/cases/z02-every-domain-unknown-key/expected.json +4 -1
  105. package/dist/center-refs.d.ts +4 -3
  106. package/dist/center-refs.js +8 -6
  107. package/dist/cli.d.ts +1 -1
  108. package/dist/config-fns.d.ts +6 -1
  109. package/dist/config-fns.js +16 -2
  110. package/dist/execution-lanes.d.ts +158 -11
  111. package/dist/execution-lanes.js +76 -11
  112. package/dist/file-edit.d.ts +1 -1
  113. package/dist/file-edit.js +2 -1
  114. package/dist/hooks-config.d.ts +23 -5
  115. package/dist/hooks-config.js +33 -5
  116. package/dist/hooks.d.ts +6 -4
  117. package/dist/hooks.js +3 -1
  118. package/dist/mcp-request-entry.d.ts +11 -21
  119. package/dist/mcp-request-entry.js +21 -38
  120. package/dist/one-time-migrations.js +1 -1
  121. package/dist/portable-domains.d.ts +1 -1
  122. package/dist/portable-domains.js +1 -1
  123. package/dist/remote-exec.d.ts +5 -0
  124. package/dist/remote-exec.js +5 -0
  125. package/dist/reserved-member-name.d.ts +13 -0
  126. package/dist/reserved-member-name.js +14 -0
  127. package/dist/retired-envs.js +2 -0
  128. package/dist/secret-refs.d.ts +1 -1
  129. package/dist/secret-refs.js +5 -1
  130. package/dist/types.d.ts +70 -41
  131. package/dist/types.js +179 -32
  132. package/dist/url-credential.d.ts +12 -2
  133. package/dist/url-credential.js +164 -9
  134. package/dist/wire-name.d.ts +19 -0
  135. package/dist/wire-name.js +21 -0
  136. package/docs/API-SURFACE.md +27 -6
  137. package/docs/CC-DIVERGENCE.md +2 -2
  138. package/docs/CONSUMER-INTEGRATION.md +72 -7
  139. package/llms-full.txt +38 -18
  140. package/llms.txt +10 -10
  141. package/package.json +1 -1
@@ -2,16 +2,28 @@
2
2
  * **「这个 URL 的 userinfo 或 query / fragment 里,有没有名字在凭据名表上的参数」的名形判官**(14.5.0;lift 自 server `src/url-credential.ts`,规则、拒因词闭集与规则句逐字搬)。纯函数,零 import(除同包的 `./url-shape`),浏览器安全。
3
3
  *
4
4
  * ## 与 `NON_SECRET_URL`(`./types`)的关系:两个问题,互补,不是一个包含另一个
5
- * 本判官按**名**判 —— 参数叫什么(名字整名在 {@link CREDENTIAL_PARAM_NAMES} 上就是凭据,不论值长什么样);`NON_SECRET_URL` 的 `urlQueryFragmentHasSecret` 按**值形**判密钥 —— 值长得像密钥
5
+ * 本判官按**名**判 —— 参数叫什么(名字是凭据名 = 整名在 {@link CREDENTIAL_PARAM_NAMES} 上、或以表上的强档词收尾,见下「名字判定」;不论值长什么样);`NON_SECRET_URL` 的 `urlQueryFragmentHasSecret` 按**值形**判密钥 —— 值长得像密钥
6
6
  * (不论参数叫什么)。所以 `?key=abc` 本判官命中而 `NON_SECRET_URL` 放行(值不像密钥),`?x=sk-live-…` 反之;userinfo 两边都判。存量域文档位(`mcp` 的 `transport.url` 等)读 `NON_SECRET_URL`,
7
7
  * 请求面等座位读本判官。把两个问题合成一只定义是另一件事,这里不做。
8
8
  *
9
9
  * ## 规则(一条):URL 里的凭据 = 日志脱敏器已经在用的那一个定义
10
10
  * 一个 URL 里凭据住三个位置:
11
11
  * · **userinfo**(`scheme://<这一段>@host`,只有用户名也算)—— 判由本包的 URL 形判官 {@link httpUrlFlaws}(`["userinfo"]`),本模块不留第二份谓词;
12
- * · **query 参数**、**fragment 参数**(同一段文法)里名字整名在 {@link CREDENTIAL_PARAM_NAMES} 上的那一个 —— 名字按收方的读法读:WHATWG `URLSearchParams` 解一次码,
13
- * ASCII 大小写不敏感(不做 Unicode 折叠),整名在表里才算(`db_password` / `tokens` / `keyword` 是另一个名字)。
14
- * 答首个命中(判序 = 上面的序),拒因闭集两词 {@link URL_CREDENTIAL_REASONS};**值从不进答案**,参数名只回显表里的那个词(小写)。
12
+ * · **query 参数**、**fragment 参数**(同一段文法)里名字是凭据名的那一个(判由 {@link credentialParamWordOf},下一节)—— 名字按收方的读法读:WHATWG `URLSearchParams` 解一次码,
13
+ * ASCII 大小写不敏感(不做 Unicode 折叠);`tokens` / `keyword` / `password_hash` 不是凭据名。
14
+ * 答首个命中(判序 = 上面的序),拒因闭集两词 {@link URL_CREDENTIAL_REASONS};**值从不进答案**,参数名只回显表里的那个词(小写;复合名回显的是它收尾的那个词,不是调用方的名字字节)。
15
+ *
16
+ * ## 名字判定:整名,或末段(18.0.0)—— 答者一只 {@link credentialParamWordOf}
17
+ * 「这个已解码的名字是不是凭据名」只有一个答者,{@link isCredentialParamName} 与本判官的 query / fragment 臂都读它(不留第二张表、第二条规则):
18
+ * 1. **整名**(ASCII 大小写不敏感)在 {@link CREDENTIAL_PARAM_NAMES} 上 ⇒ 那个词(照旧;整名优先:`client_secret` 答 `client_secret`,不答它的末段 `secret`);
19
+ * 2. 否则名字按 `_` / `-` / `.` 与驼峰边界切段(空段不存在),**末段**的尾部是**强档词**(`password` / `passwd` / `pwd` / `secret` / `apikey`;列在 {@link COMPOUND_TAIL_STANCE})⇒ 那个词 ——
20
+ * 末段整段是它(`db_password` / `dbPassword` / `x-db-secret` / `user_pwd`),或它与前缀粘连(`PGPASSWORD`)都算;词**不跨**分隔符(`app_wd` 不是 `pwd`);
21
+ * 3. 尾串闭集 {@link COMPOUND_TAIL_PHRASES}(逐词对齐,分隔符与大小写不论):`secret key` · `secret access key` ⇒ `secret`(`SECRET_KEY` / `AWS_SECRET_ACCESS_KEY`);`api key` ⇒ `api_key`(`x-api-key` / `X_API_KEY` / `xApiKey`);
22
+ * `access token` ⇒ `access_token`(`github_access_token` / `oauth-access-token`)。答词的律只有一条:尾串用 `_` 连起来本身在整名表里 ⇒ 答那个整名词,否则答强段词。
23
+ * **只看末尾**:前缀 / 中段命中不算,词后还有别的字就不是(`password_hash` / `password_policy` / `secretName` / `pwd_dir` / `passwordless`);数字尾巴不剥(`password2` 的末段是 `password2`)。
24
+ * 名表里别的 18 个词(`token` / `key` / `refresh_token` / `private_key` …)在自由文本和路径里常是普通词、或本身带分隔符,作末尾时只认整名(上面三条的尾词与尾串除外)—— 裸 `access_key` 照整名表,不借尾串扩;
25
+ * 末段不是尾串的末词不命中(`api_key_count` / `token_count` / `api_key_id`)。
26
+ * 误报面(谁受伤):以强档词收尾、但值不是凭据的名字(`show_password=1` / `is_secret=true`)也会被判成凭据名:受理口拒这样的 URL、脱敏器遮它的值 —— 方向是多拒多遮(少洗的一侧是真钥明文)。
15
27
  * 不透明形 / 解析不了的值没有可读的部件 ⇒ 答「没有」—— 这一形收不收是**座位**自己的律,不在本判官里。
16
28
  *
17
29
  * ## 判官只判「有没有」;座位决定怎么用
@@ -22,12 +34,15 @@
22
34
  * ## 名表的来源
23
35
  * {@link CREDENTIAL_PARAM_NAMES} 是 server 日志脱敏器名表(锚强度表的键)的 23 词。脱敏器另有一列「自由文本里这个词的锚强度」,那一列是脱敏器的事,不在这里;
24
36
  * 词表本身在这里成为契约面 —— 脱敏器与本判官读同一张词表(同一个定义)靠消费方把它的表键钉在本导出上(例:`Record<CredentialParamName, …>` 的穷尽映射 ⇒ 加 / 删词不同步即编译红)。
37
+ * 18.0.0 起本模块同表多一列 {@link COMPOUND_TAIL_STANCE}「这个词作为更长名字的末尾时算不算」:同一个 `Record<CredentialParamName, …>` 穷尽映射,表里加一个词而这一列不表态 ⇒ 编译红。
38
+ * 消费方(脱敏器的三个名字读者、受理口判官)读的是 {@link credentialParamWordOf} 返回的词 —— 拿它去查自己那一列(例:锚强度),不再对整名查表。
25
39
  */
26
40
  import { httpUrlFlaws, httpUrlRule, isHierarchicalUrl } from "./url-shape.js";
27
41
  /** 拒因词(闭集;判序 = 本数组序;词只在这一处写 —— {@link UrlCredentialReason} 与 {@link UrlCredential} 的成员都从它派生)。 */
28
42
  export const URL_CREDENTIAL_REASONS = Object.freeze(["url_userinfo", "url_credential_param"]);
29
43
  /**
30
- * 凭据参数名表(闭集,23 词,全部是小写 ASCII `[a-z][a-z0-9_-]*`)。query / fragment 里名字整名(ASCII 大小写不敏感)在这张表上的参数就是凭据。
44
+ * 凭据参数名表(闭集,23 词,全部是小写 ASCII `[a-z][a-z0-9_-]*`)。query / fragment 里名字整名(ASCII 大小写不敏感)在这张表上的参数就是凭据;
45
+ * 18.0.0 起以其中的强档词收尾的复合名也是(`db_password` 等,判由 {@link credentialParamWordOf})。
31
46
  * 加一个词 = 表里加一格(minor);判官与 {@link isCredentialParamName} 现算,不手抄第二份。
32
47
  */
33
48
  export const CREDENTIAL_PARAM_NAMES = Object.freeze([
@@ -44,17 +59,157 @@ export const CREDENTIAL_PARAM_NAMES = Object.freeze([
44
59
  const CREDENTIAL_PARAM_NAME_SET = new Set(CREDENTIAL_PARAM_NAMES);
45
60
  /** ASCII 大小写折叠(只折 `A-Z`,不做 Unicode 折叠:`TOKEN` 不是 `token`)。 */
46
61
  const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
62
+ /** 名表的**唯一**查口:已折成小写的拼写是名表里的词 ⇒ 那个词,否则 `undefined`(整名臂与尾串答词的解析都经它,模块里不再有第二处读 `Set`)。 */
63
+ function tableWordOf(lowerSpelling) {
64
+ return CREDENTIAL_PARAM_NAME_SET.has(lowerSpelling) ? lowerSpelling : undefined;
65
+ }
66
+ /**
67
+ * 🔴 名表的第二列:每个词作为更长名字的末尾时算不算(见 {@link CompoundTailStance})。键是 `Record<CredentialParamName, …>` 的**穷尽映射** ——
68
+ * 名表加一个词而这一列没给它表态 ⇒ 缺键,编译红;名表删一个词而这一列还留着 ⇒ 多余键,编译红。词表与这一列同住在本模块,消费方只读 {@link credentialParamWordOf} 的答案。
69
+ */
70
+ const COMPOUND_TAIL_STANCE = {
71
+ api_key: "whole-name-only", apikey: "tail-word", "api-key": "whole-name-only", key: "whole-name-only",
72
+ token: "whole-name-only", access_token: "whole-name-only", refresh_token: "whole-name-only", id_token: "whole-name-only",
73
+ auth: "whole-name-only", authorization: "whole-name-only", "proxy-authorization": "whole-name-only",
74
+ password: "tail-word", passwd: "tail-word", pwd: "tail-word",
75
+ secret: "tail-word", client_secret: "whole-name-only", private_key: "whole-name-only",
76
+ signature: "whole-name-only", sig: "whole-name-only",
77
+ "x-amz-signature": "whole-name-only", "x-amz-security-token": "whole-name-only", "x-amz-credential": "whole-name-only",
78
+ sas: "whole-name-only",
79
+ };
80
+ /**
81
+ * 尾串闭集(多词):名表里没有、但名字末尾是这几个词的串就是凭据名。
82
+ *
83
+ * 🔴 **尾串答词只有一条律,不逐项特判**:尾串的词用 `_` 连起来,若**本身就是整名表里的词**,答那个整名词(与整名的答案同形,下游挂在名表上的列〔锚强度〕随之同形);
84
+ * 否则答它的**强段词**(`strong`,名表里态度为 `tail-word` 的词)。单词尾词是同一条律的退化形(一个词连起来就是它自己,它在表里)。解析在加载期做一次({@link COMPOUND_TAILS}):
85
+ * 既不在整名表里、又没写强段词的尾串 ⇒ 抛,不静默成死条目。
86
+ * · `secret key` · `secret access key`:`secret_key` / `secret_access_key` 不在整名表 ⇒ 答 `secret` —— `SECRET_KEY` · `AWS_SECRET_ACCESS_KEY` 的真钥住在这样的名字里,而末段只是 `KEY`(常用词);
87
+ * · `api key`:`api_key` 在整名表 ⇒ 答 `api_key` —— `x-api-key` · `X_API_KEY` · `xApiKey`(名表里 `api_key` 只认整名,带前缀的头名 / 参数名由这一条收);
88
+ * · `access token`:`access_token` 在整名表 ⇒ 答 `access_token` —— `github_access_token` · `oauth-access-token`。
89
+ * 闭集,不做通配:别的名表多段词(`refresh_token` · `private_key` …)与裸 `access_key` 作末尾照今天整名表;末段不是尾串的末词不命中(`api_key_count` · `token_count`)。
90
+ */
91
+ const COMPOUND_TAIL_PHRASES = [
92
+ { words: ["secret", "key"], strong: "secret" },
93
+ { words: ["secret", "access", "key"], strong: "secret" },
94
+ { words: ["api", "key"] },
95
+ { words: ["access", "token"] },
96
+ ];
97
+ /** 这一档是不是「作为末尾算数」(穷尽 `switch`)。 */
98
+ function endsLongerNames(stance) {
99
+ switch (stance) {
100
+ case "tail-word":
101
+ return true;
102
+ case "whole-name-only":
103
+ return false;
104
+ }
105
+ }
106
+ /**
107
+ * 全部尾串 = 名表里态度为 `tail-word` 的词(单词)+ 尾串闭集,答词按 {@link COMPOUND_TAIL_PHRASES} 头注的律解析。词必须是单段 `[a-z0-9]+`
108
+ * (切段切不出分隔符,带分隔符的词永远对不上 ⇒ 加载期抛,不静默成死条目)。
109
+ */
110
+ const COMPOUND_TAILS = (() => {
111
+ const specs = [];
112
+ for (const w of CREDENTIAL_PARAM_NAMES) {
113
+ if (endsLongerNames(COMPOUND_TAIL_STANCE[w]))
114
+ specs.push({ words: [w] });
115
+ }
116
+ specs.push(...COMPOUND_TAIL_PHRASES);
117
+ return specs.map((spec) => {
118
+ for (const w of spec.words) {
119
+ if (!/^[a-z0-9]+$/.test(w))
120
+ throw new Error(`url-credential: compound-name tail word ${JSON.stringify(w)} must be one lowercase [a-z0-9] segment (names are cut at _ - . and camel boundaries)`);
121
+ }
122
+ const word = tableWordOf(spec.words.join("_")) ?? spec.strong;
123
+ if (word === undefined)
124
+ throw new Error(`url-credential: compound-name tail ${JSON.stringify(spec.words.join("_"))} is not a table word and names no strong word`);
125
+ return { words: spec.words, word };
126
+ });
127
+ })();
128
+ /** 切段只需要末尾这几段(最长的尾串有几个词就留几段)。 */
129
+ const TAIL_SEGMENTS_KEPT = Math.max(...COMPOUND_TAILS.map((t) => t.words.length));
130
+ /**
131
+ * 名字的末尾几段(小写,最多 `keep` 段,末段在最后)。切点:`_` `-` `.`(连续的、首尾的都只是分隔,空段不存在)与驼峰边界(小写字母 / 数字 → 大写字母,
132
+ * 大写串的最后一个大写字母若后面跟小写字母,它起新的一段:`HTTPPassword` → `HTTP` `Password`;`PGPASSWORD` 没有边界,是一段)。只折 ASCII 大小写,线性读,不用正则后顾断言(老浏览器加载期就抛)。
133
+ */
134
+ function tailSegmentsOf(name, keep) {
135
+ const segs = [];
136
+ let start = 0;
137
+ const cut = (end) => {
138
+ if (end <= start)
139
+ return;
140
+ segs.push(name.slice(start, end));
141
+ if (segs.length > keep)
142
+ segs.shift();
143
+ };
144
+ const isUpper = (c) => c >= 65 && c <= 90;
145
+ const isLower = (c) => c >= 97 && c <= 122;
146
+ const isDigit = (c) => c >= 48 && c <= 57;
147
+ for (let i = 0; i < name.length; i++) {
148
+ const c = name.charCodeAt(i);
149
+ if (c === 95 || c === 45 || c === 46) {
150
+ cut(i);
151
+ start = i + 1;
152
+ }
153
+ else if (i > start && isUpper(c)) {
154
+ const prev = name.charCodeAt(i - 1);
155
+ if (isLower(prev) || isDigit(prev) || (isUpper(prev) && isLower(name.charCodeAt(i + 1)))) {
156
+ cut(i);
157
+ start = i;
158
+ }
159
+ }
160
+ }
161
+ cut(name.length);
162
+ return segs.map(asciiLower);
163
+ }
164
+ /**
165
+ * 这几段的末尾是不是这条尾串:从右往左逐词吃 —— 一段末尾能吃几个词就吃几个;吃完一段还剩词,这一段必须刚好吃光(切点落在词界上:词不跨分隔符,`app_wd` 不是 `pwd`);
166
+ * 词吃完时,最左那一段允许还剩前缀(`PGPASSWORD` 的 `PG`)。
167
+ */
168
+ function endsWithTail(words, segs) {
169
+ let w = words.length;
170
+ let s = segs.length;
171
+ while (w > 0) {
172
+ if (s === 0)
173
+ return false;
174
+ let rest = segs[--s];
175
+ while (w > 0 && rest.endsWith(words[w - 1])) {
176
+ rest = rest.slice(0, rest.length - words[w - 1].length);
177
+ w--;
178
+ }
179
+ if (w > 0 && rest !== "")
180
+ return false;
181
+ }
182
+ return true;
183
+ }
184
+ /**
185
+ * 🔴 **「这个已解码的参数名是不是凭据名」的唯一答者**(18.0.0):是 ⇒ 名表里的那个词(小写);不是 ⇒ `undefined`。入参是收方读到的那一形(`URLSearchParams` 解过一次码的名字)。
186
+ * · 整名(ASCII 大小写不敏感)在 {@link CREDENTIAL_PARAM_NAMES} 里 ⇒ 那个词(整名优先);
187
+ * · 否则末段的尾部是强档词(`password` / `passwd` / `pwd` / `secret` / `apikey`),或名字末尾是尾串闭集的一条(`secret_key` / `secret_access_key` ⇒ `secret`;`api_key` ⇒ `api_key`;`access_token` ⇒ `access_token`;答词的律见 {@link COMPOUND_TAIL_PHRASES})⇒ 那个词。
188
+ * 切段、粘连与对齐的律见模块头「名字判定」。
189
+ * 返回的词是**表词**:消费方拿它去查自己挂在名表上的那一列(例:日志脱敏器的锚强度),不对整名再查一遍表。
190
+ */
191
+ export function credentialParamWordOf(decodedName) {
192
+ const whole = tableWordOf(asciiLower(decodedName));
193
+ if (whole !== undefined)
194
+ return whole;
195
+ const segs = tailSegmentsOf(decodedName, TAIL_SEGMENTS_KEPT);
196
+ for (const tail of COMPOUND_TAILS)
197
+ if (endsWithTail(tail.words, segs))
198
+ return tail.word;
199
+ return undefined;
200
+ }
47
201
  /**
48
- * 「这个**已解码**的参数名是不是凭据名」。入参是收方读到的那一形(`URLSearchParams` 解过一次码的名字):整名在 {@link CREDENTIAL_PARAM_NAMES} 里(ASCII 大小写不敏感)才算。
202
+ * 「这个**已解码**的参数名是不是凭据名」:{@link credentialParamWordOf} 有词。入参是收方读到的那一形(`URLSearchParams` 解过一次码的名字);
203
+ * 整名在 {@link CREDENTIAL_PARAM_NAMES} 里、或末段 / 尾串收在强档词上(`db_password` / `PGPASSWORD` / `SECRET_KEY`)才算。
49
204
  */
50
205
  export function isCredentialParamName(decodedName) {
51
- return CREDENTIAL_PARAM_NAME_SET.has(asciiLower(decodedName));
206
+ return credentialParamWordOf(decodedName) !== undefined;
52
207
  }
53
208
  /** 一段参数串(`?` / `#` 之后)里第一个凭据名参数的表词(小写);没有 ⇒ `undefined`。 */
54
209
  function credentialParamIn(params) {
55
210
  for (const name of new URLSearchParams(params).keys()) {
56
- const word = asciiLower(name);
57
- if (CREDENTIAL_PARAM_NAME_SET.has(word))
211
+ const word = credentialParamWordOf(name);
212
+ if (word !== undefined)
58
213
  return word;
59
214
  }
60
215
  return undefined;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 包内私有叶子模块(运行期零依赖,浏览器安全;不进 `package.json` 的 exports,主桶经 `./mcp-request-entry` 转出这里的两个公开名):
3
+ * **申报名能不能当线上名**的唯一判据 —— 一台 MCP server 或一个 A2A 对端的申报名会成为线上工具名的命名空间段(`mcp__<名>__…` / `a2a__<名>__…`)。
4
+ * 住在叶子里,是因为 `./types` 的域 schema(`a2a` 对端名)与请求面的读口(`./mcp-request-entry`)都要它,而后者反过来引 `./types` 的词表 ——
5
+ * 判据只有这一份函数体,两边 import 同一只。
6
+ */
7
+ /** 引擎 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像:一台 MCP server(或 A2A 对端)的申报名会成为线上工具名的命名空间段,引擎对超界名是**拒**(不截断)。 */
8
+ export declare const TOOL_WIRE_NAME_MAX_CHARS = 128;
9
+ /**
10
+ * 首尾带空白吗 —— 「空白」就是 `String.prototype.trim` 剥的那一组(ECMAScript 的 WhiteSpace + LineTerminator:空格 / 制表 / 换行 / NBSP / BOM / 全角空格 / 行段分隔符等)。
11
+ * {@link isUsableWireName} 与请求面的坏名摘要(`describeBadWireName`)共读这一个定义,判与说不会各按各的口径。
12
+ */
13
+ export declare const hasEdgeWhitespace: (v: string) => boolean;
14
+ /**
15
+ * 这个申报名能用吗。严判:非串、空串、超 {@link TOOL_WIRE_NAME_MAX_CHARS}、**首尾带空白**一律不可用。不截断、不 trim、不规范化 —— 截短或剥掉一个名字就是**换一个身份**,
16
+ * 而被换掉身份的那台 server 上挂的 allowTools / toolFaces 收窄声明会一起对不上号。首尾空白 = 坏名(它不是另一个名字:名字是 `mcp__<server>__<tool>` 的身份片,
17
+ * 带着空白的一端无从与不带的那个对上号);内部空白(`a b`)照旧是合法名,本判官不扩判。
18
+ */
19
+ export declare function isUsableWireName(v: unknown): v is string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 包内私有叶子模块(运行期零依赖,浏览器安全;不进 `package.json` 的 exports,主桶经 `./mcp-request-entry` 转出这里的两个公开名):
3
+ * **申报名能不能当线上名**的唯一判据 —— 一台 MCP server 或一个 A2A 对端的申报名会成为线上工具名的命名空间段(`mcp__<名>__…` / `a2a__<名>__…`)。
4
+ * 住在叶子里,是因为 `./types` 的域 schema(`a2a` 对端名)与请求面的读口(`./mcp-request-entry`)都要它,而后者反过来引 `./types` 的词表 ——
5
+ * 判据只有这一份函数体,两边 import 同一只。
6
+ */
7
+ /** 引擎 `TOOL_WIRE_NAME_MAX_CHARS` 的镜像:一台 MCP server(或 A2A 对端)的申报名会成为线上工具名的命名空间段,引擎对超界名是**拒**(不截断)。 */
8
+ export const TOOL_WIRE_NAME_MAX_CHARS = 128;
9
+ /**
10
+ * 首尾带空白吗 —— 「空白」就是 `String.prototype.trim` 剥的那一组(ECMAScript 的 WhiteSpace + LineTerminator:空格 / 制表 / 换行 / NBSP / BOM / 全角空格 / 行段分隔符等)。
11
+ * {@link isUsableWireName} 与请求面的坏名摘要(`describeBadWireName`)共读这一个定义,判与说不会各按各的口径。
12
+ */
13
+ export const hasEdgeWhitespace = (v) => v.trim() !== v;
14
+ /**
15
+ * 这个申报名能用吗。严判:非串、空串、超 {@link TOOL_WIRE_NAME_MAX_CHARS}、**首尾带空白**一律不可用。不截断、不 trim、不规范化 —— 截短或剥掉一个名字就是**换一个身份**,
16
+ * 而被换掉身份的那台 server 上挂的 allowTools / toolFaces 收窄声明会一起对不上号。首尾空白 = 坏名(它不是另一个名字:名字是 `mcp__<server>__<tool>` 的身份片,
17
+ * 带着空白的一端无从与不带的那个对上号);内部空白(`a b`)照旧是合法名,本判官不扩判。
18
+ */
19
+ export function isUsableWireName(v) {
20
+ return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS && !hasEdgeWhitespace(v);
21
+ }
@@ -4,13 +4,16 @@
4
4
  > 每次全量重算并逐名比对 —— 加 / 删 / 改形一个导出而不重生成本档 ⇒ 红。导出面的每次变动因此以本档的 diff 出现在评审里。
5
5
  > 形:`value` = 运行期拿得到的值;`type` = 只在类型层;`value+type` = 同名的值与类型(zod schema 与它推出的类型)。
6
6
 
7
- 子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1480 个(同名在多条子路径上各计一次)。
7
+ 子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1501 个(同名在多条子路径上各计一次)。
8
8
 
9
- ## `.`(773)
9
+ ## `.`(785)
10
10
 
11
+ - `A2aConfig` — value+type
12
+ - `A2aPeerSpec` — value+type
11
13
  - `ABSOLUTE_HOST_PATH_MAX` — value
12
14
  - `ACCESS_TOKEN_TTL_SECONDS` — value
13
15
  - `ANY_INDEX` — value
16
+ - `ATOMIC_SALVAGE` — value
14
17
  - `AT_MENTION_MODES` — value
15
18
  - `AUTH_BRIDGE_ALGS` — value
16
19
  - `AUTH_BRIDGE_ERROR_CODES` — value
@@ -212,6 +215,7 @@
212
215
  - `HOOK_EVENT_MATCHER_FIELD` — value
213
216
  - `HOOK_EVENT_OWNER` — value
214
217
  - `HOOK_INJECTED_ENV` — value
218
+ - `HOOK_ON_FAILURE_WORDS` — value
215
219
  - `HOSTS_ARRAY_MAX` — value
216
220
  - `HOST_NAME_MAX` — value
217
221
  - `HTTP_HOOK_ALLOWLIST_KEYS` — value
@@ -227,6 +231,7 @@
227
231
  - `HookInput` — value+type
228
232
  - `HookMatcherEntry` — value+type
229
233
  - `HookMatcherForm` — type
234
+ - `HookOnFailure` — type
230
235
  - `HookOutput` — value+type
231
236
  - `HookOwner` — value+type
232
237
  - `HookPermissionRequestDecision` — value+type
@@ -261,9 +266,11 @@
261
266
  - `InstructionsLoadedHookInput` — value
262
267
  - `JwksCacheSnapshot` — type
263
268
  - `K8S_QUANTITY` — value
269
+ - `LANE_SET_RULE` — value
264
270
  - `LAYER_ADMISSION_VERDICTS` — value
265
271
  - `LAYER_PARTS_UNREAD_CODE` — value
266
272
  - `LEASE_STATES` — value
273
+ - `LaneSetFlaw` — type
267
274
  - `LaneTenancy` — type
268
275
  - `LayerAdmissionVerdict` — type
269
276
  - `LayerPartsUnreadNotice` — type
@@ -324,7 +331,6 @@
324
331
  - `PARSE_TRANSPARENT_KEYS` — value
325
332
  - `PATH_BASE_URL_FORBIDDEN_PARTS` — value
326
333
  - `PLAIN_STRING_RECORD_FAULTS` — value
327
- - `PLAIN_STRING_RECORD_RESERVED_MEMBER` — value
328
334
  - `PLUGIN_ORIGIN` — value
329
335
  - `PRINCIPAL_INC_CLAIM` — value
330
336
  - `PRINCIPAL_MAX_LENGTH` — value
@@ -393,6 +399,7 @@
393
399
  - `REF_REFERRER_DOMAINS` — value
394
400
  - `REMOTE_EXEC_DOCUMENT` — value
395
401
  - `REMOTE_MCP_SERVER_NAME_PREFIX` — value
402
+ - `RESERVED_MEMBER_NAMES` — value
396
403
  - `RESERVED_SCOPE_IDS` — value
397
404
  - `RESERVED_WORKER_ENV` — value
398
405
  - `RETIRED_DOMAINS` — value
@@ -413,6 +420,7 @@
413
420
  - `RemoteExecSpec` — value+type
414
421
  - `RequestMcpTransport` — type
415
422
  - `RequestMcpTransportRead` — type
423
+ - `ReservedMemberName` — type
416
424
  - `ResolvedEffective` — type
417
425
  - `ResolvedMemberAdmission` — type
418
426
  - `ResolvedTokenScope` — type
@@ -525,6 +533,7 @@
525
533
  - `SkillsManifest` — type
526
534
  - `SkillsManifestSchema` — value
527
535
  - `SpecEnforcement` — value+type
536
+ - `StandaloneLane` — type
528
537
  - `StopFailureHookInput` — value
529
538
  - `StopHookInput` — value
530
539
  - `StoppedBy` — value+type
@@ -621,6 +630,7 @@
621
630
  - `compareGeneration` — value
622
631
  - `containsSecretToken` — value
623
632
  - `createSiblingResolver` — value
633
+ - `credentialParamWordOf` — value
624
634
  - `danglingRefs` — value
625
635
  - `describeBadContentOrigin` — value
626
636
  - `describeBadMcpServerName` — value
@@ -669,6 +679,7 @@
669
679
  - `isRefCheckedDomain` — value
670
680
  - `isReportableRefusalName` — value
671
681
  - `isReservedExtensionKey` — value
682
+ - `isReservedMemberName` — value
672
683
  - `isReservedScopeId` — value
673
684
  - `isRetiredDomainName` — value
674
685
  - `isRole` — value
@@ -683,6 +694,7 @@
683
694
  - `judgeRoutability` — value
684
695
  - `judgeWebAdminCredentialDeclaration` — value
685
696
  - `jwksWithinOfflineGrace` — value
697
+ - `laneSetFlaws` — value
686
698
  - `layerAdmission` — value
687
699
  - `layerReadsTightenOnly` — value
688
700
  - `leaseExhausted` — value
@@ -782,9 +794,12 @@
782
794
  - `workersReferencingSecret` — value
783
795
  - `wsUrlFlaws` — value
784
796
 
785
- ## `./types`(147)
797
+ ## `./types`(150)
786
798
 
799
+ - `A2aConfig` — value+type
800
+ - `A2aPeerSpec` — value+type
787
801
  - `ABSOLUTE_HOST_PATH_MAX` — value
802
+ - `ATOMIC_SALVAGE` — value
788
803
  - `AT_MENTION_MODES` — value
789
804
  - `AUTONOMY_MODES` — value
790
805
  - `AbsoluteHostPath` — value
@@ -961,12 +976,14 @@
961
976
  - `collectEnvRefs` — value
962
977
  - `workersReferencingSecret` — value
963
978
 
964
- ## `./execution-lanes`(19)
979
+ ## `./execution-lanes`(23)
965
980
 
966
981
  - `EXECUTION_LANES` — value
967
982
  - `EXECUTION_LANE_WORDS` — value
968
983
  - `ExecutionLane` — type
969
984
  - `ExecutionLaneRow` — type
985
+ - `LANE_SET_RULE` — value
986
+ - `LaneSetFlaw` — type
970
987
  - `LaneTenancy` — type
971
988
  - `MEMORY_ENGINE_BACKENDS` — value
972
989
  - `MemoryEngineBackend` — type
@@ -975,12 +992,14 @@
975
992
  - `SESSION_BACKENDS` — value
976
993
  - `STORE_BACKENDS` — value
977
994
  - `SessionBackend` — type
995
+ - `StandaloneLane` — type
978
996
  - `StoreBackend` — type
979
997
  - `TENANT_SEPARATION_BASES` — value
980
998
  - `TENANT_SEPARATION_WORDS` — value
981
999
  - `TenantSeparation` — type
982
1000
  - `TenantSeparationBasis` — type
983
1001
  - `isBlankCoordinate` — value
1002
+ - `laneSetFlaws` — value
984
1003
  - `retiredLaneWordRefusal` — value
985
1004
 
986
1005
  ## `./resolve-roster`(6)
@@ -1270,7 +1289,7 @@
1270
1289
  - `parseSchedulerStore` — value
1271
1290
  - `serializeSchedulerStore` — value
1272
1291
 
1273
- ## `./hooks`(94)
1292
+ ## `./hooks`(96)
1274
1293
 
1275
1294
  - `AgentHookEntry` — value+type
1276
1295
  - `AssistantTurnError` — value+type
@@ -1297,6 +1316,7 @@
1297
1316
  - `HOOK_EVENT_MATCHER_FIELD` — value
1298
1317
  - `HOOK_EVENT_OWNER` — value
1299
1318
  - `HOOK_INJECTED_ENV` — value
1319
+ - `HOOK_ON_FAILURE_WORDS` — value
1300
1320
  - `HookDecision` — value+type
1301
1321
  - `HookEntry` — value+type
1302
1322
  - `HookEntryType` — type
@@ -1306,6 +1326,7 @@
1306
1326
  - `HookInput` — value+type
1307
1327
  - `HookMatcherEntry` — value+type
1308
1328
  - `HookMatcherForm` — type
1329
+ - `HookOnFailure` — type
1309
1330
  - `HookOutput` — value+type
1310
1331
  - `HookOwner` — value+type
1311
1332
  - `HookPermissionRequestDecision` — value+type
@@ -1,7 +1,7 @@
1
1
  # CC 偏离登记(本包)
2
2
 
3
3
  本包镜像或对齐 Claude Code(CC)上游的设置语义时,**有意**不照抄的地方逐条登在这里:差在哪(CC 原形与语料坐标 / 本包的形)、哪个方向、为什么、谁读它。
4
- 锚 = CC 2.1.296 语料 `cli296.js`(只 grep,不解包;坐标写行号,行号属于那一份语料,只引不抄)。
4
+ 锚 = CC 2.1.296 语料 `cli296.js`(只 grep,不解包;坐标写行号,行号属于那一份语料,只引不抄);H7 的两个新进字段另引 `cli309.js` 的坐标。
5
5
 
6
6
  方向只有两种写法:**更严**(本包拦下 / 不放行 / 不装 CC 会放行或会装的)与**更宽**(本包拦下 CC 不拦的 —— 只出现在拒绝方向)。
7
7
  本包不登「比 CC 更松」的偏离;出现了即是缺陷。
@@ -18,7 +18,7 @@
18
18
  | H4 | 已知非守卫事件的值写成一只匹配器(不是数组)⇒ 整份拒 | 剔掉那个键(警告) | 匹配器形本身就是迹象(规则一条,不因事件名分档) |
19
19
  | H5 | 整份文档本身写成一只匹配器(顶层带 `matcher` 键 + 匹配器形)⇒ 整份拒 | 无此判 | 把一只守卫匹配器误贴成整份设置时,CC 剔掉坏的 `hooks` 后照装 |
20
20
  | H6 | `hooks` 是装着**数组**的数组 ⇒ 整份拒(与装对象同判) | 只对装对象 / 装迹象的数组整份拒,装数组的剔掉 | 套一层数组的守卫不该被当成标量剔掉 |
21
- | H7 | 条目字段以本包字段表为准(`HOOK_ENTRY_FIELDS`);CC 独有字段(`args` / `onFailure` / `cloud` / `continueOnBlock` / `rewakeMessage` …)不进装上的那一份,也不算坏块 | 认得这些字段 | 契约只收本包定义的字段;字段级的上游漂移今天没有门(type 集有,见漂移门 ①b) —— 未闭环 |
21
+ | H7 | 条目字段以本包字段表为准(`HOOK_ENTRY_FIELDS`):command 收 `args`(exec 形参数表;元素另判零 NUL —— `spawn()` 对含 NUL 的参数抛,CC 的串数组不判)、command / http 收 `onFailure`(词表 `HOOK_ON_FAILURE_WORDS` = `continue` \| `block`;缺席不补缺省);其余 CC 独有字段(`cloud` / `continueOnBlock` / `rewakeMessage` / `rewakeSummary` …,CC 标 `@internal` 的键与 prompt 条目的 `continueOnBlock`)不进装上的那一份,也不算坏块 —— `args` 写在 command 以外、`onFailure` 写在 command / http 以外的条目上同属这一类(键下装着守卫迹象的除外:H3,整份拒) | 认得这些字段(`cli309.js:252382`:`args` 只在 command 的 schema 上,`onFailure` 在 command 与 http 的 schema 上) | 契约只收本包定义的字段;`args` / `onFailure` 19.0.0 起进表(此前被剥掉:exec 形条目只剩可执行名去跑,`block` 的守卫起不来照放行);字段级的上游漂移今天没有门(type 集有,见漂移门 ①b) —— 未闭环 |
22
22
 
23
23
  不是偏差的(与 CC 同):守卫事件集 {`PreToolUse`, `PermissionRequest`};守卫事件值 `null` = 剔不拒;非守卫事件里坏匹配器 / 坏条目逐个剔;hook 条目 type 五种。
24
24
 
@@ -11,6 +11,8 @@
11
11
  > **14.7.0** 的路径基址判官(`PATH_BASE_URL_FORBIDDEN_PARTS` / `pathBaseUrlFlaws`)与公告地址 env 名(`FLEET_ADVERTISE_ADDRESS_ENV`)在 §13。
12
12
  > 中心引用解析口 `resolveCenterRefs` 与记录读器 `readPlainStringRecord`(原因词表 `PLAIN_STRING_RECORD_FAULTS`)在 §14。
13
13
  > **17.2.0** 的 settings 单元读不进处置律在 §15,`./node` 的同步读写口(`config.d` 文档读数、决定文档读 / 写)与根桶的「显式停用」谓词在 §16。
14
+ > **18.0.0** 的车道词 `none`、`REQUIRE_EXECUTION_LANE` 墓碑、车道集单一判官 `laneSetFlaws` 与便携新域 `a2a` 在 §17。
15
+ > **19.0.0** 的 hook 条目字段 `args`(exec 形参数表)与 `onFailure`(失败按放行还是阻断)在 §18。
14
16
 
15
17
  ---
16
18
 
@@ -54,16 +56,16 @@
54
56
  7.0.1 起未写盘时它**不再出现**(读到 undefined,而不是一份没落盘的「已迁」名单);要判「写没写」读 `written` 或结论行,不要靠退出码之外的猜测。
55
57
  - **拒因里的迁移指路(7.0.1)**:一句话一个常量(`MIGRATION_SIGNPOST`,命令恒为 `npx @sema-agent/settings-schema migrate-workers <file>`)。
56
58
  凡「旧形 ⇒ 迁移能搬」的拒因带它 —— 七个被删旧键,以及 `env` / `secretEnv` 里迁移会搬 / 丢的键(车道键含 `REMOTE_EXEC` /
57
- `REQUIRE_EXECUTION_LANE`、店键 `DB_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND`);迁移搬不了的保留名(`PORT` / `MODEL_*` /
59
+ `REQUIRE_EXECUTION_LANE`〔18.0.0 起它进墓碑表,拒句是墓碑句、不带迁移指路,见 §17〕、店键 `DB_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND`);迁移搬不了的保留名(`PORT` / `MODEL_*` /
58
60
  `MYSQL_*` …)不带。拒因句尾的「set it via <归属字段>」不变,读它抽归属字段的下游不受影响。
59
61
  - **三方各自要做的**:
60
- - **web-admin / 编排器(sema-web-admin)**:渲染器按两块改真 —— `REMOTE_EXEC=<lane>` + `REQUIRE_EXECUTION_LANE=true` + `coords` 逐键
62
+ - **web-admin / 编排器(sema-web-admin)**:渲染器按两块改真 —— `REMOTE_EXEC=<lane>` + `REQUIRE_EXECUTION_LANE=true`〔18.0.0 起不再渲:它进了墓碑表,见 §17〕+ `coords` 逐键
61
63
  + `secretRefs` 逐键(服务变量名 → 宿主 env-NAME);`DB_BACKEND` / `SESSION_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND` 恒渲;
62
64
  按 `store.backend` 选 `MYSQL_*` 或 `PG_*` 名族(`HOST/PORT/USER/DATABASE` ← `store.db`,`*_PASSWORD` ← `secretRefs.dbPasswordEnv`);
63
65
  `MEMORY_BACKEND` 不再产出。workers 读口接迁移;批量轮换已在本包 `workersReferencingSecret` 里看得见 `runtime.execution.secretRefs`。
64
66
  `WorkerSpec` 不再是 `ZodObject`:读 `WorkerSpec.shape.name` 的地方改 `WORKER_NAME`(同一只名字判据);编排器读 `runtime.db` 的
65
67
  改 `runtime.store.db.database`;新建向导要建出两块(`runtime: {}` 过不了写闸)。执行域 `allowedLanes`(开放词)的标签表:可声明车道取
66
- `EXECUTION_LANE_WORDS`;`in-process` 是引擎「没有 remoteExec」的状态名,不在可声明表里,要留就单独写明来源。
68
+ `EXECUTION_LANE_WORDS`;`in-process` 是引擎「没有 remoteExec」的状态名,不在可声明表里,要留就单独写明来源〔18.0.0 起可声明的词是 `none`,`in-process` 只在切换期的策略数据里并列,见 §17〕。
67
69
  - **server(引擎)**:车道词 / 必需键 / `tenantSeparation` 从 `EXECUTION_LANES` 派生(运行期事实留在按导入联合穷举的 switch);
68
70
  `DB_BACKEND` / `SESSION_BACKEND` / `MEMORY_ENGINE_BACKEND` 的闭集读 `STORE_BACKENDS` / `SESSION_BACKENDS` / `MEMORY_ENGINE_BACKENDS`。
69
71
  引擎不 import `WorkerSpec`,tsc 不会替它红 —— 派生是提货车的显式动作。
@@ -479,12 +481,13 @@ fleet 成员对中心公告的可达地址(`FLEET_ADVERTISE_ADDRESS`,中心往
479
481
  | `CENTER_REFS_VERDICTS` / `CenterRefsVerdict` | 整台结论的词,闭集:`resolved` · `unresolved` · `refs_unreadable`;包根 |
480
482
  | `CenterRefMember` / `CenterRefMemberVerdict` / `CenterRefLookup` / `CenterRefsResolution` | 型:成员行 `{ key, ref, verdict }`(`verdict` = `resolved` 或上面三词之一)· 判定词 · 查找函数型 · 读数;包根 |
481
483
  | `readPlainStringRecord(v)` | 「名 → 串」记录的读器(传输读器读 `env` / `headers` 用的就是它,同一个函数);包根 |
482
- | `PLAIN_STRING_RECORD_FAULTS` / `PlainStringRecordFault` / `PlainStringRecordRead` / `PLAIN_STRING_RECORD_RESERVED_MEMBER` | 读器的原因词表(三词)· 词的型 · 读数型 · 保留成员名 `"__proto__"`;包根 |
484
+ | `PLAIN_STRING_RECORD_FAULTS` / `PlainStringRecordFault` / `PlainStringRecordRead` | 读器的原因词表(三词)· 词的型 · 读数型;包根 |
485
+ | `RESERVED_MEMBER_NAMES` / `isReservedMemberName` / `ReservedMemberName` | 保留成员名(18.0.0 起一张表:`__proto__` / `constructor` / `prototype`)· 谓词 · 型 —— 读器、解析口与域 schema 的凭据引用集合问同一个谓词;包根与 `./mcp-request-entry` |
483
486
 
484
487
  ### 14.2 解析口的读法(一条律:每成员一句判定,整台一个结论)
485
488
 
486
- 1. 引用表经 `readPlainStringRecord` 读。读不出(整张不是纯数据、有成员的变量名不是串、读成员就抛、叫 `__proto__` 的成员)⇒ **整台 `refs_unreadable`**,`fault` 是读器的词、坏在某个成员时 `member` 点名它的回显形;**一个变量都没问**。
487
- 读器会把叫 `__proto__` 的串值成员剔出副本;解析口不许这样悄悄丢一条引用再挂,所以同样答 `refs_unreadable` / `member_name_reserved`。
489
+ 1. 引用表经 `readPlainStringRecord` 读。读不出(整张不是纯数据、有成员的变量名不是串、读成员就抛、叫保留名 `__proto__` / `constructor` / `prototype` 的成员)⇒ **整台 `refs_unreadable`**,`fault` 是读器的词、坏在某个成员时 `member` 点名它的回显形;**一个变量都没问**。
490
+ 读器会把叫保留名的串值成员剔出副本;解析口不许这样悄悄丢一条引用再挂,所以同样答 `refs_unreadable` / `member_name_reserved`(点名第一个)。
488
491
  2. 每个成员恰问一次 `lookup(变量名)`(按引用表副本的自有键序:整数形的键在前、其余按记录列出的次序;实参只有变量名),判成**一个词**。一个成员坏了不早停 —— 每个成员都有自己的判定,调用方一次点名所有缺的变量:
489
492
 
490
493
  | `lookup` 的答案 | 成员词 |
@@ -507,7 +510,7 @@ fleet 成员对中心公告的可达地址(`FLEET_ADVERTISE_ADDRESS`,中心往
507
510
  |---|---|---|
508
511
  | `not_plain_record` | 整张不是纯数据(`null` / 数组 / 原始值 / `Map` / 类实例 / 函数,或读形状就抛的对象) | 不交 |
509
512
  | `member_not_string` | 某个成员的值不是串(读成了别的种类),或它读不出(它的访问器抛了,或读它的描述符时代理陷阱抛了)—— 两种情形 `detail` 句子分说 | 不交,`member` 点名它 |
510
- | `member_name_reserved` | 某个成员叫 `__proto__`(引擎的声明收据拒这个名);名坏优先 —— 值也坏或读不出时句子并列说全,词仍是这一个 | 值是串:交其余、`reservedMember: true`;否则不交 |
513
+ | `member_name_reserved` | 某个成员叫保留名(`RESERVED_MEMBER_NAMES`:`__proto__` / `constructor` / `prototype`);名坏优先 —— 值也坏或读不出时句子并列说全,词仍是这一个 | 值是串:交其余、`reservedMembers` 按自有键序点名;否则不交 |
511
514
 
512
515
  按词分派;读器从不抛。传输读器的 `lost[].fault` 就是这张表(读成员就抛的那一形,词是 `member_not_string`,句子 `could not be read (its getter threw)`)。
513
516
 
@@ -603,3 +606,65 @@ fleet 成员对中心公告的可达地址(`FLEET_ADVERTISE_ADDRESS`,中心往
603
606
 
604
607
  - 不出裸纯判 `judgeConfigDTextReading` 与异步的 `readConfigDFileReading`(壳自读文本 = 第四只缺席读法;前者依赖 `node:path` 与原子写器,不是浏览器安全叶)。
605
608
  - 不替壳决定「读不进」之后挂不挂:读数是判决,不是处置(settings 单元的处置律见 §15)。
609
+
610
+ ---
611
+
612
+ ## 17. 车道词 `none` · `REQUIRE_EXECUTION_LANE` 墓碑 · 车道集单一判官 · 便携新域 `a2a`(18.0.0 起)—— server、web-admin、cli、client-core、sdk 各自的读法
613
+
614
+ ### 17.1 `none`:不给智能体的工具提供执行环境
615
+
616
+ - 词:`EXECUTION_LANE_WORDS` 的第八个词(尾插)。表行:分租户(依据 `no-execution-target`:没有执行目标,就没有跨租户共享的目标)、不挂副本、四栏 env 全空。
617
+ - 只能独成一集:worker 写 `runtime.execution.lanes: ["none"]`,部署写 `REMOTE_EXEC=none`(`CONFIG_PROVIDER=local` 时 `config.d/remote-exec.json` 也可写 `{ "provider": "none" }`,零字段)。
618
+ 这条规则按表行的依据列判,不点名车道词:依据列为 `no-execution-target` 的车道(型 `StandaloneLane`)不与别的项同集。
619
+ - `in-process` 仍**不是**可声明的车道词(它是引擎旧的内部状态名)。执行域策略 `allowedLanes` 是开放词表:切换期数据并列 `"in-process"` 与 `"none"`,所有读它的引擎都答 `none` 之后从数据里删掉 `"in-process"` —— 这是数据,不要在代码里写第二种读法。
620
+
621
+ ### 17.2 车道集的合法性只问一只判官
622
+
623
+ `laneSetFlaws(lanes: readonly string[])`(根桶与 `./execution-lanes`,零依赖叶)→ 拒因闭集 `LaneSetFlaw`:`empty_set` · `blank_entry{at}` · `unknown{at, word}` · `retired{at, word}` · `duplicate{at, lane}` · `not_alone{lane}`;规则句 `LANE_SET_RULE`。
624
+ 写闸(`WorkerSpec` 的 `runtime.execution.lanes`)只调它;读车道表的一侧(引擎把 `REMOTE_EXEC` 按逗号切段、表单校验)调同一只,**不抄**非空 / 去重 / 认词 / 独占任何一条。
625
+ 入参是任意值的表:不是字符串的项也是 `unknown`(`word` 是原值,判官不读它的属性;给人看时自己描述它的形,不回显);判官交回**全部**拒因(坏词与重复同在 ⇒ 两条)。一条车道缺不缺必需坐标、本引擎版本服务不服务这条车道,是调用方各自的门,不在判官里。
626
+
627
+ ### 17.3 `REQUIRE_EXECUTION_LANE` 进墓碑表
628
+
629
+ `RETIRED_ENVS` 一行 `replaced` → `REMOTE_EXEC`:旧名在场即拒(替代名在不在都一样),空串按未设。它不再是保留名(`RESERVED_WORKER_ENV`):worker 逃生口里写它 ⇒ 墓碑句(`retiredEnvRefusal`)。
630
+ 🔴 **硬顺序**:web-admin 渲染器先停渲这个键,中心起的 worker 才能上 server 7.147.0 —— 否则读墓碑表的引擎见之拒启。`none` 只对已经升级的部署产出。
631
+
632
+ ### 17.4 便携新域 `a2a`:常驻出站 A2A 对端名册
633
+
634
+ - 形:`A2aConfig = { peers: A2aPeerSpec[] }`(缺席 ≡ 空表 = 不拨任何对端;对端名唯一);`A2aPeerSpec = { name, url, cardUrl?, headerRefs?, principalHeader?, allowSkills?, scenarios(缺省 []), enabled(缺省 true) }`。
635
+ 判官全复用:`name` = `isUsableWireName`(对端名是线上工具名 `a2a__<名>__…` 的身份片,不 trim、不截断)· `url` / `cardUrl` = `NON_SECRET_URL`(不带凭据与 fragment)· `headerRefs` = 头名 → env-NAME(值在读方自己的环境里,中心不存值)· `principalHeader` = 头名判官。
636
+ - 读法:本地 `config.d/a2a.json` 与中心 `/effective.a2a` 是同一个声明座(两腿恒物化 `{ peers: [] }`);整域读不进 ⇒ 空名册;坏对端逐条丢;写权限 `domainWriteRole("a2a")` = `publisher`。
637
+ - **凭据引用集合不部分抢救**(`ATOMIC_SALVAGE`,按 schema 身份:mcp 的 `envRefs` / `headerRefs`、a2a 的 `headerRefs`、车道块的 `secretRefs`、worker 的 `secretEnv`):集合里坏一项 ⇒ 整个条目 `entry-dropped`,兄弟保留;别的集合字段照旧逐条丢。
638
+ 对已记录语料零变化;两处例外:引用名为 `__proto__` / `constructor` / `prototype` 的条目整条丢(旧版:`__proto__` 被剥并告警、`constructor` / `prototype` 照收,条目都留着);
639
+ 直接对 `WorkersConfig` 调 `salvageEntries` 时,`secretEnv` 带坏引用的 worker 整条丢(此前只丢那一条引用)。保留名只有一张表(`RESERVED_MEMBER_NAMES`),记录读器与解析口(§14)同答。
640
+ 成员读键按自有属性名判(名集 = `Object.getOwnPropertyNames` 的串键;访问器 / 隐藏键 ⇒ 读不出 ⇒ 同样判坏):`__proto__` / `constructor` / `prototype` 作引用名 ⇒ 集合坏 ⇒ 整条丢(zod 的 record 读键前跳过 `__proto__`、不判它的值,只靠它判会让条目带着其余引用活下来);读方自己按名查引用表时同样不认这三个名字。
641
+ - 密钥引用清单 `collectEnvRefs` / 本地面板 `listRequiredEnvNames` 列出对端的头引用(域 `a2a`)—— 读方「对端要的 env 在不在」用同一份清单。
642
+
643
+ ### 17.5 各消费方的读点
644
+
645
+ | 消费方 | 读点 | 动作 |
646
+ |---|---|---|
647
+ | server 7.147.0 | `EXECUTION_LANE_WORDS` / `EXECUTION_LANES` · `RETIRED_ENVS`(起服)· `RemoteExecSpec` · `laneSetFlaws` · `A2aConfig` / `EffectiveConfig.a2a` / `PORTABLE_DOMAINS` | 提货同版:`none` 车道、总开关见之拒启、文件声明的 `none`、读声明车道表调判官、`a2a` 改读契约域(本地与中心同座) |
648
+ | web-admin | `RETIRED_ENVS` · `runtime.execution.lanes` · 策略 `allowedLanes` 数据 · `A2aPeerSpec` · `domainWriteRole("a2a")` | 先停渲 `REQUIRE_EXECUTION_LANE`(硬顺序);`["none"]` ⇒ `REMOTE_EXEC=none`;策略数据切换期并列两词;`a2a` 编辑面与 publisher 写权限 |
649
+ | cli | `EXECUTION_LANES`(车道 → 必需键)· 车道词闭集 · `RETIRED_ENVS` | 随表:`none` 行无必需键、不再是表外词;起本机引擎前墓碑表拒 `REQUIRE_EXECUTION_LANE` |
650
+ | client-core | `ExecutionLane` · `EffectiveConfig` | 穷举处补 `none`;`EffectiveConfig.a2a` 必填;本地读腿多读 `config.d/a2a.json` |
651
+ | sdk | `ExecutionLane` · `EffectiveConfig`(类型面) | 穷举或自造 `EffectiveConfig` 的地方编译红,按上两行补 |
652
+
653
+ 凭据参数名(同版):「这个已解码的参数名是不是凭据名」只问包根 `credentialParamWordOf`(返回表词或 `undefined`:整名在 `CREDENTIAL_PARAM_NAMES` 上,或末段 / 尾串收在 `password` `passwd` `pwd` `secret` `apikey` 与 `secret key` / `secret access key` / `api key` / `access token` 上(后两条答各自的整名词 `api_key` / `access_token`);`password_hash` / `secretName` / `passwordless` 一类不是),`isCredentialParamName` / `findUrlCredential` 读同一只 —— 带 `?db_password=` 这类参数的 URL,受理口由受理变拒。
654
+ 读点:server 7.147.0 的日志脱敏器(URL 参数 · JSON 秘密字段 · 自由文本强锚三个名字读者与诊断值位)改读它、拿返回的词去查自己的锚强度列,受理口判官与脱敏器同一版同一天翻;cli 无读点(壳零直调这组判官);client-core 无读点(只有一格探针扫本包 d.ts 的导出名,新名字不命中它的『同类键名判官』扫描式);web-admin、sdk 不读名字判定(sdk 合同测里的固定样例 `?token=` / `?api_key=` 照旧命中)。
655
+
656
+ ## 18. hook 条目的 `args` 与 `onFailure`(19.0.0 起)—— server、web-admin、cli、client-core、sdk 各自的读法
657
+
658
+ - 字段表(`classifyHooksConfig` 与 `HookEntry` 一族 zod 面的唯一声明)多两列:command 条目的可选 `args`(串数组;`[]` 与空串元素都是值,元素含 NUL 为坏)与可选 `onFailure`,http 条目的可选 `onFailure`;
659
+ 词闭集 `HOOK_ON_FAILURE_WORDS` = `continue` | `block`(包根与 `./hooks`),型 `HookOnFailure`。缺席不补缺省(CC 的缺省 = `continue`,读方自己补)。
660
+ - 此前这两个键被分类器当契约外的键剥掉(不算坏块);自此:词表外的 `onFailure`、不合形的 `args` 是坏块(非守卫事件剔那一条,守卫事件整份拒,`field` 点名它);`args` 写在别的四型上、`onFailure` 写在 prompt / agent / mcp_tool 上仍是契约外的键(剥掉,不算坏块;键下装着守卫迹象的除外 —— 那一直是整份拒)。
661
+ `rewakeMessage` / `rewakeSummary` / `cloud` / `continueOnBlock` 仍不承运(`docs/CC-DIVERGENCE.md` H7)。
662
+ - **承运 ≠ 执行**:本包只判形。读到 `onFailure: "block"` 的消费方要么执行它(hook 起不来 / 超时 / 退出码非 0 非 2 / 输出 JSON 非法 ⇒ 按 exit 2 阻断,同步条目、非 Stop 一族事件),要么在自己的受理面明拒 —— 静默不执行等于守卫起不来照放行。
663
+
664
+ | 消费方 | 读点 | 动作 |
665
+ |---|---|---|
666
+ | server 7.147.0 | `CommandHookEntry` / `HttpHookEntry`(`args` · `onFailure`)· `HOOK_ON_FAILURE_WORDS` | 提货同版:`onFailure` 随条目到达,`block` 的失败按 exit 2 执行;`args` 是契约字段(读它的受理面以契约字段为准) |
667
+ | cli | `CommandHookEntry` / `HttpHookEntry` · `classifyHooksConfig` | 壳若自己执行 hook(引擎属主事件 `ENGINE_OWNED_HOOK_EVENTS` 以外):执行 `args` 与 `onFailure`,或明拒;「能不能装」仍只问分类器 |
668
+ | client-core | 由 `classifyHooksConfig` 生成的读器副本 | 闭包多一个私有件 `HOOK_ON_FAILURE_ACCEPTED`(与 `HOOK_SHELLS` 同法随函数抄走),自由名仍只有 `HOOK_EVENTS`;生成器照旧整函数抄 |
669
+ | web-admin | `HookEntry` 一族的编辑面 | 可选:表单承运两列 |
670
+ | sdk | `CommandHookEntry` / `HttpHookEntry`(类型面) | 加性:多两个可选键;对 `keyof` 写穷举表的地方补两列 |