@sema-agent/settings-schema 14.5.0 → 14.7.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 (77) hide show
  1. package/CHANGELOG.md +531 -0
  2. package/README.md +46 -3
  3. package/conformance/VERSION +1 -1
  4. package/conformance/cases/c84-sandbox-git-block-carried-verbatim/expected.json +153 -0
  5. package/conformance/cases/c84-sandbox-git-block-carried-verbatim/input.json +23 -0
  6. package/conformance/cases/c84-sandbox-git-block-carried-verbatim/meta.json +5 -0
  7. package/conformance/cases/c85-sandbox-git-refused-block-still-carried/expected.json +153 -0
  8. package/conformance/cases/c85-sandbox-git-refused-block-still-carried/input.json +23 -0
  9. package/conformance/cases/c85-sandbox-git-refused-block-still-carried/meta.json +5 -0
  10. package/dist/api/auth-bridge.d.ts +16 -2
  11. package/dist/api/auth-bridge.js +59 -24
  12. package/dist/api/credentials.d.ts +281 -0
  13. package/dist/api/credentials.js +149 -0
  14. package/dist/api/scopes.js +5 -1
  15. package/dist/atomic-write.js +10 -7
  16. package/dist/bundle.d.ts +1 -1
  17. package/dist/bundle.js +1 -1
  18. package/dist/config-d-file.js +33 -15
  19. package/dist/config-dir-migration.js +2 -1
  20. package/dist/config-fns.js +142 -78
  21. package/dist/conformance/canonical.js +7 -10
  22. package/dist/conformance/contract-adapter.js +26 -1
  23. package/dist/conformance/corpus.js +5 -5
  24. package/dist/conformance/derive.js +6 -5
  25. package/dist/conformance/run.js +44 -6
  26. package/dist/cross-session.d.ts +4 -4
  27. package/dist/declared-capture.d.ts +41 -0
  28. package/dist/declared-capture.js +344 -0
  29. package/dist/file-edit.js +11 -15
  30. package/dist/file-store.d.ts +1 -31
  31. package/dist/file-store.js +3 -30
  32. package/dist/fleet.d.ts +11 -0
  33. package/dist/fleet.js +60 -26
  34. package/dist/fs-failure.d.ts +12 -7
  35. package/dist/fs-failure.js +53 -29
  36. package/dist/hash.d.ts +0 -9
  37. package/dist/hash.js +64 -7
  38. package/dist/hooks-config.d.ts +2 -2
  39. package/dist/hooks-config.js +129 -33
  40. package/dist/http-hook-allowlists.d.ts +27 -10
  41. package/dist/http-hook-allowlists.js +189 -25
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.js +4 -1
  44. package/dist/mcp-request-entry.d.ts +13 -8
  45. package/dist/mcp-request-entry.js +246 -107
  46. package/dist/one-time-migrations.js +207 -95
  47. package/dist/own-key-read.d.ts +247 -0
  48. package/dist/own-key-read.js +449 -0
  49. package/dist/plain-json-object.d.ts +38 -3
  50. package/dist/plain-json-object.js +84 -7
  51. package/dist/portable-domains.d.ts +31 -0
  52. package/dist/portable-domains.js +30 -0
  53. package/dist/remote-exec.js +9 -1
  54. package/dist/safety-merge-spec.d.ts +88 -6
  55. package/dist/safety-merge-spec.js +246 -17
  56. package/dist/sandbox-git.d.ts +64 -0
  57. package/dist/sandbox-git.js +89 -0
  58. package/dist/scheduler-store.js +21 -12
  59. package/dist/settings-units.d.ts +85 -0
  60. package/dist/settings-units.js +177 -0
  61. package/dist/types.d.ts +6 -2
  62. package/dist/types.js +65 -56
  63. package/dist/url-shape.d.ts +27 -5
  64. package/dist/url-shape.js +29 -5
  65. package/dist/web-admin-credential.js +5 -2
  66. package/dist/web-admin-session-url.js +2 -2
  67. package/dist/worker-row-verdict.d.ts +11 -0
  68. package/dist/worker-row-verdict.js +51 -1
  69. package/dist/workers-rows.d.ts +2 -1
  70. package/dist/workers-rows.js +41 -12
  71. package/dist/zod-unwrap.d.ts +6 -0
  72. package/dist/zod-unwrap.js +14 -7
  73. package/docs/API-SURFACE.md +50 -5
  74. package/docs/CONSUMER-INTEGRATION.md +178 -0
  75. package/llms-full.txt +34 -5
  76. package/llms.txt +6 -3
  77. package/package.json +1 -1
@@ -4,9 +4,9 @@
4
4
  > 每次全量重算并逐名比对 —— 加 / 删 / 改形一个导出而不重生成本档 ⇒ 红。导出面的每次变动因此以本档的 diff 出现在评审里。
5
5
  > 形:`value` = 运行期拿得到的值;`type` = 只在类型层;`value+type` = 同名的值与类型(zod schema 与它推出的类型)。
6
6
 
7
- 子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1351 个(同名在多条子路径上各计一次)。
7
+ 子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1396 个(同名在多条子路径上各计一次)。
8
8
 
9
- ## `.`(671)
9
+ ## `.`(708)
10
10
 
11
11
  - `ABSOLUTE_HOST_PATH_MAX` — value
12
12
  - `ACCESS_TOKEN_TTL_SECONDS` — value
@@ -56,7 +56,9 @@
56
56
  - `ConfigChangeHookInput` — value
57
57
  - `ConfigPath` — type
58
58
  - `ConfigReader` — type
59
+ - `CredentialHandleAnswer` — value+type
59
60
  - `CredentialParamName` — type
61
+ - `CredentialResolveRequest` — value+type
60
62
  - `CrossSessionInboundSetting` — value
61
63
  - `CrossSessionInboundSettingValue` — type
62
64
  - `CrossSessionInboundValue` — type
@@ -151,6 +153,7 @@
151
153
  - `FEEDBACK_DESCRIPTION_MAX_CHARS` — value
152
154
  - `FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS` — value
153
155
  - `FEEDBACK_V1_PATH` — value
156
+ - `FLEET_ADVERTISE_ADDRESS_ENV` — value
154
157
  - `FLEET_GENERATION_ENV` — value
155
158
  - `FLEET_LIVE_PATHS` — value
156
159
  - `FLEET_PATHS` — value
@@ -280,7 +283,9 @@
280
283
  - `OAuthErrorCode` — type
281
284
  - `OAuthErrorResponse` — value+type
282
285
  - `PARSE_TRANSPARENT_KEYS` — value
286
+ - `PATH_BASE_URL_FORBIDDEN_PARTS` — value
283
287
  - `PLUGIN_ORIGIN` — value
288
+ - `PRINCIPAL_INC_CLAIM` — value
284
289
  - `PRINCIPAL_MAX_LENGTH` — value
285
290
  - `PRIVATE_KEY_PEM_HEADER` — value
286
291
  - `PROBE_CPU_CORES_MAX` — value
@@ -316,6 +321,7 @@
316
321
  - `PreModelSwitchHookInput` — value+type
317
322
  - `PreToolUseHookInput` — value
318
323
  - `PrincipalCapsWire` — value+type
324
+ - `PrincipalIncarnation` — value+type
319
325
  - `PrincipalOrgMemoryWire` — value+type
320
326
  - `ProjectContextItem` — value+type
321
327
  - `ProjectContextItemKind` — type
@@ -377,6 +383,13 @@
377
383
  - `RostersConfig` — value+type
378
384
  - `RoutingVerdict` — type
379
385
  - `SAFETY_MERGE_SPEC` — value
386
+ - `SANDBOX_GIT_MAX_ORIGINS` — value
387
+ - `SANDBOX_GIT_ORIGIN_REASONS` — value
388
+ - `SANDBOX_GIT_ORIGIN_STATES` — value
389
+ - `SANDBOX_GIT_ORIGIN_VERDICTS` — value
390
+ - `SANDBOX_GIT_PERMANENCE` — value
391
+ - `SANDBOX_GIT_REFUSALS` — value
392
+ - `SANDBOX_GIT_REFUSAL_ERRORS` — value
380
393
  - `SCOPES_V1_PATHS` — value
381
394
  - `SCOPE_CLAIM` — value
382
395
  - `SCOPE_ID_REGEX` — value
@@ -395,6 +408,7 @@
395
408
  - `SESSION_MIRROR_ENGINE_URL` — value
