@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,6 +1,6 @@
1
1
  /**
2
2
  * Fleet worker-side wiring(announce/heartbeat + usage 批报;契约 = registry-core 0.1.48+
3
- * `/fleet`,center 端点 = sema-registry `/api/fleet/*`)。设计要点:
3
+ * `/fleet`,center 端点 = web-admin `/api/fleet/*`)。设计要点:
4
4
  * - **可选分层(D3),两条环各自有门(S-866 ④)**:客户端在中心车道配齐时启动(`centerLaneConfigured` —— baseUrl + 声明了
5
5
  * 凭证 + worker,与租约管理器、用量累加器**同一只**谓词);**用量环**只要这三件;**公告环**(announce / 心跳 / 注销)另要
6
6
  * 配置席 `fleetAdvertiseAddress`(env `FLEET_ADVERTISE_ADDRESS`,`loadConfig` 解析一次;可达性是运维的话,不自猜:k8s pod IP /
@@ -13,13 +13,13 @@
13
13
  * - **`engineSignal`(7.123.0)**:引擎态整只(契约 `EngineSignal`),与扁平键同一次读数;契约包 10.x 的心跳形没有它的座位(10.x 中心
14
14
  * 默认 strip 解析丢掉它、照收心跳);扁平布尔 `draining` settings-schema 12.0.0 删、本引擎 7.124.0+ 删发。
15
15
  * - **`restart?`(S-741)**:在场 ⇔ 本副本必须重启才能应用配置 —— 取自 `/health.restart` **同一只**取值口(装配层接
16
- * `deployment.restartState`,config-center 的 `restartState()`),同一个对象,形 = settings-schema `RestartSignal`;
16
+ * `deployment.restartState`,web-admin 的 `restartState()`),同一个对象,形 = settings-schema `RestartSignal`;
17
17
  * 缺席即键缺席(中心按行整体替换,不合并 —— 一条不带它的心跳就是「不再需要」)。
18
18
  * - **`modelRefusals?`(S-768)**:在场 ⇔ 本副本对最近一次被判的中心候选目录有可报的拒(逐条名 → 码 / 模型面组拒码)—— 取自
19
19
  * `/health.modelRefusals` **同一只**取值口(装配层接 `deployment.modelRefusalsState`),同一个对象,形 = settings-schema
20
20
  * `ModelRefusalsSignal`;缺席即键缺席(同 `restart`)。
21
21
  * - **自报状态翻转即刻再 announce**(第一时间自摘流 / 自挂流 / 报重启 / 报拒因,比任何观测都快):draining 翻转由收尾链直调
22
- * `announceNow`;roster 落地、restart 信号与模型拒因变化由装配层把 config-center 的 `onSelfReportChange` 接到 `announceNow`
22
+ * `announceNow`;roster 落地、restart 信号与模型拒因变化由装配层把 web-admin 的 `onSelfReportChange` 接到 `announceNow`
23
23
  * (两处 = 两类输入各自的唯一写者,触发都是 `announceNow`)。退出前 DELETE(显式注销,幂等)。
24
24
  * - **usage 批报**:tracer 的 `brain.call` 已带 ALS principal —— FleetUsageAccumulator 按 principal×model
25
25
  * 累计,每窗口(默认 60s,半开 [windowStartMs, windowEndMs))POST /api/fleet/usage。幂等键
@@ -27,9 +27,9 @@
27
27
  * 向前合并不会双计 —— 只延迟,不丢量)。
28
28
  * - 全部 fire-and-forget + swallow-guarded:fleet 面是运维旁路,绝不影响任务面;网络错误只 warn 一次/连败类。
29
29
  */
30
- import { UsageReport, type CapabilityReading, type CenterCapabilities, type ModelRefusalsSignal } from "@sema-agent/settings-schema/fleet";
30
+ import { UsageReport, type CapabilityReading, type WebAdminCapabilities, type ModelRefusalsSignal } from "@sema-agent/settings-schema/fleet";
31
31
  import { type EngineSignalFacts } from "./fleet/engine-signal.js";
32
- import { type CenterEndpoint } from "./config-center/center-request.js";
32
+ import { type CenterEndpoint } from "./web-admin-client/center-request.js";
33
33
  /** Bound on distinct principal×model keys per window — a runaway/multi-tenant burst collapses into
34
34
  * `__other__` instead of growing unbounded (same posture as budget.ts cardinalityGuard). */
35
35
  export declare const FLEET_USAGE_KEY_CAP = 1024;
@@ -91,7 +91,7 @@ export interface FleetClientOpts {
91
91
  /** 编排器片 2:每一次**能力读数**交出去一次(装配层接服务租约客户端的 `noteCenterCapabilities`)。规则见 {@link capabilityReadingOf}:
92
92
  * 只有 `declared`(交出宣告的位)与 `undeclared`(交 `undefined` = 老中心没宣告)是读数;`unreadable`(体读不出 / 不是对象 /
93
93
  * `capabilities` 键在场而形坏)不是读数 ⇒ 不调,上一次读数与它的钟原样。失败的公告同样不调(没听到 ≠ 没有)。 */
94
- onCenterCapabilities?: (caps: CenterCapabilities | undefined) => void;
94
+ onCenterCapabilities?: (caps: WebAdminCapabilities | undefined) => void;
95
95
  usage?: FleetUsageAccumulator;
96
96
  heartbeatMs?: number;
97
97
  usageWindowMs?: number;
@@ -148,7 +148,7 @@ export declare function startFleetClient(opts: FleetClientOpts): FleetClient;
148
148
  * deployments); an address set while only the worker is missing is warned (half-configured announce).
149
149
  */
