@adhdev/daemon-core 0.9.82-rc.462 → 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/index.js CHANGED
@@ -409,10 +409,10 @@ function readInjected(value) {
409
409
  }
410
410
  function getDaemonBuildInfo() {
411
411
  if (cached) return cached;
412
- const commit = readInjected(true ? "1481e2078cc0eceea283a2370cccb4cd9102e4ff" : void 0) ?? "unknown";
413
- const commitShort = readInjected(true ? "1481e207" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
- const version = readInjected(true ? "0.9.82-rc.462" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
- const builtAt = readInjected(true ? "2026-07-04T16:50:21.874Z" : void 0);
412
+ const commit = readInjected(true ? "3441855fe9c04e158a4dc94eed196b7bcba1647e" : void 0) ?? "unknown";
413
+ const commitShort = readInjected(true ? "3441855f" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
+ const version = readInjected(true ? "0.9.82-rc.463" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
+ const builtAt = readInjected(true ? "2026-07-05T02:56:26.902Z" : void 0);
416
416
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
417
417
  return cached;
418
418
  }
@@ -40689,15 +40689,37 @@ function executeSqlite(src, input) {
40689
40689
  }
40690
40690
  try {
40691
40691
  const requested = input.providerSessionId || "";
40692
- const resolveMessagesFor = (sessionId2) => {
40693
- if (!sessionId2) return null;
40694
- let rows;
40695
- try {
40696
- rows = db.prepare(src.message_query).all(sessionId2);
40697
- } catch {
40698
- return null;
40692
+ const resolveClusterIds = (anchorId) => {
40693
+ const ids = /* @__PURE__ */ new Set();
40694
+ if (anchorId) ids.add(anchorId);
40695
+ if (src.session_cluster_query && anchorId) {
40696
+ try {
40697
+ const rows = db.prepare(src.session_cluster_query).all(anchorId);
40698
+ for (const row of rows) {
40699
+ const idRaw = Object.values(row)[0];
40700
+ if (idRaw != null && String(idRaw)) ids.add(String(idRaw));
40701
+ }
40702
+ } catch {
40703
+ }
40699
40704
  }
40700
- return rows && rows.length > 0 ? rows : null;
40705
+ return Array.from(ids);
40706
+ };
40707
+ const resolveMessagesFor = (anchorId) => {
40708
+ if (!anchorId) return null;
40709
+ const clusterIds = resolveClusterIds(anchorId);
40710
+ const merged = [];
40711
+ for (const id of clusterIds) {
40712
+ let rows;
40713
+ try {
40714
+ rows = db.prepare(src.message_query).all(id);
40715
+ } catch {
40716
+ continue;
40717
+ }
40718
+ if (rows && rows.length > 0) merged.push(...rows);
40719
+ }
40720
+ if (merged.length === 0) return null;
40721
+ if (clusterIds.length > 1) sortRowsByMappedTimestamp(merged, src.message_map);
40722
+ return merged;
40701
40723
  };
40702
40724
  const resolveNewestSessionId = () => {
40703
40725
  let sessionRow;
@@ -40754,6 +40776,20 @@ function executeSqlite(src, input) {
40754
40776
  }
40755
40777
  }
40756
40778
  }
40779
+ function sortRowsByMappedTimestamp(rows, map) {
40780
+ if (!map.timestamp_ms) return;
40781
+ const keyed = rows.map((row, index) => {
40782
+ const parsed = parseTimestamp(jsonPathGet(row, map.timestamp_ms));
40783
+ return { row, index, ts: parsed == null ? Number.NaN : parsed };
40784
+ });
40785
+ keyed.sort((a, b) => {
40786
+ const aHas = !Number.isNaN(a.ts);
40787
+ const bHas = !Number.isNaN(b.ts);
40788
+ if (aHas && bHas && a.ts !== b.ts) return a.ts - b.ts;
40789
+ return a.index - b.index;
40790
+ });
40791
+ for (let i = 0; i < keyed.length; i += 1) rows[i] = keyed[i].row;
40792
+ }
40757
40793
  function expandPath2(template, input, opts) {
40758
40794
  if (!template) return null;
40759
40795
  let out = template;
@@ -43378,6 +43414,19 @@ var CliProviderInstance = class _CliProviderInstance {
43378
43414
  }
43379
43415
  return probe;
43380
43416
  }
43417
+ /**
43418
+ * The spawned CLI's env overrides (e.g. the mesh coordinator points hermes
43419
+ * at a per-coordinator HERMES_HOME so its state.db lives in a tmpdir instead
43420
+ * of ~/.hermes). The native-history executor expands `${HERMES_HOME:-~/.hermes}`
43421
+ * from this map, so the completion gate MUST pass it through — otherwise the
43422
+ * gate reads ~/.hermes, finds no coordinator-session transcript, and
43423
+ * false-fires missing_final_assistant on every coordinator turn.
43424
+ */
43425
+ spawnedEnvOverrides() {
43426
+ const meta = typeof this.adapter?.getRuntimeMetadata === "function" ? this.adapter.getRuntimeMetadata() : void 0;
43427
+ const env = meta && typeof meta === "object" ? meta.spawnedEnv : void 0;
43428
+ return env && typeof env === "object" ? env : void 0;
43429
+ }
43381
43430
  readExternalCompletionMessages() {
43382
43431
  const adapterOwnsMessagesElsewhere = this.adapter?.chatMessagesOwnedExternally === true;
43383
43432
  if (!adapterOwnsMessagesElsewhere) return null;
@@ -43398,6 +43447,7 @@ var CliProviderInstance = class _CliProviderInstance {
43398
43447
  historyBehavior: this.provider.historyBehavior,
43399
43448
  scripts: this.provider.scripts,
43400
43449
  sessionStartedAtMs: this.startedAt,
43450
+ envOverrides: this.spawnedEnvOverrides(),
43401
43451
  forceRefresh: true
43402
43452
  });
43403
43453
  if (restoredHistory.source !== "provider-native") {
@@ -48970,14 +49020,44 @@ function openDb() {
48970
49020
  return null;
48971
49021
  }
48972
49022
  }
49023
+ function resolveClusterSessionIds(db, anchorId) {
49024
+ if (!anchorId) return [];
49025
+ try {
49026
+ const rows = db.prepare(
49027
+ `WITH RECURSIVE
49028
+ up(id) AS (
49029
+ SELECT id FROM sessions WHERE id = ?
49030
+ UNION
49031
+ SELECT s.parent_session_id FROM sessions s JOIN up ON s.id = up.id
49032
+ WHERE s.parent_session_id IS NOT NULL
49033
+ ),
49034
+ cluster(id) AS (
49035
+ SELECT id FROM up
49036
+ UNION
49037
+ SELECT s.id FROM sessions s JOIN cluster ON s.parent_session_id = cluster.id
49038
+ )
49039
+ SELECT id FROM cluster`
49040
+ ).all(anchorId);
49041
+ const ids = /* @__PURE__ */ new Set([anchorId]);
49042
+ for (const r of rows) {
49043
+ if (r && r.id != null && String(r.id)) ids.add(String(r.id));
49044
+ }
49045
+ return Array.from(ids);
49046
+ } catch {
49047
+ return [anchorId];
49048
+ }
49049
+ }
48973
49050
  function loadMessagesForSession(db, sessionId) {
49051
+ const clusterIds = resolveClusterSessionIds(db, sessionId);
49052
+ if (clusterIds.length === 0) return [];
49053
+ const placeholders = clusterIds.map(() => "?").join(", ");
48974
49054
  const rows = db.prepare(
48975
49055
  `SELECT id, role, COALESCE(NULLIF(content, ''), tool_calls) AS content, timestamp
48976
49056
  FROM messages
48977
- WHERE session_id = ?
49057
+ WHERE session_id IN (${placeholders})
48978
49058
  AND ((content IS NOT NULL AND content != '') OR (tool_calls IS NOT NULL AND tool_calls != ''))
48979
49059
  ORDER BY timestamp ASC, id ASC`
48980
- ).all(sessionId);
49060
+ ).all(...clusterIds);
48981
49061
  const out = [];
48982
49062
  for (const r of rows) {
48983
49063
  const role = normalizeHermesRole(r.role);
@@ -54917,7 +54997,6 @@ cleanOldFiles();
54917
54997
  // src/commands/router.ts
54918
54998
  init_debug_trace();
54919
54999
  init_mesh_host_ownership();
54920
- init_mesh_work_queue();
54921
55000
  var fs33 = __toESM(require("fs"));
54922
55001
 
54923
55002
  // src/mesh/mesh-node-identity.ts
@@ -59311,6 +59390,177 @@ async function cleanupMeshSessions(self, args) {
59311
59390
  };
59312
59391
  }
59313
59392
 
59393
+ // src/commands/router-aggregate-status.ts
59394
+ init_dist();
59395
+ init_mesh_work_queue();
59396
+ function cloneJsonValue(value) {
59397
+ if (typeof structuredClone === "function") return structuredClone(value);
59398
+ return JSON.parse(JSON.stringify(value));
59399
+ }
59400
+ function hydrateCachedAggregateMeshStatusFromInline(self, snapshot, mesh, options) {
59401
+ if (!mesh || typeof mesh !== "object" || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
59402
+ const inlineNodesById = /* @__PURE__ */ new Map();
59403
+ for (const node of mesh.nodes) {
59404
+ const nodeId = readInlineMeshNodeId(node);
59405
+ if (nodeId) inlineNodesById.set(nodeId, node);
59406
+ }
59407
+ if (!inlineNodesById.size) return snapshot;
59408
+ let changed = false;
59409
+ const unavailableNodeIds = /* @__PURE__ */ new Set();
59410
+ const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
59411
+ const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
59412
+ const deadNodeIds = /* @__PURE__ */ new Set();
59413
+ for (const node of mesh.nodes) {
59414
+ if (!isDeadLocalWorktreeNode(node)) continue;
59415
+ const deadId = readInlineMeshNodeId(node);
59416
+ if (deadId) deadNodeIds.add(deadId);
59417
+ }
59418
+ let droppedDeadUnavailable = false;
59419
+ for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
59420
+ const nodeId = readStringValue(entry);
59421
+ if (!nodeId) continue;
59422
+ if (deadNodeIds.has(nodeId)) {
59423
+ droppedDeadUnavailable = true;
59424
+ continue;
59425
+ }
59426
+ unavailableNodeIds.add(nodeId);
59427
+ }
59428
+ if (droppedDeadUnavailable) changed = true;
59429
+ const nodes = snapshot.nodes.map((statusNode) => {
59430
+ const nodeId = normalizeMeshNodeId(statusNode);
59431
+ const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
59432
+ if (!inlineNode) return statusNode;
59433
+ const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
59434
+ if (!liveGit) return statusNode;
59435
+ const nextStatus = { ...statusNode };
59436
+ nextStatus.git = liveGit;
59437
+ nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
59438
+ applyInlineMeshBranchConvergence(mesh, inlineNode, nextStatus);
59439
+ nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
59440
+ const connection = readObjectRecord(nextStatus.connection);
59441
+ const connectionState = readStringValue(connection.state);
59442
+ const connectionReported = readBooleanValue(connection.reported) ?? false;
59443
+ if (!connectionReported || connectionState === "unknown") {
59444
+ nextStatus.connection = buildLivePeerGitConnection(connection);
59445
+ }
59446
+ delete nextStatus.gitProbePending;
59447
+ const error = readStringValue(nextStatus.error);
59448
+ if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
59449
+ if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
59450
+ if (nodeId) unavailableNodeIds.delete(nodeId);
59451
+ changed = true;
59452
+ return nextStatus;
59453
+ });
59454
+ const aggregateDirectTruthSatisfied = sourceOfTruth.coordinatorOwnsLiveTruth === true || directPeerTruth.satisfied === true;
59455
+ if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied)) return snapshot;
59456
+ const nextSourceOfTruth = {
59457
+ ...sourceOfTruth,
59458
+ ...Object.keys(directPeerTruth).length ? {
59459
+ directPeerTruth: {
59460
+ ...directPeerTruth,
59461
+ satisfied: options?.requireDirectPeerTruth === true ? aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
59462
+ unavailableNodeIds: [...unavailableNodeIds]
59463
+ },
59464
+ ...options?.requireDirectPeerTruth === true ? {
59465
+ coordinatorOwnsLiveTruth: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0,
59466
+ currentStatus: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
59467
+ } : {}
59468
+ } : {}
59469
+ };
59470
+ return {
59471
+ ...snapshot,
59472
+ ...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied ? {
59473
+ success: false,
59474
+ code: "mesh_direct_peer_truth_unavailable",
59475
+ error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
59476
+ } : {},
59477
+ sourceOfTruth: nextSourceOfTruth,
59478
+ branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodes),
59479
+ nodes
59480
+ };
59481
+ }
59482
+ function getCachedAggregateMeshStatus(self, meshId, mesh, options) {
59483
+ const cached3 = self.aggregateMeshStatusCache.get(meshId);
59484
+ if (!cached3?.snapshot || cached3.snapshot.success !== true || !Array.isArray(cached3.snapshot.nodes)) return null;
59485
+ if (cached3.queueRevision !== getMeshQueueRevision(meshId)) return null;
59486
+ let snapshot = cloneJsonValue(cached3.snapshot);
59487
+ snapshot = hydrateCachedAggregateMeshStatusFromInline(self, snapshot, mesh, options);
59488
+ if (!options?.allowStalePending && shouldRefreshStalePendingAggregate(snapshot, options)) return null;
59489
+ const ageMs = Math.max(0, Date.now() - cached3.builtAt);
59490
+ const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
59491
+ snapshot.sourceOfTruth = {
59492
+ ...sourceOfTruth,
59493
+ aggregateSnapshot: {
59494
+ ...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
59495
+ owner: "coordinator_daemon_memory",
59496
+ cached: true,
59497
+ source: "memory",
59498
+ refreshReason: "memory_cache_hit",
59499
+ ageMs,
59500
+ cachedAt: new Date(cached3.builtAt).toISOString(),
59501
+ returnedAt: (/* @__PURE__ */ new Date()).toISOString()
59502
+ }
59503
+ };
59504
+ return snapshot;
59505
+ }
59506
+ function rememberAggregateMeshStatus(self, meshId, snapshot, refreshReason) {
59507
+ if (!snapshot || typeof snapshot !== "object" || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
59508
+ const builtAt = Date.now();
59509
+ const next = cloneJsonValue(snapshot);
59510
+ const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
59511
+ next.sourceOfTruth = {
59512
+ ...sourceOfTruth,
59513
+ aggregateSnapshot: {
59514
+ owner: "coordinator_daemon_memory",
59515
+ cached: false,
59516
+ source: "live_refresh",
59517
+ refreshReason,
59518
+ ageMs: 0,
59519
+ cachedAt: new Date(builtAt).toISOString(),
59520
+ returnedAt: new Date(builtAt).toISOString()
59521
+ }
59522
+ };
59523
+ self.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
59524
+ return next;
59525
+ }
59526
+
59527
+ // src/commands/router-mesh-session-owner.ts
59528
+ init_dist();
59529
+ function resolveRemoteMeshSessionOwnerDaemonId(self, sessionId, ownerNodeIdHint) {
59530
+ const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
59531
+ const nodeHint = typeof ownerNodeIdHint === "string" ? ownerNodeIdHint.trim() : "";
59532
+ if (!trimmed && !nodeHint) return void 0;
59533
+ const selfDaemonId = self.deps.statusInstanceId;
59534
+ const candidates = collectMeshSessionOwnerCandidateNodes(self);
59535
+ if (trimmed) {
59536
+ for (const node of candidates) {
59537
+ if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
59538
+ const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
59539
+ if (!nodeDaemonId) continue;
59540
+ if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
59541
+ return nodeDaemonId;
59542
+ }
59543
+ }
59544
+ if (nodeHint) {
59545
+ for (const node of candidates) {
59546
+ if (!meshNodeIdMatches(node, nodeHint)) continue;
59547
+ const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
59548
+ if (!nodeDaemonId) continue;
59549
+ if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
59550
+ return nodeDaemonId;
59551
+ }
59552
+ }
59553
+ return void 0;
59554
+ }
59555
+ function collectMeshSessionOwnerCandidateNodes(self) {
59556
+ const nodes = self.getCachedInlineMeshNodes();
59557
+ for (const cached3 of self.aggregateMeshStatusCache.values()) {
59558
+ const snapshotNodes = cached3?.snapshot?.nodes;
59559
+ if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
59560
+ }
59561
+ return nodes;
59562
+ }
59563
+
59314
59564
  // src/mesh/mesh-coordinator-config.ts
59315
59565
  init_logger();
59316
59566
  var yaml5 = __toESM(require("js-yaml"));
@@ -59479,7 +59729,8 @@ var DaemonCommandRouter = class {
59479
59729
  * on disk) clears the tombstone and merges normally, preserving clone
59480
59730
  * worktree visibility and legitimate node re-creation. */
59481
59731
  removedInlineMeshNodeIds = /* @__PURE__ */ new Map();
59482
- /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
59732
+ /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default.
59733
+ * Public (not private) so the extracted ./router-aggregate-status.ts orchestration can reach it via `self`. */
59483
59734
  aggregateMeshStatusCache = /* @__PURE__ */ new Map();
59484
59735
  /** Shared per-peer git_status probe dedup + recently-probed reuse gate.
59485
59736
  * Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
@@ -59501,135 +59752,19 @@ var DaemonCommandRouter = class {
59501
59752
  constructor(deps) {
59502
59753
  this.deps = deps;
59503
59754
  }
59504
- cloneJsonValue(value) {
59505
- if (typeof structuredClone === "function") return structuredClone(value);
59506
- return JSON.parse(JSON.stringify(value));
59507
- }
59755
+ // ─── Aggregate mesh-status cache ────────────────────────────────────
59756
+ // Implementation lives in ./router-aggregate-status.ts (behavior-preserving
59757
+ // code move). Kept here as thin delegators: getCachedAggregateMeshStatus /
59758
+ // rememberAggregateMeshStatus are bound into HighFamilyContext, so callers
59759
+ // reach these via `self.` for correct instance dispatch.
59508
59760
  hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options) {
59509
- if (!mesh || typeof mesh !== "object" || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
59510
- const inlineNodesById = /* @__PURE__ */ new Map();
59511
- for (const node of mesh.nodes) {
59512
- const nodeId = readInlineMeshNodeId(node);
59513
- if (nodeId) inlineNodesById.set(nodeId, node);
59514
- }
59515
- if (!inlineNodesById.size) return snapshot;
59516
- let changed = false;
59517
- const unavailableNodeIds = /* @__PURE__ */ new Set();
59518
- const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
59519
- const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
59520
- const deadNodeIds = /* @__PURE__ */ new Set();
59521
- for (const node of mesh.nodes) {
59522
- if (!isDeadLocalWorktreeNode(node)) continue;
59523
- const deadId = readInlineMeshNodeId(node);
59524
- if (deadId) deadNodeIds.add(deadId);
59525
- }
59526
- let droppedDeadUnavailable = false;
59527
- for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
59528
- const nodeId = readStringValue(entry);
59529
- if (!nodeId) continue;
59530
- if (deadNodeIds.has(nodeId)) {
59531
- droppedDeadUnavailable = true;
59532
- continue;
59533
- }
59534
- unavailableNodeIds.add(nodeId);
59535
- }
59536
- if (droppedDeadUnavailable) changed = true;
59537
- const nodes = snapshot.nodes.map((statusNode) => {
59538
- const nodeId = normalizeMeshNodeId(statusNode);
59539
- const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
59540
- if (!inlineNode) return statusNode;
59541
- const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
59542
- if (!liveGit) return statusNode;
59543
- const nextStatus = { ...statusNode };
59544
- nextStatus.git = liveGit;
59545
- nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
59546
- applyInlineMeshBranchConvergence(mesh, inlineNode, nextStatus);
59547
- nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
59548
- const connection = readObjectRecord(nextStatus.connection);
59549
- const connectionState = readStringValue(connection.state);
59550
- const connectionReported = readBooleanValue(connection.reported) ?? false;
59551
- if (!connectionReported || connectionState === "unknown") {
59552
- nextStatus.connection = buildLivePeerGitConnection(connection);
59553
- }
59554
- delete nextStatus.gitProbePending;
59555
- const error = readStringValue(nextStatus.error);
59556
- if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
59557
- if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
59558
- if (nodeId) unavailableNodeIds.delete(nodeId);
59559
- changed = true;
59560
- return nextStatus;
59561
- });
59562
- const aggregateDirectTruthSatisfied = sourceOfTruth.coordinatorOwnsLiveTruth === true || directPeerTruth.satisfied === true;
59563
- if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied)) return snapshot;
59564
- const nextSourceOfTruth = {
59565
- ...sourceOfTruth,
59566
- ...Object.keys(directPeerTruth).length ? {
59567
- directPeerTruth: {
59568
- ...directPeerTruth,
59569
- satisfied: options?.requireDirectPeerTruth === true ? aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
59570
- unavailableNodeIds: [...unavailableNodeIds]
59571
- },
59572
- ...options?.requireDirectPeerTruth === true ? {
59573
- coordinatorOwnsLiveTruth: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0,
59574
- currentStatus: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
59575
- } : {}
59576
- } : {}
59577
- };
59578
- return {
59579
- ...snapshot,
59580
- ...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied ? {
59581
- success: false,
59582
- code: "mesh_direct_peer_truth_unavailable",
59583
- error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
59584
- } : {},
59585
- sourceOfTruth: nextSourceOfTruth,
59586
- branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodes),
59587
- nodes
59588
- };
59761
+ return hydrateCachedAggregateMeshStatusFromInline(this, snapshot, mesh, options);
59589
59762
  }