396
409
  - `SETTINGS_SOURCES` — value
397
410
  - `SETTINGS_SOURCE_CONTRACT` — value
411
+ - `SETTINGS_UNITS` — value
398
412
  - `SHA256_DIGEST_RE` — value
399
413
  - `STALE_HEARTBEAT_MULTIPLIER` — value
400
414
  - `STALE_SESSION_RECORD_MS` — value
@@ -402,6 +416,19 @@
402
416
  - `SUBMIT_IDEMPOTENCY_KEY` — value
403
417
  - `SafetyMergeCategory` — type
404
418
  - `SafetyMergeMember` — type
419
+ - `SandboxGitConfig` — value+type
420
+ - `SandboxGitConnectUrl` — value+type
421
+ - `SandboxGitCredentialAnswer` — value+type
422
+ - `SandboxGitCredentialRefusal` — value+type
423
+ - `SandboxGitCredentialRequest` — value+type
424
+ - `SandboxGitOrigin` — value+type
425
+ - `SandboxGitOriginAnswer` — value+type
426
+ - `SandboxGitOriginEntry` — value+type
427
+ - `SandboxGitOriginList` — value+type
428
+ - `SandboxGitOriginReason` — type
429
+ - `SandboxGitOriginState` — type
430
+ - `SandboxGitPermanence` — type
431
+ - `SandboxGitRefusalError` — type
405
432
  - `ScenarioAssignments` — value+type
406
433
  - `ScenarioConfig` — value+type
407
434
  - `ScenarioEntry` — value+type
@@ -442,6 +469,7 @@
442
469
  - `SettingSource` — value+type
443
470
  - `SettingsSource` — type
444
471
  - `SettingsSourceContract` — type
472
+ - `SettingsUnitName` — type
445
473
  - `SetupHookInput` — value
446
474
  - `Sha256Digest` — value
447
475
  - `SiblingResolver` — type
@@ -465,6 +493,7 @@
465
493
  - `TCP_PORT` — value
466
494
  - `TENANT_SEPARATION_BASES` — value
467
495
  - `TENANT_SEPARATION_WORDS` — value
496
+ - `TIGHTEN_VERDICTS` — value
468
497
  - `TOKEN_SCOPE_REFUSALS` — value
469
498
  - `TOOL_WIRE_NAME_MAX_CHARS` — value
470
499
  - `TaskCompletedHookInput` — value
@@ -475,6 +504,7 @@
475
504
  - `TierBindings` — value+type
476
505
  - `TierGroup` — value+type
477
506
  - `TightenRule` — value+type
507
+ - `TightenVerdict` — type
478
508
  - `TokenGrantResponse` — value+type
479
509
  - `TokenQuota` — value+type
480
510
  - `TokenRefreshRequest` — value+type
@@ -585,6 +615,7 @@
585
615
  - `isRetiredDomainName` — value
586
616
  - `isRole` — value
587
617
  - `isRoutable` — value
618
+ - `isSandboxGitOrigin` — value
588
619
  - `isSchedulerRecord` — value
589
620
  - `isStringArray` — value
590
621
  - `isStringRecord` — value
@@ -601,6 +632,7 @@
601
632
  - `mcpServerPolicyVerdict` — value
602
633
  - `memberAdmission` — value
603
634
  - `memberMinTrust` — value
635
+ - `memberTighten` — value
604
636
  - `membersOf` — value
605
637
  - `mintScopedToken` — value
606
638
  - `narrowAtMention` — value
@@ -614,6 +646,7 @@
614
646
  - `parseSchedulerStore` — value
615
647
  - `parseWebAdminCredentialFile` — value
616
648
  - `parseWebAdminSessionUrl` — value
649
+ - `pathBaseUrlFlaws` — value
617
650
  - `pickInstance` — value
618
651
  - `projectWarning` — value
619
652
  - `promptArtifactFor` — value
@@ -631,6 +664,7 @@
631
664
  - `readScopedTokenBinding` — value
632
665
  - `readServingLeaseRequest` — value
633
666
  - `readServingLeaseResponse` — value
667
+ - `readSettingsUnit` — value
634
668
  - `readTokenScope` — value
635
669
  - `readUiLanguage` — value
636
670
  - `readWorkersRows` — value
@@ -645,6 +679,7 @@
645
679
  - `resolveCatalogDefaultModel` — value
646
680
  - `resolveEffectiveForWorker` — value
647
681
  - `resolveMemberAdmission` — value
682
+ - `resolveMemberTighten` — value
648
683
  - `resolveTokenScope` — value
649
684
  - `retiredDomainSentence` — value
650
685
  - `retiredEnvRefusal` — value
@@ -667,9 +702,11 @@
667
702
  - `statusRowsToPrune` — value
668
703
  - `stopAttributionText` — value
669
704
  - `stripControlAndBidi` — value
705
+ - `tightenVerdict` — value
670
706
  - `toSkillsManifest` — value
671
707
  - `toVerifiedIdentity` — value
672
708
  - `trustRank` — value
709
+ - `unitTighten` — value
673
710
  - `urlAllowPatternMatches` — value
674
711
  - `urlCredentialRule` — value
675
712
  - `urlDenyPatternMatches` — value
@@ -1047,7 +1084,7 @@
1047
1084
  - `REMOTE_EXEC_DOCUMENT` — value
1048
1085
  - `RemoteExecSpec` — value+type
1049
1086
 
1050
- ## `./safety-merge-spec`(19)
1087
+ ## `./safety-merge-spec`(24)
1051
1088
 
1052
1089
  - `FailMode` — value+type
1053
1090
  - `MIN_TRUST_WORDS` — value
@@ -1061,12 +1098,17 @@
1061
1098
  - `SafetyMergeCategory` — type
1062
1099
  - `SafetyMergeMember` — type
1063
1100
  - `SpecEnforcement` — value+type
1101
+ - `TIGHTEN_VERDICTS` — value
1064
1102
  - `TightenRule` — value+type
1103
+ - `TightenVerdict` — type
1065
1104
  - `isAtLeast` — value
1066
1105
  - `memberAdmission` — value
1067
1106
  - `memberMinTrust` — value
1107
+ - `memberTighten` — value
1068
1108
  - `membersOf` — value
1069
1109
  - `resolveMemberAdmission` — value
1110
+ - `resolveMemberTighten` — value
1111
+ - `tightenVerdict` — value
1070
1112
  - `trustRank` — value
1071
1113
 
1072
1114
  ## `./node`(39)
@@ -1242,7 +1284,7 @@
1242
1284
  - `interpolateHookEnvVars` — value
1243
1285
  - `stopAttributionText` — value
1244
1286
 
1245
- ## `./fleet`(83)
1287
+ ## `./fleet`(84)
1246
1288
 
1247
1289
  - `AggregatedPrincipalUsage` — type
1248
1290
  - `CapabilityReading` — type
@@ -1266,6 +1308,7 @@
1266
1308
  - `ENGINE_SIGNAL_KEY` — value
1267
1309
  - `EngineDraining` — value+type
1268
1310
  - `EngineSignal` — value+type
1311
+ - `FLEET_ADVERTISE_ADDRESS_ENV` — value
1269
1312
  - `FLEET_GENERATION_ENV` — value
1270
1313
  - `FLEET_LIVE_PATHS` — value
1271
1314
  - `FLEET_PATHS` — value
@@ -1354,7 +1397,7 @@
1354
1397
  - `nextPollInterval` — value
1355
1398
  - `normalizeUserCode` — value
1356
1399
 
1357
- ## `./api/auth-bridge`(21)
1400
+ ## `./api/auth-bridge`(23)
1358
1401
 
1359
1402
  - `AUTH_BRIDGE_ALGS` — value
1360
1403
  - `AUTH_BRIDGE_ERROR_CODES` — value
@@ -1365,7 +1408,9 @@
1365
1408
  - `DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC` — value
1366
1409
  - `DEFAULT_OFFLINE_GRACE_SEC` — value
1367
1410
  - `JwksCacheSnapshot` — type
1411
+ - `PRINCIPAL_INC_CLAIM` — value
1368
1412
  - `PRINCIPAL_MAX_LENGTH` — value
1413
+ - `PrincipalIncarnation` — value+type
1369
1414
  - `VerifiedIdentity` — value+type
1370
1415
  - `VerifierConfig` — value+type
1371
1416
  - `VerifierConfigInput` — type