150
150
  export declare function startFleetClientFromConfig(cfg: {
151
- configCenter?: (CenterEndpoint & {
151
+ webAdmin?: (CenterEndpoint & {
152
152
  worker?: string;
153
153
  }) | undefined;
154
154
  fleetAdvertiseAddress?: string | undefined;
@@ -1,7 +1,8 @@
1
1
  import { InstanceAnnouncement, UsageReport, DEFAULT_HEARTBEAT_MS, readCapabilityReading } from "@sema-agent/settings-schema/fleet";
2
2
  import { ENGINE_SIGNAL_KEY, buildEngineSignal, fleetMembershipKeys, heartbeatReadinessKeys } from "./fleet/engine-signal.js";
3
- import { centerFetch, centerLaneConfigured } from "./config-center/center-request.js";
3
+ import { centerFetch, centerLaneConfigured } from "./web-admin-client/center-request.js";
4
4
  import { recordFailOpen } from "./observability/fail-open.js";
5
+ import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
5
6
  export const FLEET_USAGE_KEY_CAP = 1024;
6
7
  export const FLEET_CLIENT_FETCH_TIMEOUT_MS = 5000;
7
8
  export class FleetUsageAccumulator {
@@ -230,14 +231,14 @@ export function startFleetClient(opts) {
230
231
  return { announceNow, stopRouting, flushFinalUsage };
231
232
  }
232
233
  export function startFleetClientFromConfig(cfg, opts) {
233
- const cc = cfg.configCenter;
234
+ const cc = cfg.webAdmin;
234
235
  const address = cfg.fleetAdvertiseAddress;
235
236
  if (!centerLaneConfigured(cfg)) {
236
237
  if (address !== undefined && cc?.baseUrl && cc.credential.declared) {
237
- opts.logger?.warn("fleet_client_no_worker_scope", { note: "FLEET_ADVERTISE_ADDRESS set but SEMA_REGISTRY_WORKER missing — announce needs the worker (dispatch key); fleet client not started" });
238
+ opts.logger?.warn("fleet_client_no_worker_scope", { note: `FLEET_ADVERTISE_ADDRESS set but ${WEB_ADMIN_ENV.worker} missing — announce needs the worker (dispatch key); fleet client not started` });
238
239
  }
239
240
  return undefined;
240
241
  }
241
- return startFleetClient({ ...opts, center: cfg.configCenter, advertiseAddress: address });
242
+ return startFleetClient({ ...opts, center: cfg.webAdmin, advertiseAddress: address });
242
243
  }
243
244
  //# sourceMappingURL=fleet-client.js.map
@@ -1,4 +1,4 @@
1
- import { type CenterEndpoint } from "./config-center/center-request.js";
1
+ import { type CenterEndpoint } from "./web-admin-client/center-request.js";
2
2
  /** 负缓存 TTL:非 lease-enforced 的 principal 多久后再问一次 center(entitlement 升级为 lease 的收敛上界)。 */
3
3
  export declare const NOT_LEASE_TTL_MS: number;
4
4
  /** center 判 budget exhausted 后,多久内的重复提交直接拒绝而不再打 center(防 per-request 打爆)。 */
@@ -141,11 +141,11 @@ export declare class FleetLeaseManager {
141
141
  /**
142
142
  * Env/config gate:中心车道配齐({@link centerLaneConfigured} —— 与 fleet 用量环 / 用量累加器**同一只**谓词,S-866 ④)即启;
143
143
  * 不需要 FLEET_ADVERTISE_ADDRESS(它是出站消费面,不是入站可达性声明)。缺任一件 → undefined,非 fleet 部署零行为变化。
144
- * S-651:按**声明**装、不按内容装 —— `SEMA_REGISTRY_TOKEN_FILE`(`CENTER_CREDENTIAL_ENV.tokenFile`)在场而
144
+ * S-651:按**声明**装、不按内容装 —— `SEMA_WEB_ADMIN_TOKEN_FILE`(`WEB_ADMIN_ENV.tokenFile`)在场而
145
145
  * 文件此刻不可读,管理器照样装上(每次协商取值,失败走响亮的鉴权失败臂;文件恢复即恢复)。
146
146
  */
147
147
  export declare function createFleetLeaseFromEnv(cfg: {
148
- configCenter?: (CenterEndpoint & {
148
+ webAdmin?: (CenterEndpoint & {
149
149
  worker?: string;
150
150
  }) | undefined;
151
151
  fleetLeaseFailClosed: boolean;
@@ -1,6 +1,6 @@
1
1
  import { QuotaLease, leaseExhausted, shouldRenewLease } from "@sema-agent/settings-schema/fleet";
2
2
  import { CenterCredentialUnavailableError } from "./center-credential.js";
3
- import { centerFetch, centerLaneConfigured } from "./config-center/center-request.js";
3
+ import { centerFetch, centerLaneConfigured } from "./web-admin-client/center-request.js";
4
4
  export const NOT_LEASE_TTL_MS = 10 * 60 * 1000;
5
5
  export const EXHAUSTED_RETRY_MS = 30 * 1000;
6
6
  export const LEASE_PRINCIPAL_CAP = 4096;
@@ -174,7 +174,7 @@ export class FleetLeaseManager {
174
174
  export function createFleetLeaseFromEnv(cfg, opts) {
175
175
  if (!centerLaneConfigured(cfg))
176
176
  return undefined;
177
- const cc = cfg.configCenter;
177
+ const cc = cfg.webAdmin;
178
178
  return new FleetLeaseManager({ ...opts, center: { baseUrl: cc.baseUrl, credential: cc.credential, worker: cc.worker }, failMode: cfg.fleetLeaseFailClosed ? "closed" : "open" });
179
179
  }
180
180
  //# sourceMappingURL=fleet-lease.js.map
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * ## 时刻
20
20
  * env 在**构造时**取一次快照(boot / run-local 起腿时),而不是 core 默认那样每次 spawn 现取:`NodeLspManagerOptions.env`
21
- * 是一份静态记录。本仓的 `process.env` 是 boot 期输入(热配置走配置中心,不改 `process.env`),两者在本仓部署上同值。
21
+ * 是一份静态记录。本仓的 `process.env` 是 boot 期输入(热配置走 web-admin,不改 `process.env`),两者在本仓部署上同值。
22
22
  *
23
23
  * ## 点名放行(S-699,`LSP_ENV_ALLOW`)
24
24
  * 剥除会连语言服务器自己的真实依赖一起剥掉(`.npmrc` 的 `${NPM_TOKEN}` 插值、rust-analyzer `cargo metadata` 私有 registry 的
@@ -30,11 +30,11 @@
30
30
  * (`PRINCIPAL_JWT_PUBKEYS/ISS/AUD` 任一在场;`AUTH_BRIDGE_ISSUER` 显式在场),[ref] ① 裁 (a) 撤回:
31
31
  * · **真假阳**:本机自起引擎、配了 principal-JWT / auth-bridge 签发方的单用户部署(web-admin 8090 真案)
32
32
  * 在 7.98.0 被判托管而拒启,只能滚回 7.93.5;
33
- * · **与回退 issuer 不同判**:auth-bridge 的 `?? configCenter.baseUrl` 回退(`boot/stage-09-leader.ts`)
33
+ * · **与回退 issuer 不同判**:auth-bridge 的 `?? webAdmin.baseUrl` 回退(`boot/stage-09-leader.ts`)
34
34
  * 从来不算证据,而回退桥的引擎同样把单用户授予开给每一个验签通过的 registry JWT 调用者 —— 同一个
35
35
  * 租户轴事实,「显式写了 issuer」与「从 center 推出 issuer」是同一形。裁定后两者同判。
36
36
  * ⇒ 签发方配置是**身份验签轴**(谁的 JWT 算数),不是租户轴(这台机器有几个主人)。「受控于控制面」
37
- * (`SEMA_REGISTRY_*`,含 worker 名)同理是 fleet 管理轴,本判据同样不读。
37
+ * (`SEMA_WEB_ADMIN_*`,含 worker 名)同理是 fleet 管理轴,本判据同样不读。
38
38
  *
39
39
  * 随两臂一起删掉的(按类删干净,规则集变小):S-564 腿①「托管 ∧ 未开 `REQUIRE_PRINCIPAL` ⇒ 拒启」
40
40
  * (判据只剩读 `requirePrincipal` 的那一臂之后,那条合取恒假 ⇒ 结构上不可达)及其跨键不变量行;
@@ -44,9 +44,9 @@
44
44
  *
45
45
  * 假阳 = 一台单用户部署被收走一个它一直在用的能力(7.98.0 时还会被拒启)。词表不收任何推断臂:
46
46
  * · ❌ `PRINCIPAL_JWT_*` / `AUTH_BRIDGE_ISSUER` 在场 —— 见上一段([ref] ① 撤回;加回来 = 翻案,要新裁定)。
47
- * · ❌ `configCenter`(含 `worker` 名)/ auth-bridge 的 center 回退 —— fleet 管理轴。初版(车LT)收过
47
+ * · ❌ `webAdmin`(含 `worker` 名)/ auth-bridge 的 center 回退 —— fleet 管理轴。初版(车LT)收过
48
48
  * `managed-worker` 臂,实测两个反例后撤回:`run-local.ts` 强设 `REQUIRE_PRINCIPAL=false` 的本机 CLI 在一个
49
- * 带 registry 键的共享 `.env` 上拒启;本仓自己的 `test/config-coverage-blocks.test.ts` configCenter 两格当场红。
49
+ * 带 registry 键的共享 `.env` 上拒启;本仓自己的 `test/config-coverage-blocks.test.ts` webAdmin 两格当场红。
50
50
  * · ❌ `operatorPrincipals` 非空 —— **真假阳**:`IMAGE_BAKES_ENABLED` 那条既有不变量
51
51
  * (`config-invariants.ts` 的 `bake-operator-principals`)**强制**开烤门的部署把它填非空。
52
52
  * · ❌ `SERVICE_AUTH_TOKENS` 条目数 —— 一台机器上同时给 cli 与 web-admin 发一只令牌是单用户常态。
@@ -1,5 +1,5 @@
1
1
  import { APPROVAL_REACH, MCP_IMAGE_MAX_BASE64, isApprovalReach, isPermissionMode, isThinkingLevel, expandTiers } from "@sema-agent/core";
2
- import { isModelAllowlisted, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../model-select.js";
2
+ import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../model-select.js";
3
3
  import { modelUnavailableMessage } from "../model-entry-refusal.js";
4
4
  import { HttpError } from "../security.js";
5
5
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, parseTaskSettings, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, stampEntryPermissionMode, taskHooksHonored, taskSettingsKeyIssue } from "../task-settings.js";
@@ -15,6 +15,7 @@ import { verifyRoundsFromBody } from "./verify-rounds.js";
15
15
  import { skillCarriesRetiredManifest } from "./retired-skill-manifest.js";
16
16
  import { resolveDevicePlacementIntent } from "./device-placement.js";
17
17
  import { sendError, httpErrorCode } from "./send.js";
18
+ import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
18
19
  const APPROVAL_REACH_WORDS = APPROVAL_REACH.map((word) => JSON.stringify(word)).join(" / ");
19
20
  export const MAX_SKILL_CONTENT_CHARS = 1_048_576;
20
21
  export const MAX_SYSTEM_PROMPT_CHARS = 16_384;
@@ -51,8 +52,7 @@ export const SKILL_MANIFEST_RETIRED = "skill.manifest was retired in core 7.41.0
51
52
  const MAX_IMAGES_PER_REQUEST = 20;
52
53
  const MAX_IMAGE_BASE64_BYTES = MCP_IMAGE_MAX_BASE64;
53
54
  function mentionableNames(config, catalog) {
54
- const allowlist = config.atModelAllowlist ?? [];
55
- return Object.keys(catalog).filter((n) => n !== "default" && isModelAllowlisted(n, catalog, allowlist, config.modelRefusals));
55
+ return Object.keys(catalog).filter((n) => n !== "default" && isAtMentionable(n, catalog, config.atMention, config.modelRefusals));
56
56
  }
57
57
  function refuseUnavailableModel(res, config, field, ref) {
58
58
  const hit = refusedCatalogEntry(ref, config.modelRefusals);
@@ -377,7 +377,7 @@ export function createAdmission(ctx) {
377
377
  const wantsSelfOrchestration = body.selfOrchestration === true
378
378
  || parseTaskSettings(body.settings).settings?.ultracode === true;
379
379
  if (!admission.available && wantsSelfOrchestration) {
380
- sendError(res, 501, "capability.self_orchestration_required", "self-orchestration is enabled on this worker (SELF_ORCHESTRATION_ENABLED) but it is multi-tenant (REQUIRE_PRINCIPAL) and wires NO per-principal entitlement source, so the engine's allowWorkflows gate — which is tighten-only, meaning an absent resolver leaves it UNGATED — is fail-closed for every principal: no request can ever get the workflow engine here (GET /v1/capabilities reports workflows:false with workflowsGate.denial \"entitlement_resolver_absent\"). Wire a config center (SEMA_REGISTRY_URL + token, not dry-run) to gate it per principal, or set SELF_ORCHESTRATION_ENABLED=false — the honest posture for a worker that cannot grant it. To submit this task without the workflow engine, drop selfOrchestration / settings.ultracode; ultracode's other half (the max thinking tier) is available as reasoningEffort: \"max\"");
380
+ sendError(res, 501, "capability.self_orchestration_required", `self-orchestration is enabled on this worker (SELF_ORCHESTRATION_ENABLED) but it is multi-tenant (REQUIRE_PRINCIPAL) and wires NO per-principal entitlement source, so the engine's allowWorkflows gate — which is tighten-only, meaning an absent resolver leaves it UNGATED — is fail-closed for every principal: no request can ever get the workflow engine here (GET /v1/capabilities reports workflows:false with workflowsGate.denial "entitlement_resolver_absent"). Wire a web-admin (${WEB_ADMIN_ENV.url} + token, not dry-run) to gate it per principal, or set SELF_ORCHESTRATION_ENABLED=false — the honest posture for a worker that cannot grant it. To submit this task without the workflow engine, drop selfOrchestration / settings.ultracode; ultracode's other half (the max thinking tier) is available as reasoningEffort: "max"`);
381
381
  return null;
382
382
  }
383
383
  }
@@ -77,6 +77,7 @@ export declare function buildCheckpointCtx(fields: {
77
77
  body: TaskRequestBody;
78
78
  memoryScope?: string | undefined;
79
79
  unattendedLeg?: true;
80
+ writerVersion: string;
80
81
  }): CheckpointCtx;
81
82
  /**
82
83
  * 模式词的**第二个写者** —— `plan_review` 的批准 / 编辑把这条 run 的体词从 `plan` 写成批准的档(`resume-legs.ts` `drivePlanReview`,调用点只有那一处;
@@ -130,10 +131,14 @@ export declare function allowListReadingRefusal(era: AllowListReadingEra): {
130
131
  retriable: true;
131
132
  };
132
133
  };
133
- /** 一条停驻行的持久化体在本版上**不能**重放时,是哪一条规则拒的(闭集;加一条 ⇒ {@link storedBodyReplayAnswer} 的 switch 编译红)。 */
134
+ /**
135
+ * 一条停驻行的持久化体在本版上**不能**重放时,是哪一条规则拒的(闭集;加一条 ⇒ {@link storedBodyReplayAnswer} 的 switch 编译红)。
136
+ * 答 `checkpoint.unsupported_version` 的两臂随带这一行的写者版本(续跑上下文的 `writerVersion`,缺席 = 本版之前写下的行)—— 那一码的两版本键由它铸({@link buildCheckpointVersionKeys})。
137
+ */
134
138
  export type StoredBodyReplayRefusal = {
135
139
  readonly kind: "allow_list_reading";
136
140
  readonly era: AllowListReadingEra;
141
+ readonly writerVersion?: string;
137
142
  } | {
138
143
  readonly kind: "skill_deny_inert";
139
144
  readonly entry: SkillDenyInertEntry;
@@ -141,7 +146,18 @@ export type StoredBodyReplayRefusal = {
141
146
  readonly kind: "permission_mode_reading";
142
147
  readonly stored: PermissionMode | undefined;
143
148
  readonly floor: PermissionMode;
149
+ readonly writerVersion?: string;
144
150
  };
151
+ /**
152
+ * 🔴 **`checkpoint.unsupported_version` 的 409 体上两个版本键 —— 唯一铸法;本码每一个铸点同一条律**(core 原码镜像 = `resume-legs.ts` 的 catch 臂;本服务自铸的两只拒
153
+ * = {@link storedBodyReplayAnswer} 的 allow 读法 / 模式词读法两臂):续跑上下文带写者版本 ⇒ `{ writerVersion, readerVersion }`;不带(本版之前写下的行)⇒ `{}`,不编值。
154
+ * 两值相等也照带 —— 那是事实(不是版本问题,出路照码与报文);写者比读者新才是「回到写它的那一版去决」。`readerVersion` 由调用方传本进程的服务版本
155
+ * (本模块零值依赖,不自取)。纯数据。
156
+ */
157
+ export declare function buildCheckpointVersionKeys(writerVersion: string | undefined, readerVersion: string): {
158
+ writerVersion: string;
159
+ readerVersion: string;
160
+ } | Record<string, never>;
145
161
  /** 续跑家族交回的拒答复形(`{ status, body }`,由 `sendResumeOutcome` / 各路由发送;`body` 至少 `error` + `errorCode`)。 */
146
162
  export interface ResumeRefusalAnswer {
147
163
  readonly status: number;
@@ -169,8 +185,9 @@ export declare function storedBodyReplayLogFields(refusal: StoredBodyReplayRefus
169
185
  note: string;
170
186
  };
171
187
  /**
172
- * `stored-body-replay.ts` `storedBodyReplayRefusal` 的答复:每条规则一只既有铸点(allow 记号 ⇒ {@link allowListReadingRefusal};技能拒侧 ⇒ {@link resumeBlockedByPolicyAnswer},与提交腿的 400
188
+ * `stored-body-replay.ts` `storedBodyReplayRefusal` 的答复:每条规则一只既有铸点(allow 记号 ⇒ {@link allowListReadingRefusal};模式词记号 ⇒ {@link permissionModeReadingRefusal};
189
+ * 这两只答 `checkpoint.unsupported_version`,体按那一码的键律补两版本键,`readerVersion` = 调用方传的本进程服务版本;技能拒侧 ⇒ {@link resumeBlockedByPolicyAnswer},与提交腿的 400
173
190
  * `request.field_invalid` 同码同句 —— 体上只有这一个问题时;存量体同时还踩中 `resolveSpec` 更早的拒〔三桶超帽 / 托管姿态〕时,提交侧答更早那条、续跑侧答本句,两边都是拒,指路不同)。
174
191
  */
175
- export declare function storedBodyReplayAnswer(refusal: StoredBodyReplayRefusal): ResumeRefusalAnswer;
192
+ export declare function storedBodyReplayAnswer(refusal: StoredBodyReplayRefusal, readerVersion: string): ResumeRefusalAnswer;
176
193
  //# sourceMappingURL=checkpoint-ctx.d.ts.map
@@ -14,6 +14,7 @@ export function bodyCarriesAllowList(body) {
14
14
  export function buildCheckpointCtx(fields) {
15
15
  return {
16
16
  body: fields.body,
17
+ writerVersion: fields.writerVersion,
17
18
  ...(fields.memoryScope !== undefined ? { memoryScope: fields.memoryScope } : {}),
18
19
  ...(fields.unattendedLeg === true ? { unattendedLeg: true } : {}),
19
20
  ...(bodyCarriesAllowList(fields.body) ? { permissionsAllowReading: "allow_layer" } : {}),
@@ -73,6 +74,9 @@ export function allowListReadingRefusal(era) {
73
74
  }
74
75
  }
75
76
  }
77
+ export function buildCheckpointVersionKeys(writerVersion, readerVersion) {
78
+ return writerVersion !== undefined ? { writerVersion, readerVersion } : {};
79
+ }
76
80
  export function resumeBlockedByPolicyAnswer(message, blockedBy, extra) {
77
81
  return {
78
82
  status: 409,
@@ -93,14 +97,18 @@ export function storedBodyReplayLogFields(refusal) {
93
97
  }
94
98
  }
95
99
  }
96
- export function storedBodyReplayAnswer(refusal) {
100
+ export function storedBodyReplayAnswer(refusal, readerVersion) {
97
101
  switch (refusal.kind) {
98
- case "allow_list_reading":
99
- return allowListReadingRefusal(refusal.era);
102
+ case "allow_list_reading": {
103
+ const answer = allowListReadingRefusal(refusal.era);
104
+ return { status: answer.status, body: { ...answer.body, ...buildCheckpointVersionKeys(refusal.writerVersion, readerVersion) } };
105
+ }
100
106
  case "skill_deny_inert":
101
107
  return resumeBlockedByPolicyAnswer(refusal.entry.message, "request.field_invalid");
102
- case "permission_mode_reading":
103
- return permissionModeReadingRefusal();
108
+ case "permission_mode_reading": {
109
+ const answer = permissionModeReadingRefusal();
110
+ return { status: answer.status, body: { ...answer.body, ...buildCheckpointVersionKeys(refusal.writerVersion, readerVersion) } };
111
+ }
104
112
  default: {
105
113
  const unhandled = refusal;
106
114
  throw new Error(`stored-body replay refusal ${JSON.stringify(unhandled)} has no answer`);
@@ -208,11 +208,11 @@ export function createDispatch(ctx) {
208
208
  res.end(deps.metrics.render());
209
209
  return;
210
210
  }
211
- if (deps.registryJwtVerifier) {
211
+ if (deps.webAdminJwtVerifier) {
212
212
  const authz = req.headers.authorization ?? "";
213
213
  const rawBearer = /^Bearer\s/i.test(authz) ? authz.replace(/^Bearer\s+/i, "") : "";
214
214
  if (rawBearer && looksLikeJwt(rawBearer)) {
215
- const v = await deps.registryJwtVerifier.verify(rawBearer);
215
+ const v = await deps.webAdminJwtVerifier.verify(rawBearer);
216
216
  if (v.ok) {
217
217
  setSsoPrincipal(req, v.identity.principal);
218
218
  req.headers[deps.config.principalHeader.toLowerCase()] = v.identity.principal;
@@ -9,6 +9,7 @@ import { commitSessionInheritEnv } from "./session-inherit-env.js";
9
9
  import { clearTurnActivity, readTurnActivityMs } from "../turn-activity.js";
10
10
  import { recordResourceWindow } from "../resource-window.js";
11
11
  import { buildCheckpointCtx } from "./checkpoint-ctx.js";
12
+ import { serviceVersion } from "../service-version.js";
12
13
  export async function mintAndDriveDurableRun(ctx, runStore, args) {
13
14
  const { deps } = ctx;
14
15
  const { inflightRuns, preemptableRuns, steerableRuns } = ctx.registry;
@@ -32,7 +33,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
32
33
  return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
33
34
  commitSessionInheritEnv(deps, args.prepared.spec.sessionId, args.prepared.body);
34
35
  if (deps.checkpointStore) {
35
- await deps.checkpointStore.putCtx(sessionId, buildCheckpointCtx({ body: args.prepared.body, memoryScope: args.prepared.auth?.memoryScope }));
36
+ await deps.checkpointStore.putCtx(sessionId, buildCheckpointCtx({ body: args.prepared.body, memoryScope: args.prepared.auth?.memoryScope, writerVersion: serviceVersion() }));
36
37
  }
37
38
  const anchorOwner = args.prepared.auth?.principal ?? null;
38
39
  const getLeafId = deps.sessionStorage?.getLeafId?.bind(deps.sessionStorage);
@@ -7,12 +7,12 @@
7
7
  * · `auth.required` = 全局凭据门的武装谓词(`hasServiceAuth`,由 `dispatch.ts` 交进来 —— 门与本位读同一次求值的同一只函数);
8
8
  * · `auth.kinds` = {@link IDENTITY_SOURCES}(`verifiedIdentitySource` 的回值闭集)里,这台部署的装配**能**回的那几个词:
9
9
  * `service_token` ⇐ 全局门武装(`systemFor` 只在配了 service token 时回得出值)· `sso` ⇐ 鉴权桥装了(入口预认证只在
10
- * `registryJwtVerifier` 在场时挂身份)· `direct_door_jwt` ⇐ 直连门开着(`verifiedIdentitySource` 那一臂的第一个合取项);
10
+ * `webAdminJwtVerifier` 在场时挂身份)· `direct_door_jwt` ⇐ 直连门开着(`verifiedIdentitySource` 那一臂的第一个合取项);
11
11
  * · `auth.identityAttach` = kinds 里有没有**带调用方身份**的一类({@link carriesCallerIdentity},穷举):`service_token` 认的是系统
12
12
  * (`systemFor` 回系统名或 null),不是人;
13
13
  * · `operator` = 能力事实 `operatorLane`(operator 面族的 501 门读的同一只;名单本身不上 wire);
14
14
  * · `center.issuer` = 鉴权桥 issuer 的唯一取值 `authBridgeIssuerOf`(装配 `boot/stage-09-leader.ts` 与 caps `centerWiring.issuer` 同读);
15
- * · `center.webBaseUrl` = 运维旋钮 `SEMA_REGISTRY_WEB_URL`(`config.configCenter.webBaseUrl`,接了中心才挂;起服时已判成固定基址);
15
+ * · `center.webBaseUrl` = 运维旋钮 `SEMA_WEB_ADMIN_WEB_URL`(`config.webAdmin.webBaseUrl`,接了中心才挂;起服时已判成固定基址);
16
16
  * · `center.sessionsVisible` = 中心的 web 面能不能列出本引擎的会话 —— 四件接线事实全在才可能:中心车道配齐(`centerLaneConfigured`,
17
17
  * 租约 / 用量 / 公告同一只谓词)∧ 公告地址在(配置席 `fleetAdvertiseAddress`:web 面只拨中心登记过的实例地址)∧ 鉴权桥在(web 面带的是
18
18
  * 中心铸的中继令牌,不带 service token)∧ 会话枚举面在(事实 `sessionList`,web 面中继 `GET /v1/sessions`);缺任一 ⇒ `false`。
@@ -22,7 +22,7 @@
22
22
  * ## 无鉴权面
23
23
  * `/health` 排在凭据门之前、缺省全网卡监听:本模块只出闭集词、布尔与运维要发布的 URL。两只 URL 值都过 display-safe 律
24
24
  * (`buildDisplaySafeUrl`,所有展示面 URL 的同一个铸点):userinfo 落地、query 值成记号、尾斜杠折掉 —— issuer 的来源
25
- * (`AUTH_BRIDGE_ISSUER` / `SEMA_REGISTRY_URL`)起服时不判凭据形,这一道是它上无鉴权面的唯一闸。令牌、名单、worker 名、凭证一概不上。
25
+ * (`AUTH_BRIDGE_ISSUER` / `SEMA_WEB_ADMIN_URL`)起服时不判凭据形,这一道是它上无鉴权面的唯一闸。令牌、名单、worker 名、凭证一概不上。
26
26
  * 同一张面上本模块之外还有一个**调用方自取**的串(如实,7.123.0 起;`model` / `dataRoot` / `version` 等其余非闭集的名是部署自己的事实,不是调用方放进来的):排空中的动作号 `engineSignal.draining.actionId`(`fleet/engine-signal.ts` 铸,契约 §0.4)——
27
27
  * 引擎自起的排空是引擎的闭集词,经排空口(operator-only)进的排空是调用方自取的幂等键**原样**(契约包今天只要求非空串,推荐 `manual:<uuid>`),
28
28
  * 所以那一键不是闭集词:调用方不要往里放秘密或人名。
@@ -59,5 +59,5 @@ export interface HealthSelfReport {
59
59
  * 三键的**唯一铸点**(`build*`:纯数据)。`credentialGateArmed` 由 `dispatch.ts` 交进来 —— 它是全局凭据门自己的那一句
60
60
  * (`hasServiceAuth`),本模块不另求一遍(另求就是第二个写者)。
61
61
  */
62
- export declare function buildHealthSelfReport(deps: Pick<FlatServiceDeps, "config" | "registryJwtVerifier" | "sessionStorage" | "runStore">, credentialGateArmed: boolean): HealthSelfReport;
62
+ export declare function buildHealthSelfReport(deps: Pick<FlatServiceDeps, "config" | "webAdminJwtVerifier" | "sessionStorage" | "runStore">, credentialGateArmed: boolean): HealthSelfReport;
63
63
  //# sourceMappingURL=health-self-report.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { IDENTITY_SOURCES } from "./principal-gate.js";
2
2
  import { operatorLane, sessionList } from "../capability-facts.js";
3
3
  import { authBridgeIssuerOf } from "../auth-bridge.js";
4
- import { centerLaneConfigured } from "../config-center/center-request.js";
4
+ import { centerLaneConfigured } from "../web-admin-client/center-request.js";
5
5
  import { buildDisplaySafeUrl } from "../trace/redact.js";
6
6
  export const IDENTITY_ATTACH_WORDS = ["available", "unavailable"];
7
7
  export const OPERATOR_PRESENCE_WORDS = ["present", "absent"];
@@ -35,10 +35,10 @@ function identitySourceWired(kind, a) {
35
35
  }
36
36
  export function buildHealthSelfReport(deps, credentialGateArmed) {
37
37
  const { config } = deps;
38
- const bridgeWired = deps.registryJwtVerifier !== undefined;
38
+ const bridgeWired = deps.webAdminJwtVerifier !== undefined;
39
39
  const kinds = IDENTITY_SOURCES.filter((k) => identitySourceWired(k, { credentialGateArmed, bridgeWired, directDoorActive: config.directDoorActive }));
40
40
  const issuer = authBridgeIssuerOf(config)?.issuer;
41
- const webBaseUrl = config.configCenter?.webBaseUrl;
41
+ const webBaseUrl = config.webAdmin?.webBaseUrl;
42
42
  const sessionsVisible = centerLaneConfigured(config) && config.fleetAdvertiseAddress !== undefined && bridgeWired && sessionList(deps).available ? (config.requirePrincipal ? true : "unknown") : false;
43
43
  return {
44
44
  auth: { required: credentialGateArmed, kinds, identityAttach: kinds.some(carriesCallerIdentity) ? "available" : "unavailable" },
@@ -14,7 +14,7 @@ import type { WiringManifestReceipt } from "./wire-types.js";
14
14
  * map grows past a threshold (no O(n) scan on the hot path). Single-threaded event-loop semantics: each HTTP
15
15
  * request is its own task, so there is no get→set race within a tick.
16
16
  *
17
- * (Caller context: the sema-registry review gateway mints/echoes the Idempotency-Key and routes a given key to
17
+ * (Caller context: the web-admin review gateway mints/echoes the Idempotency-Key and routes a given key to
18
18
  * the same worker, so this PER-POD cache is the worker-side backstop against a retried submission re-running —
19
19
  * and re-billing. Cross-pod / cross-restart exactly-once needs store-backing; the durable async /v1/runs path
20
20
  * is the alternative for long tasks.)
@@ -23,10 +23,11 @@ import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
23
23
  import type { createRequestGuards } from "./request-guards.js";
24
24
  import type { FlatServiceDeps } from "./server.js";
25
25
  /**
26
- * {@link CHECKPOINT_ERROR_DISPOSITION} 的读口(纯函数,resume 腿的 catch 臂与钉共用)。运行期遇到表外的码(装树 core 比本二进制多一个码的版本偏斜 ——
27
- * 型面上拼不出来)⇒ `"terminal"`:与 `by_reason` 认不得的词同一条 fail-closed 方向(不宽容默认成可重试),也与修前手列串的缺省逐字相同。
26
+ * 🔴 **一次 `CheckpointError` 拒的处置 —— 唯一读口**:{@link CHECKPOINT_ERROR_DISPOSITION} 的读法(doctrine 在那张表头)。resume 腿的 catch 臂只调它一次,
27
+ * 行的落点与 wire 的 `retriable` 都读这一个结果。纯函数,钉直接调。运行期遇到表外的码(装树 core 比本二进制多一个码的版本偏斜 —— 型面上拼不出来)
28
+ * ⇒ `"terminal"`:与 `by_reason` 认不得的词同一条 fail-closed 方向(不宽容默认成可重试),也与修前手列串的缺省逐字相同。
28
29
  */
29
- export declare function checkpointErrorDisposition(e: CheckpointError): "cancelled" | "retriable" | "terminal";
30
+ export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled" | "retriable" | "terminal";
30
31
  export declare function createResumeLegs(ctx: {
31
32
  deps: FlatServiceDeps;
32
33
  runnerFor: FlatServiceDeps["runnerFor"];
@@ -17,12 +17,15 @@ import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTermi
17
17
  import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
18
18
  import { APPROVAL_GATE_KINDS, createApprovalCardEmitter, isApprovalGateKind, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
19
19
  import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
20
+ import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
21
+ import { serviceVersion } from "../service-version.js";
20
22
  import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
21
23
  import { withPrincipal } from "../observability/principal-context.js";
22
24
  import { withModelReachScope } from "../model-reach-scope.js";
23
25
  import { createInstructionSelectionLeg } from "../instruction-selection.js";
24
26
  import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
25
27
  import { redactSecrets } from "../trace/redact.js";
28
+ import { buildRunObjectivePreview } from "../user-authored-text.js";
26
29
  import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
27
30
  import { registerEngineNoticeLeg } from "../trace/engine-notice-wire.js";
28
31
  import { reasoningDeltaBytes, reasoningSegmentEventId } from "../trace/model-segment.js";
@@ -43,7 +46,7 @@ import { commitSessionInheritEnv } from "./session-inherit-env.js";
43
46
  import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
44
47
  import { deploymentListAsks } from "../approval.js";
45
48
  import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
46
- import { resumeBlockedByPolicyAnswer, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
49
+ import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
47
50
  import { storedBodyReplayRefusal } from "./stored-body-replay.js";
48
51
  import { httpErrorCode } from "./send.js";
49
52
  function isCheckpointReopenedFailure(r) {
@@ -108,7 +111,7 @@ const CHECKPOINT_ERROR_DISPOSITION = {
108
111
  "steering.invalid_content": "terminal",
109
112
  "steering.queue_full": "terminal",
110
113
  };
111
- export function checkpointErrorDisposition(e) {
114
+ export function resumeRefusalDisposition(e) {
112
115
  const word = Object.hasOwn(CHECKPOINT_ERROR_DISPOSITION, e.code) ? CHECKPOINT_ERROR_DISPOSITION[e.code] : undefined;
113
116
  switch (word) {
114
117
  case "cancelled":
@@ -135,14 +138,26 @@ const CHECKPOINT_ERROR_FIELD_ROLE = {
135
138
  taskConfig: "config",
136
139
  inheritedGate: "config",
137
140
  };
138
- const ROLE_MINTS_RESUME_OUTCOME_INVALID = {
139
- binding: false,
140
- answer: false,
141
- outcome_cell: true,
142
- regrant: true,
143
- config: false,
141
+ const FIELD_ROLE_CAUSE = {
142
+ binding: APPROVAL_ENDING_CAUSES.BINDING_CHANGED,
143
+ answer: APPROVAL_ENDING_CAUSES.ANSWER_INVALID,
144
+ outcome_cell: APPROVAL_ENDING_CAUSES.OUTCOME_CELL_INVALID,
145
+ regrant: APPROVAL_ENDING_CAUSES.OUTCOME_CELL_INVALID,
146
+ config: undefined,
144
147
  };
145
- const NON_BINDING_INVALID_OUTCOME_FIELDS = new Set(Object.keys(CHECKPOINT_ERROR_FIELD_ROLE).filter((f) => ROLE_MINTS_RESUME_OUTCOME_INVALID[CHECKPOINT_ERROR_FIELD_ROLE[f]]));
148
+ const WIRE_CODE_OF_DECISION_REFUSAL = {
149
+ [APPROVAL_ENDING_CAUSES.BINDING_CHANGED]: "approval_binding_mismatch",
150
+ [APPROVAL_ENDING_CAUSES.ANSWER_INVALID]: "resume_outcome_invalid",
151
+ [APPROVAL_ENDING_CAUSES.OUTCOME_CELL_INVALID]: "resume_outcome_invalid",
152
+ };
153
+ const UNCLASSIFIED_INVALID_OUTCOME_CODE = WIRE_CODE_OF_DECISION_REFUSAL[APPROVAL_ENDING_CAUSES.BINDING_CHANGED];
154
+ function decisionRefusalCauseOf(e, gate) {
155
+ if (gate !== "policy_ask")
156
+ return APPROVAL_ENDING_CAUSES.OUTCOME_CELL_INVALID;
157
+ const field = e.detail?.field;
158
+ const role = field !== undefined && Object.hasOwn(CHECKPOINT_ERROR_FIELD_ROLE, field) ? CHECKPOINT_ERROR_FIELD_ROLE[field] : undefined;
159
+ return role === undefined ? undefined : FIELD_ROLE_CAUSE[role];
160
+ }
146
161
  function notResumableAnswer() {
147
162
  return { status: 409, body: { error: "run is not in a resumable (suspended) state (already resumed, decided, or expired)", errorCode: "conflict.not_resumable" } };
148
163
  }
@@ -420,7 +435,7 @@ export function createResumeLegs(ctx) {
420
435
  };
421
436
  const refuseStoredBodyReplay = (sessionId, refusal) => {
422
437
  deps.logger?.warn?.("resume_refused_stored_body", { sessionId, ...storedBodyReplayLogFields(refusal) });
423
- return storedBodyReplayAnswer(refusal);
438
+ return storedBodyReplayAnswer(refusal, serviceVersion());
424
439
  };
425
440
  const resolveResumeSpecOrBlocked = async (ctx, req, auth) => {
426
441
  const replay = storedBodyReplayRefusal(ctx, deps.config.autonomy);
@@ -460,7 +475,7 @@ export function createResumeLegs(ctx) {
460
475
  async function driveResumeLeg(args, onAccepted) {
461
476
  const resumeLegStartedAt = Date.now();
462
477
  const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx } = args;
463
- const commitResumeCtx = () => deps.checkpointStore.putCtx(resumeCtx.sessionId, resumeCtx.ctx).catch((e) => {
478
+ const commitResumeCtx = () => deps.checkpointStore.putCtx(resumeCtx.sessionId, { ...resumeCtx.ctx, writerVersion: serviceVersion() }).catch((e) => {
464
479
  deps.metrics?.inc("resume_ctx_not_persisted_total");
465
480
  deps.logger?.warn?.("resume_ctx_not_persisted", { sessionId: resumeCtx.sessionId, gate: outcome.gate, error: errorText(e), note: "this continuation runs with the body it was given; the durable rebuild input still holds the PREVIOUS body, so the NEXT continuation of this session rebuilds from that one (never wider than this leg's)" });
466
481
  });
@@ -494,7 +509,7 @@ export function createResumeLegs(ctx) {
494
509
  const freshId = uuidv7();
495
510
  const created = await deps.runStore.createRun(freshId, sessionId, principal ?? null, deps.instanceId ?? "default", {
496
511
  source: args.mintFreshRun.source,
497
- objectivePreview: resumeObjective ? redactSecrets(resumeObjective).slice(0, 120) : null,
512
+ objectivePreview: buildRunObjectivePreview(resumeObjective ?? ""),
498
513
  });
499
514
  if (!created.ok) {
500
515
  return { status: 409, body: await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, sessionId, created.activeTaskId) };
@@ -969,7 +984,8 @@ export function createResumeLegs(ctx) {
969
984
  }
970
985
  catch (e) {
971
986
  if (e instanceof CheckpointError) {
972
- const disposition = checkpointErrorDisposition(e);
987
+ const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, outcome.gate) : undefined;
988
+ const disposition = resumeRefusalDisposition(e);
973
989
  if (disposition === "cancelled") {
974
990
  const cs2 = deps.checkpointStore;
975
991
  if (cs2) {
@@ -1020,16 +1036,13 @@ export function createResumeLegs(ctx) {
1020
1036
  body: {
1021
1037
  ...(taskId !== undefined ? { taskId } : {}),
1022
1038
  error: e.message,
1023
- errorCode: e.code === "checkpoint.invalid_outcome"
1024
- ? (outcome.gate === "policy_ask" && !NON_BINDING_INVALID_OUTCOME_FIELDS.has(String(e.detail?.field ?? ""))
1025
- ? "approval_binding_mismatch"
1026
- : "resume_outcome_invalid")
1027
- : e.code,
1039
+ errorCode: e.code === "checkpoint.invalid_outcome" ? (refusalCause !== undefined ? WIRE_CODE_OF_DECISION_REFUSAL[refusalCause] : UNCLASSIFIED_INVALID_OUTCOME_CODE) : e.code,
1028
1040
  ...(e.detail?.field ? { field: e.detail.field } : {}),
1029
1041
  ...((e.code === "resume.usage_window_exhausted" || e.code === "resume.preflight_rejected") && typeof e.detail?.retryAfterMs === "number" && Number.isFinite(e.detail.retryAfterMs) && e.detail.retryAfterMs > 0
1030
1042
  ? { retryAfterSec: Math.max(1, Math.ceil(e.detail.retryAfterMs / 1000)) }
1031
1043
  : {}),
1032
1044
  ...(retriable ? { retriable: true } : {}),
1045
+ ...(e.code === "checkpoint.unsupported_version" ? buildCheckpointVersionKeys(resumeCtx.ctx.writerVersion, serviceVersion()) : {}),
1033
1046
  },
1034
1047
  };
1035
1048
  }
@@ -176,6 +176,14 @@ export interface CheckpointCtx {
176
176
  * 不改词的续跑腿写回传原 ctx ⇒ 记号随行。
177
177
  */
178
178
  permissionModeReading?: import("./checkpoint-ctx.js").PermissionModeReading;
179
+ /**
180
+ * 🔴 S-1365 —— **写这份上下文的服务版本**(`package.json` 的 `version`),**服务端写、不来自请求体**(与 {@link unattendedLeg} 同为 `body` 的兄弟键)。
181
+ * 写点两个:`buildCheckpointCtx`(每一次新的持久化写)与续跑腿的写回({@link DriveResumeArgs.resumeCtx} 的唯一写点,CAS 赢点盖本进程的版本 —— 接下来写下一张停车行的就是它)。
182
+ * 读者:`checkpoint.unsupported_version` 的 409 体 —— 原样带出 `writerVersion`,与本引擎的 `readerVersion` 并列(运维据此知道该回哪一版去决);
183
+ * 两键唯一铸法 `checkpoint-ctx.ts` `buildCheckpointVersionKeys`,本码每个铸点同调。它是**上下文**,不是拒因:成因与码都不读它。
184
+ * 缺席 = 本版之前写下的行(不回填):那一码的体不带两键,如实不猜。不是一次性迁移 —— 这一格是常设的事实位,没有删除条件。
185
+ */
186
+ writerVersion?: string;
179
187
  }
180
188
  /** B4①(命名化):`driveResumeIntoRunLog` 的入参形(resume 家族共用)——原为 9 成员内联匿名对象。 */
181
189
  export interface DriveResumeArgs {
@@ -7,7 +7,7 @@
7
7
  * 身份 → 授权 →(本口无 body,无验型步)。
8
8
  *
9
9
  * 🔴 语义 = **触发一次既有的 refresh 拍**,不是「再实现一遍拉取」:实现面是
10
- * `configCenter.refreshNow()`,它与 60s cadence 共用同一只 join 形拍(在飞则 join,绝不并发双拍)。
10
+ * `webAdmin.refreshNow()`,它与 60s cadence 共用同一只 join 形拍(在飞则 join,绝不并发双拍)。
11
11
  * 回执里的两个世代号取自**同一本**世代账(`/health` 的三键同源)—— 两面各算一次迟早会各说各话。
12
12
  *
13
13
  * 🔴 没有配置管道(纯 env 形)⇒ 诚实 409 指路,**不吞成 200**:一个「triggered:true 但什么都没发生」的
@@ -24,7 +24,7 @@ export async function handleAdminConfigRefresh(req, res, match, ctx) {
24
24
  const trigger = deps.configRefreshNow;
25
25
  const outcome = trigger ? await trigger() : "not-wired";
26
26
  if (outcome === "failed") {
27
- sendError(res, 502, "internal.config_refresh_failed", "the config refresh tick failed (source unreachable / unreadable / apply error) — the live config is unchanged; see the worker's sema_registry_refresh_failed log line for the cause");
27
+ sendError(res, 502, "internal.config_refresh_failed", "the config refresh tick failed (source unreachable / unreadable / apply error) — the live config is unchanged; see the worker's web_admin_refresh_failed log line for the cause");
28
28
  return;
29
29
  }
30
30
  if (outcome === "not-wired") {
@@ -6,7 +6,7 @@
6
6
  * 进入排空经 `drainState.enter`(收尾链装的唯一入口)。
7
7
  * · 门序 = operator 面的既有口径(`routes/retention-ops.ts` 逐字):身份 → 授权 → 验型;billable=false,**刻意不吃排空 503**
8
8
  * (排空中的重放必须能拿到 `already_draining` 回执,吃了就自锁)。
9
- * · 判序:体坏 ⇒ 400 `request.field_invalid`(零状态变化)→ 没有收尾链(嵌入宿主 / 测试夹具)⇒ 501 `capability.drain_state_required`
9
+ * · 判序:体坏(含动作号不是标识符形 —— settings-schema 11 `DRAIN_ACTION_ID`)⇒ 400 `request.field_invalid`(零状态变化)→ 没有收尾链(嵌入宿主 / 测试夹具)⇒ 501 `capability.drain_state_required`
10
10
  * → 身份不符 ⇒ 409 `conflict.fleet_identity`(零状态变化;拒句点名不符的字段,不回显值)→ 入口:已在排空 / 硬停已在进行 ⇒ 200 回执
11
11
  * (这一发的期限不会被采用);这一发会进入排空而 `deadlineAt` 离进入那一拍超过 `FLEET_LEASE_OUTAGE_MAX_MS`(`config.fleetLeaseOutageMaxMs`,
12
12
  * 本口随入参声明的准入上界,`fleet/drain-entry.ts` 规则 2)⇒ 入口答拒、本口 400 `request.field_invalid`(零状态变化;拒句点名字段、上界的