@sema-agent/server 1.222.0 → 1.224.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.
@@ -1 +1 @@
1
- {"version":3,"file":"center-prompts.d.ts","sourceRoot":"","sources":["../../src/capabilities/center-prompts.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAI9E,MAAM,WAAW,sBAAsB;IAErC,MAAM,EAAE,MAAM,CAAC;IAEf,aAAa,EAAE,MAAM,CAAC;IAEtB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAElC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC;CAC7D;AAsBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAmC9H;AAOD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,GAAG,cAAc,CAK1G"}
1
+ {"version":3,"file":"center-prompts.d.ts","sourceRoot":"","sources":["../../src/capabilities/center-prompts.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAI9E,MAAM,WAAW,sBAAsB;IAErC,MAAM,EAAE,MAAM,CAAC;IAEf,aAAa,EAAE,MAAM,CAAC;IAEtB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAElC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC;CAC7D;AA4BD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAmC9H;AAOD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,GAAG,cAAc,CAK1G"}
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  const ID_RE = /^[a-z0-9][a-z0-9/._-]*$/;
2
3
  const SLOTS = new Set(["identity", "scenario", "behavior"]);
3
4
  const HASH_RE = /^sha256:[0-9a-f]{64}$/;
@@ -13,8 +14,11 @@ function declarationIssue(d, where) {
13
14
  return `${where}: bad slot`;
14
15
  if (typeof o.text !== "string" || o.text.length === 0)
15
16
  return `${where}: text must be a non-empty string`;
16
- if (o.contentHash !== undefined && (typeof o.contentHash !== "string" || !HASH_RE.test(o.contentHash)))
17
- return `${where}: bad contentHash form`;
17
+ if (typeof o.contentHash !== "string" || !HASH_RE.test(o.contentHash))
18
+ return `${where}: contentHash is required (sha256:<64hex> — the epoch declaration axis needs it for byte-level change detection)`;
19
+ const expected = `sha256:${createHash("sha256").update(o.text).digest("hex")}`;
20
+ if (o.contentHash !== expected)
21
+ return `${where}: contentHash does not match sha256(text) — stale or tampered declaration`;
18
22
  return undefined;
19
23
  }
20
24
  export function validateCenterPrompts(raw) {
@@ -1 +1 @@
1
- {"version":3,"file":"center-prompts.js","sourceRoot":"","sources":["../../src/capabilities/center-prompts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"center-prompts.js","sourceRoot":"","sources":["../../src/capabilities/center-prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface PersistedLkg {
2
+ formatVersion: 1;
3
+ savedAt: string;
4
+ worker: string;
5
+ etag?: string;
6
+ effective: Record<string, unknown>;
7
+ }
8
+ export declare function defaultSkillCacheDir(): string;
9
+ export declare function defaultLkgPath(worker: string | undefined): string;
10
+ export declare function saveLkg(path: string, worker: string | undefined, effective: Record<string, unknown>, etag: string | undefined): Promise<void>;
11
+ export declare function loadLkg(path: string, worker: string | undefined): Promise<{
12
+ lkg: PersistedLkg;
13
+ } | {
14
+ lkg?: undefined;
15
+ reason: string;
16
+ }>;
17
+ //# sourceMappingURL=config-lkg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-lkg.d.ts","sourceRoot":"","sources":["../src/config-lkg.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAKD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAID,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAIjE;AAID,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnJ;AAMD,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,GAAG,CAAC,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA0C5I"}
@@ -0,0 +1,72 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join, dirname } from "node:path";
4
+ import { randomBytes } from "node:crypto";
5
+ import { validateCenterPrompts } from "./capabilities/center-prompts.js";
6
+ export function defaultSkillCacheDir() {
7
+ return join(process.env.SEMA_CONFIG_DIR ?? join(homedir(), ".sema"), "skill-cache");
8
+ }
9
+ export function defaultLkgPath(worker) {
10
+ const dir = process.env.SEMA_CONFIG_DIR ?? join(homedir(), ".sema");
11
+ const key = (worker ?? "global").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 64);
12
+ return join(dir, `config-lkg-${key}.json`);
13
+ }
14
+ export async function saveLkg(path, worker, effective, etag) {
15
+ const body = { formatVersion: 1, savedAt: new Date().toISOString(), worker: worker ?? "global", ...(etag !== undefined ? { etag } : {}), effective };
16
+ const tmp = `${path}.${randomBytes(6).toString("hex")}.tmp`;
17
+ await fs.mkdir(dirname(path), { recursive: true });
18
+ await fs.writeFile(tmp, JSON.stringify(body), { mode: 0o600 });
19
+ await fs.rename(tmp, path);
20
+ }
21
+ export async function loadLkg(path, worker) {
22
+ let text;
23
+ try {
24
+ text = await fs.readFile(path, "utf8");
25
+ }
26
+ catch {
27
+ return { reason: "absent" };
28
+ }
29
+ let parsed;
30
+ try {
31
+ parsed = JSON.parse(text);
32
+ }
33
+ catch {
34
+ return { reason: "corrupt json" };
35
+ }
36
+ if (parsed === null || typeof parsed !== "object")
37
+ return { reason: "bad envelope" };
38
+ const p = parsed;
39
+ if (p.formatVersion !== 1)
40
+ return { reason: `unknown formatVersion ${String(p.formatVersion)}` };
41
+ if (typeof p.worker !== "string" || p.worker !== (worker ?? "global"))
42
+ return { reason: `worker mismatch (file=${String(p.worker)})` };
43
+ if (p.effective === null || typeof p.effective !== "object")
44
+ return { reason: "bad effective" };
45
+ if (p.etag !== undefined && typeof p.etag !== "string")
46
+ return { reason: "bad etag" };
47
+ const eff = p.effective;
48
+ if (typeof eff.version !== "number")
49
+ return { reason: "bad effective.version" };
50
+ for (const [domain, arrayKey] of [["models", "models"], ["teams", null], ["skills", "skills"], ["mcp", "servers"], ["scenarios", "scenarios"], ["runtime", null]]) {
51
+ const v = eff[domain];
52
+ if (v === undefined)
53
+ continue;
54
+ if (domain === "teams") {
55
+ if (!Array.isArray(v))
56
+ return { reason: "bad effective.teams" };
57
+ continue;
58
+ }
59
+ if (v === null || typeof v !== "object" || Array.isArray(v))
60
+ return { reason: `bad effective.${domain}` };
61
+ if (arrayKey !== null) {
62
+ const arr = v[arrayKey];
63
+ if (arr !== undefined && !Array.isArray(arr))
64
+ return { reason: `bad effective.${domain}.${arrayKey}` };
65
+ }
66
+ }
67
+ const promptsRaw = p.effective.prompts;
68
+ if (promptsRaw !== undefined && !validateCenterPrompts(promptsRaw).ok)
69
+ return { reason: "prompts face fails re-validation" };
70
+ return { lkg: p };
71
+ }
72
+ //# sourceMappingURL=config-lkg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-lkg.js","sourceRoot":"","sources":["../src/config-lkg.ts"],"names":[],"mappings":"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"}
package/dist/main.js CHANGED
@@ -2,7 +2,7 @@ import { readFileSync, existsSync, realpathSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { loadRemoteExec } from "@sema-agent/registry-core/node";
5
- import { Runner, TtlSessionStore, uuidv7, defaultTaskRegistry, combinePolicies, createDurableQuestionPolicy, createAllowDenyPolicy, tightenTaskSpec, isThinkingLevel, QUESTION_AWAITS_RESUME, FileWorkflowRunStore, InMemoryWorkflowRunStore, workflowsCapability, NodeLspManager, createFileWorkflowScriptStore, expandTiers, NodeExecutionEnv } from "@sema-agent/core";
5
+ import { Runner, TtlSessionStore, uuidv7, FileRosterStore, defaultTaskRegistry, combinePolicies, createDurableQuestionPolicy, createAllowDenyPolicy, tightenTaskSpec, isThinkingLevel, QUESTION_AWAITS_RESUME, FileWorkflowRunStore, InMemoryWorkflowRunStore, workflowsCapability, NodeLspManager, createFileWorkflowScriptStore, expandTiers, NodeExecutionEnv } from "@sema-agent/core";
6
6
  import { PgMemoryEngineBackend, ensurePgMemoryEngineSchema } from "./plugins/memory-engine-pg.js";
7
7
  import { TiDBMemoryEngineBackend, ensureTiDBMemoryEngineSchema } from "./plugins/memory-engine-tidb.js";
8
8
  import { PgMemoryHistoryStore, ensurePgMemoryHistorySchema, PgMemorySyncStore, ensurePgMemorySyncSchema } from "./plugins/memory-sync-store-pg.js";
@@ -71,6 +71,8 @@ import { applyEffective, mutateInPlace, logEffectiveDiff, applyCenterSkills, res
71
71
  import { ensureSealedKeyStore, reportExecutionPublicKey } from "./sealed-key.js";
72
72
  import { createConfigProvider, raceBootFetch, BOOT_FETCH_DEFERRED } from "./config-provider.js";
73
73
  import { validateCenterPrompts, centerPromptProvider } from "./capabilities/center-prompts.js";
74
+ import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg } from "./config-lkg.js";
75
+ import { TiDBRosterStore, PgRosterStore, ensureTiDBRosterSchema, ensurePgRosterSchema } from "./plugins/roster-store-sql.js";
74
76
  import { createPrincipalCapsClient, gateExecutionLane, scopedTokenNeedsWorker, applyObserverEnvOptIn } from "./runtime-caps-resolver.js";
75
77
  import { applyRuntimeGovernance, stripDelegationTools } from "./runtime-governance.js";
76
78
  import { parseTaskSettings, applyTaskSettings, coercePermissionMode, withPermissionMode, effectiveThinking } from "./task-settings.js";
@@ -175,20 +177,75 @@ async function main() {
175
177
  centerPrompts = v.value;
176
178
  };
177
179
  let bootConfigPending;
180
+ const lkgEnabled = configProvider?.kind === "remote" && process.env.CONFIG_LKG_DISABLED !== "true";
181
+ const lkgPath = process.env.CONFIG_LKG_PATH ?? defaultLkgPath(cc?.worker);
182
+ let lkgBooted = false;
183
+ let bootLkgCandidate;
184
+ const persistLkgDurable = async (effectiveObj, etag) => {
185
+ if (!lkgEnabled)
186
+ return;
187
+ try {
188
+ if (effectiveObj.skills && config.configCenter) {
189
+ await applyCenterSkills([], effectiveObj.skills, config.configCenter.baseUrl, config.configCenter.token, logger, undefined, defaultSkillCacheDir());
190
+ }
191
+ await saveLkg(lkgPath, cc?.worker, effectiveObj, etag);
192
+ }
193
+ catch (err) {
194
+ logger.warn("config_lkg_save_failed", { path: lkgPath, err: String(err), note: "durable persist failed — restart signal proceeds anyway (a dead disk + dead center double-fault can re-loop; fix the disk)" });
195
+ }
196
+ };
178
197
  if (configProvider) {
179
198
  const dryRun = cc?.dryRun ?? false;
199
+ const bootFromLkg = async (why) => {
200
+ if (!lkgEnabled || dryRun)
201
+ return null;
202
+ const read = await loadLkg(lkgPath, cc?.worker);
203
+ if (!read.lkg) {
204
+ if (read.reason !== "absent")
205
+ 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" });
206
+ return null;
207
+ }
208
+ 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" });
209
+ return { effective: read.lkg.effective, ...(read.lkg.etag !== undefined ? { etag: read.lkg.etag } : {}) };
210
+ };
211
+ const bootApplyLkgInline = (lkgR) => {
212
+ applyEffective(config, lkgR.effective, logger, { sealedKeys });
213
+ adoptCenterPrompts(lkgR.effective, "boot-lkg");
214
+ markRosterLanded(lkgR.effective);
215
+ effective = lkgR.effective;
216
+ latestEffective = lkgR.effective;
217
+ if (lkgR.etag !== undefined)
218
+ ccEtag = lkgR.etag;
219
+ const cfgModels = (lkgR.effective.models?.models ?? []).filter((m) => m.enabled !== false).length;
220
+ logger.info("config_loaded", { source: "lkg", version: lkgR.effective.version, worker: cc?.worker ?? "(global)", models: cfgModels });
221
+ };
180
222
  try {
181
223
  const bootFetch = configProvider.fetchEffective(undefined);
182
224
  const raced = await raceBootFetch(bootFetch, config.configBootFetchBudgetMs);
225
+ let lkgFallback = null;
183
226
  if (raced === BOOT_FETCH_DEFERRED) {
184
227
  bootConfigPending = bootFetch;
185
228
  bootFetch.catch(() => { });
229
+ lkgFallback = await bootFromLkg("boot fetch deferred (budget exceeded)");
230
+ lkgBooted = lkgFallback !== null;
186
231
  logger.warn("config_boot_fetch_deferred", {
187
232
  source: configProvider.kind,
188
233
  budgetMs: config.configBootFetchBudgetMs,
189
- note: "center slow/unreachable — serving on the env fallback now; the pull continues in the background and hot-applies on arrival (roster gate opens then). Raise CONFIG_BOOT_FETCH_BUDGET_MS to block boot instead.",
234
+ lkg: lkgBooted,
235
+ note: lkgBooted
236
+ ? "center slow — booting from the persisted LKG; the pull continues in the background and reconciles via the refresh path on arrival"
237
+ : "center slow/unreachable and no usable LKG — serving on the env fallback; the pull continues in the background (late-boot adoption on arrival). Raise CONFIG_BOOT_FETCH_BUDGET_MS to block boot instead.",
190
238
  });
191
239
  }
240
+ if (raced === BOOT_FETCH_DEFERRED && lkgFallback) {
241
+ try {
242
+ bootApplyLkgInline(lkgFallback);
243
+ }
244
+ catch (applyErr) {
245
+ lkgBooted = false;
246
+ logger.warn("config_lkg_apply_failed", { err: String(applyErr), note: "persisted LKG failed to apply — env fallback serves; the cache rewrites on the next clean candidate" });
247
+ }
248
+ }
192
249
  const r = raced === BOOT_FETCH_DEFERRED ? null : raced;
193
250
  if (r) {
194
251
  const bootPromptsRaw = r.effective.prompts;
@@ -208,6 +265,8 @@ async function main() {
208
265
  effective = r.effective;
209
266
  if (bootClean)
210
267
  latestEffective = r.effective;
268
+ if (bootClean && !lkgBooted)
269
+ bootLkgCandidate = { effective: r.effective, etag: r.etag };
211
270
  const cfgModels = (r.effective.models?.models ?? []).filter((m) => m.enabled !== false).length;
212
271
  const cfgDir = configProvider.kind === "local" ? { dir: configProvider.root } : {};
213
272
  logger.info("config_loaded", { source: configProvider.kind, version: r.effective.version, worker: cc?.worker ?? "(global)", models: cfgModels, ...cfgDir });
@@ -218,6 +277,17 @@ async function main() {
218
277
  }
219
278
  catch (err) {
220
279
  logger.warn("config_source_unreachable_using_env", { source: configProvider.kind, err: String(err) });
280
+ const lkgFallback = await bootFromLkg(`boot fetch failed: ${String(err).slice(0, 120)}`);
281
+ if (lkgFallback) {
282
+ lkgBooted = true;
283
+ try {
284
+ bootApplyLkgInline(lkgFallback);
285
+ }
286
+ catch (applyErr) {
287
+ lkgBooted = false;
288
+ logger.warn("config_lkg_apply_failed", { err: String(applyErr), note: "persisted LKG failed to apply — env fallback serves" });
289
+ }
290
+ }
221
291
  }
222
292
  }
223
293
  if (!modelReadyState.ready)
@@ -333,6 +403,25 @@ async function main() {
333
403
  else {
334
404
  memoryEngine = memoryEngineBackendFor(config);
335
405
  }
406
+ let rosterStore;
407
+ {
408
+ const mysqlPool = backend?.mysqlPool?.();
409
+ const pgPool = backend?.pgPool?.();
410
+ if (pgPool) {
411
+ const q = async (text, params) => { const r = await pgPool.query(text, params); return { rows: r.rows }; };
412
+ await ensurePgRosterSchema(q);
413
+ rosterStore = new PgRosterStore(q);
414
+ }
415
+ else if (mysqlPool) {
416
+ await ensureTiDBRosterSchema(mysqlPool);
417
+ rosterStore = new TiDBRosterStore(mysqlPool);
418
+ }
419
+ else if (backend?.kind === "local") {
420
+ rosterStore = new FileRosterStore(join(config.localDataRoot ?? localRoot, "roster.json"));
421
+ }
422
+ if (rosterStore)
423
+ logger.info("roster_store_enabled", { backend: pgPool ? "pg" : mysqlPool ? "tidb" : "file" });
424
+ }
336
425
  const memoryExportBackend = config.memoryEngineBackend !== "file" ? memoryEngine?.backend : undefined;
337
426
  const memoryLane = memoryEngineRemoteLanePosture(config);
338
427
  if (config.memoryEngineBackend === "file" || !config.memoryEngineEnabled) {
@@ -725,6 +814,7 @@ async function main() {
725
814
  warn: (msg, fields) => logger.warn(msg, fields),
726
815
  });
727
816
  const runnerDeps = {
817
+ ...(rosterStore ? { rosterStore } : {}),
728
818
  brain,
729
819
  models: config.models,
730
820
  roles: config.roles,
@@ -971,7 +1061,10 @@ async function main() {
971
1061
  : undefined;
972
1062
  let skills = loadSkills(config.skillsDir);
973
1063
  if (effective?.skills && config.configCenter) {
974
- skills = await applyCenterSkills(skills, effective.skills, config.configCenter.baseUrl, config.configCenter.token, logger);
1064
+ skills = await applyCenterSkills(skills, effective.skills, config.configCenter.baseUrl, config.configCenter.token, logger, undefined, process.env.CONFIG_LKG_DISABLED !== "true" ? defaultSkillCacheDir() : undefined);
1065
+ }
1066
+ if (bootLkgCandidate && lkgEnabled) {
1067
+ await saveLkg(lkgPath, cc?.worker, bootLkgCandidate.effective, bootLkgCandidate.etag).catch((err) => logger.warn("config_lkg_save_failed", { path: lkgPath, err: String(err), note: "boot LKG persist failed — next clean candidate rewrites" }));
975
1068
  }
976
1069
  if (effective?.mcp)
977
1070
  config.mcpServers = resolveMcpServers(effective.mcp, logger);
@@ -1077,12 +1170,12 @@ async function main() {
1077
1170
  if (configProvider) {
1078
1171
  const ccRef = config.configCenter;
1079
1172
  let refreshInFlight = false;
1080
- const refreshTick = async () => {
1173
+ const refreshTick = async (prefetched) => {
1081
1174
  if (refreshInFlight)
1082
1175
  return;
1083
1176
  refreshInFlight = true;
1084
1177
  try {
1085
- const r = await configProvider.fetchEffective(ccEtag);
1178
+ const r = prefetched !== undefined ? prefetched : await configProvider.fetchEffective(ccEtag);
1086
1179
  if (r) {
1087
1180
  const promptsRaw = r.effective.prompts;
1088
1181
  const promptsGate = promptsRaw !== undefined ? validateCenterPrompts(promptsRaw) : { ok: true };
@@ -1114,9 +1207,11 @@ async function main() {
1114
1207
  }
1115
1208
  else {
1116
1209
  applyEffective(config, r.effective, logger, { teamsOnly: true, sealedKeys });
1210
+ adoptCenterPrompts(r.effective, "refresh");
1117
1211
  markRosterLanded(r.effective);
1118
1212
  mutateInPlace(pricing, buildPricing(config.models));
1119
1213
  keyResolver = buildKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
1214
+ await persistLkgDurable(r.effective, r.etag);
1120
1215
  const reasons = restartReasons(effective, r.effective);
1121
1216
  if (reasons.length === 0) {
1122
1217
  pendingRestart = undefined;
@@ -1146,9 +1241,20 @@ async function main() {
1146
1241
  };
1147
1242
  const ccTimer = setInterval(() => void refreshTick(), 60_000);
1148
1243
  ccTimer.unref?.();
1149
- const deferredBootApply = (r) => {
1244
+ const deferredBootApply = async (r) => {
1150
1245
  if (!r)
1151
1246
  return;
1247
+ if (refreshInFlight)
1248
+ return;
1249
+ refreshInFlight = true;
1250
+ try {
1251
+ await deferredBootApplyInner(r);
1252
+ }
1253
+ finally {
1254
+ refreshInFlight = false;
1255
+ }
1256
+ };
1257
+ const deferredBootApplyInner = async (r) => {
1152
1258
  const promptsRaw = r.effective.prompts;
1153
1259
  const promptsGate = promptsRaw !== undefined ? validateCenterPrompts(promptsRaw) : { ok: true };
1154
1260
  if ((r.domainErrors ?? []).length > 0 || !promptsGate.ok) {
@@ -1172,13 +1278,14 @@ async function main() {
1172
1278
  effective = r.effective;
1173
1279
  latestEffective = r.effective;
1174
1280
  ccEtag = r.etag;
1175
- const reasons = restartReasons(undefined, r.effective).filter((sl) => sl !== "prompts");
1281
+ await persistLkgDurable(r.effective, r.etag);
1282
+ const reasons = restartReasons(undefined, r.effective);
1176
1283
  if (reasons.length > 0)
1177
1284
  pendingRestart = { restartRequired: true, reasons, version: r.effective.version, since: Date.now() };
1178
1285
  logger.info("config_loaded_deferred", { source: configProvider.kind, version: r.effective.version, models: (r.effective.models?.models ?? []).filter((m) => m.enabled !== false).length, ...(reasons.length > 0 ? { restartRequired: true, restartReasons: reasons } : {}) });
1179
1286
  };
1180
1287
  if (bootConfigPending)
1181
- void bootConfigPending.then((r) => deferredBootApply(r), () => { });
1288
+ void bootConfigPending.then((r) => (lkgBooted ? refreshTick(r) : deferredBootApply(r)), () => { });
1182
1289
  }
1183
1290
  let keyResolver = buildKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
1184
1291
  const mysqlPool = backend?.mysqlPool();