@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
@@ -1,3 +1,13 @@
1
+ export const CONFIG_APPLY_GROUPS = ["limitsHttp", "approvalGate", "modelPlane"];
2
+ export function refusedGroups(report) {
3
+ return CONFIG_APPLY_GROUPS.flatMap((group) => {
4
+ const r = report.groups[group].rejected;
5
+ return r ? [{ group, keys: r.keys, reason: r.reason }] : [];
6
+ });
7
+ }
8
+ export function modelPlaneCommitted(report) {
9
+ return report !== undefined && report.groups.modelPlane.rejected === undefined;
10
+ }
1
11
  export function createConfigApplyLedger(now = () => Date.now()) {
2
12
  let targetOrd = 0;
3
13
  let appliedOrd = 0;
@@ -6,6 +16,21 @@ export function createConfigApplyLedger(now = () => Date.now()) {
6
16
  const rejectedOrds = new Set();
7
17
  let divergedSince;
8
18
  const groups = {};
19
+ const nextGroupState = (prev, r, version) => {
20
+ if (r.rejected) {
21
+ return {
22
+ appliedVersion: prev?.appliedVersion ?? 0,
23
+ ...(prev?.deferredKeys ? { deferredKeys: prev.deferredKeys } : {}),
24
+ lastRejected: { keys: [...r.rejected.keys], reason: r.rejected.reason, version, at: now() },
25
+ ...(r.rejected.restartReason ? { deferredKeys: r.rejected.keys.map((key) => ({ key, reason: r.rejected.restartReason })) } : {}),
26
+ };
27
+ }
28
+ return {
29
+ appliedVersion: version,
30
+ ...(r.deferred && r.deferred.length > 0 ? { deferredKeys: r.deferred.map((d) => ({ key: d.key, reason: d.reason })) } : {}),
31
+ ...(prev?.lastRejected ? { lastRejected: prev.lastRejected } : {}),
32
+ };
33
+ };
9
34
  const reconcile = () => {
10
35
  if (targetOrd === appliedOrd)
11
36
  divergedSince = undefined;
@@ -32,28 +57,13 @@ export function createConfigApplyLedger(now = () => Date.now()) {
32
57
  reconcile();
33
58
  },
34
59
  recordApply(report) {
35
- const prev = groups.limitsHttp;
36
60
  const ord = report.ordinal ?? report.version;
37
- if (report.limits.rejected)
61
+ if (refusedGroups(report).length > 0)
38
62
  rejectedOrds.add(ord);
39
63
  else
40
64
  rejectedOrds.delete(ord);
41
- if (report.limits.rejected) {
42
- groups.limitsHttp = {
43
- appliedVersion: prev?.appliedVersion ?? 0,
44
- ...(prev?.deferredKeys ? { deferredKeys: prev.deferredKeys } : {}),
45
- lastRejected: { keys: [...report.limits.rejected.keys], reason: report.limits.rejected.reason, version: report.version, at: now() },
46
- ...(report.limits.rejected.restartReason
47
- ? { deferredKeys: report.limits.rejected.keys.map((key) => ({ key, reason: report.limits.rejected.restartReason })) }
48
- : {}),
49
- };
50
- return;
51
- }
52
- groups.limitsHttp = {
53
- appliedVersion: report.version,
54
- ...(report.limits.deferred && report.limits.deferred.length > 0 ? { deferredKeys: report.limits.deferred.map((d) => ({ key: d.key, reason: d.reason })) } : {}),
55
- ...(prev?.lastRejected ? { lastRejected: prev.lastRejected } : {}),
56
- };
65
+ for (const g of CONFIG_APPLY_GROUPS)
66
+ groups[g] = nextGroupState(groups[g], report.groups[g], report.version);
57
67
  },
58
68
  view() {
59
69
  const stale = divergedSince !== undefined ? now() - divergedSince : undefined;
@@ -25,14 +25,20 @@
25
25
  * reference. Both brains re-resolve `model.baseUrl || config.baseUrl` inside `buildRequest()` on every
26
26
  * call (core 2.1.0 `brain/openai.js` + `brain/anthropic.js`), so a moved gateway takes effect on the
27
27
  * next task with no restart.
28
- * - the same plane under a **tier-frozen** Runner — deferred, not hot: core expands a PRIVATE catalog copy
29
- * at construction, so `main` defers the whole plane and the `models-tiers` restart slice signals.
30
- * - **`degrade-route`** — the one genuinely boot-frozen catalog consumer left: reactive degrade
28
+ * - tiers (and the whole plane under an active tier table) — **HOT** as well: core expands a PRIVATE catalog
29
+ * copy at Runner construction, and the refresh loop swaps every changed plane into all boot Runners before
30
+ * commit (`Runner.swapDeps`, core 7.10.0; production always wires it). The old "tier-frozen ⇒ defer to restart"
31
+ * arm and its `models-tiers` restart slice retired in S-668 波 0 — `/v1/models` and tier-word routing move on
32
+ * the same tick.
33
+ * - **`degrade-route`** — one of the two genuinely boot-frozen catalog consumers left (the other is the
34
+ * memory-consolidation driver seat, last bullet): reactive degrade
31
35
  * (`MODEL_DEGRADE_REACTIVE`) bakes the target Model + its gateway + its key into the brain composition
32
36
  * at boot. It cannot be hot-applied without rebuilding the brain, so it is registered on the existing
33
37
  * restartRequired /health contract instead (see `restart-signal.ts`). Fail-loud beats serving a stale
34
38
  * gateway silently on the rate-limit path.
35
- * - skills/mcp/scenarios/runtime-gates — restart-to-apply by construction (baked into the boot wiring).
39
+ * - skills(+plugins)/mcp/a2a/scenarios — restart-to-apply by construction (baked into the boot
40
+ * wiring); so is the memory-consolidation driver's model seat (`memory-consolidation` slice, S-668 波 0).
41
+ * (`governance.approvalRequire` left this list in S-668 波 2 — its own staged group, hot on both legs.)
36
42
  *
37
43
  * ([ref] A13, internal-lossless) This module is now a FACADE: the implementation lives
38
44
  * alongside it in `src/config-center/` split by responsibility group (HTTP client / EffectiveConfig
@@ -42,8 +48,8 @@
42
48
  * importers to update, which was done in the same commit.
43
49
  */
44
50
  export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
45
- export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, } from "./apply-effective.js";
46
- export { restartReasons, planeHasActiveTiers, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type RestartSignal, } from "./restart-signal.js";
51
+ export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, buildEnvOnlyModelPlane, } from "./apply-effective.js";
52
+ export { restartReasons, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type RestartSignal, } from "./restart-signal.js";
47
53
  export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
48
54
  export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";
49
55
  export type { CenterSkillManifest, CenterMcpServer, CenterA2aPeer, CenterModel, CenterReadFace, EffectiveConfig, ExecutionRuling, SessionMirrorRuling, } from "./types.js";
@@ -1,6 +1,6 @@
1
1
  export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
2
- export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, } from "./apply-effective.js";
3
- export { restartReasons, planeHasActiveTiers, modelPlaneChanged, } from "./restart-signal.js";
2
+ export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf, buildEnvOnlyModelPlane, } from "./apply-effective.js";
3
+ export { restartReasons, modelPlaneChanged, } from "./restart-signal.js";
4
4
  export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
5
5
  export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
6
6
  //# sourceMappingURL=facade.js.map
@@ -6,7 +6,7 @@
6
6
  * 键 → 组 → 换代手法 → **参数化用例 ID**(不是文件名——文件名粒度挡不住「文件在、用例没了」)。
7
7
  *
8
8
  * 机器门在 test/config-hot/batch1-hot-keys-registry.test.ts:登记为热却没有三断言用例 = 红;
9
- * 表的键集与 stage 真正消费的键集(`BATCH1_LIMIT_KEYS`)不等 = 红。
9
+ * 表的键集与 stage 真正消费的键集(`BATCH1_LIMIT_KEYS` ∪ `APPROVAL_GROUP_KEYS`)不等 = 红。
10
10
  *
11
11
  * ⚠️ 本表**只登记已落地的批**。批2-4 的键在各自批次里追加(设计稿 §6),不提前写进来 —— 一个「登记为热
12
12
  * 但没接线」的条目正是本表要消灭的那种谎。
@@ -27,8 +27,8 @@ export interface HotKeyEntry {
27
27
  readonly key: keyof ServiceConfigFlat;
28
28
  readonly group: keyof ServiceConfigGroups;
29
29
  readonly form: HotKeyForm;
30
- /** 落地批次(设计稿 §6 的批号)。 */
31
- readonly batch: 1;
30
+ /** 落地批次:1 = [ref] 批1(limitsHttp 组);2 = S-668 波 2(审批组 —— [ref] §6 原排的「批2」)。 */
31
+ readonly batch: 1 | 2;
32
32
  readonly cases: HotKeyCases;
33
33
  }
34
34
  /**
@@ -57,6 +57,13 @@ export declare const DEFERRED_LIMIT_KEYS: ReadonlyArray<{
57
57
  readonly restartReason: string;
58
58
  readonly why: string;
59
59
  }>;
60
- /** 热键表 = 批1 消费键集**减去**显式 deferred 的那些(表只登记真的热的键)。 */
60
+ /**
61
+ * S-668 波 2([ref] §3)—— **审批组**真正接线的键:`apply-effective.ts` 的 `stageApprovalGate` 消费、报告(世代账
62
+ * `groups.approvalGate`)与拒绝(拒因的键名)都取这一张表。换代手法 A′:消费点本就逐请求现读活 config(审批基线 /
63
+ * SendUserFile 子代闸 / 会话与记忆读面 / parked 赎回),leader 腿与单用户基线改成现读之后,热只差 refresh 腿的写口。
64
+ */
65
+ export declare const APPROVAL_GROUP_KEYS: readonly ["approvalRequire"];
66
+ export type ApprovalGroupKey = (typeof APPROVAL_GROUP_KEYS)[number];
67
+ /** 热键表 = 批1 消费键集**减去**显式 deferred 的那些 + 审批组(表只登记真的热的键)。 */
61
68
  export declare const HOT_KEYS_REGISTRY: readonly HotKeyEntry[];
62
69
  //# sourceMappingURL=hot-keys-registry.d.ts.map
@@ -29,11 +29,15 @@ export const DEFERRED_LIMIT_KEYS = [
29
29
  },
30
30
  ];
