@sema-agent/server 7.122.0 → 7.124.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 (256) hide show
  1. package/MIGRATION.md +209 -0
  2. package/README.md +7 -2
  3. package/README.zh-CN.md +5 -2
  4. package/USAGE.md +76 -56
  5. package/dist/approval-ask-audit-store.d.ts +27 -32
  6. package/dist/approval-ask-audit-store.js +10 -9
  7. package/dist/approval-crash-convergence.d.ts +2 -2
  8. package/dist/approval-crash-convergence.js +5 -5
  9. package/dist/approval-deny-reasons.d.ts +163 -51
  10. package/dist/approval-deny-reasons.js +79 -8
  11. package/dist/approval-policy-settlement.d.ts +4 -1
  12. package/dist/approval-policy-settlement.js +3 -2
  13. package/dist/approval-reconciler.d.ts +3 -3
  14. package/dist/approval-reconciler.js +10 -10
  15. package/dist/approval.d.ts +1 -1
  16. package/dist/approval.js +1 -1
  17. package/dist/audit.d.ts +1 -1
  18. package/dist/audit.js +53 -23
  19. package/dist/auth-bridge.d.ts +8 -8
  20. package/dist/auth-bridge.js +6 -6
  21. package/dist/auth-keys.d.ts +2 -2
  22. package/dist/auto-mode-face.d.ts +13 -9
  23. package/dist/auto-mode-face.js +8 -7
  24. package/dist/boot/budget-tracing.js +1 -1
  25. package/dist/boot/execution-env.js +1 -0
  26. package/dist/boot/leader.d.ts +1 -1
  27. package/dist/boot/memory-consolidation.d.ts +4 -4
  28. package/dist/boot/org-memory.d.ts +3 -3
  29. package/dist/boot/org-memory.js +5 -5
  30. package/dist/boot/resolve-spec.js +7 -7
  31. package/dist/boot/runner-deps.d.ts +1 -1
  32. package/dist/boot/runtime-caps.d.ts +1 -1
  33. package/dist/boot/runtime-caps.js +10 -10
  34. package/dist/boot/shutdown.d.ts +2 -2
  35. package/dist/boot/shutdown.js +2 -2
  36. package/dist/boot/side-query-lane.d.ts +3 -3
  37. package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
  38. package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
  39. package/dist/boot/stage-03-store-backend.d.ts +2 -2
  40. package/dist/boot/stage-04-stores.d.ts +1 -1
  41. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  42. package/dist/boot/stage-06-runners.d.ts +2 -2
  43. package/dist/boot/stage-06-runners.js +6 -6
  44. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  45. package/dist/boot/stage-07-capability-layer.js +6 -5
  46. package/dist/boot/stage-08-reapers.d.ts +3 -3
  47. package/dist/boot/stage-08-reapers.js +3 -3
  48. package/dist/boot/stage-09-leader.d.ts +3 -3
  49. package/dist/boot/stage-09-leader.js +7 -7
  50. package/dist/boot/stage-10-http-server.d.ts +3 -3
  51. package/dist/boot/stage-10-http-server.js +19 -19
  52. package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
  53. package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
  54. package/dist/boot/{config-center.js → web-admin.js} +39 -20
  55. package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
  56. package/dist/brain.d.ts +1 -1
  57. package/dist/brain.js +1 -1
  58. package/dist/budget.d.ts +3 -3
  59. package/dist/canonical-json.d.ts +2 -2
  60. package/dist/capabilities/center-plugins.d.ts +1 -1
  61. package/dist/capabilities/center-wiring.d.ts +12 -5
  62. package/dist/capabilities/center-wiring.js +11 -2
  63. package/dist/capabilities/composite-members.d.ts +1 -1
  64. package/dist/capabilities/composite-members.js +1 -1
  65. package/dist/capabilities/scenarios.js +2 -2
  66. package/dist/capabilities/team.d.ts +1 -1
  67. package/dist/capabilities/tool-defer.d.ts +1 -1
  68. package/dist/center-credential-renewer.d.ts +1 -1
  69. package/dist/center-credential-renewer.js +7 -7
  70. package/dist/center-credential.d.ts +9 -9
  71. package/dist/center-credential.js +6 -6
  72. package/dist/config-catalog.d.ts +7 -10
  73. package/dist/config-catalog.js +16 -16
  74. package/dist/config-d-migration.d.ts +32 -0
  75. package/dist/config-d-migration.js +59 -0
  76. package/dist/config-invariants.d.ts +14 -4
  77. package/dist/config-invariants.js +31 -2
  78. package/dist/config-lkg.d.ts +34 -11
  79. package/dist/config-lkg.js +76 -48
  80. package/dist/config-provider.d.ts +26 -21
  81. package/dist/config-provider.js +25 -27
  82. package/dist/config-types.d.ts +59 -50
  83. package/dist/config.d.ts +17 -54
  84. package/dist/config.js +99 -181
  85. package/dist/execution-lane-caps.d.ts +1 -1
  86. package/dist/fleet/drain-entry.d.ts +1 -1
  87. package/dist/fleet/engine-signal.d.ts +61 -30
  88. package/dist/fleet/engine-signal.js +29 -5
  89. package/dist/fleet/fleet-bus.d.ts +12 -3
  90. package/dist/fleet/fleet-bus.js +13 -9
  91. package/dist/fleet/serving-lease.d.ts +3 -3
  92. package/dist/fleet/serving-lease.js +1 -1
  93. package/dist/fleet-client.d.ts +15 -13
  94. package/dist/fleet-client.js +9 -7
  95. package/dist/fleet-lease.d.ts +3 -3
  96. package/dist/fleet-lease.js +2 -2
  97. package/dist/host-lsp-manager.d.ts +1 -1
  98. package/dist/hosted-posture.d.ts +4 -4
  99. package/dist/http/admission.js +4 -4
  100. package/dist/http/checkpoint-ctx.d.ts +20 -3
  101. package/dist/http/checkpoint-ctx.js +13 -5
  102. package/dist/http/dispatch.js +7 -16
  103. package/dist/http/durable-run-leg.js +2 -1
  104. package/dist/http/health-self-report.d.ts +7 -4
  105. package/dist/http/health-self-report.js +3 -3
  106. package/dist/http/idempotency.d.ts +1 -1
  107. package/dist/http/resume-legs.d.ts +4 -3
  108. package/dist/http/resume-legs.js +31 -18
  109. package/dist/http/route-ctx.d.ts +8 -0
  110. package/dist/http/routes/admin-config-refresh.d.ts +1 -1
  111. package/dist/http/routes/admin-config-refresh.js +1 -1
  112. package/dist/http/routes/admin-drain.d.ts +1 -1
  113. package/dist/http/routes/admin-drain.js +5 -4
  114. package/dist/http/routes/approvals-assistant.js +4 -4
  115. package/dist/http/routes/capabilities.js +6 -5
  116. package/dist/http/routes/config-catalog.js +2 -3
  117. package/dist/http/routes/diagnostics.js +1 -1
  118. package/dist/http/routes/runs.js +8 -4
  119. package/dist/http/routes/sessions-list.js +2 -1
  120. package/dist/http/routes/sessions.js +2 -5
  121. package/dist/http/routes/side-query.js +3 -3
  122. package/dist/http/routes/tasks.js +3 -2
  123. package/dist/http/run-meta.d.ts +1 -1
  124. package/dist/http/run-meta.js +2 -2
  125. package/dist/http/server.d.ts +7 -7
  126. package/dist/http/server.js +1 -1
  127. package/dist/http/stored-body-replay.d.ts +4 -3
  128. package/dist/http/stored-body-replay.js +5 -5
  129. package/dist/index.d.ts +2 -1
  130. package/dist/index.js +1 -1
  131. package/dist/key-resolver.d.ts +1 -1
  132. package/dist/leader/leader.d.ts +7 -6
  133. package/dist/leader/leader.js +3 -1
  134. package/dist/leader/run-entry.d.ts +2 -2
  135. package/dist/leader/run-entry.js +3 -3
  136. package/dist/leader/wire.d.ts +4 -4
  137. package/dist/leader/wire.js +2 -1
  138. package/dist/main.js +1 -1
  139. package/dist/mcp-tool-faces.d.ts +2 -2
  140. package/dist/model-entry-refusal.d.ts +20 -11
  141. package/dist/model-entry-refusal.js +22 -17
  142. package/dist/model-provider.d.ts +2 -2
  143. package/dist/model-route-endpoint.d.ts +22 -2
  144. package/dist/model-route-endpoint.js +9 -3
  145. package/dist/model-route-gap.d.ts +103 -0
  146. package/dist/model-route-gap.js +80 -0
  147. package/dist/model-route-url.d.ts +1 -1
  148. package/dist/model-route-url.js +2 -1
  149. package/dist/model-select.d.ts +24 -21
  150. package/dist/model-select.js +22 -17
  151. package/dist/namespaced-rule-name.d.ts +16 -30
  152. package/dist/namespaced-rule-name.js +2 -20
  153. package/dist/observability/fail-open.d.ts +0 -4
  154. package/dist/observability/fail-open.js +0 -4
  155. package/dist/observability/metrics.d.ts +1 -1
  156. package/dist/org-memory-admission.d.ts +1 -1
  157. package/dist/plugins/approval-ask-store-file.d.ts +4 -2
  158. package/dist/plugins/approval-ask-store-file.js +3 -3
  159. package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
  160. package/dist/plugins/approval-ask-store-memory.js +10 -4
  161. package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
  162. package/dist/plugins/approval-ask-store-sql.js +20 -13
  163. package/dist/plugins/caching-session-store.d.ts +0 -10
  164. package/dist/plugins/caching-session-store.js +0 -3
  165. package/dist/plugins/file-run-store.js +9 -3
  166. package/dist/plugins/leader-run-store-sql.d.ts +3 -2
  167. package/dist/plugins/leader-run-store-sql.js +3 -2
  168. package/dist/plugins/local-session-store.d.ts +20 -27
  169. package/dist/plugins/local-session-store.js +19 -17
  170. package/dist/plugins/memory-run-store.js +9 -3
  171. package/dist/plugins/one-time-migrations.js +51 -0
  172. package/dist/plugins/scheduler-support.d.ts +26 -7
  173. package/dist/plugins/scheduler-support.js +96 -53
  174. package/dist/plugins/session-policy-store-sql.js +1 -2
  175. package/dist/plugins/store-backend.js +1 -1
  176. package/dist/plugins/store-contracts.d.ts +20 -4
  177. package/dist/plugins/web-search.d.ts +1 -1
  178. package/dist/plugins/web-search.js +2 -1
  179. package/dist/plugins/workflow-run-store-sql.d.ts +4 -2
  180. package/dist/posture-source.d.ts +1 -1
  181. package/dist/read-face-posture.d.ts +3 -3
  182. package/dist/read-face-posture.js +1 -1
  183. package/dist/rule-entry-list.d.ts +3 -13
  184. package/dist/rule-entry-list.js +1 -12
  185. package/dist/run-local.js +10 -7
  186. package/dist/runtime-caps-resolver.d.ts +4 -4
  187. package/dist/runtime-caps-resolver.js +2 -2
  188. package/dist/runtime-governance.d.ts +36 -13
  189. package/dist/runtime-governance.js +7 -2
  190. package/dist/sealed-key.d.ts +4 -4
  191. package/dist/sealed-key.js +3 -2
  192. package/dist/security.d.ts +3 -3
  193. package/dist/service-version.d.ts +1 -1
  194. package/dist/service-version.js +5 -0
  195. package/dist/session-policy-wire.d.ts +47 -49
  196. package/dist/session-policy-wire.js +41 -36
  197. package/dist/session-sync.d.ts +5 -1
  198. package/dist/session-sync.js +2 -1
  199. package/dist/session-titler.d.ts +0 -11
  200. package/dist/session-titler.js +3 -5
  201. package/dist/shared-memory-scope-authorizer.d.ts +4 -4
  202. package/dist/spec-fields.d.ts +4 -4
  203. package/dist/spec-fields.js +1 -4
  204. package/dist/task-a2a.d.ts +1 -1
  205. package/dist/task-settings.d.ts +4 -5
  206. package/dist/task-settings.js +5 -8
  207. package/dist/tool-approval.d.ts +43 -4
  208. package/dist/tool-approval.js +147 -98
  209. package/dist/trace/core-keyset-guard.d.ts +1 -1
  210. package/dist/trace/project.d.ts +3 -3
  211. package/dist/trace/redact.d.ts +2 -23
  212. package/dist/trace/redact.js +1 -6
  213. package/dist/trace/self-minted-wire.d.ts +147 -3
  214. package/dist/trace/self-minted-wire.js +50 -9
  215. package/dist/unattended-permission-mode.d.ts +3 -3
  216. package/dist/unattended-permission-mode.js +2 -2
  217. package/dist/url-refusal.d.ts +35 -0
  218. package/dist/url-refusal.js +15 -0
  219. package/dist/user-authored-text.d.ts +31 -0
  220. package/dist/user-authored-text.js +18 -0
  221. package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
  222. package/dist/{config-center → web-admin-client}/apply-effective.js +104 -121
  223. package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
  224. package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
  225. package/dist/{config-center → web-admin-client}/center-request.js +5 -4
  226. package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
  227. package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
  228. package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
  229. package/dist/{config-center → web-admin-client}/facade.js +2 -2
  230. package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
  231. package/dist/{config-center → web-admin-client}/http-client.js +15 -15
  232. package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
  233. package/dist/{config-center → web-admin-client}/read-face.js +4 -4
  234. package/dist/web-admin-client/read-warnings.d.ts +137 -0
  235. package/dist/web-admin-client/read-warnings.js +168 -0
  236. package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
  237. package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
  238. package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
  239. package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
  240. package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
  241. package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
  242. package/dist/{config-center → web-admin-client}/types.d.ts +19 -47
  243. package/package.json +3 -3
  244. package/skills/find-skills.md +2 -2
  245. package/dist/config-center/read-warnings.d.ts +0 -109
  246. package/dist/config-center/read-warnings.js +0 -110
  247. package/dist/url-shape.d.ts +0 -31
  248. package/dist/url-shape.js +0 -41
  249. /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
  250. /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
  251. /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
  252. /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
  253. /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
  254. /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
  255. /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
  256. /package/dist/{config-center → web-admin-client}/types.js +0 -0
