@sema-agent/server 7.151.0 → 7.152.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 (286) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +15 -3
  3. package/README.zh-CN.md +10 -2
  4. package/USAGE.md +93 -64
  5. package/deploy/sema-up/README.md +5 -4
  6. package/deploy/sema-up/cluster-up.sh +6 -6
  7. package/deploy/sema-up/kube-up.sh +5 -5
  8. package/deploy/sema-up/lib/flagshape.sh +36 -8
  9. package/deploy/sema-up/machines.example.yaml +3 -1
  10. package/deploy/sema-up/sema-up.sh +22 -16
  11. package/dist/approval-ask-audit-store.d.ts +1 -1
  12. package/dist/approval-ask-machine.d.ts +4 -10
  13. package/dist/approval-ask-retention.d.ts +1 -1
  14. package/dist/approval-card.d.ts +10 -12
  15. package/dist/approval-gate-currency.d.ts +8 -8
  16. package/dist/approval-gate-currency.js +3 -4
  17. package/dist/approval-hmac.d.ts +17 -1
  18. package/dist/approval-hmac.js +7 -1
  19. package/dist/approval-queue-kind.d.ts +1 -1
  20. package/dist/approval.d.ts +2 -2
  21. package/dist/approval.js +2 -2
  22. package/dist/bind-host.d.ts +1 -3
  23. package/dist/bind-host.js +2 -3
  24. package/dist/boot/coordinators.d.ts +1 -1
  25. package/dist/boot/coordinators.js +9 -10
  26. package/dist/boot/device-lane.js +2 -0
  27. package/dist/boot/execution-env.d.ts +1 -0
  28. package/dist/boot/execution-env.js +31 -5
  29. package/dist/boot/leader.d.ts +31 -30
  30. package/dist/boot/leader.js +38 -28
  31. package/dist/boot/leg-path-adjudication.d.ts +40 -0
  32. package/dist/boot/leg-path-adjudication.js +18 -0
  33. package/dist/boot/parked-revive-gate.js +3 -15
  34. package/dist/boot/reapers.d.ts +1 -1
  35. package/dist/boot/reapers.js +1 -1
  36. package/dist/boot/resolve-spec.js +12 -26
  37. package/dist/boot/runner-deps.d.ts +39 -35
  38. package/dist/boot/runner-deps.js +92 -89
  39. package/dist/boot/shutdown.d.ts +6 -0
  40. package/dist/boot/shutdown.js +10 -4
  41. package/dist/boot/stage-02-web-admin-pull.js +4 -5
  42. package/dist/boot/stage-05-execution-env.js +2 -1
  43. package/dist/boot/stage-06-runners.d.ts +2 -0
  44. package/dist/boot/stage-06-runners.js +10 -4
  45. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  46. package/dist/boot/stage-07-capability-layer.js +1 -1
  47. package/dist/boot/stage-08-reapers.d.ts +1 -0
  48. package/dist/boot/stage-09-leader.d.ts +1 -0
  49. package/dist/boot/stage-09-leader.js +2 -2
  50. package/dist/boot/stage-10-http-server.d.ts +8 -0
  51. package/dist/boot/stage-10-http-server.js +11 -15
  52. package/dist/boot/stores.js +7 -4
  53. package/dist/boot/web-admin.js +2 -7
  54. package/dist/capabilities/approvals-push.d.ts +3 -3
  55. package/dist/capabilities/scenarios.d.ts +28 -13
  56. package/dist/capabilities/scenarios.js +2 -2
  57. package/dist/capability-facts.d.ts +22 -20
  58. package/dist/capability-facts.js +18 -34
  59. package/dist/card-rule-offers.d.ts +32 -0
  60. package/dist/card-rule-offers.js +130 -0
  61. package/dist/config-catalog.js +12 -11
  62. package/dist/config-dir-trust.js +4 -2
  63. package/dist/config-invariants.d.ts +18 -7
  64. package/dist/config-invariants.js +51 -8
  65. package/dist/config-lkg.d.ts +5 -19
  66. package/dist/config-lkg.js +8 -62
  67. package/dist/config-provider.d.ts +1 -1
  68. package/dist/config-root-migration.d.ts +2 -2
  69. package/dist/config-types.d.ts +28 -38
  70. package/dist/config.d.ts +7 -5
  71. package/dist/config.js +55 -16
  72. package/dist/control-socket.d.ts +51 -0
  73. package/dist/control-socket.js +115 -0
  74. package/dist/deployment-governance.d.ts +2 -1
  75. package/dist/device-ws-hub.d.ts +7 -0
  76. package/dist/device-ws-hub.js +7 -1
  77. package/dist/elicitation.js +2 -2
  78. package/dist/entry-problems.d.ts +40 -3
  79. package/dist/entry-problems.js +3 -1
  80. package/dist/execution-env-refusal.d.ts +8 -2
  81. package/dist/execution-env-refusal.js +3 -0
  82. package/dist/fleet/engine-signal.d.ts +12 -29
  83. package/dist/fleet/engine-signal.js +1 -7
  84. package/dist/fleet-client.js +2 -5
  85. package/dist/gate-settlement.d.ts +1 -8
  86. package/dist/gate-settlement.js +0 -4
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +2 -3
  89. package/dist/hitl-delivery.js +0 -2
  90. package/dist/hooks/hook-llm.js +4 -2
  91. package/dist/hooks/hook-runner.d.ts +3 -2
  92. package/dist/hooks/hook-runner.js +16 -1
  93. package/dist/host-decision.d.ts +11 -11
  94. package/dist/http/active-run-conflict.d.ts +35 -2
  95. package/dist/http/active-run-conflict.js +48 -19
  96. package/dist/http/approval-sweeps.d.ts +1 -1
  97. package/dist/http/approval-sweeps.js +5 -2
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +6 -2
  101. package/dist/http/dispatch.js +25 -5
  102. package/dist/http/durable-run-leg.js +0 -1
  103. package/dist/http/health-reader.d.ts +16 -9
  104. package/dist/http/health-reader.js +2 -1
  105. package/dist/http/health-self-report.js +1 -1
  106. package/dist/http/owner-access.d.ts +2 -3
  107. package/dist/http/owner-access.js +3 -4
  108. package/dist/http/principal-gate.d.ts +49 -21
  109. package/dist/http/principal-gate.js +61 -11
  110. package/dist/http/request-channel.d.ts +23 -0
  111. package/dist/http/request-channel.js +9 -0
  112. package/dist/http/request-guards.js +17 -2
  113. package/dist/http/resume-ctx-read.js +20 -3
  114. package/dist/http/resume-legs.d.ts +2 -2
  115. package/dist/http/resume-legs.js +111 -41
  116. package/dist/http/route-ctx.d.ts +73 -7
  117. package/dist/http/route-ctx.js +17 -0
  118. package/dist/http/route-table.d.ts +4 -0
  119. package/dist/http/route-table.js +4 -0
  120. package/dist/http/routes/admin-config-refresh.js +3 -3
  121. package/dist/http/routes/admin-drain.d.ts +3 -0
  122. package/dist/http/routes/admin-drain.js +7 -7
  123. package/dist/http/routes/admin-memory-optout.js +3 -3
  124. package/dist/http/routes/adoption.js +3 -5
  125. package/dist/http/routes/approvals-assistant.d.ts +2 -1
  126. package/dist/http/routes/approvals-assistant.js +95 -48
  127. package/dist/http/routes/capabilities.d.ts +4 -0
  128. package/dist/http/routes/capabilities.js +30 -5
  129. package/dist/http/routes/config-catalog.js +2 -2
  130. package/dist/http/routes/devices.js +1 -1
  131. package/dist/http/routes/diagnostics.d.ts +3 -3
  132. package/dist/http/routes/diagnostics.js +19 -7
  133. package/dist/http/routes/images.js +10 -10
  134. package/dist/http/routes/memory-bundle.js +3 -3
  135. package/dist/http/routes/memory-compliance.js +3 -3
  136. package/dist/http/routes/memory-consolidation.js +2 -2
  137. package/dist/http/routes/memory-origin.js +3 -3
  138. package/dist/http/routes/memory-policy.js +2 -2
  139. package/dist/http/routes/observability.js +3 -3
  140. package/dist/http/routes/retention-ops.js +3 -3
  141. package/dist/http/routes/rules.js +5 -5
  142. package/dist/http/routes/run-permission-mode.d.ts +2 -1
  143. package/dist/http/routes/run-permission-mode.js +2 -2
  144. package/dist/http/routes/runs.d.ts +4 -9
  145. package/dist/http/routes/runs.js +73 -33
  146. package/dist/http/routes/session-sync.d.ts +18 -6
  147. package/dist/http/routes/session-sync.js +38 -36
  148. package/dist/http/routes/sessions.js +20 -9
  149. package/dist/http/routes/tasks.js +28 -37
  150. package/dist/http/routes/workflows.js +1 -1
  151. package/dist/http/server.d.ts +9 -3
  152. package/dist/http/server.js +15 -3
  153. package/dist/http/submit-ctx.d.ts +1 -1
  154. package/dist/http/submit-ctx.js +2 -0
  155. package/dist/human-facing-args.d.ts +33 -0
  156. package/dist/human-facing-args.js +33 -0
  157. package/dist/index.d.ts +1 -1
  158. package/dist/k8s-api-credentials.d.ts +29 -0
  159. package/dist/k8s-api-credentials.js +25 -0
  160. package/dist/lane-home-probe.d.ts +1 -1
  161. package/dist/lane-home-probe.js +2 -1
  162. package/dist/leader/diffup.d.ts +19 -0
  163. package/dist/leader/diffup.js +7 -0
  164. package/dist/leader/leader.d.ts +114 -10
  165. package/dist/leader/leader.js +69 -5
  166. package/dist/leader/repair-wire.js +2 -2
  167. package/dist/leader/run-entry.d.ts +2 -1
  168. package/dist/leader/run-entry.js +1 -1
  169. package/dist/leader/wire.d.ts +42 -132
  170. package/dist/leader/wire.js +81 -67
  171. package/dist/memory-scope.d.ts +20 -9
  172. package/dist/memory-scope.js +15 -3
  173. package/dist/model-entry-headers.d.ts +9 -10
  174. package/dist/model-entry-headers.js +0 -4
  175. package/dist/model-entry-refusal.d.ts +7 -4
  176. package/dist/model-entry-refusal.js +11 -5
  177. package/dist/model-plane-face.d.ts +1 -1
  178. package/dist/model-plane-face.js +3 -1
  179. package/dist/model-route-endpoint.d.ts +13 -7
  180. package/dist/model-route-endpoint.js +14 -11
  181. package/dist/model-select.d.ts +22 -0
  182. package/dist/model-select.js +9 -0
  183. package/dist/model-unreachable-notice.d.ts +11 -0
  184. package/dist/model-unreachable-notice.js +4 -4
  185. package/dist/object-store-buckets.d.ts +640 -6
  186. package/dist/object-store-buckets.js +166 -0
  187. package/dist/observability/fail-open.d.ts +20 -16
  188. package/dist/observability/fail-open.js +20 -16
  189. package/dist/observability/logger.js +3 -1
  190. package/dist/operator-roster.d.ts +68 -0
  191. package/dist/operator-roster.js +32 -0
  192. package/dist/orchestration/workflow-park-decision.d.ts +1 -1
  193. package/dist/outbound-dispatcher.d.ts +71 -8
  194. package/dist/outbound-dispatcher.js +48 -7
  195. package/dist/owner-faces.d.ts +1 -1
  196. package/dist/parked-decide.d.ts +2 -2
  197. package/dist/permission-mode-admission.d.ts +59 -26
  198. package/dist/permission-mode-admission.js +37 -10
  199. package/dist/plugins/approval-ask-store-file.d.ts +4 -0
  200. package/dist/plugins/approval-ask-store-file.js +3 -0
  201. package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
  202. package/dist/plugins/approval-ask-store-memory.js +7 -1
  203. package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
  204. package/dist/plugins/approval-ask-store-sql.js +12 -1
  205. package/dist/plugins/blob-backend.d.ts +83 -7
  206. package/dist/plugins/blob-backend.js +57 -6
  207. package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
  208. package/dist/plugins/checkpoint-index-columns.js +3 -6
  209. package/dist/plugins/checkpoint-store-face.d.ts +119 -61
  210. package/dist/plugins/checkpoint-store-face.js +134 -55
  211. package/dist/plugins/landed-attendants.d.ts +2 -2
  212. package/dist/plugins/local-checkpoint-store.d.ts +13 -3
  213. package/dist/plugins/local-checkpoint-store.js +38 -49
  214. package/dist/plugins/one-time-migrations.d.ts +51 -21
  215. package/dist/plugins/one-time-migrations.js +67 -45
  216. package/dist/plugins/pg-pool.js +11 -9
  217. package/dist/plugins/remote-env-host.d.ts +29 -10
  218. package/dist/plugins/remote-env-host.js +99 -19
  219. package/dist/plugins/remote-env-k8s.d.ts +68 -2
  220. package/dist/plugins/remote-env-k8s.js +107 -24
  221. package/dist/plugins/replica-registration.d.ts +1 -1
  222. package/dist/plugins/replica-registration.js +1 -1
  223. package/dist/plugins/s3-presign.d.ts +23 -0
  224. package/dist/plugins/s3-presign.js +37 -3
  225. package/dist/plugins/scheduler-support.d.ts +2 -2
  226. package/dist/plugins/scheduler-support.js +0 -3
  227. package/dist/plugins/schema-indexes.js +2 -0
  228. package/dist/plugins/send-file-ledger.d.ts +2 -2
  229. package/dist/plugins/send-file-ledger.js +2 -2
  230. package/dist/plugins/send-user-file.d.ts +3 -4
  231. package/dist/plugins/send-user-file.js +56 -52
  232. package/dist/plugins/session-blob-claim-token.d.ts +15 -0
  233. package/dist/plugins/session-blob-claim-token.js +20 -0
  234. package/dist/plugins/session-blob-namespace.d.ts +46 -0
  235. package/dist/plugins/session-blob-namespace.js +116 -0
  236. package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
  237. package/dist/plugins/snapshot-bucket-presence.js +8 -2
  238. package/dist/plugins/sql-json-column.d.ts +1 -1
  239. package/dist/plugins/store-backend.d.ts +44 -2
  240. package/dist/plugins/store-backend.js +75 -53
  241. package/dist/plugins/task-attachment-store.d.ts +2 -1
  242. package/dist/plugins/task-attachment-store.js +5 -1
  243. package/dist/plugins/tidb-pool.js +19 -24
  244. package/dist/plugins/workspace-custody.d.ts +4 -3
  245. package/dist/question.d.ts +2 -1
  246. package/dist/question.js +5 -13
  247. package/dist/request-form.d.ts +6 -1
  248. package/dist/request-form.js +4 -1
  249. package/dist/rule-issue-reading.d.ts +1 -1
  250. package/dist/rules-consent.d.ts +1 -1
  251. package/dist/run-local.js +8 -9
  252. package/dist/runs.d.ts +17 -8
  253. package/dist/runs.js +15 -2
  254. package/dist/runtime-governance.d.ts +2 -2
  255. package/dist/sema-config-dir.d.ts +2 -1
  256. package/dist/session-sync.d.ts +27 -11
  257. package/dist/session-sync.js +15 -7
  258. package/dist/stores-sql/blob-index-sql.d.ts +30 -9
  259. package/dist/stores-sql/blob-index-sql.js +40 -21
  260. package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
  261. package/dist/stores-sql/checkpoint-store-sql.js +70 -146
  262. package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
  263. package/dist/stores-sql/file-history-store-sql.js +100 -28
  264. package/dist/stores-sql/retention-store-sql.js +3 -9
  265. package/dist/task-settings.d.ts +6 -5
  266. package/dist/task-settings.js +3 -3
  267. package/dist/tool-approval.d.ts +342 -518
  268. package/dist/tool-approval.js +498 -1207
  269. package/dist/trace/core-keyset-guard.d.ts +1 -1
  270. package/dist/trace/ledger-events.d.ts +3 -3
  271. package/dist/trace/project.d.ts +2 -3
  272. package/dist/trace/redact.js +3 -2
  273. package/dist/trace/self-minted-wire.d.ts +287 -157
  274. package/dist/trace/self-minted-wire.js +93 -48
  275. package/dist/web-admin-client/apply-effective.js +2 -2
  276. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  277. package/dist/web-admin-client/read-warnings.js +1 -1
  278. package/dist/web-admin-client/types.d.ts +1 -1
  279. package/dist/workspace-space.d.ts +10 -4
  280. package/dist/workspace-space.js +4 -1
  281. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
  282. package/package.json +6 -2
  283. package/scripts/embed-minimal.mjs +3 -3
  284. package/wire-changes/server@7.152.0.json +900 -0
  285. package/dist/config-d-migration.d.ts +0 -37
  286. package/dist/config-d-migration.js +0 -60