31
31
  const DEFERRED_KEYS = new Set(DEFERRED_LIMIT_KEYS.map((d) => d.key));
32
- export const HOT_KEYS_REGISTRY = BATCH1_LIMIT_KEYS.filter((key) => !DEFERRED_KEYS.has(key)).map((key) => ({
33
- key,
34
- group: "limitsHttp",
35
- form: (B_SETTER_KEYS.includes(key) ? "B-setter" : "A-prime"),
36
- batch: 1,
37
- cases: cases(key),
38
- }));
32
+ export const APPROVAL_GROUP_KEYS = ["approvalRequire"];
33
+ export const HOT_KEYS_REGISTRY = [
34
+ ...BATCH1_LIMIT_KEYS.filter((key) => !DEFERRED_KEYS.has(key)).map((key) => ({
35
+ key,
36
+ group: "limitsHttp",
37
+ form: (B_SETTER_KEYS.includes(key) ? "B-setter" : "A-prime"),
38
+ batch: 1,
39
+ cases: cases(key),
40
+ })),
41
+ ...APPROVAL_GROUP_KEYS.map((key) => ({ key, group: "approval", form: "A-prime", batch: 2, cases: cases(key) })),
42
+ ];
39
43
  //# sourceMappingURL=hot-keys-registry.js.map
