@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
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Model, type PathRuleBases, type PermissionRule } from "@sema-agent/core";
2
- import { type CenterEndpoint } from "./config-center/center-request.js";
2
+ import { type CenterEndpoint } from "./web-admin-client/center-request.js";
3
3
  import { type Autonomy } from "./runtime-governance.js";
4
4
  import { type RunnerEnvNameAllowlistKnob, type ServerEnvNameAllowlistKnob } from "./env-name-allowlist-knobs.js";
5
5
  import type { ServiceConfig, ServiceConfigFlat, ServiceConfigGroups } from "./config-types.js";
@@ -12,7 +12,7 @@ export { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config
12
12
  /** Parse the AUTONOMY env into a validated autonomy mode. Unset/empty → undefined (unmanaged → no extra
13
13
  * tightening). An UNKNOWN value FAILS at startup rather than silently becoming a no-op (a typo'd `AUTONOMY=readonly`
14
14
  * must not silently leave a deployment ungoverned — fail-loud, same discipline as numEnv). Exported so the HOT
15
- * config overlay (config-center/facade.ts `applyRuntimeHot`) can re-derive the ENV BASELINE to revert to when center
15
+ * config overlay (web-admin-client/facade.ts `applyRuntimeHot`) can re-derive the ENV BASELINE to revert to when center
16
16
  * stops managing `autonomy` (a stale center override must not stick — see applyRuntimeHot). */
17
17
  export declare function parseAutonomy(raw: string | undefined): Autonomy | undefined;
18
18
  /** [ref]-T1 — parse `USAGE_WINDOWS`(单键 JSON 数组;逐窗散键在窗数可变时组合爆炸,JSON 是唯一
@@ -32,43 +32,6 @@ export declare function parseUsageWindows(raw: string | undefined): readonly {
32
32
  maxCostUsd?: number;
33
33
  anchor: "first-use" | "rolling";
34
34
  }[] | undefined;
35
- /** [ref] 兼容面全清:退役 env 名墓碑——老名**在场即拒启**带新名指引。静默忽略=部署带着失效配置无感跑;双读=兼容层吸收
36
- * 下游冒烟。都不要。
37
- *
38
- * 🪦 **墓碑是永久的响亮拒,不设到期**(S-588)。一行墓碑就是那句拒启句本身,成本一行;删掉它 = 旧名重新被悄悄忽略。
39
- * ⇒ 本表只增不减,**唯一的删除条件是这个名字要被赋予新含义**:那一次提交同时删掉墓碑行,并在 MIGRATION 写明旧名改作何用。
40
- * (与一次性迁移不是一回事:迁移码每次起服都跑、碰真数据,所以有窗、窗关即删码 —— 见 `plugins/one-time-migrations.ts`
41
- * 的登记表;墓碑不碰数据,只拒启。)
42
- * A tombstone is a permanent loud refusal with no expiry: deleting a row lets the retired name be silently ignored again,
43
- * so a row goes only when the name is given a new meaning — in that same commit, with a MIGRATION note.
44
- *
45
- * **旧名在场即拒,替代名在不在都一样**:判据只问旧名在不在场,不看替代键设没设、设成什么 ——「新旧两个名字都留着」正是
46
- * 迁移到一半的部署最常见的形,旧名在那一形里若被静默忽略,运维会以为它还在生效。server 3.0.0 的旧负名(`X_DISABLED` →
47
- * `X_ENABLED`)同在本表(`replaced` 族:极性相反,照抄旧值过去恰好是反义),与其余墓碑走同一只 {@link rejectRetiredEnvs};
48
- * 那几个旋钮的读点只读正名(`boolEnv`),不再有第二套「正名缺席才看旧名」的判据(S-588 之前的 `boolEnvWithLegacyNegated`)。
49
- *
50
- * 墓碑分三族(A4 两族 + S-753 第三族),每族各说各的尾句 —— 族是**闭集**,渲染是穷举表(加一族不写尾句 = 编译红):
51
- * · `renamed` = 同一能力换了个键名,值语义逐字不变 ⇒ 「照新名改写即可」;
52
- * · `replaced` = 有替代键,但**值语义变了**(缺省 / `0` 义 / 计量段 / 极性不同)⇒ 点名新键 + 说清变了什么,**不许**说「语义不变」
53
- * —— 照抄旧值过去就是另一个意思(S-753:`MODEL_CONNECT_TIMEOUT_MS` → `MODEL_FIRST_BYTE_TIMEOUT_MS`);
54
- * · `removed` = 能力本身没了,`why` 是「为什么没了」,**没有可 rename 的目标**。
55
- * 拒句带码 `[config.env_key_retired]`(拒启码,拼法随 `config.*` 家族;与中心凭证两码同形,出现在拒句方括号里)。
56
- * 空串 = 未设(与 `env()` 同一条约定:compose `${X:-}` 透传的空值不是一次表态),不拒。 */
57
- export type RetiredEnv = readonly [old: string, to: string, kind: "renamed"] | readonly [old: string, to: string, kind: "replaced", why: string] | readonly [old: string, why: string, kind: "removed"];
58
- export type RetiredEnvKind = RetiredEnv[2];
59
- /** 墓碑拒启码(机读;拒句方括号里)。 */
60
- export declare const RETIRED_ENV_REFUSAL_CODE = "config.env_key_retired";
61
- export declare const RETIRED_ENVS: readonly RetiredEnv[];
62
- /** S-885 —— 墓碑表的 wire 投影一行(`GET /v1/config/catalog` 的 `retired[]`):按族定形 —— renamed 只带 `replacement`;replaced 带
63
- * `replacement` + `reason`(值语义怎么变了);removed 只带 `reason`(能力为什么没了)。 */
64
- export interface RetiredEnvWire {
65
- readonly name: string;
66
- readonly kind: RetiredEnvKind;
67
- readonly replacement?: string;
68
- readonly reason?: string;
69
- }
70
- /** 墓碑表 → wire 投影(纯数据 ⇒ `build*`)。**同一张表、同序**;族是闭集,穷举 switch(加一族不写这里 ⇒ 编译红)。 */
71
- export declare function buildRetiredEnvWire(): RetiredEnvWire[];
72
35
  /** Numeric env with validation: a non-numeric value FAILS at startup instead of silently becoming `NaN`
73
36
  * (which e.g. slips past the `runStaleSec` liveness guard and silently disables the reaper). */
74
37
  export declare function numEnv(name: string, fallback: string): number;
@@ -156,7 +119,7 @@ export declare function logConfigDiagnostics(logger: {
156
119
  type ModelCost = NonNullable<Model["cost"]>;
157
120
  /**
158
121
  * 🔴 B-089 —— `Model.cost` 这**一个键**的唯一铸点(可展开片段):有价目表 ⇒ 键在;没配 ⇒ **键不出**。
159
- * 三个铸点(env 主槽 / env cheap 槽 / config-center 车道)共用它,所以「缺席怎么表达」在本仓只有一处答案。
122
+ * 三个铸点(env 主槽 / env cheap 槽 / web-admin 车道)共用它,所以「缺席怎么表达」在本仓只有一处答案。
160
123
  *
161
124
  * `Model.cost` 自 core 7.16.0([ref])起在**类型面**上也是可选的(`dist/engine/llm/types.d.ts` 的
162
125
  * `cost?:`),与它一直以来的运行期读法([ref] 的 `isModelPriced`:这个键在不在)对齐 ——
@@ -178,7 +141,7 @@ export declare function buildModelCostKey(cost: ModelCost | undefined): Pick<Mod
178
141
  * `loadConfig` 只见 env 车道,而 `main.ts` 在第一次 effective 拉取**之前**就把 boot 诊断排空了 ——
179
142
  * 在那里判会同时犯两个方向的错:center 名册已配价、env 未配 ⇒ **假警**;env 已配价、center 名册撤了价
180
143
  * (或天花板只由 center 的 limits 下发)⇒ **漏警**。故本函数只给判据,席位在两只入口各自的
181
- * **采纳之后**(`main.ts` 的 `createConfigCenterRuntime` 之后 / `run-local.ts` 的 `applyEffective` 之后)。
144
+ * **采纳之后**(`main.ts` 的 `createWebAdminRuntime` 之后 / `run-local.ts` 的 `applyEffective` 之后)。
182
145
  *
183
146
  * 判据 = **目录里没有任何一只带价模型**,问法与 `budget.buildPricing` 的填表判据是**同一只函数**
184
147
  * (S-231:core 7.16.0 导出的 `isModelPriced`;第二参传 `undefined` = 这一问发生在注入表建起来之前,
@@ -263,17 +226,17 @@ export declare function parseEnvNameAllowlist(knob: RunnerEnvNameAllowlistKnob):
263
226
  * here claiming this was "the exact CC alignment core recommends embedders set" mis-read core's per-model
264
227
  * advice as a width rule). Returns a discriminant so the env lane can turn the removed implicit value into
265
228
  * a loud CONFIG_NOTICES line ("removed-1m"); the center lane's roster culture is explicit declaration.
266
- * Shared by the env lane (main + cheap models) and the center lane (sema-registry toModel). */
229
+ * Shared by the env lane (main + cheap models) and the center lane (web-admin toModel). */
267
230
  export declare function applyAutoCompactWindow(m: Model, explicit?: number): "explicit" | "sonnet5" | "removed-1m" | "removed";
268
231
  /**
269
- * **严格名册布防**的唯一判据(boot 就绪门 `boot/config-center.ts` 与 S-623 端点判据同调这一只)。
232
+ * **严格名册布防**的唯一判据(boot 就绪门 `boot/web-admin.ts` 与 S-623 端点判据同调这一只)。
270
233
  *
271
234
  * 布防 = 远程 registry ∧ 非 dryRun ∧ `CONFIG_REQUIRE_ROSTER=true`(严格 `"true"`,直读 env、不进 ServiceConfig —— 纯
272
235
  * boot 期姿势旋钮,原判据逐字搬来,零语义变化)。布防时运维声明 env MODEL_ID 是**占位**:名册落地前计费提交 503、
273
236
  * `/health` ready:false,落地后整张模型表被中心目录换装。dryRun 是 compare-only(名册从不被应用),local / 纯 env
274
237
  * 部署没有「中心」可等 ⇒ 都不布防。
275
238
  */
276
- export declare function isRosterGateArmed(configCenter: ServiceConfig["configCenter"]): boolean;
239
+ export declare function isRosterGateArmed(webAdmin: ServiceConfig["webAdmin"]): boolean;
277
240
  /**
278
241
  * 一条工具名**永不匹配任何现役工具**的三种拼法,及其修正指引。审批/权限名单是 server 侧 ToolPolicy 的
279
242
  * RAW 逐字比对(canonicalToolName 折叠面已随 core 5.0.0 [ref] 退役),所以写错的名字不会报错、只会静默
@@ -281,7 +244,7 @@ export declare function isRosterGateArmed(configCenter: ServiceConfig["configCen
281
244
  *
282
245
  * 判据在此**单点成文**,三条腿共用(它们此前各自为政,拼法覆盖面不一致才是真缺口):
283
246
  * ① env 腿(`APPROVAL_REQUIRE`/`DENY`/`NEVER_AUTO`,下方)—— boot 拒启;
284
- * ② 中心下发腿(`runtime.approvalRequire`,config-center/apply-effective.ts)—— 域级拒绝+保前值+warn
247
+ * ② 中心下发腿(`runtime.approvalRequire`,web-admin-client/apply-effective.ts)—— 域级拒绝+保前值+warn
285
248
  * (常驻进程不 crash);
286
249
  * ③ per-request 腿(`settings.permissions.{ask,deny}`,task-settings.ts)—— 该请求 422。
287
250
  * (两张 allow 表 —— 请求的 `settings.permissions.allow` 与运维的 `APPROVAL_GRANT` —— 自 7.117.0 / core 7.42.0 起不经这里:它们整份交给引擎的
@@ -426,7 +389,7 @@ export interface FleetFenceLane {
426
389
  */
427
390
  export declare function fleetFenceLaneOf(cfg: {
428
391
  readonly fleetGeneration?: string | undefined;
429
- readonly configCenter?: (CenterEndpoint & {
392
+ readonly webAdmin?: (CenterEndpoint & {
430
393
  readonly worker?: string | undefined;
431
394
  }) | undefined;
432
395
  readonly fleetAdvertiseAddress?: string | undefined;
package/dist/config.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { DEFAULT_MODEL_REF } from "./model-select.js";
2
+ import { DEFAULT_AT_MENTION, DEFAULT_MODEL_REF } from "./model-select.js";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, isModelPriced, PROTOCOL_TABLE, RECOMMENDED_SENSITIVE_PATTERNS, judgeRuleToolName, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable } from "@sema-agent/core";
7
- import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, CENTER_CREDENTIAL_ENV } from "@sema-agent/settings-schema";
7
+ import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, WEB_ADMIN_ENV, retiredEnvRefusal } from "@sema-agent/settings-schema";
8
8
  import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
9
9
  import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
10
10
  import { isV2ScopeKey, memoryReplicationCoverage, replicationCovers } from "./memory-scope.js";
@@ -13,7 +13,7 @@ import { GIT_API_KINDS, isGitApiKind } from "./git-api-kind.js";
13
13
  import { CONFIG_PROVIDER_WORDS, RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS, MAX_TIMER_DELAY_MS, AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, DRAIN_GRACE_FLOOR_MS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS, fleetScopeOf } from "./config-types.js";
14
14
  import { FLEET_GENERATION_ENV, FLEET_SCOPE_ENV, SERVING_LEASE_TTL_FLOOR_MS, isCanonicalGeneration } from "@sema-agent/settings-schema/fleet";
15
15
  import { ScopeId } from "@sema-agent/settings-schema/api/scopes";
16
- import { centerLaneConfigured, pathBaseUrlRefusal } from "./config-center/center-request.js";
16
+ import { centerLaneConfigured, pathBaseUrlRefusal } from "./web-admin-client/center-request.js";
17
17
  import { resolveLocalDataRoot } from "./local-data-root.js";
18
18
  import { buildWriteProtectionPosture } from "./write-protection.js";
19
19
  import { normalizeSshFingerprint } from "./ssh-host-key.js";
@@ -22,7 +22,7 @@ import { THINKING_LEVELS, THINKING_FORMATS, buildModelCompat, isThinkingFormat,
22
22
  import {} from "./posture-source.js";
23
23
  import { CONTROL_AND_BIDI_STRIP_RE } from "./text-bidi.js";
24
24
  import { describeUnmatchableRuleName, ruleEntryToolName, unmatchableRuleNameOf } from "./namespaced-rule-name.js";
25
- import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal } from "./config-invariants.js";
25
+ import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, centerIdentityRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal } from "./config-invariants.js";
26
26
  import { declaredSemaConfigDir } from "./sema-config-dir.js";
27
27
  import { lkgPathOverrideOf } from "./config-lkg.js";
28
28
  import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneOf, executionLaneRequiredRefusal, isSingleMachineDefaultLane, laneRequiredEnv } from "./execution-lane-caps.js";
@@ -33,7 +33,8 @@ import { createCenterCredentialSource, readCenterCredentialDeclaration } from ".
33
33
  import { parseCenterCredentialRenewLeadMs } from "./center-credential-renewer.js";
34
34
  import { modelProviderRefusal } from "./model-provider.js";
35
35
  import { modelRouteUrlRefusal } from "./model-route-url.js";
36
- import { buildHttpUrlRefusal, httpUrlFlaws } from "./url-shape.js";
36
+ import { httpUrlFlaws } from "@sema-agent/settings-schema";
37
+ import { buildHttpUrlRefusal } from "./url-refusal.js";
37
38
  import { csvOf, envLaneTable, judgeModelRouteGap, mintedCheapId, readModelRouteEnv } from "./model-route-gap.js";
38
39
  import { displayListEntry } from "./list-entry-display.js";
39
40
  import { buildDisplaySafeUrl, redactSecrets } from "./trace/redact.js";
@@ -113,74 +114,10 @@ function defaultSkillsDir() {
113
114
  return "skills";
114
115
  }
115
116
  }
116
- export const RETIRED_ENV_REFUSAL_CODE = "config.env_key_retired";
117
- export const RETIRED_ENVS = [
118
- ["TIDB_HOST", "MYSQL_HOST", "renamed"], ["TIDB_PORT", "MYSQL_PORT", "renamed"], ["TIDB_USER", "MYSQL_USER", "renamed"],
119
- ["TIDB_PASSWORD", "MYSQL_PASSWORD", "renamed"], ["TIDB_DATABASE", "MYSQL_DATABASE", "renamed"], ["TIDB_POOL_SIZE", "MYSQL_POOL_SIZE", "renamed"],
120
- ["CONFIG_CENTER_URL", "SEMA_REGISTRY_URL", "renamed"], ["CONFIG_CENTER_TOKEN", CENTER_CREDENTIAL_ENV.token, "renamed"],
121
- ["CONFIG_CENTER_DRY_RUN", "SEMA_REGISTRY_DRY_RUN", "renamed"], ["CONFIG_CENTER_WORKER", "SEMA_REGISTRY_WORKER", "renamed"],
122
- ["ANTHROPIC_BASEURL", "ANTHROPIC_BASE_URL", "renamed"],
123
- ["REGISTRY_ADMIN_TOKEN", "SEMA_REGISTRY_ADMIN_TOKEN", "renamed"],
124
- ["OA_API_BASEURL", "the OA scenario was removed in 5.0.0", "removed"],
125
- ["OA_SERVICE_TOKEN", "the OA scenario was removed in 5.0.0", "removed"],
126
- ["OA_ISSUE_TOKEN", "the OA scenario was removed in 5.0.0", "removed"],
127
- ["OA_ISSUE_BASEURL", "the OA scenario was removed in 5.0.0", "removed"],
128
- ["OA_ISSUE_OWNER", "the OA scenario was removed in 5.0.0", "removed"],
129
- ["OA_ISSUE_REPO", "the OA scenario was removed in 5.0.0", "removed"],
130
- ["OA_ISSUE_LABELS", "the OA scenario was removed in 5.0.0", "removed"],
131
- ["APPROVAL_POLL_MS", "the OA poll-approval lane was removed in 5.0.0; the durable checkpoint lane is the only gate", "removed"],
132
- ["LEADER_CONFLICT_ROUNDS", "the bounded merge-conflict-resolution agent was deleted in 7.83.0 按 C-R63 删除,合不上交用户:leader 交回 result.conflict(分支 + 冲突文件),run 状态 needs_human", "removed"],
133
- [
134
- "MODEL_CONNECT_TIMEOUT_MS",
135
- "MODEL_FIRST_BYTE_TIMEOUT_MS",
136
- "replaced",
137
- "since core 7.33.0 the TCP + TLS connect clock is the transport's own; the wait from sending the request to its response headers — " +
138
- "what this key really bounded — is MODEL_FIRST_BYTE_TIMEOUT_MS (unset = core's own default, plus an upload allowance and one " +
139
- "widened retry; 0 = no engine clock on that segment and the transport's header / body clocks off too — the TCP + TLS connect " +
140
- "keeps the transport's own clock)",
141
- ],
142
- ["PROJECT_MEMORY_DISABLED", "PROJECT_MEMORY_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: PROJECT_MEMORY_DISABLED=true is PROJECT_MEMORY_ENABLED=false (the positive name defaults on)"],
143
- ["CONFIG_LKG_DISABLED", "CONFIG_LKG_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: CONFIG_LKG_DISABLED=true is CONFIG_LKG_ENABLED=false (the positive name defaults on)"],
144
- ["HOST_BG_DISABLED", "HOST_BG_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: HOST_BG_DISABLED=true is HOST_BG_ENABLED=false (the positive name defaults on)"],
145
- ["HOST_EXEC_SPOOL_DISABLED", "HOST_EXEC_SPOOL_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: HOST_EXEC_SPOOL_DISABLED=true is HOST_EXEC_SPOOL_ENABLED=false (the positive name defaults on)"],
146
- ["UNATTENDED_PERMISSION_MODE", "since server 7.109.0 the unattended legs (leader workers and their repair legs, run-local, and their durable resumes) always run permissionMode \"default\" with an approver that answers by ask class: a machine answers only \"ordinary\" asks, and every other class goes to the leg's approver seat (run-local's terminal prompt) or parks / is refused — there is no mode to choose", "removed"],
147
- ];
148
- for (const row of RETIRED_ENVS)
149
- Object.freeze(row);
150
- Object.freeze(RETIRED_ENVS);
151
- export function buildRetiredEnvWire() {
152
- return RETIRED_ENVS.map((row) => {
153
- switch (row[2]) {
154
- case "renamed":
155
- return { name: row[0], kind: row[2], replacement: row[1] };
156
- case "replaced":
157
- return { name: row[0], kind: row[2], replacement: row[1], reason: row[3] };
158
- case "removed":
159
- return { name: row[0], kind: row[2], reason: row[1] };
160
- default: {
161
- const unhandled = row;
162
- throw new Error(`unhandled retired-env kind: ${String(unhandled[2])}`);
163
- }
164
- }
165
- });
166
- }
167
- const RETIRED_ENV_SENTENCE = {
168
- renamed: (hits) => `${hits.map(([old, to]) => `${old} → use ${to}`).join("; ")} — rename the env key(s); the value semantics are unchanged`,
169
- replaced: (hits) => `${hits.map(([old, to, , why]) => `${old} → ${to} (${why})`).join("; ")} — set the replacement deliberately: ` +
170
- `its value semantics differ from the retired key's, so do not copy the old value across unread`,
171
- removed: (hits) => `${hits.map(([old, why]) => `${old} (${why})`).join("; ")} — remove the env key(s): the capability itself was retired, ` +
172
- `there is no replacement name and no rename that restores it`,
173
- };
174
- function retiredEnvSentence(kind, hit) {
175
- const ofKind = hit.filter((h) => h[2] === kind);
176
- return ofKind.length > 0 ? [RETIRED_ENV_SENTENCE[kind](ofKind)] : [];
177
- }
178
117
  function rejectRetiredEnvs() {
179
- const hit = RETIRED_ENVS.filter(([o]) => process.env[o] !== undefined && process.env[o] !== "");
180
- if (hit.length === 0)
181
- return;
182
- const parts = Object.keys(RETIRED_ENV_SENTENCE).flatMap((kind) => retiredEnvSentence(kind, hit));
183
- throw new Error(`retired env name(s) set [${RETIRED_ENV_REFUSAL_CODE}] — refused while present, whether or not the replacement is also set: ${parts.join(" | ")}.`);
118
+ const refusal = retiredEnvRefusal(process.env);
119
+ if (refusal !== undefined)
120
+ throw new Error(refusal);
184
121
  }
185
122
  export function numEnv(name, fallback) {
186
123
  return parseNumOrFail(name, env(name, fallback));
@@ -247,7 +184,7 @@ function parseConfigProvider() {
247
184
  return undefined;
248
185
  const word = CONFIG_PROVIDER_WORDS.find((w) => w === raw);
249
186
  if (word === undefined) {
250
- throw new Error(`env CONFIG_PROVIDER="${raw.slice(0, 120)}" must be one of: ${CONFIG_PROVIDER_WORDS.join(", ")} — or leave it unset (then SEMA_REGISTRY_URL, if set, selects the center as the config source; otherwise this worker is env-only). ` +
187
+ throw new Error(`env CONFIG_PROVIDER="${raw.slice(0, 120)}" must be one of: ${CONFIG_PROVIDER_WORDS.join(", ")} — or leave it unset (then ${WEB_ADMIN_ENV.url}, if set, selects the center as the config source; otherwise this worker is env-only). ` +
251
188
  `The word is matched exactly (no trimming, no case folding). Refusing to boot rather than guessing: the config source, the execution-lane default and the local config root all key on this one word.`);
252
189
  }
253
190
  return word;
@@ -937,8 +874,8 @@ function parseStoreDomain(ctx) {
937
874
  : undefined,
938
875
  };
939
876
  }
940
- export function isRosterGateArmed(configCenter) {
941
- return configCenter !== undefined && !configCenter.dryRun && process.env.CONFIG_REQUIRE_ROSTER === "true";
877
+ export function isRosterGateArmed(webAdmin) {
878
+ return webAdmin !== undefined && !webAdmin.dryRun && process.env.CONFIG_REQUIRE_ROSTER === "true";
942
879
  }
943
880
  function parseModelDomain() {
944
881
  const route = readModelRouteEnv(process.env);
@@ -1009,7 +946,7 @@ function parseModelDomain() {
1009
946
  CONFIG_NOTICES.push({
1010
947
  level: "warn",
1011
948
  event: "model_cheap_slot_without_main",
1012
- fields: { hint: "MODEL_CHEAP_ID is set but MODEL_ID is not — the env cheap slot derives from the env main model, so it is not minted; the catalog (config.d/models.json or the config center) serves the roles. Set MODEL_ID to use the env slots, or unset MODEL_CHEAP_ID to silence this line." },
949
+ fields: { hint: "MODEL_CHEAP_ID is set but MODEL_ID is not — the env cheap slot derives from the env main model, so it is not minted; the catalog (config.d/models.json or the web-admin) serves the roles. Set MODEL_ID to use the env slots, or unset MODEL_CHEAP_ID to silence this line." },
1013
950
  });
1014
951
  break;
1015
952
  case "same_as_main":
@@ -1145,7 +1082,7 @@ function parseModelDomain() {
1145
1082
  tiers: {},
1146
1083
  projects: {},
1147
1084
  roles,
1148
- atModelAllowlist: [],
1085
+ atMention: DEFAULT_AT_MENTION,
1149
1086
  cascadeLadder: csv("MODEL_CASCADE_LADDER"),
1150
1087
  degrade: process.env.MODEL_DEGRADE_TO
1151
1088
  ? {
@@ -2108,7 +2045,7 @@ function parseLimitsHttpDomain() {
2108
2045
  toolResultTtlSec: (() => {
2109
2046
  const ttl = numEnv("TOOL_RESULT_TTL_SEC", "86400");
2110
2047
  if (ttl <= 0) {
2111
- throw new Error(`env TOOL_RESULT_TTL_SEC=${ttl} must be > 0 — it is the age (seconds) after which an offloaded tool result is reaped, so 0 or less puts the cutoff at or after now and every reaper tick would delete ALL of them; there is no "off": set a larger value to keep results longer (the config center refuses the same value)`);
2048
+ throw new Error(`env TOOL_RESULT_TTL_SEC=${ttl} must be > 0 — it is the age (seconds) after which an offloaded tool result is reaped, so 0 or less puts the cutoff at or after now and every reaper tick would delete ALL of them; there is no "off": set a larger value to keep results longer (the web-admin refuses the same value)`);
2112
2049
  }
2113
2050
  return ttl;
2114
2051
  })(),
@@ -2165,13 +2102,13 @@ function parseGitApiKind(raw, baseUrl) {
2165
2102
  }
2166
2103
  function parseIntegrationsDomain(ctx) {
2167
2104
  const { requirePrincipal, configProvider } = ctx;
2168
- const registryUrl = parseCenterBaseUrl("SEMA_REGISTRY_URL", process.env.SEMA_REGISTRY_URL, `It is the center base every config pull, fleet and lease request is sent to; setting it selects the center as the config source.`);
2169
- const registryLiveUrl = parseCenterBaseUrl("SEMA_REGISTRY_LIVE_URL", process.env.SEMA_REGISTRY_LIVE_URL, `It is the center base the fleet liveness endpoints (heartbeat, serving lease) are sent to; unset it to send them to SEMA_REGISTRY_URL.`);
2170
- const registryWebUrl = parseCenterBaseUrl("SEMA_REGISTRY_WEB_URL", process.env.SEMA_REGISTRY_WEB_URL, `It is the center's web console base this engine reports on GET /health (center.webBaseUrl); unset it if the center has no web console to link to.`);
2171
- const configCenter = registryUrl !== undefined ? parseConfigCenter(registryUrl, requirePrincipal, registryLiveUrl, registryWebUrl) : undefined;
2105
+ const baseUrl = parseCenterBaseUrl(WEB_ADMIN_ENV.url, process.env[WEB_ADMIN_ENV.url], `It is the center base every config pull, fleet and lease request is sent to; setting it selects the center as the config source.`);
2106
+ const liveBaseUrl = parseCenterBaseUrl(WEB_ADMIN_ENV.liveUrl, process.env[WEB_ADMIN_ENV.liveUrl], `It is the center base the fleet liveness endpoints (heartbeat, serving lease) are sent to; unset it to send them to ${WEB_ADMIN_ENV.url}.`);
2107
+ const webBaseUrl = parseCenterBaseUrl(WEB_ADMIN_ENV.webUrl, process.env[WEB_ADMIN_ENV.webUrl], `It is the center's web console base this engine reports on GET /health (center.webBaseUrl); unset it if the center has no web console to link to.`);
2108
+ const webAdmin = baseUrl !== undefined ? parseWebAdmin(baseUrl, requirePrincipal, liveBaseUrl, webBaseUrl) : undefined;
2172
2109
  const fleetGeneration = parseFleetGeneration(process.env.FLEET_GENERATION);
2173
2110
  const fleetAdvertiseAddress = parseFleetAdvertiseAddress(process.env.FLEET_ADVERTISE_ADDRESS);
2174
- fleetFenceLaneOf({ ...fleetGeneration, configCenter, ...fleetAdvertiseAddress });
2111
+ fleetFenceLaneOf({ ...fleetGeneration, webAdmin, ...fleetAdvertiseAddress });
2175
2112
  return {
2176
2113
  pluginsAllowHosts: csvOf(process.env.PLUGINS_ALLOW_HOSTS ?? "github.com"),
2177
2114
  configBootFetchBudgetMs: clampEnvWithWarn("CONFIG_BOOT_FETCH_BUDGET_MS", "1500", 0),
@@ -2181,7 +2118,7 @@ function parseIntegrationsDomain(ctx) {
2181
2118
  defaultScenario: env("DEFAULT_SCENARIO", "code"),
2182
2119
  skillsDir: env("SKILLS_DIR", defaultSkillsDir()),
2183
2120
  centerCredentialRenewLeadMs: parseCenterCredentialRenewLeadMs(process.env.CENTER_CREDENTIAL_RENEW_LEAD_MS),
2184
- configCenter,
2121
+ webAdmin,
2185
2122
  fleetLeaseFailClosed: parseFleetLeaseFailClosed(process.env.FLEET_LEASE_FAIL_CLOSED, requirePrincipal),
2186
2123
  ...fleetGeneration,
2187
2124
  fleetScope: parseFleetScope(process.env.FLEET_SCOPE),
@@ -2239,9 +2176,9 @@ export function fleetFenceLaneOf(cfg) {
2239
2176
  return undefined;
2240
2177
  const laneConfigured = centerLaneConfigured(cfg);
2241
2178
  if (laneConfigured && cfg.fleetAdvertiseAddress !== undefined)
2242
- return { center: cfg.configCenter, generation };
2179
+ return { center: cfg.webAdmin, generation };
2243
2180
  const missing = [
2244
- ...(laneConfigured ? [] : [`the center lane (SEMA_REGISTRY_URL + ${CENTER_CREDENTIAL_ENV.token} or ${CENTER_CREDENTIAL_ENV.tokenFile} + SEMA_REGISTRY_WORKER)`]),
2181
+ ...(laneConfigured ? [] : [`the center lane (${WEB_ADMIN_ENV.url} + ${WEB_ADMIN_ENV.token} or ${WEB_ADMIN_ENV.tokenFile} + ${WEB_ADMIN_ENV.worker})`]),
2245
2182
  ...(cfg.fleetAdvertiseAddress === undefined ? ["the announce address (FLEET_ADVERTISE_ADDRESS)"] : []),
2246
2183
  ];
2247
2184
  throw fleetEnvRefusal("member_unfenced", `fleet member cannot be fenced — env FLEET_GENERATION=${JSON.stringify(generation)} makes this engine a fleet member, ` +
@@ -2276,15 +2213,15 @@ function parseCenterBaseUrl(name, raw, role) {
2276
2213
  throw new Error(`${refusal}. ${role}`);
2277
2214
  return raw;
2278
2215
  }
2279
- function parseConfigCenter(baseUrl, requirePrincipal, liveBaseUrl, webBaseUrl) {
2280
- const worker = process.env.SEMA_REGISTRY_WORKER || undefined;
2216
+ function parseWebAdmin(baseUrl, requirePrincipal, liveBaseUrl, webBaseUrl) {
2217
+ const worker = process.env[WEB_ADMIN_ENV.worker] || undefined;
2281
2218
  const declared = readCenterCredentialDeclaration();
2282
2219
  return {
2283
2220
  baseUrl,
2284
2221
  ...(liveBaseUrl !== undefined ? { liveBaseUrl } : {}),
2285
2222
  ...(webBaseUrl !== undefined ? { webBaseUrl } : {}),
2286
2223
  credential: createCenterCredentialSource(declared.from === "file" ? { ...declared, multiTenant: requirePrincipal } : declared, worker),
2287
- dryRun: boolEnv("SEMA_REGISTRY_DRY_RUN", false),
2224
+ dryRun: boolEnv(WEB_ADMIN_ENV.dryRun, false),
2288
2225
  worker,
2289
2226
  };
2290
2227
  }
@@ -2371,7 +2308,7 @@ const STORE_GROUP_KEYS = [
2371
2308
  ];
2372
2309
  const MODEL_PLANE_GROUP_KEYS = [
2373
2310
  "gatewayBaseUrl", "gatewayApiKey", "gatewayFallbackUrls", "gatewayMaxRetries", "anthropic", "resilience", "model", "envModel", "models",
2374
- "modelApiKeyEnv", "modelApiKeys", "modelRefusals", "droppedCatalogEntries", "modelQuotaWeights", "tiers", "projects", "roles", "atModelAllowlist", "cascadeLadder", "degrade",
2311
+ "modelApiKeyEnv", "modelApiKeys", "modelRefusals", "droppedCatalogEntries", "modelQuotaWeights", "tiers", "projects", "roles", "atMention", "cascadeLadder", "degrade",
2375
2312
  ];
2376
2313
  const APPROVAL_GROUP_KEYS = [
2377
2314
  "approvalRequire", "approvalDeny", "approvalTimeoutSec", "approvalAutoBudget", "approvalNeverAuto", "approvalGrant",
@@ -2411,7 +2348,7 @@ const LIMITS_HTTP_GROUP_KEYS = [
2411
2348
  const OBSERVABILITY_GROUP_KEYS = ["metricsToken", "traceToken", "toolTrace", "traceThinking", "logLevel", "otel"];
2412
2349
  const INTEGRATIONS_GROUP_KEYS = [
2413
2350
  "pluginsAllowHosts", "mcpServers", "a2aPeers", "a2aServe", "configBootFetchBudgetMs", "gitApiBaseUrl", "gitApiToken", "gitApiKind", "defaultScenario",
2414
- "skillsDir", "centerCredentialRenewLeadMs", "configCenter", "fleetLeaseFailClosed", "fleetGeneration", "fleetScope", "fleetAdvertiseAddress", "fleetLeaseOutageMaxMs", "configProvider", "configLocalDir",
2351
+ "skillsDir", "centerCredentialRenewLeadMs", "webAdmin", "fleetLeaseFailClosed", "fleetGeneration", "fleetScope", "fleetAdvertiseAddress", "fleetLeaseOutageMaxMs", "configProvider", "configLocalDir",
2415
2352
  ];
2416
2353
  export const CONFIG_GROUP_KEYS = {
2417
2354
  store: STORE_GROUP_KEYS,
@@ -2473,6 +2410,10 @@ export function loadConfig() {
2473
2410
  const limitsHttp = parseLimitsHttpDomain();
2474
2411
  const observability = parseObservabilityDomain();
2475
2412
  const integrations = parseIntegrationsDomain({ requirePrincipal, configProvider });
2413
+ const centerIdentity = centerIdentityRefusal({ authBridgeIssuer: auth.authBridgeIssuer, webAdmin: integrations.webAdmin });
2414
+ if (centerIdentity !== undefined) {
2415
+ throw new Error(centerIdentity);
2416
+ }
2476
2417
  const { directApprovalDoor, durableApproval: dgDurable, approvalHmacKeys: dgHmacKeys } = approval;
2477
2418
  const { principalJwtPubkeys, principalJwtIss, principalJwtAud, operatorPrincipals: dgOperators, authTokens } = auth;
2478
2419
  const directDoorActive = directApprovalDoor && dgDurable && dgHmacKeys.length > 0 && principalJwtPubkeys.length > 0 && !!principalJwtIss && !!principalJwtAud;
@@ -2510,7 +2451,7 @@ export function loadConfig() {
2510
2451
  throw new Error(laneRequiredRefusal);
2511
2452
  }
2512
2453
  {
2513
- const routeGap = judgeModelRouteGap(process.env, { envLaneDispatchable: !isRosterGateArmed(integrations.configCenter) });
2454
+ const routeGap = judgeModelRouteGap(process.env, { envLaneDispatchable: !isRosterGateArmed(integrations.webAdmin) });
2514
2455
  if (routeGap.gap !== "none") {
2515
2456
  throw new Error(routeGap.reason);
2516
2457
  }
@@ -144,7 +144,7 @@ export declare function laneRequiredEnv(lane: RemoteExecProvider): readonly stri
144
144
  *
145
145
  * 🔴 病(S-1008,test d327-g6-engine-7112 OBS-4 / OBS-5;2026-10-03 亲核):修前**两份句子**。运行期真拒启的是 `config.ts` 手写 throw
146
146
  * (点名 `REQUIRE_EXECUTION_LANE` / `REMOTE_EXEC` + 修法,用户在 stderr 读到的就是它);注册表条目另写了一句短的
147
- * 「the deployment asked to refuse this state」,而它只在 `config-center/stage-limits.ts` 的全表校验里被调 —— 候选视图只装
147
+ * 「the deployment asked to refuse this state」,而它只在 `web-admin-client/stage-limits.ts` 的全表校验里被调 —— 候选视图只装
148
148
  * `BATCH1_LIMIT_KEYS` 的限额键,带不上这两键 ⇒ 对本条恒 `next === live`,**那一句没有任何读者**(test 六个拒启格 stderr 里零命中)。
149
149
  * 改一份另一份不跟。现形:boot 的 throw 与注册表条目的 `check` **都**调本函数 —— 一只判据、一句话,没有第二份文本可漂。
150
150
  *
@@ -21,7 +21,7 @@
21
21
  * · `worker` / `expectedGeneration`:缺席对缺席(非 fleet 成员没有代际,不带 `expectedGeneration` 的请求是单机形,不是通配)。
22
22
  * · `instanceId`:请求带了才比(它是引擎每进程自铸的身份,`/health` 上可读);「点名的是不是别的进程」与 run 行同一只判据
23
23
  * `runProvenElsewhere`。
24
- * 引擎自己的 worker / instanceId 读不出 ⇒ 带了这一项的请求不符(认不出自己就不认任何点名;没配 `SEMA_REGISTRY_WORKER` 的引擎因此
24
+ * 引擎自己的 worker / instanceId 读不出 ⇒ 带了这一项的请求不符(认不出自己就不认任何点名;没配 `SEMA_WEB_ADMIN_WORKER` 的引擎因此
25
25
  * 只能用停止信号排空)。
26
26
  * 5. **进入那一拍让出在飞**:本副本可抢占的在飞腿经 core `TaskSpec.preemptSignal` 让出(在下一个干净的回合边界耐久停驻,
27
27
  * `resource_limit` / `preempt`;不合格的腿上 core 静默不动,等到期限硬停 —— 既有 `crashed` 臂);活的审批卡按断连同路改路耐久停驻
@@ -10,7 +10,7 @@
10
10
  * `ready ∧ ¬draining ∧ leaseState ≠ denied` —— 后两项不是新判据:`draining` 本来就是就绪原因,而租约被拒**同一拍**经
11
11
  * 唯一的排空入口进入排空(`fleet/serving-lease.ts`),所以「被拒 ⇒ 排空 ⇒ 不就绪」是构造出来的,不靠第二只谓词。
12
12
  * `leaseState = expired` **不**进就绪(中心不可达不是本实例的病,见租约模块头注)。
13
- * 3. **重启**:`restart` = 已发布的重启信号;没有已发布信号而有一只**扣着**的候选(config-center 的保留候选状态机)且它确有被扣的片时,
13
+ * 3. **重启**:`restart` = 已发布的重启信号;没有已发布信号而有一只**扣着**的候选(web-admin 的保留候选状态机)且它确有被扣的片时,
14
14
  * 铸 `{reasons: 被扣的片, version, since, withheld: true}`(「要重启,但此刻换掉本实例会丢它手里的候选」)。已发布的信号恒胜(那几片
15
15
  * 是安全可重启的,扣着的片不阻止它们);被扣的片为空(只差发布重试)⇒ 不是重启事实,不铸(契约 `RestartSignal.reasons` 至少一词;
16
16
  * 那一形的面只有每拍一行运维 warn + 计数)。「扣着」在两出口上只有这一种读法(7.120.x 的 `/health.modelPlaneDeferred` 本版删除)。
@@ -54,7 +54,7 @@ export type ServingLeaseView = {
54
54
  export type FleetMembershipKeys = Pick<InstanceAnnouncement, "generation" | "leaseState" | "leaseUntil">;
55
55
  /** 非 fleet 成员 / 没装租约客户端时的读数(唯一拼法)。 */
56
56
  export declare const LEASE_NONE: ServingLeaseView;
57
- /** 扣着的候选(config-center 的 `planeDeferredNoHandoff` 投影):版本、起扣时刻、被扣的重启片。 */
57
+ /** 扣着的候选(web-admin 的 `planeDeferredNoHandoff` 投影):版本、起扣时刻、被扣的重启片。 */
58
58
  export interface RetainedCandidate {
59
59
  readonly version: number;
60
60
  readonly since: number;
@@ -62,7 +62,7 @@ export interface RetainedCandidate {
62
62
  }
63
63
  /**
64
64
  * 重启事实的**唯一**合成点(规则 3):已发布信号恒胜;否则扣着的候选有被扣的片 ⇒ `withheld:true` 的信号;否则缺席。
65
- * `build*` = 纯数据。泛型让 config-center 交进来的窄型(`EngineRestartSignal`,`reasons` 是片闭集)原样回来。
65
+ * `build*` = 纯数据。泛型让 web-admin 交进来的窄型(`EngineRestartSignal`,`reasons` 是片闭集)原样回来。
66
66
  */
67
67
  export declare function buildRestartSignal<R extends RestartSignal>(published: R | undefined, retained: (RetainedCandidate & {
68
68
  readonly blocked?: readonly R["reasons"][number][];
@@ -72,8 +72,8 @@ export declare const CONFIG_HANDOFF_STATES: readonly ["missing"];
72
72
  export type ConfigHandoffState = (typeof CONFIG_HANDOFF_STATES)[number];
73
73
  /**
74
74
  * `/health.configHandoff`(只在 `/health`,不上心跳)—— 一件**事故**:**在场 ⇔ 部署声明了耐久接力 ∧ 应用着的那一代没在下一次起服将用的身份的桶里确认耐久落盘**
75
- * (config-center 的接力账 {@link ConfigHandoffLedger}:最后确认耐久的那一代 + 写进的那只身份桶,对比应用着的那一代 + 此刻的凭证身份 —— 离线起服按此刻
76
- * 的身份选桶;部署形门在 config-center 的取值口,读判「handoff confirmed」的同一只谓词)。凭证换了人(换了身份)而新身份的桶里没有这一代 ⇒ 在场。覆盖 LKG 发布失败(含保留候选、启动期技能债)与部分应用(某组被整拒、其余组已热应用,LKG 停在上一个完整世代)两形。**部署形**不在这里报:
75
+ * (web-admin 的接力账 {@link ConfigHandoffLedger}:最后确认耐久的那一代 + 写进的那只身份桶,对比应用着的那一代 + 此刻的凭证身份 —— 离线起服按此刻
76
+ * 的身份选桶;部署形门在 web-admin 的取值口,读判「handoff confirmed」的同一只谓词)。凭证换了人(换了身份)而新身份的桶里没有这一代 ⇒ 在场。覆盖 LKG 发布失败(含保留候选、启动期技能债)与部分应用(某组被整拒、其余组已热应用,LKG 停在上一个完整世代)两形。**部署形**不在这里报:
77
77
  * 没有耐久声明(`CONFIG_LKG_DURABLE` 不真 / LKG 关)的部署「重启靠中心重拉」是配置时声明的事实 ⇒ 恒缺席;本地腿 / 没有配置管道 ⇒ 缺席。
78
78
  * 与 `restart.withheld` 两个事实两张脸(可同在):要不要重启归 `restart`,声明了的接力缺不缺归本键。
79
79
  * `version` = 应用着的那一代;`since` = 这一代开始没有确认接力的时刻(持续缺着时不刷新,确认即清)。
@@ -83,7 +83,7 @@ export interface ConfigHandoff {
83
83
  readonly version: number;
84
84
  readonly since: number;
85
85
  }
86
- /** 配置接力账(config-center 持有,`/health.configHandoff` 的唯一来源):应用着的那一代、最后一次确认耐久接力的那一代与它写进的身份桶、
86
+ /** 配置接力账(web-admin 持有,`/health.configHandoff` 的唯一来源):应用着的那一代、最后一次确认耐久接力的那一代与它写进的身份桶、
87
87
  * 下一次起服将用的身份(此刻的凭证身份 —— 离线起服选桶读的同一只)。 */
88
88
  export interface ConfigHandoffLedger {
89
89
  readonly applied?: number;
@@ -96,9 +96,9 @@ export interface ConfigHandoffLedger {
96
96
  /** 「接力缺着」的**唯一**判据(纯):应用过一代 ∧ 最后确认耐久的不是「这一代 ∧ 下一次起服将用的身份桶」。 */
97
97
  export declare function configHandoffMissing(ledger: ConfigHandoffLedger): boolean;
98
98
  /** `configHandoff` 的**唯一**合成点(纯):缺着 ⇒ `{ state: "missing", version: 应用着的那一代, since }`;否则缺席。`since` 由持账方锁存(发现缺着的那一拍)。
99
- * `build*` = 纯数据;config-center 的 `configHandoffState()` 是唯一调用方。 */
99
+ * `build*` = 纯数据;web-admin 的 `configHandoffState()` 是唯一调用方。 */
100
100
  export declare function buildConfigHandoff(ledger: ConfigHandoffLedger, since: number | undefined): ConfigHandoff | undefined;
101
- /** 世代账的一次读数(config-center 的 `configApplyState()` 交出的那一份里本模块读的五键;`/health` 扁平五键与嵌套形的两键读同一份)。 */
101
+ /** 世代账的一次读数(web-admin 的 `configApplyState()` 交出的那一份里本模块读的五键;`/health` 扁平五键与嵌套形的两键读同一份)。 */
102
102
  export interface ConfigLedgerReading {
103
103
  readonly configTargetVersion: number;
104
104
  readonly configAppliedVersion: number;
@@ -566,6 +566,7 @@ export declare function fleetRunPublisher(bus: FleetEventBus | undefined, run: {
566
566
  name?: string;
567
567
  toolName?: string;
568
568
  parentToolCallId?: string;
569
+ workflowRunId?: string;
569
570
  status?: string;
570
571
  seq?: number;
571
572
  }) => void;
@@ -576,6 +577,7 @@ export declare function fleetRunPublisher(bus: FleetEventBus | undefined, run: {
576
577
  name?: string;
577
578
  usage?: unknown;
578
579
  parentToolCallId?: string;
580
+ workflowRunId?: string;
579
581
  status?: string;
580
582
  seq?: number;
581
583
  }) => void;
@@ -639,13 +641,20 @@ export declare function fleetRunResiduals(result: {
639
641
  * session attribution → unscoped, back-compat visible). Names ride the same redactSecrets discipline.
640
642
  */
641
643
  export declare function fleetBackgroundChildPublisher(bus?: FleetEventBus, log?: (msg: string, fields: Record<string, unknown>) => void): (e: import("@sema-agent/core").BackgroundChildEvent) => void;
644
+ /**
645
+ * 🔴 **run 的短名 —— 一只折法、一个输入**:输入 = run 行的预览(`user-authored-text.ts` 的 `buildRunObjectivePreview` 的产物,run 行上存的就是它);
646
+ * fleet 行的 `name`(发布时由 objective 现算同一只预览)与 `GET /v1/assistant/tasks` 的 `name`(读 run 行上存的预览)读同一只,
647
+ * 同一个 run 两面同名。预览是 `""`(有记录、剥完没有用户正文)⇒ `(notification turn)`;否则单行折叠、超过 48 字截断带 `…`。预览已脱敏。
648
+ * 入参只收「有记录」的预览:列上的 NULL(没记录)不是通告轮,由读面自己按契约缺席,不进这只折法。
649
+ */
650
+ export declare function buildRunName(preview: string): string;
642
651
  /** BC-1 (clay-decided 2026-06-27): a top-level run's fleet-row `name` = a SHORT, single-line **objective preview**
643
652
  * (the readable "what is this task" label) — NOT the full objective dumped in, and NOT duplicated into `description`.
644
653
  * `description` is left to the publisher's LIVE-ACTIVITY update (`onEvent` tool_start → the current tool), so the row
645
654
  * shows `name`=objective + `description`=current activity (two DISTINCT things, killing the earlier "objective shown
646
- * twice" dup). Whitespace-collapsed + capped ≤48. (A subagent CHILD row's name = its agent-type — BC-2.)
647
- * SECRET-SAFE: redact secret shapes (redactSecrets) BEFORE truncating — a secret in the objective must not leak in
648
- * the row name. The durable objective is already redacted; the name preview was asymmetric (raw) until this. */
655
+ * twice" dup). (A subagent CHILD row's name = its agent-type — BC-2.)
656
+ * 一只折法({@link buildRunName}),输入 = 与 run 行同一只预览(用户写的那段 → 脱敏 → 截):`GET /v1/assistant/tasks`
657
+ * 读 run 行上存的预览、过同一只折法 ⇒ 同一个 run 两面同名。剥完没有用户正文 ⇒ `(notification turn)`;闭集外的 XML 头当普通正文标。 */
649
658
  export declare function fleetRunLabels(objective: string | undefined): {
650
659
  name: string;
651
660
  };
@@ -3,6 +3,7 @@ import { DURABLE_AGENT_HANDLE_RE, deriveAgentDisplayStatus, isDelegatedAgentTerm
3
3
  import { persistedPlaneOf } from "../terminal.js";
4
4
  import { recordFailOpen } from "../observability/fail-open.js";
5
5
  import { redactSecrets } from "../trace/redact.js";
6
+ import { buildRunObjectivePreview } from "../user-authored-text.js";
6
7
  export function isFleetAgentTerminalNotification(n) {
7
8
  return isDelegatedAgentTerminal(n);
8
9
  }
@@ -206,6 +207,7 @@ export function fleetRunPublisher(bus, run) {
206
207
  let settledLeg = false;
207
208
  const childId = (taskId) => `${run.runId} ${taskId}`;
208
209
  const tickStatus = (st) => (st === "completed" ? "completed" : st === "failed" ? "failed" : "running");
210
+ const workflowRunIdField = (ev) => (ev.workflowRunId ? { workflowRunId: ev.workflowRunId } : {});
209
211
  const toolUsesOf = (usage) => {
210
212
  const t = usage?.toolUses;
211
213
  return typeof t === "number" ? t : undefined;
@@ -269,7 +271,7 @@ export function fleetRunPublisher(bus, run) {
269
271
  childStartedAt.set(cid, Date.now());
270
272
  if (ev.parentToolCallId)
271
273
  childByToolCall.set(ev.parentToolCallId, cid);
272
- bus.publishTask({ id: cid, sourceLane: "run-leg", ...(childName !== undefined ? { name: childName, agentType: childName } : {}), parentId: run.runId, scope: run.scope, ...sessionField, status: tickStatus(ev.status), tokens: sumTokens(ev.usage), ...(typeof ev.seq === "number" ? { cycleSeq: ev.seq } : {}), ...(() => { const t = toolUsesOf(ev.usage); return t !== undefined ? { toolUses: t } : {}; })(), elapsedMs: Date.now() - childStartedAt.get(cid) });
274
+ bus.publishTask({ id: cid, sourceLane: "run-leg", ...(childName !== undefined ? { name: childName, agentType: childName } : {}), parentId: run.runId, scope: run.scope, ...sessionField, ...workflowRunIdField(ev), status: tickStatus(ev.status), tokens: sumTokens(ev.usage), ...(typeof ev.seq === "number" ? { cycleSeq: ev.seq } : {}), ...(() => { const t = toolUsesOf(ev.usage); return t !== undefined ? { toolUses: t } : {}; })(), elapsedMs: Date.now() - childStartedAt.get(cid) });
273
275
  }
274
276
  else if (ev.type === "tool_start" && ev.toolName) {
275
277
  bus.publishTask({ id: run.runId, description: ev.toolName });
@@ -290,7 +292,7 @@ export function fleetRunPublisher(bus, run) {
290
292
  childStartedAt.set(cid, Date.now());
291
293
  if (ev.parentToolCallId)
292
294
  childByToolCall.set(ev.parentToolCallId, cid);
293
- bus.publishTask({ id: cid, sourceLane: "run-leg", ...(fwdName !== undefined ? { name: fwdName, agentType: fwdName } : {}), parentId, scope: run.scope, ...sessionField, status: tickStatus(ev.status), tokens: sumTokens(ev.usage), ...(typeof ev.seq === "number" ? { cycleSeq: ev.seq } : {}), ...(() => { const t = toolUsesOf(ev.usage); return t !== undefined ? { toolUses: t } : {}; })(), elapsedMs: Date.now() - childStartedAt.get(cid) });
295
+ bus.publishTask({ id: cid, sourceLane: "run-leg", ...(fwdName !== undefined ? { name: fwdName, agentType: fwdName } : {}), parentId, scope: run.scope, ...sessionField, ...workflowRunIdField(ev), status: tickStatus(ev.status), tokens: sumTokens(ev.usage), ...(typeof ev.seq === "number" ? { cycleSeq: ev.seq } : {}), ...(() => { const t = toolUsesOf(ev.usage); return t !== undefined ? { toolUses: t } : {}; })(), elapsedMs: Date.now() - childStartedAt.get(cid) });
294
296
  },
295
297
  onChildTerminal(taskId, status, altId, toolUseId) {
296
298
  if (settledLeg)
@@ -612,14 +614,16 @@ export function fleetBackgroundChildPublisher(bus, log) {
612
614
  handleTerminal(e, m);
613
615
  };
614
616
  }
617
+ const NOTIFICATION_TURN_NAME = "(notification turn)";
618
+ const RUN_NAME_CHARS = 48;
619
+ export function buildRunName(preview) {
620
+ const oneLine = preview.replace(/\s+/g, " ").trim();
621
+ if (oneLine === "")
622
+ return NOTIFICATION_TURN_NAME;
623
+ return oneLine.length > RUN_NAME_CHARS ? `${oneLine.slice(0, RUN_NAME_CHARS - 1)}…` : oneLine;
624
+ }
615
625
  export function fleetRunLabels(objective) {
616
- const oneLine = redactSecrets((objective ?? "").replace(/\s+/g, " ")).trim();
617
- if (!oneLine)
618
- return { name: "task" };
619
- if (/^<[a-z][a-z0-9_-]*[\s>/]/i.test(oneLine))
620
- return { name: "(notification turn)" };
621
- const name = oneLine.length > 48 ? `${oneLine.slice(0, 47)}…` : oneLine;
622
- return { name };
626
+ return { name: buildRunName(buildRunObjectivePreview(objective ?? "")) };
623
627
  }
624
628
  export function isTerminalFleetStatus(status) {
625
629
  switch (status) {
@@ -1,5 +1,5 @@
1
- import { type CenterCapabilities } from "@sema-agent/settings-schema/fleet";
2
- import { type CenterEndpoint } from "../config-center/center-request.js";
1
+ import { type WebAdminCapabilities } from "@sema-agent/settings-schema/fleet";
2
+ import { type CenterEndpoint } from "../web-admin-client/center-request.js";
3
3
  import { type DrainEntry } from "./drain-entry.js";
4
4
  import type { ServingLeaseView } from "./engine-signal.js";
5
5
  /** 续租请求的上界(ms)—— 与 fleet 心跳同量级(不在提交热路径上)。 */
@@ -34,7 +34,7 @@ export interface ServingLeaseClientOpts {
34
34
  }
35
35
  export interface ServingLeaseClient {
36
36
  /** 一次中心能力读数(`undefined` = `undeclared`,中心没宣告 = 不支持)。每一次读数调一次;`unreadable` 不是读数,不调(`fleet-client.ts` `capabilityReadingOf`)。 */
37
- noteCenterCapabilities(caps: CenterCapabilities | undefined): void;
37
+ noteCenterCapabilities(caps: WebAdminCapabilities | undefined): void;
38
38
  /** 此刻的租约读数(铸点与准入判读它)。 */
39
39
  view(): ServingLeaseView;
40
40
  /** 起步读黏性拒(装配层在唯一排空入口装好之后调一次)。 */
@@ -1,7 +1,7 @@
1
1
  import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { SERVING_LEASE_TTL_DEFAULT_MS, readServingLeaseResponse } from "@sema-agent/settings-schema/fleet";
4
- import { centerFetch } from "../config-center/center-request.js";
4
+ import { centerFetch } from "../web-admin-client/center-request.js";
5
5
  import { recordFailOpen } from "../observability/fail-open.js";
6
6
  import { leaseDrainActionId } from "./drain-entry.js";
7
7
  export const SERVING_LEASE_FETCH_TIMEOUT_MS = 5_000;