@sema-agent/server 7.99.0 → 7.101.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 (174) hide show
  1. package/MIGRATION.md +35 -1
  2. package/README.md +12 -1
  3. package/README.zh-CN.md +9 -1
  4. package/USAGE.md +150 -56
  5. package/dist/approval-ask-machine.d.ts +10 -0
  6. package/dist/approval-ask-machine.js +3 -0
  7. package/dist/approval-card.d.ts +86 -1
  8. package/dist/approval-card.js +42 -8
  9. package/dist/approval-policy-settlement.d.ts +31 -0
  10. package/dist/approval-policy-settlement.js +16 -0
  11. package/dist/approval-reconciler.d.ts +8 -1
  12. package/dist/approval-reconciler.js +5 -4
  13. package/dist/approval.d.ts +44 -20
  14. package/dist/approval.js +14 -6
  15. package/dist/boot/config-center.d.ts +16 -8
  16. package/dist/boot/config-center.js +114 -90
  17. package/dist/boot/execution-env.d.ts +1 -2
  18. package/dist/boot/execution-env.js +21 -3
  19. package/dist/boot/leader.js +12 -9
  20. package/dist/boot/memory-consolidation.d.ts +38 -0
  21. package/dist/boot/memory-consolidation.js +35 -0
  22. package/dist/boot/parked-revive-gate.js +1 -0
  23. package/dist/boot/reapers.js +1 -0
  24. package/dist/boot/resolve-spec.d.ts +0 -1
  25. package/dist/boot/resolve-spec.js +28 -10
  26. package/dist/boot/runner-deps.d.ts +2 -2
  27. package/dist/boot/runner-deps.js +9 -3
  28. package/dist/boot/stage-02-registry-pull.js +9 -0
  29. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  30. package/dist/boot/stage-06-runners.d.ts +2 -2
  31. package/dist/boot/stage-06-runners.js +3 -3
  32. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  33. package/dist/boot/stage-07-capability-layer.js +6 -6
  34. package/dist/boot/stage-08-reapers.d.ts +3 -4
  35. package/dist/boot/stage-08-reapers.js +2 -2
  36. package/dist/boot/stage-09-leader.d.ts +2 -3
  37. package/dist/boot/stage-10-http-server.d.ts +2 -3
  38. package/dist/boot/stage-10-http-server.js +2 -2
  39. package/dist/boot/stores.js +4 -0
  40. package/dist/capabilities/center-plugins.d.ts +14 -2
  41. package/dist/capabilities/center-plugins.js +13 -5
  42. package/dist/capabilities/hands-lane.js +2 -1
  43. package/dist/capabilities/scenarios.js +18 -5
  44. package/dist/capabilities/tool-defer.d.ts +2 -2
  45. package/dist/config-catalog.d.ts +2 -2
  46. package/dist/config-catalog.js +16 -15
  47. package/dist/config-center/apply-effective.d.ts +59 -39
  48. package/dist/config-center/apply-effective.js +231 -120
  49. package/dist/config-center/apply-ledger.d.ts +45 -16
  50. package/dist/config-center/apply-ledger.js +28 -18
  51. package/dist/config-center/facade.d.ts +12 -6
  52. package/dist/config-center/facade.js +2 -2
  53. package/dist/config-center/hot-keys-registry.d.ts +11 -4
  54. package/dist/config-center/hot-keys-registry.js +11 -7
  55. package/dist/config-center/http-client.d.ts +4 -1
  56. package/dist/config-center/http-client.js +15 -10
  57. package/dist/config-center/read-warnings.d.ts +115 -6
  58. package/dist/config-center/read-warnings.js +115 -5
  59. package/dist/config-center/restart-signal.d.ts +21 -14
  60. package/dist/config-center/restart-signal.js +16 -22
  61. package/dist/config-center/types.d.ts +9 -3
  62. package/dist/config-invariants.d.ts +2 -2
  63. package/dist/config-invariants.js +15 -0
  64. package/dist/config-provider.d.ts +4 -0
  65. package/dist/config-provider.js +64 -9
  66. package/dist/config-types.d.ts +57 -13
  67. package/dist/config.js +87 -22
  68. package/dist/cross-session-settings.js +1 -1
  69. package/dist/deployment-governance.d.ts +45 -12
  70. package/dist/deployment-governance.js +37 -9
  71. package/dist/env-name-allowlist-knobs.d.ts +37 -0
  72. package/dist/env-name-allowlist-knobs.js +24 -0
  73. package/dist/fleet-client.d.ts +2 -1
  74. package/dist/hooks/hook-llm.js +13 -4
  75. package/dist/host-lsp-manager.d.ts +41 -0
  76. package/dist/host-lsp-manager.js +15 -0
  77. package/dist/http/active-run-conflict.d.ts +2 -12
  78. package/dist/http/active-run-conflict.js +1 -6
  79. package/dist/http/admission.js +30 -7
  80. package/dist/http/memory-store-refusal-reply.d.ts +24 -0
  81. package/dist/http/memory-store-refusal-reply.js +10 -0
  82. package/dist/http/resume-legs.d.ts +1 -1
  83. package/dist/http/resume-legs.js +14 -10
  84. package/dist/http/route-ctx.d.ts +2 -2
  85. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  86. package/dist/http/routes/approvals-assistant.js +3 -2
  87. package/dist/http/routes/capabilities.js +3 -3
  88. package/dist/http/routes/memory-bundle.js +3 -0
  89. package/dist/http/routes/memory-compliance.js +3 -0
  90. package/dist/http/routes/memory-origin.js +3 -0
  91. package/dist/http/routes/sessions.js +7 -0
  92. package/dist/http/routes/side-query.js +10 -4
  93. package/dist/http/routes/tasks.js +7 -6
  94. package/dist/http/server.d.ts +6 -5
  95. package/dist/http/wire-types.d.ts +6 -3
  96. package/dist/instruction-source-notice.d.ts +97 -0
  97. package/dist/instruction-source-notice.js +78 -0
  98. package/dist/leader/wire.d.ts +20 -12
  99. package/dist/leader/wire.js +7 -6
  100. package/dist/memory-layer-legacy-lock.d.ts +14 -0
  101. package/dist/memory-layer-legacy-lock.js +74 -0
  102. package/dist/memory-store-refusal.d.ts +37 -0
  103. package/dist/memory-store-refusal.js +14 -0
  104. package/dist/model-entry-refusal.d.ts +130 -0
  105. package/dist/model-entry-refusal.js +157 -0
  106. package/dist/model-provider.d.ts +9 -3
  107. package/dist/model-route-endpoint.d.ts +1 -1
  108. package/dist/model-select.d.ts +49 -4
  109. package/dist/model-select.js +48 -17
  110. package/dist/observability/corrupt-read-seat.d.ts +21 -4
  111. package/dist/observability/corrupt-read-seat.js +31 -0
  112. package/dist/observability/fail-open.d.ts +16 -4
  113. package/dist/observability/fail-open.js +16 -4
  114. package/dist/observability/metrics.js +1 -0
  115. package/dist/observability/run-terminal.js +1 -0
  116. package/dist/observability/secret-env-scrub.d.ts +27 -3
  117. package/dist/observability/secret-env-scrub.js +22 -6
  118. package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
  119. package/dist/orchestration/workflow-completion-inbox.js +8 -3
  120. package/dist/plugins/approval-ask-store-file.d.ts +20 -2
  121. package/dist/plugins/approval-ask-store-file.js +21 -3
  122. package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
  123. package/dist/plugins/approval-ask-store-memory.js +39 -13
  124. package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
  125. package/dist/plugins/approval-ask-store-sql.js +27 -3
  126. package/dist/plugins/caching-session-store.d.ts +6 -0
  127. package/dist/plugins/caching-session-store.js +6 -0
  128. package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
  129. package/dist/plugins/checkpoint-store-sql.js +19 -2
  130. package/dist/plugins/local-checkpoint-store.js +2 -0
  131. package/dist/plugins/local-session-store.d.ts +20 -25
  132. package/dist/plugins/local-session-store.js +111 -64
  133. package/dist/plugins/mailbox-store-sql.d.ts +7 -5
  134. package/dist/plugins/mailbox-store-sql.js +6 -4
  135. package/dist/plugins/permission-rule-store-file.js +3 -2
  136. package/dist/plugins/pg-session-storage.d.ts +7 -0
  137. package/dist/plugins/pg-session-storage.js +19 -0
  138. package/dist/plugins/remote-env-host.d.ts +3 -1
  139. package/dist/plugins/remote-env-host.js +3 -6
  140. package/dist/plugins/sql-json-column.d.ts +16 -3
  141. package/dist/plugins/sql-json-column.js +15 -4
  142. package/dist/plugins/tidb-pool.js +5 -0
  143. package/dist/plugins/tidb-session-store.d.ts +13 -0
  144. package/dist/plugins/tidb-session-store.js +14 -0
  145. package/dist/plugins/web-search.d.ts +44 -23
  146. package/dist/plugins/web-search.js +161 -50
  147. package/dist/project-memory.d.ts +10 -0
  148. package/dist/project-memory.js +28 -14
  149. package/dist/run-local.d.ts +17 -15
  150. package/dist/run-local.js +119 -18
  151. package/dist/runs.js +6 -5
  152. package/dist/runtime-governance.d.ts +50 -3
  153. package/dist/runtime-governance.js +5 -0
  154. package/dist/sealed-key.d.ts +5 -2
  155. package/dist/server-secret-env.d.ts +26 -2
  156. package/dist/server-secret-env.js +9 -0
  157. package/dist/task-settings.d.ts +1 -1
  158. package/dist/tool-approval.d.ts +150 -28
  159. package/dist/tool-approval.js +150 -54
  160. package/dist/trace/core-keyset-guard.d.ts +30 -8
  161. package/dist/trace/engine-notice-wire.d.ts +27 -1
  162. package/dist/trace/engine-notice-wire.js +8 -0
  163. package/dist/trace/ledger-sink.js +2 -2
  164. package/dist/trace/project.d.ts +13 -0
  165. package/dist/trace/project.js +22 -5
  166. package/dist/trace/projection-drop.d.ts +2 -0
  167. package/dist/trace/projection-drop.js +6 -0
  168. package/dist/trace/sema-provenance.d.ts +4 -1
  169. package/dist/trace/sema-provenance.js +1 -1
  170. package/dist/trace/task-notification-facets.d.ts +65 -0
  171. package/dist/trace/task-notification-facets.js +31 -0
  172. package/dist/trace/wire-projection-faces.d.ts +3 -3
  173. package/dist/trace/wire-projection-faces.js +3 -3
  174. package/package.json +3 -3