@@ -7,6 +7,8 @@
7
7
  > S-570 收车档;3.0.0 之后的下一次真实改动,请同一批把这份文档也带上,别再停摆。**7.0.0**(M-R2 WorkerSpec 结构块)的
8
8
  > 三方迁移句在 §0.2,`remoteExec` 删 `remote-docker` 在 §5。**8.0.0**(一致性门第四条腿 + 表态律 + CLI `--check`)在 §0.3。
9
9
  > **11.0.0** 的持久面(`runtime` 域退役、读侧兼容臂退役、一次性迁移族 `./one-time-migrations`)在 §0.4。
10
+ > **14.7.0** 的沙箱 git 凭据平面(取值口 wire 型、有效配置块 `sandboxGit`、`VerifiedIdentity.principalInc`)在 §12。
11
+ > **14.7.0** 的路径基址判官(`PATH_BASE_URL_FORBIDDEN_PARTS` / `pathBaseUrlFlaws`)与公告地址 env 名(`FLEET_ADVERTISE_ADDRESS_ENV`)在 §13。
10
12
 
11
13
  ---
12
14
 
@@ -236,6 +238,7 @@ server 直接拒启,不是静默降级。上一代 `SEMA_REGISTRY_*` 八名(`URL
236
238
  命令行 `migrate` 与这只函数同一份实现;写盘用的就是本包那一只原子写器(契约文档新建 0o644、已有的沿用原模式、符号链接保留)。
237
239
  手里已有存量文档原文、不经目录的消费方(例:命令行壳按层读 `settings` 里的 models 域,要的是「引擎起服那一次迁移之后这份文档整域读不读得进」)调同一个内核的同步口 `migrateConfigDocuments(docs, { documents })`(14.5.0,`./node`):
238
240
  同一个闭集的无 io 形(`refused` ⇒ 引擎同样不写回、整域落缺省;**反向不成立** —— 引擎对整个目录一判,别的文档被拒也会让整目录不写,单层单域的 `noop` / `migrated` 不保证引擎那一次放行;`docs` 是壳的写手要的迁移后那一份)。一层一次调用;**别自己把 `migrateStoredDocuments` 与 `parseDomain` 按这个顺序再组装一遍**。
241
+ - **路径基址判官与公告地址**:「别人要往它后面拼路径的基址」座位(中心基址三席、公告地址、会话链接基址)的禁用成分集 `PATH_BASE_URL_FORBIDDEN_PARTS` 与判官 `pathBaseUrlFlaws` 在本包,公告地址的 env 名是 `FLEET_ADVERTISE_ADDRESS_ENV`;**别手写 env 名、别手抄那三个词** —— 两方读法见 §13。
239
242
  - **会话网页链接**:要给用户一个 web-admin 里的会话链接,调 `webAdminSessionUrl(webBaseUrl, { worker, sessionId })`(`webBaseUrl` 取引擎 `/health` 自报的人用界面根 URL,
240
243
  `worker` 取引擎向 web-admin 登记用的 worker 名),得到 `<webBaseUrl>/chat?agent=<worker>&session=<sessionId>`;**别按地址形手拼** —— 路由属于 web-admin,模板与读回的
241
244
  `parseWebAdminSessionUrl(link)` 同在本包,路径与参数名只此一份。两位必填:只带 `session` 的链接会落到页面自己挑的 worker 上、可能找不到会话。
@@ -283,3 +286,178 @@ server 直接拒启,不是静默降级。上一代 `SEMA_REGISTRY_*` 八名(`URL
283
286
  这份档里重复展开。
284
287
  - 今天真接了这道门的:server(`wire` + `localRoot` 两条读腿)、管理面(`domains` 往返腿,钉在 6.x);cli 显式拒接 kit 读腿
285
288
  (壳不走上游 effective 读腿),client-core 零 import。8.0.0 起两家接线方都要对四条腿逐条表态(§0.3)。
289
+
290
+ ## 12. 沙箱 git 凭据平面(14.7.0 起)—— web-admin 与 server 两方的读法
291
+
292
+ 沙箱里的 git 用**成员本人**的凭据:凭据住在 web-admin 的凭据库,引擎的数据面按 run 的身份向取值口取,只在该成员自己的沙箱里经代理使用,永不进模型可读的容器。
293
+ 本节说**本包发什么、两方各自怎么读**。本包只有**形与词表**(纯、浏览器安全,零 IO);鉴权、委托、活表复核、行 MAC、租约与续期都是两方自己的事(§12.9 逐条列出)。
294
+
295
+ ### 12.1 本包发什么
296
+
297
+ | 件 | 在哪 | 是什么 |
298
+ |---|---|---|
299
+ | `isSandboxGitOrigin` · `SandboxGitOrigin` · `SandboxGitOriginList` · `SANDBOX_GIT_MAX_ORIGINS` | 主桶(`src/sandbox-git.ts`) | origin 的规范形判官与 zod 形;一个 origin 就是它自己的 WHATWG origin |
300
+ | `SandboxGitConfig` · `SandboxGitOriginEntry` · `SandboxGitConnectUrl` | 主桶 | 有效配置块 `sandboxGit` 的形 |
301
+ | `SandboxGitCredentialRequest` · `CredentialHandleAnswer` · `CredentialResolveRequest` · `SandboxGitCredentialAnswer` · `SandboxGitOriginAnswer` · `SandboxGitCredentialRefusal` | 主桶(`src/api/credentials.ts`,不开子路径) | 取值口两步的 wire 型 |
302
+ | `SANDBOX_GIT_ORIGIN_VERDICTS` → `SANDBOX_GIT_ORIGIN_STATES` · `SANDBOX_GIT_ORIGIN_REASONS`;`SANDBOX_GIT_REFUSALS` → `SANDBOX_GIT_REFUSAL_ERRORS`;`SANDBOX_GIT_PERMANENCE` | 主桶 | 三张词表(每个词只在 `src/api/credentials.ts` 里拼一次;状态词、原因词、错误词表与类型全由表派生) |
303
+ | `PRINCIPAL_INC_CLAIM`(`"sub_inc"`) · `PrincipalIncarnation` · `VerifiedIdentity.principalInc` | 主桶与 `./api/auth-bridge` | 令牌里的化身号声明 |
304
+ | `EffectiveWire.sandboxGit?: unknown` | 主桶(`types`) | `/effective` 载荷上的非域顶层键,逐字承运 |
305
+
306
+ 全部是加性(新导出 / 可选键),既有导出对既有输入的答案不变。
307
+
308
+ ### 12.2 有效配置块 `sandboxGit`
309
+
310
+ ```json
311
+ {
312
+ "sandboxGit": {
313
+ "origins": [
314
+ { "origin": "https://git.example.com:9999", "connectUrl": "https://console.example.com/connect/git" },
315
+ { "origin": "http://git.legacy.example:8080", "connectUrl": "https://console.example.com/connect/git" }
316
+ ]
317
+ }
318
+ }
319
+ ```
320
+
321
+ - **非域顶层键**:web-admin 按部署登记册**投影**,不是存储域 —— `buildEffective` 不产它、`FileConfigStore` 没有 `sandboxGit.json`(单机模式没有这个平面)。**可选、无默认**:键缺席 = 这个部署没有投影这一块。
322
+ - `readEffectiveWire` 对它走 open-world:**逐字承运,不校验、不丢**(`EffectiveWire.sandboxGit` 是 `unknown`)。**判它的唯一座位是 `SandboxGitConfig`**:消费方在用之前对 `wire.sandboxGit` 跑 `safeParse`。
323
+ - `origins` ≤ 16(`SANDBOX_GIT_MAX_ORIGINS`)、按 `origin` 两两不同,空表合法;解析剥未知键(前向兼容)。
324
+ - **规范形只有一条律**:`origin` 逐字等于 `new URL(origin).origin`,且 scheme ∈ {http, https}。小写 host、去缺省端口(443 / 80)、无尾斜杠、无 userinfo / 路径 / 查询 / 片段都是它的推论 ——
325
+ 凡 WHATWG 会改写的串(大写、缺省端口、IPv4 简写、百分号编码的 host、前后空白……)都不是规范形。**origin 不表达子路径部署**:`https://host/gitea` 不是 origin,判官拒;本包没有「削成 origin」的规范化器(把运维写的路径静默削掉会把凭据送到他没写的地方)。
326
+ - 🔴 **块里 http origin 可读**。它为什么送不出凭据是**逐 origin 应答里的一个词**(§12.4 第 1 行 `insecure_transport`)与能力读数里的一个词,不是块的校验拒:块若因一个 http origin 整块不可读,同块里的 https origin 一并失效,「逐 origin 说拒因」就无从谈起。
327
+ http origin 只配路由、永不取值,是 server 的事。
328
+ - `connectUrl` = 控制台「连接 Gitea(git)」页的绝对 **https** URL(`SandboxGitConnectUrl`:`NON_SECRET_URL_NO_MIGRATION` 的判据 + https)。
329
+
330
+ ### 12.3 取值口两步(wire 型)
331
+
332
+ ```
333
+ ① POST /api/v1/credentials/handles 请求 SandboxGitCredentialRequest → 200 CredentialHandleAnswer { handle, expiresAt }
334
+ ② POST /api/v1/credentials/resolve 请求 CredentialResolveRequest { handle } → 200 SandboxGitCredentialAnswer
335
+ → 非 2xx 体 SandboxGitCredentialRefusal { error, permanence, message }
336
+ ```
337
+
338
+ ```json
339
+ { "kind": "sandbox_git", "principal": "alice", "principalInc": "<化身号>", "scope": "team-android",
340
+ "origins": ["https://git.example.com:9999"], "sid": "<会话 id,审计标签>" }
341
+ ```
342
+
343
+ - `kind` 是字面量 `"sandbox_git"`;`principal` 的长度界同令牌 `sub`(`PRINCIPAL_MAX_LENGTH`);`principalInc` 就是 `PrincipalIncarnation`(与令牌 `sub_inc`、`VerifiedIdentity.principalInc` 是**同一只 schema 对象**);
344
+ `scope` 非空串(与 `readTokenScope` 读令牌声明同一条规则);`origins` 1–16 个、规范形、两两不同 —— **http origin 也收**(中心对它答判定序第 1 行,请求形不抢先拒)。
345
+ - 应答(200)里的 `principal` / `principalInc` / `scope` 是请求的**回显**;`answeredAt`(应答时刻)、`cacheUntil`(句柄到期)与 `active` 行里的 `expiresAt` 是**同一只钟**(中心的)给的毫秒:收方只该用它们的**差**,不拿它们和自己的钟比。
346
+ - 每个 origin 一行 `SandboxGitOriginAnswer`,按 `state` 判别。`active` 独有 `credentialId` `version` `username` `secret` `expiresAt`(OAuth 令牌:到期毫秒;个人访问令牌:`null`);`needs_reauth` 行带 `credentialId`;其余只有 `origin` / `state` / `reason`。
347
+ **解析一律剥未知键**,`secret` 只在 `active` 臂的形里 —— 别的臂收到也剥掉。`secret` 是应答里唯一的秘密位;`credentialId` / `version` / `auditId` 是不带值的审计位,日志里记它们、不记 `secret`。
348
+
349
+ ### 12.4 逐 origin 判定序表(`SANDBOX_GIT_ORIGIN_VERDICTS`,11 行,序即判定序)
350
+
351
+ 每个 origin 独立判,**首条命中即答**,一个 origin 的结果不影响别的 origin。`state` 六词、`reason` 十词都由这张表派生。
352
+
353
+ | 序 | 条件(什么事实命中) | `state` | `reason` |
354
+ |---|---|---|---|
355
+ | 1 | origin 的 scheme 不是 `https` | `insecure_transport` | `not_https` |
356
+ | 2 | 本部署本 scope 的登记册里没有这个 origin 的 git 应用 | `not_offered` | `not_in_registry` |
357
+ | 3 | 选行键下没有行 | `not_connected` | `no_row` |
358
+ | 4 | 行 `status = revoked`(吊销后无密文、留作审计锚的行) | `not_connected` | `revoked` |
359
+ | 5 | 行 `status = revoking` | `needs_reauth` | `revoking` |
360
+ | 6 | 行 `status = suspended` | `needs_reauth` | `suspended` |
361
+ | 7 | 行 `status = needs_reauth` | `needs_reauth` | `row_needs_reauth` |
362
+ | 8 | 行 `active`,但行 MAC 或解密 AAD 校验不过 | `needs_reauth` | `integrity_failed` |
363
+ | 9 | 行 `active`、OAuth、剩余寿命 < 600 s,单飞刷新得上游 `invalid_grant` 或其余 4xx(`invalid_grant` 时行同时置 `needs_reauth`) | `needs_reauth` | `refresh_rejected` |
364
+ | 10 | 同 9,刷新遇网络错 / 5xx / 超时,且现令牌剩余 < 60 s | `temporarily_unavailable` | `refresh_unavailable` |
365
+ | 11 | 其余(行 `active`;不需刷新,或刷新成功,或刷新暂不可用但现令牌剩余 ≥ 60 s) | `active` | — |
366
+
367
+ 选行键 = (scope, 属主键 = 主体 + 化身号, `sandbox_git`, origin);「条件」列是 web-admin 的判定逻辑,**不进运行期表**(本包不知道行状态、登记册、MAC)。状态 × 原因的配对由表定:`SandboxGitOriginAnswer` 每个状态一个臂,臂里 `reason` 的值域 = 表里该状态的行,原因贴在不属于它的状态上一律拒(状态 × 原因的整个叉积里,恰好表里那 10 对加上无原因的 `active` 被收下)。
368
+
369
+ ### 12.5 整次拒答表(`SANDBOX_GIT_REFUSALS`)
370
+
371
+ 体 = `{ "error", "permanence", "message" }`。`permanence` 两词:`terminal` = 不按定时重试;`transient` = 退避重试。**一个错误词的 permanence 是固定的**,拒体里再带一份只为收方不必查表 —— 两处不一致是畸形应答(`SandboxGitCredentialRefusal` 拒)。
372
+
373
+ | `error` | HTTP | `permanence` | 说明 |
374
+ |---|---|---|---|
375
+ | `caller_not_admitted` | 401 / 403 | terminal | 调用方不是被承认的舰队 worker |
376
+ | `scope_mismatch` | 403 | terminal | 请求的 scope 不等于调用方凭据的 scope |
377
+ | `incarnation_mismatch` | 403 | terminal | 请求的化身号不等于该主体当前的化身号 |
378
+ | `request_invalid` | 400 | terminal | 请求形不合(含 `principalInc` 缺或空) |
379
+ | `principal_not_live` | 403 | terminal | 主体活表复核不过 |
380
+ | `delegation_required` | 403 | terminal | 没有活的委托 |
381
+ | `vault_disabled` | 503 | terminal | 凭据库关着 |
382
+ | `vault_frozen` | 503 | terminal | 凭据库冻结 |
383
+ | `handle_invalid` | 401 | transient | 句柄验不过 / 已用 / 已过期 |
384
+ | `vault_unavailable` | 503 | transient | 凭据库暂不可用 |
385
+
386
+ 收方怎么处置某个词(例如哪些 terminal 可由用户动作恢复、哪些不可恢复)是收方的状态机,不在本包里。**错误词不在闭集 ⇒ 畸形应答,不是「当 transient 重试」。**
387
+
388
+ ### 12.6 `VerifiedIdentity.principalInc` 与令牌声明 `sub_inc`
389
+
390
+ 同名主体删号重建之后是**另一个人**:`sub` 只命名一个槽位,化身号才区分「同一个名字」与「同一个人」。
391
+
392
+ - `toVerifiedIdentity` 读声明 `PRINCIPAL_INC_CLAIM`(`sub_inc`):**在场且是非空串 ⇒ `identity.principalInc` 原样**(不 trim、不折大小写);**缺席 ⇒ 读出形里不铸键**(从不读成 `0` / 空串 / 别的占位);
393
+ **在场而不是非空串(数字含 0 / 空串 / `null` / 布尔 / 对象 / 数组)⇒ 该令牌 `malformed`**(与 `sub`、`iat` 同律:声明形是垃圾,不降级成缺席 —— 降级会把坏掉的签发方读成「没有化身号」)。
394
+ - **缺化身号不是本契约的拒**:读方要化身号而令牌没有时,答什么是读方的事(server 把它记成「化身号未记录」,取值一律不放)。
395
+ - 行为面唯一变化:此前令牌带了 `sub_inc` 声明也被忽略;从本版起它在场而形不对就拒。
396
+
397
+ ### 12.7 web-admin 做什么
398
+
399
+ - **铸令牌**:用 `PRINCIPAL_INC_CLAIM` 铸 `sub_inc`(值 = 会话建立时绑定的化身号,非空串)。「会话化身 = 用户行当前化身」的签发不变量是 web-admin 的,不是本包的。
400
+ - **投影 `sandboxGit`**:`origin` 必须已经是规范形(`isSandboxGitOrigin`),`connectUrl` 是 https;自己的出口先 `SandboxGitConfig.parse` 再发,拒掉的是运维配置的问题,不要静默改写。
401
+ - **收请求**:`SandboxGitCredentialRequest.safeParse` 不过 ⇒ `request_invalid`。
402
+ - **答应答**:按 `SANDBOX_GIT_ORIGIN_VERDICTS` 的序逐 origin 判,首条命中即答;用 `SandboxGitCredentialAnswer.parse` 过一遍再发(臂的键集错了在自己这里红,不要等收方判畸形)。
403
+ - **答拒**:`permanence` 取自 `SANDBOX_GIT_REFUSALS[error].permanence`,HTTP 状态取表里该词的状态集之一。
404
+
405
+ ### 12.8 server 做什么
406
+
407
+ - **读块**:`wire.sandboxGit` 缺席 ⇒ 没有这一块;在场 ⇒ `SandboxGitConfig.safeParse`,不过 ⇒ 当作没有任何 origin,并响亮说(坐标可记,拒句不回显值)。http origin 只配路由、永不取值。
408
+ - **读令牌**:`toVerifiedIdentity` 之后用 `identity.principalInc`;缺 ⇒ 化身号未记录(本契约不拒令牌)。
409
+ - **发请求 / 收应答**:`SandboxGitCredentialRequest` 构造后再发;应答先 `SandboxGitCredentialAnswer.safeParse`,**再**核逐条对应与回显(§12.9);`secret` 只存在于 `active` 行,日志里不出现。
410
+ - **收拒**:体过 `SandboxGitCredentialRefusal` 才算认得;`permanence` 读表(或读体 —— 二者已被形保证相等)。
411
+ - **时刻**:`answeredAt` / `cacheUntil` / `expiresAt` 只取差;不拿它们和 server 自己的钟比。
412
+
413
+ ### 12.9 本包不做的(如实)
414
+
415
+ - **应答与请求的逐条对应 / 回显相等**(`origins` 同序同长、`principal` / `principalInc` / `scope` 等于请求)是收方在 `safeParse` 之后要核的事,不在形里。
416
+ - **租约算术与续期状态机**(`cacheUntil` / `expiresAt` − `answeredAt` 的差、封顶、重试退避、「踢」)、哪些 terminal 词可恢复,都是 server 自己的。
417
+ - **取值口的另两种凭据**(`model` / `mcp_static`)的请求与应答形不在本节:它们落地时与 `sandbox_git` 形并成按 `kind` 判别的联合,`sandbox_git` 形不动(`kind` 字面量已经在)。`CredentialHandleAnswer` / `CredentialResolveRequest` 与种类无关,三种共用。
418
+ - **两个端点的路径常量**不在包里(上面的路径是文字,不是导出)。
419
+ - **调用方鉴权、委托、活表复核、行 MAC、冻结**:全是 web-admin 的。
420
+ - **一致性门**:没有新腿 —— `sandboxGit` 是 wire 专属键,走 §11 里 T4 那一条(open-world 逐字承运),既有语料的读数不变。新增两条语料(`c84` 合格块含一个 http origin、`c85` 不合格块):wire 腿都要把块**逐字承运、不出告警**,判它是消费方用 `SandboxGitConfig` 的事 —— 你的 wire 读腿若校验后丢掉坏块,跑语料时会在 `c85` 上红。
421
+
422
+ ## 13. 路径基址判官与公告地址 `FLEET_ADVERTISE_ADDRESS`(14.7.0 起)—— web-admin 编排器与 server 两方的读法
423
+
424
+ 有一种座位的值是「别人要往它后面拼路径的基址」:中心基址三席(`WEB_ADMIN_ENV.url` / `liveUrl` / `webUrl`,值 `SEMA_WEB_ADMIN_URL` / `SEMA_WEB_ADMIN_LIVE_URL` / `SEMA_WEB_ADMIN_WEB_URL`)、
425
+ fleet 成员对中心公告的可达地址(`FLEET_ADVERTISE_ADDRESS`,中心往它后面拼路径)、会话网页链接的基址(`webAdminSessionUrl` 的第一个参数)。这种座位有同一条规则:
426
+ 不许带凭据,也不许带 query / fragment(`http://x?a=1` 拼上 `/v1/…` 是 `http://x?a=1/v1/…`);路径前缀放行(要拼的路径接在前缀后面)。
427
+ 本节说**本包发什么、两方各自怎么读**。本包只有**判这种座位的那一只判官、它禁用的成分集与公告地址的 env 名**(纯、浏览器安全,零 IO);值从哪来、占位怎么展开、地址是不是真打得到,都是两方自己的事(§13.4)。
428
+
429
+ ### 13.1 本包发什么
430
+
431
+ | 件 | 是什么 |
432
+ |---|---|
433
+ | `PATH_BASE_URL_FORBIDDEN_PARTS` | 「后面要拼路径的基址」座位禁用的 URL 成分,闭集、冻结、判序:`userinfo` · `query` · `fragment`(**策略集,独立于判官词表 `HTTP_URL_PARTS`**:词表说 URL 有哪些成分,本集说这种座位禁哪些;今天碰巧同三词,但不由词表派生,词表加词时要显式裁);这种座位的策略只此一张,没有第二份抄本;包根 |
434
+ | `pathBaseUrlFlaws(url)` | = `httpUrlFlaws(url, PATH_BASE_URL_FORBIDDEN_PARTS)`(本包唯一的 URL 形判官,不另写判法)。合格 ⇒ `[]`;否则按判序交回规则词(`not-http-url` / `userinfo` / `query` / `fragment`),词的规则句取 `httpUrlRule(词)`;答案只有词,不含值的任何片段;包根 |
435
+ | `FLEET_ADVERTISE_ADDRESS_ENV` | 公告地址的 env 名 `"FLEET_ADVERTISE_ADDRESS"`(字面量类型),与 `FLEET_GENERATION_ENV` / `FLEET_SCOPE_ENV` 同文件同形;写的一方和读的一方都 import,不各自拼;包根与 `./fleet` |
436
+
437
+ 本包自己的 `webAdminSessionUrl` 已改读这一只判官(读数逐字不变)。判官今天的答案(逐行钉在 `test/path-base-url-forbids-14.7.0.test.ts`):
438
+
439
+ - `http://$(POD_IP):8090` ⇒ `[]`(占位在主机位:`$` `(` `)` 都是合法主机码点);`http://10.0.0.5:8090`、`http://[fd00::1]:8090`、`https://worker.example.com` ⇒ `[]`。
440
+ - `http://[$(POD_IP)]:8090` ⇒ `["not-http-url"]`:方括号里必须是 IPv6 字面量,占位不是 —— **判占位展开之后的值**,不判模板。
441
+ - 路径前缀 `http://10.0.0.5:8090/prefix` ⇒ `[]`(路径不在禁用集里:要拼的路径接在前缀后面)。
442
+ - `…?a=1`(含空 `?`)⇒ `["query"]`;`…#f`(含空 `#`)⇒ `["fragment"]`;`http://user:pw@host`(只有用户名也算)⇒ `["userinfo"]`;三件齐 ⇒ 三个词,按判序。
443
+ - `127.0.0.1:8121`(缺 scheme)、`ftp://x:1`、`file:///etc/passwd`、`data:…`、空串 ⇒ 含 `not-http-url`(`fetch` 认 `file://` / `data:`,配错的地址不许变成读本地文件)。
444
+
445
+ ### 13.2 web-admin 编排器做什么
446
+
447
+ - **渲染**:往成员 env 写公告地址时,键名取 `FLEET_ADVERTISE_ADDRESS_ENV`,不手写字面量。k8s 后端写 `http://$(POD_IP):<端口>` 这种形,`$(POD_IP)` 由部署后端展开。
448
+ - **发出前判**:对**展开之后**的值调 `pathBaseUrlFlaws`,长度非 0 就是渲染器自己的配置问题 —— 拒掉并点名规则词(规则句取 `httpUrlRule`),不要静默改写(例如悄悄削掉 query):改写会把地址送到运维没写的地方。
449
+ - **不手抄集**:不要在编排器里再写一遍 `["userinfo", "query", "fragment"]` 或自己的 URL 判法;集与判官只有本包这一份,它与引擎起服时判的是同一个答案。给用户铸会话链接用 `webAdminSessionUrl`,它判基址用的也是这一只。
450
+
451
+ ### 13.3 server 做什么
452
+
453
+ - **读 env**:`process.env[FLEET_ADVERTISE_ADDRESS_ENV]`;不手写字面量。缺席或空串(compose 的 `${NAME:-}` 常态)⇒ 没声明地址 —— 这个读法是 server 的,不在判官里(判官对 `""` 答 `not-http-url`)。
454
+ - **判**:非空值调 `pathBaseUrlFlaws`;长度非 0 ⇒ 拒启。运维旋钮律:无条件解析,没接中心的机器也判,形错的值不躺着等哪天生效。拒句点名没过的规则(`httpUrlRule`),**不回显值**(值里可能有凭据)。
455
+ - **中心基址三席同一只**:`WEB_ADMIN_ENV.url` / `liveUrl` / `webUrl` 的值(起服时与请求期)和公告地址都调 `pathBaseUrlFlaws`;server 里原先本地的禁用集副本与公告地址 env 名字面量改读本包后删除,不留第二份。判官读数与改前逐字相同(词与判官同一只)。
456
+
457
+ ### 13.4 本包不做的(如实)
458
+
459
+ - **占位展开**(`$(POD_IP)` 等):是部署后端的事,不是本包的;判官看到的永远是一个字符串。
460
+ - **可达性**:地址真不真打得到是运维的话,判官只判形;引擎不自猜可达地址。
461
+ - **缺席 / 空串的处置**:见 §13.3 —— 读的一方自己答。
462
+ - **座位名册**:一个值算不算「后面要拼路径的基址」是读它的一方的事;本包给的是判这种座位的那一只判官,不维护座位清单(§13 开头列的是今天已知的读者)。
463
+ - **不进保留 env 名表**:`FLEET_ADVERTISE_ADDRESS` 与 generation / scope 两只不同 —— 那两只只有渲染器能写,所以进 `RESERVED_WORKER_ENV`;公告地址是运维可以声明的值(渲染器也可以写),`worker.env` 设它照旧不被拒。
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  llms-full.txt — 自包含全文 · 消费方(使用者)视角 · @sema-agent/settings-schema
3
- 重写日期: 2026-10-23 | 包版本: 14.5.0
3
+ 重写日期: 2026-10-25 | 包版本: 14.7.0
4
4
  生成方式: 无生成脚本——按当前树 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版。
5
5
  证据约定: 每条"它怎么工作"的断言附 (文件: 符号) 坐标,刻意不用行号(行号逐版腐烂;符号名由
6
6
  typecheck 与 test/package-surface.test.ts、test/docs-hygiene.test.ts 等常驻门钉住)。无证据推断显式标【推断】。
@@ -297,8 +297,8 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
297
297
  - `httpHookAllowlists`(src/http-hook-allowlists.ts):类目行 `SAFETY_MERGE_SPEC.httpHookAllowlists`(denyFirst / global / setNarrower / resolver / closed)。
298
298
  `HTTP_HOOK_ALLOWLIST_KEYS`;片段 `HttpHookAllowlistSettings`;层 `HttpHookAllowlistLayer = { source: SettingsSource, 两张名单? }`(只带源名,不带档);
299
299
  层读法 `readHttpHookAllowlistLayer(source, settings)` → `{ layer, malformed }`(坏形 ⇒ `[]` 并点名);
300
- `httpHookUrlVerdict(url, layers, workspace)` → `undefined` | `HttpHookUrlBlock{ list:"allowedHttpHookUrls", rule:"empty"|"url", sources }`:每张**准入**名单
301
- (准入 ⇔ `layerAdmission(源, 工作区读数, "httpHookAllowlists") === "admitted"` —— 源表定档、类目行定地板;闭集外的源 / 读数 RangeError)都须放行 = 跨层取交;
300
+ `httpHookUrlVerdict(url, layers, workspace)`(`layers` 的每一层经读器同一只读法读 —— 读器交出的 `read.layer`、手造的寻常层、本 realm 的 `JSON.parse` 出来的层同答;读不出 `source`〔缺席 / 只靠继承 / 不一致 / 非串〕的层当坏形层全拦、判决词 `malformed` 不点名;源读得出而名单读不出 ⇒ `malformed` 点名那一层;`empty` 只给真空名单)→ `undefined` | `HttpHookUrlBlock{ list:"allowedHttpHookUrls", rule:"malformed"|"empty"|"url", sources }`:每张**准入**名单
301
+ (准入 ⇔ `layerAdmission(源, 工作区读数, "httpHookAllowlists") === "admitted"` —— 源表定档、类目行定地板;闭集外的源 / 读数 RangeError)都须放行 = 跨层取交;名单、`hookVars`、`layers` 都只认朴素数组(原型恒等于本 realm 的 `Array.prototype`〔另一个 realm 的数组按坏形,先 JSON 往返〕、`constructor` / `filter` / `flatMap` / `Symbol.iterator` 是本 realm 的内建、自有键只有下标与 `length`;子类等一律按坏形);
302
302
  `httpHookEnvVarsAllowed(hookVars, layers, workspace)` = hook 自己的 allowedEnvVars ∩ 每张准入名单。与 CC 差:拼接 → 取交、任意层 → 准入层(SS-CCD-06 / 07)。
303
303
  - `mcpServerPolicyVerdict(server, layers)`(src/mcp-server-policy.ts)→ `undefined` | `McpPolicyBlock{ name, list, rule, target, written, sources }`;闭集 `MCP_SERVER_LIST_KEYS` /
304
304
  `MCP_POLICY_RULES`(name / command / url / empty)。层 `McpPolicyLayer = { source: SettingsSource, … }`(只带源名)。拒绝绝对优先、各层都算(名字 → 命令整数组 → URL 拒绝出口);
@@ -345,6 +345,15 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
345
345
  `boolPole{strict,minTrust:"local"}`,严格极写=local、松极/非布尔/无值=global;放宽面/failIfUnavailable/
346
346
  未声明新子键回落类目 floor global = fail-closed);读法 `membersOf`/`memberAdmission`(可带候写值)/
