@adhdev/daemon-core 0.9.77-rc.33 → 0.9.77-rc.35
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/cli-adapters/provider-cli-adapter.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +104 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +102 -9
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +26 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +14 -3
- package/src/commands/router.ts +42 -1
- package/src/index.ts +1 -1
- package/src/mesh/mesh-events.ts +8 -1
- package/src/mesh/mesh-work-queue.ts +69 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed';
|
|
1
|
+
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
|
|
2
2
|
export interface MeshWorkQueueEntry {
|
|
3
3
|
id: string;
|
|
4
4
|
meshId: string;
|
|
@@ -12,6 +12,13 @@ export interface MeshWorkQueueEntry {
|
|
|
12
12
|
assignedNodeId?: string;
|
|
13
13
|
/** The session currently executing the task */
|
|
14
14
|
assignedSessionId?: string;
|
|
15
|
+
/** Human/operator reason for terminal cancellation. */
|
|
16
|
+
cancelReason?: string;
|
|
17
|
+
cancelledAt?: string;
|
|
18
|
+
/** Human/operator reason for manually requeueing a task. */
|
|
19
|
+
requeueReason?: string;
|
|
20
|
+
requeuedAt?: string;
|
|
21
|
+
requeueCount?: number;
|
|
15
22
|
createdAt: string;
|
|
16
23
|
updatedAt: string;
|
|
17
24
|
}
|
|
@@ -37,6 +44,23 @@ export declare function claimNextTask(meshId: string, nodeId: string, sessionId:
|
|
|
37
44
|
* Used when a session completes, fails, or stalls.
|
|
38
45
|
*/
|
|
39
46
|
export declare function updateTaskStatus(meshId: string, taskId: string, status: MeshTaskStatus): MeshWorkQueueEntry | null;
|
|
47
|
+
/**
|
|
48
|
+
* Mark a queue task as manually cancelled without deleting audit history.
|
|
49
|
+
*/
|
|
50
|
+
export declare function cancelTask(meshId: string, taskId: string, opts?: {
|
|
51
|
+
reason?: string;
|
|
52
|
+
}): MeshWorkQueueEntry | null;
|
|
53
|
+
/**
|
|
54
|
+
* Return a queue task to pending for retry. By default, dead session targeting
|
|
55
|
+
* and assigned ownership are cleared so stale assignments do not strand again.
|
|
56
|
+
*/
|
|
57
|
+
export declare function requeueTask(meshId: string, taskId: string, opts?: {
|
|
58
|
+
reason?: string;
|
|
59
|
+
targetNodeId?: string;
|
|
60
|
+
targetSessionId?: string;
|
|
61
|
+
clearTargetNode?: boolean;
|
|
62
|
+
clearTargetSession?: boolean;
|
|
63
|
+
}): MeshWorkQueueEntry | null;
|
|
40
64
|
/**
|
|
41
65
|
* Update the status of the task currently assigned to a specific session.
|
|
42
66
|
*/
|
|
@@ -46,6 +70,7 @@ export interface MeshWorkQueueStats {
|
|
|
46
70
|
assigned: number;
|
|
47
71
|
completed: number;
|
|
48
72
|
failed: number;
|
|
73
|
+
cancelled: number;
|
|
49
74
|
}
|
|
50
75
|
/**
|
|
51
76
|
* Return aggregate queue statistics for the given mesh.
|
package/package.json
CHANGED
|
@@ -1456,6 +1456,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1456
1456
|
|
|
1457
1457
|
// ─── Script Execution ──────────────────────────
|
|
1458
1458
|
|
|
1459
|
+
private invokeCliScript<T>(script: Function, input: any): T {
|
|
1460
|
+
const hasStateFactory = typeof this.cliScripts?.createState === 'function';
|
|
1461
|
+
const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
|
|
1462
|
+
return expectsStateArgument
|
|
1463
|
+
? script(this.scriptState, input)
|
|
1464
|
+
: script(input);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1459
1467
|
private runParseSession(): ParsedSession | null {
|
|
1460
1468
|
if (typeof this.cliScripts?.parseSession !== 'function') {
|
|
1461
1469
|
this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
|
|
@@ -1476,7 +1484,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1476
1484
|
scope: this.currentTurnScope,
|
|
1477
1485
|
runtimeSettings: this.runtimeSettings,
|
|
1478
1486
|
});
|
|
1479
|
-
const session = this.
|
|
1487
|
+
const session = this.invokeCliScript<ParsedSession | null>(
|
|
1488
|
+
this.cliScripts.parseSession,
|
|
1489
|
+
{ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) },
|
|
1490
|
+
);
|
|
1480
1491
|
this.parseErrorMessage = null;
|
|
1481
1492
|
return session && typeof session === 'object' ? session : null;
|
|
1482
1493
|
} catch (e: any) {
|
|
@@ -1491,7 +1502,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1491
1502
|
if (!this.cliScripts?.detectStatus) return null;
|
|
1492
1503
|
try {
|
|
1493
1504
|
const screenText = this.terminalScreen.getText();
|
|
1494
|
-
const status = this.cliScripts.detectStatus
|
|
1505
|
+
const status = this.invokeCliScript<string | null>(this.cliScripts.detectStatus, {
|
|
1495
1506
|
tail: text.slice(-500),
|
|
1496
1507
|
screenText,
|
|
1497
1508
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -1511,7 +1522,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1511
1522
|
try {
|
|
1512
1523
|
const screenText = this.terminalScreen.getText();
|
|
1513
1524
|
const buffer = screenText || this.accumulatedBuffer;
|
|
1514
|
-
return this.cliScripts.parseApproval
|
|
1525
|
+
return this.invokeCliScript<{ message: string; buttons: string[] } | null>(this.cliScripts.parseApproval, {
|
|
1515
1526
|
buffer,
|
|
1516
1527
|
screenText,
|
|
1517
1528
|
rawBuffer: this.accumulatedRawBuffer,
|
package/src/commands/router.ts
CHANGED
|
@@ -1346,6 +1346,41 @@ export class DaemonCommandRouter {
|
|
|
1346
1346
|
}
|
|
1347
1347
|
}
|
|
1348
1348
|
|
|
1349
|
+
case 'cancel_mesh_queue_task': {
|
|
1350
|
+
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
1351
|
+
const taskId = typeof args?.taskId === 'string' ? args.taskId.trim() : '';
|
|
1352
|
+
if (!meshId || !taskId) return { success: false, error: 'meshId and taskId required' };
|
|
1353
|
+
try {
|
|
1354
|
+
const { cancelTask } = await import('../mesh/mesh-work-queue.js');
|
|
1355
|
+
const reason = typeof args?.reason === 'string' ? args.reason : undefined;
|
|
1356
|
+
const task = cancelTask(meshId, taskId, { reason });
|
|
1357
|
+
if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
|
|
1358
|
+
return { success: true, task };
|
|
1359
|
+
} catch (e: any) {
|
|
1360
|
+
return { success: false, error: e.message };
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
case 'requeue_mesh_queue_task': {
|
|
1365
|
+
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
1366
|
+
const taskId = typeof args?.taskId === 'string' ? args.taskId.trim() : '';
|
|
1367
|
+
if (!meshId || !taskId) return { success: false, error: 'meshId and taskId required' };
|
|
1368
|
+
try {
|
|
1369
|
+
const { requeueTask } = await import('../mesh/mesh-work-queue.js');
|
|
1370
|
+
const task = requeueTask(meshId, taskId, {
|
|
1371
|
+
reason: typeof args?.reason === 'string' ? args.reason : undefined,
|
|
1372
|
+
targetNodeId: typeof args?.targetNodeId === 'string' ? args.targetNodeId.trim() : undefined,
|
|
1373
|
+
targetSessionId: typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : undefined,
|
|
1374
|
+
clearTargetNode: args?.clearTargetNode === true,
|
|
1375
|
+
clearTargetSession: args?.clearTargetSession !== false,
|
|
1376
|
+
});
|
|
1377
|
+
if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
|
|
1378
|
+
return { success: true, task };
|
|
1379
|
+
} catch (e: any) {
|
|
1380
|
+
return { success: false, error: e.message };
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1349
1384
|
case 'add_mesh_node': {
|
|
1350
1385
|
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
1351
1386
|
const workspace = typeof args?.workspace === 'string' ? args.workspace.trim() : '';
|
|
@@ -1534,7 +1569,13 @@ export class DaemonCommandRouter {
|
|
|
1534
1569
|
appendLedgerEntry(meshId, {
|
|
1535
1570
|
kind: 'node_removed',
|
|
1536
1571
|
nodeId,
|
|
1537
|
-
payload: {
|
|
1572
|
+
payload: {
|
|
1573
|
+
worktree: !!node?.isLocalWorktree,
|
|
1574
|
+
sessionCleanupMode,
|
|
1575
|
+
workspace: typeof node?.workspace === 'string' ? node.workspace : undefined,
|
|
1576
|
+
daemonId: typeof node?.daemonId === 'string' ? node.daemonId : undefined,
|
|
1577
|
+
worktreeBranch: typeof node?.worktreeBranch === 'string' ? node.worktreeBranch : undefined,
|
|
1578
|
+
},
|
|
1538
1579
|
});
|
|
1539
1580
|
} catch { /* ledger append is best-effort */ }
|
|
1540
1581
|
}
|
package/src/index.ts
CHANGED
|
@@ -154,7 +154,7 @@ export { appendLedgerEntry, readLedgerEntries, getLedgerSummary, getLedgerDir, g
|
|
|
154
154
|
export type { MeshLedgerEntry, MeshLedgerKind, MeshLedgerSummary, ReadLedgerOptions, SessionRecoveryContext } from './mesh/mesh-ledger.js';
|
|
155
155
|
|
|
156
156
|
// ── Mesh Work Queue (GUPP) ──
|
|
157
|
-
export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus } from './mesh/mesh-work-queue.js';
|
|
157
|
+
export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask } from './mesh/mesh-work-queue.js';
|
|
158
158
|
export type { MeshWorkQueueEntry, MeshTaskStatus } from './mesh/mesh-work-queue.js';
|
|
159
159
|
|
|
160
160
|
// ── Mesh Events ──
|
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -240,13 +240,15 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
240
240
|
metadataEvent: Record<string, unknown>;
|
|
241
241
|
}) {
|
|
242
242
|
// ── Task Queue & Ledger ──
|
|
243
|
+
let completedTaskForLedger: { id?: string } | null = null;
|
|
243
244
|
if (args.event === 'agent:generating_completed') {
|
|
244
245
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
245
246
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
246
247
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
247
248
|
|
|
248
249
|
if (sessionId) {
|
|
249
|
-
updateSessionTaskStatus(args.meshId, sessionId, 'completed');
|
|
250
|
+
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, 'completed');
|
|
251
|
+
completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
|
|
250
252
|
if (nodeId && providerType) {
|
|
251
253
|
// Short delay to allow completion event to propagate before pulling next
|
|
252
254
|
setTimeout(() => {
|
|
@@ -262,6 +264,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
262
264
|
? updateSessionTaskStatus(args.meshId, sessionId, 'completed')
|
|
263
265
|
: null;
|
|
264
266
|
if (completedTask) {
|
|
267
|
+
completedTaskForLedger = { id: completedTask.id };
|
|
265
268
|
try {
|
|
266
269
|
appendLedgerEntry(args.meshId, {
|
|
267
270
|
kind: 'task_completed',
|
|
@@ -274,6 +277,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
274
277
|
taskId: completedTask.id,
|
|
275
278
|
completedViaReady: true,
|
|
276
279
|
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
|
|
280
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || undefined,
|
|
277
281
|
},
|
|
278
282
|
});
|
|
279
283
|
} catch (e: any) {
|
|
@@ -318,7 +322,9 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
318
322
|
payload: {
|
|
319
323
|
event: args.event,
|
|
320
324
|
nodeLabel: args.nodeLabel,
|
|
325
|
+
taskId: completedTaskForLedger?.id || undefined,
|
|
321
326
|
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
|
|
327
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || undefined,
|
|
322
328
|
},
|
|
323
329
|
});
|
|
324
330
|
} catch (e: any) {
|
|
@@ -451,6 +457,7 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
451
457
|
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
|
|
452
458
|
providerType: readNonEmptyString(payload.providerType),
|
|
453
459
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
460
|
+
finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
|
|
454
461
|
},
|
|
455
462
|
});
|
|
456
463
|
}
|
|
@@ -3,7 +3,7 @@ import { join } from 'path';
|
|
|
3
3
|
import { randomUUID } from 'crypto';
|
|
4
4
|
import { getLedgerDir } from './mesh-ledger.js';
|
|
5
5
|
|
|
6
|
-
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed';
|
|
6
|
+
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
|
|
7
7
|
|
|
8
8
|
export interface MeshWorkQueueEntry {
|
|
9
9
|
id: string;
|
|
@@ -18,6 +18,13 @@ export interface MeshWorkQueueEntry {
|
|
|
18
18
|
assignedNodeId?: string;
|
|
19
19
|
/** The session currently executing the task */
|
|
20
20
|
assignedSessionId?: string;
|
|
21
|
+
/** Human/operator reason for terminal cancellation. */
|
|
22
|
+
cancelReason?: string;
|
|
23
|
+
cancelledAt?: string;
|
|
24
|
+
/** Human/operator reason for manually requeueing a task. */
|
|
25
|
+
requeueReason?: string;
|
|
26
|
+
requeuedAt?: string;
|
|
27
|
+
requeueCount?: number;
|
|
21
28
|
createdAt: string;
|
|
22
29
|
updatedAt: string;
|
|
23
30
|
}
|
|
@@ -136,6 +143,65 @@ export function updateTaskStatus(
|
|
|
136
143
|
return queue[idx];
|
|
137
144
|
}
|
|
138
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Mark a queue task as manually cancelled without deleting audit history.
|
|
148
|
+
*/
|
|
149
|
+
export function cancelTask(
|
|
150
|
+
meshId: string,
|
|
151
|
+
taskId: string,
|
|
152
|
+
opts?: { reason?: string },
|
|
153
|
+
): MeshWorkQueueEntry | null {
|
|
154
|
+
const queue = readQueue(meshId);
|
|
155
|
+
const idx = queue.findIndex(q => q.id === taskId);
|
|
156
|
+
if (idx === -1) return null;
|
|
157
|
+
|
|
158
|
+
const now = new Date().toISOString();
|
|
159
|
+
queue[idx].status = 'cancelled';
|
|
160
|
+
queue[idx].updatedAt = now;
|
|
161
|
+
queue[idx].cancelledAt = now;
|
|
162
|
+
if (opts?.reason) queue[idx].cancelReason = opts.reason;
|
|
163
|
+
writeQueue(meshId, queue);
|
|
164
|
+
return queue[idx];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Return a queue task to pending for retry. By default, dead session targeting
|
|
169
|
+
* and assigned ownership are cleared so stale assignments do not strand again.
|
|
170
|
+
*/
|
|
171
|
+
export function requeueTask(
|
|
172
|
+
meshId: string,
|
|
173
|
+
taskId: string,
|
|
174
|
+
opts?: {
|
|
175
|
+
reason?: string;
|
|
176
|
+
targetNodeId?: string;
|
|
177
|
+
targetSessionId?: string;
|
|
178
|
+
clearTargetNode?: boolean;
|
|
179
|
+
clearTargetSession?: boolean;
|
|
180
|
+
},
|
|
181
|
+
): MeshWorkQueueEntry | null {
|
|
182
|
+
const queue = readQueue(meshId);
|
|
183
|
+
const idx = queue.findIndex(q => q.id === taskId);
|
|
184
|
+
if (idx === -1) return null;
|
|
185
|
+
|
|
186
|
+
const entry = queue[idx];
|
|
187
|
+
const now = new Date().toISOString();
|
|
188
|
+
entry.status = 'pending';
|
|
189
|
+
delete entry.assignedNodeId;
|
|
190
|
+
delete entry.assignedSessionId;
|
|
191
|
+
delete entry.cancelledAt;
|
|
192
|
+
delete entry.cancelReason;
|
|
193
|
+
if (opts?.clearTargetNode) delete entry.targetNodeId;
|
|
194
|
+
if (typeof opts?.targetNodeId === 'string') entry.targetNodeId = opts.targetNodeId;
|
|
195
|
+
if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
|
|
196
|
+
if (typeof opts?.targetSessionId === 'string') entry.targetSessionId = opts.targetSessionId;
|
|
197
|
+
entry.updatedAt = now;
|
|
198
|
+
entry.requeuedAt = now;
|
|
199
|
+
entry.requeueCount = (entry.requeueCount || 0) + 1;
|
|
200
|
+
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
201
|
+
writeQueue(meshId, queue);
|
|
202
|
+
return entry;
|
|
203
|
+
}
|
|
204
|
+
|
|
139
205
|
/**
|
|
140
206
|
* Update the status of the task currently assigned to a specific session.
|
|
141
207
|
*/
|
|
@@ -163,6 +229,7 @@ export interface MeshWorkQueueStats {
|
|
|
163
229
|
assigned: number;
|
|
164
230
|
completed: number;
|
|
165
231
|
failed: number;
|
|
232
|
+
cancelled: number;
|
|
166
233
|
}
|
|
167
234
|
|
|
168
235
|
/**
|
|
@@ -175,5 +242,6 @@ export function getMeshQueueStats(meshId: string): MeshWorkQueueStats {
|
|
|
175
242
|
assigned: queue.filter(q => q.status === 'assigned').length,
|
|
176
243
|
completed: queue.filter(q => q.status === 'completed').length,
|
|
177
244
|
failed: queue.filter(q => q.status === 'failed').length,
|
|
245
|
+
cancelled: queue.filter(q => q.status === 'cancelled').length,
|
|
178
246
|
};
|
|
179
247
|
}
|