package/dist/config.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { DEFAULT_MODEL_REF } from "./model-select.js";
2
+ import { DEFAULT_AT_MENTION, DEFAULT_MODEL_REF } from "./model-select.js";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, isModelPriced, PROTOCOL_TABLE, RECOMMENDED_SENSITIVE_PATTERNS, judgeRuleToolName, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable } from "@sema-agent/core";
7
- import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, CENTER_CREDENTIAL_ENV } from "@sema-agent/settings-schema";
7
+ import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, WEB_ADMIN_ENV, retiredEnvRefusal } from "@sema-agent/settings-schema";
8
8
  import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
9
9
  import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
10
10
  import { isV2ScopeKey, memoryReplicationCoverage, replicationCovers } from "./memory-scope.js";
@@ -13,7 +13,7 @@ import { GIT_API_KINDS, isGitApiKind } from "./git-api-kind.js";
13
13
  import { CONFIG_PROVIDER_WORDS, RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS, MAX_TIMER_DELAY_MS, AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, DRAIN_GRACE_FLOOR_MS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS, fleetScopeOf } from "./config-types.js";
14
14
  import { FLEET_GENERATION_ENV, FLEET_SCOPE_ENV, SERVING_LEASE_TTL_FLOOR_MS, isCanonicalGeneration } from "@sema-agent/settings-schema/fleet";
