@sema-agent/server 7.123.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 (224) hide show
  1. package/MIGRATION.md +132 -0
  2. package/README.md +2 -2
  3. package/README.zh-CN.md +2 -2
  4. package/USAGE.md +53 -51
  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.js +1 -1
  18. package/dist/auth-bridge.d.ts +8 -8
  19. package/dist/auth-bridge.js +6 -6
  20. package/dist/auth-keys.d.ts +2 -2
  21. package/dist/auto-mode-face.d.ts +1 -1
  22. package/dist/boot/budget-tracing.js +1 -1
  23. package/dist/boot/execution-env.js +1 -0
  24. package/dist/boot/leader.d.ts +1 -1
  25. package/dist/boot/memory-consolidation.d.ts +4 -4
  26. package/dist/boot/org-memory.d.ts +3 -3
  27. package/dist/boot/org-memory.js +5 -5
  28. package/dist/boot/resolve-spec.js +7 -7
  29. package/dist/boot/runner-deps.d.ts +1 -1
  30. package/dist/boot/runtime-caps.d.ts +1 -1
  31. package/dist/boot/runtime-caps.js +10 -10
  32. package/dist/boot/shutdown.d.ts +2 -2
  33. package/dist/boot/shutdown.js +2 -2
  34. package/dist/boot/side-query-lane.d.ts +3 -3
  35. package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
  36. package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
  37. package/dist/boot/stage-03-store-backend.d.ts +2 -2
  38. package/dist/boot/stage-04-stores.d.ts +1 -1
  39. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  40. package/dist/boot/stage-06-runners.d.ts +2 -2
  41. package/dist/boot/stage-06-runners.js +6 -6
  42. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  43. package/dist/boot/stage-07-capability-layer.js +6 -5
  44. package/dist/boot/stage-08-reapers.d.ts +3 -3
  45. package/dist/boot/stage-08-reapers.js +3 -3
  46. package/dist/boot/stage-09-leader.d.ts +3 -3
  47. package/dist/boot/stage-09-leader.js +7 -7
  48. package/dist/boot/stage-10-http-server.d.ts +3 -3
  49. package/dist/boot/stage-10-http-server.js +19 -19
  50. package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
  51. package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
  52. package/dist/boot/{config-center.js → web-admin.js} +38 -22
  53. package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
  54. package/dist/brain.d.ts +1 -1
  55. package/dist/brain.js +1 -1
  56. package/dist/budget.d.ts +3 -3
  57. package/dist/canonical-json.d.ts +2 -2
  58. package/dist/capabilities/center-plugins.d.ts +1 -1
  59. package/dist/capabilities/center-wiring.d.ts +12 -5
  60. package/dist/capabilities/center-wiring.js +11 -2
  61. package/dist/capabilities/composite-members.d.ts +1 -1
  62. package/dist/capabilities/composite-members.js +1 -1
  63. package/dist/capabilities/scenarios.js +2 -2
  64. package/dist/capabilities/team.d.ts +1 -1
  65. package/dist/capabilities/tool-defer.d.ts +1 -1
  66. package/dist/center-credential-renewer.d.ts +1 -1
  67. package/dist/center-credential-renewer.js +7 -7
  68. package/dist/center-credential.d.ts +9 -9
  69. package/dist/center-credential.js +6 -6
  70. package/dist/config-catalog.d.ts +7 -10
  71. package/dist/config-catalog.js +15 -15
  72. package/dist/config-d-migration.d.ts +32 -0
  73. package/dist/config-d-migration.js +59 -0
  74. package/dist/config-invariants.d.ts +14 -4
  75. package/dist/config-invariants.js +31 -2
  76. package/dist/config-lkg.d.ts +26 -6
  77. package/dist/config-lkg.js +43 -14
  78. package/dist/config-provider.d.ts +23 -19
  79. package/dist/config-provider.js +24 -26
  80. package/dist/config-types.d.ts +52 -51
  81. package/dist/config.d.ts +9 -46
  82. package/dist/config.js +33 -92
  83. package/dist/execution-lane-caps.d.ts +1 -1
  84. package/dist/fleet/drain-entry.d.ts +1 -1
  85. package/dist/fleet/engine-signal.d.ts +8 -8
  86. package/dist/fleet/fleet-bus.d.ts +12 -3
  87. package/dist/fleet/fleet-bus.js +13 -9
  88. package/dist/fleet/serving-lease.d.ts +3 -3
  89. package/dist/fleet/serving-lease.js +1 -1
  90. package/dist/fleet-client.d.ts +7 -7
  91. package/dist/fleet-client.js +5 -4
  92. package/dist/fleet-lease.d.ts +3 -3
  93. package/dist/fleet-lease.js +2 -2
  94. package/dist/host-lsp-manager.d.ts +1 -1
  95. package/dist/hosted-posture.d.ts +4 -4
  96. package/dist/http/admission.js +4 -4
  97. package/dist/http/checkpoint-ctx.d.ts +20 -3
  98. package/dist/http/checkpoint-ctx.js +13 -5
  99. package/dist/http/dispatch.js +2 -2
  100. package/dist/http/durable-run-leg.js +2 -1
  101. package/dist/http/health-self-report.d.ts +4 -4
  102. package/dist/http/health-self-report.js +3 -3
  103. package/dist/http/idempotency.d.ts +1 -1
  104. package/dist/http/resume-legs.d.ts +4 -3
  105. package/dist/http/resume-legs.js +31 -18
  106. package/dist/http/route-ctx.d.ts +8 -0
  107. package/dist/http/routes/admin-config-refresh.d.ts +1 -1
  108. package/dist/http/routes/admin-config-refresh.js +1 -1
  109. package/dist/http/routes/admin-drain.d.ts +1 -1
  110. package/dist/http/routes/admin-drain.js +5 -4
  111. package/dist/http/routes/approvals-assistant.js +4 -4
  112. package/dist/http/routes/capabilities.js +5 -5
  113. package/dist/http/routes/config-catalog.js +2 -3
  114. package/dist/http/routes/diagnostics.js +1 -1
  115. package/dist/http/routes/runs.js +8 -4
  116. package/dist/http/routes/sessions-list.js +2 -1
  117. package/dist/http/routes/sessions.js +1 -1
  118. package/dist/http/routes/side-query.js +3 -3
  119. package/dist/http/routes/tasks.js +3 -2
  120. package/dist/http/run-meta.d.ts +1 -1
  121. package/dist/http/run-meta.js +2 -2
  122. package/dist/http/server.d.ts +7 -7
  123. package/dist/http/server.js +1 -1
  124. package/dist/http/stored-body-replay.js +3 -2
  125. package/dist/index.d.ts +0 -1
  126. package/dist/index.js +0 -1
  127. package/dist/key-resolver.d.ts +1 -1
  128. package/dist/leader/wire.d.ts +2 -2
  129. package/dist/leader/wire.js +2 -1
  130. package/dist/main.js +1 -1
  131. package/dist/mcp-tool-faces.d.ts +2 -2
  132. package/dist/model-entry-refusal.d.ts +3 -3
  133. package/dist/model-entry-refusal.js +4 -4
  134. package/dist/model-provider.d.ts +2 -2
  135. package/dist/model-route-endpoint.d.ts +1 -1
  136. package/dist/model-route-url.d.ts +1 -1
  137. package/dist/model-route-url.js +2 -1
  138. package/dist/model-select.d.ts +24 -21
  139. package/dist/model-select.js +22 -17
  140. package/dist/observability/metrics.d.ts +1 -1
  141. package/dist/org-memory-admission.d.ts +1 -1
  142. package/dist/plugins/approval-ask-store-file.d.ts +4 -2
  143. package/dist/plugins/approval-ask-store-file.js +3 -3
  144. package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
  145. package/dist/plugins/approval-ask-store-memory.js +10 -4
  146. package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
  147. package/dist/plugins/approval-ask-store-sql.js +20 -13
  148. package/dist/plugins/caching-session-store.d.ts +0 -10
  149. package/dist/plugins/caching-session-store.js +0 -3
  150. package/dist/plugins/file-run-store.js +9 -3
  151. package/dist/plugins/leader-run-store-sql.d.ts +3 -2
  152. package/dist/plugins/leader-run-store-sql.js +3 -2
  153. package/dist/plugins/local-session-store.d.ts +20 -27
  154. package/dist/plugins/local-session-store.js +19 -17
  155. package/dist/plugins/memory-run-store.js +9 -3
  156. package/dist/plugins/one-time-migrations.js +26 -2
  157. package/dist/plugins/scheduler-support.d.ts +26 -7
  158. package/dist/plugins/scheduler-support.js +96 -53
  159. package/dist/plugins/store-backend.js +1 -1
  160. package/dist/plugins/store-contracts.d.ts +20 -4
  161. package/dist/plugins/web-search.d.ts +1 -1
  162. package/dist/plugins/web-search.js +2 -1
  163. package/dist/posture-source.d.ts +1 -1
  164. package/dist/read-face-posture.d.ts +3 -3
  165. package/dist/read-face-posture.js +1 -1
  166. package/dist/run-local.js +6 -3
  167. package/dist/runtime-caps-resolver.d.ts +4 -4
  168. package/dist/runtime-caps-resolver.js +2 -2
  169. package/dist/runtime-governance.d.ts +5 -5
  170. package/dist/sealed-key.d.ts +4 -4
  171. package/dist/sealed-key.js +3 -2
  172. package/dist/security.d.ts +3 -3
  173. package/dist/service-version.d.ts +1 -1
  174. package/dist/service-version.js +5 -0
  175. package/dist/session-titler.d.ts +0 -11
  176. package/dist/session-titler.js +3 -5
  177. package/dist/shared-memory-scope-authorizer.d.ts +4 -4
  178. package/dist/task-a2a.d.ts +1 -1
  179. package/dist/tool-approval.d.ts +43 -4
  180. package/dist/tool-approval.js +147 -98
  181. package/dist/trace/core-keyset-guard.d.ts +1 -1
  182. package/dist/trace/project.d.ts +3 -3
  183. package/dist/trace/redact.d.ts +2 -23
  184. package/dist/trace/redact.js +1 -6
  185. package/dist/trace/self-minted-wire.d.ts +147 -3
  186. package/dist/trace/self-minted-wire.js +50 -9
  187. package/dist/url-refusal.d.ts +35 -0
  188. package/dist/url-refusal.js +15 -0
  189. package/dist/user-authored-text.d.ts +31 -0
  190. package/dist/user-authored-text.js +18 -0
  191. package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
  192. package/dist/{config-center → web-admin-client}/apply-effective.js +100 -119
  193. package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
  194. package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
  195. package/dist/{config-center → web-admin-client}/center-request.js +5 -4
  196. package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
  197. package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
  198. package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
  199. package/dist/{config-center → web-admin-client}/facade.js +2 -2
  200. package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
  201. package/dist/{config-center → web-admin-client}/http-client.js +12 -12
  202. package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
  203. package/dist/{config-center → web-admin-client}/read-face.js +4 -4
  204. package/dist/{config-center → web-admin-client}/read-warnings.d.ts +23 -16
  205. package/dist/{config-center → web-admin-client}/read-warnings.js +33 -23
  206. package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
  207. package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
  208. package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
  209. package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
  210. package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
  211. package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
  212. package/dist/{config-center → web-admin-client}/types.d.ts +12 -39
  213. package/package.json +2 -2
  214. package/skills/find-skills.md +2 -2
  215. package/dist/url-shape.d.ts +0 -31
  216. package/dist/url-shape.js +0 -41
  217. /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
  218. /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
  219. /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
  220. /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
  221. /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
  222. /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
  223. /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
  224. /package/dist/{config-center → web-admin-client}/types.js +0 -0
