@adhdev/daemon-core 0.9.77-rc.21 → 0.9.77-rc.23

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.
@@ -8,17 +8,12 @@ export interface PendingMeshCoordinatorEvent {
8
8
  }
9
9
  /** Drain and return all pending coordinator events, clearing the queue. */
10
10
  export declare function drainPendingMeshCoordinatorEvents(): PendingMeshCoordinatorEvent[];
11
- export declare function tryAssignQueueTask(components: {
12
- cliManager: any;
13
- }, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
11
+ export declare function tryAssignQueueTask(components: DaemonComponents, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
14
12
  /**
15
13
  * Triggers a queue check for all nodes in the mesh.
16
14
  * Called when a new task is enqueued, in case nodes are already idle.
17
15
  */
18
- export declare function triggerMeshQueue(components: {
19
- instanceManager: any;
20
- cliManager: any;
21
- }, meshId: string): void;
16
+ export declare function triggerMeshQueue(components: DaemonComponents, meshId: string): void;
22
17
  export declare function handleMeshForwardEvent(components: DaemonComponents, payload: Record<string, unknown>): {
23
18
  success: boolean;
24
19
  forwarded: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.77-rc.21",
3
+ "version": "0.9.77-rc.23",
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",
@@ -83,6 +83,9 @@ export interface DaemonInitConfig {
83
83
 
84
84
  /** Fired before send_chat is dispatched — used for turn snapshot hooks */
85
85
  onBeforeSendChat?: (params: { workspace: string; sessionId: string }) => void;
86
+
87
+ /** Relays a command to a remote mesh node daemon */
88
+ dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
86
89
  }
87
90
 
88
91
  // ─── Result ───
@@ -100,6 +103,7 @@ export interface DaemonComponents {
100
103
  sessionRegistry: SessionRegistry;
101
104
  detectedIdes: { value: IDEInfo[] };
102
105
  refreshProviderAvailability: (providerType?: string) => Promise<void>;
106
+ dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
103
107
  }
104
108
 
105
109
  export interface DaemonDevSupportOptions {
@@ -331,6 +335,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
331
335
  sessionRegistry,
332
336
  detectedIdes: detectedIdesRef,
333
337
  refreshProviderAvailability,
338
+ dispatchMeshCommand: config.dispatchMeshCommand,
334
339
  };
335
340
 
336
341
  // 11. Setup Mesh Event Forwarding
@@ -3,7 +3,20 @@ import { getMesh, getMeshByRepo } from '../config/mesh-config.js';
3
3
  import { LOG } from '../logging/logger.js';
4
4
  import { appendLedgerEntry, getSessionRecoveryContext } from './mesh-ledger.js';
5
5
  import type { MeshLedgerKind, SessionRecoveryContext } from './mesh-ledger.js';
6
- import { claimNextTask, updateSessionTaskStatus, enqueueTask } from './mesh-work-queue.js';
6
+ import { claimNextTask, updateSessionTaskStatus, enqueueTask, updateTaskStatus } from './mesh-work-queue.js';
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Remote Node Idle Session Tracking
10
+ // ---------------------------------------------------------------------------
11
+ // Tracks remote sessions that emitted 'agent:ready' so triggerMeshQueue
12
+ // can assign tasks to them.
13
+ // ---------------------------------------------------------------------------
14
+ interface RemoteIdleSession {
15
+ nodeId: string;
16
+ sessionId: string;
17
+ providerType: string;
18
+ }
19
+ const remoteIdleSessions = new Map<string, RemoteIdleSession>(); // key: `${nodeId}:${sessionId}`
7
20
 
8
21
  // ---------------------------------------------------------------------------
9
22
  // MCP coordinator pending-event queue
@@ -62,7 +75,7 @@ function formatCompletionMetadata(event: Record<string, unknown>): string {
62
75
  }
63
76
 
64
77
  export function tryAssignQueueTask(
65
- components: { cliManager: any },
78
+ components: DaemonComponents,
66
79
  meshId: string,
67
80
  nodeId: string,
68
81
  sessionId: string,
@@ -74,14 +87,37 @@ export function tryAssignQueueTask(
74
87
  }
75
88
 
76
89
  LOG.info('MeshQueue', `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
90
+
91
+ // Check if the node is remote
92
+ const mesh = getMesh(meshId);
93
+ const node = mesh?.nodes.find((n: any) => n.id === nodeId);
77
94
 
95
+ // If the node is explicitly remote and we have a dispatch mechanism, route via P2P
96
+ if (node?.daemonId && components.dispatchMeshCommand) {
97
+ const isLocalNode = components.cliManager.adapters.has(sessionId);
98
+ if (!isLocalNode) {
99
+ components.dispatchMeshCommand(node.daemonId, 'agent_command', {
100
+ targetSessionId: sessionId,
101
+ cliType: providerType,
102
+ action: 'send_chat',
103
+ message: task.message,
104
+ }).catch((e: any) => {
105
+ LOG.error('MeshQueue', `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
106
+ updateTaskStatus(meshId, task.id, 'failed');
107
+ });
108
+ return true;
109
+ }
110
+ }
111
+
112
+ // Local routing
78
113
  components.cliManager.handleCliCommand('agent_command', {
79
114
  targetSessionId: sessionId,
80
115
  cliType: providerType,
81
116
  action: 'send_chat',
82
117
  message: task.message,
83
118
  }).catch((e: any) => {
84
- LOG.error('MeshQueue', `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
119
+ LOG.error('MeshQueue', `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
120
+ updateTaskStatus(meshId, task.id, 'failed');
85
121
  });
86
122
 
87
123
  return true;
@@ -91,7 +127,7 @@ export function tryAssignQueueTask(
91
127
  * Triggers a queue check for all nodes in the mesh.
92
128
  * Called when a new task is enqueued, in case nodes are already idle.
93
129
  */
94
- export function triggerMeshQueue(components: { instanceManager: any; cliManager: any }, meshId: string) {
130
+ export function triggerMeshQueue(components: DaemonComponents, meshId: string) {
95
131
  const mesh = getMesh(meshId);
96
132
  if (!mesh) return;
97
133
 
@@ -118,6 +154,18 @@ export function triggerMeshQueue(components: { instanceManager: any; cliManager:
118
154
  tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType);
119
155
  }
120
156
  }
157
+
158
+ // Also check known idle remote sessions
159
+ for (const [key, idle] of remoteIdleSessions.entries()) {
160
+ // Find if this node is in the same mesh
161
+ const node = mesh.nodes.find((n: any) => n.id === idle.nodeId);
162
+ if (node) {
163
+ const assigned = tryAssignQueueTask(components, meshId, idle.nodeId, idle.sessionId, idle.providerType);
164
+ if (assigned) {
165
+ remoteIdleSessions.delete(key);
166
+ }
167
+ }
168
+ }
121
169
  }
122
170
 
123
171
  function buildMeshSystemMessage(args: {
@@ -193,12 +241,26 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
193
241
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
194
242
 
195
243
  if (sessionId && nodeId && providerType) {
244
+ remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
196
245
  setTimeout(() => {
197
- tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
246
+ const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
247
+ if (assigned) {
248
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
249
+ }
198
250
  }, 500);
199
251
  }
252
+ } else if (args.event === 'agent:generating_started') {
253
+ const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
254
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
255
+ if (sessionId && nodeId) {
256
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
257
+ }
200
258
  } else if (args.event === 'agent:stopped') {
201
259
  const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
260
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
261
+ if (sessionId && nodeId) {
262
+ remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
263
+ }
202
264
  if (sessionId) {
203
265
  updateSessionTaskStatus(args.meshId, sessionId, 'failed');
204
266
  }