@sema-agent/server 7.98.0 → 7.100.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 (153) hide show
  1. package/MIGRATION.md +19 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +86 -60
  5. package/deploy/sema-up/lib/envf.sh +110 -42
  6. package/deploy/sema-up/sema-up.sh +76 -34
  7. package/dist/approval-ask-machine.d.ts +10 -0
  8. package/dist/approval-ask-machine.js +3 -0
  9. package/dist/approval-card.d.ts +74 -1
  10. package/dist/approval-card.js +41 -8
  11. package/dist/approval.d.ts +44 -20
  12. package/dist/approval.js +14 -6
  13. package/dist/boot/config-center.d.ts +16 -8
  14. package/dist/boot/config-center.js +206 -103
  15. package/dist/boot/execution-env.d.ts +1 -2
  16. package/dist/boot/execution-env.js +2 -2
  17. package/dist/boot/leader.js +12 -9
  18. package/dist/boot/memory-consolidation.d.ts +38 -0
  19. package/dist/boot/memory-consolidation.js +35 -0
  20. package/dist/boot/org-memory.d.ts +2 -1
  21. package/dist/boot/org-memory.js +9 -5
  22. package/dist/boot/resolve-spec.d.ts +0 -1
  23. package/dist/boot/resolve-spec.js +4 -3
  24. package/dist/boot/runner-deps.d.ts +2 -2
  25. package/dist/boot/runtime-caps.js +4 -4
  26. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  27. package/dist/boot/stage-06-runners.d.ts +2 -2
  28. package/dist/boot/stage-06-runners.js +3 -3
  29. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  30. package/dist/boot/stage-07-capability-layer.js +6 -6
  31. package/dist/boot/stage-08-reapers.d.ts +3 -4
  32. package/dist/boot/stage-08-reapers.js +2 -2
  33. package/dist/boot/stage-09-leader.d.ts +2 -3
  34. package/dist/boot/stage-10-http-server.d.ts +2 -3
  35. package/dist/boot/stage-10-http-server.js +2 -2
  36. package/dist/capabilities/center-plugins.d.ts +14 -2
  37. package/dist/capabilities/center-plugins.js +13 -5
  38. package/dist/capabilities/center-prompts.d.ts +3 -0
  39. package/dist/capabilities/center-prompts.js +1 -1
  40. package/dist/capabilities/hands-lane.js +2 -1
  41. package/dist/capabilities/scenarios.js +18 -5
  42. package/dist/capabilities/tool-defer.d.ts +2 -2
  43. package/dist/center-credential.d.ts +112 -0
  44. package/dist/center-credential.js +131 -0
  45. package/dist/config-catalog.d.ts +2 -2
  46. package/dist/config-catalog.js +15 -15
  47. package/dist/config-center/apply-effective.d.ts +34 -39
  48. package/dist/config-center/apply-effective.js +100 -64
  49. package/dist/config-center/apply-ledger.d.ts +35 -16
  50. package/dist/config-center/apply-ledger.js +25 -18
  51. package/dist/config-center/center-request.d.ts +60 -0
  52. package/dist/config-center/center-request.js +71 -0
  53. package/dist/config-center/facade.d.ts +11 -5
  54. package/dist/config-center/facade.js +1 -1
  55. package/dist/config-center/hot-keys-registry.d.ts +11 -4
  56. package/dist/config-center/hot-keys-registry.js +11 -7
  57. package/dist/config-center/http-client.d.ts +17 -9
  58. package/dist/config-center/http-client.js +27 -36
  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/skills-mcp.d.ts +4 -1
  62. package/dist/config-center/skills-mcp.js +2 -2
  63. package/dist/config-center/types.d.ts +3 -3
  64. package/dist/config-invariants.d.ts +2 -2
  65. package/dist/config-invariants.js +0 -8
  66. package/dist/config-lkg.d.ts +47 -8
  67. package/dist/config-lkg.js +107 -20
  68. package/dist/config-provider.d.ts +8 -7
  69. package/dist/config-provider.js +5 -5
  70. package/dist/config-types.d.ts +30 -11
  71. package/dist/config.js +11 -13
  72. package/dist/cross-session-settings.js +1 -1
  73. package/dist/fleet-client.d.ts +8 -8
  74. package/dist/fleet-client.js +7 -7
  75. package/dist/fleet-lease.d.ts +11 -11
  76. package/dist/fleet-lease.js +17 -13
  77. package/dist/host-lsp-manager.d.ts +29 -0
  78. package/dist/host-lsp-manager.js +14 -0
  79. package/dist/hosted-posture.d.ts +49 -93
  80. package/dist/hosted-posture.js +1 -25
  81. package/dist/http/active-run-conflict.d.ts +2 -12
  82. package/dist/http/active-run-conflict.js +1 -6
  83. package/dist/http/admission.js +8 -0
  84. package/dist/http/resume-legs.d.ts +1 -1
  85. package/dist/http/resume-legs.js +14 -10
  86. package/dist/http/route-ctx.d.ts +2 -2
  87. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  88. package/dist/http/routes/approvals-assistant.js +2 -2
  89. package/dist/http/routes/memory-compliance.js +5 -0
  90. package/dist/http/routes/memory-origin.js +5 -0
  91. package/dist/http/routes/sessions.js +6 -0
  92. package/dist/http/routes/tasks.js +7 -6
  93. package/dist/http/server.d.ts +6 -5
  94. package/dist/http/wire-types.d.ts +6 -3
  95. package/dist/leader/wire.d.ts +20 -12
  96. package/dist/leader/wire.js +7 -6
  97. package/dist/memory-layer-preflight.d.ts +46 -0
  98. package/dist/memory-layer-preflight.js +121 -0
  99. package/dist/memory-operator-faces.js +17 -1
  100. package/dist/observability/corrupt-read-seat.d.ts +21 -4
  101. package/dist/observability/corrupt-read-seat.js +31 -0
  102. package/dist/observability/fail-open.d.ts +8 -4
  103. package/dist/observability/fail-open.js +8 -4
  104. package/dist/observability/run-terminal.js +1 -0
  105. package/dist/observability/secret-env-scrub.d.ts +3 -26
  106. package/dist/observability/secret-env-scrub.js +3 -2
  107. package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
  108. package/dist/orchestration/workflow-completion-inbox.js +8 -3
  109. package/dist/plugins/approval-ask-store-file.d.ts +20 -2
  110. package/dist/plugins/approval-ask-store-file.js +21 -3
  111. package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
  112. package/dist/plugins/approval-ask-store-memory.js +39 -13
  113. package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
  114. package/dist/plugins/approval-ask-store-sql.js +27 -3
  115. package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
  116. package/dist/plugins/checkpoint-store-sql.js +19 -2
  117. package/dist/plugins/local-checkpoint-store.js +2 -0
  118. package/dist/plugins/mailbox-store-sql.d.ts +7 -5
  119. package/dist/plugins/mailbox-store-sql.js +6 -4
  120. package/dist/plugins/permission-rule-store-file.js +3 -2
  121. package/dist/plugins/remote-env-host.d.ts +3 -1
  122. package/dist/plugins/remote-env-host.js +3 -2
  123. package/dist/plugins/sql-json-column.d.ts +16 -3
  124. package/dist/plugins/sql-json-column.js +15 -4
  125. package/dist/plugins/web-search.d.ts +42 -22
  126. package/dist/plugins/web-search.js +141 -49
  127. package/dist/project-memory.d.ts +28 -1
  128. package/dist/project-memory.js +22 -22
  129. package/dist/run-local.js +6 -7
  130. package/dist/runs.js +6 -5
  131. package/dist/runtime-caps-resolver.d.ts +8 -8
  132. package/dist/runtime-caps-resolver.js +16 -14
  133. package/dist/runtime-governance.d.ts +50 -3
  134. package/dist/runtime-governance.js +5 -0
  135. package/dist/sealed-key.js +3 -7
  136. package/dist/server-secret-env.d.ts +43 -0
  137. package/dist/server-secret-env.js +18 -0
  138. package/dist/task-settings.d.ts +1 -1
  139. package/dist/tool-approval.d.ts +144 -9
  140. package/dist/tool-approval.js +74 -18
  141. package/dist/trace/core-keyset-guard.d.ts +5 -5
  142. package/dist/trace/ledger-sink.js +2 -2
  143. package/dist/trace/project.d.ts +13 -0
  144. package/dist/trace/project.js +22 -5
  145. package/dist/trace/projection-drop.d.ts +2 -0
  146. package/dist/trace/projection-drop.js +6 -0
  147. package/dist/trace/sema-provenance.d.ts +4 -1
  148. package/dist/trace/sema-provenance.js +1 -1
  149. package/dist/trace/task-notification-facets.d.ts +65 -0
  150. package/dist/trace/task-notification-facets.js +31 -0
  151. package/dist/trace/wire-projection-faces.d.ts +3 -3
  152. package/dist/trace/wire-projection-faces.js +3 -3
  153. package/package.json +5 -4
