@adhdev/daemon-core 0.9.82-rc.287 → 0.9.82-rc.288
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +30 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +30 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/mesh/mesh-events-coordinator.ts +83 -8
- package/src/mesh/mesh-routing.ts +5 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.288",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.288",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -12,7 +12,7 @@ import { createSessionDelivery, markSessionDeliveriesTerminal, updateSessionDeli
|
|
|
12
12
|
import { MeshRuntimeStore } from './mesh-runtime-store.js';
|
|
13
13
|
import { queuePendingMeshCoordinatorEvent, drainPendingMeshCoordinatorEvents } from './mesh-events-pending.js';
|
|
14
14
|
import type { PendingMeshCoordinatorEvent } from './mesh-events-pending.js';
|
|
15
|
-
import { resolveWorkerDelegateRouting, recordUnroutableDelegateEvent } from './mesh-routing.js';
|
|
15
|
+
import { resolveWorkerDelegateRouting, recordUnroutableDelegateEvent, isUnroutableDelegateRejection } from './mesh-routing.js';
|
|
16
16
|
import { resolveDelegatedWorkerAutoApprove } from '../repo-mesh-types.js';
|
|
17
17
|
import {
|
|
18
18
|
findRecentTerminalLedgerEvidence,
|
|
@@ -1446,11 +1446,16 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
1446
1446
|
if (!isMeshCoordinatorEvent(eventName)) {
|
|
1447
1447
|
return { success: false, error: 'unsupported mesh event' };
|
|
1448
1448
|
}
|
|
1449
|
-
const meshId = readNonEmptyString(payload.meshId);
|
|
1450
|
-
if (!meshId) return { success: false, error: 'meshId required' };
|
|
1451
|
-
|
|
1452
1449
|
const nodeId = readNonEmptyString(payload.nodeId);
|
|
1453
1450
|
const workspace = readNonEmptyString(payload.workspace);
|
|
1451
|
+
|
|
1452
|
+
// The fallback worker-forward path (forwardUnresolvedDelegateEvent) cannot resolve a
|
|
1453
|
+
// mesh id locally on the remote worker, so it forwards the event with workspace only.
|
|
1454
|
+
// The coordinator hosting the mesh CAN resolve it: recover the mesh id by workspace
|
|
1455
|
+
// when the payload doesn't carry one.
|
|
1456
|
+
const meshId = readNonEmptyString(payload.meshId)
|
|
1457
|
+
|| (workspace ? readNonEmptyString(getCachedMeshByWorkspace(workspace)?.id) : '');
|
|
1458
|
+
if (!meshId) return { success: false, error: 'meshId required' };
|
|
1454
1459
|
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : 'Remote agent';
|
|
1455
1460
|
const relayModalMessage = readNonEmptyString(payload.modalMessage);
|
|
1456
1461
|
const relayModalButtons = Array.isArray(payload.modalButtons)
|
|
@@ -1493,6 +1498,67 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
1493
1498
|
});
|
|
1494
1499
|
}
|
|
1495
1500
|
|
|
1501
|
+
// ---------------------------------------------------------------------------
|
|
1502
|
+
// Worker-side fallback forward for unresolved-mesh delegates.
|
|
1503
|
+
//
|
|
1504
|
+
// A REMOTE worker daemon that is being P2P-remote-controlled by a coordinator is
|
|
1505
|
+
// NOT a member of the coordinator's mesh — it has no local mesh record. So when its
|
|
1506
|
+
// completion event reaches the forwarder, resolveWorkerDelegateRouting() resolves the
|
|
1507
|
+
// coordinator anchor (meshCoordinatorDaemonId) from the worker envelope but cannot
|
|
1508
|
+
// resolve the mesh id (neither meshNodeFor nor a workspace→mesh lookup yields one) and
|
|
1509
|
+
// returns isDelegate=false / mesh_unresolved. Before this fallback the event was dropped
|
|
1510
|
+
// (delivery_unroutable) and only recovered later when the coordinator happened to pull
|
|
1511
|
+
// the worker's queue — which it can't, because the worker never queued an unroutable
|
|
1512
|
+
// event. Live symptom: `WARN [MeshEvents] delivery_unroutable: ... mesh unresolved`.
|
|
1513
|
+
//
|
|
1514
|
+
// The fix: the routing object still carries coordinatorDaemonId. Forward the raw event
|
|
1515
|
+
// straight to that coordinator daemon over P2P (mesh_forward_event). The coordinator
|
|
1516
|
+
// hosts the mesh, so it recovers the mesh id by workspace in handleMeshForwardEvent and
|
|
1517
|
+
// injects/queues it normally. meshId is intentionally omitted from the payload (the
|
|
1518
|
+
// worker has none); workspace is the routing anchor the coordinator resolves from.
|
|
1519
|
+
//
|
|
1520
|
+
// No loop / no double-delivery:
|
|
1521
|
+
// - This only fires on the WORKER (the coordinator-own session is rejected by the
|
|
1522
|
+
// resolver before reaching here), and the coordinator merely injects — it does not
|
|
1523
|
+
// re-enter this forwarder for the relayed event.
|
|
1524
|
+
// - It fires only when the normal queue path did NOT run (isDelegate=false), so the
|
|
1525
|
+
// event is never both queued locally and forwarded.
|
|
1526
|
+
//
|
|
1527
|
+
// Returns true when the event was handed off to the coordinator daemon (so the caller
|
|
1528
|
+
// skips the delivery_unroutable diagnostic); false when no fallback was possible.
|
|
1529
|
+
function forwardUnresolvedDelegateEvent(
|
|
1530
|
+
components: DaemonComponents,
|
|
1531
|
+
routing: ReturnType<typeof resolveWorkerDelegateRouting>,
|
|
1532
|
+
event: Record<string, unknown>,
|
|
1533
|
+
): boolean {
|
|
1534
|
+
const coordinatorDaemonId = readNonEmptyString(routing.coordinatorDaemonId);
|
|
1535
|
+
if (!coordinatorDaemonId) return false;
|
|
1536
|
+
if (!components.dispatchMeshCommand) return false;
|
|
1537
|
+
|
|
1538
|
+
const eventName = readNonEmptyString(event.event);
|
|
1539
|
+
if (!eventName) return false;
|
|
1540
|
+
|
|
1541
|
+
// Flat payload mirroring buildForwardPayloadFromPending / what handleMeshForwardEvent
|
|
1542
|
+
// reads. meshId is omitted on purpose — the worker can't resolve it; the coordinator
|
|
1543
|
+
// recovers it from workspace. nodeId/workspace come from the worker envelope so the
|
|
1544
|
+
// coordinator can name and locate the node.
|
|
1545
|
+
const payload: Record<string, unknown> = {
|
|
1546
|
+
...event,
|
|
1547
|
+
event: eventName,
|
|
1548
|
+
nodeId: readNonEmptyString(routing.nodeId) || readNonEmptyString(event.meshNodeId) || undefined,
|
|
1549
|
+
workspace: readNonEmptyString(routing.workspace) || readNonEmptyString(event.workspace) || undefined,
|
|
1550
|
+
};
|
|
1551
|
+
|
|
1552
|
+
Promise.resolve(components.dispatchMeshCommand(coordinatorDaemonId, 'mesh_forward_event', payload))
|
|
1553
|
+
.catch((e: any) => {
|
|
1554
|
+
// The coordinator may be momentarily unreachable; the diagnostic was already
|
|
1555
|
+
// skipped, so leave a trace here so an operator can see the relay attempt failed.
|
|
1556
|
+
LOG.warn('MeshEvents', `Fallback forward of ${eventName} to coordinator ${coordinatorDaemonId} failed: ${e?.message || e}`);
|
|
1557
|
+
});
|
|
1558
|
+
LOG.info('MeshEvents', `Fallback-forwarded ${eventName} for unresolved-mesh worker at ${routing.workspace || '(no workspace)'} to coordinator daemon ${coordinatorDaemonId}`);
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1496
1562
|
export function setupMeshEventForwarding(components: DaemonComponents) {
|
|
1497
1563
|
components.instanceManager.onEvent((event) => {
|
|
1498
1564
|
// --- Coordinator idle auto-flush (fast path) ---
|
|
@@ -1572,10 +1638,19 @@ export function setupMeshEventForwarding(components: DaemonComponents) {
|
|
|
1572
1638
|
getMeshByWorkspace: (workspace) => getCachedMeshByWorkspace(workspace),
|
|
1573
1639
|
});
|
|
1574
1640
|
if (!routing.isDelegate) {
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1577
|
-
//
|
|
1578
|
-
//
|
|
1641
|
+
// Fallback: a REMOTE worker that isn't a member of the coordinator's mesh can't
|
|
1642
|
+
// resolve a mesh id locally (mesh_unresolved), but it still carries the coordinator
|
|
1643
|
+
// daemon anchor. Forward the event straight to that coordinator over P2P instead of
|
|
1644
|
+
// dropping it — the coordinator hosts the mesh and recovers the id by workspace.
|
|
1645
|
+
if (isUnroutableDelegateRejection(routing)
|
|
1646
|
+
&& forwardUnresolvedDelegateEvent(components, routing, event)) {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
// R4: a worker that presented a valid envelope but resolved to no mesh (and could
|
|
1650
|
+
// not be fallback-forwarded — e.g. no coordinator anchor) used to be dropped
|
|
1651
|
+
// silently. Leave a fail-loud diagnostic so the missing completion is traceable.
|
|
1652
|
+
// Benign non-delegate rejections (not_cli / no_workspace / etc.) are no-ops inside
|
|
1653
|
+
// recordUnroutableDelegateEvent.
|
|
1579
1654
|
recordUnroutableDelegateEvent(routing, event.event);
|
|
1580
1655
|
return;
|
|
1581
1656
|
}
|
package/src/mesh/mesh-routing.ts
CHANGED
|
@@ -82,10 +82,13 @@ export function resolveWorkerDelegateRouting(
|
|
|
82
82
|
const sessionId = readNonEmptyString(instanceId);
|
|
83
83
|
let workspace = '';
|
|
84
84
|
let coordinatorDaemonId = '';
|
|
85
|
+
// Runtime node-id stamp, surfaced even on rejection so the unresolved-mesh fallback
|
|
86
|
+
// forward can name the worker node for the coordinator.
|
|
87
|
+
let runtimeNodeId = '';
|
|
85
88
|
const reject = (rejectionReason: WorkerDelegateRejectionReason): WorkerDelegateRouting => ({
|
|
86
89
|
isDelegate: false,
|
|
87
90
|
meshId: '',
|
|
88
|
-
nodeId:
|
|
91
|
+
nodeId: runtimeNodeId,
|
|
89
92
|
nodeLabel: '',
|
|
90
93
|
coordinatorDaemonId,
|
|
91
94
|
workspace,
|
|
@@ -102,6 +105,7 @@ export function resolveWorkerDelegateRouting(
|
|
|
102
105
|
|
|
103
106
|
const settings = readSettings(state);
|
|
104
107
|
coordinatorDaemonId = readNonEmptyString(settings.meshCoordinatorDaemonId);
|
|
108
|
+
runtimeNodeId = readNonEmptyString(settings.meshNodeId);
|
|
105
109
|
|
|
106
110
|
// A coordinator session (meshCoordinatorFor set) is only treated as a worker delegate
|
|
107
111
|
// when it is itself the target of an active direct dispatch — otherwise its own events
|
|
@@ -138,7 +142,6 @@ export function resolveWorkerDelegateRouting(
|
|
|
138
142
|
if (!meshId) return reject('mesh_unresolved');
|
|
139
143
|
|
|
140
144
|
const targetNode = mesh?.nodes?.find((n: any) => n.workspace === workspace);
|
|
141
|
-
const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
|
|
142
145
|
const nodeId = readNonEmptyString(targetNode?.id) || runtimeNodeId;
|
|
143
146
|
const nodeLabel = targetNode
|
|
144
147
|
? `Node '${targetNode.id}'`
|