@adhdev/daemon-core 0.9.82-rc.461 → 0.9.82-rc.463
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/commands/router-aggregate-status.d.ts +24 -0
- package/dist/commands/router-mesh-session-owner.d.ts +59 -0
- package/dist/commands/router.d.ts +7 -47
- package/dist/index.js +388 -222
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +388 -222
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-pending.d.ts +34 -2
- package/dist/mesh/mesh-events.d.ts +2 -2
- package/dist/mesh/mesh-reconcile-loop.d.ts +12 -0
- package/dist/providers/approval-utils.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +9 -0
- package/dist/providers/spec/types.d.ts +22 -0
- package/dist/repo-mesh-types.d.ts +31 -0
- package/package.json +3 -3
- package/src/commands/high-family/mesh-events.ts +14 -1
- package/src/commands/high-family/mesh-status.ts +19 -2
- package/src/commands/router-aggregate-status.ts +209 -0
- package/src/commands/router-mesh-session-owner.ts +114 -0
- package/src/commands/router.ts +27 -255
- package/src/mesh/mesh-events-pending.ts +99 -5
- package/src/mesh/mesh-events.ts +3 -0
- package/src/mesh/mesh-reconcile-loop.ts +66 -0
- package/src/providers/approval-utils.ts +1 -1
- package/src/providers/cli-provider-instance.ts +41 -12
- package/src/providers/native-history/hermes-cli-transcript.ts +54 -2
- package/src/providers/spec/native-history-executor.ts +73 -6
- package/src/providers/spec/types.ts +22 -0
- package/src/repo-mesh-types.ts +32 -0
package/dist/index.mjs
CHANGED
|
@@ -404,10 +404,10 @@ function readInjected(value) {
|
|
|
404
404
|
}
|
|
405
405
|
function getDaemonBuildInfo() {
|
|
406
406
|
if (cached) return cached;
|
|
407
|
-
const commit = readInjected(true ? "
|
|
408
|
-
const commitShort = readInjected(true ? "
|
|
409
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
410
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
407
|
+
const commit = readInjected(true ? "3441855fe9c04e158a4dc94eed196b7bcba1647e" : void 0) ?? "unknown";
|
|
408
|
+
const commitShort = readInjected(true ? "3441855f" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
409
|
+
const version = readInjected(true ? "0.9.82-rc.463" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
410
|
+
const builtAt = readInjected(true ? "2026-07-05T02:56:26.902Z" : void 0);
|
|
411
411
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
412
412
|
return cached;
|
|
413
413
|
}
|
|
@@ -11475,6 +11475,37 @@ import { randomUUID as randomUUID8 } from "crypto";
|
|
|
11475
11475
|
function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
|
|
11476
11476
|
return expandDaemonIdForms(coordinatorDaemonId);
|
|
11477
11477
|
}
|
|
11478
|
+
function isMeshProtocolV2EnforceEnabled() {
|
|
11479
|
+
const raw = readNonEmptyString2(process.env.MESH_PROTOCOL_V2_ENFORCE);
|
|
11480
|
+
if (!raw) return false;
|
|
11481
|
+
const v = raw.trim().toLowerCase();
|
|
11482
|
+
return v === "1" || v === "true" || v === "on" || v === "yes";
|
|
11483
|
+
}
|
|
11484
|
+
function ledgerRecordQuarantinedEvent(event, reason) {
|
|
11485
|
+
try {
|
|
11486
|
+
const finalSummary = readMeshCompletionSummary(event.metadataEvent || {});
|
|
11487
|
+
appendLedgerEntry(event.meshId, {
|
|
11488
|
+
kind: "event_held",
|
|
11489
|
+
...event.nodeId ? { nodeId: event.nodeId } : {},
|
|
11490
|
+
payload: {
|
|
11491
|
+
event: event.event,
|
|
11492
|
+
reason,
|
|
11493
|
+
recoverable: true,
|
|
11494
|
+
nodeLabel: event.nodeLabel,
|
|
11495
|
+
...event.workspace ? { workspace: event.workspace } : {},
|
|
11496
|
+
targetCoordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
|
|
11497
|
+
...readNonEmptyString2(event.eventId) ? { eventId: event.eventId } : {},
|
|
11498
|
+
queuedAt: event.queuedAt,
|
|
11499
|
+
...finalSummary ? { finalSummary } : {}
|
|
11500
|
+
}
|
|
11501
|
+
});
|
|
11502
|
+
} catch (e) {
|
|
11503
|
+
LOG.warn("MeshEventsV2", `Failed to ledger-record v2-quarantined ${event.event} for mesh ${event.meshId}: ${e?.message || e}`);
|
|
11504
|
+
}
|
|
11505
|
+
}
|
|
11506
|
+
function getMeshV2DrainCounters() {
|
|
11507
|
+
return { ...meshV2DrainCounters };
|
|
11508
|
+
}
|
|
11478
11509
|
function warnV2Once(key2, message) {
|
|
11479
11510
|
if (warnedV2Violations.has(key2)) return;
|
|
11480
11511
|
warnedV2Violations.add(key2);
|
|
@@ -11506,12 +11537,22 @@ function identityDeliversTo(intendedFor, drainer) {
|
|
|
11506
11537
|
}
|
|
11507
11538
|
function routeV2EventsForDrainer(events, drainer, ctx) {
|
|
11508
11539
|
if (!drainer) return events;
|
|
11540
|
+
const enforce = isMeshProtocolV2EnforceEnabled();
|
|
11509
11541
|
const bump = (k) => {
|
|
11510
11542
|
if (ctx.countMetrics) meshV2DrainCounters[k]++;
|
|
11511
11543
|
};
|
|
11512
11544
|
const kept = [];
|
|
11513
11545
|
for (const event of events) {
|
|
11514
11546
|
if (!isV2Event(event)) {
|
|
11547
|
+
if (enforce) {
|
|
11548
|
+
bump("v1UnversionedQuarantined");
|
|
11549
|
+
if (ctx.countMetrics) ledgerRecordQuarantinedEvent(event, "v2_enforce_unversioned_quarantined");
|
|
11550
|
+
warnV2Once(
|
|
11551
|
+
`${event.meshId}::${event.eventId ?? event.event}::v1-quarantined`,
|
|
11552
|
+
`v2 ENFORCE: unversioned ${event.event} on mesh ${event.meshId} QUARANTINED (no v2 envelope \u2014 held back, not delivered; ledger-recorded recoverable). A producer path still emits v1.`
|
|
11553
|
+
);
|
|
11554
|
+
continue;
|
|
11555
|
+
}
|
|
11515
11556
|
bump("v1BroadcastAccepted");
|
|
11516
11557
|
kept.push(event);
|
|
11517
11558
|
continue;
|
|
@@ -11520,6 +11561,15 @@ function routeV2EventsForDrainer(events, drainer, ctx) {
|
|
|
11520
11561
|
try {
|
|
11521
11562
|
validated = assertPendingMeshCoordinatorEventV2(event);
|
|
11522
11563
|
} catch (e) {
|
|
11564
|
+
if (enforce) {
|
|
11565
|
+
bump("v2ValidationFailedQuarantined");
|
|
11566
|
+
if (ctx.countMetrics) ledgerRecordQuarantinedEvent(event, "v2_enforce_validation_failed_quarantined");
|
|
11567
|
+
warnV2Once(
|
|
11568
|
+
`${event.meshId}::${event.eventId ?? event.event}::invalid-quarantined`,
|
|
11569
|
+
`v2 ENFORCE: envelope validation failed for ${event.event} on mesh ${event.meshId} \u2014 QUARANTINED (held back, not delivered; ledger-recorded recoverable): ${e?.message || e}`
|
|
11570
|
+
);
|
|
11571
|
+
continue;
|
|
11572
|
+
}
|
|
11523
11573
|
bump("v2ValidationFailedAccepted");
|
|
11524
11574
|
warnV2Once(
|
|
11525
11575
|
`${event.meshId}::${event.eventId ?? event.event}::invalid`,
|
|
@@ -12101,7 +12151,15 @@ var init_mesh_events_pending = __esm({
|
|
|
12101
12151
|
* coordinatorRunId change orphaned them). */
|
|
12102
12152
|
v2ReattributedToDrainer: 0,
|
|
12103
12153
|
/** v1 (unversioned) events passed through as broadcast (rollout baseline). */
|
|
12104
|
-
v1BroadcastAccepted: 0
|
|
12154
|
+
v1BroadcastAccepted: 0,
|
|
12155
|
+
/** T6 enforce: v2 events that FAILED validation and were QUARANTINED (held back
|
|
12156
|
+
* from delivery, not dropped). Non-zero here means a producer is still emitting a
|
|
12157
|
+
* malformed envelope after enforce was turned on. */
|
|
12158
|
+
v2ValidationFailedQuarantined: 0,
|
|
12159
|
+
/** T6 enforce: v1 (unversioned) events QUARANTINED because no v2 envelope could be
|
|
12160
|
+
* derived at emit time. Non-zero here means a producer path still emits v1 after
|
|
12161
|
+
* enforce — it should reach 0 once every node is on a v2-stamping build. */
|
|
12162
|
+
v1UnversionedQuarantined: 0
|
|
12105
12163
|
};
|
|
12106
12164
|
warnedV2Violations = /* @__PURE__ */ new Set();
|
|
12107
12165
|
TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
|
|
@@ -18308,6 +18366,21 @@ function resolveAckedDeathDeadlineMs() {
|
|
|
18308
18366
|
function resolveAckedTranscriptFastTrackGraceMs() {
|
|
18309
18367
|
return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_TRANSCRIPT_FASTTRACK_GRACE_MS", 4e4, 0, 5 * 6e4);
|
|
18310
18368
|
}
|
|
18369
|
+
function getMeshV2BackstopCounters() {
|
|
18370
|
+
return { ...meshV2BackstopCounters };
|
|
18371
|
+
}
|
|
18372
|
+
function meshProtocolV2EnforceOn() {
|
|
18373
|
+
const raw = process.env.MESH_PROTOCOL_V2_ENFORCE;
|
|
18374
|
+
if (typeof raw !== "string") return false;
|
|
18375
|
+
const v = raw.trim().toLowerCase();
|
|
18376
|
+
return v === "1" || v === "true" || v === "on" || v === "yes";
|
|
18377
|
+
}
|
|
18378
|
+
function recordBackstopFire(kind, detail) {
|
|
18379
|
+
meshV2BackstopCounters[kind]++;
|
|
18380
|
+
if (meshProtocolV2EnforceOn()) {
|
|
18381
|
+
LOG.warn("MeshReconcileV2", `v2 ENFORCE last-resort backstop fired (${kind}): ${detail}. Under a healthy v2 completion contract this should be 0 \u2014 a worker's real terminal emit was lost/late.`);
|
|
18382
|
+
}
|
|
18383
|
+
}
|
|
18311
18384
|
function inFlightSynthKey(meshId, taskId) {
|
|
18312
18385
|
return `${meshId}::${taskId}`;
|
|
18313
18386
|
}
|
|
@@ -19222,6 +19295,7 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
19222
19295
|
};
|
|
19223
19296
|
const synthKey = inFlightSynthKey(mesh.id, taskId);
|
|
19224
19297
|
const isAcked = dispatch.status === "acked";
|
|
19298
|
+
let backstopKind;
|
|
19225
19299
|
let payload = null;
|
|
19226
19300
|
let readFailed = false;
|
|
19227
19301
|
try {
|
|
@@ -19286,6 +19360,7 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
19286
19360
|
const idleHeldMs = nowMs - idleSinceMs;
|
|
19287
19361
|
if (idleHeldMs >= fastTrackGraceMs) {
|
|
19288
19362
|
fastTrackReady = true;
|
|
19363
|
+
backstopKind = "ackedHoldFastTrackFired";
|
|
19289
19364
|
LOG.info("MeshReconcile", `Acked-hold transcript fast-track: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) read idle WITH a final assistant message for ${Math.round(idleHeldMs / 1e3)}s continuous (grace ${Math.round(fastTrackGraceMs / 1e3)}s) \u2014 promoting the synth ahead of the ${Math.round(deathDeadlineMs / 1e3)}s death backstop; the worker's real emit was lost/late and a later one no-ops idempotently.`);
|
|
19290
19365
|
}
|
|
19291
19366
|
} else if (holdState?.transcriptIdleSinceMs !== void 0) {
|
|
@@ -19296,6 +19371,7 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
19296
19371
|
continue;
|
|
19297
19372
|
}
|
|
19298
19373
|
if (!fastTrackReady) {
|
|
19374
|
+
backstopKind = "ackedHoldDeathDeadlineFired";
|
|
19299
19375
|
LOG.warn("MeshReconcile", `Acked-hold death deadline reached: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) still idle ${Math.round(sinceAckMs / 1e3)}s after the ack (deadline ${Math.round(deathDeadlineMs / 1e3)}s) \u2014 synthesizing the missing completion as a notification-loss net (a real emit, if it ever lands, no-ops idempotently).`);
|
|
19300
19376
|
}
|
|
19301
19377
|
}
|
|
@@ -19340,6 +19416,7 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
19340
19416
|
source: "daemon_reconcile_transcript_completion"
|
|
19341
19417
|
});
|
|
19342
19418
|
if (result.reconciled) {
|
|
19419
|
+
recordBackstopFire(backstopKind ?? "phase4SynthesisFired", `task ${taskId} on node ${nodeId} (mesh ${mesh.id}), kind=${result.kind}`);
|
|
19343
19420
|
LOG.info("MeshReconcile", `Synthesized missing completion (${result.kind}) for task ${taskId} on node ${nodeId} (mesh ${mesh.id})`);
|
|
19344
19421
|
}
|
|
19345
19422
|
} catch (e) {
|
|
@@ -19469,7 +19546,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
19469
19546
|
}
|
|
19470
19547
|
};
|
|
19471
19548
|
}
|
|
19472
|
-
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, DEFAULT_PENDING_HELD_DRAIN_ESCALATE_MS, ACKED_DEATH_CONSECUTIVE_READ_FAILURES, inFlightAckedHoldState, rehydratedHoldMeshes, coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, DELIVERED_NO_TURN_DEADLINE_MS, RECLAIM_UNKNOWN_GRACE_TICKS, deliveredNoTurnUnknownStreak, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS;
|
|
19549
|
+
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, DEFAULT_PENDING_HELD_DRAIN_ESCALATE_MS, ACKED_DEATH_CONSECUTIVE_READ_FAILURES, inFlightAckedHoldState, rehydratedHoldMeshes, meshV2BackstopCounters, coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, DELIVERED_NO_TURN_DEADLINE_MS, RECLAIM_UNKNOWN_GRACE_TICKS, deliveredNoTurnUnknownStreak, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS;
|
|
19473
19550
|
var init_mesh_reconcile_loop = __esm({
|
|
19474
19551
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
19475
19552
|
"use strict";
|
|
@@ -19497,6 +19574,14 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
19497
19574
|
ACKED_DEATH_CONSECUTIVE_READ_FAILURES = 3;
|
|
19498
19575
|
inFlightAckedHoldState = /* @__PURE__ */ new Map();
|
|
19499
19576
|
rehydratedHoldMeshes = /* @__PURE__ */ new Set();
|
|
19577
|
+
meshV2BackstopCounters = {
|
|
19578
|
+
/** PHASE-4 transcript synthesis actually reconciled a missing completion. */
|
|
19579
|
+
phase4SynthesisFired: 0,
|
|
19580
|
+
/** Acked-hold transcript fast-track promoted a synth ahead of the death deadline. */
|
|
19581
|
+
ackedHoldFastTrackFired: 0,
|
|
19582
|
+
/** Acked-hold death-deadline backstop released a held synth (the 8-min net). */
|
|
19583
|
+
ackedHoldDeathDeadlineFired: 0
|
|
19584
|
+
};
|
|
19500
19585
|
coordinatorModalParkState = /* @__PURE__ */ new Map();
|
|
19501
19586
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
19502
19587
|
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
@@ -19516,9 +19601,12 @@ __export(mesh_events_exports, {
|
|
|
19516
19601
|
__resetMeshWorkspaceCacheForTests: () => __resetMeshWorkspaceCacheForTests,
|
|
19517
19602
|
clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
|
|
19518
19603
|
drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
|
|
19604
|
+
getMeshV2BackstopCounters: () => getMeshV2BackstopCounters,
|
|
19605
|
+
getMeshV2DrainCounters: () => getMeshV2DrainCounters,
|
|
19519
19606
|
getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
|
|
19520
19607
|
handleMeshForwardEvent: () => handleMeshForwardEvent,
|
|
19521
19608
|
isMeshCoordinatorEvent: () => isMeshCoordinatorEvent,
|
|
19609
|
+
isMeshProtocolV2EnforceEnabled: () => isMeshProtocolV2EnforceEnabled,
|
|
19522
19610
|
isSessionActivelyGenerating: () => isSessionActivelyGenerating,
|
|
19523
19611
|
queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
|
|
19524
19612
|
readV2EnvelopeFromWire: () => readV2EnvelopeFromWire,
|
|
@@ -40191,15 +40279,37 @@ function executeSqlite(src, input) {
|
|
|
40191
40279
|
}
|
|
40192
40280
|
try {
|
|
40193
40281
|
const requested = input.providerSessionId || "";
|
|
40194
|
-
const
|
|
40195
|
-
|
|
40196
|
-
|
|
40197
|
-
|
|
40198
|
-
|
|
40199
|
-
|
|
40200
|
-
|
|
40282
|
+
const resolveClusterIds = (anchorId) => {
|
|
40283
|
+
const ids = /* @__PURE__ */ new Set();
|
|
40284
|
+
if (anchorId) ids.add(anchorId);
|
|
40285
|
+
if (src.session_cluster_query && anchorId) {
|
|
40286
|
+
try {
|
|
40287
|
+
const rows = db.prepare(src.session_cluster_query).all(anchorId);
|
|
40288
|
+
for (const row of rows) {
|
|
40289
|
+
const idRaw = Object.values(row)[0];
|
|
40290
|
+
if (idRaw != null && String(idRaw)) ids.add(String(idRaw));
|
|
40291
|
+
}
|
|
40292
|
+
} catch {
|
|
40293
|
+
}
|
|
40201
40294
|
}
|
|
40202
|
-
return
|
|
40295
|
+
return Array.from(ids);
|
|
40296
|
+
};
|
|
40297
|
+
const resolveMessagesFor = (anchorId) => {
|
|
40298
|
+
if (!anchorId) return null;
|
|
40299
|
+
const clusterIds = resolveClusterIds(anchorId);
|
|
40300
|
+
const merged = [];
|
|
40301
|
+
for (const id of clusterIds) {
|
|
40302
|
+
let rows;
|
|
40303
|
+
try {
|
|
40304
|
+
rows = db.prepare(src.message_query).all(id);
|
|
40305
|
+
} catch {
|
|
40306
|
+
continue;
|
|
40307
|
+
}
|
|
40308
|
+
if (rows && rows.length > 0) merged.push(...rows);
|
|
40309
|
+
}
|
|
40310
|
+
if (merged.length === 0) return null;
|
|
40311
|
+
if (clusterIds.length > 1) sortRowsByMappedTimestamp(merged, src.message_map);
|
|
40312
|
+
return merged;
|
|
40203
40313
|
};
|
|
40204
40314
|
const resolveNewestSessionId = () => {
|
|
40205
40315
|
let sessionRow;
|
|
@@ -40256,6 +40366,20 @@ function executeSqlite(src, input) {
|
|
|
40256
40366
|
}
|
|
40257
40367
|
}
|
|
40258
40368
|
}
|
|
40369
|
+
function sortRowsByMappedTimestamp(rows, map) {
|
|
40370
|
+
if (!map.timestamp_ms) return;
|
|
40371
|
+
const keyed = rows.map((row, index) => {
|
|
40372
|
+
const parsed = parseTimestamp(jsonPathGet(row, map.timestamp_ms));
|
|
40373
|
+
return { row, index, ts: parsed == null ? Number.NaN : parsed };
|
|
40374
|
+
});
|
|
40375
|
+
keyed.sort((a, b) => {
|
|
40376
|
+
const aHas = !Number.isNaN(a.ts);
|
|
40377
|
+
const bHas = !Number.isNaN(b.ts);
|
|
40378
|
+
if (aHas && bHas && a.ts !== b.ts) return a.ts - b.ts;
|
|
40379
|
+
return a.index - b.index;
|
|
40380
|
+
});
|
|
40381
|
+
for (let i = 0; i < keyed.length; i += 1) rows[i] = keyed[i].row;
|
|
40382
|
+
}
|
|
40259
40383
|
function expandPath2(template, input, opts) {
|
|
40260
40384
|
if (!template) return null;
|
|
40261
40385
|
let out = template;
|
|
@@ -42880,6 +43004,19 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42880
43004
|
}
|
|
42881
43005
|
return probe;
|
|
42882
43006
|
}
|
|
43007
|
+
/**
|
|
43008
|
+
* The spawned CLI's env overrides (e.g. the mesh coordinator points hermes
|
|
43009
|
+
* at a per-coordinator HERMES_HOME so its state.db lives in a tmpdir instead
|
|
43010
|
+
* of ~/.hermes). The native-history executor expands `${HERMES_HOME:-~/.hermes}`
|
|
43011
|
+
* from this map, so the completion gate MUST pass it through — otherwise the
|
|
43012
|
+
* gate reads ~/.hermes, finds no coordinator-session transcript, and
|
|
43013
|
+
* false-fires missing_final_assistant on every coordinator turn.
|
|
43014
|
+
*/
|
|
43015
|
+
spawnedEnvOverrides() {
|
|
43016
|
+
const meta = typeof this.adapter?.getRuntimeMetadata === "function" ? this.adapter.getRuntimeMetadata() : void 0;
|
|
43017
|
+
const env = meta && typeof meta === "object" ? meta.spawnedEnv : void 0;
|
|
43018
|
+
return env && typeof env === "object" ? env : void 0;
|
|
43019
|
+
}
|
|
42883
43020
|
readExternalCompletionMessages() {
|
|
42884
43021
|
const adapterOwnsMessagesElsewhere = this.adapter?.chatMessagesOwnedExternally === true;
|
|
42885
43022
|
if (!adapterOwnsMessagesElsewhere) return null;
|
|
@@ -42900,6 +43037,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42900
43037
|
historyBehavior: this.provider.historyBehavior,
|
|
42901
43038
|
scripts: this.provider.scripts,
|
|
42902
43039
|
sessionStartedAtMs: this.startedAt,
|
|
43040
|
+
envOverrides: this.spawnedEnvOverrides(),
|
|
42903
43041
|
forceRefresh: true
|
|
42904
43042
|
});
|
|
42905
43043
|
if (restoredHistory.source !== "provider-native") {
|
|
@@ -43425,10 +43563,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
43425
43563
|
if (buttonIndex < 0 || !hasReliableConsentAnchor) {
|
|
43426
43564
|
return autoApproveActive;
|
|
43427
43565
|
}
|
|
43566
|
+
const affirmativeAnchor = normalizeApprovalLabel(buttonLabel);
|
|
43428
43567
|
const modalSignature = [
|
|
43429
43568
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
43430
|
-
|
|
43431
|
-
buttonIndex
|
|
43569
|
+
affirmativeAnchor
|
|
43432
43570
|
].join("::");
|
|
43433
43571
|
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
43434
43572
|
const busySignature = `${approvalEntrySeq}::${modalSignature}`;
|
|
@@ -48477,14 +48615,44 @@ function openDb() {
|
|
|
48477
48615
|
return null;
|
|
48478
48616
|
}
|
|
48479
48617
|
}
|
|
48618
|
+
function resolveClusterSessionIds(db, anchorId) {
|
|
48619
|
+
if (!anchorId) return [];
|
|
48620
|
+
try {
|
|
48621
|
+
const rows = db.prepare(
|
|
48622
|
+
`WITH RECURSIVE
|
|
48623
|
+
up(id) AS (
|
|
48624
|
+
SELECT id FROM sessions WHERE id = ?
|
|
48625
|
+
UNION
|
|
48626
|
+
SELECT s.parent_session_id FROM sessions s JOIN up ON s.id = up.id
|
|
48627
|
+
WHERE s.parent_session_id IS NOT NULL
|
|
48628
|
+
),
|
|
48629
|
+
cluster(id) AS (
|
|
48630
|
+
SELECT id FROM up
|
|
48631
|
+
UNION
|
|
48632
|
+
SELECT s.id FROM sessions s JOIN cluster ON s.parent_session_id = cluster.id
|
|
48633
|
+
)
|
|
48634
|
+
SELECT id FROM cluster`
|
|
48635
|
+
).all(anchorId);
|
|
48636
|
+
const ids = /* @__PURE__ */ new Set([anchorId]);
|
|
48637
|
+
for (const r of rows) {
|
|
48638
|
+
if (r && r.id != null && String(r.id)) ids.add(String(r.id));
|
|
48639
|
+
}
|
|
48640
|
+
return Array.from(ids);
|
|
48641
|
+
} catch {
|
|
48642
|
+
return [anchorId];
|
|
48643
|
+
}
|
|
48644
|
+
}
|
|
48480
48645
|
function loadMessagesForSession(db, sessionId) {
|
|
48646
|
+
const clusterIds = resolveClusterSessionIds(db, sessionId);
|
|
48647
|
+
if (clusterIds.length === 0) return [];
|
|
48648
|
+
const placeholders = clusterIds.map(() => "?").join(", ");
|
|
48481
48649
|
const rows = db.prepare(
|
|
48482
48650
|
`SELECT id, role, COALESCE(NULLIF(content, ''), tool_calls) AS content, timestamp
|
|
48483
48651
|
FROM messages
|
|
48484
|
-
WHERE session_id
|
|
48652
|
+
WHERE session_id IN (${placeholders})
|
|
48485
48653
|
AND ((content IS NOT NULL AND content != '') OR (tool_calls IS NOT NULL AND tool_calls != ''))
|
|
48486
48654
|
ORDER BY timestamp ASC, id ASC`
|
|
48487
|
-
).all(
|
|
48655
|
+
).all(...clusterIds);
|
|
48488
48656
|
const out = [];
|
|
48489
48657
|
for (const r of rows) {
|
|
48490
48658
|
const role = normalizeHermesRole(r.role);
|
|
@@ -53040,7 +53208,12 @@ var meshEventsHandlers = {
|
|
|
53040
53208
|
return { success: true, events: [], heldForBusyLocalCoordinator: true, hasLiveCliCoordinator };
|
|
53041
53209
|
}
|
|
53042
53210
|
const events = drainPendingMeshCoordinatorEvents(meshId || void 0, coordinatorDaemonId);
|
|
53043
|
-
|
|
53211
|
+
const meshProtocolV2Counters = {
|
|
53212
|
+
enforce: isMeshProtocolV2EnforceEnabled(),
|
|
53213
|
+
drain: { ...getMeshV2DrainCounters() },
|
|
53214
|
+
backstop: { ...getMeshV2BackstopCounters() }
|
|
53215
|
+
};
|
|
53216
|
+
return { success: true, events, hasLiveCliCoordinator, meshProtocolV2Counters, ...selfCoordinatorInboxRead ? { surfacedForSelfCoordinator: true } : {} };
|
|
53044
53217
|
},
|
|
53045
53218
|
interactive_prompt_response: async (ctx, args) => {
|
|
53046
53219
|
const sessionId = typeof args?.targetSessionId === "string" && args.targetSessionId.trim() ? args.targetSessionId.trim() : typeof args?.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : "";
|
|
@@ -54066,6 +54239,11 @@ var meshStatusHandlers = {
|
|
|
54066
54239
|
const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : ctx.deps.statusInstanceId || void 0;
|
|
54067
54240
|
const pendingCoordinatorEvents = getPendingMeshCoordinatorEvents(meshId, callerCoordinatorDaemonId);
|
|
54068
54241
|
const unroutableDeliveries = getRecentUnroutableDeliveries();
|
|
54242
|
+
const meshProtocolV2Counters = {
|
|
54243
|
+
enforce: isMeshProtocolV2EnforceEnabled(),
|
|
54244
|
+
drain: { ...getMeshV2DrainCounters() },
|
|
54245
|
+
backstop: { ...getMeshV2BackstopCounters() }
|
|
54246
|
+
};
|
|
54069
54247
|
const previewFreshness = (() => {
|
|
54070
54248
|
const localRepoRoot = nodeStatuses.map((node) => readStringValue(node?.git?.repoRoot, node?.repoRoot, node?.workspace)).find((candidate) => !!candidate && fs27.existsSync(candidate));
|
|
54071
54249
|
return localRepoRoot ? buildPreviewFreshness(localRepoRoot) : void 0;
|
|
@@ -54155,6 +54333,7 @@ var meshStatusHandlers = {
|
|
|
54155
54333
|
...historicalSessions ? { historicalSessions } : {},
|
|
54156
54334
|
...pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {},
|
|
54157
54335
|
...unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {},
|
|
54336
|
+
meshProtocolV2Counters,
|
|
54158
54337
|
activeRefineJobs: Array.from(ctx.runningRefineJobs.values()).filter((job) => job.meshId === meshId).map((job) => ({
|
|
54159
54338
|
jobId: job.jobId,
|
|
54160
54339
|
nodeId: job.targetNodeId,
|
|
@@ -54164,12 +54343,13 @@ var meshStatusHandlers = {
|
|
|
54164
54343
|
targetCoordinatorDaemonId: job.targetCoordinatorDaemonId
|
|
54165
54344
|
}))
|
|
54166
54345
|
};
|
|
54167
|
-
const { pendingCoordinatorEvents: _pendingCoordinatorEvents, unroutableDeliveries: _unroutableDeliveries, ...cacheableStatusResult } = statusResult;
|
|
54346
|
+
const { pendingCoordinatorEvents: _pendingCoordinatorEvents, unroutableDeliveries: _unroutableDeliveries, meshProtocolV2Counters: _meshProtocolV2Counters, ...cacheableStatusResult } = statusResult;
|
|
54168
54347
|
const rememberedStatus = verboseMissions ? cacheableStatusResult : ctx.rememberAggregateMeshStatus(meshId, cacheableStatusResult, refreshReason);
|
|
54169
54348
|
const returnedStatus = {
|
|
54170
54349
|
...rememberedStatus,
|
|
54171
54350
|
...pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {},
|
|
54172
|
-
...unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {}
|
|
54351
|
+
...unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {},
|
|
54352
|
+
meshProtocolV2Counters
|
|
54173
54353
|
};
|
|
54174
54354
|
logRepoMeshStatusDebug("return_live", {
|
|
54175
54355
|
meshId,
|
|
@@ -54412,7 +54592,6 @@ cleanOldFiles();
|
|
|
54412
54592
|
// src/commands/router.ts
|
|
54413
54593
|
init_debug_trace();
|
|
54414
54594
|
init_mesh_host_ownership();
|
|
54415
|
-
init_mesh_work_queue();
|
|
54416
54595
|
import * as fs33 from "fs";
|
|
54417
54596
|
|
|
54418
54597
|
// src/mesh/mesh-node-identity.ts
|
|
@@ -58806,6 +58985,177 @@ async function cleanupMeshSessions(self, args) {
|
|
|
58806
58985
|
};
|
|
58807
58986
|
}
|
|
58808
58987
|
|
|
58988
|
+
// src/commands/router-aggregate-status.ts
|
|
58989
|
+
init_dist();
|
|
58990
|
+
init_mesh_work_queue();
|
|
58991
|
+
function cloneJsonValue(value) {
|
|
58992
|
+
if (typeof structuredClone === "function") return structuredClone(value);
|
|
58993
|
+
return JSON.parse(JSON.stringify(value));
|
|
58994
|
+
}
|
|
58995
|
+
function hydrateCachedAggregateMeshStatusFromInline(self, snapshot, mesh, options) {
|
|
58996
|
+
if (!mesh || typeof mesh !== "object" || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
|
|
58997
|
+
const inlineNodesById = /* @__PURE__ */ new Map();
|
|
58998
|
+
for (const node of mesh.nodes) {
|
|
58999
|
+
const nodeId = readInlineMeshNodeId(node);
|
|
59000
|
+
if (nodeId) inlineNodesById.set(nodeId, node);
|
|
59001
|
+
}
|
|
59002
|
+
if (!inlineNodesById.size) return snapshot;
|
|
59003
|
+
let changed = false;
|
|
59004
|
+
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
59005
|
+
const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
|
|
59006
|
+
const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
|
|
59007
|
+
const deadNodeIds = /* @__PURE__ */ new Set();
|
|
59008
|
+
for (const node of mesh.nodes) {
|
|
59009
|
+
if (!isDeadLocalWorktreeNode(node)) continue;
|
|
59010
|
+
const deadId = readInlineMeshNodeId(node);
|
|
59011
|
+
if (deadId) deadNodeIds.add(deadId);
|
|
59012
|
+
}
|
|
59013
|
+
let droppedDeadUnavailable = false;
|
|
59014
|
+
for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
|
|
59015
|
+
const nodeId = readStringValue(entry);
|
|
59016
|
+
if (!nodeId) continue;
|
|
59017
|
+
if (deadNodeIds.has(nodeId)) {
|
|
59018
|
+
droppedDeadUnavailable = true;
|
|
59019
|
+
continue;
|
|
59020
|
+
}
|
|
59021
|
+
unavailableNodeIds.add(nodeId);
|
|
59022
|
+
}
|
|
59023
|
+
if (droppedDeadUnavailable) changed = true;
|
|
59024
|
+
const nodes = snapshot.nodes.map((statusNode) => {
|
|
59025
|
+
const nodeId = normalizeMeshNodeId(statusNode);
|
|
59026
|
+
const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
|
|
59027
|
+
if (!inlineNode) return statusNode;
|
|
59028
|
+
const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
|
|
59029
|
+
if (!liveGit) return statusNode;
|
|
59030
|
+
const nextStatus = { ...statusNode };
|
|
59031
|
+
nextStatus.git = liveGit;
|
|
59032
|
+
nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
|
|
59033
|
+
applyInlineMeshBranchConvergence(mesh, inlineNode, nextStatus);
|
|
59034
|
+
nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
|
|
59035
|
+
const connection = readObjectRecord(nextStatus.connection);
|
|
59036
|
+
const connectionState = readStringValue(connection.state);
|
|
59037
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
59038
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
59039
|
+
nextStatus.connection = buildLivePeerGitConnection(connection);
|
|
59040
|
+
}
|
|
59041
|
+
delete nextStatus.gitProbePending;
|
|
59042
|
+
const error = readStringValue(nextStatus.error);
|
|
59043
|
+
if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
|
|
59044
|
+
if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
|
|
59045
|
+
if (nodeId) unavailableNodeIds.delete(nodeId);
|
|
59046
|
+
changed = true;
|
|
59047
|
+
return nextStatus;
|
|
59048
|
+
});
|
|
59049
|
+
const aggregateDirectTruthSatisfied = sourceOfTruth.coordinatorOwnsLiveTruth === true || directPeerTruth.satisfied === true;
|
|
59050
|
+
if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied)) return snapshot;
|
|
59051
|
+
const nextSourceOfTruth = {
|
|
59052
|
+
...sourceOfTruth,
|
|
59053
|
+
...Object.keys(directPeerTruth).length ? {
|
|
59054
|
+
directPeerTruth: {
|
|
59055
|
+
...directPeerTruth,
|
|
59056
|
+
satisfied: options?.requireDirectPeerTruth === true ? aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
|
|
59057
|
+
unavailableNodeIds: [...unavailableNodeIds]
|
|
59058
|
+
},
|
|
59059
|
+
...options?.requireDirectPeerTruth === true ? {
|
|
59060
|
+
coordinatorOwnsLiveTruth: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0,
|
|
59061
|
+
currentStatus: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
|
|
59062
|
+
} : {}
|
|
59063
|
+
} : {}
|
|
59064
|
+
};
|
|
59065
|
+
return {
|
|
59066
|
+
...snapshot,
|
|
59067
|
+
...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied ? {
|
|
59068
|
+
success: false,
|
|
59069
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
59070
|
+
error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
|
|
59071
|
+
} : {},
|
|
59072
|
+
sourceOfTruth: nextSourceOfTruth,
|
|
59073
|
+
branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodes),
|
|
59074
|
+
nodes
|
|
59075
|
+
};
|
|
59076
|
+
}
|
|
59077
|
+
function getCachedAggregateMeshStatus(self, meshId, mesh, options) {
|
|
59078
|
+
const cached3 = self.aggregateMeshStatusCache.get(meshId);
|
|
59079
|
+
if (!cached3?.snapshot || cached3.snapshot.success !== true || !Array.isArray(cached3.snapshot.nodes)) return null;
|
|
59080
|
+
if (cached3.queueRevision !== getMeshQueueRevision(meshId)) return null;
|
|
59081
|
+
let snapshot = cloneJsonValue(cached3.snapshot);
|
|
59082
|
+
snapshot = hydrateCachedAggregateMeshStatusFromInline(self, snapshot, mesh, options);
|
|
59083
|
+
if (!options?.allowStalePending && shouldRefreshStalePendingAggregate(snapshot, options)) return null;
|
|
59084
|
+
const ageMs = Math.max(0, Date.now() - cached3.builtAt);
|
|
59085
|
+
const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
|
|
59086
|
+
snapshot.sourceOfTruth = {
|
|
59087
|
+
...sourceOfTruth,
|
|
59088
|
+
aggregateSnapshot: {
|
|
59089
|
+
...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
|
|
59090
|
+
owner: "coordinator_daemon_memory",
|
|
59091
|
+
cached: true,
|
|
59092
|
+
source: "memory",
|
|
59093
|
+
refreshReason: "memory_cache_hit",
|
|
59094
|
+
ageMs,
|
|
59095
|
+
cachedAt: new Date(cached3.builtAt).toISOString(),
|
|
59096
|
+
returnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
59097
|
+
}
|
|
59098
|
+
};
|
|
59099
|
+
return snapshot;
|
|
59100
|
+
}
|
|
59101
|
+
function rememberAggregateMeshStatus(self, meshId, snapshot, refreshReason) {
|
|
59102
|
+
if (!snapshot || typeof snapshot !== "object" || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
|
|
59103
|
+
const builtAt = Date.now();
|
|
59104
|
+
const next = cloneJsonValue(snapshot);
|
|
59105
|
+
const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
|
|
59106
|
+
next.sourceOfTruth = {
|
|
59107
|
+
...sourceOfTruth,
|
|
59108
|
+
aggregateSnapshot: {
|
|
59109
|
+
owner: "coordinator_daemon_memory",
|
|
59110
|
+
cached: false,
|
|
59111
|
+
source: "live_refresh",
|
|
59112
|
+
refreshReason,
|
|
59113
|
+
ageMs: 0,
|
|
59114
|
+
cachedAt: new Date(builtAt).toISOString(),
|
|
59115
|
+
returnedAt: new Date(builtAt).toISOString()
|
|
59116
|
+
}
|
|
59117
|
+
};
|
|
59118
|
+
self.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
|
|
59119
|
+
return next;
|
|
59120
|
+
}
|
|
59121
|
+
|
|
59122
|
+
// src/commands/router-mesh-session-owner.ts
|
|
59123
|
+
init_dist();
|
|
59124
|
+
function resolveRemoteMeshSessionOwnerDaemonId(self, sessionId, ownerNodeIdHint) {
|
|
59125
|
+
const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
59126
|
+
const nodeHint = typeof ownerNodeIdHint === "string" ? ownerNodeIdHint.trim() : "";
|
|
59127
|
+
if (!trimmed && !nodeHint) return void 0;
|
|
59128
|
+
const selfDaemonId = self.deps.statusInstanceId;
|
|
59129
|
+
const candidates = collectMeshSessionOwnerCandidateNodes(self);
|
|
59130
|
+
if (trimmed) {
|
|
59131
|
+
for (const node of candidates) {
|
|
59132
|
+
if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
|
|
59133
|
+
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
59134
|
+
if (!nodeDaemonId) continue;
|
|
59135
|
+
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
59136
|
+
return nodeDaemonId;
|
|
59137
|
+
}
|
|
59138
|
+
}
|
|
59139
|
+
if (nodeHint) {
|
|
59140
|
+
for (const node of candidates) {
|
|
59141
|
+
if (!meshNodeIdMatches(node, nodeHint)) continue;
|
|
59142
|
+
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
59143
|
+
if (!nodeDaemonId) continue;
|
|
59144
|
+
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
59145
|
+
return nodeDaemonId;
|
|
59146
|
+
}
|
|
59147
|
+
}
|
|
59148
|
+
return void 0;
|
|
59149
|
+
}
|
|
59150
|
+
function collectMeshSessionOwnerCandidateNodes(self) {
|
|
59151
|
+
const nodes = self.getCachedInlineMeshNodes();
|
|
59152
|
+
for (const cached3 of self.aggregateMeshStatusCache.values()) {
|
|
59153
|
+
const snapshotNodes = cached3?.snapshot?.nodes;
|
|
59154
|
+
if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
|
|
59155
|
+
}
|
|
59156
|
+
return nodes;
|
|
59157
|
+
}
|
|
59158
|
+
|
|
58809
59159
|
// src/mesh/mesh-coordinator-config.ts
|
|
58810
59160
|
init_logger();
|
|
58811
59161
|
import * as yaml5 from "js-yaml";
|
|
@@ -58974,7 +59324,8 @@ var DaemonCommandRouter = class {
|
|
|
58974
59324
|
* on disk) clears the tombstone and merges normally, preserving clone
|
|
58975
59325
|
* worktree visibility and legitimate node re-creation. */
|
|
58976
59326
|
removedInlineMeshNodeIds = /* @__PURE__ */ new Map();
|
|
58977
|
-
/** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default.
|
|
59327
|
+
/** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default.
|
|
59328
|
+
* Public (not private) so the extracted ./router-aggregate-status.ts orchestration can reach it via `self`. */
|
|
58978
59329
|
aggregateMeshStatusCache = /* @__PURE__ */ new Map();
|
|
58979
59330
|
/** Shared per-peer git_status probe dedup + recently-probed reuse gate.
|
|
58980
59331
|
* Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
|
|
@@ -58996,135 +59347,19 @@ var DaemonCommandRouter = class {
|
|
|
58996
59347
|
constructor(deps) {
|
|
58997
59348
|
this.deps = deps;
|
|
58998
59349
|
}
|
|
58999
|
-
|
|
59000
|
-
|
|
59001
|
-
|
|
59002
|
-
|
|
59350
|
+
// ─── Aggregate mesh-status cache ────────────────────────────────────
|
|
59351
|
+
// Implementation lives in ./router-aggregate-status.ts (behavior-preserving
|
|
59352
|
+
// code move). Kept here as thin delegators: getCachedAggregateMeshStatus /
|
|
59353
|
+
// rememberAggregateMeshStatus are bound into HighFamilyContext, so callers
|
|
59354
|
+
// reach these via `self.` for correct instance dispatch.
|
|
59003
59355
|
hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options) {
|
|
59004
|
-
|
|
59005
|
-
const inlineNodesById = /* @__PURE__ */ new Map();
|
|
59006
|
-
for (const node of mesh.nodes) {
|
|
59007
|
-
const nodeId = readInlineMeshNodeId(node);
|
|
59008
|
-
if (nodeId) inlineNodesById.set(nodeId, node);
|
|
59009
|
-
}
|
|
59010
|
-
if (!inlineNodesById.size) return snapshot;
|
|
59011
|
-
let changed = false;
|
|
59012
|
-
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
59013
|
-
const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
|
|
59014
|
-
const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
|
|
59015
|
-
const deadNodeIds = /* @__PURE__ */ new Set();
|
|
59016
|
-
for (const node of mesh.nodes) {
|
|
59017
|
-
if (!isDeadLocalWorktreeNode(node)) continue;
|
|
59018
|
-
const deadId = readInlineMeshNodeId(node);
|
|
59019
|
-
if (deadId) deadNodeIds.add(deadId);
|
|
59020
|
-
}
|
|
59021
|
-
let droppedDeadUnavailable = false;
|
|
59022
|
-
for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
|
|
59023
|
-
const nodeId = readStringValue(entry);
|
|
59024
|
-
if (!nodeId) continue;
|
|
59025
|
-
if (deadNodeIds.has(nodeId)) {
|
|
59026
|
-
droppedDeadUnavailable = true;
|
|
59027
|
-
continue;
|
|
59028
|
-
}
|
|
59029
|
-
unavailableNodeIds.add(nodeId);
|
|
59030
|
-
}
|
|
59031
|
-
if (droppedDeadUnavailable) changed = true;
|
|
59032
|
-
const nodes = snapshot.nodes.map((statusNode) => {
|
|
59033
|
-
const nodeId = normalizeMeshNodeId(statusNode);
|
|
59034
|
-
const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
|
|
59035
|
-
if (!inlineNode) return statusNode;
|
|
59036
|
-
const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
|
|
59037
|
-
if (!liveGit) return statusNode;
|
|
59038
|
-
const nextStatus = { ...statusNode };
|
|
59039
|
-
nextStatus.git = liveGit;
|
|
59040
|
-
nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
|
|
59041
|
-
applyInlineMeshBranchConvergence(mesh, inlineNode, nextStatus);
|
|
59042
|
-
nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
|
|
59043
|
-
const connection = readObjectRecord(nextStatus.connection);
|
|
59044
|
-
const connectionState = readStringValue(connection.state);
|
|
59045
|
-
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
59046
|
-
if (!connectionReported || connectionState === "unknown") {
|
|
59047
|
-
nextStatus.connection = buildLivePeerGitConnection(connection);
|
|
59048
|
-
}
|
|
59049
|
-
delete nextStatus.gitProbePending;
|
|
59050
|
-
const error = readStringValue(nextStatus.error);
|
|
59051
|
-
if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
|
|
59052
|
-
if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
|
|
59053
|
-
if (nodeId) unavailableNodeIds.delete(nodeId);
|
|
59054
|
-
changed = true;
|
|
59055
|
-
return nextStatus;
|
|
59056
|
-
});
|
|
59057
|
-
const aggregateDirectTruthSatisfied = sourceOfTruth.coordinatorOwnsLiveTruth === true || directPeerTruth.satisfied === true;
|
|
59058
|
-
if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied)) return snapshot;
|
|
59059
|
-
const nextSourceOfTruth = {
|
|
59060
|
-
...sourceOfTruth,
|
|
59061
|
-
...Object.keys(directPeerTruth).length ? {
|
|
59062
|
-
directPeerTruth: {
|
|
59063
|
-
...directPeerTruth,
|
|
59064
|
-
satisfied: options?.requireDirectPeerTruth === true ? aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
|
|
59065
|
-
unavailableNodeIds: [...unavailableNodeIds]
|
|
59066
|
-
},
|
|
59067
|
-
...options?.requireDirectPeerTruth === true ? {
|
|
59068
|
-
coordinatorOwnsLiveTruth: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0,
|
|
59069
|
-
currentStatus: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
|
|
59070
|
-
} : {}
|
|
59071
|
-
} : {}
|
|
59072
|
-
};
|
|
59073
|
-
return {
|
|
59074
|
-
...snapshot,
|
|
59075
|
-
...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied ? {
|
|
59076
|
-
success: false,
|
|
59077
|
-
code: "mesh_direct_peer_truth_unavailable",
|
|
59078
|
-
error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
|
|
59079
|
-
} : {},
|
|
59080
|
-
sourceOfTruth: nextSourceOfTruth,
|
|
59081
|
-
branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodes),
|
|
59082
|
-
nodes
|
|
59083
|
-
};
|
|
59356
|
+
return hydrateCachedAggregateMeshStatusFromInline(this, snapshot, mesh, options);
|
|
59084
59357
|
}
|
|
59085
59358
|
getCachedAggregateMeshStatus(meshId, mesh, options) {
|
|
59086
|
-
|
|
59087
|
-
if (!cached3?.snapshot || cached3.snapshot.success !== true || !Array.isArray(cached3.snapshot.nodes)) return null;
|
|
59088
|
-
if (cached3.queueRevision !== getMeshQueueRevision(meshId)) return null;
|
|
59089
|
-
let snapshot = this.cloneJsonValue(cached3.snapshot);
|
|
59090
|
-
snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
|
|
59091
|
-
if (!options?.allowStalePending && shouldRefreshStalePendingAggregate(snapshot, options)) return null;
|
|
59092
|
-
const ageMs = Math.max(0, Date.now() - cached3.builtAt);
|
|
59093
|
-
const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
|
|
59094
|
-
snapshot.sourceOfTruth = {
|
|
59095
|
-
...sourceOfTruth,
|
|
59096
|
-
aggregateSnapshot: {
|
|
59097
|
-
...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
|
|
59098
|
-
owner: "coordinator_daemon_memory",
|
|
59099
|
-
cached: true,
|
|
59100
|
-
source: "memory",
|
|
59101
|
-
refreshReason: "memory_cache_hit",
|
|
59102
|
-
ageMs,
|
|
59103
|
-
cachedAt: new Date(cached3.builtAt).toISOString(),
|
|
59104
|
-
returnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
59105
|
-
}
|
|
59106
|
-
};
|
|
59107
|
-
return snapshot;
|
|
59359
|
+
return getCachedAggregateMeshStatus(this, meshId, mesh, options);
|
|
59108
59360
|
}
|
|
59109
59361
|
rememberAggregateMeshStatus(meshId, snapshot, refreshReason) {
|
|
59110
|
-
|
|
59111
|
-
const builtAt = Date.now();
|
|
59112
|
-
const next = this.cloneJsonValue(snapshot);
|
|
59113
|
-
const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
|
|
59114
|
-
next.sourceOfTruth = {
|
|
59115
|
-
...sourceOfTruth,
|
|
59116
|
-
aggregateSnapshot: {
|
|
59117
|
-
owner: "coordinator_daemon_memory",
|
|
59118
|
-
cached: false,
|
|
59119
|
-
source: "live_refresh",
|
|
59120
|
-
refreshReason,
|
|
59121
|
-
ageMs: 0,
|
|
59122
|
-
cachedAt: new Date(builtAt).toISOString(),
|
|
59123
|
-
returnedAt: new Date(builtAt).toISOString()
|
|
59124
|
-
}
|
|
59125
|
-
};
|
|
59126
|
-
this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
|
|
59127
|
-
return next;
|
|
59362
|
+
return rememberAggregateMeshStatus(this, meshId, snapshot, refreshReason);
|
|
59128
59363
|
}
|
|
59129
59364
|
getCachedInlineMeshNodes() {
|
|
59130
59365
|
const nodes = [];
|
|
@@ -59135,82 +59370,13 @@ var DaemonCommandRouter = class {
|
|
|
59135
59370
|
}
|
|
59136
59371
|
return nodes;
|
|
59137
59372
|
}
|
|
59138
|
-
|
|
59139
|
-
|
|
59140
|
-
|
|
59141
|
-
|
|
59142
|
-
|
|
59143
|
-
* instanceManager/sessionRegistry — only their cached mesh-node metadata. A
|
|
59144
|
-
* dashboard-issued session-scoped command (invoke_provider_script / resolve_action /
|
|
59145
|
-
* set_mode / …) lands on the coordinator with a targetSessionId the coordinator can't
|
|
59146
|
-
* find locally, and without forwarding it dies as "Live session not found". send_chat
|
|
59147
|
-
* happens to survive (its target resolves to the worker by another route), but the
|
|
59148
|
-
* controlbar commands do not — so the controlbar buttons appear to do nothing.
|
|
59149
|
-
*
|
|
59150
|
-
* Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
|
|
59151
|
-
* scan the candidate mesh nodes for the one hosting the targetSessionId, and return its
|
|
59152
|
-
* daemonId when that daemonId is a remote daemon (i.e. not this coordinator's own
|
|
59153
|
-
* statusInstanceId). Returns undefined for a locally-hosted session (no forward — execute
|
|
59154
|
-
* locally as before) or when ownership can't be resolved.
|
|
59155
|
-
*
|
|
59156
|
-
* The candidate set spans BOTH the cached inline-mesh nodes and the live aggregate
|
|
59157
|
-
* mesh-status snapshots. The inline cache reliably carries only each node's single primary
|
|
59158
|
-
* session (cachedStatus.activeSession), so a worker hosting more than one session exposes its
|
|
59159
|
-
* non-primary sessions only on the aggregate snapshot nodes (status.activeSessions /
|
|
59160
|
-
* activeSessionDetails, built from live session records). collectMeshNodeHostedSessionIds does
|
|
59161
|
-
* the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
|
|
59162
|
-
* session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
|
|
59163
|
-
* other consumers depend on stay untouched.
|
|
59164
|
-
*
|
|
59165
|
-
* CANCEL-STOP-RELAY: the session-id cache scan above only matches when the coordinator's
|
|
59166
|
-
* cached status snapshot already lists the worker's session id in a recognized active-sessions
|
|
59167
|
-
* shape. A worktree-clone worker session whose id form/timing differs from the cached snapshot
|
|
59168
|
-
* (or is simply not yet reflected) misses the scan, so a stop that carries the authoritative
|
|
59169
|
-
* owning nodeId (mesh_queue_cancel knows assignedNodeId) used to silently fail to forward.
|
|
59170
|
-
* `ownerNodeIdHint` adds a deterministic fallback: when the session-id scan misses, resolve the
|
|
59171
|
-
* owner daemonId by matching the node by id (meshNodeIdMatches — same form-tolerant compare the
|
|
59172
|
-
* rest of the router uses, no new raw compare). The same self-loopback guard applies to both
|
|
59173
|
-
* paths, so a coordinator-hosted node is still never force-forwarded to a remote form of itself.
|
|
59174
|
-
*/
|
|
59373
|
+
// ─── Remote mesh-session owner resolution ───────────────────────────
|
|
59374
|
+
// Implementation lives in ./router-mesh-session-owner.ts (behavior-preserving
|
|
59375
|
+
// code move). resolveRemoteMeshSessionOwnerDaemonId stays public (the [Z]
|
|
59376
|
+
// session-scoped forward in executeDaemonCommand and a unit test call it), so
|
|
59377
|
+
// it's kept here as a thin delegator.
|
|
59175
59378
|
resolveRemoteMeshSessionOwnerDaemonId(sessionId, ownerNodeIdHint) {
|
|
59176
|
-
|
|
59177
|
-
const nodeHint = typeof ownerNodeIdHint === "string" ? ownerNodeIdHint.trim() : "";
|
|
59178
|
-
if (!trimmed && !nodeHint) return void 0;
|
|
59179
|
-
const selfDaemonId = this.deps.statusInstanceId;
|
|
59180
|
-
const candidates = this.collectMeshSessionOwnerCandidateNodes();
|
|
59181
|
-
if (trimmed) {
|
|
59182
|
-
for (const node of candidates) {
|
|
59183
|
-
if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
|
|
59184
|
-
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
59185
|
-
if (!nodeDaemonId) continue;
|
|
59186
|
-
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
59187
|
-
return nodeDaemonId;
|
|
59188
|
-
}
|
|
59189
|
-
}
|
|
59190
|
-
if (nodeHint) {
|
|
59191
|
-
for (const node of candidates) {
|
|
59192
|
-
if (!meshNodeIdMatches(node, nodeHint)) continue;
|
|
59193
|
-
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
59194
|
-
if (!nodeDaemonId) continue;
|
|
59195
|
-
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
59196
|
-
return nodeDaemonId;
|
|
59197
|
-
}
|
|
59198
|
-
}
|
|
59199
|
-
return void 0;
|
|
59200
|
-
}
|
|
59201
|
-
/**
|
|
59202
|
-
* Candidate nodes for remote-session owner resolution: the cached inline-mesh nodes (which
|
|
59203
|
-
* carry each node's primary session) plus the nodes from every cached aggregate mesh-status
|
|
59204
|
-
* snapshot (which carry each node's full live session list). getCachedInlineMeshNodes()
|
|
59205
|
-
* returns a fresh array, so appending the aggregate nodes never mutates cached state.
|
|
59206
|
-
*/
|
|
59207
|
-
collectMeshSessionOwnerCandidateNodes() {
|
|
59208
|
-
const nodes = this.getCachedInlineMeshNodes();
|
|
59209
|
-
for (const cached3 of this.aggregateMeshStatusCache.values()) {
|
|
59210
|
-
const snapshotNodes = cached3?.snapshot?.nodes;
|
|
59211
|
-
if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
|
|
59212
|
-
}
|
|
59213
|
-
return nodes;
|
|
59379
|
+
return resolveRemoteMeshSessionOwnerDaemonId(this, sessionId, ownerNodeIdHint);
|
|
59214
59380
|
}
|
|
59215
59381
|
getCachedInlineMesh(meshId, inlineMesh) {
|
|
59216
59382
|
if (inlineMesh && typeof inlineMesh === "object") {
|