15
15
  import { ScopeId } from "@sema-agent/settings-schema/api/scopes";
16
- import { centerLaneConfigured, pathBaseUrlRefusal } from "./config-center/center-request.js";
16
+ import { centerLaneConfigured, pathBaseUrlRefusal } from "./web-admin-client/center-request.js";
17
17
  import { resolveLocalDataRoot } from "./local-data-root.js";
18
18
  import { buildWriteProtectionPosture } from "./write-protection.js";
19
19
  import { normalizeSshFingerprint } from "./ssh-host-key.js";
@@ -22,29 +22,27 @@ import { THINKING_LEVELS, THINKING_FORMATS, buildModelCompat, isThinkingFormat,
22
22
  import {} from "./posture-source.js";
23
23
  import { CONTROL_AND_BIDI_STRIP_RE } from "./text-bidi.js";
24
24
  import { describeUnmatchableRuleName, ruleEntryToolName, unmatchableRuleNameOf } from "./namespaced-rule-name.js";
25
- import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal } from "./config-invariants.js";
25
+ import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, centerIdentityRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal } from "./config-invariants.js";
26
26
  import { declaredSemaConfigDir } from "./sema-config-dir.js";
27
27
  import { lkgPathOverrideOf } from "./config-lkg.js";
28
28
  import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneOf, executionLaneRequiredRefusal, isSingleMachineDefaultLane, laneRequiredEnv } from "./execution-lane-caps.js";
29
29
  import { MEMORY_ENGINE_BACKENDS, SESSION_BACKENDS, STORE_BACKENDS } from "@sema-agent/settings-schema/execution-lanes";
30
30
  import { AUTONOMY_MODES } from "./runtime-governance.js";
31
- import { allowLayerEntryCountIssue, describeEmptyToolSegmentEntry, describeInertRuleEntries, emptyToolSegmentEntryOf, inertRuleEntriesOf, ruleEntryListShapeOk } from "./rule-entry-list.js";
31
+ import { allowLayerEntryCountIssue, describeInertRuleEntries, inertRuleEntriesOf, ruleEntryListShapeOk } from "./rule-entry-list.js";
32
32
  import { createCenterCredentialSource, readCenterCredentialDeclaration } from "./center-credential.js";
33
33
  import { parseCenterCredentialRenewLeadMs } from "./center-credential-renewer.js";
34
34
  import { modelProviderRefusal } from "./model-provider.js";
35
35
  import { modelRouteUrlRefusal } from "./model-route-url.js";
36
- import { buildHttpUrlRefusal, httpUrlFlaws } from "./url-shape.js";
37
- import { modelRouteEndpointRefusal } from "./model-route-endpoint.js";
36
+ import { httpUrlFlaws } from "@sema-agent/settings-schema";
37
+ import { buildHttpUrlRefusal } from "./url-refusal.js";
38
+ import { csvOf, envLaneTable, judgeModelRouteGap, mintedCheapId, readModelRouteEnv } from "./model-route-gap.js";
38
39
  import { displayListEntry } from "./list-entry-display.js";
39
40
  import { buildDisplaySafeUrl, redactSecrets } from "./trace/redact.js";
40
41
  import { allowlistedEnv } from "./server-secret-env.js";
41
42
  import { ENV_NAME_ALLOWLIST_KNOBS, ENV_NAME_ALLOWLIST_KNOB_NAMES, describeAllowlistPostures } from "./env-name-allowlist-knobs.js";