347
347
  `memberMinTrust`(禁自铸表)。
348
+ - 比较(14.6.0):唯一比较器 `tightenVerdict(rule, existing, candidate)` → `TIGHTEN_VERDICTS`(equal / stricter / looser /
349
+ unordered / unreadable);只认序不认拼写:enumRank 序内词、boolStricter 严格布尔(不真值化)、setUnion / setNarrower 字符串
350
+ 数组按集合比、numericLower 有限数或 +Infinity(无上限最松)、none 严格 JSON(深度 ≤ MAX_HASH_DEPTH);域外 / 缺席 ⇒
351
+ unreadable;policyDenyWins ⇒ RangeError。`TightenRule` 尾插无参 `setUnion` / `numericLower`。类目 `tighten` 只答整值单元;
352
+ 声明成员的比较器 `memberTighten` / `resolveMemberTighten` 由行的值类座推出(`list` / `boolPole` 恰一;皆无 / 皆有 ⇒
353
+ RangeError);memberized 类目整值与未声明成员无比较器。
354
+ - settings 单元表(14.6.0,`src/settings-units.ts`,主 barrel):`SETTINGS_UNITS` 十单元(path / category / reads / reading)、
355
+ `unitTighten(unit, member?)`、`readSettingsUnit(unit, raw, member?)`(manual ⇒ default、"disable" ⇒ true、false 仍 false、
356
+ bypassPermissions ⇒ launch_only;表外与 undefined / null ⇒ unreadable;收下原样返回)。
348
357
  - 信任阶梯(1.2.0):`trustRank`(managed 6 > global 5 > project 4 > local 3 > session 2 > task 1)、
