@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,5 @@
1
- export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
2
- export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
1
+ export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, WebAdminHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
2
+ export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
3
3
  export { restartReasons, modelPlaneChanged, } from "./restart-signal.js";
4
4
  export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
5
5
  export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
@@ -9,11 +9,7 @@ import { type CenterEndpoint } from "./center-request.js";
9
9
  * boot/refresh 的候选门直接消费。`droppedEntries`(S-704 ②):schema 逐条丢掉的条目(坐标 + 字段诊断)——
10
10
  * **不**进候选门,调用方响亮点名、其余照用(分面单点 `splitFoldedReadWarnings`,与本地腿同一只)。
11
11
  *
12
- * `readWarnings`:契约 `onWarning` 流的**原值**(未折叠),与本地腿同键同义。折叠是**有损**的——
13
- * `hosts-grandfathered` / `legacy-governance-lifted` 两员按律折成 `null`(值已被收编接受,见
14
- * `read-warnings.ts` 头注),于是只看 `domainErrors` 的人**永远**看不见兼容臂有没有开着;而契约把
15
- * `legacy-governance-lifted` 铸出来,理由逐字写在 `legacyGovernanceFromRuntime` 的 SUNSET 注里:
16
- * 「关窗的前提是有人知道它还开着」。两条读腿各返一份原流,那句话在本仓才成立。
12
+ * `readWarnings`:契约 `onWarning` 流的**原值**(未折叠),与本地腿同键同义(读者:一致性门的读腿适配器,见本地腿同名键注)。
17
13
  * 🔐 原流的 `error` 字段**未经** `redactConfigError`(那道脱敏挂在真正进日志流的 `domainErrors` 产出边界上)
18
14
  * —— 任何把本键路由进结构化日志/审计流的消费者必须自己先脱敏。
19
15
  *