@@ -1,5 +1,7 @@
1
- import { resolveActiveTiers } from "@sema-agent/settings-schema";
1
+ import { expandTiers, resolveTaskModel as coreResolveTaskModel } from "@sema-agent/core";
2
+ import { WEB_ADMIN_ENV, narrowAtMention, resolveActiveTiers, resolveCatalogDefaultModel } from "@sema-agent/settings-schema";
2
3
  import { modelKeyPoison } from "../key-resolver.js";
4
+ import { readAtMention } from "../model-select.js";
3
5
  import { applyAutoCompactWindow, buildModelCostKey, parseAutonomy, unmatchableToolNamesRefusal } from "../config.js";
4
6
  import { buildModelCompat, readModelCompat } from "../model-compat.js";
5
7
  import { buildModelHeadersKey } from "../model-entry-headers.js";
@@ -64,41 +66,27 @@ export function mutateInPlace(target, source) {
64
66
  }
65
67
  const appliedGeneration = new WeakMap();
66
68
  const envRolesBaseline = new WeakMap();
67
- export const UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY = "\u0000<unparsable-at-model-allowlist-entry>";
68
- function stageAtModelAllowlist(raw, models, logger) {
69
- if (raw === undefined || raw === null)
70
- return [];
71
- if (!Array.isArray(raw)) {
72
- logger?.warn("sema_registry_at_model_allowlist_invalid", {
73
- type: typeof raw,
74
- hint: "models.atModelAllowlist must be an array of catalog model names — an unreadable allowlist is treated as ALLOWING NOTHING (fail-closed: a broken restriction must not read as 'no restriction'); tasks still run on the default/role models",
69
+ function stageAtMention(raw, usableNames, logger) {
70
+ const read = readAtMention(raw);
71
+ if (read === undefined) {
72
+ logger?.warn("web_admin_at_mention_invalid", {
73
+ hint: "models.atMention does not read as {mode:\"all\"} | {mode:\"none\"} | {mode:\"only\", models:[…]} — treated as {mode:\"none\"} (fail-closed: a broken restriction must not read as 'no restriction'); tasks still run on the default/role models",
75
74
  });
76
- return [UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY];
75
+ return { mode: "none" };
77
76
  }
78
- const names = [];
79
- const bad = [];
80
- for (const entry of raw) {
81
- if (typeof entry === "string" && entry.length > 0)
82
- names.push(entry);
83
- else {
84
- bad.push(typeof entry === "string" ? "<empty-string>" : `<non-string:${typeof entry}>`);
85
- names.push(UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY);
77
+ const narrowed = narrowAtMention(read, usableNames);
78
+ if (read.mode === "only") {
79
+ const kept = new Set(narrowed.mode === "only" ? narrowed.models : []);
80
+ const dangling = read.models.filter((n) => !kept.has(n));
81
+ if (dangling.length > 0) {
82
+ logger?.warn("web_admin_at_mention_dangling", {
83
+ dangling,
84
+ narrowedTo: narrowed.mode,
85
+ hint: "these models.atMention names are not in this generation's usable catalog (not enabled, or unavailable on this host) — they allow nothing; when none is left the reading is {mode:\"none\"} and every user model pick is refused",
86
+ });
86
87
  }
87
88
  }
88
- if (bad.length > 0) {
89
- logger?.warn("sema_registry_at_model_allowlist_bad_entries", {
90
- bad,
91
- hint: "non-string / empty entries in models.atModelAllowlist allow NOTHING (they are kept as unresolvable placeholders, never dropped — dropping them would silently WIDEN the allowlist, and an all-bad list would collapse to the empty = allow-everything form)",
92
- });
93
- }
94
- const dangling = names.filter((n) => n !== UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY && models[n] === undefined);
95
- if (dangling.length > 0) {
96
- logger?.warn("sema_registry_at_model_allowlist_dangling", {
97
- dangling,
98
- hint: "these @-mention allowlist entries name a model that is not in the enabled catalog — they allow nothing; an allowlist that is ENTIRELY dangling refuses every user model pick",
99
- });
100
- }
101
- return names;
89
+ return narrowed;
102
90
  }
103
91
  export function applyEffective(config, eff, logger, opts) {
104
92
  const staged = stageEffective(config, eff, logger, opts);
@@ -106,7 +94,7 @@ export function applyEffective(config, eff, logger, opts) {
106
94
  const live = appliedGeneration.get(config);
107
95
  const ordered = opts.generation !== undefined || staged.version > 0;
108
96
  if (ordered && live !== undefined && gen < live) {
109
- logger?.warn("sema_registry_stale_generation_refused", { staged: staged.version, stagedOrdinal: gen, live, note: "an older staged generation must not overwrite a newer committed one — refused whole; next poll replays" });
97
+ logger?.warn("web_admin_stale_generation_refused", { staged: staged.version, stagedOrdinal: gen, live, note: "an older staged generation must not overwrite a newer committed one — refused whole; next poll replays" });
110
98
  return false;
111
99
  }
112
100
  const mp = staged.modelPlaneGroup.stages.models;
@@ -115,7 +103,7 @@ export function applyEffective(config, eff, logger, opts) {
115
103
  opts.swapPlane({ models: mp.models, tiers: mp.activeTiers });
116
104
  }
117
105
  catch (err) {
118
- logger?.warn("sema_registry_model_plane_swap_refused", {
106
+ logger?.warn("web_admin_model_plane_swap_refused", {
119
107
  version: staged.version,
120
108
  error: err instanceof Error ? err.message : String(err),
121
109
  note: "the Runner refused the candidate model plane (swapDeps screens the whole candidate atomically — the current generation stays in force untouched); candidate refused whole, re-judged next poll (/health shows target≠applied while it persists)",
@@ -143,13 +131,13 @@ export function applyEffective(config, eff, logger, opts) {
143
131
  opts.report({ ...report, fullyApplied: refusedGroups(report).length === 0, modelRefusals: modelRefusalVerdictOf(staged.modelPlaneGroup) });
144
132
  }
145
133
  if (staged.limits.changed.length > 0)
146
- logger?.info("sema_registry_limits", { version: staged.version, keys: staged.limits.changed });
134
+ logger?.info("web_admin_limits", { version: staged.version, keys: staged.limits.changed });
147
135
  if (mp)
148
- logger?.info("sema_registry_models", mp.infoLine);
136
+ logger?.info("web_admin_models", mp.infoLine);
149
137
  if (staged.approvalGate.next !== undefined)
150
- logger?.info("sema_registry_runtime", { version: staged.version, approvalRequire: staged.approvalGate.next });
138
+ logger?.info("web_admin_runtime", { version: staged.version, approvalRequire: staged.approvalGate.next });
151
139
  if (Object.keys(staged.hot.infoLine).length > 0)
152
- logger?.info("sema_registry_runtime_hot", staged.hot.infoLine);
140
+ logger?.info("web_admin_runtime_hot", staged.hot.infoLine);
153
141
  for (const n of staged.modelPlaneGroup.stages.collab?.notices ?? []) {
154
142
  if (n.level === "warn")
155
143
  logger?.warn(n.msg, n.fields);
@@ -157,7 +145,7 @@ export function applyEffective(config, eff, logger, opts) {
157
145
  logger?.info(n.msg, n.fields);
158
146
  }
159
147
  if (Object.keys(staged.projects).length > 0)
160
- logger?.info("sema_registry_projects", { projects: Object.keys(staged.projects) });
148
+ logger?.info("web_admin_projects", { projects: Object.keys(staged.projects) });
161
149
  return true;
162
150
  }
163
151
  function commitStaged(config, staged) {
@@ -172,7 +160,7 @@ function commitStaged(config, staged) {
172
160
  config.model = config.models.default;
173
161
  mutateInPlace(config.roles, mp.roles);
174
162
  mutateInPlace(config.tiers, mp.activeTiers);
175
- config.atModelAllowlist = mp.atModelAllowlist;
163
+ config.atMention = mp.atMention;
176
164
  }
177
165
  if (staged.approvalGate.next !== undefined)
178
166
  config.approvalRequire = staged.approvalGate.next;
@@ -191,7 +179,7 @@ function commitStaged(config, staged) {
191
179
  }
192
180
  export const MODEL_PLANE_DOMAINS = ["models", "collab"];
193
181
  const MODEL_PLANE_SURFACES = {
194
- models: ["models", "model", "modelRefusals", "droppedCatalogEntries", "modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "roles", "tiers", "atModelAllowlist"],
182
+ models: ["models", "model", "modelRefusals", "droppedCatalogEntries", "modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "roles", "tiers", "atMention"],
195
183
  collab: ["collabWorkflows"],
196
184
  };
197
185
  const MODEL_PLANE_GROUP_KEYS = MODEL_PLANE_DOMAINS.flatMap((d) => MODEL_PLANE_SURFACES[d]);
@@ -239,7 +227,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
239
227
  logger?.warn(refusalEventOf(r.reason), refusalWarnFields(name, r));
240
228
  const refuse = (code, reason, detail = {}) => {
241
229
  const invariant = PLANE_REFUSAL_INVARIANT_OF[code];
242
- logger?.warn("sema_registry_model_plane_refused", {
230
+ logger?.warn("web_admin_model_plane_refused", {
243
231
  version: typeof eff.version === "number" ? eff.version : 0,
244
232
  invariant,
245
233
  reason,
@@ -258,16 +246,16 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
258
246
  const envDefaults = { maxTokens: envModel.maxTokens, contextWindow: envModel.contextWindow, reasoning: envModel.reasoning, charsPerToken: envModel.charsPerToken, defaultThinking: envModel.defaultThinking, compat: envModel.compat };
259
247
  for (const m of usable) {
260
248
  if (m.baseUrl)
261
- logger?.warn("sema_registry_model_base_url", { model: m.name, baseUrl: buildDisplaySafeUrl(m.baseUrl), hint: "registry-directed gateway override — this model's key authenticates AGAINST THIS URL" });
249
+ logger?.warn("web_admin_model_base_url", { model: m.name, baseUrl: buildDisplaySafeUrl(m.baseUrl), hint: "registry-directed gateway override — this model's key authenticates AGAINST THIS URL" });
262
250
  models[m.name] = toModel(m, envDefaults, logger);
263
251
  if (m.sealedApiKey) {
264
252
  const s = m.sealedApiKey;
265
253
  if (!sealedKeys) {
266
- logger?.warn("sema_registry_model_sealed_key_no_store", { model: m.name, publicKeyId: s.publicKeyId, hint: "no sealed-key store on this host (boot custody init failed or lane unwired) — key POISONED: tasks on this model fail loud (no apiKeyEnv fallback, no gateway fallback)" });
254
+ logger?.warn("web_admin_model_sealed_key_no_store", { model: m.name, publicKeyId: s.publicKeyId, hint: "no sealed-key store on this host (boot custody init failed or lane unwired) — key POISONED: tasks on this model fail loud (no apiKeyEnv fallback, no gateway fallback)" });
267
255
  modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "no_key_store", publicKeyId: s.publicKeyId });
268
256
  }
269
257
  else if (!sealedKeys.has(s.publicKeyId)) {
270
- logger?.warn("sema_registry_model_sealed_key_no_private_key", { model: m.name, publicKeyId: s.publicKeyId, held: sealedKeys.ids(), hint: "ciphertext targets a private key this host doesn't hold (rotated away / other host) — key POISONED: tasks on this model fail loud until the operator re-pastes the key in the web UI (it seals against the newest registered public key)" });
258
+ logger?.warn("web_admin_model_sealed_key_no_private_key", { model: m.name, publicKeyId: s.publicKeyId, held: sealedKeys.ids(), hint: "ciphertext targets a private key this host doesn't hold (rotated away / other host) — key POISONED: tasks on this model fail loud until the operator re-pastes the key in the web UI (it seals against the newest registered public key)" });
271
259
  modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "stale_ciphertext", publicKeyId: s.publicKeyId });
272
260
  }
273
261
  else {
@@ -275,7 +263,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
275
263
  modelApiKeys[m.name] = sealedKeys.open(s.ciphertext, s.publicKeyId);
276
264
  }
277
265
  catch (err) {
278
- logger?.warn("sema_registry_model_sealed_key_open_failed", { model: m.name, publicKeyId: s.publicKeyId, err: String(err), hint: "corrupt/foreign ciphertext — key POISONED: tasks on this model fail loud (no apiKeyEnv fallback, no gateway fallback)" });
266
+ logger?.warn("web_admin_model_sealed_key_open_failed", { model: m.name, publicKeyId: s.publicKeyId, err: String(err), hint: "corrupt/foreign ciphertext — key POISONED: tasks on this model fail loud (no apiKeyEnv fallback, no gateway fallback)" });
279
267
  modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "sealed_decrypt_failed", publicKeyId: s.publicKeyId, errKind: err instanceof Error ? err.name : "unknown" });
280
268
  }
281
269
  }
@@ -283,7 +271,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
283
271
  else if (m.apiKeyEnv) {
284
272
  modelApiKeyEnv[m.name] = m.apiKeyEnv;
285
273
  if (!process.env[m.apiKeyEnv])
286
- logger?.warn("sema_registry_model_key_env_missing", { model: m.name, apiKeyEnv: m.apiKeyEnv, hint: `env var ${m.apiKeyEnv} not set (or empty) in this service — key POISONED: tasks on this model fail loud naming ${m.apiKeyEnv} (no gateway fallback) until it is set; or drop apiKeyEnv from the entry to use the gateway key on purpose` });
274
+ logger?.warn("web_admin_model_key_env_missing", { model: m.name, apiKeyEnv: m.apiKeyEnv, hint: `env var ${m.apiKeyEnv} not set (or empty) in this service — key POISONED: tasks on this model fail loud naming ${m.apiKeyEnv} (no gateway fallback) until it is set; or drop apiKeyEnv from the entry to use the gateway key on purpose` });
287
275
  }
288
276
  }
289
277
  const quotaWeights = {};
@@ -305,16 +293,6 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
305
293
  centerRoles[role] = { select: tgt.select };
306
294
  }
307
295
  const roles = { ...envRolesBaseline.get(config), ...centerRoles };
308
- let picked;
309
- if (usable.length > 0) {
310
- picked = resolveDefaultModelName(eff, (n) => models[n] !== undefined, usable[0].name, (source, name) => logger?.warn("sema_registry_default_dangling", {
311
- source,
312
- ...(Object.hasOwn(verdict.refused, name) ? refusedNameLogField("name", name) : { name }),
313
- ...(Object.hasOwn(verdict.refused, name) ? { unavailable: verdict.refused[name].code } : {}),
314
- hint: "explicit default names a model that is not in the usable catalog (not enabled, or unavailable on this host — see `unavailable`) — falling to the next source",
315
- }));
316
- models.default = models[picked.name];
317
- }
318
296
  const activeTiers = {};
319
297
  for (const [tier, target] of Object.entries(resolveActiveTiers(eff.models) ?? {})) {
320
298
  if (models[target] !== undefined) {
@@ -322,7 +300,39 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
322
300
  continue;
323
301
  }
324
302
  const refusal = Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
325
- logger?.warn("sema_registry_tier_binding_unavailable", { tier, ...(refusal !== undefined ? refusedNameLogField("model", target) : { model: target }), ...(refusal !== undefined ? { code: refusal.code } : {}), hint: "this tier binding names a model that is not in this generation's usable catalog — the binding is dropped (the tier word falls to the next lower bound tier, core's ladder); fix the entry or rebind the tier" });
303
+ logger?.warn("web_admin_tier_binding_unavailable", { tier, ...(refusal !== undefined ? refusedNameLogField("model", target) : { model: target }), ...(refusal !== undefined ? { code: refusal.code } : {}), hint: "this tier binding names a model that is not in this generation's usable catalog — the binding is dropped (the tier word falls to the next lower bound tier, core's ladder); fix the entry or rebind the tier" });
304
+ }
305
+ let picked;
306
+ if (usable.length > 0) {
307
+ const decided = resolveCatalogDefaultModel(eff.models, (n) => models[n] !== undefined, usable[0].name);
308
+ switch (decided.kind) {
309
+ case "named":
310
+ for (const d of decided.dangling) {
311
+ logger?.warn("web_admin_default_dangling", {
312
+ source: d.source,
313
+ ...(Object.hasOwn(verdict.refused, d.name) ? refusedNameLogField("name", d.name) : { name: d.name }),
314
+ ...(Object.hasOwn(verdict.refused, d.name) ? { unavailable: verdict.refused[d.name].code } : {}),
315
+ hint: "explicit default names a model that is not in the usable catalog (not enabled, or unavailable on this host — see `unavailable`) — falling to the next source",
316
+ });
317
+ }
318
+ picked = { name: decided.name, source: decided.source };
319
+ break;
320
+ case "select": {
321
+ const engine = engineDefaultOfSelect(decided.select, models, activeTiers);
322
+ if ("unresolved" in engine) {
323
+ return refuse(DEFAULT_MODEL_MISSING_CODE, `this catalog candidate has no default model [${DEFAULT_MODEL_MISSING_CODE}] — roles.default is a {select} that the engine resolves to no usable catalog entry ` +
324
+ "(no enabled entry this host can serve matches its criteria, and no tier binding catches the default role); widen the criteria, enable a matching entry, or bind roles.default to a model by name", { engine: engine.unresolved });
325
+ }
326
+ picked = { name: engine.name, source: "roles.default.select" };
327
+ break;
328
+ }
329
+ default: {
330
+ const unhandled = decided;
331
+ throw new Error(`catalog default: unknown kind ${JSON.stringify(unhandled)}`);
332
+ }
333
+ }
334
+ if (picked !== undefined)
335
+ models.default = models[picked.name];
326
336
  }
327
337
  const nextView = { ...config, models, tiers: activeTiers, modelRefusals: verdict.refused };
328
338
  const noDefault = crossInvariant("default-model-resolvable").check({ live: config, next: nextView });
@@ -338,10 +348,10 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
338
348
  const target = typeof spec === "string" ? spec : spec !== null && typeof spec === "object" && "model" in spec && typeof spec.model === "string" ? spec.model : undefined;
339
349
  const refusal = target !== undefined && Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
340
350
  if (refusal !== undefined) {
341
- logger?.warn("sema_registry_role_binding_unavailable", { role, ...refusedNameLogField("model", target), code: refusal.code, hint: ROLE_BINDING_UNAVAILABLE_HINT });
351
+ logger?.warn("web_admin_role_binding_unavailable", { role, ...refusedNameLogField("model", target), code: refusal.code, hint: ROLE_BINDING_UNAVAILABLE_HINT });
342
352
  }
343
353
  }
344
- const atModelAllowlist = stageAtModelAllowlist(eff.models?.atModelAllowlist, models, logger);
354
+ const atMention = stageAtMention(eff.models?.atMention, new Set(usable.map((m) => m.name)), logger);
345
355
  const defaultModel = models.default;
346
356
  const plane = {
347
357
  models,
@@ -352,8 +362,8 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
352
362
  quotaWeights,
353
363
  roles,
354
364
  activeTiers,
355
- atModelAllowlist,
356
- infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? refusedNamesLogFields("unavailable", Object.keys(verdict.refused)) : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atModelAllowlist: atModelAllowlist.length, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
365
+ atMention,
366
+ infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? refusedNamesLogFields("unavailable", Object.keys(verdict.refused)) : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atMention: atMention.mode, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
357
367
  };
358
368
  return { stages: { models: plane, collab: stageCollabDomain(eff) }, defaultName: picked.name, refused: verdict.refused };
359
369
  }
@@ -362,23 +372,23 @@ function stageCollabDomain(eff) {
362
372
  const projected = projectCollabToWorkflows(eff.collab?.templates);
363
373
  const notices = [];
364
374
  if (Object.keys(projected.workflows).length > 0)
365
- notices.push({ level: "info", msg: "sema_registry_collab_workflows", fields: { workflows: Object.keys(projected.workflows) } });
375
+ notices.push({ level: "info", msg: "web_admin_collab_workflows", fields: { workflows: Object.keys(projected.workflows) } });
366
376
  if (projected.skipped.length > 0)
367
- notices.push({ level: "warn", msg: "sema_registry_collab_skipped", fields: { skipped: projected.skipped } });
377
+ notices.push({ level: "warn", msg: "web_admin_collab_skipped", fields: { skipped: projected.skipped } });
368
378
  if (projected.notes.length > 0)
369
- notices.push({ level: "info", msg: "sema_registry_collab_notes", fields: { notes: projected.notes } });
379
+ notices.push({ level: "info", msg: "web_admin_collab_notes", fields: { notes: projected.notes } });
370
380
  return { workflows: projected.workflows, notices };
371
381
  }
372
382
  function stageEffective(config, eff, logger, opts) {
373
383
  if (opts.leg === "boot" && !eff.version) {
374
- logger?.warn("sema_registry_unpublished", {
384
+ logger?.warn("web_admin_unpublished", {
375
385
  version: eff.version,
376
386
  note: "empty/unpublished effective config — using env + built-in fallback; publish a config or unset CONFIG_PUBLISH_MODE",
377
387
  });
378
388
  }
379
389
  const modelPlaneGroup = stageModelPlane(config, eff, logger, opts.sealedKeys);
380
390
  const approvalGate = stageApprovalGate(config, eff.governance, opts, logger);
381
- const limits = stageLimits(config, { version: typeof eff.version === "number" ? eff.version : 0, ...("limits" in eff ? { limits: eff.limits } : {}), legacyGates: legacyGateValues(eff.runtime) }, logger);
391
+ const limits = stageLimits(config, { version: typeof eff.version === "number" ? eff.version : 0, ...("limits" in eff ? { limits: eff.limits } : {}) }, logger);
382
392
  const hot = stageRuntimeHot(config, eff.governance, logger);
383
393
  const projDomain = eff.projects;
384
394
  const rawProjects = (projDomain && typeof projDomain === "object" && !Array.isArray(projDomain) &&
@@ -405,7 +415,6 @@ function stageEffective(config, eff, logger, opts) {
405
415
  projects,
406
416
  };
407
417
  }
408
- export const RUNTIME_GATE_KEYS = ["rateLimitPerMin", "maxTaskCostUsd", "maxTaskTokens", "maxPrincipalCostUsd", "costQuotaWindowSec"];
409
418
  export function centerManagedConfigKeys(eff) {
410
419
  const out = new Set();
411
420
  if (!eff)
@@ -416,12 +425,6 @@ export function centerManagedConfigKeys(eff) {
416
425
  if (Object.prototype.hasOwnProperty.call(limits, k))
417
426
  out.add(k);
418
427
  }
419
- const rt = eff.runtime;
420
- if (rt) {
421
- for (const k of RUNTIME_GATE_KEYS)
422
- if (runtimeGatePresent(rt, k))
423
- out.add(k);
424
- }
425
428
  if (eff.governance) {
426
429
  if (eff.governance.autonomy !== undefined)
427
430
  out.add("autonomy");
@@ -443,16 +446,10 @@ export function centerManagedConfigKeys(eff) {
443
446
  }
444
447
  if ((eff.models?.models ?? []).some((m) => m.enabled !== false))
445
448
  out.add("models");
446
- if (eff.models?.atModelAllowlist !== undefined)
447
- out.add("atModelAllowlist");
449
+ if (eff.models?.atMention !== undefined)
450
+ out.add("atMention");
448
451
  return out;
449
452
  }
450
- export function runtimeGatePresent(rt, key) {
451
- const v = rt[key];
452
- if (key === "costQuotaWindowSec")
453
- return typeof v === "number" && v > 0;
454
- return typeof v === "number";
455
- }
456
453
  const KEPT_WOULD_MATCH_NOTHING = "the prior approvalRequire is unchanged — the published list would have silently matched nothing";
457
454
  const KEPT_WOULD_REMOVE_GATE = "the prior approvalRequire is unchanged — coercing this malformed value would have silently REMOVED the approval gate";
458
455
  const KEPT_WOULD_NEVER_PARK = "the prior approvalRequire is unchanged — this deployment has nowhere to park a gated call, so applying the list would leave it silently unenforced";
@@ -477,7 +474,7 @@ function stageApprovalRequire(raw) {
477
474
  return {
478
475
  ok: false,
479
476
  detail: unmatchable.message,
480
- reason: `published governance.approvalRequire has ${unmatchable.names.length} entr${unmatchable.names.length === 1 ? "y" : "ies"} that match no live tool (retired name / incomplete protocol name / server segment the engine never mints / CC-form rule) — the offending entries are named in the sema_registry_approval_require_invalid log line`,
477
+ reason: `published governance.approvalRequire has ${unmatchable.names.length} entr${unmatchable.names.length === 1 ? "y" : "ies"} that match no live tool (retired name / incomplete protocol name / server segment the engine never mints / CC-form rule) — the offending entries are named in the web_admin_approval_require_invalid log line`,
481
478
  names: unmatchable.names,
482
479
  kept: KEPT_WOULD_MATCH_NOTHING,
483
480
  };
@@ -504,7 +501,7 @@ function stageApprovalGate(config, gov, leg, logger) {
504
501
  if (!approvalEnvBaseline.has(config))
505
502
  approvalEnvBaseline.set(config, Object.freeze([...config.approvalRequire]));
506
503
  const refuse = (r) => {
507
- logger?.warn("sema_registry_approval_require_invalid", { keys: [...APPROVAL_GROUP_KEYS], detail: r.detail, names: [...r.names], kept: r.kept });
504
+ logger?.warn("web_admin_approval_require_invalid", { keys: [...APPROVAL_GROUP_KEYS], detail: r.detail, names: [...r.names], kept: r.kept });
508
505
  return { rejected: { keys: [...APPROVAL_GROUP_KEYS], reason: r.reason } };
509
506
  };
510
507
  const raw = gov?.approvalRequire;
@@ -524,27 +521,18 @@ function stageApprovalGate(config, gov, leg, logger) {
524
521
  const same = next.length === config.approvalRequire.length && next.every((n, i) => n === config.approvalRequire[i]);
525
522
  return same ? {} : { next };
526
523
  }
527
- function legacyGateValues(rt) {
528
- const out = {};
529
- if (!rt)
530
- return out;
531
- for (const k of RUNTIME_GATE_KEYS)
532
- if (runtimeGatePresent(rt, k))
533
- out[k] = rt[k];
534
- return out;
535
- }
536
524
  export function applyRuntimeGates(config, eff, leg, logger) {
537
525
  const approval = stageApprovalGate(config, eff?.governance, leg, logger);
538
526
  if (approval.next !== undefined)
539
527
  config.approvalRequire = approval.next;
540
- const limits = stageLimits(config, { version: 0, legacyGates: legacyGateValues(eff?.runtime) }, logger);
528
+ const limits = stageLimits(config, { version: 0, ...(eff !== undefined && "limits" in eff ? { limits: eff.limits } : {}) }, logger);
541
529
  for (const [k, v] of limits.assignments)
542
530
  config[k] = v;
543
531
  for (const k of limits.deletions)
544
532
  delete config[k];
545
533
  const info = { ...(approval.next !== undefined ? { approvalRequire: approval.next } : {}), ...Object.fromEntries(limits.assignments) };
546
534
  if (Object.keys(info).length > 0)
547
- logger?.info("sema_registry_runtime", info);
535
+ logger?.info("web_admin_runtime", info);
548
536
  }
549
537
  function stageRuntimeHot(config, rt, logger) {
550
538
  const infoLine = {};
@@ -558,7 +546,7 @@ function stageRuntimeHot(config, rt, logger) {
558
546
  if (rt?.commandPolicy !== undefined) {
559
547
  const errors = validateCommandRules(rt.commandPolicy);
560
548
  if (errors.length > 0) {
561
- logger?.error("sema_registry_commandpolicy_invalid", { errors, kept: config.commandPolicy?.length ?? 0 });
549
+ logger?.error("web_admin_commandpolicy_invalid", { errors, kept: config.commandPolicy?.length ?? 0 });
562
550
  }
563
551
  else if (JSON.stringify(config.commandPolicy) !== JSON.stringify(rt.commandPolicy)) {
564
552
  setCommandPolicy = true;
@@ -580,26 +568,19 @@ export function applyRuntimeHot(config, gov, logger) {
580
568
  if (s.setCommandPolicy)
581
569
  config.commandPolicy = s.commandPolicyNext;
582
570
  if (Object.keys(s.infoLine).length > 0)
583
- logger?.info("sema_registry_runtime_hot", s.infoLine);
571
+ logger?.info("web_admin_runtime_hot", s.infoLine);
584
572
  }
585
- const DEFAULT_ROLE_TIER_LADDER = ["pro", "flash", "lite"];
586
- export function resolveDefaultModelName(eff, has, fallback, onDangling) {
587
- const activeTiers = resolveActiveTiers(eff.models) ?? {};
588
- const structural = eff.models?.default;
589
- const rolesDefault = eff.models?.roles?.default?.model;
590
- const candidates = [
591
- { source: "models.default", name: typeof structural === "string" && structural.length > 0 ? structural : undefined },
592
- { source: "roles.default", name: typeof rolesDefault === "string" && rolesDefault.length > 0 ? rolesDefault : undefined },
593
- ...DEFAULT_ROLE_TIER_LADDER.map((t) => ({ source: `tierGroup.${t}`, name: activeTiers[t] })),
594
- ];
595
- for (const c of candidates) {
596
- if (!c.name)
597
- continue;
598
- if (has(c.name))
599
- return { name: c.name, source: c.source };
600
- onDangling?.(c.source, c.name);
573
+ function engineDefaultOfSelect(select, models, activeTiers) {
574
+ const catalog = expandTiers(models, activeTiers) ?? models;
575
+ let picked;
576
+ try {
577
+ picked = coreResolveTaskModel({ modelRole: "default" }, { models: catalog, roles: { default: { select } } }).model;
578
+ }
579
+ catch (err) {
580
+ return { unresolved: err instanceof Error ? err.message : String(err) };
601
581
  }
602
- return { name: fallback, source: "enabled[0]" };
582
+ const name = Object.keys(models).find((n) => models[n] === picked);
583
+ return name !== undefined ? { name } : { unresolved: "the engine picked a model object that is not an entry of the usable catalog" };
603
584
  }
604
585
  export function logEffectiveDiff(config, eff, logger) {
605
586
  const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
@@ -608,8 +589,8 @@ export function logEffectiveDiff(config, eff, logger) {
608
589
  const centerModels = enabled.map((m) => ({ name: m.name, id: m.id, provider: m.provider, apiKeyEnv: m.apiKeyEnv ?? null, tier: m.tier ?? null }));
609
590
  const envModels = Object.entries(config.models).map(([name, m]) => ({ name, id: m.id, provider: m.provider }));
610
591
  const centerRoles = Object.fromEntries(Object.entries(eff.models?.roles ?? {}).map(([r, t]) => [r, "model" in t ? t.model : { select: t.select }]));
611
- logger?.warn("sema_registry_dry_run", {
612
- note: "DRY RUN — registry config NOT applied; unset SEMA_REGISTRY_DRY_RUN to go live",
592
+ logger?.warn("web_admin_dry_run", {
593
+ note: `DRY RUN — web-admin config NOT applied; unset ${WEB_ADMIN_ENV.dryRun} to go live`,
613
594
  version: eff.version,
614
595
  wouldOverrideModels: enabled.length > 0,
615
596
  wouldDefaultModel: plane.defaultName ?? null,
@@ -621,9 +602,9 @@ export function logEffectiveDiff(config, eff, logger) {
621
602
  collab: { centerWouldRegister: Object.keys(projectCollabToWorkflows(eff.collab?.templates).workflows) },
622
603
  skills: { centerWouldLoad: (eff.skills?.skills ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
623
604
  mcp: { centerWouldRegister: (eff.mcp?.servers ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
624
- runtime: {
625
- center: eff.runtime ?? null,
626
- current: Object.fromEntries(RUNTIME_GATE_KEYS.map((k) => [k, config[k]])),
605
+ limits: {
606
+ center: eff.limits ?? null,
607
+ current: Object.fromEntries(BATCH1_LIMIT_KEYS.map((k) => [k, config[k]])),
627
608
  },
628
609
  governance: {
629
610
  center: eff.governance ?? null,
@@ -9,7 +9,7 @@
9
9
  * `CONFIG_PROVIDER=local` 下是 registry-core file-store 的内容哈希(u53,上游契约只承诺
10
10
  * 「同 files ⇒ 同 number」,从未承诺序)——把它当序数消费,哈希更小的合法新代会被永久
11
11
  * 静默忽略且本账双盲(target/applied 都不动)。修后:**推进判断吃收货处铸的进程内单调
12
- * ordinal**(config-center `generationOf`;等版本重放沿用同 ordinal=幂等语义保留),
12
+ * ordinal**(web-admin `generationOf`;等版本重放沿用同 ordinal=幂等语义保留),
13
13
  * `version` 只做对账身份标——configTargetVersion 语义从「max(见过)」变为「**最后**见过的
14
14
  * 源代」(remote 单调腿二者等价;local 腿这才是真相)。
15
15
  *
@@ -36,7 +36,7 @@ export type ConfigApplyGroup = (typeof CONFIG_APPLY_GROUPS)[number];
36
36
  /**
37
37
  * S-810(车QU codex r1 [high] F1,验真后修)—— 一组在**热腿**被整组拒之后,新进程在 **boot 腿**上对同一候选重判会怎样。每组一格的闭集
38
38
  * (`satisfies Record<ConfigApplyGroup, …>`:加一组 ⇒ 这里编译红,不许静默落进某一格)。部分应用的拍据此决定能不能发重启信号
39
- * (`boot/config-center.ts` 的 `signalRestart`):
39
+ * (`boot/web-admin.ts` 的 `signalRestart`):
40
40
  * · `"refuses-group"` —— boot 腿同一判据、同样整组拒,其余组照落 ⇒ 重启收敛(新进程的 boot 快照就是这份候选,同一差异不再签)。
41
41
  * `limitsHttp`:`stageLimits` 不分腿。
42
42
  * · `"may-refuse-boot"` —— boot 腿的结局取决于那一刻的部署事实,可能整个进程起不来 ⇒ 照信号重启不保证收敛(可能把一台还在服务的
@@ -5,10 +5,10 @@ export declare const CENTER_PULL_TIMEOUT_MS = 8000;
5
5
  /** 已装 settings-schema 的 major(机器派生)。版本号不成形 ⇒ 模块装载即抛(装坏了的包不该带着一个猜出来的
6
6
  * major 去跟中心协商 wire 形)。 */
7
7
  export declare const SETTINGS_SCHEMA_MAJOR: string;
8
- /** 一个中心端点 = 基址 + 凭证来源(`ServiceConfig.configCenter` 结构上满足)。 */
8
+ /** 一个中心端点 = 基址 + 凭证来源(`ServiceConfig.webAdmin` 结构上满足)。 */
9
9
  export interface CenterEndpoint {
10
10
  readonly baseUrl: string;
11
- /** 活性组的基址(env `SEMA_REGISTRY_LIVE_URL` 的回声;缺席 ⇒ 活性组也打 {@link baseUrl})。「缺席 = 主基址」这条规则只在 {@link centerFetch} 读。 */
11
+ /** 活性组的基址(env `SEMA_WEB_ADMIN_LIVE_URL` 的回声;缺席 ⇒ 活性组也打 {@link baseUrl})。「缺席 = 主基址」这条规则只在 {@link centerFetch} 读。 */
12
12
  readonly liveBaseUrl?: string | undefined;
13
13
  readonly credential: CenterCredentialSource;
14
14
  }
@@ -21,11 +21,11 @@ export interface CenterEndpoint {
21
21
  * (7.105.0 及以前累加器与用量环都挂在公告地址上,不公告的引擎于是从不报用量,租约的已耗量只剩中心自己的估计)。
22
22
  */
23
23
  export declare function centerLaneConfigured<C extends {
24
- readonly configCenter?: (CenterEndpoint & {
24
+ readonly webAdmin?: (CenterEndpoint & {
25
25
  readonly worker?: string | undefined;
26
26
  }) | undefined;
27
27
  }>(cfg: C): cfg is C & {
28
- readonly configCenter: NonNullable<C["configCenter"]> & {
28
+ readonly webAdmin: NonNullable<C["webAdmin"]> & {
29
29
  readonly worker: string;
30
30
  };
31
31
  };
@@ -73,8 +73,8 @@ export interface CenterRequestInit {
73
73
  readonly fetchImpl?: typeof fetch;
74
74
  }
75
75
  /**
76
- * 「后面要拼路径的基址」的拒句 —— 中心基址三席(`SEMA_REGISTRY_URL` / `SEMA_REGISTRY_LIVE_URL` / `SEMA_REGISTRY_WEB_URL`)与本实例的
77
- * 公告地址(`FLEET_ADVERTISE_ADDRESS`,中心往它后面拼 `/v1/...`)同读。判官是 `url-shape.ts` 那一只(层级式 http(s) URL;Node 的 fetch
76
+ * 「后面要拼路径的基址」的拒句 —— 中心基址三席(`SEMA_WEB_ADMIN_URL` / `SEMA_WEB_ADMIN_LIVE_URL` / `SEMA_WEB_ADMIN_WEB_URL`)与本实例的
77
+ * 公告地址(`FLEET_ADVERTISE_ADDRESS`,中心往它后面拼 `/v1/...`)同读。判官是 settings-schema 的 `httpUrlFlaws`(层级式 http(s) URL;Node 的 fetch
78
78
  * 认 `file://` / `data:`,配错的地址不许变成读本地文件),本函数只声明这一席禁哪几件({@link PATH_BASE_FORBIDS})。起服时 `config.ts` 判
79
79
  * (拒启),请求期 {@link centerFetch} 对同一份基址再判一次(自拼 `ServiceConfig` 的嵌入方也吃同一条规则)。合格 ⇒ `undefined`;否则交回
80
80
  * `<subject> <规则词>; got "<展示面安全形>"` —— 拒句里没有从值里解析出来的任何记号(scheme / userinfo / 主机名),捕获点照原样落日志即可。
@@ -93,7 +93,7 @@ export declare function centerTokenRefresh(issuer: string, refreshToken: string,
93
93
  }): Promise<Response>;
94
94
  /**
95
95
  * 对中心发一条请求。返回响应与**这次请求所用凭证的身份**(调用方据此给拿回来的候选打分箱标记,
96
- * 见 `boot/config-center.ts` 的 LKG / prompt 状态分箱)。
96
+ * 见 `boot/web-admin.ts` 的 LKG / prompt 状态分箱)。
97
97
  */
98
98
  export declare function centerFetch(center: CenterEndpoint, route: CenterRoute, init: CenterRequestInit): Promise<{
99
99
  res: Response;
@@ -2,7 +2,8 @@ import settingsSchemaPackage from "@sema-agent/settings-schema/package.json" wit
2
2
  import { AUTH_V1_PATHS } from "@sema-agent/settings-schema/api/auth";
3
3
  import { FLEET_LIVE_PATHS, FLEET_PATHS } from "@sema-agent/settings-schema/fleet";
4
4
  import { CenterCredentialUnavailableError } from "../center-credential.js";
5
- import { buildHttpUrlRefusal, httpUrlFlaws } from "../url-shape.js";
5
+ import { WEB_ADMIN_ENV, httpUrlFlaws } from "@sema-agent/settings-schema";
6
+ import { buildHttpUrlRefusal } from "../url-refusal.js";
6
7
  export const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
7
8
  export const CENTER_PULL_TIMEOUT_MS = 8000;
8
9
  export const SETTINGS_SCHEMA_MAJOR = (() => {
@@ -13,7 +14,7 @@ export const SETTINGS_SCHEMA_MAJOR = (() => {
13
14
  return m[1];
14
15
  })();
15
16
  export function centerLaneConfigured(cfg) {
16
- const cc = cfg.configCenter;
17
+ const cc = cfg.webAdmin;
17
18
  return cc !== undefined && cc.baseUrl !== "" && cc.credential.declared && cc.worker !== undefined && cc.worker !== "";
18
19
  }
19
20
  export function pullViewQuery(view) {
@@ -83,7 +84,7 @@ export async function centerTokenRefresh(issuer, refreshToken, init) {
83
84
  });
84
85
  }
85
86
  export async function centerFetch(center, route, init) {
86
- assertPathBaseUrl(center.baseUrl, "SEMA_REGISTRY_URL");
87
+ assertPathBaseUrl(center.baseUrl, WEB_ADMIN_ENV.url);
87
88
  const cred = center.credential.read();
88
89
  if (!cred.ok)
89
90
  throw new CenterCredentialUnavailableError(cred.reason);
@@ -97,7 +98,7 @@ export async function centerFetch(center, route, init) {
97
98
  const path = routePath(route, cred.kind);
98
99
  const liveBase = FLEET_LIVE_PATHS.includes(path) ? center.liveBaseUrl : undefined;
99
100
  if (liveBase !== undefined)
100
- assertPathBaseUrl(liveBase, "SEMA_REGISTRY_LIVE_URL");
101
+ assertPathBaseUrl(liveBase, WEB_ADMIN_ENV.liveUrl);
101
102
  const url = `${(liveBase ?? center.baseUrl).replace(/\/+$/, "")}${path}`;
102
103
  const res = await (init.fetchImpl ?? fetch)(url, {
103
104
  method: init.method ?? "GET",
@@ -4,7 +4,7 @@
4
4
  * 🔴 三条纪律:
5
5
  * 1. `EFFECTIVE_CONFIG_KEYS` 与 {@link EffectiveConfig} **类型级**绑定(`satisfies Record<keyof …, true>`):接口加键 /
6
6
  * 删键而不动这里 = **编译错**——运行时键表永不与类型面漂移(这不是第二份表,是同一份接口的运行时投影;
7
- * runtime / governance 两域的键表同法,用于与 schema zod shape 的集合等式墓碑)。
7
+ * governance 域的键表同法,用于与 schema zod shape 的集合等式墓碑;settings-schema 11 退役了 `runtime` 域,它的键表随之删)。
8
8
  * 2. 三张登记表(非域白名单 / 不消费表 / 远端专属表)每行**必须带理由**;行的成立性由对账门**双向**钉——登记了
9
9
  * 却不成立(死账)与成立了却没登记(半接线)都红,理由失效之日格子自己翻红(墓碑),禁裸豁免。
10
10
  * 3. schema 侧的数据(`DOMAINS` / `PORTABLE_DOMAINS` / zod shape)一律由对账门**读 dist**,绝不抄进这里
@@ -15,8 +15,6 @@ import type { EffectiveConfig } from "./types.js";
15
15
  export declare const EFFECTIVE_CONFIG_KEYS: ReadonlyArray<keyof EffectiveConfig>;
16
16
  /** 信封键(不是域)。 */
17
17
  export declare const EFFECTIVE_ENVELOPE_KEYS: readonly ["version", "updatedAt"];
18
- /** `runtime` 域(限额残余)的键集——对账门与 schema `RuntimeConfig.shape` 钉集合等式([ref] 件3 退役键墓碑)。 */
19
- export declare const RUNTIME_DOMAIN_KEYS: readonly string[];
20
18
  /** `governance` 域(治理三件)的键集——对账门与 schema `GovernanceConfig.shape` 钉集合等式。 */
21
19
  export declare const GOVERNANCE_DOMAIN_KEYS: readonly string[];
22
20
  /** EffectiveConfig 上**不是** settings-schema 域的顶层键(键 → 成文理由)。⚠️ 墓碑语义:任一键将来出现在 schema
@@ -7,7 +7,6 @@ const EFFECTIVE_CONFIG_KEY_TABLE = {
7
7
  mcp: true,
8
8
  a2a: true,
9
9
  scenarios: true,
10
- runtime: true,
11
10
  limits: true,
12
11
  projects: true,
13
12
  governance: true,
@@ -17,14 +16,6 @@ const EFFECTIVE_CONFIG_KEY_TABLE = {
17
16
  };
18
17
  export const EFFECTIVE_CONFIG_KEYS = Object.keys(EFFECTIVE_CONFIG_KEY_TABLE);
19
18
  export const EFFECTIVE_ENVELOPE_KEYS = ["version", "updatedAt"];
20
- const RUNTIME_DOMAIN_KEY_TABLE = {
21
- rateLimitPerMin: true,
22
- maxTaskCostUsd: true,
23
- maxTaskTokens: true,
24
- maxPrincipalCostUsd: true,
25
- costQuotaWindowSec: true,
26
- };
27
- export const RUNTIME_DOMAIN_KEYS = Object.keys(RUNTIME_DOMAIN_KEY_TABLE);
28
19
  const GOVERNANCE_DOMAIN_KEY_TABLE = {
29
20
  autonomy: true,
30
21
  commandPolicy: true,
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Config-center adapter — pull the effective config from the config-center control plane and
2
+ * Web-admin adapter — pull the effective config from the web-admin control plane and
3
3
  * apply it OVER the env-derived defaults (env = fallback, center = override). Follows the
4
4
  * "universal internal schema → translate at the boundary" pattern, with our security boundary kept:
5
5
  *
6
6
  * (name history: this module tracks the control plane's product name, which has changed over time
7
7
  * — registry → sema-web → sema-web-admin (2026-08-26 repo/product rename; an earlier revision of
8
8
  * this note listed "planned sema-admin" as the next step). The module itself keeps a neutral, product-name-free
9
- * identifier ("config-center") and does not chase the rename.)
9
+ * identifier ("web-admin") and does not chase the rename.)
10
10
  *
11
11
  * - the CENTER owns the LOGICAL config: the model roster (names/capabilities/tier), the role map,
12
12
  * and collab (multi-agent discussion) templates;
@@ -41,14 +41,14 @@
41
41
  * (`governance.approvalRequire` left this list in S-668 波 2 — its own staged group, hot on both legs.)
42
42
  *
43
43
  * ([ref] A13, internal-lossless) This module is now a FACADE: the implementation lives
44
- * alongside it in `src/config-center/` split by responsibility group (HTTP client / EffectiveConfig
44
+ * alongside it in `src/web-admin-client/` split by responsibility group (HTTP client / EffectiveConfig
45
45
  * application / restart-signal detection / skills+MCP consumption). Every symbol below is
46
- * re-exported UNCHANGED — existing `from ".../config-center/facade.js"` imports need zero changes
46
+ * re-exported UNCHANGED — existing `from ".../web-admin-client/facade.js"` imports need zero changes
47
47
  * across a rename of this file's *contents*; a path rename (this move, 2026-07-29) does require the
48
48
  * importers to update, which was done in the same commit.
49
49
  */
50
- export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
51
- export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
50
+ export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, WebAdminHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
51
+ export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
52
52
  export { restartReasons, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type EngineRestartSignal, } from "./restart-signal.js";
53
53
  export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
54
54
  export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";