@sema-agent/server 7.43.0 → 7.44.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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,40 +1,9 @@
1
- /**
2
- * #322 —— **世代账**(design/322 §2 世代账,codex 设计审 F4)。
3
- *
4
- * 身份标 = 上游 `EffectiveConfig.version`,**刻意不造跨进程 epoch**(codex F4 语义不变):
5
- * · 同版本幂等重放(etag 未推进的下一拍)不该伪增世代;
6
- * · 双副本对账「是不是同一代」必须能跨进程判 —— 同 source version = 同一代。
7
- *
8
- * 🔴 #325(test [4809],[4810] 阻断裁定):**序轴与身份标分离**。source version 在
9
- * `CONFIG_PROVIDER=local` 下是 registry-core file-store 的内容哈希(u53,上游契约只承诺
10
- * 「同 files ⇒ 同 number」,从未承诺序)——把它当序数消费,哈希更小的合法新代会被永久
11
- * 静默忽略且本账双盲(target/applied 都不动)。修后:**推进判断吃收货处铸的进程内单调
12
- * ordinal**(config-center `generationOf`;等版本重放沿用同 ordinal=幂等语义保留),
13
- * `version` 只做对账身份标——configTargetVersion 语义从「max(见过)」变为「**最后**见过的
14
- * 源代」(remote 单调腿二者等价;local 腿这才是真相)。
15
- *
16
- * 读面两个:`/health` 的三键(target / applied / 失败持续时长;#325 起 additive 补
17
- * targetOrdinal/appliedOrdinal 两键=推进/停滞的机器判据)给编排器判「这个副本要不要摘」;
18
- * 诊断端点的 `configApply` 段给运维看**按组**的细节(哪一组停在哪一代、上一次为什么被拒)。
19
- *
20
- * 🔴 原子承诺的**域**是单进程(§2):本账记的是「本副本的收敛状态」,不是 fleet 的。fleet 级判据 = 各副本
21
- * /health 的 applied == target,拉齐工具归运维面。
22
- */
23
1
  export function createConfigApplyLedger(now = () => Date.now()) {
24
2
  let targetOrd = 0;
25
3
  let appliedOrd = 0;
26
4
  let targetVersion = 0;
27
5
  let appliedVersion = 0;
28
- /**
29
- * 🔴 codex R1-F3(真 finding,红先):**有组被拒的世代不算「本副本应用了这一代」**。
30
- *
31
- * applyEffective 对一个 limits 被整组拒的候选仍返回 true(其余面照常提交),生产腿随后无条件调
32
- * `applied(...)` —— 修前于是 `/health` 报 applied==target、stale 时长消失,而限额面其实停在旧代。
33
- * 世代账存在的全部理由就是让这种漂移可见,所以拒过的世代在这里被记下,`applied()` 拒绝为它收敛。
34
- * #325 起按 **ordinal** 记(local 腿同 version 可能隔代重现,序数才是「这一次候选」的唯一键)。
35
- */
36
6
  const rejectedOrds = new Set();
37
- /** 首次分歧的时刻 —— **持续**时长的锚(每拍刷新会把「卡了一小时」写成「卡了 0 秒」)。 */
38
7
  let divergedSince;
39
8
  const groups = {};
40
9
  const reconcile = () => {
@@ -53,10 +22,9 @@ export function createConfigApplyLedger(now = () => Date.now()) {
53
22
  },
54
23
  applied(ordinal, version) {
55
24
  if (ordinal > targetOrd) {
56
- targetOrd = ordinal; // 迟到 boot / LKG 腿:应用的那一代自然也是见过的
25
+ targetOrd = ordinal;
57
26
  targetVersion = version;
58
27
  }
59
- // 本世代有组被拒 ⇒ 只推进 target(诚实的「见过、没能完整应用」),applied 停在上一个完整世代。
60
28
  if (!rejectedOrds.has(ordinal) && ordinal > appliedOrd) {
61
29
  appliedOrd = ordinal;
62
30
  appliedVersion = version;
@@ -65,14 +33,14 @@ export function createConfigApplyLedger(now = () => Date.now()) {
65
33
  },
66
34
  recordApply(report) {
67
35
  const prev = groups.limitsHttp;
68
- const ord = report.ordinal ?? report.version; // 兼容臂:未传 ordinal 的单元调用退回 version 键
36
+ const ord = report.ordinal ?? report.version;
69
37
  if (report.limits.rejected)
70
38
  rejectedOrds.add(ord);
71
39
  else
72
- rejectedOrds.delete(ord); // 同一世代重放且这次过了(拒因已修)⇒ 允许收敛
40
+ rejectedOrds.delete(ord);
73
41
  if (report.limits.rejected) {
74
42
  groups.limitsHttp = {
75
- appliedVersion: prev?.appliedVersion ?? 0, // 被拒的世代不许推进组的 applied
43
+ appliedVersion: prev?.appliedVersion ?? 0,
76
44
  ...(prev?.deferredKeys ? { deferredKeys: prev.deferredKeys } : {}),
77
45
  lastRejected: { keys: [...report.limits.rejected.keys], reason: report.limits.rejected.reason, version: report.version, at: now() },
78
46
  ...(report.limits.rejected.restartReason
@@ -83,9 +51,8 @@ export function createConfigApplyLedger(now = () => Date.now()) {
83
51
  }
84
52
  groups.limitsHttp = {
85
53
  appliedVersion: report.version,
86
- // 逐键 defer(值合法但键不热):每代重算 —— 不再被发布的 defer 自然消失,不粘。
87
54
  ...(report.limits.deferred && report.limits.deferred.length > 0 ? { deferredKeys: report.limits.deferred.map((d) => ({ key: d.key, reason: d.reason })) } : {}),
88
- ...(prev?.lastRejected ? { lastRejected: prev.lastRejected } : {}), // 上一次拒因保留:修好之后仍要能查到发生过什么
55
+ ...(prev?.lastRejected ? { lastRejected: prev.lastRejected } : {}),
89
56
  };
90
57
  },
91
58
  view() {
@@ -1,44 +1,3 @@
1
- /**
2
- * Config-center adapter — pull the effective config from the config-center control plane and
3
- * apply it OVER the env-derived defaults (env = fallback, center = override). Follows the
4
- * "universal internal schema → translate at the boundary" pattern, with our security boundary kept:
5
- *
6
- * (name history: this module tracks the control plane's product name, which has changed over time
7
- * — registry → sema-web → planned sema-admin. The module itself keeps a neutral, product-name-free
8
- * identifier ("config-center") and does not chase the rename.)
9
- *
10
- * - the CENTER owns the LOGICAL config: the model roster (names/capabilities/tier), the role map,
11
- * and team templates;
12
- * - the SERVICE env still owns the SECRETS: API keys stay in env (the center never stores a secret);
13
- * keys are per-model via `apiKeyEnv` → `config.modelApiKeyEnv` → the spec's `getApiKeyAndHeaders`
14
- * (core 1.45), so each model/cascade-rung authenticates with its own upstream key.
15
- *
16
- * Per-model `baseUrl` IS transported (a catalog model may live on a different gateway;
17
- * core brain honors model.baseUrl, absent = "" = boot-env endpoint).
18
- *
19
- * Hot-reload status (复审 2026-07-29 P1-11 — 亲读判定,取代此处旧的 "models/roles are restart-to-apply"
20
- * TODO, which went stale when `mutateInPlace` landed):
21
- * - models/roles/roster/teams/projects/autonomy — **HOT**. `applyEffective` mutates `config.models`/
22
- * `config.roles` IN PLACE, and core's Runner reads `this.deps.models/roles` per task off that very
23
- * reference. Both brains re-resolve `model.baseUrl || config.baseUrl` inside `buildRequest()` on every
24
- * call (core 2.1.0 `brain/openai.js` + `brain/anthropic.js`), so a moved gateway takes effect on the
25
- * next task with no restart.
26
- * - the same plane under a **tier-frozen** Runner — deferred, not hot: core expands a PRIVATE catalog copy
27
- * at construction, so `main` defers the whole plane and the `models-tiers` restart slice signals.
28
- * - **`degrade-route`** — the one genuinely boot-frozen catalog consumer left: reactive degrade
29
- * (`MODEL_DEGRADE_REACTIVE`) bakes the target Model + its gateway + its key into the brain composition
30
- * at boot. It cannot be hot-applied without rebuilding the brain, so it is registered on the existing
31
- * restartRequired /health contract instead (see `restart-signal.ts`). Fail-loud beats serving a stale
32
- * gateway silently on the rate-limit path.
33
- * - skills/mcp/scenarios/runtime-gates — restart-to-apply by construction (baked into the boot wiring).
34
- *
35
- * (design/158 A13, internal-lossless) This module is now a FACADE: the implementation lives
36
- * alongside it in `src/config-center/` split by responsibility group (HTTP client / EffectiveConfig
37
- * application / restart-signal detection / skills+MCP consumption). Every symbol below is
38
- * re-exported UNCHANGED — existing `from ".../config-center/facade.js"` imports need zero changes
39
- * across a rename of this file's *contents*; a path rename (this move, 2026-07-29) does require the
40
- * importers to update, which was done in the same commit.
41
- */
42
1
  export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
43
2
  export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, runtimeHasActiveGate, } from "./apply-effective.js";
44
3
  export { restartReasons, planeHasActiveTiers, modelPlaneChanged, } from "./restart-signal.js";
@@ -1,10 +1,4 @@
1
1
  const cases = (key) => ({ effect: `HK-${key}-effect`, refuse: `HK-${key}-refuse`, rollback: `HK-${key}-rollback` });
2
- /**
3
- * 批1(cost 族)真正接线的 14 键 —— **stage 消费的单一真源**:`stage-limits.ts` 按本表遍历,
4
- * 所以「表里有、stage 不写」或「stage 写、表里没有」都不可能悄悄发生。
5
- *
6
- * 前 3 个是 B 簇1(限额座 setter);其余 11 个是 limitsHttp 组的 A′ 接线键。
7
- */
8
2
  export const BATCH1_LIMIT_KEYS = [
9
3
  "rateLimitPerMin",
10
4
  "maxPrincipalCostUsd",
@@ -22,19 +16,6 @@ export const BATCH1_LIMIT_KEYS = [
22
16
  "toolResultTtlSec",
23
17
  ];
24
18
  const B_SETTER_KEYS = ["rateLimitPerMin", "maxPrincipalCostUsd"];
25
- /**
26
- * 🔴 **stage 消费但不热**的键(codex R2-F2,验真后降档)。
27
- *
28
- * `costQuotaWindowSec` 在设计稿里归 B 簇(「加个 setter 就好」),那个判断的前提是「限额是比较参数,不是
29
- * 行状态」—— 对**限额**成立,对**窗长**不成立:SQL 腿的窗长是**桶键的一部分**(`floor(now/windowMs)`,
30
- * 进 DB 主键)。热切窗长于是有两个真后果:①A→B→A 会在原 A 窗内**复活**切换前的花费(旧桶还在,桶号又
31
- * 撞上);②B 期间 A 的高编号旧桶不被 `window_bucket < current` 的 reap 清理。内存腿只能靠「换窗即清」
32
- * 才安全,于是两腿语义必然分裂 —— 同一条发布在不同后端给出相反的成本判决(后端相关的 fail-open)。
33
- *
34
- * 处置:窗长**降为 restart-to-apply**(值仍逐次校验,变更逐次铸因、进世代账的 deferredKeys + 一条响亮
35
- * warn),而不是给一个「在某些后端上会复活旧账」的热能力。真做热要给桶键加**世代隔离**(把窗长世代并进
36
- * 持久化主键 + 跨副本一致的世代来源),那是独立的一件,归后续批。
37
- */
38
19
  export const DEFERRED_LIMIT_KEYS = [
39
20
  {
40
21
  key: "costQuotaWindowSec",
@@ -43,7 +24,6 @@ export const DEFERRED_LIMIT_KEYS = [
43
24
  },
44
25
  ];
45
26
  const DEFERRED_KEYS = new Set(DEFERRED_LIMIT_KEYS.map((d) => d.key));
46
- /** 热键表 = 批1 消费键集**减去**显式 deferred 的那些(表只登记真的热的键)。 */
47
27
  export const HOT_KEYS_REGISTRY = BATCH1_LIMIT_KEYS.filter((key) => !DEFERRED_KEYS.has(key)).map((key) => ({
48
28
  key,
49
29
  group: "limitsHttp",
@@ -1,25 +1,10 @@
1
- /**
2
- * config-center HTTP client — the transport calls the service makes against the config-center
3
- * (`fetchEffective`/`fetchPrincipalCaps`/`fetchPrincipalOrgMemory`/`fetchSkillContent`/`fetchPromptArtifact`/
4
- * `fetchPromptBlob`) plus the shared HTTP-error type. 后两条 `?principal=` 腿共用 `requestPrincipalView`
5
- * 传输半场与 `assertPrincipalEcho` 回声核,只在**解析投影**上分家(见 `fetchPrincipalOrgMemory` 头注)。 Split out of `facade.ts` (design/158 A13, internal-lossless — the
6
- * facade re-exports every symbol below unchanged).
7
- */
8
1
  import { createHash } from "node:crypto";
9
2
  import { readEffectiveWire, PrincipalCapsWire } from "@sema-agent/registry-core";
10
- import { centerPromptsFromEffective } from "../prompts-domain-validate.js"; // #100 worker 腿 prompts 边界归一(中立叶)
11
- /** GET the effective config (Bearer + ETag). null = 304 (unchanged). Throws on transport/HTTP error, and on a
12
- * payload that is not an effective config at all(非对象 / version 非有限数 / gate 域坏形——[2281] 裁B)。
13
- * `domainErrors`:坏 catalog 域(schema default 已落)逐域单列——与本地腿 `FetchEffectiveResult` 同键同义,
14
- * boot/refresh 的候选门直接消费。 */
3
+ import { centerPromptsFromEffective } from "../prompts-domain-validate.js";
15
4
  export async function fetchEffective(baseUrl, token, etag, fetchImpl = fetch, worker) {
16
- // Defensive scheme guard: Node's fetch supports file:// — a mis-set
17
- // SEMA_REGISTRY_URL must not turn into a local-file read. Reject anything but http(s).
18
5
  const scheme = new URL(baseUrl).protocol;
19
6
  if (scheme !== "http:" && scheme !== "https:")
20
7
  throw new Error(`SEMA_REGISTRY_URL must be http(s), got "${scheme}"`);
21
- // worker-scoped pull: `?worker=<name>` returns that worker's roster; omitted → the global roster
22
- // (backward compatible — the center returns today's global config when no worker is given).
23
8
  const url = `${baseUrl.replace(/\/+$/, "")}/api/config/effective${worker ? `?worker=${encodeURIComponent(worker)}` : ""}`;
24
9
  const res = await fetchImpl(url, {
25
10
  headers: { authorization: `Bearer ${token}`, ...(etag ? { "if-none-match": etag } : {}) },
@@ -29,24 +14,13 @@ export async function fetchEffective(baseUrl, token, etag, fetchImpl = fetch, wo
29
14
  return null;
30
15
  if (!res.ok)
31
16
  throw new Error(`config-center HTTP ${res.status}`);
32
- // [2281] 裁B(§M1):wire 载荷在**这个消费端边界**过 registry-core `readEffectiveWire`(0.12.0)真校验,
33
- // 不再裸断言——「两个契约共用一个拼写不是一个被检查的契约」。判据与本地腿同源(parseDomain +
34
- // DOMAIN_READ_FALLBACK):catalog 域坏形 → 该域 schema default + 下面折进 domainErrors(候选门在
35
- // refresh 拒候选、boot 逐域 warn);gate 域坏形/垃圾载荷 → throw(caller 整包 catch 回落 env/LKG)。
36
- // 未知顶层键 verbatim 透传(open-world:新 center 新域、legacy teams 键都不丢)。grandfather 类
37
- // 警告(值已被收编接受)不算 error,不进 domainErrors——与本地店同口径。
38
17
  const warnings = [];
39
18
  const wire = readEffectiveWire(await res.json(), (w) => warnings.push(w));
40
- // #100 边界单点归一(§V2-B):worker 腿 prompts 键在**这里**定形——0.13.0 起 wire 值 verbatim 到手,
41
- // 存储原文(global lane 合法下发形/emptyEffective 底座)归一为「键缺席」或 catalog 投影,下游
42
- // boot/adopt/refresh/deferred/LKG 存取全部只见「缺席或消费形」(裸 `.prompts` 直读有棘轮钉拦)。
43
19
  const centerPrompts = centerPromptsFromEffective(wire);
44
20
  if (centerPrompts === undefined)
45
21
  delete wire.prompts;
46
22
  else
47
23
  wire.prompts = centerPrompts;
48
- // registry-core 0.19.0 响亮化三 kind 同批收编(与 local 腿 config-provider.ts 同映射,LANDING-7.38 件③):
49
- // dropped/unread=写了没生效(defaulted 同级),carried=承运待键表方判——全入诊断面,禁静默丢。
50
24
  const domainErrors = warnings
51
25
  .map((w) => w.kind === "domain-defaulted"
52
26
  ? { domain: w.domain, error: (w.error instanceof Error ? w.error.message : String(w.error)).slice(0, 600) }
@@ -58,38 +32,13 @@ export async function fetchEffective(baseUrl, token, etag, fetchImpl = fetch, wo
58
32
  ? { domain: w.domain ?? "config.d", error: `unread config file ${w.file} (${w.why})`.slice(0, 600) }
59
33
  : undefined)
60
34
  .filter((r) => r !== undefined);
61
- // 经真校验后的 wire 值到 service 拼写副本的换装:两型同一契约面(EffectiveWire 是 service 型的同源超集,
62
- // service 型只声明自己消费的域且全 optional)——此断言的前提正是上面那次校验,不再是裸信任。
63
35
  const effective = wire;
64
36
  return { effective, etag: res.headers.get("etag") ?? undefined, ...(domainErrors.length > 0 ? { domainErrors } : {}) };
65
37
  }
66
- /**
67
- * design/99 §K (core 1.157 `RunnerDeps.runtimeCapsResolver`) — GET the PER-PRINCIPAL runtime
68
- * ENTITLEMENTS the engine enforces server-side, from center's `GET /api/config/effective?principal=&worker=`.
69
- * Center pre-resolves the tighten-only intersection (fleet defaults ∩ tier ⊕ roles + overrides) and returns a
70
- * `runtimeCaps` block — the service passes it through verbatim (no further tightening, per center's ruling).
71
- *
72
- * 🔐 TOKEN/WORKER (center ruling, sema-registry `aeb4979`): two valid postures —
73
- * • FULL `SERVICE_PULL_TOKEN` + `?principal=` → full per-user body (caps + per-user config).
74
- * • worker-scoped `wpt_<self>` + `?principal=&worker=<self>` → **caps-only** body (`{principal, configured,
75
- * runtimeCaps, budget}`; never other tenants' config) — a worker MAY resolve its OWN worker's principals.
76
- * A `wpt_` with NO `?worker=` (or a mismatched worker) still 403s. So we ALWAYS pass `?worker=` when the
77
- * deployment has one (`SEMA_REGISTRY_WORKER`): it authorizes the wpt_ path AND is harmless on the full token
78
- * (the caps value is worker-independent — `resolveEntitlement` resolves from the principal alone; worker only
79
- * scopes the config view we ignore). This re-adds the `worker` arg dropped in 1.14.0 (L2-3) — center's ruling
80
- * makes it load-bearing for AUTHORIZATION, not for the caps value.
81
- * `null` = 304 (caller keeps its cached value). THROWS on transport / non-304 HTTP error → the resolver
82
- * fail-closes (core then DENIES the optional `allowWorkflows` cap — an outage must never silently GRANT). ETag is
83
- * content-hashed (`p-<hash>`); an `if-none-match` poll is cheap. Mirrors `fetchEffective` (Bearer + 8s + scheme guard).
84
- */
85
38
  export async function fetchPrincipalCaps(baseUrl, token, principal, etag, fetchImpl = fetch, worker) {
86
39
  const raw = await requestPrincipalView(baseUrl, token, principal, etag, fetchImpl, worker);
87
40
  if (raw === null)
88
41
  return null;
89
- // 边界读=schema(clay 宪法 [2704] §2,同源锚=registry-core PrincipalCapsWire):治理面
90
- // runtimeCaps/budget 严格+passthrough(值漂移整体拒 ⇒ 本函数 throw ⇒ resolver fail-close,
91
- // 「outage 绝不静默 GRANT」同向);[565]/[580] fail-open 面与 orgMemory(C3 失败域隔离)在
92
- // schema 里保持宽读,归一/校验仍在下方与目录腿(极性不因 schema 化翻转)。
93
42
  const parsedBody = PrincipalCapsWire.safeParse(raw.json);
94
43
  if (!parsedBody.success) {
95
44
  throw new Error(`config-center principal-caps body failed schema validation: ${parsedBody.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ").slice(0, 600)}`);
@@ -99,10 +48,6 @@ export async function fetchPrincipalCaps(baseUrl, token, principal, etag, fetchI
99
48
  return {
100
49
  runtimeCaps: body.runtimeCaps ?? null,
101
50
  configured: Boolean(body.configured),
102
- // The caps view carries the RESOLVED per-principal scenario ruling (two keys, shape pinned
103
- // by center tests). An older center has NO such keys → undefined (no governance, legacy passthrough) —
104
- // distinct from the "assigned nothing" ruling {scenario:null, allowlist:[]} a newer center returns.
105
- // Defensive on shape (scenario face is fail-open — see gateScenarioRequest): non-string entries dropped.
106
51
  ...("scenario" in body || "allowlist" in body
107
52
  ? {
108
53
  scenario: {
@@ -111,17 +56,7 @@ export async function fetchPrincipalCaps(baseUrl, token, principal, etag, fetchI
111
56
  },
112
57
  }
113
58
  : {}),
114
- // (stage7 P5, registry-core 0.8 execution 域): the caps view's per-principal execution
115
- // ruling `{required, allowedLanes}` — same car, same no-cache semantics as the scenario ruling. An
116
- // older center has no key → undefined (no enforcement). allowedLanes is an OPEN value domain
117
- // (adding a lane is never BREAKING) — tolerate unknown names, drop non-string ELEMENTS defensively.
118
- // shape DRIFT (required non-boolean / allowedLanes non-array) must NOT be
119
- // coerced — the old coercion turned "allowedLanes came as a comma string" + required=true into
120
- // allowedLanes=[] = fleet-wide deny-all (fail-CLOSED on a wire glitch, opposite of the fail-open posture)
121
- // and a drifted `required` into silent non-enforcement. Now: drift ⇒ DROP the whole ruling (fail-open,
122
- // gate passes) + `executionDrift` so the caller's audit line fires (the resolver routes it to onError).
123
59
  ...((() => {
124
- // [2704] §1:unknown 面的窄化走运行时类型谓词(真检查),不走断言。
125
60
  const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
126
61
  const exRaw = body.execution;
127
62
  if (exRaw === undefined || exRaw === null)
@@ -133,10 +68,6 @@ export async function fetchPrincipalCaps(baseUrl, token, principal, etag, fetchI
133
68
  if (!Array.isArray(exRaw.allowedLanes))
134
69
  return { executionDrift: `execution.allowedLanes is ${typeof exRaw.allowedLanes}, expected string[] — ruling dropped (fail-open)` };
135
70
  const execution = { required: exRaw.required, allowedLanes: exRaw.allowedLanes.filter((s) => typeof s === "string") };
136
- // registry-core 0.10.1-0.10.2:execution.sessionMirror 宽读。observation-only 子面
137
- // (server 非执法端,见 ExecutionRuling.sessionMirror 注),所以形状漂移只丢**本子面**并走 executionDrift
138
- // 审计线(resolver 路由到 onError),**绝不**连带丢 lane ruling(那是真执法面,不能被观测子面拖垮)。
139
- // required 缺省 false = registry-core zod 默认(便利态);engineUrl 必须非空 string。
140
71
  if (exRaw.sessionMirror !== undefined && exRaw.sessionMirror !== null) {
141
72
  const sm = exRaw.sessionMirror;
142
73
  if (isRecord(sm) && typeof sm.engineUrl === "string" && sm.engineUrl.length > 0 && (sm.required === undefined || typeof sm.required === "boolean")) {
@@ -148,31 +79,19 @@ export async function fetchPrincipalCaps(baseUrl, token, principal, etag, fetchI
148
79
  }
149
80
  return { execution };
150
81
  })()),
151
- // design/170 件A:org 记忆解析段**原值透传**(键在场保在场、缺席保缺席——缺席=旧 center 能力握手,
152
- // org 目录腿据此判 section_absent 瞬时;形状校验是 org-memory-admission 模块用 PrincipalOrgMemoryWire
153
- // 的事,本腿保持薄透传不预判)。
154
82
  ...("orgMemory" in body ? { orgMemory: body.orgMemory } : {}),
155
- etag: raw.etag, // 与提取共用件之前逐字等价:键恒在场,无 ETag 头时值为 undefined(消费端只读真值)
83
+ etag: raw.etag,
156
84
  };
157
85
  }
158
- /** design/170 件A C8(响应 principal 回声核):center 两条 `?principal=` 腿的 body 都回带 principal——
159
- * 在场且与请求不符 = 中间层/LB 把别的 principal 的视图递了过来,弃用整响应(throw ⇒ caps 腿
160
- * fail-close、org 目录腿记 unavailable)。键缺席 = 旧 center 形,按无证据容忍(回声核只拦「有回声
161
- * 且对不上」的确定性错递,不把旧形升级成故障)。两条腿共用同一道闸,不各写一遍。 */
162
86
  function assertPrincipalEcho(echoed, principal) {
163
87
  if (echoed !== undefined && echoed !== principal) {
164
88
  throw new Error(`config-center principal-caps response echoes principal ${JSON.stringify(echoed)} but ${JSON.stringify(principal)} was requested — discarding the whole response`);
165
89
  }
166
90
  }
167
- /** 两条 `?principal=` 腿共用的**传输**半场:scheme 闸 / `?principal=[&worker=]` / Bearer / 8s 超时 /
168
- * 304 语义。逐字来自原 {@link fetchPrincipalCaps} 函数体,提成共用件是为了第二条腿(org 段读取)
169
- * 不必抄一遍传输纪律——抄一遍就等于给同一条边界立两套会各自漂移的规矩。
170
- * 返回 `null` = 合法的 304(仅当我们真的发了条件请求);否则是**解析前**的 body 原值。 */
171
91
  async function requestPrincipalView(baseUrl, token, principal, etag, fetchImpl, worker) {
172
92
  const scheme = new URL(baseUrl).protocol;
173
93
  if (scheme !== "http:" && scheme !== "https:")
174
94
  throw new Error(`SEMA_REGISTRY_URL must be http(s), got "${scheme}"`);
175
- // `?principal=<id>[&worker=<self>]` — worker authorizes the wpt_<self> caps-only path; harmless on a full token.
176
95
  const params = new URLSearchParams({ principal });
177
96
  if (worker)
178
97
  params.set("worker", worker);
@@ -182,9 +101,6 @@ async function requestPrincipalView(baseUrl, token, principal, etag, fetchImpl,
182
101
  signal: AbortSignal.timeout(8000),
183
102
  });
184
103
  if (res.status === 304) {
185
- // A 304 is only valid as a reply to OUR conditional. If we sent no etag and still got 304, center is
186
- // misbehaving — DON'T treat it as "unchanged" (that surfaces undefined caps = no restriction = fail OPEN).
187
- // Throw so the resolver fail-closes (review B). With an etag, 304 = genuine "unchanged" → null.
188
104
  if (!etag)
189
105
  throw new Error("config-center returned 304 to a non-conditional principal-caps request");
190
106
  return null;
@@ -194,31 +110,9 @@ async function requestPrincipalView(baseUrl, token, principal, etag, fetchImpl,
194
110
  const responseEtag = res.headers.get("etag");
195
111
  return { json: await res.json(), ...(responseEtag !== null ? { etag: responseEtag } : {}) };
196
112
  }
197
- /** 件A C3 失败域隔离的读取投影:org 目录腿只消费两个字段——`principal`(回声核)与 `orgMemory` 段
198
- * 原值。用 `PrincipalCapsWire.pick()` 取子集(schema 属主仍是 registry-core,这里不复制形状定义,
199
- * `.passthrough()` 随 `_def` 一并继承)。 */
200
113
  const PrincipalOrgMemoryEnvelope = PrincipalCapsWire.pick({ principal: true, orgMemory: true });
201
- /**
202
- * design/170 件A —— 读 `?principal=` 响应里的 org 记忆解析段(org 目录腿的取数口)。
203
- *
204
- * 🔬 **为什么不复用 {@link fetchPrincipalCaps}**(复审车 D-2 修):C3「两张表失败域互不干扰」是在档
205
- * 裁定,registry-core 的 `PrincipalCapsWire` 也正是为此把 org 段留成 `unknown`(「在本 schema 里校验
206
- * 会把 org 段坏形连坐进 caps 腿」)。但隔离此前只做了**单向**:org 段坏不连坐 caps,caps 段坏却整体
207
- * 连坐 org —— 治理面 `runtimeCaps`/`budget` 的一次值漂移会让整封 safeParse 失败、函数 throw,于是一个
208
- * **形状完全正确**的 org 授权段读不出来,目录记 `fetch_failed`、准入铸瞬时拒。治理面的 fail-closed
209
- * 极性是对的(它管的是「别静默放松治理」),但它不该决定 org 授权面的可用性。
210
- *
211
- * 所以这条腿用 {@link PrincipalOrgMemoryEnvelope} 只读它真正消费的两个字段:治理面漂移在这里是被
212
- * `.passthrough()` 原样放过的未知键,不构成本腿的故障。段本身的形状校验仍在目录腿
213
- * (`PrincipalOrgMemoryWire`),本腿保持薄透传不预判。
214
- *
215
- * 返回值三值语义:段键缺席 ⇒ `undefined`(旧 center 能力握手 ⇒ 目录判 `section_absent` 瞬时);
216
- * 段在场 ⇒ 原值(空表 = 取数成功的负结果,与缺席不同格)。条件请求不适用(本腿恒不带 etag),
217
- * 因此 304 在传输层就是 fail-loud。
218
- */
219
114
  export async function fetchPrincipalOrgMemory(baseUrl, token, principal, fetchImpl = fetch, worker) {
220
115
  const raw = await requestPrincipalView(baseUrl, token, principal, undefined, fetchImpl, worker);
221
- // etag 恒缺席 ⇒ 304 已在传输层 throw;这一支只为类型收口(不可达)。
222
116
  if (raw === null)
223
117
  throw new Error("config-center returned 304 to a non-conditional org-directory fetch");
224
118
  const parsed = PrincipalOrgMemoryEnvelope.safeParse(raw.json);
@@ -228,8 +122,6 @@ export async function fetchPrincipalOrgMemory(baseUrl, token, principal, fetchIm
228
122
  assertPrincipalEcho(parsed.data.principal, principal);
229
123
  return parsed.data.orgMemory;
230
124
  }
231
- /** An HTTP error from a config-center fetch that carries the response status so callers can branch on it
232
- * (e.g. translate a 404 to "unknown hash → undefined") WITHOUT parsing the message string. */
233
125
  export class ConfigCenterHttpError extends Error {
234
126
  status;
235
127
  constructor(message, status) {
@@ -238,14 +130,6 @@ export class ConfigCenterHttpError extends Error {
238
130
  this.name = "ConfigCenterHttpError";
239
131
  }
240
132
  }
241
- /**
242
- * Fetch one skill's body by content hash (`GET /api/config/skills/content/<hash>`, Bearer) and VERIFY its
243
- * sha256 matches the manifest hash (integrity + tamper check). The content is immutable per hash, so the
244
- * caller caches by hash and never re-fetches an unchanged skill. Throws a {@link ConfigCenterHttpError}
245
- * (carrying `.status`) on an HTTP error — INCLUDING 404 — or a plain Error on hash mismatch. Callers that
246
- * want "unknown hash → undefined" parity with the local backend inspect `.status === 404` and swallow it;
247
- * the legacy `applyCenterSkills` path keeps treating every throw as "skip this skill, keep baseline".
248
- */
249
133
  export async function fetchSkillContent(baseUrl, token, contentHash, fetchImpl = fetch) {
250
134
  const url = `${baseUrl.replace(/\/+$/, "")}/api/config/skills/content/${encodeURIComponent(contentHash)}`;
251
135
  const res = await fetchImpl(url, { headers: { authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(8000) });
@@ -258,10 +142,6 @@ export async function fetchSkillContent(baseUrl, token, contentHash, fetchImpl =
258
142
  throw new Error(`skill content hash mismatch for ${contentHash}: computed sha256:${got}`);
259
143
  return content;
260
144
  }
261
- /** [985]b / [1285]① — §9.4 by-digest 补拉二函(fetchSkillContent 同姿势:HTTP+Bearer+8s 超时+
262
- * 消费侧自证)。artifact=envelope JSON(验真=调用方过 core verifyPromptArtifact,canonical 重算——
263
- * 本函数只做传输与形读,不越权解释);blob=段文本 raw-byte(sha256 逐位自证,fetchSkillContent 同款)。
264
- * digest 参数收带前缀/裸 hex 两形([1285] 参数口径)。 */
265
145
  export async function fetchPromptArtifact(baseUrl, token, artifactDigest, fetchImpl = fetch) {
266
146
  const url = `${baseUrl.replace(/\/+$/, "")}/api/config/prompts/epoch-artifacts/${encodeURIComponent(artifactDigest)}`;
267
147
  const res = await fetchImpl(url, { headers: { authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(8000) });
@@ -274,9 +154,6 @@ export async function fetchPromptBlob(baseUrl, token, contentDigest, fetchImpl =
274
154
  const res = await fetchImpl(url, { headers: { authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(8000) });
275
155
  if (!res.ok)
276
156
  throw new ConfigCenterHttpError(`prompt blob HTTP ${res.status} for ${contentDigest}`, res.status);
277
- // codex F2(补拉批):哈希必须打在**响应原始字节**上(res.text() 的 UTF-8 宽容解码会剥 BOM/替换坏序列
278
- // ——验的就不再是送来的字节,content-addressed 完整性破功);验过再严格解码(fatal:true,坏 UTF-8
279
- // 掷错而非静默替换)。
280
157
  const bytes = new Uint8Array(await res.arrayBuffer());
281
158
  const want = contentDigest.replace(/^sha256:/, "").toLowerCase();
282
159
  const got = createHash("sha256").update(bytes).digest("hex");
@@ -1,45 +1,17 @@
1
- /**
2
- * #324(core 5.50 design/338 提货件)—— mid-turn MCP 撤销台账:`RunnerDeps.mcpRevocations` 席的宿主实现。
3
- *
4
- * 语义(与 core 契约对表,types.d.ts `RunnerDeps.mcpRevocations` 注):
5
- * · 权威单点在宿主 —— core 每次 MCP dispatch 前**同步**探 `isRevoked(serverName)`,从不缓存答案。
6
- * · 本台账记「中心**曾发布为 enabled、后从 enabled 集移除**」的服务器名。MCP mount 是 boot-baked
7
- * (`resolveMcpServers` 只在启动期跑),所以**删除向**在 5.50 前只能等 restart 才生效——这正是
8
- * design/338 要消的窗:治理面最要紧的动作(撤销一条对外通道)现在即时生效,restart 信号的
9
- * 删除向豁免见 `restart-signal.ts` 的 mcp 特判(`mcpRevocationLive` 前提,fail-closed:席没接线
10
- * 的部署保持旧的 restart 语义)。
11
- * · **重现恢复是指纹判,不是名字判**(codex F1,confirmed):被撤的名字回到 enabled 集时,只有当
12
- * 它的条目指纹与 **baseline(boot mount 真身)逐字相同**才解除撤销——重加同名**不同址/不同配置**
13
- * 的条目,复活的会是 boot-frozen 的旧 mount(新配置要 restart 才装上),那是把已撤下的旧端点在
14
- * restart 窗口内重新放行(fail-open)。指纹不匹配 ⇒ 名字保持 revoked 直到 restart 收敛。
15
- * · **baseline 只由 boot bake 拍铸**(codex F2,confirmed):它必须是「实际 mount 的集合」——迟到
16
- * boot 采用/refresh 拍的候选集都不是 mount 真身,不得铸 baseline。`hasBaseline()` 是 restart 豁免
17
- * 的第二道门(boot 超时形:mount=env 集,中心撤销语义整个不适用,豁免必须关死走旧 restart 语义)。
18
- * · **域缺席(null)不动**:中心停止发布 mcp 域 ≠ 逐服务器撤销指令(fingerprint 上 null↔非 null
19
- * 自会签 restart);域在场但 `servers: []` 是明确的「清空」指令 ⇒ 全撤。
20
- * · baseline 之后新增再删除的名字照 revoke(它从未 mount,isRevoked=true 无 mount 可拒=无害,
21
- * fail-closed 向);这类名字重加时因无 baseline 指纹**不解除**——同样无害,restart 收敛。
22
- *
23
- * 纯内存、同步、无 I/O —— 每副本各自持有(mount 也是副本各自的,天然同界)。进程重启即清零:
24
- * 重启后按新配置 mount,台账语义自洽。
25
- */
26
1
  export function createMcpRevocationLedger(onChange) {
27
- /** boot mount 真身(name→指纹);undefined = baseline 未铸(hasBaseline=false)。铸后不变。 */
28
2
  let baseline;
29
- let seen; // 上一拍 enabled 名集;undefined = 还没见过任何一拍
3
+ let seen;
30
4
  const revoked = new Set();
31
5
  return {
32
6
  isRevoked: (serverName) => revoked.has(serverName),
33
7
  hasBaseline: () => baseline !== undefined,
34
8
  applyEnabledEntries: (entries, opts) => {
35
9
  if (entries === null)
36
- return; // 域未发布 ≠ 撤销指令(成文见文件头)
10
+ return;
37
11
  if (opts?.isBaseline === true && baseline === undefined)
38
12
  baseline = new Map(entries);
39
13
  const restored = [];
40
14
  for (const [n, fp] of entries) {
41
- // codex F1:恢复=指纹判——只有回归到 baseline(mount 真身)的逐字同形才解除;
42
- // 同名不同形(改址/改配置)保持 revoked,新配置候 restart 装上(fail-closed)。
43
15
  if (revoked.has(n) && baseline?.get(n) === fp) {
44
16
  revoked.delete(n);
45
17
  restored.push(n);
@@ -62,10 +34,6 @@ export function createMcpRevocationLedger(onChange) {
62
34
  view: () => ({ revoked: [...revoked].sort() }),
63
35
  };
64
36
  }
65
- /** 一拍 EffectiveConfig 的 ENABLED MCP 服务器条目表(`resolveMcpServers` 同判据:`enabled !== false`;
66
- * 值=整条目的 key 排序 JSON 指纹,Map 插入序=发布序);mcp 域缺席 ⇒ `null`(台账不动)。
67
- * 指纹口径与 restart-signal 的 stableStringify 同精神(key 序归一),但**刻意独立实现**:两处指纹
68
- * 只在各自域内自比,不跨域比对,无同源约束。 */
69
37
  export function enabledMcpEntries(mcp) {
70
38
  if (!mcp)
71
39
  return null;
@@ -1,33 +1,4 @@
1
- /**
2
- * #A4([4147] 窗三件批 件1;[4073]/[4083] 承诺件)—— READ 容纳面四键的 **config-center 消费腿**。
3
- *
4
- * 部署 env 的四键(`READ_FACE` / `READ_DENY_PATTERNS` / `READ_DENY_BUILTIN_TIERS` /
5
- * `READ_DENY_BUILTIN_EXCLUDE`,见 config.ts)此前是这条轴上的唯一入口:一个用 config-center 管理整片
6
- * worker 的组织,想统一收紧 READ 面只能逐机改 env。本模块把 `EffectiveConfig.readFace`(见 types.ts
7
- * {@link CenterReadFace})投影到同样的四个 `ServiceConfig` 键上,**restart-to-apply**(与 mcp/a2a 同线:
8
- * `createRunnerDeps` 在 boot 期捕获这四键;改动由 `read-face` restart 片通告)。
9
- *
10
- * ── 三条规则,每条都有失败方向的论证 ──────────────────────────────────────────────────────────
11
- *
12
- * ① **env 显式恒赢,逐键判**(operator-knob 律)。env 是**这台机器**的部署主权表达,center 是组织下发;
13
- * 冲突时保留 env 并留一条点名 warn。为什么不照 6 闸的「center 覆盖 env」:那六闸是**限额/治理**面
14
- * (组织比单机更知道该收多紧),而 READ 容纳面描述的是**这台机器的磁盘上有什么** —— 运维在这台机器上
15
- * 写下 `READ_FACE=roots` 时知道的事情,组织的下发面不知道。静默盖掉它是本仓「部署级旋钮」纪律的反面。
16
- * 逐键而非整域:env 只写了 face 时,center 的三个 deny 键照常落(否则一个 env 键会把整片组织策略挡掉)。
17
- * 判据 = **键在场**(`!== undefined`):`readDenyBuiltinTiers: []` 是 env `none` 的显式空姿态,
18
- * 不是「没设过」—— 用真值判会把「一档内建都不要」这个显式表态判成缺席。
19
- *
20
- * ② **坏值整域拒 + 响亮**(#157 安全轴)。任何一个成员过不了引擎自己的校验器 ⇒ 整个 readFace 域不落,
21
- * 保留 env 前值 + warn 点名。两个方向都要:半应用一份 READ 姿态(收紧了一半)会让运维以为策略生效;
22
- * 而 throw 会让一次坏发布把常驻进程炸掉(env 腿 throw 是对的 —— 那是启动期、由本机运维自己写的值)。
23
- * 与 `stageApprovalRequire` 的域级拒绝先例同形。
24
- *
25
- * ③ **校验一律交给引擎**(`compileReadDeny` / `resolveReadDenyBuiltins`)。宿主不自造第二份判定语义,
26
- * 与 env 腿逐字同款;且校验跑在**合成后的最终姿态**上(env 键 + center 键混合形),否则
27
- * 「env 选了档位、center 删了一个不在该档里的行名」这类跨腿坏组合会漏过。
28
- */
29
1
  import { compileReadDeny, resolveReadDenyBuiltins } from "@sema-agent/core";
30
- /** center 域成员 → `ServiceConfig` 键的对照表(单一真源:warn 文案、env-wins 判定、落配三处同吃)。 */
31
2
  const KEY_MAP = [
32
3
  ["face", "readFace"],
33
4
  ["denyPatterns", "readDenyPatterns"],
@@ -36,9 +7,6 @@ const KEY_MAP = [
36
7
  ];
37
8
  class ReadFaceDomainError extends Error {
38
9
  }
39
- /** `denyPatterns` 的归一:裸串简写 ≡ `{pattern, caseSensitive:false}`(core `ReadDenyEntry` 同宽,
40
- * 与 env 腿 `parseReadDenyPatternsEnv` 逐字同词表)。形坏一律抛 ⇒ 整域拒(绝不静默丢元素:丢光会
41
- * 塌成空表,而空表的语义恰好是「没有追加保护」)。 */
42
10
  function normalizeDenyPatterns(raw) {
43
11
  if (!Array.isArray(raw))
44
12
  throw new ReadFaceDomainError("readFace.denyPatterns must be an array of \"pattern\" strings or {pattern, caseSensitive?} objects");
@@ -54,20 +22,17 @@ function normalizeDenyPatterns(raw) {
54
22
  return { pattern: it.pattern, caseSensitive: cs === true };
55
23
  });
56
24
  }
57
- /** `face` 的词表校验(闭集两词;坏词抛 ⇒ 整域拒)。 */
58
25
  function assertFace(raw) {
59
26
  if (raw !== "open" && raw !== "roots")
60
27
  throw new ReadFaceDomainError(`readFace.face must be "open" or "roots" — got ${JSON.stringify(raw)}`);
61
28
  return raw;
62
29
  }
63
- /** `resolveReadDenyBuiltins` 的入参拼装(两处调用同源:发布件自校验 + 合成后终校验)。 */
64
30
  function builtinsArg(tiers, exclude) {
65
31
  return {
66
32
  ...(tiers !== undefined ? { tiers } : {}),
67
33
  ...(exclude !== undefined ? { exclude } : {}),
68
34
  };
69
35
  }
70
- /** 名字表(档位 / 行名)的形校验:必须是非空串数组;`[]` 合法(显式空姿态)。 */
71
36
  function normalizeNameList(field, raw) {
72
37
  if (!Array.isArray(raw))
73
38
  throw new ReadFaceDomainError(`readFace.${field} must be an array of names`);
@@ -77,17 +42,7 @@ function normalizeNameList(field, raw) {
77
42
  return n.trim();
78
43
  });
79
44
  }
80
- /**
81
- * 应用 center 的 readFace 域(**boot-only**:调用点在 boot 拉取之后、`createRunnerDeps` 之前)。
82
- * 缺席 ⇒ 零触碰。规则见文件头注 ①②③。
83
- *
84
- * 返回值 = **本机 env 显式占住的 center 键名**(与本次发布内容无关的部署事实)。调用方把它喂给
85
- * `RestartSliceCtx.readFaceEnvHeld`,`read-face` restart 片于是只指纹「真会 boot-bake 的那部分」——
86
- * 否则 center 改一个本机永远用不上的键,整片 fleet 会为一次对自己毫无影响的发布滚动重启一遍
87
- * (codex 对抗复审 finding 2,验真后采纳的那一半)。
88
- */
89
45
  export function applyCenterReadFace(config, eff, logger) {
90
- // 部署事实先算(与发布件在不在场无关):env 占住哪几个键,是这台机器的属性。
91
46
  const envHeldKeys = KEY_MAP.filter(([, cfgKey]) => config[cfgKey] !== undefined).map(([centerKey]) => centerKey);
92
47
  const domain = eff.readFace;
93
48
  if (domain === undefined || domain === null)
@@ -100,33 +55,21 @@ export function applyCenterReadFace(config, eff, logger) {
100
55
  return envHeldKeys;
101
56
  }
102
57
  const d = domain;
103
- // ── ① env 恒赢(逐键):env 写过的键,center 的同名成员只留声、不落配 ────────────────────────
104
58
  const envHeld = KEY_MAP.filter(([centerKey, cfgKey]) => d[centerKey] !== undefined && config[cfgKey] !== undefined).map(([centerKey, cfgKey]) => `${cfgKey} (readFace.${centerKey})`);
105
- // ── ②③ stage ────────────────────────────────────────────────────────────────────────────────
106
59
  let staged;
107
60
  try {
108
- // ② **全域预校验先行,不看 env 是否覆盖**(codex 对抗复审 finding 1,验真属实后重排的次序)。
109
- // 旧序把校验挂在「取 center 值」之后,而被 env 占住的键根本不取值 ⇒ 那个成员**完全不过校验**:
110
- // 「env 钉死 face × center 把 face 写错一个字母 + 同域发一个放宽的 denyBuiltinTiers:[]」这一形下,
111
- // 非法成员静默无声、放宽成员照落 —— 本文件承诺的「任一坏值整域拒」被证否,且偏离方向是 fail-open。
112
- // 现在:域里**每一个在场成员**都先过形/词表/引擎校验(且校验的是**这份发布自己的**姿态),
113
- // 任一不合法 ⇒ 抛 ⇒ 整域拒;合法之后才做 env-wins 逐键合成。
114
61
  const normalized = {
115
62
  ...(d.face !== undefined ? { readFace: assertFace(d.face) } : {}),
116
63
  ...(d.denyPatterns !== undefined ? { readDenyPatterns: normalizeDenyPatterns(d.denyPatterns) } : {}),
117
64
  ...(d.denyBuiltinTiers !== undefined ? { readDenyBuiltinTiers: normalizeNameList("denyBuiltinTiers", d.denyBuiltinTiers) } : {}),
118
65
  ...(d.denyBuiltinExclude !== undefined ? { readDenyBuiltinExclude: normalizeNameList("denyBuiltinExclude", d.denyBuiltinExclude) } : {}),
119
66
  };
120
- // 发布件自身的引擎校验(与 env 覆盖无关 —— 一个坏发布就是坏发布,不因本机恰好用不上它而变合法)。
121
67
  if (normalized.readDenyPatterns !== undefined)
122
68
  compileReadDeny(normalized.readDenyPatterns);
123
69
  if (normalized.readDenyBuiltinTiers !== undefined || normalized.readDenyBuiltinExclude !== undefined) {
124
70
  resolveReadDenyBuiltins(builtinsArg(normalized.readDenyBuiltinTiers, normalized.readDenyBuiltinExclude));
125
71
  }
126
- // ③ env-wins 逐键合成:只保留**没被 env 占住**的键。
127
72
  staged = Object.fromEntries(KEY_MAP.filter(([, cfgKey]) => config[cfgKey] === undefined).map(([, cfgKey]) => [cfgKey, normalized[cfgKey]]).filter(([, v]) => v !== undefined));
128
- // 引擎规则再验一次,这次跑在**最终合成姿态**上(env 键 + 本次 center 键):跨腿坏组合
129
- // (env 选档位 × center 删一个不在该档里的行名)只有在合成后才看得见。
130
73
  const finalPatterns = staged.readDenyPatterns ?? config.readDenyPatterns;
131
74
  const finalTiers = staged.readDenyBuiltinTiers ?? config.readDenyBuiltinTiers;
132
75
  const finalExclude = staged.readDenyBuiltinExclude ?? config.readDenyBuiltinExclude;
@@ -142,7 +85,6 @@ export function applyCenterReadFace(config, eff, logger) {
142
85
  });
143
86
  return envHeldKeys;
144
87
  }
145
- // ── commit:纯赋值 ────────────────────────────────────────────────────────────────────────────
146
88
  if (staged.readFace !== undefined)
147
89
  config.readFace = staged.readFace;
148
90
  if (staged.readDenyPatterns !== undefined)
@@ -159,7 +101,6 @@ export function applyCenterReadFace(config, eff, logger) {
159
101
  }
160
102
  if (Object.keys(staged).length > 0) {
161
103
  logger?.info("sema_registry_read_face", {
162
- // 落的是**哪些键**,不是 deny 表的内容(策略内容不进日志面,与能力位的最小披露同判)。
163
104
  applied: Object.keys(staged),
164
105
  ...(staged.readFace !== undefined ? { face: staged.readFace } : {}),
165
106
  note: "restart-to-apply — these keys are captured when the runner deps are assembled at boot",