@@ -1,11 +1,13 @@
1
1
  import { type EffectiveReadWarning, type EntitlementRuntimeCaps } from "@sema-agent/settings-schema";
2
2
  import type { ScenarioRuling } from "../capabilities/scenarios.js";
3
3
  import type { EffectiveConfig, ExecutionRuling } from "./types.js";
4
+ import { type DroppedConfigEntry } from "./read-warnings.js";
4
5
  import { type CenterEndpoint } from "./center-request.js";
5
6
  /** GET the effective config (Bearer + ETag). null = 304 (unchanged). Throws on transport/HTTP error, and on a
6
7
  * payload that is not an effective config at all(非对象 / version 非有限数 / gate 域坏形——[ref] 裁B)。
7
8
  * `domainErrors`:坏 catalog 域(schema default 已落)逐域单列——与本地腿 `FetchEffectiveResult` 同键同义,
8
- * boot/refresh 的候选门直接消费。
9
+ * boot/refresh 的候选门直接消费。`droppedEntries`(S-704 ②):schema 逐条丢掉的条目(坐标 + 字段诊断)——
10
+ * **不**进候选门,调用方响亮点名、其余照用(分面单点 `splitFoldedReadWarnings`,与本地腿同一只)。
9
11
  *
10
12
  * `readWarnings`:契约 `onWarning` 流的**原值**(未折叠),与本地腿同键同义。折叠是**有损**的——
11
13
  * `hosts-grandfathered` / `legacy-governance-lifted` 两员按律折成 `null`(值已被收编接受,见
@@ -24,6 +26,7 @@ export declare function fetchEffective(center: CenterEndpoint, etag: string | un
24
26
  domain: string;
25
27
  error: string;
26
28
  }>;
29
+ droppedEntries?: DroppedConfigEntry[];
27
30
  readWarnings: readonly EffectiveReadWarning[];
28
31
  credentialIdentity: string;
29
32
  } | null>;
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readEffectiveWire, PrincipalCapsWire } from "@sema-agent/settings-schema";
3
3
  import { centerPromptsFromEffective } from "../prompts-domain-validate.js";
4
- import { foldEffectiveReadWarning } from "./read-warnings.js";
4
+ import { splitFoldedReadWarnings, withDroppedIdentities } from "./read-warnings.js";
5
5
  import { centerFetch } from "./center-request.js";
6
6
  import { answerStillCurrent } from "../center-credential.js";
7
7
  const CENTER_PULL_TIMEOUT_MS = 8000;
@@ -12,20 +12,25 @@ export async function fetchEffective(center, etag, fetchImpl = fetch, worker) {
12
12
  if (!res.ok)
13
13
  throw new Error(`config-center HTTP ${res.status}`);
14
14
  const warnings = [];
15
- const wire = readEffectiveWire(await res.json(), (w) => warnings.push(w));
15
+ const body = await res.json();
16
+ const wire = readEffectiveWire(body, (w) => warnings.push(w));
16
17
  const centerPrompts = centerPromptsFromEffective(wire);
17
18
  if (centerPrompts === undefined)
18
19
  delete wire.prompts;
19
20
  else
20
21
  wire.prompts = centerPrompts;
21
- const domainErrors = [];
22
- for (const w of warnings) {
23
- const f = foldEffectiveReadWarning(w);
24
- if (f !== null)
25
- domainErrors.push(f);
26
- }
27
- const effective = wire;
28
- return { effective, etag: res.headers.get("etag") ?? undefined, readWarnings: warnings, credentialIdentity: identity, ...(domainErrors.length > 0 ? { domainErrors } : {}) };
22
+ const rawModels = typeof body === "object" && body !== null && !Array.isArray(body) ? body.models : undefined;
23
+ const parsedModels = wire.models?.models;
24
+ const { domainErrors, droppedEntries, droppedIdentities } = splitFoldedReadWarnings(warnings, { raw: rawModels, parsed: Array.isArray(parsedModels) ? parsedModels : undefined });
25
+ const effective = withDroppedIdentities(wire, droppedIdentities);
26
+ return {
27
+ effective,
28
+ etag: res.headers.get("etag") ?? undefined,
29
+ readWarnings: warnings,
30
+ credentialIdentity: identity,
31
+ ...(domainErrors.length > 0 ? { domainErrors } : {}),
32
+ ...(droppedEntries.length > 0 ? { droppedEntries } : {}),
33
+ };
29
34
  }
30
35
  export async function fetchPrincipalCaps(center, principal, etag, fetchImpl = fetch, worker) {
31
36
  const raw = await requestPrincipalView(center, principal, etag, fetchImpl, worker);
@@ -10,20 +10,129 @@
10
10
  * 截断:schema 的键列表有界(64),超界带 `truncatedFrom`(真实总数)——文案如实带上,消费方永远不把有界列表当穷举。
11
11
  * 词表是闭集:`switch` 穷举 `kind`,schema 加新 kind 这里编译红,不会静默漏臂。
12
12
  *
13
- * **归族只有一条判据**(下游只有一个消费者——候选门:非空 ⇒ 这棵树整拒、保 LKG):
14
- * 运维**写了而没生效**的 ⇒ 候选门错误;值**已被收编接受**(写了、生效了、只是换了个位置或被夹住)⇒ 不入诊断面。
15
- * settings-schema 2.0.0 的两员新告警各自入族,没有新开第三档:`entry-dropped`(逐条容错丢掉的那条条目——
16
- * 运维写下的模型/服务器没进目录,与 `domain-defaulted` 是同一件事的细/粗两档)归前者;
17
- * `legacy-governance-lifted`(0.5 期 runtime 文档里的治理三闸被抬进 governance 域)归后者——那几个键**生效了**
13
+ * **归族判据两问**(下游两个消费者:候选门 = 非空 ⇒ 这棵树整拒、保 LKG;逐条丢弃面 = 响亮点名、其余照用):
14
+ * ① 值**已被收编接受**(写了、生效了、只是换了个位置或被夹住)⇒ 不入诊断面(`null`);
15
+ * ② 写了没生效的,按**失效的颗粒**分:丢的是**一条条目**(`entry-dropped`)⇒ `scope:"entry"` —— 这条条目单独
16
+ * 不在,同域其余条目照用,候选**不**因它整拒;丢的是一个域 / 一组键 / 一个文件 ⇒ `scope:"domain"` —— 候选门错误。
17
+ *
18
+ * 🔴 S-704 ②(2026-09-24,凭据轴):此前 `entry-dropped` 归「候选门错误」(settings-schema 2.0.0 提货时的归族),于是
19
+ * schema 的**逐条容错**在本仓的 refresh / 迟到 boot 两条腿上被**还原成整份拒** —— 目录里一条 `contextWindow:"abc"`
20
+ * 就让整份目录停在上一代;而上一代若还是 env(boot 拉取失败 / 超窗且无 LKG),env 模型就一直服务、env 钥匙一直被送到
21
+ * 网关缺省端点(红先实测:`config_candidate_rejected` + env 模型继续服务)。粒度对了规则反而更少:颗粒是条目的失效
22
+ * 留在条目上,只有颗粒更粗的失效才动候选。
23
+ * `legacy-governance-lifted`(0.5 期 runtime 文档里的治理三闸被抬进 governance 域)归①——那几个键**生效了**
18
24
  * (`buildEffective` 用抬升结果解析 governance),把它折成错误会让一份合法的 0.5 期存量配置每一次 refresh 都被
19
25
  * 候选门整拒,而它在 1.10.0 上零告警零 domainErrors(亲验读数)。兼容臂的关窗观测归属主(settings-schema 的
20
26
  * `onWarning` 流),不借候选门这条带副作用的通道传信息。
21
27
  */