@@ -5,8 +5,8 @@ import { DEVICE_RELAY_NONE, WEB_ADMIN_ENV, DOMAIN_SCHEMAS, buildRetiredEnvWire }
5
5
  import { CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS, CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS, CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS } from "./center-credential-renewer.js";
6
6
  import { SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS } from "./sandbox-egress-policy.js";
7
7
  import { WORKSPACE_CACHE_DEFAULT, WORKSPACE_CACHE_MOUNT_DEFAULT, WORKSPACE_CACHE_SIZE_DEFAULT, WORKSPACE_CACHE_WORDS } from "./workspace-cache.js";
8
- import { SESSION_SNAPSHOT_BUCKET_DEFAULT, WORKSPACE_SNAPSHOT_BUCKET_DEFAULT } from "./object-store-buckets.js";
9
- import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_PREFIX_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE } from "./workspace-space.js";
8
+ import { LEGACY_BLOB_ROOT_DEFAULT, S3_PUBLIC_BUCKET_DEFAULT, SESSION_SNAPSHOT_BUCKET_DEFAULT, SIGV4_PRESIGN_MAX_SEC, WORKSPACE_SNAPSHOT_BUCKET_DEFAULT, WORKSPACE_SNAPSHOT_PREFIX_DEFAULT, objectStoreActionOf } from "./object-store-buckets.js";
9
+ import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE } from "./workspace-space.js";
10
10
  import { APPROVAL_ASK_RETENTION_DEFAULT_MS, APPROVAL_ASK_RETENTION_FLOOR_MS } from "./approval-ask-retention.js";
