@adhdev/daemon-core 0.9.82-rc.395 → 0.9.82-rc.396
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 +40 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +40 -23
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-stale.d.ts +9 -0
- package/package.json +2 -2
- package/src/mesh/mesh-events-stale.ts +43 -17
- package/src/mesh/mesh-reconcile-loop.ts +26 -3
|
@@ -34,6 +34,15 @@ export declare function reconcileDirectDispatchCompletionFromTranscript(args: {
|
|
|
34
34
|
transcriptMessageAt?: string;
|
|
35
35
|
completedAt?: string;
|
|
36
36
|
targetCoordinatorDaemonId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* NOTIF-DROP-SYNTH-NO-MESSAGE: the originating coordinator SESSION that dispatched this
|
|
39
|
+
* task (the `coordinatorSessionId` stamped onto the task_dispatched ledger payload at
|
|
40
|
+
* dispatch). Stamped onto the synthesized completion's targetCoordinatorSessionId so PHASE 2
|
|
41
|
+
* STRICT routing matches the exact coordinator session — instead of relying on the
|
|
42
|
+
* daemon-keyed fallback. When the caller does not pass it, it is recovered from the dispatch
|
|
43
|
+
* ledger entry below; absent on legacy rows → daemon-level routing (unchanged).
|
|
44
|
+
*/
|
|
45
|
+
targetCoordinatorSessionId?: string;
|
|
37
46
|
source?: string;
|
|
38
47
|
}): {
|
|
39
48
|
reconciled: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.396",
|
|
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",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.396",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -3,7 +3,7 @@ import type { MeshLedgerKind } from './mesh-ledger.js';
|
|
|
3
3
|
import { updateDirectDispatchStatus, cleanupTerminalDirectDispatches } from './mesh-work-queue.js';
|
|
4
4
|
import { markSessionDeliveriesTerminal } from './mesh-delivery-policy.js';
|
|
5
5
|
import { queuePendingMeshCoordinatorEvent } from './mesh-events-pending.js';
|
|
6
|
-
import { readNonEmptyString, readRecord, resolveEventSessionId, readWorkerResultMetadata, isWeakCompletionEvidence } from './mesh-events-utils.js';
|
|
6
|
+
import { readNonEmptyString, readRecord, resolveEventSessionId, readWorkerResultMetadata, isWeakCompletionEvidence, buildMeshSystemMessage } from './mesh-events-utils.js';
|
|
7
7
|
import { meshNodeIdMatches, type MeshNodeIdentified } from '@adhdev/mesh-shared';
|
|
8
8
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
@@ -188,6 +188,15 @@ export function reconcileDirectDispatchCompletionFromTranscript(args: {
|
|
|
188
188
|
transcriptMessageAt?: string;
|
|
189
189
|
completedAt?: string;
|
|
190
190
|
targetCoordinatorDaemonId?: string;
|
|
191
|
+
/**
|
|
192
|
+
* NOTIF-DROP-SYNTH-NO-MESSAGE: the originating coordinator SESSION that dispatched this
|
|
193
|
+
* task (the `coordinatorSessionId` stamped onto the task_dispatched ledger payload at
|
|
194
|
+
* dispatch). Stamped onto the synthesized completion's targetCoordinatorSessionId so PHASE 2
|
|
195
|
+
* STRICT routing matches the exact coordinator session — instead of relying on the
|
|
196
|
+
* daemon-keyed fallback. When the caller does not pass it, it is recovered from the dispatch
|
|
197
|
+
* ledger entry below; absent on legacy rows → daemon-level routing (unchanged).
|
|
198
|
+
*/
|
|
199
|
+
targetCoordinatorSessionId?: string;
|
|
191
200
|
source?: string;
|
|
192
201
|
}): { reconciled: boolean; kind?: MeshLedgerKind; alreadyTerminal?: boolean; workerResult?: unknown; ledgerEntryId?: string; reason?: string } {
|
|
193
202
|
const finalSummary = readNonEmptyString(args.finalSummary);
|
|
@@ -282,27 +291,44 @@ export function reconcileDirectDispatchCompletionFromTranscript(args: {
|
|
|
282
291
|
updateDirectDispatchStatus(args.meshId, args.sessionId, kind === 'task_completed' ? 'completed' : 'failed', args.taskId);
|
|
283
292
|
markSessionDeliveriesTerminal(args.meshId, args.sessionId, kind === 'task_completed' ? 'completed' : 'failed');
|
|
284
293
|
setImmediate(() => cleanupTerminalDirectDispatches());
|
|
294
|
+
// NOTIF-DROP-SYNTH-NO-MESSAGE: the queued synth completion MUST carry a coordinatorMessage,
|
|
295
|
+
// or injectPendingIntoCoordinator early-returns (`!pending.coordinatorMessage`) and the row is
|
|
296
|
+
// drained-without-inject → no [System] surface. The ~8s-later native completion then collides
|
|
297
|
+
// on the taskId-anchored fingerprint and is blocked at INSERT, so the notification is lost
|
|
298
|
+
// forever. Build the SAME [System] message the native path builds (buildMeshSystemMessage) so
|
|
299
|
+
// the synth is itself a complete, deliverable completion. Routing is made STRICT too: stamp the
|
|
300
|
+
// originating coordinator session so PHASE 2 strict-match delivers to the exact coordinator
|
|
301
|
+
// session that dispatched the task instead of falling back to a daemon-level broadcast.
|
|
302
|
+
const eventName = kind === 'task_completed' ? 'agent:generating_completed' : 'agent:stopped';
|
|
303
|
+
const nodeLabel = nodeId ? `Node '${nodeId}'` : 'Remote agent';
|
|
304
|
+
const metadataEvent = {
|
|
305
|
+
targetSessionId: args.sessionId,
|
|
306
|
+
providerType: providerType || undefined,
|
|
307
|
+
providerSessionId: readNonEmptyString(args.providerSessionId),
|
|
308
|
+
finalSummary,
|
|
309
|
+
taskId: args.taskId,
|
|
310
|
+
workerResult,
|
|
311
|
+
completionDiagnostic: {
|
|
312
|
+
reason: 'direct_task_transcript_reconciliation',
|
|
313
|
+
terminalLedgerKind: kind,
|
|
314
|
+
terminalLedgerId: entry.id,
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
// Originating coordinator session: prefer the explicit arg; otherwise recover it from the
|
|
318
|
+
// task_dispatched ledger payload (the MCP dispatch path stamps `coordinatorSessionId` there).
|
|
319
|
+
// Absent on legacy rows → undefined → daemon-level routing (unchanged, no regression).
|
|
320
|
+
const targetCoordinatorSessionId = readNonEmptyString(args.targetCoordinatorSessionId)
|
|
321
|
+
|| readNonEmptyString(dispatch?.payload?.coordinatorSessionId);
|
|
285
322
|
queuePendingMeshCoordinatorEvent({
|
|
286
|
-
event:
|
|
323
|
+
event: eventName,
|
|
287
324
|
meshId: args.meshId,
|
|
288
|
-
nodeLabel
|
|
325
|
+
nodeLabel,
|
|
289
326
|
nodeId: nodeId || undefined,
|
|
290
|
-
metadataEvent
|
|
291
|
-
|
|
292
|
-
providerType: providerType || undefined,
|
|
293
|
-
providerSessionId: readNonEmptyString(args.providerSessionId),
|
|
294
|
-
finalSummary,
|
|
295
|
-
taskId: args.taskId,
|
|
296
|
-
workerResult,
|
|
297
|
-
completionDiagnostic: {
|
|
298
|
-
reason: 'direct_task_transcript_reconciliation',
|
|
299
|
-
terminalLedgerKind: kind,
|
|
300
|
-
terminalLedgerId: entry.id,
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
coordinatorMessage: undefined,
|
|
327
|
+
metadataEvent,
|
|
328
|
+
coordinatorMessage: buildMeshSystemMessage({ event: eventName, nodeLabel, metadataEvent }),
|
|
304
329
|
queuedAt: Date.now(),
|
|
305
330
|
...(readNonEmptyString(args.targetCoordinatorDaemonId) ? { targetCoordinatorDaemonId: readNonEmptyString(args.targetCoordinatorDaemonId) } : {}),
|
|
331
|
+
...(targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}),
|
|
306
332
|
});
|
|
307
333
|
|
|
308
334
|
return { reconciled: true, kind, workerResult, ledgerEntryId: entry.id };
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
ackUnresolvedDelegateForward,
|
|
56
56
|
expireStaleUnresolvedDelegateForwards,
|
|
57
57
|
} from './mesh-unresolved-forward-outbox.js';
|
|
58
|
-
import { readNonEmptyString, readMeshCompletionSummary } from './mesh-events-utils.js';
|
|
58
|
+
import { readNonEmptyString, readMeshCompletionSummary, buildMeshSystemMessage } from './mesh-events-utils.js';
|
|
59
59
|
import { traceMeshEventStage, traceMeshEventDrop } from './mesh-event-trace.js';
|
|
60
60
|
import { expandDaemonIdForms, daemonIdsEquivalent } from '@adhdev/mesh-shared';
|
|
61
61
|
import { getActiveDirectDispatches, getQueue, reclaimStrandedAssignedTask, updateTaskStatus } from './mesh-work-queue.js';
|
|
@@ -368,7 +368,30 @@ function injectPendingIntoCoordinator(
|
|
|
368
368
|
coordinator: LiveCoordinator['instance'],
|
|
369
369
|
pending: PendingMeshCoordinatorEvent,
|
|
370
370
|
): void {
|
|
371
|
-
if (!coordinator
|
|
371
|
+
if (!coordinator) return;
|
|
372
|
+
// NOTIF-DROP-SYNTH-NO-MESSAGE (defence-in-depth): a queued event with no coordinatorMessage
|
|
373
|
+
// used to be dropped here (drain-without-inject) — the row had already been consumed
|
|
374
|
+
// (drained=1) by the caller's drain, so silently returning lost it forever. The primary fix
|
|
375
|
+
// makes the transcript-reconcile synth always carry a coordinatorMessage, but as a backstop,
|
|
376
|
+
// lazily synthesize the [System] text for any force-inject (terminal: completion / approval /
|
|
377
|
+
// stop / refine·bootstrap) event that still arrives message-less, so it surfaces instead of
|
|
378
|
+
// vanishing. A NON-force lifecycle event (agent:ready / generating_started) legitimately
|
|
379
|
+
// carries no message and must NOT be injected (it is queued only to re-drive the claim state
|
|
380
|
+
// machine on pull) — for it we still return without injecting.
|
|
381
|
+
let coordinatorMessage = pending.coordinatorMessage;
|
|
382
|
+
if (!coordinatorMessage) {
|
|
383
|
+
if (!shouldForceInjectMeshEvent(pending.event)) return;
|
|
384
|
+
const metadataEvent = pending.metadataEvent && typeof pending.metadataEvent === 'object'
|
|
385
|
+
? pending.metadataEvent
|
|
386
|
+
: {};
|
|
387
|
+
coordinatorMessage = buildMeshSystemMessage({
|
|
388
|
+
event: pending.event,
|
|
389
|
+
nodeLabel: pending.nodeLabel,
|
|
390
|
+
metadataEvent,
|
|
391
|
+
});
|
|
392
|
+
if (!coordinatorMessage) return; // builder produced nothing — nothing to surface
|
|
393
|
+
LOG.warn('MeshReconcile', `Lazily synthesized missing coordinatorMessage for ${pending.event} (mesh ${pending.meshId}) at inject time — a queued terminal event arrived message-less`);
|
|
394
|
+
}
|
|
372
395
|
const force = shouldForceInjectMeshEvent(pending.event);
|
|
373
396
|
// EVTTRACE: event surfaced to the coordinator (injected into its live CLI session).
|
|
374
397
|
// This is the terminal happy-path stage. Observation only.
|
|
@@ -380,7 +403,7 @@ function injectPendingIntoCoordinator(
|
|
|
380
403
|
event: pending.event,
|
|
381
404
|
}, force ? 'force-inject' : 'inject');
|
|
382
405
|
coordinator.onEvent('send_message', {
|
|
383
|
-
input: { text:
|
|
406
|
+
input: { text: coordinatorMessage, textFallback: coordinatorMessage },
|
|
384
407
|
...(force ? { force: true } : {}),
|
|
385
408
|
});
|
|
386
409
|
}
|