@adhdev/daemon-core 0.9.82-rc.60 → 0.9.82-rc.62
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 +84 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -5
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +11 -0
- package/package.json +1 -1
- package/src/commands/router.ts +8 -2
- package/src/mesh/mesh-events.ts +81 -0
|
@@ -26,6 +26,15 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
26
26
|
forwarded: number;
|
|
27
27
|
suppressed: boolean;
|
|
28
28
|
intentionalCleanupStop: boolean;
|
|
29
|
+
duplicateRefineTerminalEvent?: undefined;
|
|
30
|
+
duplicateCompletion?: undefined;
|
|
31
|
+
error?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
success: boolean;
|
|
34
|
+
forwarded: number;
|
|
35
|
+
suppressed: boolean;
|
|
36
|
+
duplicateRefineTerminalEvent: boolean;
|
|
37
|
+
intentionalCleanupStop?: undefined;
|
|
29
38
|
duplicateCompletion?: undefined;
|
|
30
39
|
error?: undefined;
|
|
31
40
|
} | {
|
|
@@ -34,12 +43,14 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
34
43
|
suppressed: boolean;
|
|
35
44
|
duplicateCompletion: boolean;
|
|
36
45
|
intentionalCleanupStop?: undefined;
|
|
46
|
+
duplicateRefineTerminalEvent?: undefined;
|
|
37
47
|
error?: undefined;
|
|
38
48
|
} | {
|
|
39
49
|
success: boolean;
|
|
40
50
|
forwarded: number;
|
|
41
51
|
suppressed?: undefined;
|
|
42
52
|
intentionalCleanupStop?: undefined;
|
|
53
|
+
duplicateRefineTerminalEvent?: undefined;
|
|
43
54
|
duplicateCompletion?: undefined;
|
|
44
55
|
error?: undefined;
|
|
45
56
|
} | {
|
package/package.json
CHANGED
package/src/commands/router.ts
CHANGED
|
@@ -925,6 +925,7 @@ type MeshRefineJobHandle = {
|
|
|
925
925
|
startedAt: string;
|
|
926
926
|
completedAt?: string;
|
|
927
927
|
duplicate?: boolean;
|
|
928
|
+
retryOfJobId?: string;
|
|
928
929
|
eventDelivery: {
|
|
929
930
|
pendingEvents: true;
|
|
930
931
|
ledger: true;
|
|
@@ -2188,6 +2189,7 @@ export class DaemonCommandRouter {
|
|
|
2188
2189
|
completedAt?: string;
|
|
2189
2190
|
jobId?: string;
|
|
2190
2191
|
interactionId?: string;
|
|
2192
|
+
retryOfJobId?: string;
|
|
2191
2193
|
}): MeshRefineJobHandle {
|
|
2192
2194
|
return {
|
|
2193
2195
|
success: true,
|
|
@@ -2202,6 +2204,7 @@ export class DaemonCommandRouter {
|
|
|
2202
2204
|
workspace: readStringValue(args.node?.workspace),
|
|
2203
2205
|
startedAt: args.startedAt || new Date().toISOString(),
|
|
2204
2206
|
...(args.completedAt ? { completedAt: args.completedAt } : {}),
|
|
2207
|
+
...(args.retryOfJobId ? { retryOfJobId: args.retryOfJobId } : {}),
|
|
2205
2208
|
eventDelivery: { pendingEvents: true, ledger: true },
|
|
2206
2209
|
evidence: {
|
|
2207
2210
|
pendingEventsCommand: 'get_pending_mesh_events',
|
|
@@ -2229,6 +2232,7 @@ export class DaemonCommandRouter {
|
|
|
2229
2232
|
status: handle.status,
|
|
2230
2233
|
startedAt: handle.startedAt,
|
|
2231
2234
|
completedAt: handle.completedAt,
|
|
2235
|
+
retryOfJobId: handle.retryOfJobId,
|
|
2232
2236
|
...(result ? { result } : {}),
|
|
2233
2237
|
},
|
|
2234
2238
|
queuedAt: Date.now(),
|
|
@@ -2253,8 +2257,10 @@ export class DaemonCommandRouter {
|
|
|
2253
2257
|
workspace: handle.workspace,
|
|
2254
2258
|
startedAt: handle.startedAt,
|
|
2255
2259
|
completedAt: handle.completedAt,
|
|
2260
|
+
retryOfJobId: handle.retryOfJobId,
|
|
2256
2261
|
},
|
|
2257
2262
|
async: true,
|
|
2263
|
+
retryOfJobId: handle.retryOfJobId,
|
|
2258
2264
|
...(result ? {
|
|
2259
2265
|
success: result.success === true,
|
|
2260
2266
|
result,
|
|
@@ -2509,6 +2515,7 @@ export class DaemonCommandRouter {
|
|
|
2509
2515
|
completedAt,
|
|
2510
2516
|
jobId: handle.jobId,
|
|
2511
2517
|
interactionId: handle.interactionId,
|
|
2518
|
+
retryOfJobId: handle.retryOfJobId,
|
|
2512
2519
|
node: { daemonId: handle.targetDaemonId, workspace: handle.workspace },
|
|
2513
2520
|
});
|
|
2514
2521
|
const terminal: MeshRefineTerminalJob = { ...terminalHandle, result };
|
|
@@ -2524,7 +2531,6 @@ export class DaemonCommandRouter {
|
|
|
2524
2531
|
const running = this.runningRefineJobs.get(key);
|
|
2525
2532
|
if (running) return { ...running, duplicate: true };
|
|
2526
2533
|
const terminal = this.terminalRefineJobs.get(key);
|
|
2527
|
-
if (terminal) return { ...terminal, duplicate: true };
|
|
2528
2534
|
|
|
2529
2535
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
2530
2536
|
const mesh = meshRecord?.mesh;
|
|
@@ -2532,7 +2538,7 @@ export class DaemonCommandRouter {
|
|
|
2532
2538
|
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
|
|
2533
2539
|
if (!node.isLocalWorktree || !node.workspace) return { success: false, error: `Refinery requires a local worktree node` };
|
|
2534
2540
|
|
|
2535
|
-
const handle = this.buildRefineJobHandle({ meshId, nodeId, node });
|
|
2541
|
+
const handle = this.buildRefineJobHandle({ meshId, nodeId, node, retryOfJobId: terminal?.jobId });
|
|
2536
2542
|
this.runningRefineJobs.set(key, handle);
|
|
2537
2543
|
await this.appendRefineJobLedger('task_dispatched', handle);
|
|
2538
2544
|
this.queueRefineJobEvent('refine:accepted', handle);
|
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -58,6 +58,29 @@ export interface PendingMeshCoordinatorEvent {
|
|
|
58
58
|
queuedAt: number;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
const REFINE_TERMINAL_EVENTS = new Set(['refine:completed', 'refine:failed']);
|
|
62
|
+
|
|
63
|
+
function readRefineJobId(event: { metadataEvent?: Record<string, unknown> } | Record<string, unknown>): string {
|
|
64
|
+
const metadata = readRecord((event as any).metadataEvent) || event as Record<string, unknown>;
|
|
65
|
+
const result = readRecord(metadata.result);
|
|
66
|
+
const refineJob = readRecord(result?.refineJob);
|
|
67
|
+
return readNonEmptyString(metadata.jobId) || readNonEmptyString(refineJob?.jobId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildRefineTerminalEventFingerprint(meshId: string, eventName: string, metadataEvent: Record<string, unknown>): string {
|
|
71
|
+
const jobId = readRefineJobId({ metadataEvent });
|
|
72
|
+
return jobId && REFINE_TERMINAL_EVENTS.has(eventName) ? `${meshId}::${eventName}::${jobId}` : '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hasPendingRefineTerminalEventDuplicate(event: PendingMeshCoordinatorEvent): boolean {
|
|
76
|
+
if (!REFINE_TERMINAL_EVENTS.has(event.event)) return false;
|
|
77
|
+
const jobId = readRefineJobId(event);
|
|
78
|
+
if (!jobId) return false;
|
|
79
|
+
return getPendingMeshCoordinatorEvents(event.meshId).some((pending) =>
|
|
80
|
+
pending.event === event.event && readRefineJobId(pending) === jobId,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
function getPendingEventsPath(meshId: string): string {
|
|
62
85
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
63
86
|
return join(getLedgerDir(), `${safe}.pending-events.jsonl`);
|
|
@@ -65,6 +88,10 @@ function getPendingEventsPath(meshId: string): string {
|
|
|
65
88
|
|
|
66
89
|
export function queuePendingMeshCoordinatorEvent(event: PendingMeshCoordinatorEvent): boolean {
|
|
67
90
|
try {
|
|
91
|
+
if (hasPendingRefineTerminalEventDuplicate(event)) {
|
|
92
|
+
LOG.info('MeshEvents', `Suppressed duplicate pending ${event.event} for refine job ${readRefineJobId(event)}`);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
68
95
|
appendFileSync(getPendingEventsPath(event.meshId), JSON.stringify(event) + '\n', 'utf-8');
|
|
69
96
|
return true;
|
|
70
97
|
} catch (e: any) {
|
|
@@ -131,6 +158,9 @@ const MESH_COORDINATOR_EVENTS = new Set([
|
|
|
131
158
|
'agent:stopped',
|
|
132
159
|
'agent:ready',
|
|
133
160
|
'monitor:long_generating',
|
|
161
|
+
'refine:accepted',
|
|
162
|
+
'refine:completed',
|
|
163
|
+
'refine:failed',
|
|
134
164
|
]);
|
|
135
165
|
|
|
136
166
|
const EVENT_TO_LEDGER_KIND: Record<string, MeshLedgerKind> = {
|
|
@@ -266,6 +296,18 @@ function isDuplicateMeshCompletionEvent(args: {
|
|
|
266
296
|
return false;
|
|
267
297
|
}
|
|
268
298
|
|
|
299
|
+
function isDuplicateRefineTerminalEvent(meshId: string, eventName: string, metadataEvent: Record<string, unknown>): boolean {
|
|
300
|
+
const fingerprint = buildRefineTerminalEventFingerprint(meshId, eventName, metadataEvent);
|
|
301
|
+
if (!fingerprint) return false;
|
|
302
|
+
const now = Date.now();
|
|
303
|
+
for (const [key, seenAt] of recentCompletionFingerprints.entries()) {
|
|
304
|
+
if (now - seenAt > RECENT_COMPLETION_FINGERPRINT_TTL_MS) recentCompletionFingerprints.delete(key);
|
|
305
|
+
}
|
|
306
|
+
if (recentCompletionFingerprints.has(fingerprint)) return true;
|
|
307
|
+
recentCompletionFingerprints.set(fingerprint, now);
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
|
|
269
311
|
|
|
270
312
|
export function tryAssignQueueTask(
|
|
271
313
|
components: DaemonComponents,
|
|
@@ -700,6 +742,32 @@ function buildMeshSystemMessage(args: {
|
|
|
700
742
|
if (args.event === 'monitor:long_generating') {
|
|
701
743
|
return `[System] ${args.nodeLabel} has been generating for a long time${metadata}. Use mesh_read_chat once for a status check, but do not poll repeatedly.`;
|
|
702
744
|
}
|
|
745
|
+
if (args.event === 'refine:accepted') {
|
|
746
|
+
const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
|
|
747
|
+
return `[System] Refinery accepted async job${jobId ? ` ${jobId}` : ''} for ${args.nodeLabel}. Completion/failure will be delivered as a terminal refine event; do not poll repeatedly.`;
|
|
748
|
+
}
|
|
749
|
+
if (args.event === 'refine:completed') {
|
|
750
|
+
const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
|
|
751
|
+
const result = readRecord(args.metadataEvent.result);
|
|
752
|
+
const validationSummary = readRecord(result?.validationSummary);
|
|
753
|
+
const validationStatus = readNonEmptyString(validationSummary?.status);
|
|
754
|
+
const into = readNonEmptyString(result?.into);
|
|
755
|
+
const branch = readNonEmptyString(result?.branch);
|
|
756
|
+
const details = [
|
|
757
|
+
jobId ? `job_id=${jobId}` : '',
|
|
758
|
+
branch && into ? `${branch}→${into}` : '',
|
|
759
|
+
validationStatus ? `validation=${validationStatus}` : '',
|
|
760
|
+
].filter(Boolean).join('; ');
|
|
761
|
+
return `[System] Refinery async job for ${args.nodeLabel} completed successfully${details ? ` (${details})` : ''}. The worktree was merged and cleanup completed; continue from the updated mesh state.`;
|
|
762
|
+
}
|
|
763
|
+
if (args.event === 'refine:failed') {
|
|
764
|
+
const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
|
|
765
|
+
const result = readRecord(args.metadataEvent.result);
|
|
766
|
+
const code = readNonEmptyString(result?.code);
|
|
767
|
+
const error = readNonEmptyString(result?.error);
|
|
768
|
+
const details = [jobId ? `job_id=${jobId}` : '', code ? `code=${code}` : ''].filter(Boolean).join('; ');
|
|
769
|
+
return `[System] Refinery async job for ${args.nodeLabel} failed${details ? ` (${details})` : ''}${error ? `: ${error}` : '.'} Review the terminal refine event/ledger before retrying.`;
|
|
770
|
+
}
|
|
703
771
|
return '';
|
|
704
772
|
}
|
|
705
773
|
|
|
@@ -728,6 +796,11 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
728
796
|
return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
|
|
729
797
|
}
|
|
730
798
|
|
|
799
|
+
if (isDuplicateRefineTerminalEvent(args.meshId, args.event, args.metadataEvent)) {
|
|
800
|
+
LOG.info('MeshEvents', `Suppressed duplicate ${args.event} for refine job ${readRefineJobId({ metadataEvent: args.metadataEvent })}`);
|
|
801
|
+
return { success: true, forwarded: 0, suppressed: true, duplicateRefineTerminalEvent: true };
|
|
802
|
+
}
|
|
803
|
+
|
|
731
804
|
const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
|
|
732
805
|
if (args.event === 'agent:generating_completed' && eventSessionId) {
|
|
733
806
|
const duplicateCompletion = isDuplicateMeshCompletionEvent({
|
|
@@ -1005,6 +1078,14 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
1005
1078
|
providerType: readNonEmptyString(payload.providerType),
|
|
1006
1079
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
1007
1080
|
finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
|
|
1081
|
+
jobId: readNonEmptyString(payload.jobId),
|
|
1082
|
+
interactionId: readNonEmptyString(payload.interactionId),
|
|
1083
|
+
status: readNonEmptyString(payload.status),
|
|
1084
|
+
targetDaemonId: readNonEmptyString(payload.targetDaemonId),
|
|
1085
|
+
startedAt: readNonEmptyString(payload.startedAt),
|
|
1086
|
+
completedAt: readNonEmptyString(payload.completedAt),
|
|
1087
|
+
retryOfJobId: readNonEmptyString(payload.retryOfJobId),
|
|
1088
|
+
...(payload.result && typeof payload.result === 'object' && !Array.isArray(payload.result) ? { result: payload.result } : {}),
|
|
1008
1089
|
...(payload.timestamp !== undefined ? { timestamp: payload.timestamp } : {}),
|
|
1009
1090
|
intentional: payload.intentional === true,
|
|
1010
1091
|
intentionalStop: payload.intentionalStop === true,
|