@adhdev/daemon-core 0.9.82-rc.381 → 0.9.82-rc.383
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.d.ts +11 -1
- package/dist/index.js +234 -117
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +234 -117
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-event-forwarding.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +18 -0
- package/package.json +2 -2
- package/src/commands/high-family/mesh-status.ts +36 -0
- package/src/commands/router.ts +50 -17
- package/src/mesh/mesh-event-forwarding.ts +46 -1
- package/src/mesh/mesh-reconcile-loop.ts +74 -30
- package/src/providers/cli-provider-instance.ts +27 -0
|
@@ -156,8 +156,18 @@ export declare class DaemonCommandRouter {
|
|
|
156
156
|
* the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
|
|
157
157
|
* session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
|
|
158
158
|
* other consumers depend on stay untouched.
|
|
159
|
+
*
|
|
160
|
+
* CANCEL-STOP-RELAY: the session-id cache scan above only matches when the coordinator's
|
|
161
|
+
* cached status snapshot already lists the worker's session id in a recognized active-sessions
|
|
162
|
+
* shape. A worktree-clone worker session whose id form/timing differs from the cached snapshot
|
|
163
|
+
* (or is simply not yet reflected) misses the scan, so a stop that carries the authoritative
|
|
164
|
+
* owning nodeId (mesh_queue_cancel knows assignedNodeId) used to silently fail to forward.
|
|
165
|
+
* `ownerNodeIdHint` adds a deterministic fallback: when the session-id scan misses, resolve the
|
|
166
|
+
* owner daemonId by matching the node by id (meshNodeIdMatches — same form-tolerant compare the
|
|
167
|
+
* rest of the router uses, no new raw compare). The same self-loopback guard applies to both
|
|
168
|
+
* paths, so a coordinator-hosted node is still never force-forwarded to a remote form of itself.
|
|
159
169
|
*/
|
|
160
|
-
resolveRemoteMeshSessionOwnerDaemonId(sessionId: string): string | undefined;
|
|
170
|
+
resolveRemoteMeshSessionOwnerDaemonId(sessionId: string, ownerNodeIdHint?: string): string | undefined;
|
|
161
171
|
/**
|
|
162
172
|
* Candidate nodes for remote-session owner resolution: the cached inline-mesh nodes (which
|
|
163
173
|
* carry each node's primary session) plus the nodes from every cached aggregate mesh-status
|
package/dist/index.js
CHANGED
|
@@ -383,10 +383,10 @@ function readInjected(value) {
|
|
|
383
383
|
}
|
|
384
384
|
function getDaemonBuildInfo() {
|
|
385
385
|
if (cached) return cached;
|
|
386
|
-
const commit = readInjected(true ? "
|
|
387
|
-
const commitShort = readInjected(true ? "
|
|
388
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
389
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
386
|
+
const commit = readInjected(true ? "6cca365bcea9757dd350c6bac460ca5a169a9280" : void 0) ?? "unknown";
|
|
387
|
+
const commitShort = readInjected(true ? "6cca365b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
388
|
+
const version = readInjected(true ? "0.9.82-rc.383" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
389
|
+
const builtAt = readInjected(true ? "2026-06-25T15:14:41.579Z" : void 0);
|
|
390
390
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
391
391
|
return cached;
|
|
392
392
|
}
|
|
@@ -8581,6 +8581,109 @@ var init_mesh_active_work = __esm({
|
|
|
8581
8581
|
}
|
|
8582
8582
|
});
|
|
8583
8583
|
|
|
8584
|
+
// src/mesh/mesh-scheduling-runtime.ts
|
|
8585
|
+
var mesh_scheduling_runtime_exports = {};
|
|
8586
|
+
__export(mesh_scheduling_runtime_exports, {
|
|
8587
|
+
buildMeshSchedulingRuntime: () => buildMeshSchedulingRuntime
|
|
8588
|
+
});
|
|
8589
|
+
function isReadonly(task) {
|
|
8590
|
+
return task.taskMode === "live_debug_readonly";
|
|
8591
|
+
}
|
|
8592
|
+
function isAssigned(task) {
|
|
8593
|
+
return task.status === "assigned";
|
|
8594
|
+
}
|
|
8595
|
+
function buildMeshSchedulingRuntime(mesh, queue) {
|
|
8596
|
+
const strategy = normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
|
|
8597
|
+
const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
|
|
8598
|
+
const maxReadonlyParallelTasks = Math.max(2, maxParallelTasks * 2);
|
|
8599
|
+
const assignedTasks = (Array.isArray(queue) ? queue : []).filter(isAssigned);
|
|
8600
|
+
const activeWriteAssigned = assignedTasks.filter((t) => !isReadonly(t)).length;
|
|
8601
|
+
const activeReadonlyAssigned = assignedTasks.filter(isReadonly).length;
|
|
8602
|
+
const globalWriteCapReached = activeWriteAssigned >= maxParallelTasks;
|
|
8603
|
+
const globalReadonlyCapReached = activeReadonlyAssigned >= maxReadonlyParallelTasks;
|
|
8604
|
+
const writeAssignedByNode = /* @__PURE__ */ new Map();
|
|
8605
|
+
const assignedByNode = /* @__PURE__ */ new Map();
|
|
8606
|
+
const providerCountByNode = /* @__PURE__ */ new Map();
|
|
8607
|
+
for (const task of assignedTasks) {
|
|
8608
|
+
const nodeId = typeof task.assignedNodeId === "string" ? task.assignedNodeId.trim() : "";
|
|
8609
|
+
if (!nodeId) continue;
|
|
8610
|
+
assignedByNode.set(nodeId, (assignedByNode.get(nodeId) ?? 0) + 1);
|
|
8611
|
+
if (!isReadonly(task)) {
|
|
8612
|
+
writeAssignedByNode.set(nodeId, (writeAssignedByNode.get(nodeId) ?? 0) + 1);
|
|
8613
|
+
}
|
|
8614
|
+
const provider = typeof task.assignedProviderType === "string" ? task.assignedProviderType : "";
|
|
8615
|
+
if (provider) {
|
|
8616
|
+
let byProvider = providerCountByNode.get(nodeId);
|
|
8617
|
+
if (!byProvider) {
|
|
8618
|
+
byProvider = /* @__PURE__ */ new Map();
|
|
8619
|
+
providerCountByNode.set(nodeId, byProvider);
|
|
8620
|
+
}
|
|
8621
|
+
byProvider.set(provider, (byProvider.get(provider) ?? 0) + 1);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8624
|
+
const nodes = [];
|
|
8625
|
+
for (const rawNode of Array.isArray(mesh?.nodes) ? mesh.nodes : []) {
|
|
8626
|
+
const nodeId = normalizeMeshNodeId(rawNode);
|
|
8627
|
+
if (!nodeId) continue;
|
|
8628
|
+
const policy = rawNode?.policy || void 0;
|
|
8629
|
+
const load4 = assignedByNode.get(nodeId) ?? 0;
|
|
8630
|
+
const schedulingPriority = resolveNodeSchedulingPriority(policy);
|
|
8631
|
+
const capReasons = [];
|
|
8632
|
+
if (globalWriteCapReached) capReasons.push("global_max_parallel_tasks_reached");
|
|
8633
|
+
if ((writeAssignedByNode.get(nodeId) ?? 0) > 0) capReasons.push("node_has_active_assignment");
|
|
8634
|
+
let providerRoles;
|
|
8635
|
+
const declaredRoles = Array.isArray(policy?.providerRoles) ? policy.providerRoles : [];
|
|
8636
|
+
if (declaredRoles.length) {
|
|
8637
|
+
const byProvider = providerCountByNode.get(nodeId);
|
|
8638
|
+
providerRoles = [];
|
|
8639
|
+
for (const role of declaredRoles) {
|
|
8640
|
+
if (!role || typeof role !== "object") continue;
|
|
8641
|
+
const providerType = typeof role.providerType === "string" ? role.providerType.trim() : "";
|
|
8642
|
+
if (!providerType) continue;
|
|
8643
|
+
const maxParallel = resolveProviderMaxParallel(policy, providerType);
|
|
8644
|
+
const activeAssigned = byProvider?.get(providerType) ?? 0;
|
|
8645
|
+
const capReached = maxParallel !== void 0 && activeAssigned >= maxParallel;
|
|
8646
|
+
providerRoles.push({
|
|
8647
|
+
providerType,
|
|
8648
|
+
...maxParallel !== void 0 ? { maxParallel } : {},
|
|
8649
|
+
activeAssigned,
|
|
8650
|
+
capReached
|
|
8651
|
+
});
|
|
8652
|
+
if (capReached) capReasons.push(`max_provider_parallel_reached:${providerType}`);
|
|
8653
|
+
}
|
|
8654
|
+
if (!providerRoles.length) providerRoles = void 0;
|
|
8655
|
+
}
|
|
8656
|
+
const maxConcurrentSessions = Number(policy?.maxConcurrentSessions);
|
|
8657
|
+
const hasSessionCap = Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0;
|
|
8658
|
+
nodes.push({
|
|
8659
|
+
nodeId,
|
|
8660
|
+
load: load4,
|
|
8661
|
+
schedulingPriority,
|
|
8662
|
+
...hasSessionCap ? { maxConcurrentSessions: Math.floor(maxConcurrentSessions) } : {},
|
|
8663
|
+
...providerRoles ? { providerRoles } : {},
|
|
8664
|
+
capReached: capReasons.length > 0,
|
|
8665
|
+
capReasons
|
|
8666
|
+
});
|
|
8667
|
+
}
|
|
8668
|
+
return {
|
|
8669
|
+
strategy,
|
|
8670
|
+
maxParallelTasks,
|
|
8671
|
+
maxReadonlyParallelTasks,
|
|
8672
|
+
activeWriteAssigned,
|
|
8673
|
+
activeReadonlyAssigned,
|
|
8674
|
+
globalWriteCapReached,
|
|
8675
|
+
globalReadonlyCapReached,
|
|
8676
|
+
nodes
|
|
8677
|
+
};
|
|
8678
|
+
}
|
|
8679
|
+
var init_mesh_scheduling_runtime = __esm({
|
|
8680
|
+
"src/mesh/mesh-scheduling-runtime.ts"() {
|
|
8681
|
+
"use strict";
|
|
8682
|
+
init_repo_mesh_types();
|
|
8683
|
+
init_dist();
|
|
8684
|
+
}
|
|
8685
|
+
});
|
|
8686
|
+
|
|
8584
8687
|
// src/mesh/mesh-events-utils.ts
|
|
8585
8688
|
function readNonEmptyString2(value) {
|
|
8586
8689
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
@@ -13810,6 +13913,20 @@ function recoverMeshIdByNodeId(nodeId) {
|
|
|
13810
13913
|
}
|
|
13811
13914
|
return "";
|
|
13812
13915
|
}
|
|
13916
|
+
function recoverMeshIdByCoordinatorAndNode(coordinatorDaemonId, nodeId) {
|
|
13917
|
+
if (!coordinatorDaemonId) return "";
|
|
13918
|
+
const hosted = listMeshes().filter((mesh) => {
|
|
13919
|
+
const host = resolveMeshHostStatus(mesh);
|
|
13920
|
+
return host.role === "host" && (!host.hostDaemonId || daemonIdsEquivalent(host.hostDaemonId, coordinatorDaemonId));
|
|
13921
|
+
});
|
|
13922
|
+
if (hosted.length === 0) return "";
|
|
13923
|
+
if (nodeId) {
|
|
13924
|
+
const byNode = hosted.find((mesh) => Array.isArray(mesh.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, nodeId)));
|
|
13925
|
+
if (byNode) return readNonEmptyString2(byNode.id);
|
|
13926
|
+
return "";
|
|
13927
|
+
}
|
|
13928
|
+
return hosted.length === 1 ? readNonEmptyString2(hosted[0].id) : "";
|
|
13929
|
+
}
|
|
13813
13930
|
function resolveForwardEventMeshId(components, payload) {
|
|
13814
13931
|
const direct = readNonEmptyString2(payload.meshId);
|
|
13815
13932
|
if (direct) return direct;
|
|
@@ -14556,7 +14673,10 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
14556
14673
|
}
|
|
14557
14674
|
const nodeId = readNonEmptyString2(payload.nodeId);
|
|
14558
14675
|
const workspace = readNonEmptyString2(payload.workspace);
|
|
14559
|
-
const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "") || recoverMeshIdByNodeId(nodeId)
|
|
14676
|
+
const meshId = readNonEmptyString2(payload.meshId) || (workspace ? readNonEmptyString2(getCachedMeshByWorkspace(workspace)?.id) : "") || recoverMeshIdByNodeId(nodeId) || recoverMeshIdByCoordinatorAndNode(
|
|
14677
|
+
readNonEmptyString2(payload.meshCoordinatorDaemonId) || readNonEmptyString2(payload.coordinatorDaemonId),
|
|
14678
|
+
nodeId
|
|
14679
|
+
);
|
|
14560
14680
|
if (!meshId) {
|
|
14561
14681
|
traceMeshEventDrop("meshId_required", {
|
|
14562
14682
|
taskId: payload.taskId,
|
|
@@ -14611,7 +14731,12 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
|
14611
14731
|
...event,
|
|
14612
14732
|
event: eventName,
|
|
14613
14733
|
nodeId: readNonEmptyString2(routing.nodeId) || readNonEmptyString2(event.meshNodeId) || void 0,
|
|
14614
|
-
workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0
|
|
14734
|
+
workspace: readNonEmptyString2(routing.workspace) || readNonEmptyString2(event.workspace) || void 0,
|
|
14735
|
+
// Fix B: carry the resolved coordinator anchor so the coordinator's receive-side
|
|
14736
|
+
// recovery (recoverMeshIdByCoordinatorAndNode) can match this forward to one of the
|
|
14737
|
+
// meshes it hosts when workspace + nodeId both miss. routing.coordinatorDaemonId is
|
|
14738
|
+
// the same anchor this forward is addressed to (coordinatorDaemonId below).
|
|
14739
|
+
meshCoordinatorDaemonId: coordinatorDaemonId
|
|
14615
14740
|
};
|
|
14616
14741
|
const resolvedMeshId = resolveForwardEventMeshId(components, payload);
|
|
14617
14742
|
if (resolvedMeshId) payload.meshId = resolvedMeshId;
|
|
@@ -14740,6 +14865,7 @@ var init_mesh_event_forwarding = __esm({
|
|
|
14740
14865
|
init_mesh_runtime_store();
|
|
14741
14866
|
init_mesh_events_pending();
|
|
14742
14867
|
init_mesh_routing();
|
|
14868
|
+
init_mesh_host_ownership();
|
|
14743
14869
|
init_mesh_unresolved_forward_outbox();
|
|
14744
14870
|
init_mesh_event_trace();
|
|
14745
14871
|
init_snapshot();
|
|
@@ -15031,8 +15157,7 @@ async function runMeshReconcileTick(components) {
|
|
|
15031
15157
|
const idleCoordinators = meshCoordinators.filter((c) => c.idle);
|
|
15032
15158
|
const generatingCoordinators = meshCoordinators.filter((c) => !c.idle && !c.modalParked);
|
|
15033
15159
|
const modalParkedCoordinators = meshCoordinators.filter((c) => !c.idle && c.modalParked);
|
|
15034
|
-
const targetCoordinators = idleCoordinators
|
|
15035
|
-
const forceOnly = idleCoordinators.length === 0;
|
|
15160
|
+
const targetCoordinators = idleCoordinators;
|
|
15036
15161
|
if (targetCoordinators.length === 0) {
|
|
15037
15162
|
if (modalParkedCoordinators.length > 0) {
|
|
15038
15163
|
const liveSessionIds = new Set(
|
|
@@ -15099,6 +15224,23 @@ async function runMeshReconcileTick(components) {
|
|
|
15099
15224
|
modalParkedCoordinators.length
|
|
15100
15225
|
);
|
|
15101
15226
|
}
|
|
15227
|
+
} else if (generatingCoordinators.length > 0) {
|
|
15228
|
+
let hasPending = true;
|
|
15229
|
+
if (store) {
|
|
15230
|
+
try {
|
|
15231
|
+
hasPending = store.pendingEventCount(meshId) > 0;
|
|
15232
|
+
} catch {
|
|
15233
|
+
}
|
|
15234
|
+
}
|
|
15235
|
+
if (hasPending) {
|
|
15236
|
+
LOG.info("MeshReconcile", `Reconcile skip \u2192 generating: holding pending event(s) for mesh ${meshId} (${generatingCoordinators.length} coordinator(s) busy; events left queued for the next idle tick)`);
|
|
15237
|
+
recordHeldTerminalEventsToLedger(
|
|
15238
|
+
meshId,
|
|
15239
|
+
drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId ? [localDaemonId] : [],
|
|
15240
|
+
"generating_no_idle_coordinator",
|
|
15241
|
+
generatingCoordinators.length
|
|
15242
|
+
);
|
|
15243
|
+
}
|
|
15102
15244
|
}
|
|
15103
15245
|
continue;
|
|
15104
15246
|
}
|
|
@@ -15112,16 +15254,14 @@ async function runMeshReconcileTick(components) {
|
|
|
15112
15254
|
try {
|
|
15113
15255
|
pendingEvents = drainPendingMeshCoordinatorEvents(
|
|
15114
15256
|
meshId,
|
|
15115
|
-
drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId
|
|
15116
|
-
forceOnly ? { onlyEvents: MESH_FORCE_INJECT_EVENTS } : void 0
|
|
15257
|
+
drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId
|
|
15117
15258
|
);
|
|
15118
15259
|
} catch (e) {
|
|
15119
15260
|
LOG.warn("MeshReconcile", `Drain failed for mesh ${meshId}: ${e?.message || e}`);
|
|
15120
15261
|
continue;
|
|
15121
15262
|
}
|
|
15122
15263
|
if (pendingEvents.length === 0) continue;
|
|
15123
|
-
|
|
15124
|
-
LOG.info("MeshReconcile", `Reconcile ${mode}: ${pendingEvents.length} pending event(s) \u2192 ${targetCoordinators.length} coordinator(s) for mesh ${meshId}`);
|
|
15264
|
+
LOG.info("MeshReconcile", `Reconcile inject \u2192 idle: ${pendingEvents.length} pending event(s) \u2192 ${targetCoordinators.length} coordinator(s) for mesh ${meshId}`);
|
|
15125
15265
|
for (const pending of pendingEvents) {
|
|
15126
15266
|
const wantSession = readNonEmptyString2(pending.targetCoordinatorSessionId);
|
|
15127
15267
|
if (wantSession) {
|
|
@@ -23355,102 +23495,7 @@ function buildMeshLedgerReconciliationEvidence(meshId, replicas) {
|
|
|
23355
23495
|
init_mesh_work_queue();
|
|
23356
23496
|
init_mesh_active_work();
|
|
23357
23497
|
init_mesh_refine_status();
|
|
23358
|
-
|
|
23359
|
-
// src/mesh/mesh-scheduling-runtime.ts
|
|
23360
|
-
init_repo_mesh_types();
|
|
23361
|
-
init_dist();
|
|
23362
|
-
function isReadonly(task) {
|
|
23363
|
-
return task.taskMode === "live_debug_readonly";
|
|
23364
|
-
}
|
|
23365
|
-
function isAssigned(task) {
|
|
23366
|
-
return task.status === "assigned";
|
|
23367
|
-
}
|
|
23368
|
-
function buildMeshSchedulingRuntime(mesh, queue) {
|
|
23369
|
-
const strategy = normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
|
|
23370
|
-
const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
|
|
23371
|
-
const maxReadonlyParallelTasks = Math.max(2, maxParallelTasks * 2);
|
|
23372
|
-
const assignedTasks = (Array.isArray(queue) ? queue : []).filter(isAssigned);
|
|
23373
|
-
const activeWriteAssigned = assignedTasks.filter((t) => !isReadonly(t)).length;
|
|
23374
|
-
const activeReadonlyAssigned = assignedTasks.filter(isReadonly).length;
|
|
23375
|
-
const globalWriteCapReached = activeWriteAssigned >= maxParallelTasks;
|
|
23376
|
-
const globalReadonlyCapReached = activeReadonlyAssigned >= maxReadonlyParallelTasks;
|
|
23377
|
-
const writeAssignedByNode = /* @__PURE__ */ new Map();
|
|
23378
|
-
const assignedByNode = /* @__PURE__ */ new Map();
|
|
23379
|
-
const providerCountByNode = /* @__PURE__ */ new Map();
|
|
23380
|
-
for (const task of assignedTasks) {
|
|
23381
|
-
const nodeId = typeof task.assignedNodeId === "string" ? task.assignedNodeId.trim() : "";
|
|
23382
|
-
if (!nodeId) continue;
|
|
23383
|
-
assignedByNode.set(nodeId, (assignedByNode.get(nodeId) ?? 0) + 1);
|
|
23384
|
-
if (!isReadonly(task)) {
|
|
23385
|
-
writeAssignedByNode.set(nodeId, (writeAssignedByNode.get(nodeId) ?? 0) + 1);
|
|
23386
|
-
}
|
|
23387
|
-
const provider = typeof task.assignedProviderType === "string" ? task.assignedProviderType : "";
|
|
23388
|
-
if (provider) {
|
|
23389
|
-
let byProvider = providerCountByNode.get(nodeId);
|
|
23390
|
-
if (!byProvider) {
|
|
23391
|
-
byProvider = /* @__PURE__ */ new Map();
|
|
23392
|
-
providerCountByNode.set(nodeId, byProvider);
|
|
23393
|
-
}
|
|
23394
|
-
byProvider.set(provider, (byProvider.get(provider) ?? 0) + 1);
|
|
23395
|
-
}
|
|
23396
|
-
}
|
|
23397
|
-
const nodes = [];
|
|
23398
|
-
for (const rawNode of Array.isArray(mesh?.nodes) ? mesh.nodes : []) {
|
|
23399
|
-
const nodeId = normalizeMeshNodeId(rawNode);
|
|
23400
|
-
if (!nodeId) continue;
|
|
23401
|
-
const policy = rawNode?.policy || void 0;
|
|
23402
|
-
const load4 = assignedByNode.get(nodeId) ?? 0;
|
|
23403
|
-
const schedulingPriority = resolveNodeSchedulingPriority(policy);
|
|
23404
|
-
const capReasons = [];
|
|
23405
|
-
if (globalWriteCapReached) capReasons.push("global_max_parallel_tasks_reached");
|
|
23406
|
-
if ((writeAssignedByNode.get(nodeId) ?? 0) > 0) capReasons.push("node_has_active_assignment");
|
|
23407
|
-
let providerRoles;
|
|
23408
|
-
const declaredRoles = Array.isArray(policy?.providerRoles) ? policy.providerRoles : [];
|
|
23409
|
-
if (declaredRoles.length) {
|
|
23410
|
-
const byProvider = providerCountByNode.get(nodeId);
|
|
23411
|
-
providerRoles = [];
|
|
23412
|
-
for (const role of declaredRoles) {
|
|
23413
|
-
if (!role || typeof role !== "object") continue;
|
|
23414
|
-
const providerType = typeof role.providerType === "string" ? role.providerType.trim() : "";
|
|
23415
|
-
if (!providerType) continue;
|
|
23416
|
-
const maxParallel = resolveProviderMaxParallel(policy, providerType);
|
|
23417
|
-
const activeAssigned = byProvider?.get(providerType) ?? 0;
|
|
23418
|
-
const capReached = maxParallel !== void 0 && activeAssigned >= maxParallel;
|
|
23419
|
-
providerRoles.push({
|
|
23420
|
-
providerType,
|
|
23421
|
-
...maxParallel !== void 0 ? { maxParallel } : {},
|
|
23422
|
-
activeAssigned,
|
|
23423
|
-
capReached
|
|
23424
|
-
});
|
|
23425
|
-
if (capReached) capReasons.push(`max_provider_parallel_reached:${providerType}`);
|
|
23426
|
-
}
|
|
23427
|
-
if (!providerRoles.length) providerRoles = void 0;
|
|
23428
|
-
}
|
|
23429
|
-
const maxConcurrentSessions = Number(policy?.maxConcurrentSessions);
|
|
23430
|
-
const hasSessionCap = Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0;
|
|
23431
|
-
nodes.push({
|
|
23432
|
-
nodeId,
|
|
23433
|
-
load: load4,
|
|
23434
|
-
schedulingPriority,
|
|
23435
|
-
...hasSessionCap ? { maxConcurrentSessions: Math.floor(maxConcurrentSessions) } : {},
|
|
23436
|
-
...providerRoles ? { providerRoles } : {},
|
|
23437
|
-
capReached: capReasons.length > 0,
|
|
23438
|
-
capReasons
|
|
23439
|
-
});
|
|
23440
|
-
}
|
|
23441
|
-
return {
|
|
23442
|
-
strategy,
|
|
23443
|
-
maxParallelTasks,
|
|
23444
|
-
maxReadonlyParallelTasks,
|
|
23445
|
-
activeWriteAssigned,
|
|
23446
|
-
activeReadonlyAssigned,
|
|
23447
|
-
globalWriteCapReached,
|
|
23448
|
-
globalReadonlyCapReached,
|
|
23449
|
-
nodes
|
|
23450
|
-
};
|
|
23451
|
-
}
|
|
23452
|
-
|
|
23453
|
-
// src/index.ts
|
|
23498
|
+
init_mesh_scheduling_runtime();
|
|
23454
23499
|
init_mesh_host_ownership();
|
|
23455
23500
|
init_mesh_events();
|
|
23456
23501
|
init_mesh_events_utils();
|
|
@@ -39233,9 +39278,35 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
39233
39278
|
* terminal state. Leaving meshNodeFor pinned would route this session's
|
|
39234
39279
|
* subsequent unrelated turns (e.g. ad-hoc dashboard chats) to the
|
|
39235
39280
|
* coordinator as if they were task completions.
|
|
39281
|
+
*
|
|
39282
|
+
* MESHID-DROP-ON-DETACH (Fix C): a coordinator-LAUNCHED worker session
|
|
39283
|
+
* (launchedByCoordinator) holds its mesh membership (meshNodeFor / meshNodeId /
|
|
39284
|
+
* meshCoordinatorDaemonId) at the SESSION level — set once at launch
|
|
39285
|
+
* (mesh_launch_session / queue auto-launch), independent of any single task.
|
|
39286
|
+
* The original detach wiped meshNodeFor + meshNodeId together with the
|
|
39287
|
+
* task-level meshActiveTaskId, so the FIRST task completion stripped the
|
|
39288
|
+
* membership and EVERY subsequent completion forwarded with meshId absent —
|
|
39289
|
+
* resolveWorkerDelegateRouting fell to mesh_unresolved and the coordinator
|
|
39290
|
+
* rejected the forward "meshId required". For a launched member we therefore
|
|
39291
|
+
* clear ONLY the task-level marker (meshActiveTaskId) and preserve the
|
|
39292
|
+
* session-level membership so its next task's completion still resolves.
|
|
39293
|
+
* A task-less ad-hoc turn on a preserved-membership session is NOT misrouted:
|
|
39294
|
+
* its completion carries no taskId and the session holds no active assignment,
|
|
39295
|
+
* so the forwarder's WARMUPGAP guard skips the dispatch-row flip (it only
|
|
39296
|
+
* injects a benign task-less notification). A NON-launched session (a plain CLI
|
|
39297
|
+
* session adopted by mesh_send_task --direct, launchedByCoordinator falsy)
|
|
39298
|
+
* keeps the original full clear so an ad-hoc session is never left pinned.
|
|
39236
39299
|
*/
|
|
39237
39300
|
detachMeshAssignment() {
|
|
39238
39301
|
if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
|
|
39302
|
+
if (this.settings.launchedByCoordinator === true) {
|
|
39303
|
+
if (!this.settings.meshActiveTaskId) return;
|
|
39304
|
+
const { meshActiveTaskId: meshActiveTaskId2, ...rest2 } = this.settings;
|
|
39305
|
+
void meshActiveTaskId2;
|
|
39306
|
+
this.settings = rest2;
|
|
39307
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
39308
|
+
return;
|
|
39309
|
+
}
|
|
39239
39310
|
const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
|
|
39240
39311
|
void meshNodeFor;
|
|
39241
39312
|
void meshActiveTaskId;
|
|
@@ -49104,6 +49175,9 @@ var meshStatusHandlers = {
|
|
|
49104
49175
|
const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
49105
49176
|
const queue = getQueue2(meshId);
|
|
49106
49177
|
const queueSummary = getMeshQueueStats2(meshId);
|
|
49178
|
+
const { buildMeshSchedulingRuntime: buildMeshSchedulingRuntime2 } = await Promise.resolve().then(() => (init_mesh_scheduling_runtime(), mesh_scheduling_runtime_exports));
|
|
49179
|
+
const schedulingRuntime = buildMeshSchedulingRuntime2(mesh, queue);
|
|
49180
|
+
const schedulingByNode = new Map(schedulingRuntime.nodes.map((n) => [n.nodeId, n]));
|
|
49107
49181
|
const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
49108
49182
|
const ledgerEntries = readLedgerEntries2(meshId, { tail: 20 });
|
|
49109
49183
|
const asyncRefineLedgerEntries = readLedgerEntries2(meshId, { tail: 100 });
|
|
@@ -49218,6 +49292,11 @@ var meshStatusHandlers = {
|
|
|
49218
49292
|
activeSessionDetails: [],
|
|
49219
49293
|
launchReady: false
|
|
49220
49294
|
};
|
|
49295
|
+
const nodeScheduling = schedulingByNode.get(nodeId);
|
|
49296
|
+
if (nodeScheduling) {
|
|
49297
|
+
const { nodeId: _omitNodeId, ...nodeSchedulingRest } = nodeScheduling;
|
|
49298
|
+
status.scheduling = nodeSchedulingRest;
|
|
49299
|
+
}
|
|
49221
49300
|
if (isSelfNode) {
|
|
49222
49301
|
status.connection = {
|
|
49223
49302
|
perspective: "selected_coordinator",
|
|
@@ -49418,6 +49497,19 @@ var meshStatusHandlers = {
|
|
|
49418
49497
|
branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodeStatuses),
|
|
49419
49498
|
...previewFreshness ? { previewFreshness, deployFreshness: previewFreshness } : {},
|
|
49420
49499
|
nodes: nodeStatuses,
|
|
49500
|
+
// Mesh-level scheduling rollup (strategy + global cap consumption). Mirrors
|
|
49501
|
+
// the MCP `mesh_status` tool's `scheduling` block field-for-field so both
|
|
49502
|
+
// surfaces read the same runtime; per-node detail lives on each
|
|
49503
|
+
// nodes[].scheduling above.
|
|
49504
|
+
scheduling: {
|
|
49505
|
+
strategy: schedulingRuntime.strategy,
|
|
49506
|
+
maxParallelTasks: schedulingRuntime.maxParallelTasks,
|
|
49507
|
+
maxReadonlyParallelTasks: schedulingRuntime.maxReadonlyParallelTasks,
|
|
49508
|
+
activeWriteAssigned: schedulingRuntime.activeWriteAssigned,
|
|
49509
|
+
activeReadonlyAssigned: schedulingRuntime.activeReadonlyAssigned,
|
|
49510
|
+
globalWriteCapReached: schedulingRuntime.globalWriteCapReached,
|
|
49511
|
+
globalReadonlyCapReached: schedulingRuntime.globalReadonlyCapReached
|
|
49512
|
+
},
|
|
49421
49513
|
queue: { tasks: queue, summary: queueSummary },
|
|
49422
49514
|
ledger: { entries: ledgerEntries, summary: ledgerSummary },
|
|
49423
49515
|
...missions.length > 0 ? { missions } : {},
|
|
@@ -52362,17 +52454,40 @@ var DaemonCommandRouter = class {
|
|
|
52362
52454
|
* the wider plural-shape scan so a controlbar/modal command targeting a non-primary remote
|
|
52363
52455
|
* session still resolves its owner — the singular readCachedInlineMeshActiveSessions semantics
|
|
52364
52456
|
* other consumers depend on stay untouched.
|
|
52457
|
+
*
|
|
52458
|
+
* CANCEL-STOP-RELAY: the session-id cache scan above only matches when the coordinator's
|
|
52459
|
+
* cached status snapshot already lists the worker's session id in a recognized active-sessions
|
|
52460
|
+
* shape. A worktree-clone worker session whose id form/timing differs from the cached snapshot
|
|
52461
|
+
* (or is simply not yet reflected) misses the scan, so a stop that carries the authoritative
|
|
52462
|
+
* owning nodeId (mesh_queue_cancel knows assignedNodeId) used to silently fail to forward.
|
|
52463
|
+
* `ownerNodeIdHint` adds a deterministic fallback: when the session-id scan misses, resolve the
|
|
52464
|
+
* owner daemonId by matching the node by id (meshNodeIdMatches — same form-tolerant compare the
|
|
52465
|
+
* rest of the router uses, no new raw compare). The same self-loopback guard applies to both
|
|
52466
|
+
* paths, so a coordinator-hosted node is still never force-forwarded to a remote form of itself.
|
|
52365
52467
|
*/
|
|
52366
|
-
resolveRemoteMeshSessionOwnerDaemonId(sessionId) {
|
|
52468
|
+
resolveRemoteMeshSessionOwnerDaemonId(sessionId, ownerNodeIdHint) {
|
|
52367
52469
|
const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
52368
|
-
|
|
52470
|
+
const nodeHint = typeof ownerNodeIdHint === "string" ? ownerNodeIdHint.trim() : "";
|
|
52471
|
+
if (!trimmed && !nodeHint) return void 0;
|
|
52369
52472
|
const selfDaemonId = this.deps.statusInstanceId;
|
|
52370
|
-
|
|
52371
|
-
|
|
52372
|
-
const
|
|
52373
|
-
|
|
52374
|
-
|
|
52375
|
-
|
|
52473
|
+
const candidates = this.collectMeshSessionOwnerCandidateNodes();
|
|
52474
|
+
if (trimmed) {
|
|
52475
|
+
for (const node of candidates) {
|
|
52476
|
+
if (!collectMeshNodeHostedSessionIds(node).has(trimmed)) continue;
|
|
52477
|
+
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
52478
|
+
if (!nodeDaemonId) continue;
|
|
52479
|
+
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
52480
|
+
return nodeDaemonId;
|
|
52481
|
+
}
|
|
52482
|
+
}
|
|
52483
|
+
if (nodeHint) {
|
|
52484
|
+
for (const node of candidates) {
|
|
52485
|
+
if (!meshNodeIdMatches(node, nodeHint)) continue;
|
|
52486
|
+
const nodeDaemonId = readMeshNodeDaemonId(readObjectRecord(node));
|
|
52487
|
+
if (!nodeDaemonId) continue;
|
|
52488
|
+
if (selfDaemonId && daemonIdsEquivalent(nodeDaemonId, selfDaemonId)) return void 0;
|
|
52489
|
+
return nodeDaemonId;
|
|
52490
|
+
}
|
|
52376
52491
|
}
|
|
52377
52492
|
return void 0;
|
|
52378
52493
|
}
|
|
@@ -54506,7 +54621,9 @@ ${hintLines.join("\n")}` : "",
|
|
|
54506
54621
|
const localInstance = this.deps.instanceManager?.getInstance(targetSessionId);
|
|
54507
54622
|
const localRegistry = this.deps.sessionRegistry?.get?.(targetSessionId);
|
|
54508
54623
|
if (!localInstance && !localRegistry) {
|
|
54509
|
-
const
|
|
54624
|
+
const meshContext = readObjectRecord(args?.meshContext);
|
|
54625
|
+
const ownerNodeIdHint = readStringValue(meshContext.nodeId);
|
|
54626
|
+
const ownerDaemonId = this.resolveRemoteMeshSessionOwnerDaemonId(targetSessionId, ownerNodeIdHint);
|
|
54510
54627
|
if (ownerDaemonId) {
|
|
54511
54628
|
LOG.info("Mesh", `[Mesh] Forwarding session-scoped '${cmd}' for remote worker session ${targetSessionId.split("_")[0]} \u2192 daemon ${ownerDaemonId.slice(0, 12)}`);
|
|
54512
54629
|
const forwarded = await this.deps.dispatchMeshCommand(ownerDaemonId, cmd, {
|