@@ -21,7 +21,7 @@
21
21
  * 没有方法、没有捕获的行为。
22
22
  */
23
23
  import { z } from "zod";
24
- import { READ_ROOT_CANDIDATE_DIR_MAX, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
24
+ import { READ_ROOT_CANDIDATE_DIR_MAX, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
25
25
  import type { AskRow } from "./plugins/approval-ask-store-sql.js";
26
26
  /** 模型自由文本(`sourceAgentName` / `delegation.agentName`)的限长(设计稿 §6.2)。设计 §3.1 把
27
27
  * 「限长 + 脱敏」写成 **server 新增责任**(引擎无此层):spawning model 挑的名字是自由文本,
@@ -351,12 +351,97 @@ export declare const RuleOffersAbsenceSchema: z.ZodEnum<{
351
351
  lane_cannot_speak: "lane_cannot_speak";
352
352
  shadowed: "shadowed";
353
353
  }>;
354
+ /**
355
+ * `ruleOffersAbsence` 座的**三态读数**(S-663 codex r1 起;此前是 {@link readRuleOffersAbsence} 函数体内的两步)。
356
+ * 一只 schema、一处解析,两个读者各自处置「**在场却读不出**」(集外词 / 非串值 = 世代差或第三方 producer):
357
+ * · 展示面 {@link readRuleOffersAbsence}(活卡帧 / `card_json`)—— 按缺席丢并计 F 类(卡上少一行解释 ≪ 卡上出现生词);
358
+ * · 耐久 `/decide` 腿的 remember 判据(`tool-approval.ts` 的 `parkRowStandingAnswerFacts` 的 `mandated` 一格,读 park 行上的
359
+ * 孪生座 `PendingAction.tool_approval.ruleOffersAbsence`,core 同一 factory 铸)—— 判不出是不是强制 ⇒ **按强制**(fail-closed)。
360
+ * 同一个词在两处方向相反是刻意的:展示面的最坏后果是少一行字,权限面的最坏后果是一张会话级免询问。
361
+ * 本函数**不计数**(计不计、计哪个 tag 是读者的处置,不是读数)。
362
+ */
363
+ export type RuleOffersAbsenceSeat = {
364
+ readonly state: "absent";
365
+ } | {
366
+ readonly state: "word";
367
+ readonly word: RuleOffersAbsence;
368
+ } | {
369
+ readonly state: "unreadable";
370
+ readonly raw: unknown;
371
+ };
372
+ export declare function readRuleOffersAbsenceSeat(x: unknown): RuleOffersAbsenceSeat;
354
373
  /**
355
374
  * `AskRequest.ruleOffersAbsence`(core [ref] 修②)的**边界窄读** —— 活卡帧与 `card_json` 的唯一铸造点
356
375
  * (与 {@link readProbeCause} 同款分工),两面结构性同值。闭集词 verbatim(非内容族,零 redact);
357
376
  * 形不合/缺席 ⇒ 不铸键(缺席不是断言 —— 它同时覆盖「有 offers」与结构性无车道的门,core 契约逐字)。
377
+ * 解析在 {@link readRuleOffersAbsenceSeat}(与耐久腿判据共用一只 schema)。S-700 起第三个读者:运维队列行经
378
+ * {@link parkRowFacts} 读寄存行上的 park 孪生座(`PendingAction.tool_approval.ruleOffersAbsence`,core 同一个 factory 铸的同一
379
+ * 闭集)—— 同一只 schema、同一个集外 tag,三面结构性同值;集外 detail 经 {@link outOfSetDetail}(只读值,从不调用值)。
358
380
  */
359
381
  export declare function readRuleOffersAbsence(req: unknown): RuleOffersAbsence | undefined;
382
+ /**
383
+ * park 行上两只 **`?: true` 孪生座**的名(core d.ts:「present (`true`) ⇔ … Absent otherwise (never `false`)」):
384
+ * · `requiresRealApproval`([ref])—— 这一问只有**真人**清得掉(连一揽子席也不得答;蕴含下一位);
385
+ * · `mandated`(#1079,core 7.31.0;S-663 二段 / S-723)—— 没有任何持久规则、也没有任何站着的答案退得掉这一问
386
+ * (`AskRequest.mandated` 的 park 孪生,core 从行的规则报价工厂读的**同一份**清障事实铸)。
387
+ * 两位同形同律 ⇒ 同一只读法 {@link readParkRowTrueSeat}(一处解析,名字是参数,不是两只各写一遍的函数)。
388
+ * 型从 core 寄存行上取键(`keyof ToolApprovalPark` 约束):core 改名 / 退役任一位 ⇒ 这里 tsc 红。
389
+ */
390
+ export type ParkRowTrueSeat = Extract<keyof ToolApprovalPark, "requiresRealApproval" | "mandated">;
391
+ /**
392
+ * park 行 `?: true` 座的读数(NP-1 起;S-663 二段起名字是参数)—— **`=== true` 严判**,`false` 与其它值读作缺席
393
+ * (core d.ts「Absent otherwise (never `false`)」;与 core `summarizeCheckpoint` 同一条判法:它把两位都筛成字面 `true`)。
394
+ * `kind` 由调用方先判(本函数只读这一位)。两个读者共用这一只,解析单源、处置各归读者(与 {@link readRuleOffersAbsenceSeat}
395
+ * 同一条分工):展示投影 {@link parkRowFacts}(队列行顶层展开)与耐久 `/decide` 腿的 remember 判据(`tool-approval.ts`
396
+ * 的 `parkRowStandingAnswerFacts`)。这两位在两处方向相同(在场才算、读不出 = 缺席)⇒ 不需要三态、不计数(inbox 面对同一形
397
+ * 同样静默缺席,两面同律);也因此权限面读它们**不经** `parkRowFacts` —— 那只展示投影顺手读 `ruleOffersAbsence` 并对集外词计
398
+ * 展示面的 F 类 tag,而权限面对同一格是 fail-closed 的另一种处置(S-663 codex r1 钉「权限面不计展示面 tag」)。
399
+ */
400
+ export declare function readParkRowTrueSeat(toolApproval: object, seat: ParkRowTrueSeat): boolean;
401
+ /** core 寄存行的 `tool_approval` 臂(park 行事实全部住在这一臂上;其余臂没有工具字段)。 */
402
+ type ToolApprovalPark = Extract<CorePendingAction, {
403
+ kind: "tool_approval";
404
+ }>;
405
+ /**
406
+ * 🔴 S-700 P1(client-core CC-180 / cli L-652 的根子;板 [ref] 订正 → [ref] 立案)—— **park 行事实**:
407
+ * 运维队列行(`GET /v1/approvals` + `/stream`)顶层展开的那几位。型**从 core 寄存行派生,不手抄**(S-249 同一条
408
+ * 纪律):core 改名 / 退役任一位 ⇒ 这里 tsc 红;这一位与 {@link RuleOffersAbsence}(从 `AskRequest` 派生)两个
409
+ * 联合漂开 ⇒ {@link parkRowFacts} 的返回处 tsc 红(core 说两座是同一个 factory 铸的同一闭集,本仓照此钉)。
410
+ *
411
+ * 两只店的 `PendingCheckpoint` **直接继承本型**(`extends`),于是 core 再给这一臂加一位同类事实时,两店与路由零改 ——
412
+ * S-663 二段(core #1079,7.31.0)的 `mandated` 就是这么到货的。要改的三处,编译器逐一点名:这张 Pick 表加一个名 +
413
+ * {@link parkRowFacts} 函数体一行 + `trace/core-keyset-guard.ts` ⑧b 的 EXCLUDED 簿删掉这个名(⑧b 的事实段**从本型派生**,
414
+ * 8D-1 / 车OC 起单写者 —— 名字进了本表却还躺在 EXCLUDED 里 ⇒ `_NoDoubleBookParkArmQueue` 当拍红);`test/park-row-facts.test.ts`
415
+ * 的 `FACT_KEYS` 对本型双向围栏,少列一名同样编译红。
416
+ * core d.ts 说三位彼此独立:`requiresRealApproval`(只有人能清 —— 蕴含 `mandated`)、`mandated`(没有规则能清)、
417
+ * `ruleOffersAbsence`(报价车道有什么可给;「never a substitute for this one」)—— 投影逐位照投,不互推。
418
+ */
419
+ export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence" | "mandated">;
420
+ /**
421
+ * S-700 —— park 行事实的**唯一投影**(`pendingAction` 是寄存行上 core 铸的那一只,读自持久层 ⇒ `unknown`)。
422
+ *
423
+ * 病:core 在寄存行 `PendingAction.tool_approval` 上铸的「卡读者该看的事实」(core [ref] 原话:「the SAME fact,
424
+ * restated where a card reader looks for it」)中 `requiresRealApproval` 经 core `summarizeCheckpoint` 早就上了 inbox(`ruleOffersAbsence`
425
+ * 不在 core 的 `CheckpointSummary` 上),而运维队列的两只店
426
+ * `listPending` 是逐键手工组行、一位都没取 ⇒ 同一张卡「inbox 上看得到只有真人能清、队列上看不到」,壳的 park
427
+ * 卡因此恒不知道这件事(client-core 0.82.4 `26fca98b` 的透传在真部署上恒不触发)。
428
+ *
429
+ * 判据(每一条都是 core d.ts 的消费纪律,不是本仓自定):
430
+ * · **kind 先判**(「Every consumer that reads the tool fields MUST branch on `kind` first」)—— 非对象 / 缺席 /
431
+ * 非 `tool_approval` 臂 ⇒ `{}`,那些臂上出现的同名键不是这两位;
432
+ * · `requiresRealApproval` / `mandated` —— 经 {@link readParkRowTrueSeat}(`=== true` 严判,真才带;`false` 与其它值读作
433
+ * 缺席,「Absent otherwise (never `false`)」)。与 core `summarizeCheckpoint` 逐字同一条判法 ⇒ inbox 行与队列行对同一张卡
434
+ * 同答(`test/park-row-facts.test.ts` 钉)。不计数:inbox 那一面对同一形同样静默缺席,两面同律;
435
+ * · `ruleOffersAbsence` —— 经 {@link readRuleOffersAbsence}(活卡帧 / `card_json` 同一只 schema、同一个集外 tag):
436
+ * park 孪生座与同步座是 core 同一个 factory 铸的同一闭集,不另起第二份词表;
437
+ * · 缺席 ≠ false:两位都只在「真」时在场,消费端读**在场**。
438
+ *
439
+ * 🔴 **这是展示/分诊投影,不是权限判据**(core 对两位的定性都是 display metadata:「the resume belts keep reading
440
+ * the gate's own bit」)。本投影对「在场却读不出」的方向是**缺席**(卡上少一行 ≪ 卡上出现生词);一条要从
441
+ * `ruleOffersAbsence` 推「强制」的**权限**判据需要的是三态(读不出 ⇒ 按强制,fail-closed),方向相反,所以权限面
442
+ * 不许拿本函数的产物去推强制 —— 两者共用的是 schema,不是折叠。
443
+ */
444
+ export declare function parkRowFacts(pendingAction: unknown): ParkRowFacts;
360
445
  /** S-114(core 7.4.0 [ref])—— 回落卡的计数与窗。`limit` 是**闭二词**(core d.ts 逐字),两个计数是
361
446
  * 非负整数,`autoDenyAfterMs` 是 core 契约里的 `0..2147483647` 非负整数(`0` = 本卡不武装窗:部署把
362
447
  * 旋钮关了,或 TOTAL 档的卡按 [ref]① 恒 0 等人)。四成员**全必填**——core 的 `DenialLimitFallback` 上
@@ -236,15 +236,49 @@ export function readRuleEvidence(req) {
236
236
  const RULE_OFFERS_ABSENCE_VALUES = ["mandated", "lane_cannot_speak", "shadowed"];
237
237
  export const RuleOffersAbsenceSchema = z.enum(RULE_OFFERS_ABSENCE_VALUES);
238
238
  const RuleOffersAbsenceEnvelopeSchema = z.object({ ruleOffersAbsence: RuleOffersAbsenceSchema.optional() });
239
- export function readRuleOffersAbsence(req) {
240
- const parsed = RuleOffersAbsenceEnvelopeSchema.safeParse(req);
239
+ export function readRuleOffersAbsenceSeat(x) {
240
+ const parsed = RuleOffersAbsenceEnvelopeSchema.safeParse(x);
241
241
  if (parsed.success)
242
- return parsed.data.ruleOffersAbsence;
243
- const raw = req !== null && typeof req === "object" ? req.ruleOffersAbsence : undefined;
244
- if (raw === undefined)
245
- return undefined;
246
- recordFailOpen("server.approval-card.closed-word-out-of-set", `ruleOffersAbsence=${redactSecrets(String(raw)).slice(0, 40)}`);
247
- return undefined;
242
+ return parsed.data.ruleOffersAbsence === undefined ? { state: "absent" } : { state: "word", word: parsed.data.ruleOffersAbsence };
243
+ const raw = x !== null && typeof x === "object" ? x.ruleOffersAbsence : undefined;
244
+ return raw === undefined ? { state: "absent" } : { state: "unreadable", raw };
245
+ }
246
+ export function readRuleOffersAbsence(req) {
247
+ const seat = readRuleOffersAbsenceSeat(req);
248
+ switch (seat.state) {
249
+ case "word":
250
+ return seat.word;
251
+ case "absent":
252
+ return undefined;
253
+ case "unreadable":
254
+ recordFailOpen("server.approval-card.closed-word-out-of-set", `ruleOffersAbsence=${outOfSetDetail(seat.raw)}`);
255
+ return undefined;
256
+ }
257
+ }
258
+ function outOfSetDetail(raw) {
259
+ if (typeof raw === "string")
260
+ return redactSecrets(raw).slice(0, 40);
261
+ if (raw === null)
262
+ return "<null>";
263
+ if (Array.isArray(raw))
264
+ return "<array>";
265
+ if (typeof raw === "object" || typeof raw === "function")
266
+ return `<${typeof raw}>`;
267
+ return String(raw).slice(0, 40);
268
+ }
269
+ export function readParkRowTrueSeat(toolApproval, seat) {
270
+ const v = seat in toolApproval ? toolApproval[seat] : undefined;
271
+ return v === true;
272
+ }
273
+ export function parkRowFacts(pendingAction) {
274
+ if (typeof pendingAction !== "object" || pendingAction === null || !("kind" in pendingAction) || pendingAction.kind !== "tool_approval")
275
+ return {};
276
+ const ruleOffersAbsence = readRuleOffersAbsence(pendingAction);
277
+ return {
278
+ ...(readParkRowTrueSeat(pendingAction, "requiresRealApproval") ? { requiresRealApproval: true } : {}),
279
+ ...(readParkRowTrueSeat(pendingAction, "mandated") ? { mandated: true } : {}),
280
+ ...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
281
+ };
248
282
  }
249
283
  const DenialLimitFallbackSchema = z
250
284
  .object({
@@ -0,0 +1,31 @@
1
+ /**
2
+ * S-702 / S-706 —— 「无人可答」的**政策感知终态 claim**:全仓唯一一只(NP-2 的政策结算抽出来,四处调用,不写第二份)。
3
+ *
4
+ * 一句话:一只没人答的 ask 在持久 ask 行上**落什么终局**,由部署政策(`UNATTENDED_APPROVAL_POLICY`)一处决定 ——
5
+ * · `park` ⇒ `expire` 意图:STREAM_PENDING→PARKING、批转投递面、同批流内兄弟连坐 VOID(park 路由的单位是整批,core 随后
6
+ * durable park,对账收敛器再把行绑到 checkpoint 上);
7
+ * · `deny` ⇒ `refuse` 意图:**按 ask 作用域**把这一只行 CAS 成 DECIDED(deny) + `settled_by="policy"`,不碰批、不连坐兄弟
8
+ * (deny 政策下没有 park 路由这个单位,7.99.0 铸的整腿批存量行同判)。落行的来源词是店侧常量 `POLICY_REFUSAL_DECISION`,
9
+ * 协调器交给 core 的部署政策拒对象读的是**同一个**常量 ⇒ 同 ask 重入 / 收敛后重入由 `outcomeOfDecidedRow` 读回同一个对象,
10
+ * 不退化成裸 `false`(core 的 onAsk 契约里那是「一个人拒了」,可停批)。
11
+ *
12
+ * 四个调用点(都调这一只,谁也不自己挑意图):
13
+ * 1. live 腿 —— 窗到期臂的终局 claim(`tool-approval.ts` `windowExpired`,经 claim 阶梯 `resolveLocalOutcome`);
14
+ * 2. live 腿 —— emit 全灭臂的终局 claim(同上,`emitFailed` 支);
15
+ * 3. 两兜底臂 —— `ensureAsk` 行不可用 / ensure 身份不定(`tool-approval.ts` `landUnattendedOutcome`,只在交给 core 的是**结算**时
16
+ * 调:park 路由不是结算,行留给投递面的既有机器,park 行为逐字不变),以及后者迟到落盘那条行的收尾;
17
+ * 4. 崩溃恢复腿 —— 对账收敛器段二的孤儿 STREAM_PENDING 代打(`approval-reconciler.ts`):它就是窗到期竞争者在崩溃后的补位,
18
+ * 同一只 claim、同一个转移(S-706 之前它无条件 `expireAsk`,deny 部署上崩溃后行进 PARKING,run 完成后成不带来源的 DENIED)。
19
+ *
20
+ * 形:纯委托 —— 店的原子终局 claim(`ApprovalAskStore.claimTerminal`,三 twin 一台 CAS 机器,律 2)+ 政策→意图的穷举映射。
21
+ * 答案自足(律 1):赢 ⇒ `{claimed:true, voidedSiblings}`(refuse 恒空名单);输 ⇒ 携行真相;抛错 ⇒ 什么也不证明。
22
+ * 各调用点对这三种答案的处置归各自(阶梯 / 兜底 / 收敛器),不在这里。
23
+ */
24
+ import type { ApprovalAskStore, AskRow, AskTerminalClaimOutcome } from "./plugins/approval-ask-store-sql.js";
25
+ import type { UnattendedApprovalPolicy } from "./tool-approval.js";
26
+ /**
27
+ * 按部署政策给这一只 ask 行打原子终局 claim(见文件头:四个调用点、两个意图、答案自足)。`row` 只取 scope 两维 ——
28
+ * `batchId` 必须是**行上**的批(幂等命中的存量行可能是别的政策 / 旧版本铸的整腿批,错 scope 的 claim 如实 `absent`)。
29
+ */
30
+ export declare function settleByPolicy(store: Pick<ApprovalAskStore, "claimTerminal">, row: Pick<AskRow, "askId" | "batchId">, policy: UnattendedApprovalPolicy): Promise<AskTerminalClaimOutcome>;
31
+ //# sourceMappingURL=approval-policy-settlement.d.ts.map
@@ -0,0 +1,16 @@
1
+ function unattendedClaimIntent(policy) {
2
+ switch (policy) {
3
+ case "park":
4
+ return "expire";
5
+ case "deny":
6
+ return "refuse";
7
+ default: {
8
+ const unreachable = policy;
9
+ throw new Error(`unattendedClaimIntent: unknown UNATTENDED_APPROVAL_POLICY word ${String(unreachable)}`);
10
+ }
11
+ }
12
+ }
13
+ export function settleByPolicy(store, row, policy) {
14
+ return store.claimTerminal(row.askId, row.batchId, unattendedClaimIntent(policy));
15
+ }
16
+ //# sourceMappingURL=approval-policy-settlement.js.map
@@ -1,4 +1,5 @@
1
1
  import type { AskRow, ApprovalAskStore } from "./plugins/approval-ask-store-sql.js";
2
+ import type { UnattendedApprovalPolicy } from "./tool-approval.js";
2
3
  import type { BatchState } from "./approval-ask-machine.js";
3
4
  import type { CheckpointAskCandidate } from "./plugins/checkpoint-store-sql.js";
4
5
  import { type RunRecord } from "./plugins/store-contracts.js";
@@ -189,7 +190,8 @@ export interface ReconcileStats {
189
190
  /** 段一/段二因**墙钟预算**用尽而提前收工的段数(0/1/2)。>0 ⇒ 依赖在退化,剩余行留给下一轮
190
191
  * (见 {@link RECONCILE_SEGMENT_BUDGET_MS})。 */
191
192
  budgetExhausted: number;
192
- /** 段二:被代打 `expireAsk` 的孤儿 `STREAM_PENDING` 行数(赢 CAS 的)。 */
193
+ /** 段二:被代打**窗到期终局 claim** 的孤儿 `STREAM_PENDING` 行数(赢 CAS 的)。「expired」指触发(窗过了没人收),
194
+ * 不指意图 —— 与 live 腿 `window_expired` 同一个读法:park 部署 ⇒ expire(进投递面);deny 部署 ⇒ refuse(当场落政策结算,S-706)。 */
193
195
  orphansExpired: number;
194
196
  }
195
197
  /** 撤卡帧的投递面(逐 ctx 分发:durable 腿落账本、sync 腿 live,见 `ApprovalRevokeFrame` 顶注)。
@@ -222,6 +224,11 @@ export interface ApprovalReconcilerDeps {
222
224
  pendingGraceMs: number;
223
225
  adhocGraceMs: number;
224
226
  orphanTtlMs: number;
227
+ /**
228
+ * S-706:部署的无人值守政策(`UNATTENDED_APPROVAL_POLICY`)—— 段二孤儿代打打的是哪个终局 claim。**必填**、恒传(与协调器
229
+ * 读同一个部署值;没有缺省 —— 缺省 park 会让 deny 部署在崩溃恢复这一形上静默退回「进投递面 → 不带来源的 DENIED」)。
230
+ */
231
+ unattendedPolicy: UnattendedApprovalPolicy;
225
232
  emitRevoke?: ApprovalRevokeEmitter | undefined;
226
233
  }
227
234
  /** {@link createApprovalReconciler} 返回的活对象。 */
@@ -1,4 +1,5 @@
1
1
  import { CANCELLED_CODE } from "./run-cancel-context.js";
2
+ import { settleByPolicy } from "./approval-policy-settlement.js";
2
3
  import { isTerminalRunStatus } from "./plugins/store-contracts.js";
3
4
  import { DENY_REASONS, VOID_REASONS } from "./approval-deny-reasons.js";
4
5
  import { buildRevokeFrame } from "./approval-card.js";
@@ -78,7 +79,7 @@ export function decideReconcileAction(input) {
78
79
  return { kind: "hold" };
79
80
  }
80
81
  export function createApprovalReconciler(deps) {
81
- const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, emitRevoke } = deps;
82
+ const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, unattendedPolicy, emitRevoke } = deps;
82
83
  const revoke = (row, askIds, reason, nowMs) => {
83
84
  if (askIds.length === 0 || !emitRevoke)
84
85
  return;
@@ -245,8 +246,8 @@ export function createApprovalReconciler(deps) {
245
246
  if (row.expiresAtMs + pendingGraceMs >= nowMs)
246
247
  continue;
247
248
  try {
248
- const res = await withDeadline(askStore.expireAsk(row.askId, row.batchId), "expireAsk(orphan)");
249
- if (!res.won)
249
+ const res = await withDeadline(settleByPolicy(askStore, row, unattendedPolicy), "claimTerminal(orphan)");
250
+ if (!res.claimed)
250
251
  continue;
251
252
  stats.orphansExpired += 1;
252
253
  revoke(row, res.voidedSiblings, "superseded_by_park", nowMs);
@@ -254,7 +255,7 @@ export function createApprovalReconciler(deps) {
254
255
  catch (err) {
255
256
  stats.failed += 1;
256
257
  try {
257
- logger.warn("approval_orphan_expire_failed", { askId: row.askId, taskId: row.taskId, err: err instanceof Error ? err.message : String(err) });
258
+ logger.warn("approval_orphan_expire_failed", { askId: row.askId, taskId: row.taskId, policy: unattendedPolicy, err: err instanceof Error ? err.message : String(err) });
258
259
  }
259
260
  catch {
260
261
  }
@@ -7,7 +7,26 @@ import { type ToolPolicy } from "@sema-agent/core";
7
7
  import { type PostureSource } from "./posture-source.js";
8
8
  import { type ExecutionLane } from "./execution-lane-caps.js";
9
9
  /**
10
- * 运维**是否表达了门意图** —— boot 的单用户 allow-all 基线只在"零门意图"时才允许铺开,
10
+ * 门意图判据读的五个量(`ServiceConfig` 结构满足)。单独成型是为了让**热路径**能拿「候选视图」来判
11
+ * (S-668 波 2:`{ ...config, approvalRequire: 候选名单 }`)—— 判据只有一份,输入可以是活配置也可以是候选。
12
+ */
13
+ export interface GateIntentView {
14
+ readonly approvalRequire: readonly string[];
15
+ readonly approvalDeny: readonly string[];
16
+ readonly approvalNeverAuto: readonly string[];
17
+ readonly durableApproval: boolean;
18
+ readonly durableApprovalSource: PostureSource;
19
+ }
20
+ /**
21
+ * 三张审批名单(require / deny / never-auto)里**有没有一张在场** —— 「这台部署有一份要执行的审批策略」。
22
+ *
23
+ * 🔴 S-668 波 2(codex r1 F1):leader 车道的 durable 审批席曾只按 `approvalRequire` 非空给出。require 名单转热之后,
24
+ * 中心热撤空它就把 leader worker 上**独立**的 deny / never-auto 两张表一起撤掉(主车道照旧 deny / ask)—— 三张表是
25
+ * 三条独立的限制,任何一张在场都要装策略。{@link hasOperatorGateIntent} 的名单三格就是本函数(同一判据,不写两份)。
26
+ */
27
+ export declare function hasApprovalListIntent(config: Pick<GateIntentView, "approvalRequire" | "approvalDeny" | "approvalNeverAuto">): boolean;
28
+ /**
29
+ * 运维**是否表达了门意图** —— 单用户 allow-all 基线只在"零门意图"时才允许铺开,
11
30
  * 所以这个谓词漏一格 = 那一格的意图被 allow-all 静默吞掉。
12
31
  *
13
32
  * 🔴 由来(2026-07-31 缝合审):这判据本来是 `main.ts` 里的一行内联表达式,只枚举了
@@ -20,26 +39,31 @@ import { type ExecutionLane } from "./execution-lane-caps.js";
20
39
  * 判定挪到**与 inv#2 同一个文件**就是为了这个:两者再想漂开,得有人同时改这两段。
21
40
  * 新增任何"门意图"配置项时,这里必须同步加一格(下面的表驱动测试会点名漏的那格)。
22
41
  */
23
- export declare function hasOperatorGateIntent(config: {
24
- approvalRequire: readonly string[];
25
- approvalDeny: readonly string[];
26
- approvalNeverAuto: readonly string[];
27
- durableApproval: boolean;
28
- durableApprovalSource: PostureSource;
42
+ export declare function hasOperatorGateIntent(config: GateIntentView): boolean;
43
+ /**
44
+ * 单用户 turnkey 的 **auto-accept 基线**适用吗 —— 单用户(未开 `REQUIRE_PRINCIPAL`)∧ 运维零门意图。
45
+ *
46
+ * 🔴 S-668 波 2 ③:**每请求现算**,不是 boot 常量。判据的输入 `approvalRequire` 自本批起在进程内会被中心
47
+ * 热换;boot 期算好的一个布尔会让「名单从空变非空」之后 allow-all 基线仍挂在每个新任务上 —— 运维刚表达的
48
+ * 门意图被基线静默吞掉([ref] 方向)。所以消费点(`boot/resolve-spec.ts` 每个任务、stage-07 的 boot 姿态行、
49
+ * run-local 的补偿 warn)一律调本函数,谁也不持有它的快照。
50
+ */
51
+ export declare function singleUserAutoAcceptBaselineApplies(config: GateIntentView & {
52
+ readonly requirePrincipal: boolean;
29
53
  }): boolean;
30
54
  /**
31
- * 门意图不可服务=boot 拒启(全窗复审 D3-F1,HIGH;与 env 墓碑族同谱系)。
32
- * v4.3.0 的轮询门形(APPROVAL_REQUIRE + DB backend + DURABLE_APPROVAL 未设)在 5.0.0 只剩 core 每任务
33
- * warn——**原先拦、现在放**(方向反转)。「留 undefined 让 core warn」对新配置是曝露误配,对升级存量是
34
- * 门静默消失,后者必须响亮。放这文件与 hasOperatorGateIntent/inv#2 同源(意图判据再漂要同时改两段)。
55
+ * 门意图**不可服务**的判据 —— 返回拒因(可服务 ⇒ `undefined`)。**全仓唯一一份**,两处调用、处置不同:
56
+ * · **boot**(`boot/stage-07-capability-layer.ts`):对装配完的整份 config 判,拒因 ⇒ **拒启**(全窗复审 D3-F1,
57
+ * HIGH;与 env 墓碑族同谱系)。v4.3.0 的轮询门形(APPROVAL_REQUIRE + DB backend + DURABLE_APPROVAL 未设)在
58
+ * 5.0.0 只剩 core 每任务 warn——**原先拦、现在放**(方向反转)。「留 undefined 让 core warn」对新配置是
59
+ * 曝露误配,对升级存量是门静默消失,后者必须响亮。
60
+ * · **热路径**(S-668 波 2 ④,`config-center/apply-effective.ts` 的审批组 stage):对**候选视图**
61
+ * (活配置 + 候选名单)判,拒因 ⇒ 审批组**整组拒**、活配置零变化、世代账停旧代。进程在跑,拒启不是选项;
62
+ * 而放行一份「没有 checkpoint 店可停车」的名单 = 单用户 auto-accept 基线仍在、名单静默不生效。
63
+ * `checkpointStorePresent` 是**部署事实**(本进程建没建出 checkpoint 店),不在 `ServiceConfig` 里 ⇒ 调用方显式递。
64
+ * 放这文件与 hasOperatorGateIntent/inv#2 同源(意图判据再漂要同时改两段)。
35
65
  */
36
- export declare function assertGateIntentServiceable(config: {
37
- approvalRequire: readonly string[];
38
- approvalDeny: readonly string[];
39
- approvalNeverAuto: readonly string[];
40
- durableApproval: boolean;
41
- durableApprovalSource: PostureSource;
42
- }, checkpointStorePresent: boolean): void;
66
+ export declare function gateIntentUnserviceableReason(config: GateIntentView, checkpointStorePresent: boolean): string | undefined;
43
67
  /**
44
68
  * S-602 C3 —— 「**无应答面**」两条 boot warn 的判据与文案(纯函数;stage-07 只负责打日志)。
45
69
  *
@@ -85,8 +109,8 @@ export declare function buildNoResponderBootWarns(input: {
85
109
  * 为什么在 **boot** 判而不是 resolve 时判:两个入参都是**部署级**的(entitlement 源在不在、durable 门开
86
110
  * 不开),per-principal 的那一半改变不了结论;放 resolve 里就得自己造去重,而 boot 天然只跑一次。
87
111
  *
88
- * 纯函数(返回一行或 undefined,不自己写日志)= 红先测得动;调用点在 main.ts 的
89
- * {@link assertGateIntentServiceable} 之后(同一段门装配)。
112
+ * 纯函数(返回一行或 undefined,不自己写日志)= 红先测得动;调用点在 stage-07 的
113
+ * {@link gateIntentUnserviceableReason} 拒启判之后(同一段门装配)。
90
114
  */
91
115
  export declare function buildForceDurableGateInertNotice(input: {
92
116
  /** center per-principal entitlement 源在不在(`centerEntitlementSourceWired` 的结果 —— 判据单源在那里)。 */
package/dist/approval.js CHANGED
@@ -3,19 +3,27 @@ import { operatorAsk } from "./operator-ask.js";
3
3
  import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
4
4
  import { isExplicitPosture } from "./posture-source.js";
5
5
  import { handsOnWorkerFilePlane } from "./execution-lane-caps.js";
6
- export function hasOperatorGateIntent(config) {
6
+ export function hasApprovalListIntent(config) {
7
7
  return (config.approvalRequire.length > 0 ||
8
8
  config.approvalDeny.length > 0 ||
9
- config.approvalNeverAuto.length > 0 ||
9
+ config.approvalNeverAuto.length > 0);
10
+ }
11
+ export function hasOperatorGateIntent(config) {
12
+ return (hasApprovalListIntent(config) ||
10
13
  (config.durableApproval && isExplicitPosture(config.durableApprovalSource)));
11
14
  }
12
- export function assertGateIntentServiceable(config, checkpointStorePresent) {
15
+ export function singleUserAutoAcceptBaselineApplies(config) {
16
+ return config.requirePrincipal !== true && !hasOperatorGateIntent(config);
17
+ }
18
+ export function gateIntentUnserviceableReason(config, checkpointStorePresent) {
13
19
  if (!hasOperatorGateIntent(config) || checkpointStorePresent)
14
- return;
20
+ return undefined;
15
21
  if (!config.durableApproval) {
16
- throw new Error("APPROVAL_REQUIRE/APPROVAL_DENY/APPROVAL_NEVER_AUTO are set but the durable gate is off — since 5.0.0 the poll lane is retired and durable is the ONLY gate. Set DURABLE_APPROVAL=true (with a DB/file backend that has a checkpoint face), or remove the APPROVAL_* lists to run ungated on purpose.");
22
+ return ("an approval list is in force (APPROVAL_REQUIRE / APPROVAL_DENY / APPROVAL_NEVER_AUTO, or the center's governance.approvalRequire) but the durable gate is off — " +
23
+ "since 5.0.0 the poll lane is retired and durable is the ONLY gate, so the list would never be enforced. Set DURABLE_APPROVAL=true (with a DB/file backend that has a checkpoint face), " +
24
+ "or remove the approval lists to run ungated on purpose.");
17
25
  }
18
- throw new Error("DURABLE_APPROVAL=true but no checkpoint store is available — the configured DB backend has no checkpoint face (or no backend is configured). Wire a MySQL-protocol/PostgreSQL/file backend, or unset DURABLE_APPROVAL.");
26
+ return "DURABLE_APPROVAL=true but no checkpoint store is available — the configured DB backend has no checkpoint face (or no backend is configured). Wire a MySQL-protocol/PostgreSQL/file backend, or unset DURABLE_APPROVAL.";
19
27
  }
20
28
  export function buildNoResponderBootWarns(input) {
21
29
  if (input.approvalFaceWired)
@@ -8,6 +8,7 @@ import { type ModelKeyRef } from "../key-resolver.js";
8
8
  import type { Logger } from "../observability/logger.js";
9
9
  import type { Metrics } from "../observability/metrics.js";
10
10
  import type { UnpricedCeilingVerdict } from "./unpriced-ceiling-verdict.js";
11
+ import type { ConsolidationSeatWatch } from "./memory-consolidation.js";
11
12
  import { type RestartSignal } from "../config-center/facade.js";
12
13
  import { type ConfigApplyLedgerView } from "../config-center/apply-ledger.js";
13
14
  import type { LimitSync } from "./limit-sync.js";
@@ -43,19 +44,25 @@ export interface ConfigCenterRuntime {
43
44
  /** 60s refresh cadence + boot-deferred 到货续接。
44
45
  * `limitSync`([ref] 批1)= 恒构造的两只限额座的换代口:commit 之后**零 await 内**推一把,与 pricing /
45
46
  * keyResolver 那两件派生重建同族(缺席 = 这条腿本部署没装配,例如 run-local)。
46
- * `swapRunnerDeps`(core 7.10.0 `Runner.swapDeps` 一扇门;5.49 起的 `swapModels` 口在 7.10.0 退役、
47
- * 本参数同语义改名)= **可换席**的换装口:模型面 plane 变更候选在 commit 前喂给全部 boot Runner
48
- * 原子换代(在场 ⇒ models-tiers defer/restart 臂退役,缺席 ⇒ 旧 defer 行为逐字保留);S-174 起
49
- * read **face** 席也走同一只函数(见 refresh 拍里的 `applyCenterReadFace` 调用点)。
47
+ * `swapRunnerDeps`(core 7.10.0 `Runner.swapDeps` 一扇门)= **可换席**的换装口,**必填**:模型面 plane
48
+ * 变更候选在 commit 前喂给全部 boot Runner 原子换代;S-174 起 read **face** 席也走同一只函数(见 refresh
49
+ * 拍里的 `applyLiveReadFace`)。S-668 波 0 起必填 —— 它缺席的那一形(模型面整面 defer 到重启 + `models-tiers`
50
+ * 重启片)在生产上从来到不了(stage-08 唯一调用点恒传),随片一起退役;要刷新环就要有 Runner 可换。
50
51
  * `ceilingVerdict`(S-232)= 「配了 $ 天花板但目录未定价」这条判决的活载体,与 `limitSync` **同族同序**
51
52
  * (commit 之后、零 await 内重判一次;判决不翻转就静默)。缺席 = 这条腿本部署没装配(run-local 一次性
52
- * CLI 之后不再有刷新拍,它继续用纯判据判一次)。 */
53
+ * CLI 之后不再有刷新拍,它继续用纯判据判一次)。
54
+ * `consolidationSeat`(S-668 波 0,过渡)= 整理驱动席的两侧读数口,喂 `memory-consolidation` 重启片;
55
+ * 缺席 = 阀门关着(没有 boot 烤死的席)。
56
+ * `checkpointStorePresent`(S-668 波 2,**必填**)= 部署事实「本进程建没建出 checkpoint 店」(stage-06 的产物,
57
+ * 不在 ServiceConfig 里)。审批名单转热之后,热腿要用它判 ④「门意图不可服务」(与 boot 拒启同一只判据);
58
+ * 必填 = 刷新环不可能在不知道这件事的情况下热应用审批名单。 */
53
59
  startRefreshLoop(a: {
54
- runnerTierFrozen: boolean;
55
60
  pricing: ReturnType<typeof buildPricing>;
56
61
  limitSync?: LimitSync;
57
- swapRunnerDeps?: (next: SwappableDeps) => void;
62
+ swapRunnerDeps: (next: SwappableDeps) => void;
58
63
  ceilingVerdict?: UnpricedCeilingVerdict;
64
+ consolidationSeat?: ConsolidationSeatWatch;
65
+ checkpointStorePresent: boolean;
59
66
  }): void;
60
67
  /** [ref]-2:停刷新环(hardShutdown 收尾链;幂等,未起环时 no-op)。 */
61
68
  stopRefreshLoop(): void;
@@ -78,7 +85,8 @@ export interface ConfigCenterRuntime {
78
85
  modelReady(): boolean;
79
86
  /** /health.restart(orchestrator 自动重启信号)。 */
80
87
  restartState(): RestartSignal | undefined;
81
- /** /health.modelPlaneDeferred(codex R18 卡死的 plane,无 next-boot handoff)。 */
88
+ /** /health.modelPlaneDeferred:被扣着、还没有 next-boot handoff 的候选(codex R18 起的状态机)。S-668 波 0 起
89
+ * 模型面不再 defer,键名保留,今天承载的是 durable LKG 发布债与 skills 债(见 `planeDeferredNoHandoff`)。 */
82
90
  planeDeferredState(): {
83
91
  version: number;
84
92
  since: number;