59590
59763
  getCachedAggregateMeshStatus(meshId, mesh, options) {
59591
- const cached3 = this.aggregateMeshStatusCache.get(meshId);
59592
- if (!cached3?.snapshot || cached3.snapshot.success !== true || !Array.isArray(cached3.snapshot.nodes)) return null;
59593
- if (cached3.queueRevision !== getMeshQueueRevision(meshId)) return null;
59594
- let snapshot = this.cloneJsonValue(cached3.snapshot);
59595
- snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
59596
- if (!options?.allowStalePending && shouldRefreshStalePendingAggregate(snapshot, options)) return null;
59597
- const ageMs = Math.max(0, Date.now() - cached3.builtAt);
59598
- const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
59599
- snapshot.sourceOfTruth = {
59600
- ...sourceOfTruth,
59601
- aggregateSnapshot: {
59602
- ...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
59603
- owner: "coordinator_daemon_memory",
59604
- cached: true,
59605
- source: "memory",
59606
- refreshReason: "memory_cache_hit",
59607
- ageMs,
59608
- cachedAt: new Date(cached3.builtAt).toISOString(),
59609
- returnedAt: (/* @__PURE__ */ new Date()).toISOString()
59610
- }
59611
- };
59612
- return snapshot;
59764
+ return getCachedAggregateMeshStatus(this, meshId, mesh, options);
59613
59765
  }
