@adhdev/daemon-core 0.9.77-rc.33 → 0.9.77-rc.34

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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.77-rc.33",
3
+ "version": "0.9.77-rc.34",
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",
@@ -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() : '';
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 ──
@@ -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
  }