349
358
  `isAtLeast`;`MinTrust` 含 `task`(声明序非阶梯,阶梯唯一真源是 `trustRank`)。
350
359
  词表是一只纯常量 `MIN_TRUST_WORDS`(`local, session, project, global, managed, task`;`MinTrust` zod 枚举由它派生):`trustRank` 的闭包自由名 = `MIN_TRUST_WORDS` + JS 内建
@@ -421,6 +430,7 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
421
430
  `HEARTBEAT_REPLY_CAPABILITIES_KEY = "capabilities"`(`WebAdminCapabilities` 的唯一载体 = 心跳 2xx 答复体的这个键,键查找在 `readCapabilityReading` 里;位按调用方凭证答,共享令牌看不到 `servingLease`);
422
431
  env 名常量 `FLEET_GENERATION_ENV` = `"FLEET_GENERATION"`(进 `RESERVED_WORKER_ENV`;渲染器与引擎共用,不各自拼;实例身份 `instanceId`(体的顶层)是引擎每进程自铸的,无 env 名)、
423
432
  `FLEET_SCOPE_ENV` = `"FLEET_SCOPE"`(8.3.0,8.3.1 注改真;成员所属租户 scope,与该成员 per-worker 令牌绑定的 scope 同值,渲染器写、引擎读作自身身份的 scope、只用来比对 `DrainRequest.scope`(请求不带 scope ⇒ 读成默认 scope)、不从令牌格式反推;续租 / 心跳的 scope 由凭证定、不取这只 env,两者同值是渲染器的义务;缺席 = 默认 scope `GLOBAL_SCOPE`;同样进 `RESERVED_WORKER_ENV`);
