@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
@@ -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) {
@@ -166,12 +154,13 @@ function commitStaged(config, staged) {
166
154
  config.modelApiKeyEnv = mp.modelApiKeyEnv;
167
155
  config.modelApiKeys = mp.modelApiKeys;
168
156
  config.modelRefusals = mp.modelRefusals;
157
+ config.droppedCatalogEntries = mp.droppedCatalogEntries;
169
158
  mutateInPlace(config.modelQuotaWeights, mp.quotaWeights);
170
159
  mutateInPlace(config.models, mp.models);
171
160
  config.model = config.models.default;
172
161
  mutateInPlace(config.roles, mp.roles);
173
162
  mutateInPlace(config.tiers, mp.activeTiers);
174
- config.atModelAllowlist = mp.atModelAllowlist;
163
+ config.atMention = mp.atMention;
175
164
  }
176
165
  if (staged.approvalGate.next !== undefined)
177
166
  config.approvalRequire = staged.approvalGate.next;
@@ -190,7 +179,7 @@ function commitStaged(config, staged) {
190
179
  }
191
180
  export const MODEL_PLANE_DOMAINS = ["models", "collab"];
192
181
  const MODEL_PLANE_SURFACES = {
193
- models: ["models", "model", "modelRefusals", "modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "roles", "tiers", "atModelAllowlist"],
182
+ models: ["models", "model", "modelRefusals", "droppedCatalogEntries", "modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "roles", "tiers", "atMention"],
194
183
  collab: ["collabWorkflows"],
195
184
  };
196
185
  const MODEL_PLANE_GROUP_KEYS = MODEL_PLANE_DOMAINS.flatMap((d) => MODEL_PLANE_SURFACES[d]);
@@ -230,15 +219,15 @@ function modelPlaneGroupReport(group) {
230
219
  const ROLE_BINDING_UNAVAILABLE_HINT = "tasks resolving this role fail: alias/tier expansion cannot mint this name; the engine refuses it loud — bind the role to a usable model, or make the entry usable (see the entry's own warn)";
231
220
  function stageModelPlane(config, eff, logger, sealedKeys) {
232
221
  const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
233
- const verdict = judgeCatalogEntries(enabled, { anthropicRouteMounted: config.anthropic !== undefined }, eff.models?.droppedIdentities);
234
- if (enabled.length === 0 && Object.keys(verdict.refused).length === 0 && verdict.malformed.length === 0)
222
+ const verdict = judgeCatalogEntries(enabled, { anthropicRouteMounted: config.anthropic !== undefined }, eff.models?.droppedCatalogEntries);
223
+ if (enabled.length === 0 && verdict.malformed.length === 0 && verdict.dropped.every((row) => row.identity?.enabled === false))
235
224
  return { stages: { collab: stageCollabDomain(eff) } };
236
225
  {
237
226
  for (const [name, r] of Object.entries(verdict.refused))
238
227
  logger?.warn(refusalEventOf(r.reason), refusalWarnFields(name, r));
239
228
  const refuse = (code, reason, detail = {}) => {
240
229
  const invariant = PLANE_REFUSAL_INVARIANT_OF[code];
241
- logger?.warn("sema_registry_model_plane_refused", {
230
+ logger?.warn("web_admin_model_plane_refused", {
242
231
  version: typeof eff.version === "number" ? eff.version : 0,
243
232
  invariant,
244
233
  reason,
@@ -257,16 +246,16 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
257
246
  const envDefaults = { maxTokens: envModel.maxTokens, contextWindow: envModel.contextWindow, reasoning: envModel.reasoning, charsPerToken: envModel.charsPerToken, defaultThinking: envModel.defaultThinking, compat: envModel.compat };
258
247
  for (const m of usable) {
259
248
  if (m.baseUrl)
260
- 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" });
261
250
  models[m.name] = toModel(m, envDefaults, logger);
262
251
  if (m.sealedApiKey) {
263
252
  const s = m.sealedApiKey;
264
253
  if (!sealedKeys) {
265
- 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)" });
266
255
  modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "no_key_store", publicKeyId: s.publicKeyId });
267
256
  }
268
257
  else if (!sealedKeys.has(s.publicKeyId)) {
269
- 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)" });
270
259
  modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "stale_ciphertext", publicKeyId: s.publicKeyId });
271
260
  }
272
261
  else {
@@ -274,7 +263,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
274
263
  modelApiKeys[m.name] = sealedKeys.open(s.ciphertext, s.publicKeyId);
275
264
  }
276
265
  catch (err) {
277
- 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)" });
278
267
  modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "sealed_decrypt_failed", publicKeyId: s.publicKeyId, errKind: err instanceof Error ? err.name : "unknown" });
279
268
  }
280
269
  }
@@ -282,7 +271,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
282
271
  else if (m.apiKeyEnv) {
283
272
  modelApiKeyEnv[m.name] = m.apiKeyEnv;
284
273
  if (!process.env[m.apiKeyEnv])
285
- 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` });
286
275
  }
287
276
  }
288
277
  const quotaWeights = {};
@@ -304,16 +293,6 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
304
293
  centerRoles[role] = { select: tgt.select };
305
294
  }
306
295
  const roles = { ...envRolesBaseline.get(config), ...centerRoles };
307
- let picked;
308
- if (usable.length > 0) {
309
- picked = resolveDefaultModelName(eff, (n) => models[n] !== undefined, usable[0].name, (source, name) => logger?.warn("sema_registry_default_dangling", {
310
- source,
311
- ...(Object.hasOwn(verdict.refused, name) ? refusedNameLogField("name", name) : { name }),
312
- ...(Object.hasOwn(verdict.refused, name) ? { unavailable: verdict.refused[name].code } : {}),
313
- 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",
314
- }));
315
- models.default = models[picked.name];
316
- }
317
296
  const activeTiers = {};
318
297
  for (const [tier, target] of Object.entries(resolveActiveTiers(eff.models) ?? {})) {
319
298
  if (models[target] !== undefined) {
@@ -321,7 +300,39 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
321
300
  continue;
322
301
  }
323
302
  const refusal = Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
324
- 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];
325
336
  }
326
337
  const nextView = { ...config, models, tiers: activeTiers, modelRefusals: verdict.refused };
327
338
  const noDefault = crossInvariant("default-model-resolvable").check({ live: config, next: nextView });
@@ -337,21 +348,22 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
337
348
  const target = typeof spec === "string" ? spec : spec !== null && typeof spec === "object" && "model" in spec && typeof spec.model === "string" ? spec.model : undefined;
338
349
  const refusal = target !== undefined && Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
339
350
  if (refusal !== undefined) {
340
- 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 });
341
352
  }
342
353
  }
343
- const atModelAllowlist = stageAtModelAllowlist(eff.models?.atModelAllowlist, models, logger);
354
+ const atMention = stageAtMention(eff.models?.atMention, new Set(usable.map((m) => m.name)), logger);
344
355
  const defaultModel = models.default;
345
356
  const plane = {
346
357
  models,
347
358
  modelApiKeyEnv,
348
359
  modelApiKeys,
349
360
  modelRefusals: { ...verdict.refused },
361
+ droppedCatalogEntries: [...verdict.dropped],
350
362
  quotaWeights,
351
363
  roles,
352
364
  activeTiers,
353
- atModelAllowlist,
354
- 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 },
355
367
  };
356
368
  return { stages: { models: plane, collab: stageCollabDomain(eff) }, defaultName: picked.name, refused: verdict.refused };
357
369
  }
@@ -360,23 +372,23 @@ function stageCollabDomain(eff) {
360
372
  const projected = projectCollabToWorkflows(eff.collab?.templates);
361
373
  const notices = [];
362
374
  if (Object.keys(projected.workflows).length > 0)
363
- 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) } });
364
376
  if (projected.skipped.length > 0)
365
- 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 } });
366
378
  if (projected.notes.length > 0)
367
- 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 } });
368
380
  return { workflows: projected.workflows, notices };
369
381
  }
370
382
  function stageEffective(config, eff, logger, opts) {
371
383
  if (opts.leg === "boot" && !eff.version) {
372
- logger?.warn("sema_registry_unpublished", {
384
+ logger?.warn("web_admin_unpublished", {
373
385
  version: eff.version,
374
386
  note: "empty/unpublished effective config — using env + built-in fallback; publish a config or unset CONFIG_PUBLISH_MODE",
375
387
  });
376
388
  }
377
389
  const modelPlaneGroup = stageModelPlane(config, eff, logger, opts.sealedKeys);
378
390
  const approvalGate = stageApprovalGate(config, eff.governance, opts, logger);
379
- 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);
380
392
  const hot = stageRuntimeHot(config, eff.governance, logger);
381
393
  const projDomain = eff.projects;
382
394
  const rawProjects = (projDomain && typeof projDomain === "object" && !Array.isArray(projDomain) &&
@@ -403,7 +415,6 @@ function stageEffective(config, eff, logger, opts) {
403
415
  projects,
404
416
  };
405
417
  }
406
- export const RUNTIME_GATE_KEYS = ["rateLimitPerMin", "maxTaskCostUsd", "maxTaskTokens", "maxPrincipalCostUsd", "costQuotaWindowSec"];
407
418
  export function centerManagedConfigKeys(eff) {
408
419
  const out = new Set();
409
420
  if (!eff)
@@ -414,12 +425,6 @@ export function centerManagedConfigKeys(eff) {
414
425
  if (Object.prototype.hasOwnProperty.call(limits, k))
415
426
  out.add(k);
416
427
  }
417
- const rt = eff.runtime;
418
- if (rt) {
419
- for (const k of RUNTIME_GATE_KEYS)
420
- if (runtimeGatePresent(rt, k))
421
- out.add(k);
422
- }
423
428
  if (eff.governance) {
424
429
  if (eff.governance.autonomy !== undefined)
425
430
  out.add("autonomy");
@@ -441,16 +446,10 @@ export function centerManagedConfigKeys(eff) {
441
446
  }
442
447
  if ((eff.models?.models ?? []).some((m) => m.enabled !== false))
443
448
  out.add("models");
444
- if (eff.models?.atModelAllowlist !== undefined)
445
- out.add("atModelAllowlist");
449
+ if (eff.models?.atMention !== undefined)
450
+ out.add("atMention");
446
451
  return out;
447
452
  }
448
- export function runtimeGatePresent(rt, key) {
449
- const v = rt[key];
450
- if (key === "costQuotaWindowSec")
451
- return typeof v === "number" && v > 0;
452
- return typeof v === "number";
453
- }
454
453
  const KEPT_WOULD_MATCH_NOTHING = "the prior approvalRequire is unchanged — the published list would have silently matched nothing";
455
454
  const KEPT_WOULD_REMOVE_GATE = "the prior approvalRequire is unchanged — coercing this malformed value would have silently REMOVED the approval gate";
456
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";
@@ -475,7 +474,7 @@ function stageApprovalRequire(raw) {
475
474
  return {
476
475
  ok: false,
477
476
  detail: unmatchable.message,
478
- 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`,
479
478
  names: unmatchable.names,
480
479
  kept: KEPT_WOULD_MATCH_NOTHING,
481
480
  };
@@ -502,7 +501,7 @@ function stageApprovalGate(config, gov, leg, logger) {
502
501
  if (!approvalEnvBaseline.has(config))
503
502
  approvalEnvBaseline.set(config, Object.freeze([...config.approvalRequire]));
504
503
  const refuse = (r) => {
505
- 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 });
506
505
  return { rejected: { keys: [...APPROVAL_GROUP_KEYS], reason: r.reason } };
507
506
  };
508
507
  const raw = gov?.approvalRequire;
@@ -522,27 +521,18 @@ function stageApprovalGate(config, gov, leg, logger) {
522
521
  const same = next.length === config.approvalRequire.length && next.every((n, i) => n === config.approvalRequire[i]);
523
522
  return same ? {} : { next };
524
523
  }
525
- function legacyGateValues(rt) {
526
- const out = {};
527
- if (!rt)
528
- return out;
529
- for (const k of RUNTIME_GATE_KEYS)
530
- if (runtimeGatePresent(rt, k))
531
- out[k] = rt[k];
532
- return out;
533
- }
534
524
  export function applyRuntimeGates(config, eff, leg, logger) {
535
525
  const approval = stageApprovalGate(config, eff?.governance, leg, logger);
536
526
  if (approval.next !== undefined)
537
527
  config.approvalRequire = approval.next;
538
- 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);
539
529
  for (const [k, v] of limits.assignments)
540
530
  config[k] = v;
541
531
  for (const k of limits.deletions)
542
532
  delete config[k];
543
533
  const info = { ...(approval.next !== undefined ? { approvalRequire: approval.next } : {}), ...Object.fromEntries(limits.assignments) };
544
534
  if (Object.keys(info).length > 0)
545
- logger?.info("sema_registry_runtime", info);
535
+ logger?.info("web_admin_runtime", info);
546
536
  }
547
537
  function stageRuntimeHot(config, rt, logger) {
548
538
  const infoLine = {};
@@ -556,7 +546,7 @@ function stageRuntimeHot(config, rt, logger) {
556
546
  if (rt?.commandPolicy !== undefined) {
557
547
  const errors = validateCommandRules(rt.commandPolicy);
558
548
  if (errors.length > 0) {
559
- 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 });
560
550
  }
561
551
  else if (JSON.stringify(config.commandPolicy) !== JSON.stringify(rt.commandPolicy)) {
562
552
  setCommandPolicy = true;
@@ -578,26 +568,19 @@ export function applyRuntimeHot(config, gov, logger) {
578
568
  if (s.setCommandPolicy)
579
569
  config.commandPolicy = s.commandPolicyNext;
580
570
  if (Object.keys(s.infoLine).length > 0)
581
- logger?.info("sema_registry_runtime_hot", s.infoLine);
571
+ logger?.info("web_admin_runtime_hot", s.infoLine);
582
572
  }
583
- const DEFAULT_ROLE_TIER_LADDER = ["pro", "flash", "lite"];
584
- export function resolveDefaultModelName(eff, has, fallback, onDangling) {
585
- const activeTiers = resolveActiveTiers(eff.models) ?? {};
586
- const structural = eff.models?.default;
587
- const rolesDefault = eff.models?.roles?.default?.model;
588
- const candidates = [
589
- { source: "models.default", name: typeof structural === "string" && structural.length > 0 ? structural : undefined },
590
- { source: "roles.default", name: typeof rolesDefault === "string" && rolesDefault.length > 0 ? rolesDefault : undefined },
591
- ...DEFAULT_ROLE_TIER_LADDER.map((t) => ({ source: `tierGroup.${t}`, name: activeTiers[t] })),
592
- ];
593
- for (const c of candidates) {
594
- if (!c.name)
595
- continue;
596
- if (has(c.name))
597
- return { name: c.name, source: c.source };
598
- 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) };
599
581
  }
600
- 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" };
601
584
  }
602
585
  export function logEffectiveDiff(config, eff, logger) {
603
586
  const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
@@ -606,8 +589,8 @@ export function logEffectiveDiff(config, eff, logger) {
606
589
  const centerModels = enabled.map((m) => ({ name: m.name, id: m.id, provider: m.provider, apiKeyEnv: m.apiKeyEnv ?? null, tier: m.tier ?? null }));
607
590
  const envModels = Object.entries(config.models).map(([name, m]) => ({ name, id: m.id, provider: m.provider }));
608
591
  const centerRoles = Object.fromEntries(Object.entries(eff.models?.roles ?? {}).map(([r, t]) => [r, "model" in t ? t.model : { select: t.select }]));
609
- logger?.warn("sema_registry_dry_run", {
610
- 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`,
611
594
  version: eff.version,
612
595
  wouldOverrideModels: enabled.length > 0,
613
596
  wouldDefaultModel: plane.defaultName ?? null,
@@ -619,9 +602,9 @@ export function logEffectiveDiff(config, eff, logger) {
619
602
  collab: { centerWouldRegister: Object.keys(projectCollabToWorkflows(eff.collab?.templates).workflows) },
620
603
  skills: { centerWouldLoad: (eff.skills?.skills ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
621
604
  mcp: { centerWouldRegister: (eff.mcp?.servers ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
622
- runtime: {
623
- center: eff.runtime ?? null,
624
- 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]])),
625
608
  },
626
609
  governance: {
627
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,