@@ -11,16 +11,17 @@ import { applyCatalogToSource, withPromptArtifactBackfill } from "../capabilitie
11
11
  import { validatePromptsDomain, CORE_ENGINE_VERSION } from "../prompts-domain-validate.js";
12
12
  import { centerScenarios, centerScenarioDetails } from "../capabilities/scenarios.js";
13
13
  import { configLkgEnabled, isRosterGateArmed } from "../config.js";
14
- import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg } from "../config-lkg.js";
14
+ import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg, legacyV1LkgPath, migrateV1Lkg } from "../config-lkg.js";
15
+ import { answerStillCurrent, centerStateBin, credentialIdentityNow } from "../center-credential.js";
15
16
  import { centerManagedConfigKeys } from "../config-center/apply-effective.js";
16
17
  import { createConfigProvider, raceBootFetch, BOOT_FETCH_DEFERRED } from "../config-provider.js";
17
18
  import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
18
19
  import { createKeyResolver } from "../key-resolver.js";
19
20
  import { ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
20
- import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, planeHasActiveTiers, fetchPromptArtifact } from "../config-center/facade.js";
21
+ import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, fetchPromptArtifact } from "../config-center/facade.js";
21
22
  import { createMcpRevocationLedger, enabledMcpEntries } from "../config-center/mcp-revocation.js";
22
23
  import { SHA256_HEX_RE } from "../digest-form.js";