433
+ 14.7.0 additive(公告地址 env 名;`.` 与 `./fleet`):env 名常量 `FLEET_ADVERTISE_ADDRESS_ENV` = `"FLEET_ADVERTISE_ADDRESS"`(成员对中心公告的可达基址,中心往它后面拼路径;渲染的一方与读的一方都 import,不各自拼;**不**进 `RESERVED_WORKER_ENV`——运维可经 `worker.env` 声明;它的判官见 4.10d);
424
434
  派发(`DISPATCH_STRATEGIES` round-robin|least-inflight|consistent-hash、`DispatchPolicy`、`pickInstance`、
425
435
  `SUBMIT_IDEMPOTENCY_KEY`);配额租约(`QuotaLease`/`leaseRemaining`/`leaseExhausted`/`shouldRenewLease`/
426
436
  `reconcileLease`);用量上报(`UsageEntry`/`UsageReport`/`aggregateUsage`/`QuotaResetEvent`)。
@@ -444,7 +454,8 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
444
454
  四个消费仓对现包名自始至终零 import —— 仅 sdk 的同源锚测试经退役包名 `registry-core` 的冻结版仍取它)。`api/scopes` 同理持有 `SCOPES_V1_PATHS`/`SCOPE_CLAIM`。
445
455
  - `api/auth-bridge`:worker 验 web-admin 签发用户 JWT 的契约——RS256-only、JWKS(`WEB_ADMIN_JWKS_PATH`,
446
456
  kid-miss 强刷 + ≥60s 节流)、时钟偏移 60s(exp / nbf / iat 同一个容忍)、24h 离线宽限后 fail-closed;`VerifierConfig`/`VerifiedIdentity`
