@adhdev/daemon-core 0.9.77-rc.31 → 0.9.77-rc.33
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 +58 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/commands/router.ts +15 -0
- package/src/mesh/mesh-events.ts +44 -10
- package/src/mesh/mesh-work-queue.ts +8 -0
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',
|
|
@@ -145,13 +153,17 @@ export function triggerMeshQueue(components: DaemonComponents, meshId: string) {
|
|
|
145
153
|
const settings = state.settings as Record<string, unknown> || {};
|
|
146
154
|
|
|
147
155
|
const instMeshId = readNonEmptyString(settings.meshNodeFor);
|
|
148
|
-
if (instMeshId !== meshId
|
|
156
|
+
if (instMeshId !== meshId) continue;
|
|
149
157
|
|
|
150
158
|
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
151
159
|
if (!nodeId) continue;
|
|
152
160
|
|
|
153
|
-
//
|
|
154
|
-
|
|
161
|
+
// Only genuinely idle live sessions can pull work. Restored/stopped
|
|
162
|
+
// records are kept for transcript/recovery visibility, but assigning
|
|
163
|
+
// queue items to them strands tasks in assigned/pending without chat.
|
|
164
|
+
const status = readNonEmptyString(state.status).toLowerCase();
|
|
165
|
+
if (['stopped', 'failed', 'terminated', 'exited', 'closed'].includes(status)) continue;
|
|
166
|
+
if (status !== 'idle' && state.activeChat?.status !== 'waiting_input') continue;
|
|
155
167
|
|
|
156
168
|
const sessionId = state.instanceId;
|
|
157
169
|
const providerType = state.type || readNonEmptyString(settings.providerType);
|
|
@@ -229,7 +241,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
229
241
|
}) {
|
|
230
242
|
// ── Task Queue & Ledger ──
|
|
231
243
|
if (args.event === 'agent:generating_completed') {
|
|
232
|
-
const sessionId =
|
|
244
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
233
245
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
234
246
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
235
247
|
|
|
@@ -243,9 +255,31 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
243
255
|
}
|
|
244
256
|
}
|
|
245
257
|
} else if (args.event === 'agent:ready') {
|
|
246
|
-
const sessionId =
|
|
258
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
247
259
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
248
260
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
261
|
+
const completedTask = sessionId
|
|
262
|
+
? updateSessionTaskStatus(args.meshId, sessionId, 'completed')
|
|
263
|
+
: null;
|
|
264
|
+
if (completedTask) {
|
|
265
|
+
try {
|
|
266
|
+
appendLedgerEntry(args.meshId, {
|
|
267
|
+
kind: 'task_completed',
|
|
268
|
+
nodeId: nodeId || undefined,
|
|
269
|
+
sessionId,
|
|
270
|
+
providerType: providerType || undefined,
|
|
271
|
+
payload: {
|
|
272
|
+
event: args.event,
|
|
273
|
+
nodeLabel: args.nodeLabel,
|
|
274
|
+
taskId: completedTask.id,
|
|
275
|
+
completedViaReady: true,
|
|
276
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
} catch (e: any) {
|
|
280
|
+
LOG.warn('MeshLedger', `Failed to record task_completed from ready: ${e?.message || e}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
249
283
|
|
|
250
284
|
if (sessionId && nodeId && providerType) {
|
|
251
285
|
remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
|
|
@@ -257,13 +291,13 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
257
291
|
}, 500);
|
|
258
292
|
}
|
|
259
293
|
} else if (args.event === 'agent:generating_started') {
|
|
260
|
-
const sessionId =
|
|
294
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
261
295
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
262
296
|
if (sessionId && nodeId) {
|
|
263
297
|
remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
264
298
|
}
|
|
265
299
|
} else if (args.event === 'agent:stopped') {
|
|
266
|
-
const sessionId =
|
|
300
|
+
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
267
301
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
268
302
|
if (sessionId && nodeId) {
|
|
269
303
|
remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
@@ -279,7 +313,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
279
313
|
appendLedgerEntry(args.meshId, {
|
|
280
314
|
kind: ledgerKind,
|
|
281
315
|
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined,
|
|
282
|
-
sessionId:
|
|
316
|
+
sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || undefined,
|
|
283
317
|
providerType: readNonEmptyString(args.metadataEvent.providerType) || undefined,
|
|
284
318
|
payload: {
|
|
285
319
|
event: args.event,
|
|
@@ -301,7 +335,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
301
335
|
const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
|
|
302
336
|
|
|
303
337
|
recoveryContext = getSessionRecoveryContext(args.meshId, {
|
|
304
|
-
sessionId:
|
|
338
|
+
sessionId: resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || undefined,
|
|
305
339
|
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined,
|
|
306
340
|
maxRetries,
|
|
307
341
|
});
|
|
@@ -414,7 +448,7 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
414
448
|
nodeLabel,
|
|
415
449
|
event: eventName,
|
|
416
450
|
metadataEvent: {
|
|
417
|
-
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
|
|
451
|
+
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
|
|
418
452
|
providerType: readNonEmptyString(payload.providerType),
|
|
419
453
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
420
454
|
},
|
|
@@ -84,6 +84,14 @@ export function getQueue(meshId: string, opts?: { status?: MeshTaskStatus[] }):
|
|
|
84
84
|
*/
|
|
85
85
|
export function claimNextTask(meshId: string, nodeId: string, sessionId: string): MeshWorkQueueEntry | null {
|
|
86
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;
|
|
87
95
|
|
|
88
96
|
// Find highest priority task:
|
|
89
97
|
// 1. Pending tasks explicitly targeted at this runtime session
|