@adhdev/daemon-standalone 1.0.28-rc.10 → 1.0.28-rc.11
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.
package/dist/index.js
CHANGED
|
@@ -33262,10 +33262,10 @@ var require_dist3 = __commonJS({
|
|
|
33262
33262
|
}
|
|
33263
33263
|
function getDaemonBuildInfo() {
|
|
33264
33264
|
if (cached2) return cached2;
|
|
33265
|
-
const commit = readInjected(true ? "
|
|
33266
|
-
const commitShort = readInjected(true ? "
|
|
33267
|
-
const version2 = readInjected(true ? "1.0.28-rc.
|
|
33268
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
33265
|
+
const commit = readInjected(true ? "5cd04eec1e043188c3ef97821372ad5ff615cd04" : void 0) ?? "unknown";
|
|
33266
|
+
const commitShort = readInjected(true ? "5cd04eec" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
33267
|
+
const version2 = readInjected(true ? "1.0.28-rc.11" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
33268
|
+
const builtAt = readInjected(true ? "2026-07-26T07:08:53.585Z" : void 0);
|
|
33269
33269
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
33270
33270
|
return cached2;
|
|
33271
33271
|
}
|
|
@@ -59929,6 +59929,11 @@ ${cleanBody}`;
|
|
|
59929
59929
|
return errs;
|
|
59930
59930
|
}
|
|
59931
59931
|
if ("cursor_above" in w && "changed" in w) return errs;
|
|
59932
|
+
if ("signal" in w) {
|
|
59933
|
+
if (typeof w.signal !== "string" || !w.signal.trim()) errs.push(`${path46}.signal must be a non-empty string`);
|
|
59934
|
+
if (w.equals !== void 0 && typeof w.equals !== "boolean") errs.push(`${path46}.equals must be a boolean`);
|
|
59935
|
+
return errs;
|
|
59936
|
+
}
|
|
59932
59937
|
if ("elapsed_ms" in w) {
|
|
59933
59938
|
if (typeof w.elapsed_ms !== "number") errs.push(`${path46}.elapsed_ms must be a number`);
|
|
59934
59939
|
return errs;
|
|
@@ -60242,6 +60247,49 @@ ${cleanBody}`;
|
|
|
60242
60247
|
"use strict";
|
|
60243
60248
|
}
|
|
60244
60249
|
});
|
|
60250
|
+
function unavailableSignalSnapshot(now, reason, profile) {
|
|
60251
|
+
return {
|
|
60252
|
+
kind: SIGNAL_SNAPSHOT_KIND,
|
|
60253
|
+
sampledAt: now,
|
|
60254
|
+
available: false,
|
|
60255
|
+
unavailableReason: reason,
|
|
60256
|
+
...profile ? { profile } : {},
|
|
60257
|
+
signals: { final_assistant_present: null, in_turn_progress: null, transcript_growing: null },
|
|
60258
|
+
detail: { msgCount: 0, sourceMtimeMs: 0, ageMs: null }
|
|
60259
|
+
};
|
|
60260
|
+
}
|
|
60261
|
+
function evaluateSignalLeaf(cond, snapshot) {
|
|
60262
|
+
const expected = cond.equals ?? true;
|
|
60263
|
+
if (!snapshot || !snapshot.available) {
|
|
60264
|
+
const why = !snapshot ? "no snapshot" : `unavailable(${snapshot.unavailableReason ?? "unknown"})`;
|
|
60265
|
+
return {
|
|
60266
|
+
value: null,
|
|
60267
|
+
shadowResult: null,
|
|
60268
|
+
detail: `signal ${cond.signal} ${why} \u2192 fail-open`
|
|
60269
|
+
};
|
|
60270
|
+
}
|
|
60271
|
+
const raw = Object.prototype.hasOwnProperty.call(snapshot.signals, cond.signal) ? snapshot.signals[cond.signal] : null;
|
|
60272
|
+
if (raw === null || raw === void 0) {
|
|
60273
|
+
return {
|
|
60274
|
+
value: null,
|
|
60275
|
+
shadowResult: null,
|
|
60276
|
+
detail: `signal ${cond.signal} unknown \u2192 fail-open`
|
|
60277
|
+
};
|
|
60278
|
+
}
|
|
60279
|
+
const shadowResult = raw === expected;
|
|
60280
|
+
return {
|
|
60281
|
+
value: raw,
|
|
60282
|
+
shadowResult,
|
|
60283
|
+
detail: `signal ${cond.signal}=${raw} expected=${expected} \u2192 ${shadowResult}`
|
|
60284
|
+
};
|
|
60285
|
+
}
|
|
60286
|
+
var SIGNAL_SNAPSHOT_KIND;
|
|
60287
|
+
var init_signal_envelope = __esm2({
|
|
60288
|
+
"src/providers/spec/signal-envelope.ts"() {
|
|
60289
|
+
"use strict";
|
|
60290
|
+
SIGNAL_SNAPSHOT_KIND = "adhdev:fsm/signal-snapshot@0";
|
|
60291
|
+
}
|
|
60292
|
+
});
|
|
60245
60293
|
var fsm_evaluator_exports = {};
|
|
60246
60294
|
__export2(fsm_evaluator_exports, {
|
|
60247
60295
|
evaluateConditionPreview: () => evaluateConditionPreview,
|
|
@@ -60274,24 +60322,74 @@ ${cleanBody}`;
|
|
|
60274
60322
|
function isNot(c) {
|
|
60275
60323
|
return "not" in c;
|
|
60276
60324
|
}
|
|
60277
|
-
function
|
|
60325
|
+
function isSignal(c) {
|
|
60326
|
+
return "signal" in c;
|
|
60327
|
+
}
|
|
60328
|
+
function containsSignal(c) {
|
|
60329
|
+
if (isSignal(c)) return true;
|
|
60330
|
+
if (isAll(c)) return c.all.some(containsSignal);
|
|
60331
|
+
if (isAny(c)) return c.any.some(containsSignal);
|
|
60332
|
+
if (isNot(c)) return containsSignal(c.not);
|
|
60333
|
+
return false;
|
|
60334
|
+
}
|
|
60335
|
+
function foldShadow(cond) {
|
|
60336
|
+
if (cond.kind === "signal") {
|
|
60337
|
+
const s2 = cond.signal;
|
|
60338
|
+
if (s2 && s2.shadowResult !== null && s2.shadowResult !== void 0) {
|
|
60339
|
+
return { value: s2.shadowResult, unknown: false };
|
|
60340
|
+
}
|
|
60341
|
+
return { value: true, unknown: true };
|
|
60342
|
+
}
|
|
60343
|
+
if (cond.kind === "not" && cond.children?.length) {
|
|
60344
|
+
const c = foldShadow(cond.children[0]);
|
|
60345
|
+
return { value: !c.value, unknown: c.unknown };
|
|
60346
|
+
}
|
|
60347
|
+
if (cond.kind === "all" && cond.children) {
|
|
60348
|
+
const kids = cond.children.map(foldShadow);
|
|
60349
|
+
if (kids.some((k) => !k.unknown && !k.value)) return { value: false, unknown: false };
|
|
60350
|
+
if (kids.some((k) => k.unknown)) return { value: true, unknown: true };
|
|
60351
|
+
return { value: true, unknown: false };
|
|
60352
|
+
}
|
|
60353
|
+
if (cond.kind === "any" && cond.children) {
|
|
60354
|
+
const kids = cond.children.map(foldShadow);
|
|
60355
|
+
if (kids.some((k) => !k.unknown && k.value)) return { value: true, unknown: false };
|
|
60356
|
+
if (kids.some((k) => k.unknown)) return { value: false, unknown: true };
|
|
60357
|
+
return { value: false, unknown: false };
|
|
60358
|
+
}
|
|
60359
|
+
return { value: cond.result, unknown: false };
|
|
60360
|
+
}
|
|
60361
|
+
function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId, signalSnapshot) {
|
|
60278
60362
|
if (isAll(cond)) {
|
|
60279
|
-
const children = cond.all.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
60363
|
+
const children = cond.all.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId, signalSnapshot));
|
|
60280
60364
|
const result = children.every((c) => c.result);
|
|
60281
60365
|
const remainingMs = result ? 0 : Math.max(0, ...children.filter((c) => !c.result).map((c) => c.remainingMs ?? 0));
|
|
60282
60366
|
return { kind: "all", result, detail: `all(${children.length})`, remainingMs, children };
|
|
60283
60367
|
}
|
|
60284
60368
|
if (isAny(cond)) {
|
|
60285
|
-
const children = cond.any.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
60369
|
+
const children = cond.any.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId, signalSnapshot));
|
|
60286
60370
|
const result = children.some((c) => c.result);
|
|
60287
60371
|
const pending = children.filter((c) => !c.result).map((c) => c.remainingMs ?? Infinity);
|
|
60288
60372
|
const remainingMs = result ? 0 : pending.length ? Math.min(...pending) : 0;
|
|
60289
60373
|
return { kind: "any", result, detail: `any(${children.length})`, remainingMs: Number.isFinite(remainingMs) ? remainingMs : 0, children };
|
|
60290
60374
|
}
|
|
60291
60375
|
if (isNot(cond)) {
|
|
60292
|
-
const child = evalCond(cond.not, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId);
|
|
60376
|
+
const child = evalCond(cond.not, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId, signalSnapshot);
|
|
60293
60377
|
return { kind: "not", result: !child.result, detail: `not`, remainingMs: 0, children: [child] };
|
|
60294
60378
|
}
|
|
60379
|
+
if (isSignal(cond)) {
|
|
60380
|
+
const leaf = evaluateSignalLeaf(cond, signalSnapshot);
|
|
60381
|
+
return {
|
|
60382
|
+
kind: "signal",
|
|
60383
|
+
result: true,
|
|
60384
|
+
detail: `${leaf.detail} (stage0 shadow \u2014 pass-through)`,
|
|
60385
|
+
signal: {
|
|
60386
|
+
name: cond.signal,
|
|
60387
|
+
available: !!signalSnapshot?.available,
|
|
60388
|
+
value: leaf.value,
|
|
60389
|
+
shadowResult: leaf.shadowResult
|
|
60390
|
+
}
|
|
60391
|
+
};
|
|
60392
|
+
}
|
|
60295
60393
|
if (isElapsed(cond)) {
|
|
60296
60394
|
const age = clock.now - clock.stateEnteredAt;
|
|
60297
60395
|
const result = age >= cond.elapsed_ms;
|
|
@@ -60330,7 +60428,7 @@ ${cleanBody}`;
|
|
|
60330
60428
|
const from = Array.isArray(t.from) ? t.from.join("|") : t.from;
|
|
60331
60429
|
return t.label ?? `${from}\u2192${t.to}`;
|
|
60332
60430
|
}
|
|
60333
|
-
function evaluateFsm(spec, currentStateId, screenText, cursor, prevLines, clock) {
|
|
60431
|
+
function evaluateFsm(spec, currentStateId, screenText, cursor, prevLines, clock, signalSnapshot) {
|
|
60334
60432
|
const legacyTrace = [];
|
|
60335
60433
|
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
60336
60434
|
const cleanScreen = lines.join("\n");
|
|
@@ -60346,7 +60444,7 @@ ${cleanBody}`;
|
|
|
60346
60444
|
let cond;
|
|
60347
60445
|
let condResult = true;
|
|
60348
60446
|
if (t.when) {
|
|
60349
|
-
cond = evalCond(t.when, sections, cleanScreen, cursor, prevLines, clock, legacyTrace, `${currentStateId}\u2192${t.to}
|
|
60447
|
+
cond = evalCond(t.when, sections, cleanScreen, cursor, prevLines, clock, legacyTrace, `${currentStateId}\u2192${t.to}`, signalSnapshot);
|
|
60350
60448
|
condResult = cond.result;
|
|
60351
60449
|
}
|
|
60352
60450
|
const fires = holdSatisfied && condResult;
|
|
@@ -60361,6 +60459,14 @@ ${cleanBody}`;
|
|
|
60361
60459
|
fires,
|
|
60362
60460
|
priority: t.priority ?? 0
|
|
60363
60461
|
};
|
|
60462
|
+
if (t.when && containsSignal(t.when) && cond) {
|
|
60463
|
+
const folded = foldShadow(cond);
|
|
60464
|
+
te.shadow = {
|
|
60465
|
+
condResult: folded.value,
|
|
60466
|
+
fires: holdSatisfied && folded.value,
|
|
60467
|
+
unknown: folded.unknown
|
|
60468
|
+
};
|
|
60469
|
+
}
|
|
60364
60470
|
transitions.push(te);
|
|
60365
60471
|
if (fires && !fired) fired = te;
|
|
60366
60472
|
}
|
|
@@ -60383,6 +60489,7 @@ ${cleanBody}`;
|
|
|
60383
60489
|
"use strict";
|
|
60384
60490
|
init_evaluator();
|
|
60385
60491
|
init_fsm_types();
|
|
60492
|
+
init_signal_envelope();
|
|
60386
60493
|
WHOLE_SCREEN = -1;
|
|
60387
60494
|
}
|
|
60388
60495
|
});
|
|
@@ -78044,12 +78151,21 @@ ${body}
|
|
|
78044
78151
|
appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
|
|
78045
78152
|
await waitForPidExit(payload.parentPid, 15e3);
|
|
78046
78153
|
}
|
|
78047
|
-
if (process.platform === "win32") {
|
|
78154
|
+
if (process.platform === "win32" || payload.killSessionHost === true) {
|
|
78048
78155
|
await stopSessionHostProcesses(sessionHostAppName);
|
|
78049
78156
|
} else {
|
|
78050
78157
|
appendUpgradeLog("POSIX \u2014 session-host left running (survives upgrade; sessions rebind on next boot)");
|
|
78051
78158
|
}
|
|
78052
78159
|
removeDaemonPidFile();
|
|
78160
|
+
if (payload.skipInstall) {
|
|
78161
|
+
appendUpgradeLog("Restart-only mode \u2014 package install skipped, re-spawning daemon");
|
|
78162
|
+
spawnDetachedDaemonRestart(restartArgv, payload.cwd);
|
|
78163
|
+
try {
|
|
78164
|
+
fs15.unlinkSync(getUpgradeFailureNoticePath());
|
|
78165
|
+
} catch {
|
|
78166
|
+
}
|
|
78167
|
+
return;
|
|
78168
|
+
}
|
|
78053
78169
|
const instanceDir = resolveInstanceDir();
|
|
78054
78170
|
const windowsInstallerLayout = resolveWindowsInstallerLayout({
|
|
78055
78171
|
homeDir: os14.homedir(),
|
|
@@ -78168,6 +78284,13 @@ ${body}
|
|
|
78168
78284
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
78169
78285
|
appendUpgradeLog("Post-install staging cleanup complete");
|
|
78170
78286
|
}
|
|
78287
|
+
spawnDetachedDaemonRestart(restartArgv, payload.cwd);
|
|
78288
|
+
try {
|
|
78289
|
+
fs15.unlinkSync(getUpgradeFailureNoticePath());
|
|
78290
|
+
} catch {
|
|
78291
|
+
}
|
|
78292
|
+
}
|
|
78293
|
+
function spawnDetachedDaemonRestart(restartArgv, cwd) {
|
|
78171
78294
|
if (restartArgv.length > 0) {
|
|
78172
78295
|
const env2 = { ...process.env };
|
|
78173
78296
|
delete env2[UPGRADE_HELPER_ENV];
|
|
@@ -78176,17 +78299,13 @@ ${body}
|
|
|
78176
78299
|
detached: true,
|
|
78177
78300
|
stdio: "ignore",
|
|
78178
78301
|
windowsHide: true,
|
|
78179
|
-
cwd:
|
|
78302
|
+
cwd: cwd || process.cwd(),
|
|
78180
78303
|
env: env2
|
|
78181
78304
|
});
|
|
78182
78305
|
child.unref();
|
|
78183
78306
|
} else {
|
|
78184
78307
|
appendUpgradeLog("No restart argv provided; upgrade completed without restart");
|
|
78185
78308
|
}
|
|
78186
|
-
try {
|
|
78187
|
-
fs15.unlinkSync(getUpgradeFailureNoticePath());
|
|
78188
|
-
} catch {
|
|
78189
|
-
}
|
|
78190
78309
|
}
|
|
78191
78310
|
async function maybeRunDaemonUpgradeHelperFromEnv2() {
|
|
78192
78311
|
const raw = process.env[UPGRADE_HELPER_ENV];
|
|
@@ -78277,6 +78396,41 @@ ${body}
|
|
|
78277
78396
|
return { success: false, error: e.message };
|
|
78278
78397
|
}
|
|
78279
78398
|
},
|
|
78399
|
+
// Restart-only path (mesh restart_daemon_node mode="restart"): re-spawn the
|
|
78400
|
+
// daemon WITHOUT the npm reinstall daemon_upgrade performs. Used to reset
|
|
78401
|
+
// daemon state (memory leaks, zombie sessions, wedged internals) when the
|
|
78402
|
+
// version is already correct — downtime drops to the detached re-spawn.
|
|
78403
|
+
// killSessionHost is an explicit opt-in hard refresh (see upgrade-helper).
|
|
78404
|
+
daemon_restart: async (ctx, args) => {
|
|
78405
|
+
LOG2.info("Restart", "Restart-only requested (no package reinstall)");
|
|
78406
|
+
try {
|
|
78407
|
+
const isStandalone = ctx.deps.packageName === "@adhdev/daemon-standalone" || process.argv[1]?.includes("daemon-standalone");
|
|
78408
|
+
const pkgName = isStandalone ? "@adhdev/daemon-standalone" : "adhdev";
|
|
78409
|
+
const killSessionHost = args?.killSessionHost === true;
|
|
78410
|
+
if (killSessionHost) {
|
|
78411
|
+
LOG2.warn("Restart", "killSessionHost requested \u2014 session-host will be stopped and ALL hosted sessions destroyed");
|
|
78412
|
+
}
|
|
78413
|
+
spawnDetachedDaemonUpgradeHelper({
|
|
78414
|
+
packageName: pkgName,
|
|
78415
|
+
targetVersion: "",
|
|
78416
|
+
parentPid: process.pid,
|
|
78417
|
+
restartArgv: process.argv.slice(1),
|
|
78418
|
+
cwd: process.cwd(),
|
|
78419
|
+
sessionHostAppName: process.env.ADHDEV_SESSION_HOST_NAME || "adhdev",
|
|
78420
|
+
skipInstall: true,
|
|
78421
|
+
killSessionHost
|
|
78422
|
+
});
|
|
78423
|
+
LOG2.info("Restart", "Scheduled detached restart-only helper");
|
|
78424
|
+
setTimeout(() => {
|
|
78425
|
+
LOG2.info("Restart", "Exiting daemon so detached helper can re-spawn it...");
|
|
78426
|
+
process.exit(0);
|
|
78427
|
+
}, 3e3);
|
|
78428
|
+
return { success: true, restarted: true, restarting: true, mode: "restart", killSessionHost };
|
|
78429
|
+
} catch (e) {
|
|
78430
|
+
LOG2.error("Restart", `Failed: ${e.message}`);
|
|
78431
|
+
return { success: false, error: e.message };
|
|
78432
|
+
}
|
|
78433
|
+
},
|
|
78280
78434
|
set_machine_nickname: async (_ctx, args) => {
|
|
78281
78435
|
const nickname = args?.nickname;
|
|
78282
78436
|
updateConfig({ machineNickname: nickname || null });
|
|
@@ -78754,6 +78908,91 @@ ${body}
|
|
|
78754
78908
|
init_provider_input_support();
|
|
78755
78909
|
init_hash();
|
|
78756
78910
|
init_transcript_evidence();
|
|
78911
|
+
init_logger();
|
|
78912
|
+
init_signal_envelope();
|
|
78913
|
+
var TranscriptSignalSource = class {
|
|
78914
|
+
constructor(opts) {
|
|
78915
|
+
this.opts = opts;
|
|
78916
|
+
}
|
|
78917
|
+
/** msgCount seen at the previous update — drives in_turn_progress. */
|
|
78918
|
+
prevMsgCount = -1;
|
|
78919
|
+
/** Fingerprint of the last LOGGED snapshot, so the shadow log fires on
|
|
78920
|
+
* change only (a quiet session must not spam one line per read). */
|
|
78921
|
+
lastLoggedFingerprint = "";
|
|
78922
|
+
/**
|
|
78923
|
+
* Normalize one daemon read into a SignalSnapshot and emit the Stage-0
|
|
78924
|
+
* shadow log when the observation changed. Pure w.r.t. the outside world:
|
|
78925
|
+
* the only side effect is the (change-gated) log line. Returns the
|
|
78926
|
+
* snapshot the caller should inject into the FSM driver.
|
|
78927
|
+
*/
|
|
78928
|
+
update(sample, now = Date.now()) {
|
|
78929
|
+
const snapshot = this.buildSnapshot(sample, now);
|
|
78930
|
+
this.logOnChange(snapshot);
|
|
78931
|
+
return snapshot;
|
|
78932
|
+
}
|
|
78933
|
+
/** Pure normalization — separated from update() so tests can assert the
|
|
78934
|
+
* envelope without touching the log path. */
|
|
78935
|
+
buildSnapshot(sample, now) {
|
|
78936
|
+
const profileRef = { class: this.opts.profile.class, timing: this.opts.profile.timing };
|
|
78937
|
+
if (this.opts.profile.class !== "native-source") {
|
|
78938
|
+
return unavailableSignalSnapshot(now, "no_native_source", profileRef);
|
|
78939
|
+
}
|
|
78940
|
+
if (sample.error) {
|
|
78941
|
+
return unavailableSignalSnapshot(now, "error", profileRef);
|
|
78942
|
+
}
|
|
78943
|
+
const probe = sample.probe;
|
|
78944
|
+
if (!probe || !Array.isArray(sample.messages)) {
|
|
78945
|
+
return unavailableSignalSnapshot(now, "unresolved", profileRef);
|
|
78946
|
+
}
|
|
78947
|
+
const msgCount = typeof probe.msgCount === "number" && Number.isFinite(probe.msgCount) ? probe.msgCount : sample.messages.length;
|
|
78948
|
+
const sourceMtimeMs = typeof probe.sourceMtimeMs === "number" && Number.isFinite(probe.sourceMtimeMs) ? probe.sourceMtimeMs : 0;
|
|
78949
|
+
const ageMs = sourceMtimeMs > 0 ? Math.max(0, now - sourceMtimeMs) : null;
|
|
78950
|
+
let turnStartedAt;
|
|
78951
|
+
try {
|
|
78952
|
+
turnStartedAt = this.opts.turnStartedAt?.();
|
|
78953
|
+
} catch {
|
|
78954
|
+
turnStartedAt = void 0;
|
|
78955
|
+
}
|
|
78956
|
+
let finalAssistant = null;
|
|
78957
|
+
try {
|
|
78958
|
+
finalAssistant = this.opts.finalAssistantPresent(sample.messages, turnStartedAt);
|
|
78959
|
+
} catch {
|
|
78960
|
+
finalAssistant = null;
|
|
78961
|
+
}
|
|
78962
|
+
const countAdvanced = this.prevMsgCount >= 0 && msgCount > this.prevMsgCount;
|
|
78963
|
+
const fresh = ageMs !== null && ageMs < this.opts.growthQuietMs;
|
|
78964
|
+
const inTurnProgress = countAdvanced || fresh;
|
|
78965
|
+
const transcriptGrowing = ageMs === null ? null : fresh;
|
|
78966
|
+
this.prevMsgCount = msgCount;
|
|
78967
|
+
return {
|
|
78968
|
+
kind: SIGNAL_SNAPSHOT_KIND,
|
|
78969
|
+
sampledAt: now,
|
|
78970
|
+
available: true,
|
|
78971
|
+
profile: profileRef,
|
|
78972
|
+
signals: {
|
|
78973
|
+
final_assistant_present: finalAssistant,
|
|
78974
|
+
in_turn_progress: inTurnProgress,
|
|
78975
|
+
transcript_growing: transcriptGrowing
|
|
78976
|
+
},
|
|
78977
|
+
detail: { msgCount, sourceMtimeMs, ageMs }
|
|
78978
|
+
};
|
|
78979
|
+
}
|
|
78980
|
+
/** Stage-0 shadow log: emit one line when the normalized observation
|
|
78981
|
+
* CHANGES. This log is the Stage 1-3 judgment input ("which signals were
|
|
78982
|
+
* actually observable, and when"), so it carries the full signal set —
|
|
78983
|
+
* but only on change, so a steady-state session stays quiet. */
|
|
78984
|
+
logOnChange(snapshot) {
|
|
78985
|
+
const s2 = snapshot.signals;
|
|
78986
|
+
const fingerprint = snapshot.available ? `1|fa=${s2.final_assistant_present}|tp=${s2.in_turn_progress}|tg=${s2.transcript_growing}|n=${snapshot.detail.msgCount}` : `0|${snapshot.unavailableReason ?? "unknown"}`;
|
|
78987
|
+
if (fingerprint === this.lastLoggedFingerprint) return;
|
|
78988
|
+
this.lastLoggedFingerprint = fingerprint;
|
|
78989
|
+
const age = snapshot.detail.ageMs === null ? "n/a" : `${snapshot.detail.ageMs}ms`;
|
|
78990
|
+
LOG2.info(
|
|
78991
|
+
"TranscriptSignalSource",
|
|
78992
|
+
`[${this.opts.label}] [shadow] signals available=${snapshot.available}` + (snapshot.available ? ` final_assistant_present=${s2.final_assistant_present} in_turn_progress=${s2.in_turn_progress} transcript_growing=${s2.transcript_growing} msgCount=${snapshot.detail.msgCount} age=${age}` : ` reason=${snapshot.unavailableReason ?? "unknown"}`)
|
|
78993
|
+
);
|
|
78994
|
+
}
|
|
78995
|
+
};
|
|
78757
78996
|
var fs222 = __toESM2(require("fs"));
|
|
78758
78997
|
var path26 = __toESM2(require("path"));
|
|
78759
78998
|
init_provider_cli_adapter();
|
|
@@ -79107,6 +79346,15 @@ ${body}
|
|
|
79107
79346
|
* transition — the rich pre-transition table that lastFsmEval only keeps
|
|
79108
79347
|
* for the single most recent evaluation. Separate from stateHistory. */
|
|
79109
79348
|
fsmSnapshotHistory = [];
|
|
79349
|
+
/** TX-FSM Stage 0 (shadow): the latest daemon-injected signal observation.
|
|
79350
|
+
* Read by evalFsmNow for the shadow verdict of `signal` conditions ONLY —
|
|
79351
|
+
* the Stage-0 pass-through in the evaluator means it can never alter
|
|
79352
|
+
* which transition fires. */
|
|
79353
|
+
signalObservation = null;
|
|
79354
|
+
/** Per-transition last-logged shadow divergence (`${from}→${to}` → whether
|
|
79355
|
+
* the shadow verdict currently disagrees with the real one), so the
|
|
79356
|
+
* shadow log emits on FLIP only, not every frame. */
|
|
79357
|
+
shadowDivergenceLast = /* @__PURE__ */ new Map();
|
|
79110
79358
|
subscribe(listener) {
|
|
79111
79359
|
this.listeners.add(listener);
|
|
79112
79360
|
return () => {
|
|
@@ -79179,6 +79427,16 @@ ${body}
|
|
|
79179
79427
|
updateMeta(meta3, replace = false) {
|
|
79180
79428
|
this.adapter.updateMeta(meta3, replace);
|
|
79181
79429
|
}
|
|
79430
|
+
/**
|
|
79431
|
+
* TX-FSM Stage 0 (shadow): receive the daemon-normalized signal
|
|
79432
|
+
* observation. The driver stores it verbatim — it does NOT poll, parse,
|
|
79433
|
+
* or read anything itself (generic-PTY-engine boundary). The observation
|
|
79434
|
+
* only feeds the shadow verdict of `signal` conditions; the evaluator's
|
|
79435
|
+
* Stage-0 pass-through guarantees it cannot change a transition.
|
|
79436
|
+
*/
|
|
79437
|
+
setSignalObservation(snapshot) {
|
|
79438
|
+
this.signalObservation = snapshot ?? null;
|
|
79439
|
+
}
|
|
79182
79440
|
snapshot() {
|
|
79183
79441
|
return this.adapter.snapshot();
|
|
79184
79442
|
}
|
|
@@ -79369,7 +79627,35 @@ ${body}
|
|
|
79369
79627
|
}
|
|
79370
79628
|
evalFsmNow(screen, cursor, now) {
|
|
79371
79629
|
const prev = this.prevScreenLines.length > 0 ? this.prevScreenLines : void 0;
|
|
79372
|
-
return evaluateFsm(this.spec, this.currentStateId, screen, cursor, prev, this.buildClock(now));
|
|
79630
|
+
return evaluateFsm(this.spec, this.currentStateId, screen, cursor, prev, this.buildClock(now), this.signalObservation);
|
|
79631
|
+
}
|
|
79632
|
+
/**
|
|
79633
|
+
* TX-FSM Stage 0 (shadow): compare each signal-guarded transition's
|
|
79634
|
+
* counterfactual verdict against the real (PTY-only) one and log on FLIP.
|
|
79635
|
+
* This is the "만약 적용했다면" half of the shadow log — the Stage 1-3
|
|
79636
|
+
* evidence for whether signal gating would have changed any verdict, and
|
|
79637
|
+
* in which direction. Read-only: runs after the evaluation is complete
|
|
79638
|
+
* and never feeds back into it.
|
|
79639
|
+
*/
|
|
79640
|
+
logShadowDivergence(ev) {
|
|
79641
|
+
for (const t of ev.transitions) {
|
|
79642
|
+
if (!t.shadow) continue;
|
|
79643
|
+
const key2 = `${this.currentStateId}\u2192${t.to}`;
|
|
79644
|
+
const diverges = t.shadow.fires !== t.fires;
|
|
79645
|
+
if ((this.shadowDivergenceLast.get(key2) ?? false) === diverges) continue;
|
|
79646
|
+
this.shadowDivergenceLast.set(key2, diverges);
|
|
79647
|
+
if (diverges) {
|
|
79648
|
+
LOG2.info(
|
|
79649
|
+
"FsmDriver",
|
|
79650
|
+
`[${this.specTag()}] [shadow] ${key2} (${t.label}): real fires=${t.fires} but signal-gated verdict would be fires=${t.shadow.fires} (shadowCond=${t.shadow.condResult}${t.shadow.unknown ? ", signal unknown/fail-open" : ""})`
|
|
79651
|
+
);
|
|
79652
|
+
} else {
|
|
79653
|
+
LOG2.info(
|
|
79654
|
+
"FsmDriver",
|
|
79655
|
+
`[${this.specTag()}] [shadow] ${key2} (${t.label}): shadow verdict realigned with real fires=${t.fires}`
|
|
79656
|
+
);
|
|
79657
|
+
}
|
|
79658
|
+
}
|
|
79373
79659
|
}
|
|
79374
79660
|
reevaluate(forceEmit = false) {
|
|
79375
79661
|
const now = Date.now();
|
|
@@ -79380,6 +79666,7 @@ ${body}
|
|
|
79380
79666
|
const ev = this.evalFsmNow(screen, cursor, now);
|
|
79381
79667
|
this.lastFsmEval = ev;
|
|
79382
79668
|
this.prevScreenLines = currentLines;
|
|
79669
|
+
this.logShadowDivergence(ev);
|
|
79383
79670
|
if (ev.fired) {
|
|
79384
79671
|
this.commitTransition(ev.fired, now, ev);
|
|
79385
79672
|
this.emitStateChanged(forceEmit);
|
|
@@ -82016,6 +82303,18 @@ ${body}
|
|
|
82016
82303
|
}
|
|
82017
82304
|
refreshProviderDefinition() {
|
|
82018
82305
|
}
|
|
82306
|
+
/**
|
|
82307
|
+
* TX-FSM Stage 0 (shadow): forward the daemon's normalized signal
|
|
82308
|
+
* observation into the FSM driver. Observation-only — failures here must
|
|
82309
|
+
* never break the adapter, and the driver treats the envelope as a pure
|
|
82310
|
+
* injected value (no reads cross the engine boundary).
|
|
82311
|
+
*/
|
|
82312
|
+
setSignalObservation(snapshot) {
|
|
82313
|
+
try {
|
|
82314
|
+
this.driver.setSignalObservation?.(snapshot);
|
|
82315
|
+
} catch {
|
|
82316
|
+
}
|
|
82317
|
+
}
|
|
82019
82318
|
handleEvent(ev) {
|
|
82020
82319
|
this.lastEvent = ev;
|
|
82021
82320
|
switch (ev.kind) {
|
|
@@ -83846,6 +84145,10 @@ ${body}
|
|
|
83846
84145
|
completedDebounceTimer = null;
|
|
83847
84146
|
completedDebouncePending = null;
|
|
83848
84147
|
lastExternalCompletionProbe = null;
|
|
84148
|
+
/** TX-FSM Stage 0 (shadow): lazily-created transcript signal normalizer.
|
|
84149
|
+
* Fed ONLY by transcript reads this instance already performs — it adds
|
|
84150
|
+
* zero I/O and its output never feeds back into any verdict. */
|
|
84151
|
+
transcriptSignalSource = null;
|
|
83849
84152
|
/**
|
|
83850
84153
|
* The final assistant summary of the last completed turn, cached at
|
|
83851
84154
|
* completion-emit time. For a native-source provider (antigravity) whose
|
|
@@ -84011,6 +84314,7 @@ ${body}
|
|
|
84011
84314
|
});
|
|
84012
84315
|
if (restoredHistory.source !== "provider-native") {
|
|
84013
84316
|
this.lastExternalCompletionProbe = null;
|
|
84317
|
+
this.publishTranscriptSignalObservation(null);
|
|
84014
84318
|
return null;
|
|
84015
84319
|
}
|
|
84016
84320
|
this.lastExternalCompletionProbe = buildExternalTranscriptProbe(
|
|
@@ -84018,8 +84322,48 @@ ${body}
|
|
|
84018
84322
|
restoredHistory.sourcePath,
|
|
84019
84323
|
restoredHistory.sourceMtimeMs
|
|
84020
84324
|
);
|
|
84325
|
+
this.publishTranscriptSignalObservation(restoredHistory.messages);
|
|
84021
84326
|
return restoredHistory.messages;
|
|
84022
84327
|
}
|
|
84328
|
+
/**
|
|
84329
|
+
* TX-FSM Stage 0 (shadow): normalize the transcript read that JUST
|
|
84330
|
+
* happened into a SignalSnapshot and inject it into the FSM driver as a
|
|
84331
|
+
* pure observation (daemon → SpecCliAdapter → FsmDriver). Fed ONLY by
|
|
84332
|
+
* reads this method's caller already performs — it adds zero I/O, so the
|
|
84333
|
+
* getState() zero-native-read invariant and the stall-path read cadence
|
|
84334
|
+
* are untouched. The snapshot is shadow data: nothing on this path feeds
|
|
84335
|
+
* back into completion/stall/redrive verdicts. Fail-open end to end — a
|
|
84336
|
+
* missing adapter hook (non-spec providers), an unresolved transcript, or
|
|
84337
|
+
* any throw degrades to "no observation", never to a wedge.
|
|
84338
|
+
*/
|
|
84339
|
+
publishTranscriptSignalObservation(messages, error48 = false) {
|
|
84340
|
+
const adapter = this.adapter;
|
|
84341
|
+
if (typeof adapter?.setSignalObservation !== "function") return;
|
|
84342
|
+
try {
|
|
84343
|
+
if (!this.transcriptSignalSource) {
|
|
84344
|
+
this.transcriptSignalSource = new TranscriptSignalSource({
|
|
84345
|
+
label: this.type,
|
|
84346
|
+
// Choke point: class/timing come from the P0 profile
|
|
84347
|
+
// resolver, never from raw predicates or provider names.
|
|
84348
|
+
profile: resolveTranscriptAuthorityProfile(this.provider),
|
|
84349
|
+
turnStartedAt: () => {
|
|
84350
|
+
const t = this.adapter?.currentTurnStartedAt;
|
|
84351
|
+
return typeof t === "number" && Number.isFinite(t) ? t : void 0;
|
|
84352
|
+
},
|
|
84353
|
+
// Reuse the exact completion machinery (I1) for the
|
|
84354
|
+
// final_assistant_present signal rather than duplicating
|
|
84355
|
+
// the message scan.
|
|
84356
|
+
finalAssistantPresent: (msgs, ts2) => this.completionHasFinalAssistantMessage(msgs, ts2),
|
|
84357
|
+
growthQuietMs: MISSING_ASSISTANT_TRANSCRIPT_GROWTH_QUIET_MS
|
|
84358
|
+
});
|
|
84359
|
+
}
|
|
84360
|
+
const snapshot = this.transcriptSignalSource.update(
|
|
84361
|
+
{ messages, probe: this.lastExternalCompletionProbe, error: error48 }
|
|
84362
|
+
);
|
|
84363
|
+
adapter.setSignalObservation(snapshot);
|
|
84364
|
+
} catch {
|
|
84365
|
+
}
|
|
84366
|
+
}
|
|
84023
84367
|
/**
|
|
84024
84368
|
* The content of the LAST visible assistant bubble in a message list, or ''
|
|
84025
84369
|
* when the tail is not an assistant reply. Skips trailing system/tool/activity
|
|
@@ -95437,17 +95781,134 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
95437
95781
|
}
|
|
95438
95782
|
};
|
|
95439
95783
|
init_dist();
|
|
95784
|
+
init_logger();
|
|
95440
95785
|
var RESTART_BLOCKING_STATES = /* @__PURE__ */ new Set(["generating", "waiting_approval", "starting"]);
|
|
95441
|
-
|
|
95786
|
+
var DEFERRED_RESTART_DEFAULT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
95787
|
+
var DEFERRED_RESTART_MAX_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
|
|
95788
|
+
var DEFERRED_RESTART_POLL_MS = 1e4;
|
|
95789
|
+
function collectBlockingSessions(ctx, meshId) {
|
|
95790
|
+
const blocking = [];
|
|
95442
95791
|
const states = ctx.deps.instanceManager.collectAllStates();
|
|
95792
|
+
const consider = (state) => {
|
|
95793
|
+
const status = String(state?.status || "");
|
|
95794
|
+
if (!RESTART_BLOCKING_STATES.has(status)) return;
|
|
95795
|
+
blocking.push({
|
|
95796
|
+
instanceId: typeof state?.instanceId === "string" ? state.instanceId : null,
|
|
95797
|
+
status,
|
|
95798
|
+
// The coordinator marker is stamped by mesh_coordinator_launch and
|
|
95799
|
+
// re-stamped on restore (cli-manager.restoreHostedSessions). Ad-hoc
|
|
95800
|
+
// coordinator sessions have no marker and are NOT waived by selfOnly.
|
|
95801
|
+
selfCoordinator: !!meshId && state?.settings?.meshCoordinatorFor === meshId
|
|
95802
|
+
});
|
|
95803
|
+
};
|
|
95443
95804
|
for (const state of states) {
|
|
95444
|
-
|
|
95805
|
+
consider(state);
|
|
95445
95806
|
const childStates = "extensions" in state && Array.isArray(state.extensions) ? state.extensions : [];
|
|
95446
|
-
for (const child of childStates)
|
|
95447
|
-
|
|
95448
|
-
|
|
95807
|
+
for (const child of childStates) consider(child);
|
|
95808
|
+
}
|
|
95809
|
+
return blocking;
|
|
95810
|
+
}
|
|
95811
|
+
var pendingDeferredRestart = null;
|
|
95812
|
+
function deferredRestartInfo() {
|
|
95813
|
+
if (!pendingDeferredRestart) return null;
|
|
95814
|
+
return {
|
|
95815
|
+
meshId: pendingDeferredRestart.meshId,
|
|
95816
|
+
nodeId: pendingDeferredRestart.nodeId,
|
|
95817
|
+
mode: pendingDeferredRestart.mode,
|
|
95818
|
+
killSessionHost: pendingDeferredRestart.killSessionHost,
|
|
95819
|
+
scheduledAt: new Date(pendingDeferredRestart.scheduledAt).toISOString(),
|
|
95820
|
+
expiresAt: new Date(pendingDeferredRestart.expiresAt).toISOString(),
|
|
95821
|
+
runCondition: "executes automatically once no session is generating / waiting_approval / starting"
|
|
95822
|
+
};
|
|
95823
|
+
}
|
|
95824
|
+
function clearPendingDeferredRestart() {
|
|
95825
|
+
if (pendingDeferredRestart) clearInterval(pendingDeferredRestart.timer);
|
|
95826
|
+
pendingDeferredRestart = null;
|
|
95827
|
+
}
|
|
95828
|
+
function normalizeRestartMode(value) {
|
|
95829
|
+
return value === "restart" ? "restart" : "upgrade";
|
|
95830
|
+
}
|
|
95831
|
+
function restartWarnings(args) {
|
|
95832
|
+
const warnings = [];
|
|
95833
|
+
if (args.forced) {
|
|
95834
|
+
warnings.push("forced restart over active sessions \u2014 in-flight turns were interrupted and the in-memory pendingOutboundQueue is permanently lost (no persistence/restore path).");
|
|
95835
|
+
}
|
|
95836
|
+
if (args.killSessionHost) {
|
|
95837
|
+
warnings.push("killSessionHost: the session-host process was stopped \u2014 ALL hosted CLI sessions on this machine are destroyed (hard refresh).");
|
|
95838
|
+
}
|
|
95839
|
+
if (process.platform === "win32") {
|
|
95840
|
+
warnings.push("Windows: the daemon restart/upgrade path stops the session-host regardless of options \u2014 all hosted sessions terminate.");
|
|
95841
|
+
} else {
|
|
95842
|
+
warnings.push("POSIX: hosted PTY sessions survive a plain daemon restart and rebind on next boot (unless killSessionHost was set).");
|
|
95843
|
+
}
|
|
95844
|
+
return warnings;
|
|
95845
|
+
}
|
|
95846
|
+
async function executeRestart(ctx, args, opts) {
|
|
95847
|
+
const mode = normalizeRestartMode(args?.mode);
|
|
95848
|
+
const killSessionHost = args?.killSessionHost === true;
|
|
95849
|
+
const result = mode === "restart" ? await daemonLifecycleHandlers.daemon_restart({ deps: ctx.deps }, { killSessionHost }) : await daemonLifecycleHandlers.daemon_upgrade({ deps: ctx.deps }, args);
|
|
95850
|
+
const restarting = result?.restarting === true;
|
|
95851
|
+
return {
|
|
95852
|
+
...result,
|
|
95853
|
+
mode,
|
|
95854
|
+
restarted: restarting,
|
|
95855
|
+
...restarting ? {
|
|
95856
|
+
warnings: restartWarnings({ killSessionHost, forced: opts.forced }),
|
|
95857
|
+
// MCP IPC has no automatic reconnect/retry — tell the caller when to
|
|
95858
|
+
// talk to this daemon again.
|
|
95859
|
+
clientHint: "daemon restarting \u2014 retry your next call in ~10s"
|
|
95860
|
+
} : {}
|
|
95861
|
+
};
|
|
95862
|
+
}
|
|
95863
|
+
function scheduleDeferredRestart(ctx, args, meshId, nodeId) {
|
|
95864
|
+
clearPendingDeferredRestart();
|
|
95865
|
+
const requestedTimeout = typeof args?.timeoutMs === "number" && Number.isFinite(args.timeoutMs) ? args.timeoutMs : DEFERRED_RESTART_DEFAULT_TIMEOUT_MS;
|
|
95866
|
+
const timeoutMs = Math.min(Math.max(requestedTimeout, DEFERRED_RESTART_POLL_MS), DEFERRED_RESTART_MAX_TIMEOUT_MS);
|
|
95867
|
+
const record2 = {
|
|
95868
|
+
meshId,
|
|
95869
|
+
nodeId,
|
|
95870
|
+
mode: normalizeRestartMode(args?.mode),
|
|
95871
|
+
killSessionHost: args?.killSessionHost === true,
|
|
95872
|
+
scheduledAt: Date.now(),
|
|
95873
|
+
expiresAt: Date.now() + timeoutMs,
|
|
95874
|
+
timer: setInterval(() => {
|
|
95875
|
+
void deferredRestartTick(ctx);
|
|
95876
|
+
}, DEFERRED_RESTART_POLL_MS)
|
|
95877
|
+
};
|
|
95878
|
+
record2.timer.unref?.();
|
|
95879
|
+
pendingDeferredRestart = record2;
|
|
95880
|
+
LOG2.info("MeshRestart", `Deferred restart scheduled for node ${nodeId} (mode=${record2.mode}, expires in ${Math.round(timeoutMs / 6e4)}min)`);
|
|
95881
|
+
return {
|
|
95882
|
+
success: true,
|
|
95883
|
+
restarted: false,
|
|
95884
|
+
scheduled: true,
|
|
95885
|
+
code: "restart_scheduled_when_idle",
|
|
95886
|
+
reason: "Restart scheduled \u2014 it will execute automatically as soon as no session on this daemon is generating / waiting_approval / starting. Running at an idle point also avoids pendingOutboundQueue loss, making this the safest restart path.",
|
|
95887
|
+
deferredRestart: deferredRestartInfo()
|
|
95888
|
+
};
|
|
95889
|
+
}
|
|
95890
|
+
async function deferredRestartTick(ctx) {
|
|
95891
|
+
const record2 = pendingDeferredRestart;
|
|
95892
|
+
if (!record2) return;
|
|
95893
|
+
if (Date.now() >= record2.expiresAt) {
|
|
95894
|
+
LOG2.warn("MeshRestart", `Deferred restart for node ${record2.nodeId} expired without reaching an idle point; dropping the schedule`);
|
|
95895
|
+
clearPendingDeferredRestart();
|
|
95896
|
+
return;
|
|
95897
|
+
}
|
|
95898
|
+
const blocking = collectBlockingSessions(ctx, record2.meshId);
|
|
95899
|
+
if (blocking.length > 0) return;
|
|
95900
|
+
LOG2.info("MeshRestart", `Deferred restart for node ${record2.nodeId} executing \u2014 daemon is idle`);
|
|
95901
|
+
clearPendingDeferredRestart();
|
|
95902
|
+
try {
|
|
95903
|
+
await executeRestart(ctx, {
|
|
95904
|
+
meshId: record2.meshId,
|
|
95905
|
+
nodeId: record2.nodeId,
|
|
95906
|
+
mode: record2.mode,
|
|
95907
|
+
killSessionHost: record2.killSessionHost
|
|
95908
|
+
}, { forced: false });
|
|
95909
|
+
} catch (e) {
|
|
95910
|
+
LOG2.error("MeshRestart", `Deferred restart execution failed: ${e?.message || String(e)}`);
|
|
95449
95911
|
}
|
|
95450
|
-
return false;
|
|
95451
95912
|
}
|
|
95452
95913
|
var meshRestartHandlers = {
|
|
95453
95914
|
restart_daemon_node: async (ctx, args) => {
|
|
@@ -95468,16 +95929,43 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
95468
95929
|
});
|
|
95469
95930
|
return forwarded ?? { success: false, error: "no response from remote node" };
|
|
95470
95931
|
}
|
|
95471
|
-
if (
|
|
95932
|
+
if (args?.cancelWhenIdle === true) {
|
|
95933
|
+
const had = pendingDeferredRestart !== null;
|
|
95934
|
+
clearPendingDeferredRestart();
|
|
95935
|
+
return { success: true, restarted: false, cancelled: had, deferredRestart: null };
|
|
95936
|
+
}
|
|
95937
|
+
if (args?.whenIdleStatus === true) {
|
|
95938
|
+
return { success: true, restarted: false, deferredRestart: deferredRestartInfo() };
|
|
95939
|
+
}
|
|
95940
|
+
const blocking = collectBlockingSessions(ctx, meshId);
|
|
95941
|
+
if (blocking.length > 0) {
|
|
95942
|
+
if (args?.force === true) {
|
|
95943
|
+
LOG2.warn("MeshRestart", `force restart over ${blocking.length} blocking session(s): ${blocking.map((b) => `${b.instanceId || "?"}(${b.status})`).join(", ")} \u2014 pendingOutboundQueue will be lost`);
|
|
95944
|
+
return executeRestart(ctx, args, { forced: true });
|
|
95945
|
+
}
|
|
95946
|
+
const foreignBlocking = blocking.filter((b) => !b.selfCoordinator);
|
|
95947
|
+
if (args?.selfOnly === true && foreignBlocking.length === 0) {
|
|
95948
|
+
LOG2.info("MeshRestart", `selfOnly restart: waiving ${blocking.length} self-coordinator session(s) for mesh ${meshId}`);
|
|
95949
|
+
return executeRestart(ctx, args, { forced: false });
|
|
95950
|
+
}
|
|
95951
|
+
if (args?.whenIdle === true) {
|
|
95952
|
+
return scheduleDeferredRestart(ctx, args, meshId, nodeId);
|
|
95953
|
+
}
|
|
95472
95954
|
return {
|
|
95473
95955
|
success: false,
|
|
95474
95956
|
restarted: false,
|
|
95475
95957
|
code: "blocking_sessions",
|
|
95476
|
-
reason: "Daemon has an active session (generating / waiting_approval / starting); restart refused to avoid interrupting in-flight work. Retry when the node is idle."
|
|
95958
|
+
reason: "Daemon has an active session (generating / waiting_approval / starting); restart refused to avoid interrupting in-flight work. Retry when the node is idle.",
|
|
95959
|
+
blockingSessions: blocking,
|
|
95960
|
+
options: {
|
|
95961
|
+
selfOnly: "waive only this mesh's own coordinator session (settings.meshCoordinatorFor === meshId)",
|
|
95962
|
+
force: "bypass the idle-gate entirely \u2014 kills in-flight turns and loses the unpersisted pendingOutboundQueue",
|
|
95963
|
+
whenIdle: "schedule the restart to run automatically once the daemon goes idle (safest)"
|
|
95964
|
+
},
|
|
95965
|
+
deferredRestart: deferredRestartInfo()
|
|
95477
95966
|
};
|
|
95478
95967
|
}
|
|
95479
|
-
|
|
95480
|
-
return { ...result, restarted: result?.restarting === true };
|
|
95968
|
+
return executeRestart(ctx, args, { forced: false });
|
|
95481
95969
|
}
|
|
95482
95970
|
};
|
|
95483
95971
|
var medFamilyRegistry = new Map(
|