447
- (携 `scope?` 与 `iat?`:签发时刻整数秒原样带出,缺席不铸键,在场而非非负整数 ⇒ 该令牌 malformed)/`AuthBridgeErrorCode` + 纯判定 `shouldForceJwksRefresh`/`jwksWithinOfflineGrace`/
457
+ (携 `scope?`、`iat?` 与 `principalInc?`:`iat` 签发时刻整数秒原样带出,缺席不铸键,在场而非非负整数 ⇒ 该令牌 malformed;`principalInc`(14.7.0)= 令牌声明 `PRINCIPAL_INC_CLAIM`(`"sub_inc"`)的化身号,
458
+ 非空串原样,缺席不铸键(从不读成 0),在场而非非空串 ⇒ 该令牌 malformed;`PrincipalIncarnation` 是所有化身号字段共读的那一只 schema)/`AuthBridgeErrorCode` + 纯判定 `shouldForceJwksRefresh`/`jwksWithinOfflineGrace`/
448
459
  `classifyTimeClaims`/`toVerifiedIdentity`/`looksLikeJwt`。
449
460
  - `api/scopes`:多租户 scope 契约——`Scope`/`ScopeMember`(viewer<editor<publisher<admin)、保留
450
461
  `GLOBAL_SCOPE="global"`、JWT `scope` claim(缺席=global);判定纯函数 `resolveTokenScope`/`canSwitchScope`/
@@ -480,6 +491,23 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
480
491
  `retiredEnvRefusal(env)`(纯,读传入的 env;码 `RETIRED_ENV_REFUSAL_CODE` = `config.env_key_retired`;只点名键不回显值);
481
492
  目录 wire 投影 `buildRetiredEnvWire()`。引擎起服 / 壳起本机引擎 / web-admin 渲染 worker env 判同一张表。
482
493
 
494
+ --- 4.10c 沙箱 git 凭据平面(src/sandbox-git.ts + src/api/credentials.ts,主 barrel,不开子路径;14.7.0) ---
495
+ - 沙箱里的 git 用成员本人的凭据:数据面向 web-admin 的凭据取值口按 run 身份申领句柄、凭句柄兑换。本包只有形与词表,零 IO。
496
+ - origin 规范形:`isSandboxGitOrigin(raw)` —— 串逐字等于 `new URL(raw).origin` 且 scheme ∈ {http, https}(小写 host、去缺省端口、无尾斜杠、无 userinfo / 路径 / 查询 / 片段都是这一个等式的推论;origin 不表达子路径部署,无「削成 origin」的规范化器);
497
+ zod 形 `SandboxGitOrigin` / `SandboxGitOriginList`(≤ `SANDBOX_GIT_MAX_ORIGINS` = 16、两两不同、空表合法)。
498
+ - 有效配置块 `sandboxGit`:`SandboxGitConfig` = `{ origins: [{ origin, connectUrl }] }`(`connectUrl` 过 `SandboxGitConnectUrl` = 非秘密 https URL)。`/effective` 载荷上的**非域顶层键**,可选、无默认;`readEffectiveWire` 逐字承运(`EffectiveWire.sandboxGit` 是 `unknown`),
499
+ 消费方用 `SandboxGitConfig` 判。块里 http origin 可读(它的拒因是逐 origin 应答里的 `insecure_transport`,不是块的校验拒)。
500
+ - 取值口 wire(`kind: "sandbox_git"`):`SandboxGitCredentialRequest`(申领)/ `CredentialHandleAnswer` / `CredentialResolveRequest`(与种类无关)/ `SandboxGitCredentialAnswer`(每 origin 一行 `SandboxGitOriginAnswer`,按 `state` 判别;
501
+ `secret` 只在 `active` 臂,别的臂剥掉;`needs_reauth` 行必带 `credentialId`)/ `SandboxGitCredentialRefusal`(`{ error, permanence, message }`,`permanence` 必须等于拒答表里这一行的)。解析一律剥未知键。
502
+ - 三张词表,各一张、每个词只在 `src/api/credentials.ts` 里拼一次:`SANDBOX_GIT_ORIGIN_VERDICTS`(逐 origin 判定序表 11 行,序即判定序;`SANDBOX_GIT_ORIGIN_STATES` 六词、`SANDBOX_GIT_ORIGIN_REASONS` 十词与应答臂的原因值域由它派生)·
503
+ `SANDBOX_GIT_REFUSALS`(十个整次拒答词 → HTTP 状态集 + `permanence`;`SANDBOX_GIT_REFUSAL_ERRORS` 由它派生)· `SANDBOX_GIT_PERMANENCE`(`terminal` / `transient`)。两方各自怎么读:`docs/CONSUMER-INTEGRATION.md` §12。
504
+
505
+ --- 4.10d 「后面要拼路径的基址」座位判官(src/url-shape.ts,主 barrel;14.7.0) ---
506
+ - 一种座位的值是别人要往它后面拼路径的基址(中心基址三席、成员对中心公告的地址 `FLEET_ADVERTISE_ADDRESS_ENV`、会话链接的基址):不许带凭据 / query / fragment,路径前缀放行。
507
+ - `PATH_BASE_URL_FORBIDDEN_PARTS`:这种座位禁用的 URL 成分,闭集、冻结、判序 `userinfo` · `query` · `fragment`(策略集,独立于判官词表 `HTTP_URL_PARTS`:词表说 URL 有哪些成分,本集说这种座位禁哪些;今天碰巧同三词,不由词表派生,词表加词时要显式裁)。
508
+ - `pathBaseUrlFlaws(url)` = `httpUrlFlaws(url, PATH_BASE_URL_FORBIDDEN_PARTS)`:`[]` = 合格,否则按判序交回规则词(`not-http-url` / `userinfo` / `query` / `fragment`),不含值的片段;
509
+ 判占位展开之后的值(`http://$(POD_IP):8090` 过,`http://[$(POD_IP)]:8090` 拒);`webAdminSessionUrl` 也读它。占位展开、可达性、缺席 / 空串的处置不在本包;两方读法见 `docs/CONSUMER-INTEGRATION.md` §13。
510
+
483
511
  --- 4.11 Node-only(`./node`:file-store / file-edit / bundle / local-load / scheduler-store-node / 目录迁移) ---
