@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/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 +281 -215
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +281 -215
- package/dist/index.mjs.map +1 -1
- 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/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.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
|
}
|
|
@@ -40279,15 +40279,37 @@ function executeSqlite(src, input) {
|
|
|
40279
40279
|
}
|
|
40280
40280
|
try {
|
|
40281
40281
|
const requested = input.providerSessionId || "";
|
|
40282
|
-
const
|
|
40283
|
-
|
|
40284
|
-
|
|
40285
|
-
|
|
40286
|
-
|
|
40287
|
-
|
|
40288
|
-
|
|
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
|
+
}
|
|
40289
40294
|
}
|
|
40290
|
-
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;
|
|
40291
40313
|
};
|
|
40292
40314
|
const resolveNewestSessionId = () => {
|
|
40293
40315
|
let sessionRow;
|
|
@@ -40344,6 +40366,20 @@ function executeSqlite(src, input) {
|
|
|
40344
40366
|
}
|
|
40345
40367
|
}
|
|
40346
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
|
+
}
|
|
40347
40383
|
function expandPath2(template, input, opts) {
|
|
40348
40384
|
if (!template) return null;
|
|
40349
40385
|
let out = template;
|
|
@@ -42968,6 +43004,19 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42968
43004
|
}
|
|
42969
43005
|
return probe;
|
|
42970
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
|
+
}
|
|
42971
43020
|
readExternalCompletionMessages() {
|
|
42972
43021
|
const adapterOwnsMessagesElsewhere = this.adapter?.chatMessagesOwnedExternally === true;
|
|
42973
43022
|
if (!adapterOwnsMessagesElsewhere) return null;
|
|
@@ -42988,6 +43037,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42988
43037
|
historyBehavior: this.provider.historyBehavior,
|
|
42989
43038
|
scripts: this.provider.scripts,
|
|
42990
43039
|
sessionStartedAtMs: this.startedAt,
|
|
43040
|
+
envOverrides: this.spawnedEnvOverrides(),
|
|
42991
43041
|
forceRefresh: true
|
|
42992
43042
|
});
|
|
42993
43043
|
if (restoredHistory.source !== "provider-native") {
|
|
@@ -48565,14 +48615,44 @@ function openDb() {
|
|
|
48565
48615
|
return null;
|
|
48566
48616
|
}
|
|
48567
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
|
+
}
|
|
48568
48645
|
function loadMessagesForSession(db, sessionId) {
|
|
48646
|
+
const clusterIds = resolveClusterSessionIds(db, sessionId);
|
|
48647
|
+
if (clusterIds.length === 0) return [];
|
|
48648
|
+
const placeholders = clusterIds.map(() => "?").join(", ");
|
|
48569
48649
|
const rows = db.prepare(
|
|
48570
48650
|
`SELECT id, role, COALESCE(NULLIF(content, ''), tool_calls) AS content, timestamp
|
|
48571
48651
|
FROM messages
|
|
48572
|
-
WHERE session_id
|
|
48652
|
+
WHERE session_id IN (${placeholders})
|
|
48573
48653
|
AND ((content IS NOT NULL AND content != '') OR (tool_calls IS NOT NULL AND tool_calls != ''))
|
|
48574
48654
|
ORDER BY timestamp ASC, id ASC`
|
|
48575
|
-
).all(
|
|
48655
|
+
).all(...clusterIds);
|
|
48576
48656
|
const out = [];
|
|
48577
48657
|
for (const r of rows) {
|
|
48578
48658
|
const role = normalizeHermesRole(r.role);
|
|
@@ -54512,7 +54592,6 @@ cleanOldFiles();
|
|
|
54512
54592
|
// src/commands/router.ts
|
|
54513
54593
|
init_debug_trace();
|
|
54514
54594
|
init_mesh_host_ownership();
|
|
54515
|
-
init_mesh_work_queue();
|
|
54516
54595
|
import * as fs33 from "fs";
|
|
54517
54596
|
|
|
54518
54597
|
// src/mesh/mesh-node-identity.ts
|
|
@@ -58906,6 +58985,177 @@ async function cleanupMeshSessions(self, args) {
|
|
|
58906
58985
|
};
|
|
58907
58986
|
}
|
|
58908
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
|
+
|
|
58909
59159
|
// src/mesh/mesh-coordinator-config.ts
|
|
58910
59160
|
init_logger();
|
|
58911
59161
|
import * as yaml5 from "js-yaml";
|
|
@@ -59074,7 +59324,8 @@ var DaemonCommandRouter = class {
|
|
|
59074
59324
|
* on disk) clears the tombstone and merges normally, preserving clone
|
|
59075
59325
|
* worktree visibility and legitimate node re-creation. */
|
|
59076
59326
|
removedInlineMeshNodeIds = /* @__PURE__ */ new Map();
|
|
59077
|
-
/** 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`. */
|
|
59078
59329
|
aggregateMeshStatusCache = /* @__PURE__ */ new Map();
|
|
59079
59330
|
/** Shared per-peer git_status probe dedup + recently-probed reuse gate.
|
|
59080
59331
|
* Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
|
|
@@ -59096,135 +59347,19 @@ var DaemonCommandRouter = class {
|
|
|
59096
59347
|
constructor(deps) {
|
|
59097
59348
|
this.deps = deps;
|
|
59098
59349
|
}
|
|
59099
|
-
|
|
59100
|
-
|
|
59101
|
-
|
|
59102
|
-
|
|
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.
|
|
59103
59355
|
hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options) {
|
|
59104
|
-
|
|
59105
|
-
const inlineNodesById = /* @__PURE__ */ new Map();
|
|
59106
|
-
for (const node of mesh.nodes) {
|
|
59107
|
-
const nodeId = readInlineMeshNodeId(node);
|
|
59108
|
-
if (nodeId) inlineNodesById.set(nodeId, node);
|
|
59109
|
-
}
|
|
59110
|
-
if (!inlineNodesById.size) return snapshot;
|
|
59111
|
-
let changed = false;
|
|
59112
|
-
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
59113
|
-
const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
|
|
59114
|
-
const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
|
|
59115
|
-
const deadNodeIds = /* @__PURE__ */ new Set();
|
|
59116
|
-
for (const node of mesh.nodes) {
|
|
59117
|
-
if (!isDeadLocalWorktreeNode(node)) continue;
|
|
59118
|
-
const deadId = readInlineMeshNodeId(node);
|
|
59119
|
-
if (deadId) deadNodeIds.add(deadId);
|
|
59120
|
-
}
|
|
59121
|
-
let droppedDeadUnavailable = false;
|
|
59122
|
-
for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
|
|
59123
|
-
const nodeId = readStringValue(entry);
|
|
59124
|
-
if (!nodeId) continue;
|
|
59125
|
-
if (deadNodeIds.has(nodeId)) {
|
|
59126
|
-
droppedDeadUnavailable = true;
|
|
59127
|
-
continue;
|
|
59128
|
-
}
|
|
59129
|
-
unavailableNodeIds.add(nodeId);
|
|
59130
|
-
}
|
|
59131
|
-
if (droppedDeadUnavailable) changed = true;
|
|
59132
|
-
const nodes = snapshot.nodes.map((statusNode) => {
|
|
59133
|
-
const nodeId = normalizeMeshNodeId(statusNode);
|
|
59134
|
-
const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
|
|
59135
|
-
if (!inlineNode) return statusNode;
|
|
59136
|
-
const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
|
|
59137
|
-
if (!liveGit) return statusNode;
|
|
59138
|
-
const nextStatus = { ...statusNode };
|
|
59139
|
-
nextStatus.git = liveGit;
|
|
59140
|
-
nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
|
|
59141
|
-
applyInlineMeshBranchConvergence(mesh, inlineNode, nextStatus);
|
|
59142
|
-
nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
|
|
59143
|
-
const connection = readObjectRecord(nextStatus.connection);
|
|
59144
|
-
const connectionState = readStringValue(connection.state);
|
|
59145
|
-
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
59146
|
-
if (!connectionReported || connectionState === "unknown") {
|
|
59147
|
-
nextStatus.connection = buildLivePeerGitConnection(connection);
|
|
59148
|
-
}
|
|
59149
|
-
delete nextStatus.gitProbePending;
|
|
59150
|
-
const error = readStringValue(nextStatus.error);
|
|
59151
|
-
if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
|
|
59152
|
-
if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
|
|
59153
|
-
if (nodeId) unavailableNodeIds.delete(nodeId);
|
|
59154
|
-
changed = true;
|
|
59155
|
-
return nextStatus;
|
|
59156
|
-
});
|
|
59157
|
-
const aggregateDirectTruthSatisfied = sourceOfTruth.coordinatorOwnsLiveTruth === true || directPeerTruth.satisfied === true;
|
|
59158
|
-
if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied)) return snapshot;
|
|
59159
|
-
const nextSourceOfTruth = {
|
|
59160
|
-
...sourceOfTruth,
|
|
59161
|
-
...Object.keys(directPeerTruth).length ? {
|
|
59162
|
-
directPeerTruth: {
|
|
59163
|
-
...directPeerTruth,
|
|
59164
|
-
satisfied: options?.requireDirectPeerTruth === true ? aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
|
|
59165
|
-
unavailableNodeIds: [...unavailableNodeIds]
|
|
59166
|
-
},
|
|
59167
|
-
...options?.requireDirectPeerTruth === true ? {
|
|
59168
|
-
coordinatorOwnsLiveTruth: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0,
|
|
59169
|
-
currentStatus: aggregateDirectTruthSatisfied || unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
|
|
59170
|
-
} : {}
|
|
59171
|
-
} : {}
|
|
59172
|
-
};
|
|
59173
|
-
return {
|
|
59174
|
-
...snapshot,
|
|
59175
|
-
...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 && !aggregateDirectTruthSatisfied ? {
|
|
59176
|
-
success: false,
|
|
59177
|
-
code: "mesh_direct_peer_truth_unavailable",
|
|
59178
|
-
error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
|
|
59179
|
-
} : {},
|
|
59180
|
-
sourceOfTruth: nextSourceOfTruth,
|
|
59181
|
-
branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodes),
|
|
59182
|
-
nodes
|
|
59183
|
-
};
|
|
59356
|
+
return hydrateCachedAggregateMeshStatusFromInline(this, snapshot, mesh, options);
|
|
59184
59357
|
}
|
|
59185
59358
|
getCachedAggregateMeshStatus(meshId, mesh, options) {
|
|
59186
|
-
|
|
59187
|
-
if (!cached3?.snapshot || cached3.snapshot.success !== true || !Array.isArray(cached3.snapshot.nodes)) return null;
|
|
59188
|
-
if (cached3.queueRevision !== getMeshQueueRevision(meshId)) return null;
|
|
59189
|
-
let snapshot = this.cloneJsonValue(cached3.snapshot);
|
|
59190
|
-
snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
|
|
59191
|
-
if (!options?.allowStalePending && shouldRefreshStalePendingAggregate(snapshot, options)) return null;
|
|
59192
|
-
const ageMs = Math.max(0, Date.now() - cached3.builtAt);
|
|
59193
|
-
const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
|
|
59194
|
-
snapshot.sourceOfTruth = {
|
|
59195
|
-
...sourceOfTruth,
|
|
59196
|
-
aggregateSnapshot: {
|
|
59197
|
-
...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
|
|
59198
|
-
owner: "coordinator_daemon_memory",
|
|
59199
|
-
cached: true,
|
|
59200
|
-
source: "memory",
|
|
59201
|
-
refreshReason: "memory_cache_hit",
|
|
59202
|
-
ageMs,
|
|
59203
|
-
cachedAt: new Date(cached3.builtAt).toISOString(),
|
|
59204
|
-
returnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
59205
|
-
}
|
|
59206
|
-
};
|
|
59207
|
-
return snapshot;
|
|
59359
|
+
return getCachedAggregateMeshStatus(this, meshId, mesh, options);
|
|
59208
59360
|
}
|
|
59209
59361
|
rememberAggregateMeshStatus(meshId, snapshot, refreshReason) {
|
|
59210
|
-
|
|
59211
|
-
const builtAt = Date.now();
|
|
59212
|
-
const next = this.cloneJsonValue(snapshot);
|
|
59213
|
-
const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
|
|
59214
|
-
next.sourceOfTruth = {
|
|
59215
|
-
...sourceOfTruth,
|
|
59216
|
-
aggregateSnapshot: {
|
|
59217
|
-
owner: "coordinator_daemon_memory",
|
|
59218
|
-
cached: false,
|
|
59219
|
-
source: "live_refresh",
|
|
59220
|
-
refreshReason,
|
|
59221
|
-
ageMs: 0,
|
|
59222
|
-
cachedAt: new Date(builtAt).toISOString(),
|
|
59223
|
-
returnedAt: new Date(builtAt).toISOString()
|
|
59224
|
-
}
|
|
59225
|
-
};
|
|
59226
|
-
this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
|
|
59227
|
-
return next;
|
|
59362
|
+
return rememberAggregateMeshStatus(this, meshId, snapshot, refreshReason);
|
|
59228
59363
|
}
|
|
59229
59364
|
getCachedInlineMeshNodes() {
|
|
59230
59365
|
const nodes = [];
|
|
@@ -59235,82 +59370,13 @@ var DaemonCommandRouter = class {
|
|
|
59235
59370
|
}
|
|
59236
59371
|
return nodes;
|
|
59237
59372
|
}
|
|
59238
|
-
|
|
59239
|
-
|
|
59240
|
-
|
|
59241
|
-
|
|
59242
|
-
|
|
59243
|
-
* instanceManager/sessionRegistry — only their cached mesh-node metadata. A
|
|
59244
|
-
* dashboard-issued session-scoped command (invoke_provider_script / resolve_action /
|
|
59245
|
-
* set_mode / …) lands on the coordinator with a targetSessionId the coordinator can't
|
|
59246
|
-
* find locally, and without forwarding it dies as "Live session not found". send_chat
|
|
59247
|
-
* happens to survive (its target resolves to the worker by another route), but the
|
|
59248
|
-
* controlbar commands do not — so the controlbar buttons appear to do nothing.
|
|
59249
|
-
*
|
|
59250
|
-
* Mirror the existing node-level remote-forward pattern (fast_forward_mesh_node etc.):
|
|
59251
|
-
* scan the candidate mesh nodes for the one hosting the targetSessionId, and return its
|
|
59252
|
-
* daemonId when that daemonId is a remote daemon (i.e. not this coordinator's own
|
|
59253
|
-
* statusInstanceId). Returns undefined for a locally-hosted session (no forward — execute
|
|
59254
|
-
* locally as before) or when ownership can't be resolved.
|
|
59255
|
-
*
|
|
59256
|
-
* The candidate set spans BOTH the cached inline-mesh nodes and the live aggregate
|
|
59257
|
-
* mesh-status snapshots. The inline cache reliably carries only each node's single primary
|
|
59258
|
-
* session (cachedStatus.activeSession), so a worker hosting more than one session exposes its
|
|
59259
|
-
* non-primary sessions only on the aggregate snapshot nodes (status.activeSessions /
|
|
59260
|
-
* activeSessionDetails, built from live session records). collectMeshNodeHostedSessionIds does
|
|
59261
|
-
* the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
|
|
59262
|
-
* session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
|
|
59263
|
-
* other consumers depend on stay untouched.
|
|
59264
|
-
*
|
|
59265
|
-
* CANCEL-STOP-RELAY: the session-id cache scan above only matches when the coordinator's
|
|
59266
|
-
* cached status snapshot already lists the worker's session id in a recognized active-sessions
|
|
59267
|
-
* shape. A worktree-clone worker session whose id form/timing differs from the cached snapshot
|
|
59268
|
-
* (or is simply not yet reflected) misses the scan, so a stop that carries the authoritative
|
|
59269
|
-
* owning nodeId (mesh_queue_cancel knows assignedNodeId) used to silently fail to forward.
|
|
59270
|
-
* `ownerNodeIdHint` adds a deterministic fallback: when the session-id scan misses, resolve the
|
|
59271
|
-
* owner daemonId by matching the node by id (meshNodeIdMatches — same form-tolerant compare the
|
|
59272
|
-
* rest of the router uses, no new raw compare). The same self-loopback guard applies to both
|
|
59273
|
-
* paths, so a coordinator-hosted node is still never force-forwarded to a remote form of itself.
|
|
59274
|
-
*/
|
|
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.
|
|
59275
59378
|
resolveRemoteMeshSessionOwnerDaemonId(sessionId, ownerNodeIdHint) {
|
|
59276
|
-
|
|
59277
|
-
const nodeHint = typeof ownerNodeIdHint === "string" ? ownerNodeIdHint.trim() : "";
|
|
59278
|
-
if (!trimmed && !nodeHint) return void 0;
|
|
59279
|
-
const selfDaemonId = this.deps.statusInstanceId;
|
|
59280
|
-
const candidates = this.collectMeshSessionOwnerCandidateNodes();
|
|
59281
|
-
if (trimmed) {
|
|
59282
|
-
for (const node of candidates) {
|
|
59283
|
-
if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
|
|
59284
|
-
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
59285
|
-
if (!nodeDaemonId) continue;
|
|
59286
|
-
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
59287
|
-
return nodeDaemonId;
|
|
59288
|
-
}
|
|
59289
|
-
}
|
|
59290
|
-
if (nodeHint) {
|
|
59291
|
-
for (const node of candidates) {
|
|
59292
|
-
if (!meshNodeIdMatches(node, nodeHint)) continue;
|
|
59293
|
-
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
59294
|
-
if (!nodeDaemonId) continue;
|
|
59295
|
-
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
59296
|
-
return nodeDaemonId;
|
|
59297
|
-
}
|
|
59298
|
-
}
|
|
59299
|
-
return void 0;
|
|
59300
|
-
}
|
|
59301
|
-
/**
|
|
59302
|
-
* Candidate nodes for remote-session owner resolution: the cached inline-mesh nodes (which
|
|
59303
|
-
* carry each node's primary session) plus the nodes from every cached aggregate mesh-status
|
|
59304
|
-
* snapshot (which carry each node's full live session list). getCachedInlineMeshNodes()
|
|
59305
|
-
* returns a fresh array, so appending the aggregate nodes never mutates cached state.
|
|
59306
|
-
*/
|
|
59307
|
-
collectMeshSessionOwnerCandidateNodes() {
|
|
59308
|
-
const nodes = this.getCachedInlineMeshNodes();
|
|
59309
|
-
for (const cached3 of this.aggregateMeshStatusCache.values()) {
|
|
59310
|
-
const snapshotNodes = cached3?.snapshot?.nodes;
|
|
59311
|
-
if (Array.isArray(snapshotNodes)) nodes.push(...snapshotNodes);
|
|
59312
|
-
}
|
|
59313
|
-
return nodes;
|
|
59379
|
+
return resolveRemoteMeshSessionOwnerDaemonId(this, sessionId, ownerNodeIdHint);
|
|
59314
59380
|
}
|
|
59315
59381
|
getCachedInlineMesh(meshId, inlineMesh) {
|
|
59316
59382
|
if (inlineMesh && typeof inlineMesh === "object") {
|