22
- import { type EffectiveReadWarning } from "@sema-agent/settings-schema";
28
+ import { type ConfigPath, type EffectiveReadWarning } from "@sema-agent/settings-schema";
29
+ /** 折叠产物:`scope` 决定消费方做什么 —— `"domain"` 进候选门(`domainErrors`),`"entry"` 进逐条丢弃面(`droppedEntries`)。 */
23
30
  export type FoldedReadWarning = {
31
+ readonly scope: "domain";
32
+ readonly domain: string;
33
+ readonly error: string;
34
+ } | {
35
+ readonly scope: "entry";
36
+ readonly domain: string;
37
+ readonly entry: string;
38
+ readonly error: string;
39
+ };
40
+ /** 逐条丢弃面的一行(两条读腿的 `droppedEntries` 同形):`entry` = 被丢那条的出门形坐标(`models.1`),`error` = 哪些字段、为什么。 */
41
+ export type DroppedConfigEntry = {
24
42
  readonly domain: string;
43
+ readonly entry: string;
25
44
  readonly error: string;
26
45
  };
46
+ /**
47
+ * zod 形的错误 → 一行可读的字段诊断(`contextWindow: expected number, received string; api: custom`)。
48
+ * **只读**每条 issue 的 `path`(键名)与 `code`,`invalid_type` 臂另读 `expected` / `received`(两者都是**类型词**)——
49
+ * 刻意不读 `message`(自定义 refine 的 message 可以回声值)与 `invalid_enum_value` 等臂的 `received`(那是原值)。
50
+ * 读不出 issues 形 ⇒ `undefined`(调用方退回既有文案)。模型面 stage 的逐条判据与本折叠点共用这一只(一个刻度)。
51
+ */
52
+ export declare function describeSchemaIssues(error: unknown): string | undefined;
53
+ /**
54
+ * zod 形错误 → 坏字段的**键路径**(去重保序;`(entry)` = 整条)。与 {@link describeSchemaIssues} 同一只读法(只读 `path`),
55
+ * 不含值。模型面 stage 的逐条判据(`judgeCatalogEntry`)与本折叠点的被丢条目身份共用这一只 —— 一个刻度。
56
+ */
57
+ export declare function describeSchemaIssueFields(error: unknown): string[];
27
58
  /** null = 不入诊断面(`hosts-grandfathered` / `legacy-governance-lifted`:值已被收编接受,与本地店同口径)。 */