484
512
  - `loadLocalConfig(root, {onWarning}?)` → `LocalConfig { effective, remoteExec, env, requiredEnv, warnings }`。
485
513
  - `loadRemoteExec(root)`(三态,§8 T3)、`writeRemoteExec`(校验+原子写)、`remoteExecPath`、`loadEnvFile`
@@ -650,7 +678,8 @@ R5. fleet 配置种到本地/反向:`exportBundle`/`writeBundle`/`readBundle`(re
650
678
  R6. 复用 secret 边界:直接用 `ENV_NAME`/`NON_SECRET_URL`/`containsSecretToken`,别自写正则。
651
679
  R7. ETag/漂移检测:`stableHash`;skills 内容寻址:`skillContentHash`/`sha256Hex`(prompts 的身份是引擎铸的 `artifactDigest`,本包不铸)。
652
680
  R8. 消费端读 wire:`readEffectiveWire(raw, onWarning)`(容错极性是裁定过的语义,别自己 parse 全量域)。
653
- R9. settings resolver:类别合并律读 `SAFETY_MERGE_SPEC` + `trustRank`/`isAtLeast`/`memberAdmission`,层准入读 `layerAdmission` +
681
+ R9. settings resolver:类别合并律读 `SAFETY_MERGE_SPEC` + `trustRank`/`isAtLeast`/`memberAdmission`,「谁更严」读 `tightenVerdict` +
682
+ `unitTighten` / `memberTighten`,单元与原值读法读 `SETTINGS_UNITS` / `readSettingsUnit`(14.6.0),层准入读 `layerAdmission` +
654
683
  `SETTINGS_SOURCE_CONTRACT`(查口 `settingsSourceContract`),禁自铸 rank/成员/层档表;hooks 坏形读 `classifyHooksConfig`,禁自铸判官。
655
684
 
656
685
  ================================================================================
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
3
- 重写日期: 2026-10-23 | 包版本: 14.5.0
3
+ 重写日期: 2026-10-25 | 包版本: 14.7.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 相等、
@@ -97,7 +97,9 @@ llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
97
97
  (src/cross-session.ts: `CrossSessionSettings` / `readCrossSessionInbound` / `readDialogExpiry`)
98
98
  - **安全合并表** `SAFETY_MERGE_SPEC` + 信任阶梯 `trustRank` / `isAtLeast`(词表来源 = 纯常量 `MIN_TRUST_WORDS`,闭包零 zod)+ 成员级准入 `memberAdmission`;关停位 `disableBypassPermissionsMode` / `disableAutoMode`
99
99
  = 类目 `killSwitches`(尾插):任一层可收紧、不放宽 —— 开 = tighten@local(任一设置文件层),关 / 非布尔 / 不带值 = 行地板 global,合并 boolStricter;没有「只许托管层」的地板。
100
- (src/safety-merge-spec.ts)
100
+ 14.6.0:唯一比较器 `tightenVerdict(rule, existing, candidate)`(判词闭集 `TIGHTEN_VERDICTS`;只认序不认拼写,域外 / 缺席 ⇒ `unreadable`;`TightenRule` 尾插无参 `setUnion` / `numericLower`);
101
+ 成员比较器 `memberTighten` 由成员行值类座推出(`MemberAdmission.list` / `boolPole`,每行恰一);settings 单元表 `SETTINGS_UNITS` + `unitTighten` + `readSettingsUnit`(单元读法,表外一切 `unreadable`)。
102
+ (src/safety-merge-spec.ts · src/settings-units.ts)
101
103
  - **设置源表 + 层准入**:`SETTINGS_SOURCES`(合并序)/ `SETTINGS_SOURCE_CONTRACT`(信任档 + 是否仓库可控;本地层与项目层同 `project` 档)/
102
104
  `WORKSPACE_TRUST_STATES` / `layerAdmission(source, workspace, category, member?, value?)`(地板先判;仓库可控层未受信只收 tighten)/
103
105
  `settingsSourceContract(source)`(源表唯一查口:本包要层档的函数都经它,不收调用方给的档;层只带源名)。
@@ -108,7 +110,7 @@ llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
108
110
  http/https/ws/wss 且 `ws+unix:` 套接字段逐字、解析不出的模式不放、解码出的字面 `*` 不当通配)/ `urlDenyPatternMatches`(⊇ `g9` ∪ 整串 glob)。
109
111
  偏离登 `docs/CC-DIVERGENCE.md`。(src/url-pattern.ts)
110
112
  - **http hook 两张名单**(13.0.0):类目 `SAFETY_MERGE_SPEC.httpHookAllowlists`(地板 global);`readHttpHookAllowlistLayer(source, settings)`(坏形读成 `[]`)·
111
- `httpHookUrlVerdict(url, layers, workspace)` → `HttpHookUrlBlock{list, rule, sources}` 或 `undefined`(每张准入名单都须放行 = 跨层取交)·
113
+ `httpHookUrlVerdict(url, layers, workspace)`(`layers` 的每一层经读器同一只读法读 —— 读器交出的 `read.layer`、手造的寻常层、本 realm 的 `JSON.parse` 出来的层同答;另一个 realm 的数组按坏形全拦)→ `HttpHookUrlBlock{list, rule, sources}` 或 `undefined`(每张准入名单都须放行 = 跨层取交)·
112
114
  `httpHookEnvVarsAllowed(hookVars, layers, workspace)`(取交);准入 ⇔ `layerAdmission(源, 工作区读数, "httpHookAllowlists") === "admitted"`。
113
115
  (src/http-hook-allowlists.ts)
114
116
  - **MCP 名单判决**(13.0.0):`mcpServerPolicyVerdict(server, layers)` → `McpPolicyBlock{name, list, rule, target, written, sources}` 或 `undefined`;
@@ -139,6 +141,7 @@ llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
139
141
  判官只读信号:`judgeReadiness(signal)` / `judgeRoutability(row, nowMs)` → `{ routable }` 或 `{ routable:false, code ∈ NOT_ROUTABLE_CODES, why }`,
140
142
  `isReadyForTraffic` / `isRoutable` 是它们的布尔形;没有信号(旧引擎)= `no-engine-signal`,不可路由;布尔对 `draining` / `drainingSince` 标 `@deprecated`、本包零读点、退役已登记;
141
143
  8.3.0 additive:env 名常量 `FLEET_SCOPE_ENV`(`"FLEET_SCOPE"`,成员所属租户 scope,渲染器写、引擎读;缺席 = 默认 scope `GLOBAL_SCOPE`;进保留 env 名表);
144
+ 14.7.0 additive:公告地址 env 名常量 `FLEET_ADVERTISE_ADDRESS_ENV`(`"FLEET_ADVERTISE_ADDRESS"`)+「后面要拼路径的基址」座位的禁用成分集 `PATH_BASE_URL_FORBIDDEN_PARTS`(`userinfo` / `query` / `fragment`)+ 判官 `pathBaseUrlFlaws(url)`(= `httpUrlFlaws` 配这一集;判展开之后的值;`webAdminSessionUrl` 也读它);
142
145
  9.0.0 BREAKING:`readCenterCapabilities` 删,换三态读器 `readCapabilityReading(reply): CapabilityReading`(`declared` | `undeclared` | `unreadable`;键在场而形坏 ≠ 没宣告)、`EngineSignal.configTargetVersion` / `configAppliedVersion` 由串改数)、**scheduler-store 契约**(自唤醒
143
146
  存储形 + node 绑定)、**auth / auth-bridge / scopes / wire** 契约(RFC 8628 设备流、RS256 JWKS 鉴权桥、
144
147
  多租户 scope)。(src/fleet.ts;src/scheduler-store*.ts;src/api/*.ts)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/settings-schema",
3
- "version": "14.5.0",
3
+ "version": "14.7.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",