42
43
  export { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config-types.js";
43
44
  function csv(name) {
44
- return (process.env[name] ?? "")
45
- .split(",")
46
- .map((s) => s.trim())
47
- .filter(Boolean);
45
+ return csvOf(process.env[name]);
48
46
  }
49
47
  export function parseAutonomy(raw) {
50
48
  const v = raw?.trim();
@@ -116,74 +114,10 @@ function defaultSkillsDir() {
116
114
  return "skills";
117
115
  }
118
116
  }
119
- export const RETIRED_ENV_REFUSAL_CODE = "config.env_key_retired";
120
- export const RETIRED_ENVS = [
121
- ["TIDB_HOST", "MYSQL_HOST", "renamed"], ["TIDB_PORT", "MYSQL_PORT", "renamed"], ["TIDB_USER", "MYSQL_USER", "renamed"],
122
- ["TIDB_PASSWORD", "MYSQL_PASSWORD", "renamed"], ["TIDB_DATABASE", "MYSQL_DATABASE", "renamed"], ["TIDB_POOL_SIZE", "MYSQL_POOL_SIZE", "renamed"],
123
- ["CONFIG_CENTER_URL", "SEMA_REGISTRY_URL", "renamed"], ["CONFIG_CENTER_TOKEN", CENTER_CREDENTIAL_ENV.token, "renamed"],
124
- ["CONFIG_CENTER_DRY_RUN", "SEMA_REGISTRY_DRY_RUN", "renamed"], ["CONFIG_CENTER_WORKER", "SEMA_REGISTRY_WORKER", "renamed"],
125
- ["ANTHROPIC_BASEURL", "ANTHROPIC_BASE_URL", "renamed"],
126
- ["REGISTRY_ADMIN_TOKEN", "SEMA_REGISTRY_ADMIN_TOKEN", "renamed"],
127
- ["OA_API_BASEURL", "the OA scenario was removed in 5.0.0", "removed"],
128
- ["OA_SERVICE_TOKEN", "the OA scenario was removed in 5.0.0", "removed"],
129
- ["OA_ISSUE_TOKEN", "the OA scenario was removed in 5.0.0", "removed"],
130
- ["OA_ISSUE_BASEURL", "the OA scenario was removed in 5.0.0", "removed"],
131
- ["OA_ISSUE_OWNER", "the OA scenario was removed in 5.0.0", "removed"],
132
- ["OA_ISSUE_REPO", "the OA scenario was removed in 5.0.0", "removed"],
133
- ["OA_ISSUE_LABELS", "the OA scenario was removed in 5.0.0", "removed"],
134
- ["APPROVAL_POLL_MS", "the OA poll-approval lane was removed in 5.0.0; the durable checkpoint lane is the only gate", "removed"],
135
- ["LEADER_CONFLICT_ROUNDS", "the bounded merge-conflict-resolution agent was deleted in 7.83.0 按 C-R63 删除,合不上交用户:leader 交回 result.conflict(分支 + 冲突文件),run 状态 needs_human", "removed"],
136
- [
137
- "MODEL_CONNECT_TIMEOUT_MS",
138
- "MODEL_FIRST_BYTE_TIMEOUT_MS",
139
- "replaced",
140
- "since core 7.33.0 the TCP + TLS connect clock is the transport's own; the wait from sending the request to its response headers — " +
141
- "what this key really bounded — is MODEL_FIRST_BYTE_TIMEOUT_MS (unset = core's own default, plus an upload allowance and one " +
142
- "widened retry; 0 = no engine clock on that segment and the transport's header / body clocks off too — the TCP + TLS connect " +
143
- "keeps the transport's own clock)",
144
- ],
145
- ["PROJECT_MEMORY_DISABLED", "PROJECT_MEMORY_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: PROJECT_MEMORY_DISABLED=true is PROJECT_MEMORY_ENABLED=false (the positive name defaults on)"],
146
- ["CONFIG_LKG_DISABLED", "CONFIG_LKG_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: CONFIG_LKG_DISABLED=true is CONFIG_LKG_ENABLED=false (the positive name defaults on)"],
147
- ["HOST_BG_DISABLED", "HOST_BG_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: HOST_BG_DISABLED=true is HOST_BG_ENABLED=false (the positive name defaults on)"],
148
- ["HOST_EXEC_SPOOL_DISABLED", "HOST_EXEC_SPOOL_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: HOST_EXEC_SPOOL_DISABLED=true is HOST_EXEC_SPOOL_ENABLED=false (the positive name defaults on)"],
149
- ["UNATTENDED_PERMISSION_MODE", "since server 7.109.0 the unattended legs (leader workers and their repair legs, run-local, and their durable resumes) always run permissionMode \"default\" with an approver that answers by ask class: a machine answers only \"ordinary\" asks, and every other class goes to the leg's approver seat (run-local's terminal prompt) or parks / is refused — there is no mode to choose", "removed"],
150
- ];
151
- for (const row of RETIRED_ENVS)
152
- Object.freeze(row);
153
- Object.freeze(RETIRED_ENVS);
154
- export function buildRetiredEnvWire() {
155
- return RETIRED_ENVS.map((row) => {
156
- switch (row[2]) {
157
- case "renamed":
158
- return { name: row[0], kind: row[2], replacement: row[1] };
159
- case "replaced":
160
- return { name: row[0], kind: row[2], replacement: row[1], reason: row[3] };
161
- case "removed":
162
- return { name: row[0], kind: row[2], reason: row[1] };
163
- default: {
164
- const unhandled = row;
165
- throw new Error(`unhandled retired-env kind: ${String(unhandled[2])}`);
166
- }
167
- }
168
- });
169
- }
170
- const RETIRED_ENV_SENTENCE = {
171
- renamed: (hits) => `${hits.map(([old, to]) => `${old} → use ${to}`).join("; ")} — rename the env key(s); the value semantics are unchanged`,
172
- replaced: (hits) => `${hits.map(([old, to, , why]) => `${old} → ${to} (${why})`).join("; ")} — set the replacement deliberately: ` +
173
- `its value semantics differ from the retired key's, so do not copy the old value across unread`,
174
- removed: (hits) => `${hits.map(([old, why]) => `${old} (${why})`).join("; ")} — remove the env key(s): the capability itself was retired, ` +
175
- `there is no replacement name and no rename that restores it`,
176
- };
177
- function retiredEnvSentence(kind, hit) {
178
- const ofKind = hit.filter((h) => h[2] === kind);
179
- return ofKind.length > 0 ? [RETIRED_ENV_SENTENCE[kind](ofKind)] : [];
180
- }
181
117
  function rejectRetiredEnvs() {
182
- const hit = RETIRED_ENVS.filter(([o]) => process.env[o] !== undefined && process.env[o] !== "");
183
- if (hit.length === 0)
184
- return;
185
- const parts = Object.keys(RETIRED_ENV_SENTENCE).flatMap((kind) => retiredEnvSentence(kind, hit));
186
- throw new Error(`retired env name(s) set [${RETIRED_ENV_REFUSAL_CODE}] — refused while present, whether or not the replacement is also set: ${parts.join(" | ")}.`);
118
+ const refusal = retiredEnvRefusal(process.env);
119
+ if (refusal !== undefined)
120
+ throw new Error(refusal);
187
121
  }
188
122
  export function numEnv(name, fallback) {
189
123
  return parseNumOrFail(name, env(name, fallback));
@@ -250,7 +184,7 @@ function parseConfigProvider() {
250
184
  return undefined;
251
185
  const word = CONFIG_PROVIDER_WORDS.find((w) => w === raw);
252
186
  if (word === undefined) {
253
- throw new Error(`env CONFIG_PROVIDER="${raw.slice(0, 120)}" must be one of: ${CONFIG_PROVIDER_WORDS.join(", ")} — or leave it unset (then SEMA_REGISTRY_URL, if set, selects the center as the config source; otherwise this worker is env-only). ` +
187
+ throw new Error(`env CONFIG_PROVIDER="${raw.slice(0, 120)}" must be one of: ${CONFIG_PROVIDER_WORDS.join(", ")} — or leave it unset (then ${WEB_ADMIN_ENV.url}, if set, selects the center as the config source; otherwise this worker is env-only). ` +
254
188
  `The word is matched exactly (no trimming, no case folding). Refusing to boot rather than guessing: the config source, the execution-lane default and the local config root all key on this one word.`);
255
189
  }
256
190
  return word;
@@ -312,14 +246,6 @@ export function logConfigDiagnostics(logger) {
312
246
  }
313
247
  }
314
248
  }
315
- function firstSetEnv(...names) {
316
- for (const n of names) {
317
- const v = process.env[n];
318
- if (v !== undefined && v !== "")
319
- return v;
320
- }
321
- return undefined;
322
- }
323
249
  function clampEnvWithWarn(name, fallback, min, max = Number.POSITIVE_INFINITY) {
324
250
  const n = numEnv(name, fallback);
325
251
  const used = Math.min(max, Math.max(min, n));
@@ -948,48 +874,41 @@ function parseStoreDomain(ctx) {
948
874
  : undefined,
949
875
  };
950
876
  }
951
- export function isRosterGateArmed(configCenter) {
952
- return configCenter !== undefined && !configCenter.dryRun && process.env.CONFIG_REQUIRE_ROSTER === "true";
877
+ export function isRosterGateArmed(webAdmin) {
878
+ return webAdmin !== undefined && !webAdmin.dryRun && process.env.CONFIG_REQUIRE_ROSTER === "true";
953
879
  }
954
880
  function parseModelDomain() {
955
- const gatewayBaseUrl = firstSetEnv("MODEL_GATEWAY_BASEURL");
956
- const modelId = env("MODEL_ID", "");
957
- const explicitProvider = process.env.MODEL_PROVIDER;
958
- if (explicitProvider !== undefined && explicitProvider !== "") {
959
- const refusal = modelProviderRefusal(explicitProvider);
881
+ const route = readModelRouteEnv(process.env);
882
+ const gatewayBaseUrl = route.gatewayBaseUrl;
883
+ const modelId = route.mainId;
884
+ if (route.providerSource === "explicit") {
885
+ const refusal = modelProviderRefusal(route.provider);
960
886
  if (refusal !== undefined) {
961
887
  throw new Error(refusal);
962
888
  }
963
889
  }
964
890
  for (const [envKey, raw, knob] of [
965
891
  ["MODEL_GATEWAY_BASEURL", gatewayBaseUrl ?? "", "MODEL_API_KEY"],
966
- ...csv("MODEL_GATEWAY_FALLBACK_URLS").map((u, i) => [`MODEL_GATEWAY_FALLBACK_URLS[${i}]`, u, "MODEL_API_KEY"]),
967
- ["ANTHROPIC_BASE_URL", firstSetEnv("ANTHROPIC_BASE_URL") ?? "", "ANTHROPIC_API_KEY"],
892
+ ...route.gatewayFallbackUrls.map((u, i) => [`MODEL_GATEWAY_FALLBACK_URLS[${i}]`, u, "MODEL_API_KEY"]),
893
+ ["ANTHROPIC_BASE_URL", route.anthropicBaseUrl ?? "", "ANTHROPIC_API_KEY"],
968
894
  ]) {
969
895
  const refusal = modelRouteUrlRefusal(envKey, raw, knob);
970
896
  if (refusal !== undefined) {
971
897
  throw new Error(refusal);
972
898
  }
973
899
  }
974
- const inferredAnthropicBaseUrl = firstSetEnv("ANTHROPIC_BASE_URL");
975
- const inferredAnthropicCred = firstSetEnv("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN");
976
- const provider = (() => {
977
- if (explicitProvider !== undefined && explicitProvider !== "")
978
- return explicitProvider;
979
- if (inferredAnthropicBaseUrl && inferredAnthropicCred) {
980
- CONFIG_NOTICES.push({
981
- level: "warn",
982
- event: "model_provider_inferred",
983
- fields: {
984
- provider: "anthropic",
985
- baseUrl: buildDisplaySafeUrl(inferredAnthropicBaseUrl),
986
- hint: "MODEL_PROVIDER is unset but an explicit ANTHROPIC base URL + credential are configured — inferred provider=anthropic (set MODEL_PROVIDER explicitly to override)",
987
- },
988
- });
989
- return "anthropic";
990
- }
991
- return "gateway";
992
- })();
900
+ const provider = route.provider;
901
+ if (route.providerSource === "inferred") {
902
+ CONFIG_NOTICES.push({
903
+ level: "warn",
904
+ event: "model_provider_inferred",
905
+ fields: {
906
+ provider: "anthropic",
907
+ baseUrl: buildDisplaySafeUrl(route.anthropicBaseUrl),
908
+ hint: "MODEL_PROVIDER is unset but an explicit ANTHROPIC base URL + credential are configured — inferred provider=anthropic (set MODEL_PROVIDER explicitly to override)",
909
+ },
910
+ });
911
+ }
993
912
  const extraBody = buildExtraBody();
994
913
  const thinkingDefault = parseDefaultThinking(process.env.MODEL_DEFAULT_THINKING);
995
914
  const envCompatDeclared = buildModelCompat({
@@ -1021,35 +940,40 @@ function parseModelDomain() {
1021
940
  ...(envCompat ? { compat: envCompat } : {}),
1022
941
  ...(extraBody ? { extraBody } : {}),
1023
942
  };
1024
- const cheapId = process.env.MODEL_CHEAP_ID;
1025
- if (cheapId && modelId === "") {
1026
- CONFIG_NOTICES.push({
1027
- level: "warn",
1028
- event: "model_cheap_slot_without_main",
1029
- fields: { hint: "MODEL_CHEAP_ID is set but MODEL_ID is not — the env cheap slot derives from the env main model, so it is not minted; the catalog (config.d/models.json or the config center) serves the roles. Set MODEL_ID to use the env slots, or unset MODEL_CHEAP_ID to silence this line." },
1030
- });
1031
- }
1032
- const cheapIsMain = cheapId !== undefined && cheapId !== "" && modelId !== "" && cheapId === modelId;
1033
- if (cheapIsMain) {
1034
- CONFIG_NOTICES.push({
1035
- level: "warn",
1036
- event: "config_env_entry_cheap_same_as_main",
1037
- fields: {
1038
- env: "MODEL_CHEAP_ID",
1039
- model: modelId,
1040
- hint: `MODEL_CHEAP_ID names the same model as MODEL_ID ("${modelId}") — the env cheap slot is the main gateway under a different id, ` +
1041
- `so the same id IS the main model: no cheap slot is minted and subagent / summarize stay on the main model (roles -> default); ` +
1042
- `any other MODEL_CHEAP_* key set here describes that slot and is not applied. Set MODEL_CHEAP_ID to a different (cheaper) model ` +
1043
- `id to run those roles cheap, or declare two differently-named entries in config.d/models.json to give one upstream id a second ` +
1044
- `shape (price / reasoning / window); unset MODEL_CHEAP_ID to silence this line.`,
1045
- },
1046
- });
943
+ const cheapSlot = route.cheapSlot;
944
+ switch (cheapSlot.kind) {
945
+ case "without_main":
946
+ CONFIG_NOTICES.push({
947
+ level: "warn",
948
+ event: "model_cheap_slot_without_main",
949
+ fields: { hint: "MODEL_CHEAP_ID is set but MODEL_ID is not — the env cheap slot derives from the env main model, so it is not minted; the catalog (config.d/models.json or the web-admin) serves the roles. Set MODEL_ID to use the env slots, or unset MODEL_CHEAP_ID to silence this line." },
950
+ });
951
+ break;
952
+ case "same_as_main":
953
+ CONFIG_NOTICES.push({
954
+ level: "warn",
955
+ event: "config_env_entry_cheap_same_as_main",
956
+ fields: {
957
+ env: "MODEL_CHEAP_ID",
958
+ model: modelId,
959
+ hint: `MODEL_CHEAP_ID names the same model as MODEL_ID ("${modelId}") — the env cheap slot is the main gateway under a different id, ` +
960
+ `so the same id IS the main model: no cheap slot is minted and subagent / summarize stay on the main model (roles -> default); ` +
961
+ `any other MODEL_CHEAP_* key set here describes that slot and is not applied. Set MODEL_CHEAP_ID to a different (cheaper) model ` +
962
+ `id to run those roles cheap, or declare two differently-named entries in config.d/models.json to give one upstream id a second ` +
963
+ `shape (price / reasoning / window); unset MODEL_CHEAP_ID to silence this line.`,
964
+ },
965
+ });
966
+ break;
967
+ case "absent":
968
+ case "minted":
969
+ break;
1047
970
  }
1048
- const cheapModel = cheapId && modelId !== "" && !cheapIsMain
971
+ const cheapMintedId = mintedCheapId(cheapSlot);
972
+ const cheapModel = cheapMintedId !== undefined
1049
973
  ? {
1050
974
  ...model,
1051
- id: cheapId,
1052
- name: cheapId,
975
+ id: cheapMintedId,
976
+ name: cheapMintedId,
1053
977
  input: ((v) => (v === undefined ? model.input : v ? ["text", "image"] : ["text"]))(parseBoolWord("MODEL_CHEAP_VISION", process.env.MODEL_CHEAP_VISION)),
1054
978
  ...buildModelCostKey(buildCostOrAbsent({
1055
979
  input: costEnv("MODEL_CHEAP_COST_INPUT", "declares-zero"),
@@ -1093,9 +1017,7 @@ function parseModelDomain() {
1093
1017
  }
1094
1018
  noticeAutoCompactAboveWindow("cheap", "MODEL_CHEAP_AUTO_COMPACT_TOKENS", cheapModel);
1095
1019
  }
1096
- const models = modelId === "" ? {} : { default: model, [model.id]: model };
1097
- if (cheapModel)
1098
- models[cheapModel.id] = cheapModel;
1020
+ const models = envLaneTable(modelId, model, cheapModel);
1099
1021
  const gatewayApiKey = process.env.MODEL_API_KEY;
1100
1022
  if (gatewayBaseUrl === undefined && gatewayApiKey !== undefined && gatewayApiKey !== "") {
1101
1023
  CONFIG_NOTICES.push({
@@ -1129,13 +1051,13 @@ function parseModelDomain() {
1129
1051
  const degradeOn = parseDegradeOn(csv("MODEL_DEGRADE_ON"));
1130
1052
  return {
1131
1053
  ...(gatewayBaseUrl !== undefined ? { gatewayBaseUrl, gatewayApiKey } : {}),
1132
- gatewayFallbackUrls: csv("MODEL_GATEWAY_FALLBACK_URLS"),
1054
+ gatewayFallbackUrls: route.gatewayFallbackUrls,
1133
1055
  ...(process.env.GATEWAY_MAX_RETRIES ? { gatewayMaxRetries: numEnvBounded("GATEWAY_MAX_RETRIES", "2", 0, 20) } : {}),
1134
- anthropic: firstSetEnv("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN")
1056
+ anthropic: route.anthropicRouteMounted
1135
1057
  ? {
1136
- apiKey: firstSetEnv("ANTHROPIC_API_KEY"),
1137
- authToken: firstSetEnv("ANTHROPIC_AUTH_TOKEN"),
1138
- baseUrl: firstSetEnv("ANTHROPIC_BASE_URL"),
1058
+ apiKey: route.anthropicApiKey,
1059
+ authToken: route.anthropicAuthToken,
1060
+ baseUrl: route.anthropicBaseUrl,
1139
1061
  version: process.env.ANTHROPIC_VERSION,
1140
1062
  cacheBreakpoints: boolEnv("ANTHROPIC_CACHE_BREAKPOINTS", true),
1141
1063
  ...(process.env.ANTHROPIC_MAX_RETRIES ? { maxRetries: numEnv("ANTHROPIC_MAX_RETRIES", "2") } : {}),
@@ -1155,11 +1077,12 @@ function parseModelDomain() {
1155
1077
  modelApiKeyEnv: {},
1156
1078
  modelApiKeys: {},
1157
1079
  modelRefusals: {},
1080
+ droppedCatalogEntries: [],
1158
1081
  modelQuotaWeights: {},
1159
1082
  tiers: {},
1160
1083
  projects: {},
1161
1084
  roles,
1162
- atModelAllowlist: [],
1085
+ atMention: DEFAULT_AT_MENTION,
1163
1086
  cascadeLadder: csv("MODEL_CASCADE_LADDER"),
1164
1087
  degrade: process.env.MODEL_DEGRADE_TO
1165
1088
  ? {
@@ -1272,9 +1195,6 @@ export function approvalGrantRefusal(names) {
1272
1195
  const over = allowLayerEntryCountIssue(APPROVAL_GRANT_ENV, names);
1273
1196
  if (over !== undefined)
1274
1197
  return `${over.message} — refusing to start rather than running with a truncated list`;
1275
- const empty = emptyToolSegmentEntryOf(names);
1276
- if (empty !== undefined)
1277
- return `${describeEmptyToolSegmentEntry(APPROVAL_GRANT_ENV, empty)} — refusing to start rather than running without it`;
1278
1198
  const inert = inertRuleEntriesOf(names, "allow");
1279
1199
  if (inert.length > 0)
1280
1200
  return `${describeInertRuleEntries(APPROVAL_GRANT_ENV, inert)} — the engine would grant nothing for ${inert.length === 1 ? "it" : "them"}; refusing to start rather than running with an allow rule that never takes effect`;
@@ -1375,7 +1295,7 @@ function parseApprovalDomain(ctx) {
1375
1295
  const trimmed = raw.trim();
1376
1296
  if (trimmed === "" || trimmed.toLowerCase() === "off")
1377
1297
  return [];
1378
- return trimmed.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
1298
+ return csvOf(trimmed);
1379
1299
  })(process.env.SENSITIVE_WRITE_PATTERNS),
1380
1300
  ...((decision) => (decision === undefined ? {} : { writeProtectedPaths: decision }))(parseWriteProtectedTableEnv(process.env.WRITE_PROTECTED_EXTRA, process.env.WRITE_PROTECTED_TABLE_REPLACE)),
1381
1301
  ...((raw) => {
@@ -1826,7 +1746,7 @@ function parseAuthDomain(ctx) {
1826
1746
  authToken: process.env.SERVICE_AUTH_TOKEN,
1827
1747
  authTokens,
1828
1748
  allowUnauthedWrites: boolEnv("ALLOW_UNAUTHED_WRITES", false),
1829
- corsOrigins: (process.env.CORS_ORIGIN ?? "").split(",").map((s) => s.trim()).filter(Boolean),
1749
+ corsOrigins: csv("CORS_ORIGIN"),
1830
1750
  principalHeader: headerNameEnv("PRINCIPAL_HEADER", "x-agent-principal"),
1831
1751
  requirePrincipal,
1832
1752
  autonomy: parseAutonomy(process.env.AUTONOMY),
@@ -2011,7 +1931,7 @@ function parseOrchestrationDomain(ctx) {
2011
1931
  ? {
2012
1932
  repoRoot: process.env.WORKTREE_REPO_ROOT,
2013
1933
  ...(process.env.WORKTREE_ALLOWED_ROOTS
2014
- ? { allowedRoots: process.env.WORKTREE_ALLOWED_ROOTS.split(",").map((s) => s.trim()).filter(Boolean) }
1934
+ ? { allowedRoots: csv("WORKTREE_ALLOWED_ROOTS") }
2015
1935
  : {}),
2016
1936
  ...(process.env.WORKTREE_BASE_COMMIT ? { commit: process.env.WORKTREE_BASE_COMMIT } : {}),
2017
1937
  }
@@ -2095,7 +2015,7 @@ function parseLimitsHttpDomain() {
2095
2015
  workspaceFileMaxBytes: optFinitePositiveEnv("WORKSPACE_FILE_MAX_BYTES") ?? 8 * 1024 * 1024,
2096
2016
  attachmentMaxBytes: clampEnvWithWarn("ATTACHMENT_MAX_BYTES", String(32 * 1024 * 1024), LIMITS_HTTP_FLOORS.attachmentMaxBytes),
2097
2017
  ...(process.env.ATTACHMENT_MIME_ALLOWLIST
2098
- ? { attachmentMimeAllowlist: process.env.ATTACHMENT_MIME_ALLOWLIST.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean) }
2018
+ ? { attachmentMimeAllowlist: csv("ATTACHMENT_MIME_ALLOWLIST").map((s) => s.toLowerCase()) }
2099
2019
  : {}),
2100
2020
  attachmentUnboundTtlMs: clampEnvWithWarn("ATTACHMENT_UNBOUND_TTL_MS", String(24 * 3600 * 1000), LIMITS_HTTP_FLOORS.attachmentUnboundTtlMs),
2101
2021
  infraCostRates: {
@@ -2125,7 +2045,7 @@ function parseLimitsHttpDomain() {
2125
2045
  toolResultTtlSec: (() => {
2126
2046
  const ttl = numEnv("TOOL_RESULT_TTL_SEC", "86400");
2127
2047
  if (ttl <= 0) {
2128
- throw new Error(`env TOOL_RESULT_TTL_SEC=${ttl} must be > 0 — it is the age (seconds) after which an offloaded tool result is reaped, so 0 or less puts the cutoff at or after now and every reaper tick would delete ALL of them; there is no "off": set a larger value to keep results longer (the config center refuses the same value)`);
2048
+ throw new Error(`env TOOL_RESULT_TTL_SEC=${ttl} must be > 0 — it is the age (seconds) after which an offloaded tool result is reaped, so 0 or less puts the cutoff at or after now and every reaper tick would delete ALL of them; there is no "off": set a larger value to keep results longer (the web-admin refuses the same value)`);
2129
2049
  }
2130
2050
  return ttl;
2131
2051
  })(),
@@ -2182,15 +2102,15 @@ function parseGitApiKind(raw, baseUrl) {
2182
2102
  }
2183
2103
  function parseIntegrationsDomain(ctx) {
2184
2104
  const { requirePrincipal, configProvider } = ctx;
2185
- const registryUrl = parseCenterBaseUrl("SEMA_REGISTRY_URL", process.env.SEMA_REGISTRY_URL, `It is the center base every config pull, fleet and lease request is sent to; setting it selects the center as the config source.`);
2186
- const registryLiveUrl = parseCenterBaseUrl("SEMA_REGISTRY_LIVE_URL", process.env.SEMA_REGISTRY_LIVE_URL, `It is the center base the fleet liveness endpoints (heartbeat, serving lease) are sent to; unset it to send them to SEMA_REGISTRY_URL.`);
2187
- const registryWebUrl = parseCenterBaseUrl("SEMA_REGISTRY_WEB_URL", process.env.SEMA_REGISTRY_WEB_URL, `It is the center's web console base this engine reports on GET /health (center.webBaseUrl); unset it if the center has no web console to link to.`);
2188
- const configCenter = registryUrl !== undefined ? parseConfigCenter(registryUrl, requirePrincipal, registryLiveUrl, registryWebUrl) : undefined;
2105
+ const baseUrl = parseCenterBaseUrl(WEB_ADMIN_ENV.url, process.env[WEB_ADMIN_ENV.url], `It is the center base every config pull, fleet and lease request is sent to; setting it selects the center as the config source.`);
2106
+ const liveBaseUrl = parseCenterBaseUrl(WEB_ADMIN_ENV.liveUrl, process.env[WEB_ADMIN_ENV.liveUrl], `It is the center base the fleet liveness endpoints (heartbeat, serving lease) are sent to; unset it to send them to ${WEB_ADMIN_ENV.url}.`);
2107
+ const webBaseUrl = parseCenterBaseUrl(WEB_ADMIN_ENV.webUrl, process.env[WEB_ADMIN_ENV.webUrl], `It is the center's web console base this engine reports on GET /health (center.webBaseUrl); unset it if the center has no web console to link to.`);
2108
+ const webAdmin = baseUrl !== undefined ? parseWebAdmin(baseUrl, requirePrincipal, liveBaseUrl, webBaseUrl) : undefined;
2189
2109
  const fleetGeneration = parseFleetGeneration(process.env.FLEET_GENERATION);
2190
2110
  const fleetAdvertiseAddress = parseFleetAdvertiseAddress(process.env.FLEET_ADVERTISE_ADDRESS);
2191
- fleetFenceLaneOf({ ...fleetGeneration, configCenter, ...fleetAdvertiseAddress });
2111
+ fleetFenceLaneOf({ ...fleetGeneration, webAdmin, ...fleetAdvertiseAddress });
2192
2112
  return {
2193
- pluginsAllowHosts: (process.env.PLUGINS_ALLOW_HOSTS ?? "github.com").split(",").map((h) => h.trim()).filter(Boolean),
2113
+ pluginsAllowHosts: csvOf(process.env.PLUGINS_ALLOW_HOSTS ?? "github.com"),
2194
2114
  configBootFetchBudgetMs: clampEnvWithWarn("CONFIG_BOOT_FETCH_BUDGET_MS", "1500", 0),
2195
2115
  gitApiBaseUrl: process.env.GIT_API_BASEURL,
2196
2116
  gitApiToken: process.env.GIT_API_TOKEN,
@@ -2198,7 +2118,7 @@ function parseIntegrationsDomain(ctx) {
2198
2118
  defaultScenario: env("DEFAULT_SCENARIO", "code"),
2199
2119
  skillsDir: env("SKILLS_DIR", defaultSkillsDir()),
2200
2120
  centerCredentialRenewLeadMs: parseCenterCredentialRenewLeadMs(process.env.CENTER_CREDENTIAL_RENEW_LEAD_MS),
2201
- configCenter,
2121
+ webAdmin,
2202
2122
  fleetLeaseFailClosed: parseFleetLeaseFailClosed(process.env.FLEET_LEASE_FAIL_CLOSED, requirePrincipal),
2203
2123
  ...fleetGeneration,
2204
2124
  fleetScope: parseFleetScope(process.env.FLEET_SCOPE),
@@ -2256,9 +2176,9 @@ export function fleetFenceLaneOf(cfg) {
2256
2176
  return undefined;
2257
2177
  const laneConfigured = centerLaneConfigured(cfg);
2258
2178
  if (laneConfigured && cfg.fleetAdvertiseAddress !== undefined)
2259
- return { center: cfg.configCenter, generation };
2179
+ return { center: cfg.webAdmin, generation };
2260
2180
  const missing = [
2261
- ...(laneConfigured ? [] : [`the center lane (SEMA_REGISTRY_URL + ${CENTER_CREDENTIAL_ENV.token} or ${CENTER_CREDENTIAL_ENV.tokenFile} + SEMA_REGISTRY_WORKER)`]),
2181
+ ...(laneConfigured ? [] : [`the center lane (${WEB_ADMIN_ENV.url} + ${WEB_ADMIN_ENV.token} or ${WEB_ADMIN_ENV.tokenFile} + ${WEB_ADMIN_ENV.worker})`]),
2262
2182
  ...(cfg.fleetAdvertiseAddress === undefined ? ["the announce address (FLEET_ADVERTISE_ADDRESS)"] : []),
2263
2183
  ];
2264
2184
  throw fleetEnvRefusal("member_unfenced", `fleet member cannot be fenced — env FLEET_GENERATION=${JSON.stringify(generation)} makes this engine a fleet member, ` +
@@ -2293,15 +2213,15 @@ function parseCenterBaseUrl(name, raw, role) {
2293
2213
  throw new Error(`${refusal}. ${role}`);
2294
2214
  return raw;
2295
2215
  }
2296
- function parseConfigCenter(baseUrl, requirePrincipal, liveBaseUrl, webBaseUrl) {
2297
- const worker = process.env.SEMA_REGISTRY_WORKER || undefined;
2216
+ function parseWebAdmin(baseUrl, requirePrincipal, liveBaseUrl, webBaseUrl) {
2217
+ const worker = process.env[WEB_ADMIN_ENV.worker] || undefined;
2298
2218
  const declared = readCenterCredentialDeclaration();
2299
2219
  return {
2300
2220
  baseUrl,
2301
2221
  ...(liveBaseUrl !== undefined ? { liveBaseUrl } : {}),
2302
2222
  ...(webBaseUrl !== undefined ? { webBaseUrl } : {}),
2303
2223
  credential: createCenterCredentialSource(declared.from === "file" ? { ...declared, multiTenant: requirePrincipal } : declared, worker),
2304
- dryRun: boolEnv("SEMA_REGISTRY_DRY_RUN", false),
2224
+ dryRun: boolEnv(WEB_ADMIN_ENV.dryRun, false),
2305
2225
  worker,
2306
2226
  };
2307
2227
  }
@@ -2388,7 +2308,7 @@ const STORE_GROUP_KEYS = [
2388
2308
  ];
2389
2309
  const MODEL_PLANE_GROUP_KEYS = [
2390
2310
  "gatewayBaseUrl", "gatewayApiKey", "gatewayFallbackUrls", "gatewayMaxRetries", "anthropic", "resilience", "model", "envModel", "models",
2391
- "modelApiKeyEnv", "modelApiKeys", "modelRefusals", "modelQuotaWeights", "tiers", "projects", "roles", "atModelAllowlist", "cascadeLadder", "degrade",
2311
+ "modelApiKeyEnv", "modelApiKeys", "modelRefusals", "droppedCatalogEntries", "modelQuotaWeights", "tiers", "projects", "roles", "atMention", "cascadeLadder", "degrade",
2392
2312
  ];
2393
2313
  const APPROVAL_GROUP_KEYS = [
2394
2314
  "approvalRequire", "approvalDeny", "approvalTimeoutSec", "approvalAutoBudget", "approvalNeverAuto", "approvalGrant",
@@ -2428,7 +2348,7 @@ const LIMITS_HTTP_GROUP_KEYS = [
2428
2348
  const OBSERVABILITY_GROUP_KEYS = ["metricsToken", "traceToken", "toolTrace", "traceThinking", "logLevel", "otel"];
2429
2349
  const INTEGRATIONS_GROUP_KEYS = [
2430
2350
  "pluginsAllowHosts", "mcpServers", "a2aPeers", "a2aServe", "configBootFetchBudgetMs", "gitApiBaseUrl", "gitApiToken", "gitApiKind", "defaultScenario",
2431
- "skillsDir", "centerCredentialRenewLeadMs", "configCenter", "fleetLeaseFailClosed", "fleetGeneration", "fleetScope", "fleetAdvertiseAddress", "fleetLeaseOutageMaxMs", "configProvider", "configLocalDir",
2351
+ "skillsDir", "centerCredentialRenewLeadMs", "webAdmin", "fleetLeaseFailClosed", "fleetGeneration", "fleetScope", "fleetAdvertiseAddress", "fleetLeaseOutageMaxMs", "configProvider", "configLocalDir",
2432
2352
  ];
2433
2353
  export const CONFIG_GROUP_KEYS = {
2434
2354
  store: STORE_GROUP_KEYS,
@@ -2490,6 +2410,10 @@ export function loadConfig() {
2490
2410
  const limitsHttp = parseLimitsHttpDomain();
2491
2411
  const observability = parseObservabilityDomain();
2492
2412
  const integrations = parseIntegrationsDomain({ requirePrincipal, configProvider });
2413
+ const centerIdentity = centerIdentityRefusal({ authBridgeIssuer: auth.authBridgeIssuer, webAdmin: integrations.webAdmin });
2414
+ if (centerIdentity !== undefined) {
2415
+ throw new Error(centerIdentity);
2416
+ }
2493
2417
  const { directApprovalDoor, durableApproval: dgDurable, approvalHmacKeys: dgHmacKeys } = approval;
2494
2418
  const { principalJwtPubkeys, principalJwtIss, principalJwtAud, operatorPrincipals: dgOperators, authTokens } = auth;
2495
2419
  const directDoorActive = directApprovalDoor && dgDurable && dgHmacKeys.length > 0 && principalJwtPubkeys.length > 0 && !!principalJwtIss && !!principalJwtAud;
@@ -2527,15 +2451,9 @@ export function loadConfig() {
2527
2451
  throw new Error(laneRequiredRefusal);
2528
2452
  }
2529
2453
  {
2530
- const envLaneDispatchable = !isRosterGateArmed(integrations.configCenter);
2531
- const refusal = modelRouteEndpointRefusal({
2532
- gatewayBaseUrl: modelPlane.gatewayBaseUrl,
2533
- gatewayFallbackUrls: modelPlane.gatewayFallbackUrls,
2534
- anthropic: modelPlane.anthropic,
2535
- models: envLaneDispatchable ? modelPlane.models : {},
2536
- });
2537
- if (refusal !== undefined) {
2538
- throw new Error(refusal);
2454
+ const routeGap = judgeModelRouteGap(process.env, { envLaneDispatchable: !isRosterGateArmed(integrations.webAdmin) });
2455
+ if (routeGap.gap !== "none") {
2456
+ throw new Error(routeGap.reason);
2539
2457
  }
2540
2458
  }
2541
2459
  EXEC_LANE_NOTICE = buildExecLaneNotice(orchestration.remoteExec);
@@ -144,7 +144,7 @@ export declare function laneRequiredEnv(lane: RemoteExecProvider): readonly stri
144
144
  *
145
145
  * 🔴 病(S-1008,test d327-g6-engine-7112 OBS-4 / OBS-5;2026-10-03 亲核):修前**两份句子**。运行期真拒启的是 `config.ts` 手写 throw
146
146
  * (点名 `REQUIRE_EXECUTION_LANE` / `REMOTE_EXEC` + 修法,用户在 stderr 读到的就是它);注册表条目另写了一句短的
147
- * 「the deployment asked to refuse this state」,而它只在 `config-center/stage-limits.ts` 的全表校验里被调 —— 候选视图只装
147
+ * 「the deployment asked to refuse this state」,而它只在 `web-admin-client/stage-limits.ts` 的全表校验里被调 —— 候选视图只装
148
148
  * `BATCH1_LIMIT_KEYS` 的限额键,带不上这两键 ⇒ 对本条恒 `next === live`,**那一句没有任何读者**(test 六个拒启格 stderr 里零命中)。
149
149
  * 改一份另一份不跟。现形:boot 的 throw 与注册表条目的 `check` **都**调本函数 —— 一只判据、一句话,没有第二份文本可漂。
150
150
  *
@@ -21,7 +21,7 @@
21
21
  * · `worker` / `expectedGeneration`:缺席对缺席(非 fleet 成员没有代际,不带 `expectedGeneration` 的请求是单机形,不是通配)。
22
22
  * · `instanceId`:请求带了才比(它是引擎每进程自铸的身份,`/health` 上可读);「点名的是不是别的进程」与 run 行同一只判据
23
23
  * `runProvenElsewhere`。
24
- * 引擎自己的 worker / instanceId 读不出 ⇒ 带了这一项的请求不符(认不出自己就不认任何点名;没配 `SEMA_REGISTRY_WORKER` 的引擎因此
24
+ * 引擎自己的 worker / instanceId 读不出 ⇒ 带了这一项的请求不符(认不出自己就不认任何点名;没配 `SEMA_WEB_ADMIN_WORKER` 的引擎因此
25
25
  * 只能用停止信号排空)。
26
26
  * 5. **进入那一拍让出在飞**:本副本可抢占的在飞腿经 core `TaskSpec.preemptSignal` 让出(在下一个干净的回合边界耐久停驻,
27
27
  * `resource_limit` / `preempt`;不合格的腿上 core 静默不动,等到期限硬停 —— 既有 `crashed` 臂);活的审批卡按断连同路改路耐久停驻