@@ -36,12 +32,12 @@ export declare function fetchEffective(center: CenterEndpoint, etag: string | un
36
32
  * Center pre-resolves the tighten-only intersection (fleet defaults ∩ tier ⊕ roles + overrides) and returns a
37
33
  * `runtimeCaps` block — the service passes it through verbatim (no further tightening, per center's ruling).
38
34
  *
39
- * 🔐 TOKEN/WORKER (center ruling, sema-registry `aeb4979`): two valid postures —
35
+ * 🔐 TOKEN/WORKER (center ruling, web-admin `aeb4979`): two valid postures —
40
36
  * • FULL `SERVICE_PULL_TOKEN` + `?principal=` → full per-user body (caps + per-user config).
41
37
  * • worker-scoped `wpt_<self>` + `?principal=&worker=<self>` → **caps-only** body (`{principal, configured,
42
38
  * runtimeCaps, budget}`; never other tenants' config) — a worker MAY resolve its OWN worker's principals.
43
39
  * A `wpt_` with NO `?worker=` (or a mismatched worker) still 403s. So we ALWAYS pass `?worker=` when the
44
- * deployment has one (`SEMA_REGISTRY_WORKER`): it authorizes the wpt_ path AND is harmless on the full token
40
+ * deployment has one (`SEMA_WEB_ADMIN_WORKER`): it authorizes the wpt_ path AND is harmless on the full token
45
41
  * (the caps value is worker-independent — `resolveEntitlement` resolves from the principal alone; worker only
46
42
  * scopes the config view we ignore). This re-adds the `worker` arg dropped in 1.14.0 (L2-3) — center's ruling
47
43
  * makes it load-bearing for AUTHORIZATION, not for the caps value.
@@ -77,9 +73,9 @@ export declare function fetchPrincipalCaps(center: CenterEndpoint, principal: st
77
73
  * 因此 304 在传输层就是 fail-loud。
78
74
  */
79
75
  export declare function fetchPrincipalOrgMemory(center: CenterEndpoint, principal: string, fetchImpl?: typeof fetch, worker?: string): Promise<unknown>;
80
- /** An HTTP error from a config-center fetch that carries the response status so callers can branch on it
76
+ /** An HTTP error from a web-admin fetch that carries the response status so callers can branch on it
81
77
  * (e.g. translate a 404 to "unknown hash → undefined") WITHOUT parsing the message string. */
82
- export declare class ConfigCenterHttpError extends Error {
78
+ export declare class WebAdminHttpError extends Error {
83
79
  readonly status: number;
84
80
  constructor(message: string, status: number);
85
81
  }
@@ -87,7 +83,7 @@ export declare class ConfigCenterHttpError extends Error {
87
83
  * Fetch one skill's body by content hash (`GET <content root>/skills/content/<hash>`, Bearer; the root is
88
84
  * `/api/v1/me` for a user-JWT credential and `/api/config` otherwise — center-request.ts) and VERIFY its
89
85
  * sha256 matches the manifest hash (integrity + rewrite check). The content is immutable per hash, so the
90
- * caller caches by hash and never re-fetches an unchanged skill. Throws a {@link ConfigCenterHttpError}
86
+ * caller caches by hash and never re-fetches an unchanged skill. Throws a {@link WebAdminHttpError}
91
87
  * (carrying `.status`) on an HTTP error — INCLUDING 404 — or a plain Error on hash mismatch. Callers that
92
88
  * want "unknown hash → undefined" parity with the local backend inspect `.status === 404` and swallow it;
93
89
  * the legacy `applyCenterSkills` path keeps treating every throw as "skip this skill, keep baseline".
@@ -9,7 +9,7 @@ export async function fetchEffective(center, etag, fetchImpl = fetch, worker) {
9
9
  if (res.status === 304)
10
10
  return null;
11
11
  if (!res.ok)
12
- throw new Error(`config-center HTTP ${res.status}`);
12
+ throw new Error(`web-admin HTTP ${res.status}`);
13
13
  const warnings = [];
14
14
  const body = await res.json();
15
15
  const wire = readEffectiveWire(body, (w) => warnings.push(w));
@@ -35,7 +35,7 @@ export async function fetchPrincipalCaps(center, principal, etag, fetchImpl = fe
35
35
  return null;
36
36
  const parsedBody = PrincipalCapsWire.safeParse(raw.json);
37
37
  if (!parsedBody.success) {
38
- throw new Error(`config-center principal-caps body failed schema validation: ${parsedBody.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ").slice(0, 600)}`);
38
+ throw new Error(`web-admin principal-caps body failed schema validation: ${parsedBody.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ").slice(0, 600)}`);
39
39
  }
40
40
  const body = parsedBody.data;
41
41
  assertPrincipalEcho(body.principal, principal);
@@ -79,7 +79,7 @@ export async function fetchPrincipalCaps(center, principal, etag, fetchImpl = fe
79
79
  }
80
80
  function assertPrincipalEcho(echoed, principal) {
81
81
  if (echoed !== undefined && echoed !== principal) {
82
- throw new Error(`config-center principal-caps response echoes principal ${JSON.stringify(echoed)} but ${JSON.stringify(principal)} was requested — discarding the whole response`);
82
+ throw new Error(`web-admin principal-caps response echoes principal ${JSON.stringify(echoed)} but ${JSON.stringify(principal)} was requested — discarding the whole response`);
83
83
  }
84
84
  }
85
85
  async function requestPrincipalView(center, principal, etag, fetchImpl, worker) {
@@ -91,12 +91,12 @@ async function requestPrincipalView(center, principal, etag, fetchImpl, worker)
91
91
  };
92
92
  if (res.status === 304) {
93
93
  if (!etag)
94
- throw new Error("config-center returned 304 to a non-conditional principal-caps request");
94
+ throw new Error("web-admin returned 304 to a non-conditional principal-caps request");
95
95
  stillCurrent();
96
96
  return null;
97
97
  }
98
98
  if (!res.ok)
99
- throw new Error(`config-center principal-caps HTTP ${res.status}`);
99
+ throw new Error(`web-admin principal-caps HTTP ${res.status}`);
100
100
  const responseEtag = res.headers.get("etag");
101
101
  const json = await res.json();
102
102
  stillCurrent();
@@ -106,26 +106,26 @@ const PrincipalOrgMemoryEnvelope = PrincipalCapsWire.pick({ principal: true, org
106
106
  export async function fetchPrincipalOrgMemory(center, principal, fetchImpl = fetch, worker) {
107
107
  const raw = await requestPrincipalView(center, principal, undefined, fetchImpl, worker);
108
108
  if (raw === null)
109
- throw new Error("config-center returned 304 to a non-conditional org-directory fetch");
109
+ throw new Error("web-admin returned 304 to a non-conditional org-directory fetch");
110
110
  const parsed = PrincipalOrgMemoryEnvelope.safeParse(raw.json);
111
111
  if (!parsed.success) {
112
- throw new Error(`config-center org-memory section failed schema validation: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ").slice(0, 600)}`);
112
+ throw new Error(`web-admin org-memory section failed schema validation: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ").slice(0, 600)}`);
113
113
  }
114
114
  assertPrincipalEcho(parsed.data.principal, principal);
115
115
  return parsed.data.orgMemory;
116
116
  }
117
- export class ConfigCenterHttpError extends Error {
117
+ export class WebAdminHttpError extends Error {
118
118
  status;
119
119
  constructor(message, status) {
120
120
  super(message);
121
121
  this.status = status;
122
- this.name = "ConfigCenterHttpError";
122
+ this.name = "WebAdminHttpError";
123
123
  }
124
124
  }
125
125
  export async function fetchSkillContent(center, contentHash, fetchImpl = fetch) {
126
126
  const { res } = await centerFetch(center, { kind: "skill-content", contentHash }, { timeoutMs: CENTER_PULL_TIMEOUT_MS, fetchImpl });
127
127
  if (!res.ok)
128
- throw new ConfigCenterHttpError(`skill content HTTP ${res.status} for ${contentHash}`, res.status);
128
+ throw new WebAdminHttpError(`skill content HTTP ${res.status} for ${contentHash}`, res.status);
129
129
  const content = await res.text();
130
130
  const want = contentHash.replace(/^sha256:/, "").toLowerCase();
131
131
  const got = createHash("sha256").update(content, "utf8").digest("hex");
@@ -136,13 +136,13 @@ export async function fetchSkillContent(center, contentHash, fetchImpl = fetch)
136
136
  export async function fetchPromptArtifact(center, artifactDigest, fetchImpl = fetch) {
137
137
  const { res } = await centerFetch(center, { kind: "prompt-artifact", digest: artifactDigest }, { timeoutMs: CENTER_PULL_TIMEOUT_MS, fetchImpl });
138
138
  if (!res.ok)
139
- throw new ConfigCenterHttpError(`prompt artifact HTTP ${res.status} for ${artifactDigest}`, res.status);
139
+ throw new WebAdminHttpError(`prompt artifact HTTP ${res.status} for ${artifactDigest}`, res.status);
140
140
  return await res.json();
141
141
  }
142
142
  export async function fetchPromptBlob(center, contentDigest, fetchImpl = fetch) {
143
143
  const { res } = await centerFetch(center, { kind: "prompt-blob", digest: contentDigest }, { timeoutMs: CENTER_PULL_TIMEOUT_MS, fetchImpl });
144
144
  if (!res.ok)
145
- throw new ConfigCenterHttpError(`prompt blob HTTP ${res.status} for ${contentDigest}`, res.status);
145
+ throw new WebAdminHttpError(`prompt blob HTTP ${res.status} for ${contentDigest}`, res.status);
146
146
  const bytes = new Uint8Array(await res.arrayBuffer());
147
147
  const want = contentDigest.replace(/^sha256:/, "").toLowerCase();
148
148
  const got = createHash("sha256").update(bytes).digest("hex");
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * S-866 ②(设计稿 v1.1 §2②)—— 「这台引擎**此刻**是不是真的接着中心」的两件读数:最近一次实拉的结局(`lastPull`)与生效修订号
3
- * (`effectiveRevision`)。属主 = config-center 运行时(`boot/config-center.ts` 的 `centerPullState()`);读者 = caps `centerWiring`。
3
+ * (`effectiveRevision`)。属主 = web-admin 运行时(`boot/web-admin.ts` 的 `centerPullState()`);读者 = caps `centerWiring`。
4
4
  *
5
5
  * 纯叶(零值 import):词表与读面形住这里,运行时与读面两侧都从这里取型,不各写一份。
6
6
  */
@@ -59,7 +59,7 @@ export function applyCenterReadFace(config, eff, logger, apply = { phase: "boot"
59
59
  return report();
60
60
  }
61
61
  if (typeof domain !== "object" || Array.isArray(domain)) {
62
- logger?.warn("sema_registry_read_face_invalid", {
62
+ logger?.warn("web_admin_read_face_invalid", {
63
63
  detail: `published readFace domain must be an object with optional {face,denyPatterns,denyBuiltinTiers,denyBuiltinExclude} — got ${Array.isArray(domain) ? "array" : typeof domain}`,
64
64
  kept: "the deployment's env READ-face posture is unchanged (refusing a malformed domain WHOLE — a half-applied read-containment posture is worse than none)",
65
65
  });
@@ -90,7 +90,7 @@ export function applyCenterReadFace(config, eff, logger, apply = { phase: "boot"
90
90
  resolveReadDenyBuiltins(builtinsArg(finalTiers, finalExclude));
91
91
  }
92
92
  catch (e) {
93
- logger?.warn("sema_registry_read_face_invalid", {
93
+ logger?.warn("web_admin_read_face_invalid", {
94
94
  detail: e instanceof Error ? e.message : String(e),
95
95
  kept: "the deployment's env READ-face posture is unchanged (refusing the readFace domain WHOLE — applying half of a read-containment posture would read as 'policy landed' while part of it silently did not)",
96
96
  });
@@ -112,13 +112,13 @@ export function applyCenterReadFace(config, eff, logger, apply = { phase: "boot"
112
112
  config.readDenyBuiltinExclude = staged.readDenyBuiltinExclude;
113
113
  }
114
114
  if (envHeld.length > 0) {
115
- logger?.warn("sema_registry_read_face_env_wins", {
115
+ logger?.warn("web_admin_read_face_env_wins", {
116
116
  keys: envHeld,
117
117
  note: "this machine's explicit env READ-face keys WIN over the published readFace domain (deployment sovereignty) — the published values for these keys were NOT applied; unset the env key if the org publication should own it",
118
118
  });
119
119
  }
120
120
  if (Object.keys(staged).length > 0) {
121
- logger?.info("sema_registry_read_face", {
121
+ logger?.info("web_admin_read_face", {
122
122
  applied: Object.keys(staged).filter((k) => apply.phase === "boot" || KEY_MAP.some((r) => r.cfgKey === k && CENTER_READ_FACE_LIVE_KEYS.has(r.centerKey))),
123
123
  ...(staged.readFace !== undefined ? { face: staged.readFace } : {}),
124
124
  note: apply.phase === "boot"
@@ -10,20 +10,17 @@
10
10
  * 截断:schema 的键列表有界(64),超界带 `truncatedFrom`(真实总数)——文案如实带上,消费方永远不把有界列表当穷举。
11
11
  * 词表是闭集:`switch` 穷举 `kind`,schema 加新 kind 这里编译红,不会静默漏臂。
12
12
  *
13
- * **归族判据两问**(下游两个消费者:候选门 = 非空 ⇒ 这棵树整拒、保 LKG;逐条丢弃面 = 响亮点名、其余照用):
14
- * ① 值**已被收编接受**(写了、生效了、只是换了个位置或被夹住)⇒ 不入诊断面(`null`);
15
- * ② 写了没生效的,按**失效的颗粒**分:丢的是**一条条目**(`entry-dropped`)⇒ `scope:"entry"` —— 这条条目单独
16
- * 不在,同域其余条目照用,候选**不**因它整拒;丢的是一个域 / 一组键 / 一个文件 ⇒ `scope:"domain"` —— 候选门错误。
13
+ * **归族判据**(下游两个消费者:候选门 = 非空 ⇒ 这棵树整拒、保 LKG;逐条丢弃面 = 响亮点名、其余照用):每一种告警都是
14
+ * 「写了没生效」,按**失效的颗粒**分:丢的是**一条条目**(`entry-dropped`)⇒ `scope:"entry"` —— 这条条目单独不在,同域其余条目照用,
15
+ * 候选**不**因它整拒;丢的是一个域 / 一组键 / 一个文件 ⇒ `scope:"domain"` —— 候选门错误。(settings-schema 11 起读路径上零抬升臂:
16
+ * 此前「值已被收编接受」的两种告警 `hosts-grandfathered` / `legacy-governance-lifted` 随抬升臂一起退役,旧形由一次性迁移改写,
17
+ * 读侧遇旧形一律拒 —— 没有「不入诊断面」的那一族了。)
17
18
  *
18
19
  * 🔴 S-704 ②(2026-09-24,凭据轴):此前 `entry-dropped` 归「候选门错误」(settings-schema 2.0.0 提货时的归族),于是
19
20
  * schema 的**逐条容错**在本仓的 refresh / 迟到 boot 两条腿上被**还原成整份拒** —— 目录里一条 `contextWindow:"abc"`
20
21
  * 就让整份目录停在上一代;而上一代若还是 env(boot 拉取失败 / 超窗且无 LKG),env 模型就一直服务、env 钥匙一直被送到
21
22
  * 网关缺省端点(红先实测:`config_candidate_rejected` + env 模型继续服务)。粒度对了规则反而更少:颗粒是条目的失效
22
23
  * 留在条目上,只有颗粒更粗的失效才动候选。
23
- * `legacy-governance-lifted`(0.5 期 runtime 文档里的治理三闸被抬进 governance 域)归①——那几个键**生效了**
24
- * (`buildEffective` 用抬升结果解析 governance),把它折成错误会让一份合法的 0.5 期存量配置每一次 refresh 都被
25
- * 候选门整拒,而它在 1.10.0 上零告警零 domainErrors(亲验读数)。兼容臂的关窗观测归属主(settings-schema 的
26
- * `onWarning` 流),不借候选门这条带副作用的通道传信息。
27
24
  */
28
25
  import { z } from "zod";
29
26
  import { type DroppedEntryIdentity, type DroppedEntryReasonCode, type EffectiveReadWarning } from "@sema-agent/settings-schema";
@@ -49,19 +46,28 @@ export type DroppedConfigEntry = {
49
46
  readonly reasonCode: DroppedEntryReasonCode;
50
47
  };
51
48
  /**
52
- * zod 形的错误 → 一行可读的字段诊断(`contextWindow: expected number, received string; api: custom`)。
53
- * **只读**每条 issue 的 `path`(键名)与 `code`,`invalid_type` 臂另读 `expected` / `received`(两者都是**类型词**)——
54
- * 刻意不读 `message`(自定义 refine 的 message 可以回声值)与 `invalid_enum_value` 等臂的 `received`(那是原值)。
55
- * 读不出 issues 形 ⇒ `undefined`(调用方退回既有文案)。模型面 stage 的逐条判据与本折叠点共用这一只(一个刻度)。
49
+ * zod 形的错误 → 一行可读的字段诊断(`contextWindow: Invalid input: expected number, received string; apiKeyEnv: <契约句>`)。
50
+ * 每条 issue 的句子只有一只读法 {@link issueSentenceOf}、坐标只有一只写法 {@link issueCoordinateOf}(整域面 {@link describeDomainIssues} 读同两只);
51
+ * 坐标缺席(issue 指整条)写 `(entry)`。
52
+ * 读不出 issues 形 ⇒ `undefined`(调用方退回既有文案)。模型面 stage 的逐条判据、目录载体的 `issues` 与本折叠点共用这一只(一个刻度)。
56
53
  */
57
54
  export declare function describeSchemaIssues(error: unknown): string | undefined;
58
55
  /**
59
- * zod 形错误 → 坏字段的**键路径**(去重保序;`(entry)` = 整条)。与 {@link describeSchemaIssues} 同一只读法(只读 `path`),
56
+ * zod 形错误 → 坏字段的**键路径**(去重保序;`(entry)` = 整条)。与 {@link describeSchemaIssues} 同一只坐标写法({@link issueCoordinateOf}),
60
57
  * 不含值。模型面 stage 的逐条判据(`judgeCatalogEntry`)与本折叠点的被丢条目身份共用这一只 —— 一个刻度。
61
58
  */
62
59
  export declare function describeSchemaIssueFields(error: unknown): string[];
63
- /** null = 不入诊断面(`hosts-grandfathered` / `legacy-governance-lifted`:值已被收编接受,与本地店同口径)。 */
64
- export declare function foldEffectiveReadWarning(w: EffectiveReadWarning): FoldedReadWarning | null;
60
+ /** 契约句子里**值**的位置在本仓诊断面上的写法(一个洞,不回显值;句子的固定词 —— 迁移指路、词表出路、字段名 —— 原样)。 */
61
+ export declare const SCHEMA_OPERAND_HOLE = "\u00ABvalue\u00BB";
62
+ /**
63
+ * 整域失效(`domain-defaulted`)的诊断文案:逐条 issue `坐标: 句子`,句子读 {@link issueSentenceOf}(与逐条丢弃面同一只):带结构化操作数的契约句重铸、
64
+ * 操作数换成 {@link SCHEMA_OPERAND_HOLE};不带的(zod 内建句、退役键墓碑句、迁移指路)原样。读不出 issues 形 ⇒ 退回 {@link errorText}。
65
+ * 为什么不直接用 `error.message`:zod 4 的 message 是整份 issues 的 JSON —— 本地腿出口的引号段指纹(`redactConfigError`)会把它从第一个
66
+ * 引号到最后一个引号整段吞掉,契约拒句里点名的迁移 id(「旧形读写两面响亮拒、拒句点名迁移 id」)就到不了运维眼前。
67
+ */
68
+ export declare function describeDomainIssues(error: unknown): string;
69
+ /** 一条读告警 → 它的诊断面(每一种都进:见文件头「归族判据」)。 */
70
+ export declare function foldEffectiveReadWarning(w: EffectiveReadWarning): FoldedReadWarning;
65
71
  /**
66
72
  * 🔴 S-712 / S-1295 —— **被丢目录条目的载体行**(载体 = `EffectiveConfig.models.droppedCatalogEntries`,读腿铸,随 effective 走 —— 盘上 LKG
67
73
  * 从同一个对象落盘,重启从 LKG 起服时它还在)。形的**唯一定义**(型由它推出;判据按它判盘上来的行)。
@@ -97,7 +103,8 @@ export type DroppedCatalogEntry = z.infer<typeof DroppedCatalogEntryRow>;
97
103
  * 两条读腿(远端 `http-client.ts` / 本地 `config-provider.ts`)共用的**分面单点**:折叠产物按 `scope` 分进候选门
98
104
  * (`domainErrors`)与逐条丢弃面(`droppedEntries`);逐条丢弃里的**目录条目**同一拍铸进目录载体(`droppedCatalogEntries`,
99
105
  * 一次遍历、一行折叠、一个词 —— 日志行与载体行不各算一次)。`switch` 穷举 —— 加一种 scope 不补臂 = 编译红,两腿不会各分各的。
100
- * 文案**未脱敏**:进日志流之前各腿在自己的产出边界上过 `redactConfigError`(与 `domainErrors` 同一道)。
106
+ * 文案**未脱敏**:进日志流之前各腿在自己的产出边界上脱敏 —— `domainErrors` 过 `redactConfigError`(引号段读法);`droppedEntries` 的文案是结构化句子
107
+ * (键名坐标 + 类型词 + 契约整句,值位是洞)、不含配置里的值,只过 `redactSecrets`(契约句的固定词里有引号 / 撇号,引号段读法会把迁移指路整段吞掉)。
101
108
  * 坏行本身照样走 `droppedEntries`(坐标 + 字段 + 词,不带值)。
102
109
  */
103
110
  export declare function splitFoldedReadWarnings(warnings: readonly EffectiveReadWarning[]): {
@@ -1,37 +1,52 @@
1
1
  import { z } from "zod";
2
- import { DROPPED_ENTRY_REASON_CODES, formatConfigPath, isDisclosableIdentity, } from "@sema-agent/settings-schema";
2
+ import { DROPPED_ENTRY_REASON_CODES, formatConfigPath, isDisclosableIdentity, renderQuotedSentence, } from "@sema-agent/settings-schema";
3
3
  export function describeSchemaIssues(error) {
4
4
  const issues = schemaIssuesOf(error);
5
5
  if (issues === undefined)
6
6
  return undefined;
7
7
  return issues
8
- .map((raw) => {
9
- const i = (typeof raw === "object" && raw !== null ? raw : {});
10
- const field = Array.isArray(i.path) && i.path.length > 0 ? i.path.map(String).join(".") : "(entry)";
11
- const what = i.code === "invalid_type" && typeof i.expected === "string" && typeof i.received === "string"
12
- ? `expected ${i.expected}, received ${i.received}`
13
- : typeof i.code === "string"
14
- ? i.code
15
- : "invalid";
16
- return `${field}: ${what}`;
17
- })
8
+ .map((raw) => `${issueCoordinateOf(raw) ?? "(entry)"}: ${issueSentenceOf(raw)}`)
18
9
  .join("; ");
19
10
  }
11
+ function issueCoordinateOf(raw) {
12
+ const p = typeof raw === "object" && raw !== null ? raw.path : undefined;
13
+ return Array.isArray(p) && p.length > 0 ? formatConfigPath(p) : undefined;
14
+ }
15
+ function issueSentenceOf(raw) {
16
+ const message = typeof raw === "object" && raw !== null ? raw.message : undefined;
17
+ return contractSentenceOf(raw) ?? (typeof message === "string" ? message : "(unreadable issue)");
18
+ }
19
+ function contractSentenceOf(raw) {
20
+ const params = (typeof raw === "object" && raw !== null ? raw.params : undefined);
21
+ return params !== undefined && Array.isArray(params.template) && Array.isArray(params.operands)
22
+ ? renderQuotedSentence(params, () => SCHEMA_OPERAND_HOLE)
23
+ : undefined;
24
+ }
20
25
  function schemaIssuesOf(error) {
21
26
  const issues = typeof error === "object" && error !== null ? error.issues : undefined;
22
27
  return Array.isArray(issues) && issues.length > 0 ? issues : undefined;
23
28
  }
24
29
  export function describeSchemaIssueFields(error) {
25
30
  const issues = schemaIssuesOf(error) ?? [];
26
- const fields = issues.map((raw) => {
27
- const p = typeof raw === "object" && raw !== null ? raw.path : undefined;
28
- return Array.isArray(p) && p.length > 0 ? p.map(String).join(".") : "(entry)";
29
- });
30
- return [...new Set(fields)];
31
+ return [...new Set(issues.map((raw) => issueCoordinateOf(raw) ?? "(entry)"))];
31
32
  }
32
33
  const MAX_ERROR_CHARS = 600;
33
- const cap = (s) => s.slice(0, MAX_ERROR_CHARS);
34
+ const TRUNCATED_MARK = "…(truncated)";
35
+ const cap = (s) => (s.length <= MAX_ERROR_CHARS ? s : `${s.slice(0, MAX_ERROR_CHARS - TRUNCATED_MARK.length)}${TRUNCATED_MARK}`);
34
36
  const errorText = (e) => (e instanceof Error ? e.message : String(e));
37
+ export const SCHEMA_OPERAND_HOLE = "«value»";
38
+ export function describeDomainIssues(error) {
39
+ const issues = schemaIssuesOf(error);
40
+ if (issues === undefined)
41
+ return errorText(error);
42
+ return issues
43
+ .map((raw) => {
44
+ const at = issueCoordinateOf(raw);
45
+ const sentence = issueSentenceOf(raw);
46
+ return at !== undefined ? `${at}: ${sentence}` : sentence;
47
+ })
48
+ .join("; ");
49
+ }
35
50
  const keyList = (prefix, keys, truncatedFrom) => {
36
51
  const total = truncatedFrom ?? keys.length;
37
52
  const budget = MAX_ERROR_CHARS - prefix.length;
@@ -50,11 +65,8 @@ const keyList = (prefix, keys, truncatedFrom) => {
50
65
  };
51
66
  export function foldEffectiveReadWarning(w) {
52
67
  switch (w.kind) {
53
- case "hosts-grandfathered":
54
- case "legacy-governance-lifted":
55
- return null;
56
68
  case "domain-defaulted":
57
- return { scope: "domain", domain: w.domain, error: cap(errorText(w.error)) };
69
+ return { scope: "domain", domain: w.domain, error: cap(describeDomainIssues(w.error)) };
58
70
  case "entry-dropped":
59
71
  return { scope: "entry", domain: w.domain, entry: formatConfigPath(w.path), error: cap(`entry dropped by domain schema: ${describeSchemaIssues(w.error) ?? errorText(w.error)}`), reasonCode: droppedReasonCodeOf(w) };
60
72
  case "unknown-keys-dropped":
@@ -99,8 +111,6 @@ export function splitFoldedReadWarnings(warnings) {
99
111
  const droppedCatalogEntries = [];
100
112
  for (const w of warnings) {
101
113
  const f = foldEffectiveReadWarning(w);
102
- if (f === null)
103
- continue;
104
114
  switch (f.scope) {
105
115
  case "domain":
106
116
  domainErrors.push({ domain: f.domain, error: f.error });
@@ -39,8 +39,8 @@ export interface RestartSliceCtx {
39
39
  }
40
40
  /** S-741(车OG):本引擎发布的重启信号。形的唯一定义是 settings-schema 6.1.0 `RestartSignal`(`{ reasons, version, since }`,在场即信号,
41
41
  * 不夹 `restartRequired` 布尔;本地手写的那份接口已删),这里只把 `reasons` 收窄到本文件的闭集 {@link RestartSlice} —— 写口
42
- * (`boot/config-center.ts` `publishRestart`)按它收,拼错 / 已退役的词编译红(opus 对抗腿 #2:直接用上游型时 `reasons` 是
43
- * `string[]`,闭集的编译期判据丢了)。`/health.restart` 与 fleet 心跳 `restart` 两个出口交 config-center 同一只 `restartState()`
42
+ * (`boot/web-admin.ts` `publishRestart`)按它收,拼错 / 已退役的词编译红(opus 对抗腿 #2:直接用上游型时 `reasons` 是
43
+ * `string[]`,闭集的编译期判据丢了)。`/health.restart` 与 fleet 心跳 `restart` 两个出口交 web-admin 同一只 `restartState()`
44
44
  * 的同一个对象,按契约形出门。 */
45
45
  export type EngineRestartSignal = RestartSignal & {
46
46
  reasons: RestartSlice[];
@@ -89,6 +89,8 @@ export declare function restartReasons(boot: EffectiveConfig | undefined, curren
89
89
  * undefined` (env-boot plane, no EffectiveConfig to compare) is conservatively "changed": the first center plane
90
90
  * always swaps. Not gated on tier activity: a Runner's expanded catalog goes stale on ANY plane change,
91
91
  * including a tiers-off flip, and the default entry rides the fingerprint because the Runner's copy carries its
92
- * own `default` (codex R8; SAME resolver as applyEffective — resolveDefaultModelName, single source). */
92
+ * own `default` (codex R8; SAME resolver as applyEffective — settings-schema `resolveCatalogDefaultModel`, single source: a
93
+ * `named` default fingerprints by its name, a `{select}` default by its criteria — the engine resolves the same criteria over
94
+ * the same fingerprinted catalog and tier table, so equal criteria on an unchanged plane give the same entry). */
93
95
  export declare function modelPlaneChanged(prev: EffectiveConfig | undefined, next: EffectiveConfig): boolean;
94
96
  //# sourceMappingURL=restart-signal.d.ts.map
@@ -1,7 +1,6 @@
1
- import { resolveActiveTiers } from "@sema-agent/settings-schema";
1
+ import { resolveActiveTiers, resolveCatalogDefaultModel } from "@sema-agent/settings-schema";
2
2
  import { stableStringify } from "@sema-agent/settings-schema/hash";
3
3
  import { DEFAULT_PLUGIN_ALLOW_HOSTS, materializablePlugins } from "../capabilities/center-plugins.js";
4
- import { resolveDefaultModelName } from "./apply-effective.js";
5
4
  import { CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
6
5
  const RESTART_SLICES = ["skills", "mcp", "a2a", "scenarios", "degrade-route", "read-face", "memory-consolidation"];
7
6
  function enabledOnly(rows) {
@@ -83,8 +82,8 @@ export function modelPlaneChanged(prev, next) {
83
82
  const enabled = enabledOnly(e.models?.models);
84
83
  const active = e.models ? (resolveActiveTiers(e.models) ?? null) : null;
85
84
  const names = new Set((enabled ?? []).map((m) => m.name));
86
- const def = resolveDefaultModelName(e, (n) => names.has(n), enabled?.[0]?.name ?? "");
87
- return stableStringify({ models: planeEntriesFingerprint(e.models?.models), tiers: active, default: def.name });
85
+ const def = e.models ? resolveCatalogDefaultModel(e.models, (n) => names.has(n), enabled?.[0]?.name ?? "") : undefined;
86
+ return stableStringify({ models: planeEntriesFingerprint(e.models?.models), tiers: active, default: def === undefined ? null : def.kind === "named" ? def.name : { select: def.select } });
88
87
  };
89
88
  return fp(prev) !== fp(next);
90
89
  }
@@ -5,7 +5,7 @@ import type { LoadedSkill } from "../capabilities/skills.js";
5
5
  import type { CenterEndpoint } from "./center-request.js";
6
6
  import type { CenterA2aPeer, CenterMcpServer, CenterSkillManifest } from "./types.js";
7
7
  /**
8
- * Merge center skills OVER the image baseline ([ref]-sibling B1; `sema-registry docs/MCP-SKILLS.md`). Load order =
8
+ * Merge center skills OVER the image baseline ([ref]-sibling B1; `web-admin docs/MCP-SKILLS.md`). Load order =
9
9
  * `loadSkills(SKILLS_DIR)` baseline → center skills lazily fetched by hash and overlaid BY NAME (center
10
10
  * wins on a clash → can override a built-in; guarded center-side by publisher RBAC + the publish gate).
11
11
  * `enabled:false` is skipped. Content is verified + cached by hash (same hash fetched once). A skill whose
@@ -18,7 +18,7 @@ export async function applyCenterSkills(baseline, manifest, center, logger, fetc
18
18
  const text = await fsp.readFile(joinPath(diskCacheDir, hex), "utf8");
19
19
  if (skillContentHash(text) === hash)
20
20
  return text;
21
- logger?.warn("sema_registry_skill_cache_corrupt", { hash, note: "on-disk body fails its own hash — ignored, refetching from center" });
21
+ logger?.warn("web_admin_skill_cache_corrupt", { hash, note: "on-disk body fails its own hash — ignored, refetching from center" });
22
22
  }
23
23
  catch {
24
24
  }
@@ -34,7 +34,7 @@ export async function applyCenterSkills(baseline, manifest, center, logger, fetc
34
34
  const tmp = joinPath(diskCacheDir, `${hex}.${Math.random().toString(36).slice(2, 8)}.tmp`);
35
35
  await fsp.writeFile(tmp, content, { mode: 0o600 });
36
36
  await fsp.rename(tmp, joinPath(diskCacheDir, hex));
37
- })().catch((err) => logger?.warn("sema_registry_skill_cache_write_failed", { hash, err: String(err) })));
37
+ })().catch((err) => logger?.warn("web_admin_skill_cache_write_failed", { hash, err: String(err) })));
38
38
  };
39
39
  const byName = new Map(baseline.map((s) => [s.spec.name, s]));
40
40
  const contentCache = new Map();
@@ -43,7 +43,7 @@ export async function applyCenterSkills(baseline, manifest, center, logger, fetc
43
43
  if (m.enabled === false)
44
44
  continue;
45
45
  if (seenNames.has(m.name))
46
- logger?.warn("sema_registry_skill_duplicate_name", { name: m.name, note: "duplicate name in center manifest — last entry wins" });
46
+ logger?.warn("web_admin_skill_duplicate_name", { name: m.name, note: "duplicate name in center manifest — last entry wins" });
47
47
  seenNames.add(m.name);
48
48
  try {
49
49
  let content = contentCache.get(m.contentHash);
@@ -56,16 +56,16 @@ export async function applyCenterSkills(baseline, manifest, center, logger, fetc
56
56
  contentCache.set(m.contentHash, content);
57
57
  const overrodeBuiltin = byName.has(m.name);
58
58
  byName.set(m.name, { spec: { name: m.name, description: m.description, content }, scenarios: m.scenarios ?? [] });
59
- logger?.info("sema_registry_skill", { name: m.name, hash: m.contentHash, scenarios: m.scenarios, overrodeBuiltin });
59
+ logger?.info("web_admin_skill", { name: m.name, hash: m.contentHash, scenarios: m.scenarios, overrodeBuiltin });
60
60
  }
61
61
  catch (err) {
62
- logger?.warn("sema_registry_skill_failed", { name: m.name, hash: m.contentHash, err: String(err), note: "keeping baseline if any; skipping center version" });
62
+ logger?.warn("web_admin_skill_failed", { name: m.name, hash: m.contentHash, err: String(err), note: "keeping baseline if any; skipping center version" });
63
63
  }
64
64
  }
65
65
  await Promise.all(pendingWrites);
66
66
  return [...byName.values()];
67
67
  }
68
- function resolveRefs(refs, server, kind, logger, event = "sema_registry_mcp_env_missing", skipNote = "skipping this MCP server — referenced env var is unset in this service") {
68
+ function resolveRefs(refs, server, kind, logger, event = "web_admin_mcp_env_missing", skipNote = "skipping this MCP server — referenced env var is unset in this service") {
69
69
  if (!refs)
70
70
  return {};
71
71
  const out = {};
@@ -86,7 +86,7 @@ export function resolveMcpServers(mcp, logger) {
86
86
  if (s.enabled === false)
87
87
  continue;
88
88
  if (!isUsableWireName(s.name)) {
89
- logger?.warn("sema_registry_mcp_name_unusable", {
89
+ logger?.warn("web_admin_mcp_name_unusable", {
90
90
  server: describeBadWireName(s.name),
91
91
  note: "server SKIPPED — an MCP server name is its identity (it becomes the tool namespace prefix) and core REFUSES an out-of-bound name at prepare, which would fail every task in this scenario. Shorten the name and republish; sibling servers are unaffected.",
92
92
  });
@@ -99,7 +99,7 @@ export function resolveMcpServers(mcp, logger) {
99
99
  if (isContentOriginWord(s.contentOrigin))
100
100
  contentOrigin = { contentOrigin: s.contentOrigin };
101
101
  else
102
- logger?.warn("sema_registry_mcp_content_class_invalid", {
102
+ logger?.warn("web_admin_mcp_content_class_invalid", {
103
103
  server: s.name,
104
104
  value: describeBadContentOrigin(s.contentOrigin),
105
105
  allowed: CONTENT_ORIGIN_WORDS,
@@ -109,7 +109,7 @@ export function resolveMcpServers(mcp, logger) {
109
109
  const source = readMcpSourceLabel(s.name, s.source);
110
110
  const facesRead = readMcpToolFaces(s.toolFaces);
111
111
  if (!facesRead.ok) {
112
- logger?.warn("sema_registry_mcp_tool_faces_invalid", {
112
+ logger?.warn("web_admin_mcp_tool_faces_invalid", {
113
113
  server: s.name,
114
114
  problem: facesRead.problem,
115
115
  note: "server SKIPPED — a toolFaces overlay is a NARROWING declaration (its pathTarget is what the sensitive-path / write fences read); mounting the server without it would silently drop the protection you declared. Fix the entry and republish.",
@@ -137,7 +137,7 @@ export function resolveMcpServers(mcp, logger) {
137
137
  }
138
138
  }
139
139
  if (out.length > 0)
140
- logger?.info("sema_registry_mcp", { servers: out.map((s) => s.spec.name) });
140
+ logger?.info("web_admin_mcp", { servers: out.map((s) => s.spec.name) });
141
141
  return { servers: out, reservedNames };
142
142
  }
143
143
  export function mcpForScenario(center, scenario) {
@@ -153,13 +153,13 @@ export function resolveA2aPeers(a2a, logger) {
153
153
  if (p.enabled === false)
154
154
  continue;
155
155
  if (!isUsableWireName(p.name)) {
156
- logger?.warn("sema_registry_a2a_name_unusable", {
156
+ logger?.warn("web_admin_a2a_name_unusable", {
157
157
  peer: describeBadWireName(p.name),
158
158
  note: "peer SKIPPED — an A2A peer name is its identity (it lands as the roster's `origin.peer`) and core REFUSES an out-of-bound name at prepare, which would fail every task in this scenario. Shorten the name and republish; sibling peers are unaffected.",
159
159
  });
160
160
  continue;
161
161
  }
162
- const headers = resolveRefs(p.headerRefs, p.name, "header", logger, "sema_registry_a2a_env_missing", "skipping this A2A peer — referenced env var is unset in this service");
162
+ const headers = resolveRefs(p.headerRefs, p.name, "header", logger, "web_admin_a2a_env_missing", "skipping this A2A peer — referenced env var is unset in this service");
163
163
  if (headers === null)
164
164
  continue;
165
165
  out.push({
@@ -175,7 +175,7 @@ export function resolveA2aPeers(a2a, logger) {
175
175
  });
176
176
  }
177
177
  if (out.length > 0)
178
- logger?.info("sema_registry_a2a", { peers: out.map((p) => p.spec.name) });
178
+ logger?.info("web_admin_a2a", { peers: out.map((p) => p.spec.name) });
179
179
  return out;
180
180
  }
181
181
  export function a2aForScenario(peers, scenario) {
@@ -17,17 +17,11 @@ import type { Logger } from "../observability/logger.js";
17
17
  import { type Batch1LimitKey } from "./hot-keys-registry.js";
18
18
  import type { EffectiveLimits } from "./types.js";
19
19
  /**
20
- * stage 的入参视图。
21
- *
22
- * 🔴 `legacyGates` 是**已过旧位 presence 判据**的闸值(判据属主 = `runtimeGatePresent`,调用方过一遍再传)。
23
- * 两位刻意判据不同,不是疏忽:旧位(`runtime`)的容忍语义(例如 `costQuotaWindowSec: 0` 判「缺席」保 env)
24
- * 是既有 center 的在营契约,收紧它会把一个存量发布变成整批拒;新位(`limits`)从第一天起就是严判
25
- * (坏值 = 整批响亮拒)。判据属主各只有一个,谁都没有被复刻第二份。
20
+ * stage 的入参视图:`limits` 域是这些键的**唯一**发布位(settings-schema 11 退役了 `runtime` 域,它的旧位读法随之删)。
26
21
  */
27
22
  export interface LimitsSource {
28
23
  readonly version: number;
29
24
  readonly limits?: EffectiveLimits;
30
- readonly legacyGates?: Readonly<Record<string, unknown>>;
31
25
  }
32
26
  /** 逐键 defer 的铸因(codex R2-F2):值合法但这一键**不热**,变更只能重启生效。 */
33
27
  export interface DeferredLimit {
@@ -69,7 +69,6 @@ function candidateView(config, assignments) {
69
69
  }
70
70
  export function stageLimits(config, eff, logger) {
71
71
  const baseline = baselineOf(config);
72
- const legacy = eff.legacyGates ?? {};
73
72
  const assignments = [];
74
73
  const deletions = [];
75
74
  const changed = [];
@@ -79,7 +78,7 @@ export function stageLimits(config, eff, logger) {
79
78
  const rawLimits = eff.limits;
80
79
  if (rawLimits !== undefined && (rawLimits === null || typeof rawLimits !== "object" || Array.isArray(rawLimits))) {
81
80
  const rejected = { keys: [...BATCH1_LIMIT_KEYS], reason: `the published limits domain must be a JSON object — got ${rawLimits === null ? "null" : Array.isArray(rawLimits) ? "array" : typeof rawLimits}` };
82
- logger?.warn("sema_registry_limits_invalid", {
81
+ logger?.warn("web_admin_limits_invalid", {
83
82
  ...rejected,
84
83
  version: eff.version,
85
84
  kept: "the whole limits batch is refused — a malformed container must never read as 'the center manages none of these keys' (that would silently revert every published ceiling to the env baseline)",
@@ -91,7 +90,7 @@ export function stageLimits(config, eff, logger) {
91
90
  const known = new Set([...BATCH1_LIMIT_KEYS, ...DEFERRED_LIMIT_KEYS.map((d) => d.key)]);
92
91
  const unconsumed = Object.keys(limits).filter((k) => !known.has(k));
93
92
  if (unconsumed.length > 0) {
94
- logger?.warn("sema_registry_limits_unconsumed_keys", {
93
+ logger?.warn("web_admin_limits_unconsumed_keys", {
95
94
  keys: unconsumed,
96
95
  version: eff.version,
97
96
  note: "published in the limits domain but not consumed by this server build (misspelled, or a later-batch key) — the value is inert until a batch that owns it ships",
@@ -99,7 +98,7 @@ export function stageLimits(config, eff, logger) {
99
98
  }
100
99
  }
101
100
  for (const key of BATCH1_LIMIT_KEYS) {
102
- const published = limits !== undefined && Object.prototype.hasOwnProperty.call(limits, key) ? limits[key] : legacy[key];
101
+ const published = limits !== undefined && Object.prototype.hasOwnProperty.call(limits, key) ? limits[key] : undefined;
103
102
  let next;
104
103
  if (published === undefined) {
105
104
  next = baseline[key];
@@ -129,7 +128,7 @@ export function stageLimits(config, eff, logger) {
129
128
  }
130
129
  if (badKeys.length > 0) {
131
130
  const rejected = { keys: badKeys, reason: reason };
132
- logger?.warn("sema_registry_limits_invalid", {
131
+ logger?.warn("web_admin_limits_invalid", {
133
132
  ...rejected,
134
133
  version: eff.version,
135
134
  kept: "the whole limits batch is refused — the prior values keep serving (a half-applied limit face means the published numbers and the live gate are different generations)",
@@ -140,7 +139,7 @@ export function stageLimits(config, eff, logger) {
140
139
  if (!verdict.ok) {
141
140
  const involved = changed.length > 0 ? changed : BATCH1_LIMIT_KEYS.slice();
142
141
  const rejected = { keys: [...involved], reason: verdict.reason, restartReason: verdict.restartReason };
143
- logger?.warn("sema_registry_limits_invalid", {
142
+ logger?.warn("web_admin_limits_invalid", {
144
143
  ...rejected,
145
144
  invariant: verdict.id,
146
145
  version: eff.version,
@@ -149,7 +148,7 @@ export function stageLimits(config, eff, logger) {
149
148
  return { assignments: [], deletions: [], changed: [], deferred: [], rejected };
150
149
  }
151
150
  if (deferred.length > 0) {
152
- logger?.warn("sema_registry_limits_deferred", {
151
+ logger?.warn("web_admin_limits_deferred", {
153
152
  keys: deferred.map((d) => d.key),
154
153
  version: eff.version,
155
154
  reasons: [...new Set(DEFERRED_LIMIT_KEYS.filter((d) => deferred.some((x) => x.key === d.key)).map((d) => d.why))],