@adhdev/daemon-core 0.9.77-rc.30 → 0.9.77-rc.32
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 +60 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -9
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +3 -0
- package/package.json +1 -1
- package/src/commands/router.ts +15 -0
- package/src/mesh/mesh-events.ts +37 -7
- package/src/mesh/mesh-work-queue.ts +20 -5
|
@@ -6,6 +6,8 @@ export interface MeshWorkQueueEntry {
|
|
|
6
6
|
status: MeshTaskStatus;
|
|
7
7
|
/** If specified, only this node can claim the task (used by legacy mesh_send_task) */
|
|
8
8
|
targetNodeId?: string;
|
|
9
|
+
/** If specified, only this runtime session can claim the task */
|
|
10
|
+
targetSessionId?: string;
|
|
9
11
|
/** The node that actually claimed and is executing the task */
|
|
10
12
|
assignedNodeId?: string;
|
|
11
13
|
/** The session currently executing the task */
|
|
@@ -18,6 +20,7 @@ export interface MeshWorkQueueEntry {
|
|
|
18
20
|
*/
|
|
19
21
|
export declare function enqueueTask(meshId: string, message: string, opts?: {
|
|
20
22
|
targetNodeId?: string;
|
|
23
|
+
targetSessionId?: string;
|
|
21
24
|
}): MeshWorkQueueEntry;
|
|
22
25
|
/**
|
|
23
26
|
* Get all tasks in the queue, optionally filtered by status.
|
package/package.json
CHANGED
package/src/commands/router.ts
CHANGED
|
@@ -1331,6 +1331,21 @@ export class DaemonCommandRouter {
|
|
|
1331
1331
|
}
|
|
1332
1332
|
}
|
|
1333
1333
|
|
|
1334
|
+
case 'get_mesh_queue': {
|
|
1335
|
+
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
1336
|
+
if (!meshId) return { success: false, error: 'meshId required' };
|
|
1337
|
+
try {
|
|
1338
|
+
const { getQueue } = await import('../mesh/mesh-work-queue.js');
|
|
1339
|
+
const status = Array.isArray(args?.status)
|
|
1340
|
+
? args.status.map((s: any) => typeof s === 'string' ? s.trim() : '').filter(Boolean)
|
|
1341
|
+
: undefined;
|
|
1342
|
+
const queue = getQueue(meshId, { status: status as any });
|
|
1343
|
+
return { success: true, queue };
|
|
1344
|
+
} catch (e: any) {
|
|
1345
|
+
return { success: false, error: e.message };
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1334
1349
|
case 'add_mesh_node': {
|
|
1335
1350
|
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
1336
1351
|
const workspace = typeof args?.workspace === 'string' ? args.workspace.trim() : '';
|
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -46,7 +46,15 @@ function readNonEmptyString(value: unknown): string {
|
|
|
46
46
|
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function resolveEventSessionId(event: Record<string, unknown>, fallback?: unknown): string {
|
|
50
|
+
return readNonEmptyString(event.targetSessionId)
|
|
51
|
+
|| readNonEmptyString(event.sessionId)
|
|
52
|
+
|| readNonEmptyString(event.instanceId)
|
|
53
|
+
|| readNonEmptyString(fallback);
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
const MESH_COORDINATOR_EVENTS = new Set([
|
|
57
|
+
'agent:generating_started',
|
|
50
58
|
'agent:generating_completed',
|
|
51
59
|
'agent:waiting_approval',
|
|
52
60
|
'agent:stopped',
|
|
@@ -229,7 +237,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
229
237
|
}) {
|
|
230
238
|
// ── Task Queue & Ledger ──
|
|
231
239
|
if (args.event === 'agent:generating_completed') {
|
|
232
|
-
const sessionId =
|
|
240
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
233
241
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
234
242
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
235
243
|
|
|
@@ -243,9 +251,31 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
243
251
|
}
|
|
244
252
|
}
|
|
245
253
|
} else if (args.event === 'agent:ready') {
|
|
246
|
-
const sessionId =
|
|
254
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
247
255
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
248
256
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
257
|
+
const completedTask = sessionId
|
|
258
|
+
? updateSessionTaskStatus(args.meshId, sessionId, 'completed')
|
|
259
|
+
: null;
|
|
260
|
+
if (completedTask) {
|
|
261
|
+
try {
|
|
262
|
+
appendLedgerEntry(args.meshId, {
|
|
263
|
+
kind: 'task_completed',
|
|
264
|
+
nodeId: nodeId || undefined,
|
|
265
|
+
sessionId,
|
|
266
|
+
providerType: providerType || undefined,
|
|
267
|
+
payload: {
|
|
268
|
+
event: args.event,
|
|
269
|
+
nodeLabel: args.nodeLabel,
|
|
270
|
+
taskId: completedTask.id,
|
|
271
|
+
completedViaReady: true,
|
|
272
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
} catch (e: any) {
|
|
276
|
+
LOG.warn('MeshLedger', `Failed to record task_completed from ready: ${e?.message || e}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
249
279
|
|
|
250
280
|
if (sessionId && nodeId && providerType) {
|
|
251
281
|
remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
|
|
@@ -257,13 +287,13 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
257
287
|
}, 500);
|
|
258
288
|
}
|
|
259
289
|
} else if (args.event === 'agent:generating_started') {
|
|
260
|
-
const sessionId =
|
|
290
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
261
291
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
262
292
|
if (sessionId && nodeId) {
|
|
263
293
|
remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
264
294
|
}
|
|
265
295
|
} else if (args.event === 'agent:stopped') {
|
|
266
|
-
const sessionId =
|
|
296
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
267
297
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
268
298
|
if (sessionId && nodeId) {
|
|
269
299
|
remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
@@ -279,7 +309,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
279
309
|
appendLedgerEntry(args.meshId, {
|
|
280
310
|
kind: ledgerKind,
|
|
281
311
|
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined,
|
|
282
|
-
sessionId:
|
|
312
|
+
sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || undefined,
|
|
283
313
|
providerType: readNonEmptyString(args.metadataEvent.providerType) || undefined,
|
|
284
314
|
payload: {
|
|
285
315
|
event: args.event,
|
|
@@ -301,7 +331,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
301
331
|
const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
|
|
302
332
|
|
|
303
333
|
recoveryContext = getSessionRecoveryContext(args.meshId, {
|
|
304
|
-
sessionId:
|
|
334
|
+
sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || undefined,
|
|
305
335
|
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined,
|
|
306
336
|
maxRetries,
|
|
307
337
|
});
|
|
@@ -414,7 +444,7 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
414
444
|
nodeLabel,
|
|
415
445
|
event: eventName,
|
|
416
446
|
metadataEvent: {
|
|
417
|
-
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
|
|
447
|
+
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
|
|
418
448
|
providerType: readNonEmptyString(payload.providerType),
|
|
419
449
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
420
450
|
},
|
|
@@ -12,6 +12,8 @@ export interface MeshWorkQueueEntry {
|
|
|
12
12
|
status: MeshTaskStatus;
|
|
13
13
|
/** If specified, only this node can claim the task (used by legacy mesh_send_task) */
|
|
14
14
|
targetNodeId?: string;
|
|
15
|
+
/** If specified, only this runtime session can claim the task */
|
|
16
|
+
targetSessionId?: string;
|
|
15
17
|
/** The node that actually claimed and is executing the task */
|
|
16
18
|
assignedNodeId?: string;
|
|
17
19
|
/** The session currently executing the task */
|
|
@@ -47,7 +49,7 @@ function writeQueue(meshId: string, queue: MeshWorkQueueEntry[]): void {
|
|
|
47
49
|
export function enqueueTask(
|
|
48
50
|
meshId: string,
|
|
49
51
|
message: string,
|
|
50
|
-
opts?: { targetNodeId?: string }
|
|
52
|
+
opts?: { targetNodeId?: string; targetSessionId?: string }
|
|
51
53
|
): MeshWorkQueueEntry {
|
|
52
54
|
const queue = readQueue(meshId);
|
|
53
55
|
const entry: MeshWorkQueueEntry = {
|
|
@@ -56,6 +58,7 @@ export function enqueueTask(
|
|
|
56
58
|
message,
|
|
57
59
|
status: 'pending',
|
|
58
60
|
targetNodeId: opts?.targetNodeId,
|
|
61
|
+
targetSessionId: opts?.targetSessionId,
|
|
59
62
|
createdAt: new Date().toISOString(),
|
|
60
63
|
updatedAt: new Date().toISOString(),
|
|
61
64
|
};
|
|
@@ -81,13 +84,25 @@ export function getQueue(meshId: string, opts?: { status?: MeshTaskStatus[] }):
|
|
|
81
84
|
*/
|
|
82
85
|
export function claimNextTask(meshId: string, nodeId: string, sessionId: string): MeshWorkQueueEntry | null {
|
|
83
86
|
const queue = readQueue(meshId);
|
|
87
|
+
|
|
88
|
+
// A worker must finish or fail its current queued assignment before it can
|
|
89
|
+
// claim another one. maxParallelTasks limits total mesh concurrency; it is
|
|
90
|
+
// not permission for one node/session to accumulate multiple assigned items.
|
|
91
|
+
const hasActiveAssignment = queue.some(q => q.status === 'assigned' && (
|
|
92
|
+
q.assignedSessionId === sessionId || q.assignedNodeId === nodeId
|
|
93
|
+
));
|
|
94
|
+
if (hasActiveAssignment) return null;
|
|
84
95
|
|
|
85
96
|
// Find highest priority task:
|
|
86
|
-
// 1. Pending tasks explicitly targeted at this
|
|
87
|
-
// 2. Pending tasks
|
|
88
|
-
|
|
97
|
+
// 1. Pending tasks explicitly targeted at this runtime session
|
|
98
|
+
// 2. Pending tasks explicitly targeted at this node (but not another session)
|
|
99
|
+
// 3. Pending tasks with no target node/session
|
|
100
|
+
let targetIdx = queue.findIndex(q => q.status === 'pending' && q.targetSessionId === sessionId);
|
|
101
|
+
if (targetIdx === -1) {
|
|
102
|
+
targetIdx = queue.findIndex(q => q.status === 'pending' && q.targetNodeId === nodeId && !q.targetSessionId);
|
|
103
|
+
}
|
|
89
104
|
if (targetIdx === -1) {
|
|
90
|
-
targetIdx = queue.findIndex(q => q.status === 'pending' && !q.targetNodeId);
|
|
105
|
+
targetIdx = queue.findIndex(q => q.status === 'pending' && !q.targetNodeId && !q.targetSessionId);
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
if (targetIdx === -1) return null;
|