28
59
  export declare function foldEffectiveReadWarning(w: EffectiveReadWarning): FoldedReadWarning | null;
60
+ /**
61
+ * 🔴 S-712 —— 一条被 schema 逐条丢掉的**启用**目录条目的身份(模型面墓碑的来源;载体 = `EffectiveConfig.models.droppedIdentities`,
62
+ * 随 effective 走 —— 盘上 LKG 从同一个对象落盘,重启从 LKG 起服墓碑不丢)。
63
+ *
64
+ * 为什么要交出身份:条目被丢之后它的名字从目录里消失,而档位增广(core `expandTiers`)会把一个**空出来的**档位词 / CC 别名
65
+ * (条目恰好叫 `sonnet` / `pro` …)重新铸到另一条条目上 —— 显式点它的请求与点名它的角色绑定就这样被静默换了目的地
66
+ * (gpt-6 腿 F2 / F3,真复现)。身份进 `modelRefusals` 之后,不变量 `refused-names-not-minted` 才判得到它。
67
+ *
68
+ * 🔐 纯身份,不含值:`name` / `id` / `provider` 只取**字符串**(条目自己写的词,本就进日志与拒因句),`fields` 是坏字段的
69
+ * **键路径**,`issues` 是 {@link describeSchemaIssues} 的一行(键名 + 类型词)—— 被丢条目的原值(可能是一段贴错位置的凭据)
70
+ * 绝不进载体、日志、LKG 与 400 拒因句。
71
+ */
72
+ export interface DroppedModelIdentity {
73
+ /** 出门形坐标(`models.1`),与同一次读的 `droppedEntries[].entry` 同一串。 */
74
+ readonly entry: string;
75
+ /** 条目写的 `name`。**缺席 = 身份证明不了**(只剩一形:原文档与本次解析对不上 / 坐标在原文档里不存在,见 {@link droppedEntryIdentity})
76
+ * ⇒ 模型面整组拒:证明不了的名字无从证明它不被增广铸出。原文档那一格读到了却没有字符串名的行**不进**载体(没有可保留的名字,
77
+ * 与停用同处置 —— 7.101.0 验修轮 V-2)。 */
78
+ readonly name?: string;
79
+ readonly id?: string;
80
+ readonly provider?: string;
81
+ readonly fields: readonly string[];
82
+ readonly issues: string;
83
+ }
84
+ /** 本次读的模型域**原文**与**解析产物**(两腿各自手里都有这两件:远端 = 响应体与 `readEffectiveWire` 输出;本地 = 盘上
85
+ * `config.d/models.json` 与 `FileConfigStore` 输出)。 */
86
+ export interface ModelsDomainSource {
87
+ /** 模型域的原始文档(`{ models: [...], roles, … }`);读不到 ⇒ `undefined`(身份一律证明不了)。 */
88
+ readonly raw: unknown;
89
+ /** 同一次读解析后幸存的条目(schema 逐条丢弃之后的 `models.models`)。 */
90
+ readonly parsed: readonly unknown[] | undefined;
91
+ }
92
+ /**
93
+ * 一条被丢行在原文档里那一格的读数 —— **三态**(7.101.0 验修轮 V-2,规则变小:「读不出」只剩一种含义):
94
+ * · `named`:格读到了、`name` 是字符串 ⇒ 身份已定(启用的铸墓碑,停用的不是);
95
+ * · `nameless`:格读到了、没有字符串 `name`(`null` / 数 / 整格不是对象)⇒ 身份**也**已定:**没有可保留的名字** —— 请求、
96
+ * `@`-mention、角色绑定都点不到一个非字符串名,档位增广也铸不出它 ⇒ 与 `enabled:false` 同处置(不是墓碑、不拒组);
97
+ * · `undefined`(由 {@link droppedEntryIdentity} 答):格读不到 —— 坐标在原文档里不存在;调用方在原文档与本次解析对不上
98
+ * (`sameRead` 假)时根本不来读 ⇒ 两者同是「身份证明不了」,交一条不带 `name` 的载体(模型面据此整组拒)。
99
+ * 此前 `nameless` 被折进「读不出」⇒ 一行 `{ name: null }` 的坏行(哪怕是停用的)就让整个模型面被拒(codex 验修腿 V-2,boot 腿对
100
+ * v7.100.0 的窄回归:那时 boot 对同一文档宽容采纳幸存条目)。
101
+ */
102
+ export type DroppedEntryCell = {
103
+ readonly kind: "named";
104
+ readonly name: string;
105
+ readonly id?: string;
106
+ readonly provider?: string;
107
+ readonly enabled: boolean;
108
+ } | {
109
+ readonly kind: "nameless";
110
+ };
111
+ /**
112
+ * 🔴 S-712 **唯一**的身份读法:按告警坐标(`path`,模型域文档内的段坐标 —— settings-schema 已把它回写成原文档坐标)回读
113
+ * 原文档那一格,答 {@link DroppedEntryCell} 三态之一;坐标在原文档里走不通 ⇒ `undefined`(格读不到)。字符串才算
114
+ * (`name` / `id` / `provider`),`enabled` 只认字面 `false` 为停用。
115
+ * settings-schema 6.1.0 的 `entry-dropped` 自带 `name` / `id` 到货后,本函数改读告警(同一只函数,换数据源),原文档入参随之删除。
116
+ */
117
+ export declare function droppedEntryIdentity(rawDoc: unknown, path: ConfigPath): DroppedEntryCell | undefined;
118
+ /**
119
+ * 两条读腿(远端 `http-client.ts` / 本地 `config-provider.ts`)共用的**分面单点**:折叠产物按 `scope` 分进候选门
120
+ * (`domainErrors`)与逐条丢弃面(`droppedEntries`)。`switch` 穷举 —— 加一种 scope 不补臂 = 编译红,两腿不会各分各的。
121
+ * 文案**未脱敏**:进日志流之前各腿在自己的产出边界上过 `redactConfigError`(与 `domainErrors` 同一道)。
122
+ * S-712:同一次折叠把被丢**模型目录条目**的身份交出(`droppedIdentities`,见 {@link DroppedModelIdentity}),按 {@link DroppedEntryCell}
123
+ * 三态分流:有名且启用 ⇒ 墓碑;停用的、没有字符串名的 ⇒ 不进载体(停用 = 不在目录,与 7.100.0 同义;无名 = 没有可保留的名字);
124
+ * 格读不到 / 原文档对不上 ⇒ 不带 `name` 的载体(证明不了)。坏行本身照样走 `droppedEntries`(坐标 + 字段,不带值)。
125
+ */
126
+ export declare function splitFoldedReadWarnings(warnings: readonly EffectiveReadWarning[], models: ModelsDomainSource): {
127
+ domainErrors: Array<{
128
+ domain: string;
129
+ error: string;
130
+ }>;
131
+ droppedEntries: DroppedConfigEntry[];
132
+ droppedIdentities: DroppedModelIdentity[];
133
+ };
134
+ /** S-712:把本次读交出的被丢身份挂上 effective 的模型域(两腿同一只;空 ⇒ 不挂键)。 */
135
+ export declare function withDroppedIdentities<E extends {
136
+ models?: object;
137
+ }>(effective: E, droppedIdentities: readonly DroppedModelIdentity[]): E;
29
138
  //# sourceMappingURL=read-warnings.d.ts.map