11
11
  import { SANDBOX_SECRET_SEAT_VERBS } from "./plugins/k8s-exec-protocol.js";
12
12
  import { ADOPTION_IMPORT_MAX_SESSIONS_DEFAULT, ADOPTION_IMPORT_MAX_SESSIONS_FLOOR, ADOPTION_IMPORT_MAX_SESSION_BYTES_CEILING, ADOPTION_IMPORT_MAX_SESSION_BYTES_DEFAULT, ADOPTION_IMPORT_MAX_SESSION_BYTES_FLOOR } from "./adoption/import-limits.js";
@@ -24,6 +24,7 @@ import { buildDisplaySafeUrlChecked } from "./trace/redact.js";
24
24
  import { describeAllowlistPostures } from "./env-name-allowlist-knobs.js";
25
25
  import { buildOutboundProxyPosture } from "./outbound-dispatcher.js";
26
26
  import { modelImageInput, modelReasoningDecl } from "./model-select.js";
27
+ const BLOB_TTL_DEFAULT_SEC = ((ttl) => (ttl === "unsigned" ? undefined : ttl.defaultSec))(objectStoreActionOf("file-history/blob:GET").ttl);
27
28
  export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
28
29
  export const CATALOG_TIMINGS = ["hot", "restart", "deploy", "inert"];
29
30
  export const CATALOG_DANGER_AXES = ["security", "billing", "data"];
