@adhdev/daemon-core 0.9.82-rc.462 → 0.9.82-rc.464
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 +285 -219
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +285 -219
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-pending.d.ts +9 -8
- package/dist/providers/cli-provider-instance.d.ts +9 -0
- package/dist/providers/spec/types.d.ts +22 -0
- package/package.json +3 -3
- 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 +11 -10
- package/src/mesh/mesh-reconcile-loop.ts +4 -3
- package/src/providers/cli-provider-instance.ts +17 -0
- 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/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 ? "
|
|
413
|
-
const commitShort = readInjected(true ? "
|
|
414
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
415
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
412
|
+
const commit = readInjected(true ? "260f54788520ce6f51b44fa097d6d3ee337fe79d" : void 0) ?? "unknown";
|
|
413
|
+
const commitShort = readInjected(true ? "260f5478" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
414
|
+
const version = readInjected(true ? "0.9.82-rc.464" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
415
|
+
const builtAt = readInjected(true ? "2026-07-05T05:20:43.844Z" : void 0);
|
|
416
416
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
417
417
|
return cached;
|
|
418
418
|
}
|
|
@@ -11481,9 +11481,9 @@ function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
|
|
|
11481
11481
|
}
|
|
11482
11482
|
function isMeshProtocolV2EnforceEnabled() {
|
|
11483
11483
|
const raw = readNonEmptyString2(process.env.MESH_PROTOCOL_V2_ENFORCE);
|
|
11484
|
-
if (!raw) return
|
|
11484
|
+
if (!raw) return true;
|
|
11485
11485
|
const v = raw.trim().toLowerCase();
|
|
11486
|
-
return v === "
|
|
11486
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
11487
11487
|
}
|
|
11488
11488
|
function ledgerRecordQuarantinedEvent(event, reason) {
|
|
11489
11489
|
try {
|
|
@@ -18375,9 +18375,9 @@ function getMeshV2BackstopCounters() {
|
|
|
18375
18375
|
}
|
|
18376
18376
|
function meshProtocolV2EnforceOn() {
|
|
18377
18377
|
const raw = process.env.MESH_PROTOCOL_V2_ENFORCE;
|
|
18378
|
-
if (typeof raw !== "string") return
|
|
18378
|
+
if (typeof raw !== "string" || !raw.trim()) return true;
|
|
18379
18379
|
const v = raw.trim().toLowerCase();
|
|
18380
|
-
return v === "
|
|
18380
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
18381
18381
|
}
|
|
18382
18382
|
function recordBackstopFire(kind, detail) {
|
|
18383
18383
|
meshV2BackstopCounters[kind]++;
|
|
@@ -40689,15 +40689,37 @@ function executeSqlite(src, input) {
|
|
|
40689
40689
|
}
|
|
40690
40690
|
try {
|
|
40691
40691
|
const requested = input.providerSessionId || "";
|
|
40692
|
-
const
|
|
40693
|
-
|
|
40694
|
-
|
|
40695
|
-
|
|
40696
|
-
|
|
40697
|
-
|
|
40698
|
-
|
|
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
|
+
}
|
|
40704
|
+
}
|
|
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);
|
|
40699
40719
|
}
|
|
40700
|
-
|
|
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(
|
|
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
|
-
|
|
59505
|
-
|
|
59506
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59645
|
-
|
|
59646
|
-
|
|
59647
|
-
|
|
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
|
-
|
|
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") {
|