@@ -1,4 +1,33 @@
1
1
  import { formatConfigPath } from "@sema-agent/settings-schema";
2
+ export function describeSchemaIssues(error) {
3
+ const issues = schemaIssuesOf(error);
4
+ if (issues === undefined)
5
+ return undefined;
6
+ return issues
7
+ .map((raw) => {
8
+ const i = (typeof raw === "object" && raw !== null ? raw : {});
9
+ const field = Array.isArray(i.path) && i.path.length > 0 ? i.path.map(String).join(".") : "(entry)";
10
+ const what = i.code === "invalid_type" && typeof i.expected === "string" && typeof i.received === "string"
11
+ ? `expected ${i.expected}, received ${i.received}`
12
+ : typeof i.code === "string"
13
+ ? i.code
14
+ : "invalid";
15
+ return `${field}: ${what}`;
16
+ })
17
+ .join("; ");
18
+ }
19
+ function schemaIssuesOf(error) {
20
+ const issues = typeof error === "object" && error !== null ? error.issues : undefined;
21
+ return Array.isArray(issues) && issues.length > 0 ? issues : undefined;
22
+ }
23
+ export function describeSchemaIssueFields(error) {
24
+ const issues = schemaIssuesOf(error) ?? [];
25
+ const fields = issues.map((raw) => {
26
+ const p = typeof raw === "object" && raw !== null ? raw.path : undefined;
27
+ return Array.isArray(p) && p.length > 0 ? p.map(String).join(".") : "(entry)";
28
+ });
29
+ return [...new Set(fields)];
30
+ }
2
31
  const MAX_ERROR_CHARS = 600;
3
32
  const cap = (s) => s.slice(0, MAX_ERROR_CHARS);
4
33
  const errorText = (e) => (e instanceof Error ? e.message : String(e));