23
- import { createConfigApplyLedger } from "../config-center/apply-ledger.js";
24
+ import { createConfigApplyLedger, refusedGroups } from "../config-center/apply-ledger.js";
24
25
  export async function createConfigCenterRuntime(ctx) {
25
26
  const { config, logger, metrics, localRoot } = ctx;
26
27
  let ccEtag;
@@ -53,6 +54,43 @@ export async function createConfigCenterRuntime(ctx) {
53
54
  let bootSkillStale = false;
54
55
  let pendingRestart;
55
56
  const cc = config.configCenter;
57
+ const bootCredential = cc?.credential.read();
58
+ if (cc !== undefined && bootCredential !== undefined && !bootCredential.ok) {
59
+ logger.error("center_credential_unreadable", {
60
+ key: "SEMA_REGISTRY_TOKEN_FILE",
61
+ reason: bootCredential.reason,
62
+ effect: "every center request fails its own failure arm until the file is readable (config pull keeps env/LKG-less boot, caps fail-closed, lease takes the loud auth-error arm); no on-disk LKG / prompt state is read back (credential identity unknown)",
63
+ });
64
+ }
65
+ const currentIdentity = () => (cc === undefined ? undefined : credentialIdentityNow(cc.credential));
66
+ const candidateIdentity = new WeakMap();
67
+ const candidateIdentityOf = (eff) => candidateIdentity.get(eff);
68
+ const validatorIdentity = new Map();
69
+ const bindValidator = (etag, identity) => {
70
+ if (etag === undefined)
71
+ return;
72
+ validatorIdentity.delete(etag);
73
+ validatorIdentity.set(etag, identity);
74
+ const oldest = validatorIdentity.keys().next();
75
+ if (validatorIdentity.size > 16 && oldest.done !== true)
76
+ validatorIdentity.delete(oldest.value);
77
+ };
78
+ const pullEffective = async (provider, etag) => {
79
+ if (provider.kind === "local")
80
+ return provider.fetchEffective(etag);
81
+ const before = currentIdentity();
82
+ const conditional = etag !== undefined && before !== undefined && validatorIdentity.get(etag) === before ? etag : undefined;
83
+ const r = await provider.fetchEffective(conditional);
84
+ const used = r === null ? before : r.credentialIdentity;
85
+ if (cc === undefined || !answerStillCurrent(cc.credential, used)) {
86
+ throw new Error("center credential identity changed while the effective pull was in flight — response discarded; the next pull runs under the current credential");
87
+ }
88
+ if (r !== null) {
89
+ candidateIdentity.set(r.effective, used);
90
+ bindValidator(r.etag, used);
91
+ }
92
+ return r;
93
+ };
56
94
  let sealedKeys;
57
95
  try {
58
96
  sealedKeys = await ensureSealedKeyStore(join(config.localDataRoot ?? localRoot, "sealed-key"), logger);
@@ -70,7 +108,7 @@ export async function createConfigCenterRuntime(ctx) {
70
108
  const configProvider = cc || config.configProvider === "local"
71
109
  ? createConfigProvider({
72
110
  ...(config.configProvider ? { provider: config.configProvider } : {}),
73
- ...(cc ? { configCenter: { baseUrl: cc.baseUrl, token: cc.token, worker: cc.worker, dryRun: cc.dryRun } } : {}),
111
+ ...(cc ? { configCenter: { baseUrl: cc.baseUrl, credential: cc.credential, worker: cc.worker, dryRun: cc.dryRun } } : {}),
74
112
  ...(config.configProvider === "local" ? { localDir: localRoot } : {}),
75
113
  })
76
114
  : undefined;
@@ -88,41 +126,106 @@ export async function createConfigCenterRuntime(ctx) {
88
126
  if (!pendingCatalogFaces)
89
127
  return;
90
128
  const { target } = pendingCatalogFaces;
91
- if (await applyCatalogToSource(promptSource, target, centerPrompts, logger, phase)) {
129
+ if (await applyCatalogToSource(catalogSource, target, centerPrompts, logger, phase)) {
92
130
  centerPrompts = target;
93
131
  pendingCatalogFaces = undefined;
94
132
  }
95
133
  };
96
134
  const promptEpochDir = join(config.localDataRoot ?? localRoot, "prompt-epoch");
97
- const promptStateDir = join(promptEpochDir, "state", cc?.worker === undefined ? "global" : `worker-${createHash("sha256").update(cc.worker).digest("hex").slice(0, 16)}`);
98
- const withByDigestBackfill = (inner) => cc?.baseUrl ? withPromptArtifactBackfill(inner, (d) => fetchPromptArtifact(cc.baseUrl, cc.token, d), logger) : inner;
135
+ const promptStateRoot = join(promptEpochDir, "state");
136
+ let promptStateIdentity = currentIdentity();
137
+ const promptStateBin = () => (cc === undefined ? "global" : promptStateIdentity === undefined ? undefined : centerStateBin(cc.worker, promptStateIdentity));
138
+ const storePerBin = (make) => {
139
+ const stores = new Map();
140
+ return (bin) => {
141
+ const hit = stores.get(bin);
142
+ if (hit !== undefined)
143
+ return hit;
144
+ const made = make(bin);
145
+ stores.set(bin, made);
146
+ return made;
147
+ };
148
+ };
149
+ const fileStateStoreFor = storePerBin((bin) => new FilePromptSourceStateStore(join(promptStateRoot, bin)));
150
+ const binnedPromptStateStore = (storeFor) => ({
151
+ load: async () => {
152
+ const bin = promptStateBin();
153
+ return bin === undefined ? undefined : storeFor(bin).load();
154
+ },
155
+ save: async (state) => {
156
+ const bin = promptStateBin();
157
+ if (bin === undefined)
158
+ throw new Error("center credential identity unknown (credential unreadable at adoption) — prompt source state not persisted");
159
+ await storeFor(bin).save(state);
160
+ },
161
+ });
162
+ let promptPersisted;
163
+ const recordingStateStore = (inner) => ({
164
+ load: async () => {
165
+ promptPersisted = await inner.load();
166
+ return promptPersisted;
167
+ },
168
+ save: async (state) => {
169
+ await inner.save(state);
170
+ promptPersisted = state;
171
+ },
172
+ });
173
+ const withByDigestBackfill = (inner) => cc?.baseUrl ? withPromptArtifactBackfill(inner, (d) => fetchPromptArtifact(cc, d), logger) : inner;
99
174
  const promptSource = (() => {
100
175
  try {
176
+ const bootBin = promptStateBin();
177
+ if (bootBin !== undefined)
178
+ fileStateStoreFor(bootBin);
101
179
  return new CenterPromptSource({
102
180
  store: withByDigestBackfill(new FilePromptArtifactStore(promptEpochDir)),
103
- stateStore: new FilePromptSourceStateStore(promptStateDir),
181
+ stateStore: recordingStateStore(binnedPromptStateStore(fileStateStoreFor)),
104
182
  engineVersion: CORE_ENGINE_VERSION,
105
183
  });
106
184
  }
107
185
  catch (e) {
108
186
  logger.warn("center_prompts_store_unwritable", { dir: promptEpochDir, err: String(e), note: "file-backed prompt-epoch store unavailable — memory fallback (catalog candidate not restart-durable); built-ins/declaration axis unaffected" });
109
- return new CenterPromptSource({ store: withByDigestBackfill(new MemoryPromptArtifactStore()), stateStore: new MemoryPromptSourceStateStore(), engineVersion: CORE_ENGINE_VERSION });
187
+ return new CenterPromptSource({
188
+ store: withByDigestBackfill(new MemoryPromptArtifactStore()),
189
+ stateStore: recordingStateStore(binnedPromptStateStore(storePerBin(() => new MemoryPromptSourceStateStore()))),
190
+ engineVersion: CORE_ENGINE_VERSION,
191
+ });
110
192
  }
111
193
  })();
112
- if (configProvider && !cc?.dryRun) {
194
+ const catalogSource = {
195
+ applySnapshot: (raw) => promptSource.applySnapshot(raw),
196
+ applyDisabled: (sourceRevision) => promptSource.applyDisabled(sourceRevision),
197
+ current: () => promptSource.current(),
198
+ persistedActive: () => promptPersisted?.state === "active",
199
+ };
200
+ const rebasePromptState = async (identity, phase) => {
201
+ promptStateIdentity = identity;
202
+ centerPrompts = undefined;
203
+ pendingCatalogFaces = undefined;
204
+ promptPersisted = undefined;
113
205
  try {
114
206
  await promptSource.restore();
115
207
  }
116
208
  catch (e) {
117
- logger.warn("center_prompts_catalog_restore_failed", { err: String(e), note: "disk LKG unavailable — catalog candidate absent until the next adopt; declaration axis and built-ins unaffected" });
209
+ logger.warn("center_prompts_catalog_restore_failed", { phase, err: String(e), note: "the persisted catalog state for this credential identity could not be loaded — catalog candidate absent until the next adopt; declaration axis and built-ins unaffected" });
118
210
  }
119
- }
211
+ if (cc !== undefined && identity !== undefined && currentIdentity() !== identity) {
212
+ promptStateIdentity = undefined;
213
+ await promptSource.restore();
214
+ logger.warn("center_prompts_catalog_identity_changed", { phase, note: "the center credential changed while the persisted catalog state was being read — that state is not used; the current identity's next candidate re-establishes it" });
215
+ }
216
+ };
217
+ if (configProvider && !cc?.dryRun)
218
+ await rebasePromptState(promptStateIdentity, "boot");
120
219
  const adoptCenterPrompts = async (eff, phase) => {
220
+ const adoptedIdentity = candidateIdentityOf(eff);
221
+ if (adoptedIdentity !== undefined && adoptedIdentity !== promptStateIdentity) {
222
+ await rebasePromptState(adoptedIdentity, phase);
223
+ }
121
224
  const raw = eff?.prompts;
122
225
  if (raw === undefined) {
123
226
  if (centerPrompts)
124
227
  logger.info("center_prompts_cleared", { phase, note: "effective carries no prompts key (publish gate off) — built-in providers resume" });
125
- const cleared = await applyCatalogToSource(promptSource, undefined, centerPrompts, logger, phase);
228
+ const cleared = await applyCatalogToSource(catalogSource, undefined, centerPrompts, logger, phase);
126
229
  if (cleared) {
127
230
  centerPrompts = undefined;
128
231
  pendingCatalogFaces = undefined;
@@ -148,7 +251,7 @@ export async function createConfigCenterRuntime(ctx) {
148
251
  note: "new tasks assemble the new pack (epoch re-pins at the next task boundary); in-flight sessions stay pinned",
149
252
  });
150
253
  }
151
- const transitioned = await applyCatalogToSource(promptSource, v.value, centerPrompts, logger, phase);
254
+ const transitioned = await applyCatalogToSource(catalogSource, v.value, centerPrompts, logger, phase);
152
255
  if (transitioned) {
153
256
  centerPrompts = v.value;
154
257
  pendingCatalogFaces = undefined;
@@ -159,7 +262,7 @@ export async function createConfigCenterRuntime(ctx) {
159
262
  };
160
263
  let bootConfigPending;
161
264
  const lkgEnabled = configProvider?.kind === "remote" && configLkgEnabled();
162
- const lkgPath = process.env.CONFIG_LKG_PATH ?? defaultLkgPath(cc?.worker);
265
+ const lkgPathFor = (identity) => process.env.CONFIG_LKG_PATH ?? defaultLkgPath(cc?.worker, identity);
163
266
  const lkgSurvivesRestart = lkgEnabled && process.env.CONFIG_LKG_DURABLE === "true";
164
267
  let lkgBooted = false;
165
268
  let bootLkgCandidate;
@@ -170,9 +273,15 @@ export async function createConfigCenterRuntime(ctx) {
170
273
  return true;
171
274
  if (!lkgEnabled)
172
275
  return false;
276
+ const identity = candidateIdentityOf(effectiveObj);
277
+ if (identity === undefined) {
278
+ logger.warn("config_lkg_save_failed", { err: "candidate carries no credential identity", note: "LKG NOT written (the bin is keyed by the identity that fetched the candidate) — handoff NOT confirmed" });
279
+ return false;
280
+ }
281
+ const lkgPath = lkgPathFor(identity);
173
282
  try {
174
283
  if (effectiveObj.skills && config.configCenter) {
175
- await applyCenterSkills([], effectiveObj.skills, config.configCenter.baseUrl, config.configCenter.token, logger, undefined, defaultSkillCacheDir());
284
+ await applyCenterSkills([], effectiveObj.skills, config.configCenter, logger, undefined, defaultSkillCacheDir());
176
285
  }
177
286
  const enabledSkills = (effectiveObj.skills?.skills ?? []).filter((m) => m.enabled !== false);
178
287
  const cacheDir = defaultSkillCacheDir();
@@ -193,7 +302,7 @@ export async function createConfigCenterRuntime(ctx) {
193
302
  }
194
303
  }
195
304
  lastPrewarmBodiesVerified = true;
196
- await saveLkg(lkgPath, cc?.worker, effectiveObj, etag);
305
+ await saveLkg(lkgPath, cc?.worker, identity, effectiveObj, etag);
197
306
  return lkgSurvivesRestart;
198
307
  }
199
308
  catch (err) {
@@ -206,19 +315,45 @@ export async function createConfigCenterRuntime(ctx) {
206
315
  const bootFromLkg = async (why) => {
207
316
  if (!lkgEnabled || dryRun)
208
317
  return null;
209
- const read = await loadLkg(lkgPath, cc?.worker);
318
+ const identity = currentIdentity();
319
+ if (identity === undefined) {
320
+ logger.warn("config_lkg_unusable", { reason: "center credential unreadable — credential identity unknown, no LKG bin is read back", note: "env fallback serves; the live pull retries on the refresh cadence once the credential is readable" });
321
+ return null;
322
+ }
323
+ const lkgPath = lkgPathFor(identity);
324
+ const legacy = await migrateV1Lkg({ legacyPath: process.env.CONFIG_LKG_PATH ?? legacyV1LkgPath(cc?.worker), binPath: lkgPath, worker: cc?.worker, identity });
325
+ switch (legacy.outcome) {
326
+ case "none":
327
+ break;
328
+ case "migrated":
329
+ logger.info("config_lkg_migrated", { from: legacy.from, retiredTo: legacy.retiredTo, to: lkgPath, note: "formatVersion 1 LKG (pull-token identity = this worker) migrated once into this credential identity's bin; the old file was renamed, not deleted" });
330
+ if (legacy.retireError !== undefined)
331
+ logger.warn("config_lkg_legacy_not_retired", { path: legacy.from, reason: legacy.retireError, note: "the bin was written and is used; only the old file's rename / copy failed" });
332
+ break;
333
+ case "not-migrated":
334
+ logger.warn("config_lkg_unusable", { path: legacy.path, reason: legacy.reason, note: "env fallback serves; the cache rewrites on the next clean candidate" });
335
+ return null;
336
+ }
337
+ const read = await loadLkg(lkgPath, cc?.worker, identity);
210
338
  if (!read.lkg) {
211
339
  if (read.reason !== "absent")
212
340
  logger.warn("config_lkg_unusable", { path: lkgPath, reason: read.reason, note: "ignoring the on-disk LKG — env fallback serves; the cache rewrites on the next clean candidate" });
213
341
  return null;
214
342
  }
343
+ if (currentIdentity() !== identity) {
344
+ logger.warn("config_lkg_unusable", { path: lkgPath, reason: "center credential identity changed while the LKG was being read — not used", note: "env fallback serves; the current identity's next pull re-establishes its own LKG" });
345
+ return null;
346
+ }
215
347
  logger.info("config_loaded_lkg", { path: lkgPath, savedAt: read.lkg.savedAt, why, note: "booting from the persisted last-known-good (all restart-to-apply faces included) — the live pull continues in the background and reconciles on arrival" });
216
- return { effective: read.lkg.effective, ...(read.lkg.etag !== undefined ? { etag: read.lkg.etag } : {}) };
348
+ const lkgEffective = read.lkg.effective;
349
+ candidateIdentity.set(lkgEffective, identity);
350
+ bindValidator(read.lkg.etag, identity);
351
+ return { effective: lkgEffective, ...(read.lkg.etag !== undefined ? { etag: read.lkg.etag } : {}) };
217
352
  };
218
353
  const bootApplyLkgInline = async (lkgR) => {
219
354
  const lkgGen = generationOf(lkgR.effective.version);
220
355
  applyLedger.seenTarget(lkgGen, lkgR.effective.version);
221
- applyEffective(config, lkgR.effective, logger, { sealedKeys, generation: lkgGen, report: (rep) => applyLedger.recordApply(rep) });
356
+ applyEffective(config, lkgR.effective, logger, { leg: "boot", sealedKeys, generation: lkgGen, report: (rep) => applyLedger.recordApply(rep) });
222
357
  applyLedger.applied(lkgGen, lkgR.effective.version);
223
358
  await adoptCenterPrompts(lkgR.effective, "boot-lkg");
224
359
  markRosterLanded(lkgR.effective);
@@ -231,7 +366,7 @@ export async function createConfigCenterRuntime(ctx) {
231
366
  logger.info("config_loaded", { source: "lkg", version: lkgR.effective.version, worker: cc?.worker ?? "(global)", models: cfgModels });
232
367
  };
233
368
  try {
234
- const bootFetch = configProvider.fetchEffective(undefined);
369
+ const bootFetch = pullEffective(configProvider, undefined);
235
370
  const raced = await raceBootFetch(bootFetch, config.configBootFetchBudgetMs);
236
371
  let lkgFallback = null;
237
372
  if (raced === BOOT_FETCH_DEFERRED) {
@@ -274,6 +409,7 @@ export async function createConfigCenterRuntime(ctx) {
274
409
  applyLedger.seenTarget(bootGen, r.effective.version);
275
410
  let bootReport;
276
411
  applyEffective(config, r.effective, logger, {
412
+ leg: "boot",
277
413
  sealedKeys,
278
414
  generation: bootGen,
279
415
  report: (rep) => {
@@ -327,6 +463,8 @@ export async function createConfigCenterRuntime(ctx) {
327
463
  }
328
464
  }
329
465
  }
466
+ if (!dryRun && cc !== undefined && currentIdentity() !== promptStateIdentity)
467
+ await rebasePromptState(currentIdentity(), "boot-end");
330
468
  }
331
469
  const readFaceBaseline = { ...(config.readFace !== undefined ? { readFace: config.readFace } : {}), readFaceSource: config.readFaceSource };
332
470
  const readFaceEnvHeld = applyCenterReadFace(config, effective ?? { version: 0, updatedAt: "", models: { models: [], roles: {} } }, effective ? logger : undefined).envHeld;
@@ -337,8 +475,10 @@ export async function createConfigCenterRuntime(ctx) {
337
475
  let keyResolver;
338
476
  const mcpRevocations = createMcpRevocationLedger((ev) => logger.info("mcp_revocation_ledger", { ...(ev.revoked.length > 0 ? { revoked: ev.revoked } : {}), ...(ev.restored.length > 0 ? { restored: ev.restored } : {}) }));
339
477
  let mcpRevocationWired = false;
340
- const restartCtx = () => ({
478
+ const restartCtx = (consolidationSeat) => ({
341
479
  ...(config.degrade?.reactive ? { reactiveDegradeTo: config.degrade.to } : {}),
480
+ pluginsAllowHosts: config.pluginsAllowHosts,
481
+ ...(consolidationSeat !== undefined ? { consolidationSeat } : {}),
342
482
  ...(readFaceEnvHeld.length > 0 ? { readFaceEnvHeld } : {}),
343
483
  ...(mcpRevocationWired && mcpRevocations.hasBaseline() ? { mcpRevocationLive: true } : {}),
344
484
  });
@@ -348,10 +488,6 @@ export async function createConfigCenterRuntime(ctx) {
348
488
  const { faceChanged } = applyCenterReadFace(config, eff, logger, { phase: "live", baseline: readFaceBaseline });
349
489
  if (!faceChanged)
350
490
  return;
351
- if (swap === undefined) {
352
- logger.warn("read_face_swap_unwired", { face: config.readFace ?? null, phase, note: "the center's read FACE changed but no Runner swap door was wired — the value landed in config while the engine keeps the seat it booted with" });
353
- return;
354
- }
355
491
  try {
356
492
  swap({ readFace: config.readFace });
357
493
  logger.info("read_face_swapped", { from: before ?? null, to: config.readFace ?? null, phase, note: "hot seat (core swapDeps) — legs that already prepared keep the face they prepared under" });
@@ -438,7 +574,7 @@ export async function createConfigCenterRuntime(ctx) {
438
574
  },
439
575
  async applyCenterCapabilities(skills) {
440
576
  if (effective?.skills && config.configCenter) {
441
- skills = await applyCenterSkills(skills, effective.skills, config.configCenter.baseUrl, config.configCenter.token, logger, undefined, configLkgEnabled() ? defaultSkillCacheDir() : undefined);
577
+ skills = await applyCenterSkills(skills, effective.skills, config.configCenter, logger, undefined, configLkgEnabled() ? defaultSkillCacheDir() : undefined);
442
578
  }
443
579
  if (effective?.plugins) {
444
580
  const pluginOut = await applyCenterPlugins(skills, effective, {
@@ -458,7 +594,7 @@ export async function createConfigCenterRuntime(ctx) {
458
594
  }
459
595
  if (bootLkgCandidate && lkgEnabled) {
460
596
  const bootPublished = await persistLkgDurable(bootLkgCandidate.effective, bootLkgCandidate.etag).catch((err) => {
461
- logger.warn("config_lkg_save_failed", { path: lkgPath, err: String(err), note: "boot LKG persist failed — retained for the per-tick retry" });
597
+ logger.warn("config_lkg_save_failed", { err: String(err), note: "boot LKG persist failed — retained for the per-tick retry" });
462
598
  return false;
463
599
  });
464
600
  {
@@ -507,7 +643,7 @@ export async function createConfigCenterRuntime(ctx) {
507
643
  }
508
644
  },
509
645
  startRefreshLoop(a) {
510
- const { runnerTierFrozen, pricing, limitSync, swapRunnerDeps, ceilingVerdict } = a;
646
+ const { pricing, limitSync, swapRunnerDeps, ceilingVerdict, consolidationSeat, checkpointStorePresent } = a;
511
647
  if (configProvider) {
512
648
  const ccRef = config.configCenter;
513
649
  let refreshInFlight = false;
@@ -521,18 +657,18 @@ export async function createConfigCenterRuntime(ctx) {
521
657
  metrics.inc("models_tiers_plane_deferred_stuck_total");
522
658
  logger.warn("models_tiers_deferred_no_handoff", { version: planeDeferredNoHandoff.version, since: planeDeferredNoHandoff.since, note: "candidate still retained without a next-boot handoff — mount restart-surviving LKG storage + CONFIG_LKG_DURABLE=true, or restart manually after ensuring the candidate is re-pulled at boot" });
523
659
  }
524
- const r = arrival !== undefined ? arrival.result : await configProvider.fetchEffective(ccEtag);
660
+ const r = arrival !== undefined ? arrival.result : await pullEffective(configProvider, ccEtag);
525
661
  if (r === null && planeDeferredNoHandoff) {
526
662
  const skillsDebt = (planeDeferredNoHandoff.blocked ?? []).includes("skills");
527
663
  const promoted = lkgSurvivesRestart || skillsDebt ? await persistLkgDurable(planeDeferredNoHandoff.candidate, planeDeferredNoHandoff.candidateEtag) : false;
528
- const skillsProofPromotion = !promoted && !lkgSurvivesRestart && skillsDebt && lastPrewarmBodiesVerified && planeDeferredNoHandoff.planeDeferred !== true;
664
+ const skillsProofPromotion = !promoted && !lkgSurvivesRestart && skillsDebt && lastPrewarmBodiesVerified;
529
665
  if (skillsProofPromotion) {
530
666
  pendingRestart = { restartRequired: true, reasons: ["skills"], version: planeDeferredNoHandoff.version, since: Date.now() };
531
667
  logger.info("config_boot_skill_debt_promoted", { version: planeDeferredNoHandoff.version, note: "skill bodies verified on a source-confirmed-current candidate — the withheld skills restart is now published (non-durable lane, repair proven)" });
532
668
  planeDeferredNoHandoff = undefined;
533
669
  }
534
670
  else if (promoted) {
535
- const promotedReasons = [...new Set([...(planeDeferredNoHandoff.blocked ?? []), ...(planeDeferredNoHandoff.planeDeferred ? ["models-tiers"] : [])])];
671
+ const promotedReasons = [...new Set(planeDeferredNoHandoff.blocked ?? [])];
536
672
  if (promotedReasons.length > 0) {
537
673
  pendingRestart = { restartRequired: true, reasons: promotedReasons, version: planeDeferredNoHandoff.version, since: Date.now() };
538
674
  }
@@ -573,18 +709,17 @@ export async function createConfigCenterRuntime(ctx) {
573
709
  }
574
710
  else {
575
711
  const planeChanged = modelPlaneChanged(appliedPlaneEff, r.effective);
576
- const planeDeferred = swapRunnerDeps === undefined && (runnerTierFrozen || planeHasActiveTiers(r.effective)) && planeChanged;
577
712
  let applyReport;
578
713
  const committed = applyEffective(config, r.effective, logger, {
579
- teamsOnly: true,
714
+ leg: "hot",
715
+ checkpointStorePresent,
580
716
  sealedKeys,
581
717
  generation: tickGen,
582
718
  report: (rep) => {
583
719
  applyReport = rep;
584
720
  applyLedger.recordApply(rep);
585
721
  },
586
- ...(planeDeferred ? { deferModelPlane: true } : {}),
587
- ...(swapRunnerDeps !== undefined && planeChanged ? { swapPlane: swapRunnerDeps } : {}),
722
+ ...(planeChanged ? { swapPlane: swapRunnerDeps } : {}),
588
723
  });
589
724
  if (!committed)
590
725
  return "ok";
@@ -595,57 +730,37 @@ export async function createConfigCenterRuntime(ctx) {
595
730
  mcpRevocations.applyEnabledEntries(enabledMcpEntries(r.effective.mcp));
596
731
  applyLiveReadFace(r.effective, swapRunnerDeps, "refresh");
597
732
  applyLedger.applied(tickGen, r.effective.version);
598
- if (planeDeferred) {
599
- logger.warn("models_tiers_plane_deferred", { version: r.effective.version, note: "tier-frozen Runner: the changed model plane (models/roles/tiers/default) is NOT hot-applied — admission stays on the Runner's generation; restart applies the new plane (models-tiers restart signal rides /health)" });
600
- }
601
- else {
602
- appliedPlaneEff = r.effective;
603
- planeDeferredNoHandoff = undefined;
604
- }
733
+ appliedPlaneEff = r.effective;
734
+ planeDeferredNoHandoff = undefined;
605
735
  await adoptCenterPrompts(r.effective, "refresh");
606
- if (!planeDeferred)
607
- markRosterLanded(r.effective);
736
+ markRosterLanded(r.effective);
608
737
  const fullyApplied = applyReport?.fullyApplied !== false;
609
738
  if (!fullyApplied) {
739
+ ccEtag = undefined;
740
+ const refused = applyReport ? refusedGroups(applyReport) : [];
610
741
  logger.warn("config_candidate_partially_applied", {
611
742
  version: r.effective.version,
612
- rejected: applyReport?.limits.rejected?.keys ?? [],
743
+ rejected: refused.flatMap((g) => g.keys),
744
+ groups: refused.map((g) => g.group),
613
745
  note: "a group of this candidate was refused — the live config keeps serving the prior values for it; the LKG and the ETag stay on the last FULLY applied generation (this tree is re-judged next tick)",
614
746
  });
615
747
  return "ok";
616
748
  }
617
749
  const lkgPersisted = await persistLkgDurable(r.effective, r.etag);
618
- const reasons = restartReasons(effective, r.effective, restartCtx());
619
- if (swapRunnerDeps !== undefined) {
620
- const mi = reasons.indexOf("models-tiers");
621
- if (mi >= 0)
622
- reasons.splice(mi, 1);
623
- }
750
+ const reasons = restartReasons(effective, r.effective, restartCtx(consolidationSeat?.read()));
624
751
  const skillsChangeDetected = reasons.includes("skills");
625
752
  if (bootSkillStale && !skillsChangeDetected)
626
753
  reasons.push("skills");
627
- if (planeDeferred) {
628
- if (lkgPersisted) {
629
- if (!reasons.includes("models-tiers"))
630
- reasons.push("models-tiers");
631
- planeDeferredNoHandoff = undefined;
632
- }
633
- else {
634
- const blocked = reasons.splice(0, reasons.length);
635
- planeDeferredNoHandoff = { version: r.effective.version, since: planeDeferredNoHandoff?.since ?? Date.now(), ...(blocked.length > 0 ? { blocked } : {}), planeDeferred: true, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
636
- logger.warn("models_tiers_deferred_no_handoff", { version: r.effective.version, blockedReasons: blocked, note: "plane deferred but no NEXT-BOOT handoff (LKG disabled/unwritable, or storage not declared restart-surviving — set CONFIG_LKG_DURABLE=true) — the WHOLE restart signal is withheld (it would loop); the old generation keeps serving" });
637
- }
638
- }
639
- else if (lkgSurvivesRestart && !lkgPersisted) {
754
+ if (lkgSurvivesRestart && !lkgPersisted) {
640
755
  const blocked = reasons.splice(0, reasons.length);
641
- planeDeferredNoHandoff = { version: r.effective.version, since: planeDeferredNoHandoff?.since ?? Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
756
+ planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
642
757
  logger.warn("restart_candidate_publication_incomplete", { version: r.effective.version, blockedReasons: blocked, note: "durable LKG declared but the candidate's publication is incomplete (skill bodies unverified) — restart signal withheld; retried every tick until the cache completes" });
643
758
  }
644
- else if (!planeDeferred && !lkgSurvivesRestart && bootSkillStale && !skillsChangeDetected && !lastPrewarmBodiesVerified) {
759
+ else if (!lkgSurvivesRestart && bootSkillStale && !skillsChangeDetected && !lastPrewarmBodiesVerified) {
645
760
  const i = reasons.indexOf("skills");
646
761
  if (i >= 0) {
647
762
  reasons.splice(i, 1);
648
- planeDeferredNoHandoff = { version: r.effective.version, since: planeDeferredNoHandoff?.since ?? Date.now(), blocked: ["skills"], candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
763
+ planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked: ["skills"], candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
649
764
  logger.warn("config_boot_skill_debt_withheld", { version: r.effective.version, note: "boot skill debt still unproven on a non-durable deployment (body cache does not verify) — skills restart withheld this tick; re-verified each tick and published once the bodies prove fetchable" });
650
765
  }
651
766
  }
@@ -660,7 +775,7 @@ export async function createConfigCenterRuntime(ctx) {
660
775
  }
661
776
  logger.info("sema_registry_refreshed", {
662
777
  version: r.effective.version,
663
- note: "collab + runtime governance + limits + models/roles/roster hot-applied; skills/mcp/runtime-gates(approvalRequire)/scenarios are restart-to-apply",
778
+ note: "collab + runtime governance (incl. approvalRequire) + limits + models/roles/roster hot-applied; skills/mcp/a2a/scenarios are restart-to-apply",
664
779
  ...(pendingRestart ? { restartRequired: true, restartReasons: pendingRestart.reasons } : {}),
665
780
  });
666
781
  latestEffective = r.effective;
@@ -721,18 +836,17 @@ export async function createConfigCenterRuntime(ctx) {
721
836
  return;
722
837
  }
723
838
  const planeChangedLate = modelPlaneChanged(appliedPlaneEff, r.effective);
724
- const planeDeferredLate = swapRunnerDeps === undefined && (runnerTierFrozen || planeHasActiveTiers(r.effective)) && planeChangedLate;
725
839
  let applyReportLate;
726
840
  const committedLate = applyEffective(config, r.effective, logger, {
727
- teamsOnly: true,
841
+ leg: "hot",
842
+ checkpointStorePresent,
728
843
  sealedKeys,
729
844
  generation: lateGen,
730
845
  report: (rep) => {
731
846
  applyReportLate = rep;
732
847
  applyLedger.recordApply(rep);
733
848
  },
734
- ...(planeDeferredLate ? { deferModelPlane: true } : {}),
735
- ...(swapRunnerDeps !== undefined && planeChangedLate ? { swapPlane: swapRunnerDeps } : {}),
849
+ ...(planeChangedLate ? { swapPlane: swapRunnerDeps } : {}),
736
850
  });
737
851
  if (!committedLate)
738
852
  return;
@@ -742,20 +856,18 @@ export async function createConfigCenterRuntime(ctx) {
742
856
  ceilingVerdict?.judge();
743
857
  applyLiveReadFace(r.effective, swapRunnerDeps, "boot-deferred");
744
858
  applyLedger.applied(lateGen, r.effective.version);
745
- if (planeDeferredLate)
746
- logger.warn("models_tiers_plane_deferred", { version: r.effective.version, note: "tier-frozen Runner (env tiers): the late-boot center model plane is NOT hot-applied — restart applies it" });
747
- else {
748
- appliedPlaneEff = r.effective;
749
- planeDeferredNoHandoff = undefined;
750
- }
859
+ appliedPlaneEff = r.effective;
860
+ planeDeferredNoHandoff = undefined;
751
861
  await adoptCenterPrompts(r.effective, "boot-deferred");
752
- if (!planeDeferredLate)
753
- markRosterLanded(r.effective);
862
+ markRosterLanded(r.effective);
754
863
  const fullyAppliedLate = applyReportLate?.fullyApplied !== false;
755
864
  if (!fullyAppliedLate) {
865
+ ccEtag = undefined;
866
+ const refusedLate = applyReportLate ? refusedGroups(applyReportLate) : [];
756
867
  logger.warn("config_candidate_partially_applied", {
757
868
  version: r.effective.version,
758
- rejected: applyReportLate?.limits.rejected?.keys ?? [],
869
+ rejected: refusedLate.flatMap((g) => g.keys),
870
+ groups: refusedLate.map((g) => g.group),
759
871
  note: "late-boot candidate had a refused group — the LKG/ETag stay on the last FULLY applied generation; the refresh cadence re-judges the same tree",
760
872
  });
761
873
  return;
@@ -764,27 +876,10 @@ export async function createConfigCenterRuntime(ctx) {
764
876
  latestEffective = r.effective;
765
877
  ccEtag = r.etag;
766
878
  const lkgPersistedLate = await persistLkgDurable(r.effective, r.etag);
767
- const reasons = restartReasons(undefined, r.effective, restartCtx());
768
- if (swapRunnerDeps !== undefined) {
769
- const miLate = reasons.indexOf("models-tiers");
770
- if (miLate >= 0)
771
- reasons.splice(miLate, 1);
772
- }
773
- if (planeDeferredLate) {
774
- if (lkgPersistedLate) {
775
- if (!reasons.includes("models-tiers"))
776
- reasons.push("models-tiers");
777
- planeDeferredNoHandoff = undefined;
778
- }
779
- else {
780
- const blocked = reasons.splice(0, reasons.length);
781
- planeDeferredNoHandoff = { version: r.effective.version, since: planeDeferredNoHandoff?.since ?? Date.now(), ...(blocked.length > 0 ? { blocked } : {}), planeDeferred: true, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
782
- logger.warn("models_tiers_deferred_no_handoff", { version: r.effective.version, blockedReasons: blocked, note: "late-boot plane deferred but no NEXT-BOOT handoff (see CONFIG_LKG_DURABLE) — the WHOLE restart signal is withheld (it would loop); old generation keeps serving" });
783
- }
784
- }
785
- else if (planeDeferredLate === false && lkgSurvivesRestart && !lkgPersistedLate) {
879
+ const reasons = restartReasons(undefined, r.effective, restartCtx(consolidationSeat?.read()));
880
+ if (lkgSurvivesRestart && !lkgPersistedLate) {
786
881
  const blocked = reasons.splice(0, reasons.length);
787
- planeDeferredNoHandoff = { version: r.effective.version, since: planeDeferredNoHandoff?.since ?? Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
882
+ planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
788
883
  logger.warn("restart_candidate_publication_incomplete", { version: r.effective.version, blockedReasons: blocked, note: "late-boot candidate publication incomplete on a durable-declared deployment — restart signal withheld; retried every tick" });
789
884
  }
790
885
  if (reasons.length > 0)
@@ -793,7 +888,15 @@ export async function createConfigCenterRuntime(ctx) {
793
888
  };
794
889
  if (bootConfigPending) {
795
890
  const arrivalGen = reserveArrivalGeneration();
796
- void bootConfigPending.then((r) => void (lkgBooted ? runTick({ result: r, ordinal: arrivalGen }) : deferredBootApply(r, arrivalGen)), () => { });
891
+ const continueArrival = (r) => void (lkgBooted ? runTick({ result: r, ordinal: arrivalGen }) : deferredBootApply(r, arrivalGen));
892
+ void bootConfigPending.then((r) => {
893
+ if (r !== null && configProvider.kind === "remote" && candidateIdentityOf(r.effective) !== currentIdentity()) {
894
+ logger.warn("config_boot_arrival_identity_changed", { note: "the late boot pull landed under a center credential that is no longer current — not adopted; re-pulling once under the current credential" });
895
+ void pullEffective(configProvider, undefined).then(continueArrival, (err) => logger.warn("config_boot_arrival_repull_failed", { err: String(err), note: "the refresh cadence retries under the current credential" }));
896
+ return;
897
+ }
898
+ continueArrival(r);
899
+ }, () => { });
797
900
  }
798
901
  }
799
902
  },
@@ -1,4 +1,3 @@
1
- import { NodeLspManager } from "@sema-agent/core";
2
1
  import { TaskEnvRegistry } from "../capabilities/sandbox-file-send.js";
3
2
  import { SessionEnvironmentSelection } from "../capabilities/select-environment-tool.js";
4
3
  import type { ServiceConfig } from "../config.js";
@@ -67,6 +66,6 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
67
66
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
68
67
  worktreeReap: (() => Promise<void>) | undefined;
69
68
  sendUserFileTaskEnvs: TaskEnvRegistry | undefined;
70
- lspManager: import("../lsp/manager.js").E2bLspManager | NodeLspManager | undefined;
69
+ lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
71
70
  };
72
71
  //# sourceMappingURL=execution-env.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { NodeLspManager } from "@sema-agent/core";
3
+ import { createHostLspManager } from "../host-lsp-manager.js";
4
4
  import { sandboxSendLaneEnabled, TaskEnvRegistry } from "../capabilities/sandbox-file-send.js";
5
5
  import { SessionEnvironmentSelection } from "../capabilities/select-environment-tool.js";
6
6
  import { createE2bLspManager } from "../lsp/e2b-manager.js";
@@ -290,7 +290,7 @@ export function createExecutionEnv(ctx) {
290
290
  })
291
291
  :
292
292
  lspProvider === "host" && config.lspHostEnabled && config.requirePrincipal !== true
293
- ? new NodeLspManager({ log: (event, fields) => logger.info(event, fields) })
293
+ ? createHostLspManager((event, fields) => logger.info(event, fields))
294
294
  : undefined;
295
295
  if (lspManager)
296
296
  logger.info("lsp_enabled", { provider: lspProvider });