59614
59766
  rememberAggregateMeshStatus(meshId, snapshot, refreshReason) {
59615
- if (!snapshot || typeof snapshot !== "object" || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
59616
- const builtAt = Date.now();
59617
- const next = this.cloneJsonValue(snapshot);
59618
- const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
59619
- next.sourceOfTruth = {
59620
- ...sourceOfTruth,
59621
- aggregateSnapshot: {
59622
- owner: "coordinator_daemon_memory",
59623
- cached: false,
59624
- source: "live_refresh",
59625
- refreshReason,
59626
- ageMs: 0,
59627
- cachedAt: new Date(builtAt).toISOString(),
59628
- returnedAt: new Date(builtAt).toISOString()
59629
- }
59630
- };
59631
- this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
59632
- return next;
59767
+ return rememberAggregateMeshStatus(this, meshId, snapshot, refreshReason);
59633
59768
  }
59634
59769
  getCachedInlineMeshNodes() {
59635
59770
  const nodes = [];
@@ -59640,82 +59775,13 @@ var DaemonCommandRouter = class {
59640
59775
  }
59641
59776
  return nodes;
59642
59777
  }
59643
- /**
59644
- * Resolve the REMOTE worker daemonId that owns a given session, when the session
59645
- * belongs to a mesh node hosted on a DIFFERENT daemon than this coordinator.
59646
- *
59647
- * The coordinator does not host remote-worker session instances in its own
59648
- * instanceManager/sessionRegistry — only their cached mesh-node metadata. A
59649
- * dashboard-issued session-scoped command (invoke_provider_script / resolve_action /
59650
- * set_mode / …) lands on the coordinator with a targetSessionId the coordinator can't
59651
- * find locally, and without forwarding it dies as "Live session not found". send_chat
59652
- * happens to survive (its target resolves to the worker by another route), but the
59653
- * controlbar commands do not — so the controlbar buttons appear to do nothing.
59654
- *
59655
- * Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
59656
- * scan the candidate mesh nodes for the one hosting the targetSessionId, and return its
59657
- * daemonId when that daemonId is a remote daemon (i.e. not this coordinator's own
59658
- * statusInstanceId). Returns undefined for a locally-hosted session (no forward — execute
59659
- * locally as before) or when ownership can't be resolved.
59660
- *
59661
- * The candidate set spans BOTH the cached inline-mesh nodes and the live aggregate
59662
- * mesh-status snapshots. The inline cache reliably carries only each node's single primary
59663
- * session (cachedStatus.activeSession), so a worker hosting more than one session exposes its
59664
- * non-primary sessions only on the aggregate snapshot nodes (status.activeSessions /
59665
- * activeSessionDetails, built from live session records). collectMeshNodeHostedSessionIds does
59666
- * the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
59667
- * session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
59668
- * other consumers depend on stay untouched.
59669
- *
59670
- * CANCEL-STOP-RELAY: the session-id cache scan above only matches when the coordinator's
59671
- * cached status snapshot already lists the worker's session id in a recognized active-sessions
59672
- * shape. A worktree-clone worker session whose id form/timing differs from the cached snapshot
59673
- * (or is simply not yet reflected) misses the scan, so a stop that carries the authoritative
59674
- * owning nodeId (mesh_queue_cancel knows assignedNodeId) used to silently fail to forward.
59675
- * `ownerNodeIdHint` adds a deterministic fallback: when the session-id scan misses, resolve the
59676
- * owner daemonId by matching the node by id (meshNodeIdMatches — same form-tolerant compare the
59677
- * rest of the router uses, no new raw compare). The same self-loopback guard applies to both
59678
- * paths, so a coordinator-hosted node is still never force-forwarded to a remote form of itself.
59679
- */
59778
+ // ─── Remote mesh-session owner resolution ───────────────────────────
59779
+ // Implementation lives in ./router-mesh-session-owner.ts (behavior-preserving
59780
+ // code move). resolveRemoteMeshSessionOwnerDaemonId stays public (the [Z]
59781
+ // session-scoped forward in executeDaemonCommand and a unit test call it), so
59782
+ // it's kept here as a thin delegator.
59680
59783
  resolveRemoteMeshSessionOwnerDaemonId(sessionId, ownerNodeIdHint) {
59681
- const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
59682
- const nodeHint = typeof ownerNodeIdHint === "string" ? ownerNodeIdHint.trim() : "";
59683
- if (!trimmed && !nodeHint) return void 0;
59684
- const selfDaemonId = this.deps.statusInstanceId;
59685
- const candidates = this.collectMeshSessionOwnerCandidateNodes();
59686
- if (trimmed) {
59687
- for (const node of candidates) {
59688
- if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
59689
- const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
59690
- if (!nodeDaemonId) continue;
59691
- if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
59692
- return nodeDaemonId;
59693
- }
59694
- }
59695
- if (nodeHint) {
59696
- for (const node of candidates) {
59697
- if (!meshNodeIdMatches(node, nodeHint)) continue;
59698
- const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
59699
- if (!nodeDaemonId) continue;
59700
- if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
59701
- return nodeDaemonId;
59702
- }
59703
- }
59704
- return void 0;
59705
- }
59706
- /**
59707
- * Candidate nodes for remote-session owner resolution: the cached inline-mesh nodes (which
59708
- * carry each node's primary session) plus the nodes from every cached aggregate mesh-status
59709
- * snapshot (which carry each node's full live session list). getCachedInlineMeshNodes()
59710
- * returns a fresh array, so appending the aggregate nodes never mutates cached state.
59711
- */
59712
- collectMeshSessionOwnerCandidateNodes() {
59713
- const nodes = this.getCachedInlineMeshNodes();
59714
- for (const cached3 of this.aggregateMeshStatusCache.values()) {
59715
- const snapshotNodes = cached3?.snapshot?.nodes;
59716
- if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
59717
- }
59718
- return nodes;
59784
+ return resolveRemoteMeshSessionOwnerDaemonId(this, sessionId, ownerNodeIdHint);
59719
59785
  }
59720
59786
  getCachedInlineMesh(meshId, inlineMesh) {
59721
59787
  if (inlineMesh && typeof inlineMesh === "object") {