@@ -384,8 +385,9 @@ export const CONFIG_CATALOG = [
384
385
  r("MYSQL_PORT", "store", "number", "MySQL 协议端口", { staticDefault: "3306" }),
385
386
  r("MYSQL_USER", "store", "string", "MySQL 协议用户", { danger: DATA }),
386
387
  r("NO_PROXY", "limitsHttp", "csv", "不走代理的目标(小写 `no_proxy` 同认且优先);语法 = undici 的:逗号或空白分隔,`host` 精确且含其子域、`.host` / `*.host` 后缀、`host:port` 限端口、`*` 全直连;**回环 / 未指定地址目标无论写没写都直连**(本仓的无条件规则,不是 NO_PROXY 的一条);boot 冻结", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 除回环外一律按 HTTP(S)_PROXY 走", resolve: (_c, env) => buildOutboundProxyPosture(env).noProxy }),
387
- r("OPERATOR_PRINCIPALS", "auth", "csv", "operator 名单(全部运维面同一条读法:名单内的验过主体是运维;空名单 = 谁都不是;与 OPERATOR_SINGLE_USER 互斥;单用户部署与 OPERATOR_SINGLE_USER 二者必写其一,都不写则拒启 —— 服务进程起服第 1 段即拒,直接调 `createHttpServer` 的嵌入方装配时拒)", { danger: SEC, configKey: "operatorPrincipals" }),
388
- r("OPERATOR_SINGLE_USER", "auth", "boolean", "单用户形当自己运维的显式声明(true ⇒ 这台部署的每个调用方在全部运维面都是运维;只在 REQUIRE_PRINCIPAL 未开且 OPERATOR_PRINCIPALS 为空时成立,否则拒启;单用户部署与具名 OPERATOR_PRINCIPALS 二者必写其一 —— 都不写则拒启(服务进程起服第 1 段;嵌入方装配运维面时),false 与缺席同判;起服冻结)", { staticDefault: "false", danger: SEC, configKey: "operatorSingleUser" }),
388
+ r("OPERATOR_DRAIN_PRINCIPALS", "auth", "csv", "名册的排空档 —— 只持排空一权的服务主体名单(名单内的验过主体可调 POST /v1/admin/drain,不是人:它的身份在其余每条路由上整次请求 403 auth.principal_confined,GET /health 按没出示凭据分档,它名下登记过的设备在 GET /v1/device/ws 的 hello 被拒 auth_failed;空 / 缺席 = 这一档没人;与 OPERATOR_PRINCIPALS 同一个名字 ⇒ 拒启;与 OPERATOR_SINGLE_USER 互斥;不算运维声明;起服冻结)", { danger: SEC, configKey: "operatorDrainPrincipals" }),
389
+ r("OPERATOR_PRINCIPALS", "auth", "csv", "operator 名单 —— 名册的全运维档(全部运维面同一条读法:名单内的验过主体是运维,持每一只运维权力,排空在内;空名单 = 谁都不是;与 OPERATOR_DRAIN_PRINCIPALS 不相交;与 OPERATOR_SINGLE_USER 互斥;单用户部署与 OPERATOR_SINGLE_USER 二者必写其一,都不写则拒启 —— 服务进程起服第 1 段即拒,直接调 `createHttpServer` 的嵌入方装配时拒)", { danger: SEC, configKey: "operatorPrincipals" }),
390
+ r("OPERATOR_SINGLE_USER", "auth", "boolean", "单用户形当自己运维的显式声明(true ⇒ 这台部署的每个调用方在全部运维面都是运维;只在 REQUIRE_PRINCIPAL 未开且 OPERATOR_PRINCIPALS 与 OPERATOR_DRAIN_PRINCIPALS 都为空时成立,否则拒启;单用户部署与具名 OPERATOR_PRINCIPALS 二者必写其一 —— 都不写则拒启(服务进程起服第 1 段;嵌入方装配运维面时),false 与缺席同判;起服冻结)", { staticDefault: "false", danger: SEC, configKey: "operatorSingleUser" }),
389
391
  r("OTEL_EXPORTER_OTLP_ENDPOINT", "observability", "url", "OTLP 出口(设了=开 otel)", { credentialPolicy: "mask" }),
390
392
  r("OTEL_EXPORTER_OTLP_HEADERS", "observability", "secret", "OTLP 出口头(k=v CSV;常载鉴权头 ⇒ 按凭据处置)", { credentialForm: "whole", danger: SEC }),
391
393
  r("OTEL_EXPORT_INTERVAL_MS", "observability", "number", "OTLP 批报周期(ms)", { staticDefault: "15000" }),
@@ -436,8 +438,8 @@ export const CONFIG_CATALOG = [
436
438
  r("RUN_MAX_CONCURRENT", "orchestration", "number", "本副本在飞 run 的全局并发帽(0 = 关;整数,坏值拒启):提交门(POST /v1/runs · A2A message/send · POST /v1/tasks · POST /v1/tasks/stream)在幂等重放之后判在飞数,已到帽 ⇒ 429 limit.rate_exceeded + reason run_concurrency_cap + Retry-After: 5;续跑 / 唤醒 / 审批作答不判", { staticDefault: "0", configKey: "runMaxConcurrent" }),
437
439
  r("RUN_MAX_CONCURRENT_PER_PRINCIPAL", "orchestration", "number", "同一已验提交主体在本副本的在飞 run 并发帽(0 = 关;整数,坏值拒启;无主体请求不判这一帽,只受 RUN_MAX_CONCURRENT)", { staticDefault: "0", configKey: "runMaxConcurrentPerPrincipal" }),
438
440
  r("RUN_STORE_STRICT_HYDRATE", "store", "enum", "file run-store 列不出 runs/ 时拒启的 fail-closed 门(缺省带病起动+响亮披露)", { enumValues: ["on", "off"], staticDefault: "off", danger: DATA, configKey: "runStoreStrictHydrate", booleanEnum: { true: "on", false: "off" } }),
439
- r("S3_ENDPOINT", "store", "url", "外接 S3 兼容位(单独在场即开公链签发,兼作公网面)", { credentialPolicy: "mask", danger: DATA }),
440
- r("S3_PUBLIC_BUCKET", "store", "string", "SendUserFile 公链桶", { derivedDefaultNote: "缺席 ⇒ sema-public" }),
441
+ r("S3_ENDPOINT", "store", "url", "外接 S3 兼容位(MINIO_ENDPOINT 缺席时作 SendUserFile 的对象存储端点,与 MINIO_ACCESS_KEY / MINIO_SECRET_KEY 同在才开签发;S3_PUBLIC_ENDPOINT 缺席时兼作公网面)", { credentialPolicy: "mask", danger: DATA }),
442
+ r("S3_PUBLIC_BUCKET", "store", "string", "SendUserFile 公链桶", { derivedDefaultNote: `缺席 ⇒ ${S3_PUBLIC_BUCKET_DEFAULT}` }),
441
443
  r("S3_PUBLIC_ENDPOINT", "store", "url", "SendUserFile 公网面 endpoint(跨仓契约名,禁改)", { credentialPolicy: "mask" }),
442
444
  r("SANDBOX_BASE_REF", "orchestration", "string", "bake 缺省 base ref(缺席回落 index 最新 sandbox-base)"),
443
445
  r("SANDBOX_ENV_FACTS", "orchestration", "boolean", "沙箱事实注入(# Environment 块的 profile/capabilities/pkgSource/egress/resume 事实);会话 scratchpad 的宣告不归本键管(它是写面的宣告,关事实不关 scratchpad)", { staticDefault: "true", configKey: "envFactsEnabled" }),
@@ -473,7 +475,7 @@ export const CONFIG_CATALOG = [
473
475
  r(WEB_ADMIN_ENV.webUrl, "integrations", "url", `web-admin 人用界面的固定基址:引擎原样转述到 /health 的 center.webBaseUrl,壳按它拼会话深链(路径归壳)。缺席即缺席(引擎不拼不猜);只在接了中心(${WEB_ADMIN_ENV.url})时挂;必须与中心身份(AUTH_BRIDGE_ISSUER,缺席 = ${WEB_ADMIN_ENV.url})同主机同端口(http ↔ https 同族),否则拒启。${CENTER_BASE_URL_RULE}`, { credentialPolicy: "refuse" }),
474
476
  r(WEB_ADMIN_ENV.worker, "integrations", "string", "本 worker 在 web-admin 上的名字(per-worker effective)"),
475
477
  r("SEND_USER_FILE_SANDBOX_PUT_ENDPOINT", "store", "url", "沙箱侧 presigned PUT Host 绑定 endpoint(in-cluster 可用内网面)", { credentialPolicy: "mask", derivedDefaultNote: "缺席 ⇒ publicEndpoint" }),
476
- r("SEND_USER_FILE_URL_TTL", "store", "number", "公链 TTL(秒;非法值回 0=永久轨,绝不 NaN 进签名)", { staticDefault: "0" }),
478
+ r("SEND_USER_FILE_URL_TTL", "store", "number", `SendUserFile 链接有效期(秒;不设 / 0 = 永久公链轨,正整数 = 私有预签名链接,至多 ${SIGV4_PRESIGN_MAX_SEC}(SigV4 的 7 天);超过上限或不是十进制整数(如 \`1h\` / \`-5\` / \`3600.5\` / \`1e3\`)起服拒)`, { staticDefault: "0" }),
477
479
  r("SENSITIVE_WRITE_PATTERNS", "approval", "csv", "敏感路径写 deny 集(unset=core 推荐集;显式值整表替换;off/空=关 —— 真放松杆)。拒的是文件工具(Write / Edit / NotebookEdit,按路径);shell 命令写同一路径(重定向 / tee / cp …)不被这个集拒绝,走普通审批问", { danger: SEC, derivedDefaultNote: "缺席 ⇒ core RECOMMENDED_SENSITIVE_PATTERNS", configKey: "sensitiveWritePatterns" }),
478
480
  r("SERVICE_AUTH_TOKEN", "auth", "secret", "共享 service token(写面门;缺席且未开 ALLOW_UNAUTHED_WRITES ⇒ 拒提交)", { credentialForm: "whole", danger: SEC }),
479
481
  r("SERVICE_AUTH_TOKENS", "auth", "secret", "per-system 凭据表(tok=system CSV;source 从凭据派生,绝不自报)", { credentialForm: "token-table", danger: SEC }),
@@ -486,8 +488,8 @@ export const CONFIG_CATALOG = [
486
488
  r("SESSION_EVENTS_MAX_PROBES", "limitsHttp", "number", "session SSE 共享探针数帽", { derivedDefaultNote: "缺席 ⇒ 512" }),
487
489
  r("SESSION_EVENTS_WARM_MS", "limitsHttp", "number", "session SSE 温探针间隔(ms)", { derivedDefaultNote: "缺席 ⇒ 2000" }),
488
490
  r("SESSION_SNAPSHOT_BUCKET", "store", "string", "快照 blob 桶(兼 SendUserFile 私桶)", { derivedDefaultNote: `缺席 ⇒ ${SESSION_SNAPSHOT_BUCKET_DEFAULT}` }),
489
- r("SESSION_SNAPSHOT_PREFIX", "store", "string", "快照 blob key 前缀", { derivedDefaultNote: "缺席 ⇒ blobs/(MinioBlobBackend 默认)" }),
490
- r("SESSION_SNAPSHOT_TTL_SEC", "store", "number", "快照 presign TTL(秒;坏值省略走默认,绝不 NaN 签名)", { derivedDefaultNote: "缺席 ⇒ 3600(MinioBlobBackend 默认)" }),
491
+ r("SESSION_SNAPSHOT_PREFIX", "store", "string", "会话 blob 命名空间的 key 前缀(文件历史 / 附件 / 认领物件;共用一只会话快照桶的引擎各设各的,同桶同前缀的第二只库起服拒;以 attachments/ 或 file-history/ 结尾的值起服拒)", { derivedDefaultNote: `缺席 ⇒ 文件历史、附件与认领物件在桶根,退役快照纪元读 ${LEGACY_BLOB_ROOT_DEFAULT}` }),
492
+ r("SESSION_SNAPSHOT_TTL_SEC", "store", "number", `会话 blob 请求的 presign TTL(秒;十进制整数,0 告警后走缺省;超过 ${SIGV4_PRESIGN_MAX_SEC}(SigV4 的 7 天)或不是十进制整数(如 \`1h\` / \`-5\` / \`3600.5\` / \`1e3\` / \`0x10\`)起服拒)`, { derivedDefaultNote: `缺席 ⇒ ${BLOB_TTL_DEFAULT_SEC}(对象存储动作表 blob 行的缺省)` }),
491
493
  r("SIGHUP_IDLE_GRACE_MS", "limitsHttp", "number", "SIGHUP 空闲关停窗(ms;共享引擎形防误杀)", { staticDefault: "120000", configKey: "sighupIdleGraceMs" }),
492
494
  r("SKILLS_DIR", "integrations", "path", "出厂 skill 目录(设了就整个替换包内 skills/,不叠加;配置源的技能再按名盖上)", { derivedDefaultNote: "缺席 ⇒ 包内 skills/(相对模块解析,不是 cwd)", configKey: "skillsDir" }),
493
495
  r("SNAPSHOT_BLOB_ALLOW_SQL_BYTES", "store", "boolean", "云形缺 MinIO 时显式接受 SQL 店放快照字节(严格 'true';安全姿势开关不收宽松真值)", { staticDefault: "false", danger: DATA, configKey: "snapshotBlobAllowSql" }),
@@ -504,7 +506,6 @@ export const CONFIG_CATALOG = [
504
506
  r("STREAM_APPROVAL_ADHOC_GRACE_MS", "approval", "number", "流内审批 adhoc 腿窗后宽限(ms;须 window+grace < orphanTtl,破坏形拒启)", { staticDefault: "60000" }),
505
507
  r("STREAM_APPROVAL_ADMIT_MAX_PER_OWNER", "approval", "number", "流内审批 per-owner 未决帽", { staticDefault: "256" }),
506
508
  r("STREAM_APPROVAL_ADMIT_MAX_PER_TASK", "approval", "number", "流内审批 per-task 未决帽(超限 unavailable ⇒ park 路由)", { staticDefault: "32" }),
507
- r("STREAM_APPROVAL_ENABLED", "approval", "boolean", "流内审批协议总开关(false=整链回 7.3.0 前行为的唯一干净还原键)", { staticDefault: "true", danger: SEC, configKey: "streamApproval" }),
508
509
  r("STREAM_APPROVAL_ORPHAN_TTL_MS", "approval", "number", "遗孤 ask 最终可判上界(ms,量 createdAt)", { staticDefault: String(7 * 24 * 60 * 60 * 1000) }),
509
510
  r("STREAM_APPROVAL_PENDING_GRACE_MS", "approval", "number", "崩溃恢复扫描宽限(ms;活属主常态必胜,reaper 只兜纯崩溃)", { staticDefault: "30000" }),
510
511
  r("STREAM_APPROVAL_RECONCILE_BATCH", "approval", "number", "对账收敛器每 tick 每段行数帽(整数)", { staticDefault: "200" }),
@@ -550,7 +551,7 @@ export const CONFIG_CATALOG = [
550
551
  r("WORKSPACE_SNAPSHOT_KEEP", "orchestration", "number", `k8s 工作区每个空间(会话住客)保留的快照份数(整数 [${WORKSPACE_SNAPSHOT_KEEP_RANGE.min}, ${WORKSPACE_SNAPSHOT_KEEP_RANGE.max}];写新快照后删最老的;域外拒启)`, { staticDefault: String(WORKSPACE_SNAPSHOT_KEEP_DEFAULT), danger: DATA }),
551
552
  r("WORKSPACE_SNAPSHOT_PREFIX", "orchestration", "string", "k8s 工作区快照 key 前缀:`/` 分隔的一段或多段普通 key 段(kata、libA/ws;每段以字母或数字开头,其后字母数字 . _ -);空段 / . / .. / 前导或尾随 / / 其它字符 ⇒ 拒启并点名第几段(config.workspace_snapshot_prefix_invalid)", { derivedDefaultNote: `缺席 ⇒ ${WORKSPACE_SNAPSHOT_PREFIX_DEFAULT}` }),
552
553
  r("WORKSPACE_SNAPSHOT_TIMEOUT_MS", "orchestration", "number", `k8s 工作区快照的时限(毫秒,整数 [${WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE.min}, ${WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE.max}];域外拒启):一个数管四处 —— run 末上传与连沙箱时恢复的墙钟上界、空间结束时回收座一次调用的绝对截止(卷与对象都在内)、下一条 run 等上一条存完的最长等待(提交围栏的存活上界);大文件读写的墙钟也是它。大工作区(构建目录 GB 级)撞上界 ⇒ 调大它或配 WORKSPACE_SNAPSHOT_EXCLUDE`, { staticDefault: String(WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT) }),
553
- r("WORKSPACE_SNAPSHOT_TTL_SEC", "orchestration", "number", "k8s 工作区快照 presign TTL(秒)"),
554
+ r("WORKSPACE_SNAPSHOT_TTL_SEC", "orchestration", "number", `k8s 工作区快照 presign TTL(秒;十进制整数,0 = 缺省;按行的策略帽截到 3600;超过 ${SIGV4_PRESIGN_MAX_SEC}(SigV4 的 7 天)或不是十进制整数(如 \`1h\` / \`-5\` / \`3600.5\` / \`1e3\` / \`0x10\`)起服拒)`),
554
555
  r("WORKTREE_ALLOWED_ROOTS", "orchestration", "csv", "worktree 隔离纵深白名单(缺省 = [repoRoot])", { danger: SEC }),
555
556
  r("WORKTREE_BASE_COMMIT", "orchestration", "string", "worktree 基准 ref(缺省 core HEAD)"),
556
557
  r("WORKTREE_ISOLATION_ENABLED", "orchestration", "boolean", "git-worktree 隔离(与 REPO_ROOT 同在才开,半配保持 OFF)", { staticDefault: "false", danger: SEC }),
@@ -1,7 +1,8 @@
1
1
  import { lstat, readdir, readlink, realpath, stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { CONFIG_ROOT_DOCUMENTS } from "@sema-agent/settings-schema";
4
- import { LOCAL_READER_DOCUMENTS } from "./config-d-migration.js";
3
+ import { CONFIG_ROOT_DOCUMENTS, RETIRED_DOMAINS, isRetiredDomainName } from "@sema-agent/settings-schema";
4
+ import { PORTABLE_DOMAINS } from "@sema-agent/settings-schema/node";
5
+ import { REMOTE_EXEC_DOCUMENT } from "@sema-agent/settings-schema/remote-exec";
5
6
  import { recordFailOpen } from "./observability/fail-open.js";
6
7
  import { HOST_PLATFORM } from "./plugins/host-platform.js";
7
8
  import { coversPath, parseMountinfo, readProcessMountinfo, visibleMountOf } from "./mountinfo.js";
@@ -219,6 +220,7 @@ async function confirmUnreached(lexical) {
219
220
  }
220
221
  return { kind: "unresolvable", path: lexical };
221
222
  }
223
+ const LOCAL_READER_DOCUMENTS = [...PORTABLE_DOMAINS, ...Object.keys(RETIRED_DOMAINS).filter(isRetiredDomainName), REMOTE_EXEC_DOCUMENT];
222
224
  const documentFiles = () => LOCAL_READER_DOCUMENTS.map((d) => `${d}.json`);
223
225
  const rootDocumentFiles = () => CONFIG_ROOT_DOCUMENTS.map((d) => `${d}.json`);
224
226
  async function reach(lexical, want, ctx) {
@@ -35,9 +35,9 @@ import { type EnvTableReplacementInput } from "./env-model-table-gate.js";
35
35
  */
36
36
  export declare const LIVENESS_HEARTBEAT_MS = 30000;
37
37
  /** 闭集:新增一条不变量必须在这里加词(拼错 = 编译红,而不是运行期悄悄不判)。 */
38
- export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "operator-single-user" | "operator-declaration-required" | "operator-single-user-boundary" | "single-user-anonymous-boundary" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "multi-tenant-shared-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "anthropic-credential-exclusive" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "env-table-replacement-acknowledged" | "center-one-identity" | "workflow-park-decision-domain" | "remote-config-provider-url" | "retention-lane-complete";
38
+ export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "operator-single-user" | "operator-roster-well-formed" | "operator-declaration-required" | "operator-single-user-boundary" | "single-user-anonymous-boundary" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "multi-tenant-shared-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "anthropic-credential-exclusive" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "env-table-replacement-acknowledged" | "center-one-identity" | "workflow-park-decision-domain" | "remote-config-provider-url" | "retention-lane-complete";
39
39
  /** 表读的键集(取自平铺配置型 ⇒ 键名/类型永不与真配置漂)。 */
40
- export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "operatorSingleUser" | "bindHost" | "bindHostSource" | "sensitiveWritePatterns" | "leaderEnabled" | "selfOrchestrationEnabled" | "workflowRunStoreBackend" | "remoteExec" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models" | "modelRefusals" | "tiers" | "authBridgeIssuer" | "webAdmin" | "configProvider" | "retentionPolicy" | "retentionSweep">>;
40
+ export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "operatorDrainPrincipals" | "operatorSingleUser" | "bindHost" | "sensitiveWritePatterns" | "leaderEnabled" | "selfOrchestrationEnabled" | "workflowRunStoreBackend" | "remoteExec" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models" | "modelRefusals" | "tiers" | "authBridgeIssuer" | "webAdmin" | "configProvider" | "retentionPolicy" | "retentionSweep">>;
41
41
  export interface CrossInvariantInput {
42
42
  /** 现役配置(boot 期 = 候选自身)。 */
43
43
  readonly live: CrossInvariantView;
@@ -80,10 +80,21 @@ export declare function halfOpenDirectDoorRefusal(v: Pick<CrossInvariantView, "d
80
80
  */
81
81
  export declare function operatorPrincipalsRequiredRefusal(v: Pick<CrossInvariantView, "durableApproval" | "requirePrincipal" | "operatorPrincipals">): string | undefined;
82
82
  /**
83
- * `operator-single-user`([ref],[ref] ① 裁 A):「谁是 operator」一个事实一个来源。`OPERATOR_SINGLE_USER=true`(单用户形的每个调用方都是运维)
84
- * 只在单用户形、名单为空时成立:多租形没有「单一用户」(声明会把每个租户都变成运维);名单非空时两个来源答同一个问题。两形都拒,句子点名两根旋钮与出路。
83
+ * `operator-single-user`([ref],[ref] ① 裁 A):「谁是 operator」一个事实一个来源。`OPERATOR_SINGLE_USER=true`(单用户形的每个调用方都落全运维档)
84
+ * 只在单用户形、名册各档名单都空时成立:多租形没有「单一用户」(声明会把每个租户都变成运维);任一档名单非空时两个来源答同一个问题(档表 `operator-roster.ts` 逐档读)。
85
+ * 两形都拒,句子点名两根旋钮与出路(一档一句,穷举 switch:加一档不接句子 = 编译红)。
85
86
  */
86
- export declare function operatorSingleUserRefusal(v: Pick<CrossInvariantView, "operatorSingleUser" | "requirePrincipal" | "operatorPrincipals">): string | undefined;
87
+ export declare function operatorSingleUserRefusal(v: Pick<CrossInvariantView, "operatorSingleUser" | "requirePrincipal" | "operatorPrincipals" | "operatorDrainPrincipals">): string | undefined;
88
+ /**
89
+ * `operator-roster-well-formed` —— **名册良构**(档表 `operator-roster.ts` 逐档读;三臂,按序答第一条):
90
+ * ① 每一档的名单是名字的列表(绕过类型手搓的配置放一串字符串 ⇒ `includes` 读成子串匹配 = 放宽);
91
+ * ② **非人档**的每个名字都是一位主体的名字(与身份边界同一只名形判官 `principalShapeProblem`:空 / 不可见字符 / 保留标识 / 超长)—— 坏形的名字永远读不成一位验过的人,
92
+ * 也就永远圈禁不到(圈禁判在读人抛名形错时不判,这一臂让「不判」结构上无害);人档的坏形名字只是永远对不上任何人,不拒(没写新键的部署零差);
93
+ * ③ 各档名单不相交 —— 一个名字的运维权力只在一档里答(与 `operator-single-user` 同理:两个来源答同一个问题 ⇒ 拒)。
94
+ * 拒句点名 env 与条数,不回显名字(名单是部署的身份配置,拒句进 stderr 与起服日志)。**两处调用、一只判据**:`loadConfig`(服务入口起服即拒)与装配路由的
95
+ * `createHttpServer`(嵌入方只过这一座)。env 独有、起服冻结 —— 热路径上同判(条目在下)。
96
+ */
97
+ export declare function operatorRosterWellFormedRefusal(v: Pick<CrossInvariantView, "operatorPrincipals" | "operatorDrainPrincipals">): string | undefined;
87
98
  /**
88
99
  * `operator-declaration-required`([ref])—— **单用户部署必须说出谁是运维**。单用户形(租户轴不成立,`DEPLOYMENT_SHAPE_PREDICATES.multiTenant`)上
89
100
  * 「谁是运维」只有两种答法:`OPERATOR_SINGLE_USER=true`(每个调用方都是)或具名 `OPERATOR_PRINCIPALS`;两样都没有 = 没人能用这台部署的运维面
@@ -95,7 +106,7 @@ export declare function operatorSingleUserRefusal(v: Pick<CrossInvariantView, "o
95
106
  */
96
107
  export declare function operatorDeclarationRefusal(v: Pick<CrossInvariantView, "requirePrincipal" | "operatorPrincipals" | "operatorSingleUser">): string | undefined;
97
108
  /** {@link operatorSingleUserBoundaryRefusal} 读的键(条目的 `keys` 与「判正在改的那份」比较同读这一张)。 */
98
- declare const SINGLE_USER_BOUNDARY_KEYS: readonly ["operatorSingleUser", "authToken", "authTokens", "allowUnauthedWrites", "bindHost", "bindHostSource"];
109
+ declare const SINGLE_USER_BOUNDARY_KEYS: readonly ["operatorSingleUser", "authToken", "authTokens", "allowUnauthedWrites", "bindHost"];
99
110
  type SingleUserBoundaryView = Pick<CrossInvariantView, (typeof SINGLE_USER_BOUNDARY_KEYS)[number]>;
100
111
  /**
101
112
  * `operator-single-user-boundary` —— **单用户声明要有一道身份边界**。`OPERATOR_SINGLE_USER=true` 让这台部署的每个调用方都是运维(`explicitOperatorOk`),
@@ -109,7 +120,7 @@ type SingleUserBoundaryView = Pick<CrossInvariantView, (typeof SINGLE_USER_BOUND
109
120
  */
110
121
  export declare function operatorSingleUserBoundaryRefusal(v: SingleUserBoundaryView): string | undefined;
111
122
  /** {@link singleUserAnonymousBoundaryRefusal} 读的键(条目的 `keys` 与「判正在改的那份」比较同读这一张)。 */
112
- declare const ANONYMOUS_BOUNDARY_KEYS: readonly ["requirePrincipal", "authToken", "authTokens", "allowUnauthedWrites", "bindHost", "bindHostSource"];
123
+ declare const ANONYMOUS_BOUNDARY_KEYS: readonly ["requirePrincipal", "authToken", "authTokens", "allowUnauthedWrites", "bindHost"];
113
124
  type AnonymousBoundaryView = Pick<CrossInvariantView, (typeof ANONYMOUS_BOUNDARY_KEYS)[number]>;
114
125
  /**
115
126
  * `single-user-anonymous-boundary` —— **单用户部署上不带主体的 run 要有一道身份边界**。单用户形(部署形表 `multiTenant` 不成立)上,不带主体的 run 就是这台部署的那一个人:
@@ -2,6 +2,7 @@ import { assertGuardPatternsUsable } from "./deployment-governance.js";
2
2
  import { modelProviderRefusal } from "./model-provider.js";
3
3
  import { modelRouteUrlRefusal } from "./model-route-url.js";
4
4
  import { anthropicCredentialRefusal } from "./model-route-gap.js";
5
+ import { CATALOG_REFUSAL_LEG } from "./model-select.js";
5
6
  import { modelRouteEndpointRefusal } from "./model-route-endpoint.js";
6
7
  import { defaultModelRefusal, refusedNameMintedRefusal } from "./model-entry-refusal.js";
7
8
  import { judgeEnvTableReplacement } from "./env-model-table-gate.js";
@@ -11,6 +12,8 @@ import { buildDisplaySafeUrl } from "./trace/redact.js";
11
12
  import { ALL_EXECUTION_LANES, executionLaneCaps } from "./execution-lane-caps.js";
12
13
  import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
13
14
  import { bindMustNarrow, listensLoopbackOnly, resolveBindHost } from "./bind-host.js";
15
+ import { ROSTER_TIERS, ROSTER_TIER_ORDER, rosterNamesOf } from "./operator-roster.js";
16
+ import { principalShapeProblem } from "./principal-shape.js";
14
17
  export const LIVENESS_HEARTBEAT_MS = 30_000;
15
18
  const directDoorConjuncts = (v) => Boolean(v.directApprovalDoor) && Boolean(v.durableApproval) && v.approvalHmacKeys.length > 0 && v.principalJwtPubkeys.length > 0 && Boolean(v.principalJwtIss) && Boolean(v.principalJwtAud);
16
19
  function multiTenantSharedLaneRefusal(v) {
@@ -51,8 +54,41 @@ export function operatorSingleUserRefusal(v) {
51
54
  return undefined;
52
55
  if (v.requirePrincipal)
53
56
  return "OPERATOR_SINGLE_USER=true but REQUIRE_PRINCIPAL=true — the declaration makes every caller an operator, which only fits a single-user deployment; a multi-tenant worker names its operators in OPERATOR_PRINCIPALS. Unset OPERATOR_SINGLE_USER, or unset REQUIRE_PRINCIPAL if this is a single-user deployment";
54
- if (v.operatorPrincipals.length > 0)
55
- return "OPERATOR_SINGLE_USER=true but OPERATOR_PRINCIPALS is also set — the two answer the same question (who is an operator) in two ways. Keep one: OPERATOR_SINGLE_USER=true (every caller of this single-user deployment) or OPERATOR_PRINCIPALS (only the named principals)";
57
+ const named = ROSTER_TIER_ORDER.find((tier) => rosterNamesOf(tier, v).length > 0);
58
+ if (named === undefined)
59
+ return undefined;
60
+ switch (named) {
61
+ case "operator":
62
+ return "OPERATOR_SINGLE_USER=true but OPERATOR_PRINCIPALS is also set — the two answer the same question (who is an operator) in two ways. Keep one: OPERATOR_SINGLE_USER=true (every caller of this single-user deployment) or OPERATOR_PRINCIPALS (only the named principals)";
63
+ case "drain":
64
+ return "OPERATOR_SINGLE_USER=true but OPERATOR_DRAIN_PRINCIPALS is also set — the declaration already makes every caller an operator who may drain, so a drain-only roster answers the same question (who may drain) a second way. Keep one: OPERATOR_SINGLE_USER=true (every caller of this single-user deployment) or OPERATOR_DRAIN_PRINCIPALS (only the named drain-only service principals, with the operators named in OPERATOR_PRINCIPALS)";
65
+ default: {
66
+ const unknownTier = named;
67
+ throw new Error(`operatorSingleUserRefusal: unknown roster tier ${String(unknownTier)}`);
68
+ }
69
+ }
70
+ }
71
+ export function operatorRosterWellFormedRefusal(v) {
72
+ for (const tier of ROSTER_TIER_ORDER) {
73
+ const names = v[ROSTER_TIERS[tier].field];
74
+ if (names !== undefined && (!Array.isArray(names) || names.some((n) => typeof n !== "string")))
75
+ return `${ROSTER_TIERS[tier].env} must be a list of principal names, and the assembled configuration carries something else in that cell — an embedder passes an array of names (loadConfig always does) (invariant operator-roster-well-formed). Refusing to boot.`;
76
+ }
77
+ for (const tier of ROSTER_TIER_ORDER) {
78
+ if (ROSTER_TIERS[tier].person)
79
+ continue;
80
+ const malformed = rosterNamesOf(tier, v).filter((n) => principalShapeProblem(n) !== undefined).length;
81
+ if (malformed > 0)
82
+ return `${ROSTER_TIERS[tier].env} lists ${malformed} entr${malformed === 1 ? "y that is" : "ies that are"} not a principal name (empty, invisible or control characters, a reserved identifier, or over the principal length budget) — no verified caller can carry such a name, so it could never be confined. Remove those entries (MIGRATION.md, invariant operator-roster-well-formed). Refusing to boot.`;
83
+ }
84
+ for (const [i, a] of ROSTER_TIER_ORDER.entries()) {
85
+ const names = new Set(rosterNamesOf(a, v));
86
+ for (const b of ROSTER_TIER_ORDER.slice(i + 1)) {
87
+ const shared = rosterNamesOf(b, v).filter((n) => names.has(n)).length;
88
+ if (shared > 0)
89
+ return `${ROSTER_TIERS[a].env} and ${ROSTER_TIERS[b].env} name the same principal (${shared} name${shared === 1 ? "" : "s"} in both lists) — a principal's operator powers must be answered by one roster tier only. Remove each such name from one of the two lists (MIGRATION.md, invariant operator-roster-well-formed). Refusing to boot.`;
90
+ }
91
+ }
56
92
  return undefined;
57
93
  }
58
94
  export function operatorDeclarationRefusal(v) {
@@ -63,7 +99,7 @@ export function operatorDeclarationRefusal(v) {
63
99
  "Declare who operates it in one line: OPERATOR_SINGLE_USER=true (every caller of this single-user deployment is its operator — with a service credential or a loopback-only listener, or anyone who can reach the port is) or OPERATOR_PRINCIPALS=<principal,...> (only the named principals); " +
64
100
  "a worker called by more than one person is multi-tenant — set REQUIRE_PRINCIPAL=true instead (MIGRATION.md, invariant operator-declaration-required). Refusing to boot.");
65
101
  }
66
- const SINGLE_USER_BOUNDARY_KEYS = ["operatorSingleUser", "authToken", "authTokens", "allowUnauthedWrites", "bindHost", "bindHostSource"];
102
+ const SINGLE_USER_BOUNDARY_KEYS = ["operatorSingleUser", "authToken", "authTokens", "allowUnauthedWrites", "bindHost"];
67
103
  function sameSingleUserBoundary(a, b) {
68
104
  return a.operatorSingleUser === b.operatorSingleUser && hasServiceAuth(a) === hasServiceAuth(b) && resolveBindHost(a) === resolveBindHost(b);
69
105
  }
@@ -83,9 +119,9 @@ function offHostListener(v) {
83
119
  const bound = resolveBindHost(v);
84
120
  if (listensLoopbackOnly(bound))
85
121
  return undefined;
86
- return bound === undefined ? "every network interface (BIND_HOST is not set)" : `${bound}${v.bindHostSource === "HOST" ? " (from the HOST env var)" : ""}`;
122
+ return bound === undefined ? "every network interface (BIND_HOST is not set)" : bound;
87
123
  }
88
- const ANONYMOUS_BOUNDARY_KEYS = ["requirePrincipal", "authToken", "authTokens", "allowUnauthedWrites", "bindHost", "bindHostSource"];
124
+ const ANONYMOUS_BOUNDARY_KEYS = ["requirePrincipal", "authToken", "authTokens", "allowUnauthedWrites", "bindHost"];
89
125
  function sameAnonymousBoundary(a, b) {
90
126
  return DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(a) === DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(b) && bindMustNarrow(a) === bindMustNarrow(b) && resolveBindHost(a) === resolveBindHost(b);
91
127
  }
@@ -243,11 +279,18 @@ export const CONFIG_CROSS_INVARIANTS = [
243
279
  },
244
280
  {
245
281
  id: "operator-single-user",
246
- keys: ["operatorSingleUser", "requirePrincipal", "operatorPrincipals"],
282
+ keys: ["operatorSingleUser", "requirePrincipal", "operatorPrincipals", "operatorDrainPrincipals"],
247
283
  restartReason: "operator-principals",
248
284
  bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:operator-single-user" },
249
285
  check: ({ next }) => operatorSingleUserRefusal(next),
250
286
  },
287
+ {
288
+ id: "operator-roster-well-formed",
289
+ keys: ["operatorPrincipals", "operatorDrainPrincipals"],
290
+ restartReason: "operator-principals",
291
+ bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:operator-roster-well-formed" },
292
+ check: ({ next }) => operatorRosterWellFormedRefusal(next),
293
+ },
251
294
  {
252
295
  id: "operator-declaration-required",
253
296
  keys: ["requirePrincipal", "operatorPrincipals", "operatorSingleUser"],
@@ -384,14 +427,14 @@ export const CONFIG_CROSS_INVARIANTS = [
384
427
  keys: ["gatewayBaseUrl", "gatewayFallbackUrls", "anthropic", "models"],
385
428
  restartReason: "model-route-endpoint",
386
429
  bootAnchor: { file: "src/boot/stage-02-web-admin-pull.ts", marker: "CROSS-INVARIANT:model-route-endpoint" },
387
- check: ({ live, next }) => (next.models === live.models ? undefined : modelRouteEndpointRefusal(next, "catalog")),
430
+ check: ({ live, next }) => (next.models === live.models ? undefined : modelRouteEndpointRefusal(next, CATALOG_REFUSAL_LEG)),
388
431
  },
389
432
  {
390
433
  id: "default-model-resolvable",
391
434
  keys: ["models", "modelRefusals"],
392
435
  restartReason: "default-model",
393
436
  bootAnchor: { file: "src/boot/stage-02-web-admin-pull.ts", marker: "CROSS-INVARIANT:default-model-resolvable" },
394
- check: ({ live, next }) => (next.models === live.models ? undefined : defaultModelRefusal(next, "catalog")),
437
+ check: ({ live, next }) => (next.models === live.models ? undefined : defaultModelRefusal(next, CATALOG_REFUSAL_LEG)),
395
438
  },
396
439
  {
397
440
  id: "refused-names-not-minted",
@@ -1,4 +1,3 @@
1
- import { type MigrationReceipt } from "@sema-agent/settings-schema/one-time-migrations";
2
1
  import { type PublishedPromptArtifact } from "@sema-agent/core";
3
2
  import { type ConfigDirTrustHost } from "./config-dir-trust.js";
4
3
  /** 落盘信封。`effective`/`etag` 逐字=当时的 fetchEffective 返回(JSON verbatim 面)。`formatVersion` 的语义见头注(5 = 载荷的 prompts 是中心层唯一的那种 wire 形)。 */
@@ -65,11 +64,10 @@ export declare function lkgReadDenyRows(configLkgPath: string | undefined): {
65
64
  * 影响在飞的应用链——LKG 缓存丢失的代价只是下次冷启动回到 env 兜底。 */
66
65
  export declare function saveLkg(path: string, worker: string | undefined, identity: string, effective: Record<string, unknown>, etag: string | undefined): Promise<void>;
67
66
  /** 一次读回做过的一次性迁移(调用方响亮打一行 `config_lkg_migrated`,并按 5 写回原位):`fromFormat` = 盘上的版本号;`note` = 信封层的说明
68
- * (2 → 3 那一步 {@link migrateV2Lkg} / 4 → 5 那一步 {@link migrateV4Lkg});`receipts` = 载荷层 settings-schema 迁移的收据(只点名坐标与处置,不带值)。 */
67
+ * (4 → 5 那一步 {@link migrateV4Lkg})。 */
69
68
  export interface LkgReadMigration {
70
- readonly fromFormat: 2 | 3 | 4 | 5;
71
- readonly note?: string;
72
- readonly receipts: readonly MigrationReceipt[];
69
+ readonly fromFormat: 4;
70
+ readonly note: string;
73
71
  }
74
72
  /** {@link loadLkg} 读之前判官要的两件:声明的属主(`CONFIG_LOCAL_OWNER_UID`,配置席 `configLocalOwnerUid`;与本地配置根同一张可信集)+ 宿主事实(测试注入;缺席 = 本进程)。 */
75
73
  export interface LkgReadTrust {
@@ -80,8 +78,8 @@ export interface LkgReadTrust {
80
78
  * 读盘上 LKG。返回 undefined 的所有情形都「安静地回 env 兜底」(调用方 warn 点名 reason):
81
79
  * 别人改得动这份文件(目录信任判官的发现)/
82
80
  * 文件缺席/JSON 坏/信封形不对/worker 或凭证身份不匹配(防同名文件跨 worker / 跨账号串包;`CONFIG_LKG_PATH`
83
- * 单一路径时这是唯一的一道)/载荷有迁不动的旧形({@link migrateLkgPayload})/prompts 面复判失败。
84
- * `migrated`:读回时做过一次性迁移(信封 2 → 3 / 4 → 5 或载荷的 settings-schema 迁移)—— 调用方响亮打一行,并把交回的 5 写回原位。
81
+ * 单一路径时这是唯一的一道)/载荷带着 4 之前的旧形(3 及更早 ⇒ 版本拒)/prompts 面复判失败。
82
+ * `migrated`:读回时做过一次性迁移(信封 4 → 5)—— 调用方响亮打一行,并把交回的 5 写回原位。
85
83
  * `prompts`:候选判官对这份载荷 prompts 面交出的中心层(引擎 verify 过的发布形 artifact;`undefined` = 载荷不带 prompts 键)—— 调用方采纳直接用它。
86
84
  */
87
85
  export declare function loadLkg(path: string, worker: string | undefined, identity: string, trust: LkgReadTrust): Promise<{
@@ -92,16 +90,4 @@ export declare function loadLkg(path: string, worker: string | undefined, identi
92
90
  lkg?: undefined;
93
91
  reason: string;
94
92
  }>;
95
- /**
96
- * LKG 载荷(一份冻结的 effective)→ settings-schema 11 的形:调契约的 `migrateEffectiveSnapshot`(本仓不写任何一条改写规则;只认形、幂等,
97
- * `version` / `updatedAt` 与其余信封键原样)。有收据 `refused`(迁不动 —— 例:同一份文档里 `atModelAllowlist` 与 `atMention` 并存、
98
- * `runtime` 文档读不进)⇒ 交回拒因(**点名迁移 id 与契约的拒句**,不带值),调用方不读回这份 LKG:本仓应用 LKG 不经契约读腿,迁不动的旧形
99
- * 留在载荷里就会被下游当作缺席静默略过(例:`atModelAllowlist` 不再有读者 = 限制被静默放宽)。
100
- */
101
- export declare function migrateLkgPayload(effective: Record<string, unknown>): {
102
- effective: Record<string, unknown>;
103
- receipts: readonly MigrationReceipt[];
104
- } | {
105
- refused: string;
106
- };
107
93
  //# sourceMappingURL=config-lkg.d.ts.map
@@ -1,6 +1,4 @@
1
1
  import { existsSync, promises as fs } from "node:fs";
2
- import { migrateEffectiveSnapshot } from "@sema-agent/settings-schema/one-time-migrations";
3
- import { formatConfigPath } from "@sema-agent/settings-schema";
4
2
  import { join, dirname, resolve } from "node:path";
5
3
  import { ensureDir } from "@sema-agent/core";
6
4
  import { judgeConfigCandidate } from "./web-admin-client/candidate-gate.js";
@@ -67,7 +65,7 @@ export async function loadLkg(path, worker, identity, trust) {
67
65
  const formatVersion = p.formatVersion;
68
66
  if (formatVersion === 1)
69
67
  return { reason: PRE_IDENTITY_LKG_REASON };
70
- if (formatVersion !== 2 && formatVersion !== 3 && formatVersion !== 4 && formatVersion !== 5)
68
+ if (formatVersion !== 4 && formatVersion !== 5)
71
69
  return { reason: `unknown formatVersion ${String(formatVersion)}` };
72
70
  if (typeof p.worker !== "string" || p.worker !== (worker ?? "global"))
73
71
  return { reason: `worker mismatch (file=${String(p.worker)})` };
@@ -75,42 +73,19 @@ export async function loadLkg(path, worker, identity, trust) {
75
73
  return { reason: "credential identity mismatch (the LKG was fetched under another credential identity — not read back)" };
76
74
  if (p.effective === null || typeof p.effective !== "object" || Array.isArray(p.effective))
77
75
  return { reason: "bad effective" };
78
- let envelope = p;
79
- let note;
80
- if (formatVersion === 2) {
81
- const v2 = migrateV2Lkg(envelope);
82
- if (v2.lkg === undefined)
83
- return v2;
84
- envelope = v2.lkg;
85
- note = v2.migrated;
86
- }
87
- if (formatVersion !== 5) {
76
+ const envelope = p;
77
+ let migrated;
78
+ if (formatVersion === 4) {
88
79
  const v4 = migrateV4Lkg(envelope, formatVersion);
89
80
  if (v4.lkg === undefined)
90
81
  return v4;
91
- note = note === undefined ? v4.migrated : `${note}; ${v4.migrated}`;
82
+ migrated = { fromFormat: formatVersion, note: v4.migrated };
92
83
  }
93
- const payload = migrateLkgPayload(envelope.effective);
94
- if ("refused" in payload)
95
- return { reason: payload.refused };
96
- const judged = lkgPayloadJudged(payload.effective, envelope.etag);
84
+ const judged = lkgPayloadJudged(envelope.effective, envelope.etag);
97
85
  if ("problem" in judged)
98
86
  return { reason: judged.problem };
99
- const lkg = { ...envelope, formatVersion: 5, effective: payload.effective };
100
- const migrated = formatVersion !== 5 || payload.receipts.length > 0;
101
- return migrated ? { lkg, prompts: judged.prompts, migrated: { fromFormat: formatVersion, ...(note !== undefined ? { note } : {}), receipts: payload.receipts } } : { lkg, prompts: judged.prompts };
102
- }
103
- export function migrateLkgPayload(effective) {
104
- const m = migrateEffectiveSnapshot(effective);
105
- const refused = m.receipts.filter((r) => r.appliedAs === "refused");
106
- if (refused.length > 0) {
107
- return {
108
- refused: "settings-schema one-time migration refused part of this LKG's payload (" +
109
- refused.map((r) => `${r.migration} ${r.document}:${formatConfigPath(r.path)} — ${r.reason}`).join("; ") +
110
- ") — not read back: the engine applies an LKG without the contract's reader, so a form the migration cannot rewrite would be silently skipped; the next clean candidate rewrites it",
111
- };
112
- }
113
- return { effective: m.effective, receipts: m.receipts };
87
+ const lkg = { ...envelope, formatVersion: 5 };
88
+ return migrated === undefined ? { lkg, prompts: judged.prompts } : { lkg, prompts: judged.prompts, migrated };
114
89
  }
115
90
  function migrateV4Lkg(lkg, fromFormat) {
116
91
  if (Object.hasOwn(lkg.effective, "prompts")) {
@@ -122,35 +97,6 @@ function migrateV4Lkg(lkg, fromFormat) {
122
97
  }
123
98
  return { lkg, migrated: `formatVersion ${fromFormat} → 5 (one-time): the payload carries no prompts face, read back unchanged` };
124
99
  }
125
- const RETIRED_CATALOG_COORDINATE = /^models\.(0|[1-9][0-9]*)$/;
126
- function migrateDroppedIdentityRow(row) {
127
- if (row === null || typeof row !== "object")
128
- return row;
129
- const r = row;
130
- const at = typeof r.entry === "string" ? RETIRED_CATALOG_COORDINATE.exec(r.entry) : null;
131
- const fields = r.fields;
132
- if (at === null || typeof r.name !== "string" || (r.id !== undefined && typeof r.id !== "string") || !Array.isArray(fields) || !fields.every((f) => typeof f === "string") || typeof r.issues !== "string")
133
- return row;
134
- return { index: Number(at[1]), reasonCode: "entry-schema-invalid", identity: { name: r.name, ...(typeof r.id === "string" ? { id: r.id } : {}) }, fields: [...fields], issues: r.issues };
135
- }
136
- function migrateV2Lkg(lkg) {
137
- const { etag: _incompleteCarrierValidator, ...envelope } = lkg;
138
- const models = lkg.effective.models;
139
- const hasRetired = models !== undefined && models !== null && typeof models === "object" && Object.hasOwn(models, "droppedIdentities");
140
- let effective = lkg.effective;
141
- if (hasRetired) {
142
- const { droppedIdentities: retired, ...rest } = models;
143
- if (Object.hasOwn(rest, "droppedCatalogEntries")) {
144
- return { reason: "both the retired dropped-entry carrier (models.droppedIdentities) and its successor (models.droppedCatalogEntries) are present — no writer produces that shape; not read back" };
145
- }
146
- effective = { ...lkg.effective, models: { ...rest, droppedCatalogEntries: Array.isArray(retired) ? retired.map(migrateDroppedIdentityRow) : retired } };
147
- }
148
- return {
149
- lkg: { ...envelope, effective },
150
- migrated: "formatVersion 2 → 3 (one-time): this LKG was written before every dropped catalog entry was recorded, so its etag is not reused — the next pull re-reads the whole candidate and re-mints the full record" +
151
- (hasRetired ? "; the retired carrier models.droppedIdentities was moved to models.droppedCatalogEntries" : ""),
152
- };
153
- }
154
100
  function lkgPayloadJudged(effective, etag) {
155
101
  const problem = lkgPayloadShapeProblem(effective, etag);
156
102
  if (problem !== undefined)
@@ -25,7 +25,7 @@ import type { ConfigProviderWord } from "./config-types.js";
25
25
  * `effective.models.droppedCatalogEntries`(`GET /v1/models.droppedEntries` 的源,与 `models` 同世代)。 */
26
26
  /** `unreadFiles`:`config.d` 里读腿根本不读的文件(不是域 / 不是便携域)—— **不**进候选门(不是读的域判形失败),调用方一份一行
27
27
  * `config_unread_file` 点名、其余照用;缺席 = 没有(本地读腿才会有)。 */
28
- /** `refusedDocuments`:被拒文档 —— 契约的类型 `ConfigDocumentRefusal`(`{ document, reason, error }`,墓碑另带 `migration`;`reason` 是契约闭集
28
+ /** `refusedDocuments`:被拒文档 —— 契约的类型 `ConfigDocumentRefusal`(`{ document, reason, error }`,墓碑与文件层两臂同形;`reason` 是契约闭集
29
29
  * `CONFIG_DOCUMENT_REFUSAL_REASONS`:`empty` / `invalid_json` / `not_object` / `io` / `too_deep` 文件层,`retired_domain` 墓碑)。读腿不抛,交成发现,候选判官以发现自带的词整拒这份候选
30
30
  * (与域判形失败同一条回落链、同一行拒判);缺席 = 没有。远端腿:wire 上只有墓碑一种,那一键不进 effective、其余域照读、版本照读;本地腿:契约的单读口
31
31
  * `FileConfigStore.read()` 交 `refused` ⇒ 被拒文档 = 那一读的拒单原样,什么域内容都没装配 —— 候选是空的 effective(被整拒,不会被应用),版本 = 那一读的契约版本(按文档内容寻址)。 */
@@ -1,5 +1,5 @@
1
1
  import { type EnvModelsMigration } from "@sema-agent/settings-schema/node";
2
- import type { ConfigDirMigrationLog } from "./config-d-migration.js";
2
+ import type { Logger } from "./observability/logger.js";
3
3
  /** 一次起服的输入:配置根、目标引擎的 env(契约只读两槽、只用来点名被顶替的 env 模型)、env 表是不是占位(严格名册布防 —— 与模型面闸同一只谓词)。 */
4
4
  export interface EnvModelsMigrationInput {
5
5
  readonly root: string;
@@ -9,5 +9,5 @@ export interface EnvModelsMigrationInput {
9
9
  /** 契约的迁移体调一次(本仓不改写它的判序,只调度)。 */
10
10
  export declare function runEnvModelsMigration(input: EnvModelsMigrationInput): Promise<EnvModelsMigration>;
11
11
  /** 结局的**唯一**说法(两个入口同调)。 */
12
- export declare function logEnvModelsMigration(log: ConfigDirMigrationLog, input: EnvModelsMigrationInput, m: EnvModelsMigration): void;
12
+ export declare function logEnvModelsMigration(log: Pick<Logger, "info" | "warn">, input: EnvModelsMigrationInput, m: EnvModelsMigration): void;
13
13
  //# sourceMappingURL=config-root-migration.d.ts.map