@@ -24,19 +53,100 @@ export function foldEffectiveReadWarning(w) {
24
53
  case "legacy-governance-lifted":
25
54
  return null;
26
55
  case "domain-defaulted":
27
- return { domain: w.domain, error: cap(errorText(w.error)) };
56
+ return { scope: "domain", domain: w.domain, error: cap(errorText(w.error)) };
28
57
  case "entry-dropped":
29
- return { domain: w.domain, error: cap(`entry dropped by domain schema: ${formatConfigPath(w.path)} (${errorText(w.error)})`) };
58
+ return { scope: "entry", domain: w.domain, entry: formatConfigPath(w.path), error: cap(`entry dropped by domain schema: ${describeSchemaIssues(w.error) ?? errorText(w.error)}`) };
30
59
  case "unknown-keys-dropped":
31
- return { domain: w.domain, error: keyList("unknown keys dropped by domain schema: ", w.keys, w.truncatedFrom) };
60
+ return { scope: "domain", domain: w.domain, error: keyList("unknown keys dropped by domain schema: ", w.keys, w.truncatedFrom) };
32
61
  case "unknown-keys-carried":
33
- return { domain: w.domain, error: keyList("unknown keys carried to consumer (spelling?): ", w.keys, w.truncatedFrom) };
62
+ return { scope: "domain", domain: w.domain, error: keyList("unknown keys carried to consumer (spelling?): ", w.keys, w.truncatedFrom) };
34
63
  case "unread-config-file":
35
- return { domain: w.domain ?? "config.d", error: cap(`unread config file ${w.file} (${w.why})`) };
64
+ return { scope: "domain", domain: w.domain ?? "config.d", error: cap(`unread config file ${w.file} (${w.why})`) };
36
65
  default: {
37
66
  const never = w;
38
67
  throw new Error(`unhandled EffectiveReadWarning kind: ${JSON.stringify(never)}`);
39
68
  }
40
69
  }
41
70
  }
71
+ const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
72
+ export function droppedEntryIdentity(rawDoc, path) {
73
+ let at = rawDoc;
74
+ for (const seg of path) {
75
+ if (typeof seg === "number") {
76
+ if (!Array.isArray(at) || !Number.isInteger(seg) || seg < 0 || seg >= at.length)
77
+ return undefined;
78
+ at = at[seg];
79
+ }
80
+ else {
81
+ if (!isRecord(at) || !Object.hasOwn(at, seg))
82
+ return undefined;
83
+ at = at[seg];
84
+ }
85
+ }
86
+ if (!isRecord(at) || typeof at.name !== "string")
87
+ return { kind: "nameless" };
88
+ return { kind: "named", name: at.name, ...(typeof at.id === "string" ? { id: at.id } : {}), ...(typeof at.provider === "string" ? { provider: at.provider } : {}), enabled: at.enabled !== false };
89
+ }
90
+ function isModelEntryDrop(w) {
91
+ return w.kind === "entry-dropped" && w.domain === "models" && w.path.length === 2 && w.path[0] === "models" && typeof w.path[1] === "number";
92
+ }
93
+ function sameRead(source, dropped) {
94
+ const rawModels = isRecord(source.raw) ? source.raw.models : undefined;
95
+ if (!Array.isArray(rawModels) || source.parsed === undefined)
96
+ return false;
97
+ if ([...dropped].some((i) => i >= rawModels.length))
98
+ return false;
99
+ const survivors = rawModels.filter((_, i) => !dropped.has(i));
100
+ return survivors.length === source.parsed.length && survivors.every((s, k) => isRecord(s) && isRecord(source.parsed[k]) && s.name === source.parsed[k].name);
101
+ }
102
+ export function splitFoldedReadWarnings(warnings, models) {
103
+ const domainErrors = [];
104
+ const droppedEntries = [];
105
+ for (const w of warnings) {
106
+ const f = foldEffectiveReadWarning(w);
107
+ if (f === null)
108
+ continue;
109
+ switch (f.scope) {
110
+ case "domain":
111
+ domainErrors.push({ domain: f.domain, error: f.error });
112
+ break;
113
+ case "entry":
114
+ droppedEntries.push({ domain: f.domain, entry: f.entry, error: f.error });
115
+ break;
116
+ default: {
117
+ const never = f;
118
+ throw new Error(`unhandled folded read-warning scope: ${JSON.stringify(never)}`);
119
+ }
120
+ }
121
+ }
122
+ const modelDrops = warnings.filter(isModelEntryDrop);
123
+ const readable = sameRead(models, new Set(modelDrops.map((w) => w.path[1])));
124
+ const droppedIdentities = [];
125
+ for (const w of modelDrops) {
126
+ const unproven = { entry: formatConfigPath(w.path), fields: describeSchemaIssueFields(w.error), issues: describeSchemaIssues(w.error) ?? "(schema issues unreadable)" };
127
+ const cell = readable ? droppedEntryIdentity(models.raw, w.path) : undefined;
128
+ if (cell === undefined) {
129
+ droppedIdentities.push(unproven);
130
+ continue;
131
+ }
132
+ switch (cell.kind) {
133
+ case "nameless":
134
+ break;
135
+ case "named":
136
+ if (cell.enabled)
137
+ droppedIdentities.push({ ...unproven, name: cell.name, ...(cell.id !== undefined ? { id: cell.id } : {}), ...(cell.provider !== undefined ? { provider: cell.provider } : {}) });
138
+ break;
139
+ default: {
140
+ const never = cell;
141
+ throw new Error(`unhandled dropped-entry cell: ${JSON.stringify(never)}`);
142
+ }
143
+ }
144
+ }
145
+ return { domainErrors, droppedEntries, droppedIdentities };
146
+ }
147
+ export function withDroppedIdentities(effective, droppedIdentities) {
148
+ if (droppedIdentities.length === 0 || effective.models === undefined)
149
+ return effective;
150
+ return { ...effective, models: { ...effective.models, droppedIdentities: [...droppedIdentities] } };
151
+ }
42
152
  //# sourceMappingURL=read-warnings.js.map
@@ -1,5 +1,5 @@
1
1
  import type { EffectiveConfig } from "./types.js";
2
- export type RestartSlice = "skills" | "mcp" | "a2a" | "scenarios" | "runtime-gates" | "models-tiers" | "degrade-route" | "read-face";
2
+ export type RestartSlice = "skills" | "mcp" | "a2a" | "scenarios" | "degrade-route" | "read-face" | "memory-consolidation";
3
3
  /** What the CALLER must contribute about boot-baked catalog consumers that live OUTSIDE the Runner — the
4
4
  * slices cannot see them, because they are configured from env (`ServiceConfig`), not from the center's
5
5
  * EffectiveConfig. Absent/empty = that lane is off ⇒ its slice is inert (null on both sides). */
@@ -18,6 +18,18 @@ export interface RestartSliceCtx {
18
18
  * 新增/改址/域整体出现或消失仍签(新 mount / 改址只能 restart 才生效)。缺席/false = 旧语义照签
19
19
  * (fail-closed:没接席的部署里删除向没人承接,restart 是它唯一的生效路径)。 */
20
20
  mcpRevocationLive?: boolean;
21
+ /** S-668 波 0(过渡;**波 1 转热后删**):记忆整理驱动席的两侧读数 —— `baked` = boot 期 core 解析器选中的
22
+ * 那只 Model 的指纹,`current` = 同一只解析器对本拍活配置再解一次的指纹(读法见
23
+ * boot/memory-consolidation.ts `createConsolidationSeatWatch`)。缺席 = 阀门关着(`MEMORY_CONSOLIDATION_DRIVER`
24
+ * 不是 on,或记忆引擎没接线)⇒ 没有 boot 烤死的席 ⇒ `memory-consolidation` 片恒不签。 */
25
+ consolidationSeat?: {
26
+ readonly baked: unknown;
27
+ readonly current: unknown;
28
+ };
29
+ /** S-668 波 0(codex r1 [medium]):插件 host 白名单(env `PLUGINS_ALLOW_HOSTS`,boot 常量)—— 白名单外的声明
30
+ * 装载器整条跳过,它们的变化不是重启理由。生产 `restartCtx` 恒传 `config.pluginsAllowHosts`;缺席(单元调用)
31
+ * = 装载器的出厂名单 `DEFAULT_PLUGIN_ALLOW_HOSTS`。 */
32
+ pluginsAllowHosts?: readonly string[];
21
33
  }
22
34
  /** Structured restart signal an orchestrator consumes (GET /health → `restart`). Change-detected against the
23
35
  * process's BOOT config, NOT presence — so it stays absent when nothing baked-at-boot changed (presence would
@@ -46,18 +58,13 @@ export declare function stableStringify(v: unknown): string;
46
58
  * can auto rolling-restart on a non-empty result WITHOUT a restart loop, because the comparison is always
47
59
  * against boot (a refresh that re-fires the same diff is idempotent, not a fresh trigger). */
48
60
  export declare function restartReasons(boot: EffectiveConfig | undefined, current: EffectiveConfig, ctx?: RestartSliceCtx): RestartSlice[];
49
- /** codex R10 (models-tiers 窗收口): TRUE when the MODEL PLANE (enabled models + active tier table + resolved
50
- * default) of `next` differs from the last-APPLIED plane `prev`. Under a tier-frozen Runner (tiers non-empty at
51
- * construction → private expanded copy) main.ts DEFERS the whole plane mutation until restart when this is true —
52
- * admission (/v1/models, the catalog gates) and the Runner then stay on ONE generation through the restart
53
- * window, so a same-key retarget can never silently route/bill the stale Model object. `prev === undefined`
54
- * (env-boot plane, no EffectiveConfig to compare) is conservatively "changed" — one deferred adoption + restart
55
- * converges. Deliberately NOT gated on tier activity of either side (unlike the restart slice): a frozen Runner
56
- * goes stale on ANY plane change, including a tiers-off flip. */
57
- /** codex R12: TRUE when this candidate resolves an ACTIVE tier table. Used by main.ts's defer predicate — a
58
- * tier-less-booted Runner never re-runs expandTiers, so hot-applying an ARRIVING tier table splits admission
59
- * (tier words pass the expanded gate) from execution (core throws "Unknown model ref"). Hot-apply of the model
60
- * plane is safe only when BOTH the constructed generation and the candidate are tier-less. */
61
- export declare function planeHasActiveTiers(eff: EffectiveConfig): boolean;
61
+ /** The refresh loop's SWAP gate: TRUE when the MODEL PLANE (enabled models + active tier table + resolved
62
+ * default) of `next` differs from `prev`, the last plane handed to `swapRunnerDeps` — only then does the tick
63
+ * pass `swapPlane` to applyEffective, so a candidate that moved only hot fields (pricing values / quota weights /
64
+ * the `enabled:true` spelling, see catalogEntryFingerprint) or nothing on the plane swaps nothing. `prev ===
65
+ * undefined` (env-boot plane, no EffectiveConfig to compare) is conservatively "changed": the first center plane
66
+ * always swaps. Not gated on tier activity: a Runner's expanded catalog goes stale on ANY plane change,
67
+ * including a tiers-off flip, and the default entry rides the fingerprint because the Runner's copy carries its
68
+ * own `default` (codex R8; SAME resolver as applyEffective — resolveDefaultModelName, single source). */
62
69
  export declare function modelPlaneChanged(prev: EffectiveConfig | undefined, next: EffectiveConfig): boolean;
63
70
  //# sourceMappingURL=restart-signal.d.ts.map