@adhdev/daemon-core 0.9.82-rc.132 → 0.9.82-rc.133
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 +30 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +30 -6
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-active-work.d.ts +15 -4
- package/dist/mesh/mesh-events.d.ts +17 -0
- package/package.json +1 -1
- package/src/mesh/coordinator-prompt.ts +1 -1
- package/src/mesh/mesh-active-work.ts +45 -6
- package/src/mesh/mesh-events.ts +32 -2
|
@@ -21,6 +21,12 @@ export interface MeshActiveWorkRecord {
|
|
|
21
21
|
terminalKind?: string;
|
|
22
22
|
terminalAt?: string;
|
|
23
23
|
staleReason?: string;
|
|
24
|
+
/**
|
|
25
|
+
* When true, the session targeted by this direct dispatch still exists in the live mesh
|
|
26
|
+
* but did not transition to generating — the dispatch was not acknowledged by the provider.
|
|
27
|
+
* Distinct from historical/orphaned stale entries where the node/session is gone.
|
|
28
|
+
*/
|
|
29
|
+
staleDispatchUnacknowledged?: boolean;
|
|
24
30
|
}
|
|
25
31
|
export interface MeshActiveWorkSummary {
|
|
26
32
|
totalActiveCount: number;
|
|
@@ -34,10 +40,15 @@ export interface MeshActiveWorkSummary {
|
|
|
34
40
|
statusCounts: Record<MeshActiveWorkStatus, number>;
|
|
35
41
|
staleDirectCount: number;
|
|
36
42
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
|
|
43
|
+
* Count of stale direct entries where the target session still exists in the live mesh
|
|
44
|
+
* but never transitioned to generating — these are fresh dispatch failures, not historical orphans.
|
|
45
|
+
* Requires immediate recovery: launch a fresh session and retry the task.
|
|
46
|
+
*/
|
|
47
|
+
staleDirectUnacknowledgedCount?: number;
|
|
48
|
+
/**
|
|
49
|
+
* When staleDirectCount > 0, this note clarifies what kind of stale records exist.
|
|
50
|
+
* Fresh unacknowledged dispatches carry an actionable recovery note; orphaned historical
|
|
51
|
+
* entries carry the "historical evidence only" note. Both may coexist.
|
|
41
52
|
*/
|
|
42
53
|
staleDirectNote?: string;
|
|
43
54
|
}
|
|
@@ -32,6 +32,7 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
32
32
|
duplicateRefineTerminalEvent?: undefined;
|
|
33
33
|
duplicateCompletion?: undefined;
|
|
34
34
|
error?: undefined;
|
|
35
|
+
bufferedForGeneratingCoordinator?: undefined;
|
|
35
36
|
} | {
|
|
36
37
|
success: boolean;
|
|
37
38
|
forwarded: number;
|
|
@@ -42,6 +43,7 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
42
43
|
duplicateRefineTerminalEvent?: undefined;
|
|
43
44
|
duplicateCompletion?: undefined;
|
|
44
45
|
error?: undefined;
|
|
46
|
+
bufferedForGeneratingCoordinator?: undefined;
|
|
45
47
|
} | {
|
|
46
48
|
success: boolean;
|
|
47
49
|
forwarded: number;
|
|
@@ -52,6 +54,7 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
52
54
|
terminalLedgerKind?: undefined;
|
|
53
55
|
duplicateCompletion?: undefined;
|
|
54
56
|
error?: undefined;
|
|
57
|
+
bufferedForGeneratingCoordinator?: undefined;
|
|
55
58
|
} | {
|
|
56
59
|
success: boolean;
|
|
57
60
|
forwarded: number;
|
|
@@ -62,6 +65,7 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
62
65
|
terminalLedgerKind?: undefined;
|
|
63
66
|
duplicateRefineTerminalEvent?: undefined;
|
|
64
67
|
error?: undefined;
|
|
68
|
+
bufferedForGeneratingCoordinator?: undefined;
|
|
65
69
|
} | {
|
|
66
70
|
success: boolean;
|
|
67
71
|
forwarded: number;
|
|
@@ -72,6 +76,7 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
72
76
|
terminalLedgerKind?: undefined;
|
|
73
77
|
duplicateRefineTerminalEvent?: undefined;
|
|
74
78
|
error?: undefined;
|
|
79
|
+
bufferedForGeneratingCoordinator?: undefined;
|
|
75
80
|
} | {
|
|
76
81
|
success: boolean;
|
|
77
82
|
forwarded: number;
|
|
@@ -82,6 +87,18 @@ export declare function handleMeshForwardEvent(components: DaemonComponents, pay
|
|
|
82
87
|
duplicateRefineTerminalEvent?: undefined;
|
|
83
88
|
duplicateCompletion?: undefined;
|
|
84
89
|
error?: undefined;
|
|
90
|
+
bufferedForGeneratingCoordinator?: undefined;
|
|
91
|
+
} | {
|
|
92
|
+
success: boolean;
|
|
93
|
+
forwarded: number;
|
|
94
|
+
bufferedForGeneratingCoordinator: boolean;
|
|
95
|
+
suppressed?: undefined;
|
|
96
|
+
intentionalCleanupStop?: undefined;
|
|
97
|
+
terminalLedgerEvidence?: undefined;
|
|
98
|
+
terminalLedgerKind?: undefined;
|
|
99
|
+
duplicateRefineTerminalEvent?: undefined;
|
|
100
|
+
duplicateCompletion?: undefined;
|
|
101
|
+
error?: undefined;
|
|
85
102
|
} | {
|
|
86
103
|
success: boolean;
|
|
87
104
|
error: string;
|
package/package.json
CHANGED
|
@@ -211,7 +211,7 @@ function buildRulesSection(coordinatorCliType?: string): string {
|
|
|
211
211
|
|
|
212
212
|
- **Minimize coordinator context.** The coordinator's job is routing, not implementing. Do not read source files, run commands, or analyze code directly — delegate all of that to node agents. Your context should stay lean.
|
|
213
213
|
- **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to the queue or a node. Do not do it yourself.
|
|
214
|
-
- **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes → \`hermes-cli\`, Claude Code/Claude → \`claude-cli\`, Codex → \`codex-cli\`, Gemini → \`gemini-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
|
|
214
|
+
- **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes → \`hermes-cli\`, Claude Code/Claude → \`claude-cli\`, Codex → \`codex-cli\`, Gemini → \`gemini-cli\`, Antigravity → \`antigravity-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
|
|
215
215
|
- **Front-load new task messages.** When calling \`mesh_enqueue_task\` or \`mesh_send_task\` for a new task, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
|
|
216
216
|
- **Avoid context-wasting restarts.** For follow-up, retry, commit/push, preview, or cleanup work on the same issue, prefer the existing idle session and send only the delta from its last verified state. Start a fresh chat/session only for genuinely independent work, explicit provider/user request, unsafe transcript contamination, or required branch/worktree isolation.
|
|
217
217
|
- **Don't inspect code.** Treat delegated agent summaries as self-reports, not verification. Verify side effects via \`mesh_git_status\` (including related repo freshness when configured), not by reading source files.
|
|
@@ -23,6 +23,12 @@ export interface MeshActiveWorkRecord {
|
|
|
23
23
|
terminalKind?: string;
|
|
24
24
|
terminalAt?: string;
|
|
25
25
|
staleReason?: string;
|
|
26
|
+
/**
|
|
27
|
+
* When true, the session targeted by this direct dispatch still exists in the live mesh
|
|
28
|
+
* but did not transition to generating — the dispatch was not acknowledged by the provider.
|
|
29
|
+
* Distinct from historical/orphaned stale entries where the node/session is gone.
|
|
30
|
+
*/
|
|
31
|
+
staleDispatchUnacknowledged?: boolean;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
export interface MeshActiveWorkSummary {
|
|
@@ -37,10 +43,15 @@ export interface MeshActiveWorkSummary {
|
|
|
37
43
|
statusCounts: Record<MeshActiveWorkStatus, number>;
|
|
38
44
|
staleDirectCount: number;
|
|
39
45
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
46
|
+
* Count of stale direct entries where the target session still exists in the live mesh
|
|
47
|
+
* but never transitioned to generating — these are fresh dispatch failures, not historical orphans.
|
|
48
|
+
* Requires immediate recovery: launch a fresh session and retry the task.
|
|
49
|
+
*/
|
|
50
|
+
staleDirectUnacknowledgedCount?: number;
|
|
51
|
+
/**
|
|
52
|
+
* When staleDirectCount > 0, this note clarifies what kind of stale records exist.
|
|
53
|
+
* Fresh unacknowledged dispatches carry an actionable recovery note; orphaned historical
|
|
54
|
+
* entries carry the "historical evidence only" note. Both may coexist.
|
|
44
55
|
*/
|
|
45
56
|
staleDirectNote?: string;
|
|
46
57
|
}
|
|
@@ -163,6 +174,7 @@ export function buildMeshActiveWorkSummary(activeWork: MeshActiveWorkRecord[]):
|
|
|
163
174
|
statusCounts[item.status] += 1;
|
|
164
175
|
}
|
|
165
176
|
const staleDirectCount = activeWork.filter(item => item.source === 'direct' && item.staleReason).length;
|
|
177
|
+
const staleDirectUnacknowledgedCount = activeWork.filter(item => item.source === 'direct' && item.staleDispatchUnacknowledged).length;
|
|
166
178
|
return {
|
|
167
179
|
totalActiveCount: activeWork.length,
|
|
168
180
|
queueActiveCount: sourceCounts.queue,
|
|
@@ -174,6 +186,7 @@ export function buildMeshActiveWorkSummary(activeWork: MeshActiveWorkRecord[]):
|
|
|
174
186
|
sourceCounts,
|
|
175
187
|
statusCounts,
|
|
176
188
|
staleDirectCount,
|
|
189
|
+
...(staleDirectUnacknowledgedCount > 0 ? { staleDirectUnacknowledgedCount } : {}),
|
|
177
190
|
...(staleDirectCount > 0 ? { staleDirectNote: 'Stale direct records are orphaned ledger entries whose node/session no longer exists. They are historical recovery evidence only — not active or unresolved work. The queue (source: queue) is authoritative for pending/assigned tasks.' } : {}),
|
|
178
191
|
};
|
|
179
192
|
}
|
|
@@ -216,11 +229,25 @@ export function buildMeshActiveWork(opts: BuildMeshActiveWorkOptions): { activeW
|
|
|
216
229
|
const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
|
|
217
230
|
const status = terminalStatus || live.status || 'assigned';
|
|
218
231
|
const terminalRow = Boolean(terminal && terminal.kind !== 'task_approval_needed');
|
|
219
|
-
|
|
232
|
+
// dispatchedToIdleSession is recorded by mesh_send_task when the target session was idle
|
|
233
|
+
// at dispatch time. Combined with status === 'idle', it confirms no runtime transition.
|
|
234
|
+
const dispatchedToIdleSession = dispatch.payload?.dispatchedToIdleSession === true;
|
|
235
|
+
// Flag stale when no terminal event AND either:
|
|
236
|
+
// - session is now idle (dispatch not acknowledged), OR
|
|
237
|
+
// - no live status reported (node/session absent from live mesh), OR
|
|
238
|
+
// - payload flagged idle-at-dispatch AND session is still not generating (redundant guard for clarity).
|
|
239
|
+
const isNoTransition = !terminalStatus && !live.status;
|
|
240
|
+
const isIdleUnacknowledged = status === 'idle';
|
|
241
|
+
const ledgerOnlyStaleReason = !terminalRow && (isIdleUnacknowledged || isNoTransition || (dispatchedToIdleSession && isIdleUnacknowledged))
|
|
220
242
|
? 'direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition'
|
|
221
243
|
: undefined;
|
|
222
244
|
const message = readString(dispatch.payload?.message) || readString(dispatch.payload?.summary) || '';
|
|
223
245
|
const { title, summary } = summarizeMessage(message);
|
|
246
|
+
// A dispatch is a fresh unacknowledged failure when the node and session both still exist
|
|
247
|
+
// in the live mesh but the session never transitioned to generating. This is distinct from
|
|
248
|
+
// historical/orphaned stale entries where the node or session is gone from the live mesh.
|
|
249
|
+
// fresh-unacknowledged: ledgerOnlyStaleReason is set (session didn't ack) AND no live.staleReason (node/session is present)
|
|
250
|
+
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
|
|
224
251
|
const record: MeshActiveWorkRecord = {
|
|
225
252
|
taskId,
|
|
226
253
|
source: 'direct',
|
|
@@ -240,6 +267,7 @@ export function buildMeshActiveWork(opts: BuildMeshActiveWorkOptions): { activeW
|
|
|
240
267
|
terminalKind: terminal?.kind,
|
|
241
268
|
terminalAt: terminal?.timestamp,
|
|
242
269
|
staleReason: live.staleReason || ledgerOnlyStaleReason,
|
|
270
|
+
...(isFreshUnacknowledged ? { staleDispatchUnacknowledged: true } : {}),
|
|
243
271
|
};
|
|
244
272
|
if (terminalRow) {
|
|
245
273
|
terminalDirectWork.push(record);
|
|
@@ -257,8 +285,19 @@ export function buildMeshActiveWork(opts: BuildMeshActiveWorkOptions): { activeW
|
|
|
257
285
|
terminalDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
258
286
|
const summary = buildMeshActiveWorkSummary(records);
|
|
259
287
|
summary.staleDirectCount = staleDirectWork.length;
|
|
288
|
+
const unacknowledgedCount = staleDirectWork.filter(r => r.staleDispatchUnacknowledged).length;
|
|
289
|
+
if (unacknowledgedCount > 0) {
|
|
290
|
+
summary.staleDirectUnacknowledgedCount = unacknowledgedCount;
|
|
291
|
+
}
|
|
292
|
+
// Build a note that accurately describes what's in the stale set.
|
|
293
|
+
// When there are fresh unacknowledged entries (node/session still live, session never started),
|
|
294
|
+
// use the actionable recovery note instead of the misleading "historical evidence only" note.
|
|
260
295
|
const staleDirectWorkNote = staleDirectWork.length > 0
|
|
261
|
-
?
|
|
296
|
+
? unacknowledgedCount > 0 && unacknowledgedCount === staleDirectWork.length
|
|
297
|
+
? `${unacknowledgedCount} direct dispatch(es) were not acknowledged by the target session — the session received the agent_command but never transitioned to generating. This is a fresh dispatch failure, not historical noise. Recovery: launch a fresh session on the same node and retry the task, or use mesh_enqueue_task for queue-based assignment.`
|
|
298
|
+
: unacknowledgedCount > 0
|
|
299
|
+
? `${unacknowledgedCount} of ${staleDirectWork.length} stale direct record(s) are fresh unacknowledged dispatch failures (session still live but never transitioned to generating); the rest are orphaned historical entries whose node/session no longer exists. Fresh unacknowledged dispatches need recovery: launch a fresh session and retry. Orphaned entries are historical evidence only — not active or unresolved work.`
|
|
300
|
+
: 'These are orphaned ledger entries whose original node or session no longer exists in the live mesh. They are historical/recovery evidence only — not active or unresolved work. Do not treat staleDirectCount as a status mismatch; use the queue (source: queue) as authoritative for pending/assigned tasks.'
|
|
262
301
|
: undefined;
|
|
263
302
|
if (staleDirectWorkNote) {
|
|
264
303
|
summary.staleDirectNote = staleDirectWorkNote;
|
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -1332,7 +1332,24 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
1332
1332
|
// that poll via get_pending_mesh_events (dual delivery). Refine terminal events are
|
|
1333
1333
|
// forwarded directly only — they must not accumulate in the pending queue when a live
|
|
1334
1334
|
// coordinator already received them.
|
|
1335
|
-
|
|
1335
|
+
//
|
|
1336
|
+
// Exception: if ALL live CLI coordinator instances are in a generating/active state
|
|
1337
|
+
// when a refine terminal event fires (e.g. a Codex CLI coordinator that triggered an
|
|
1338
|
+
// async refine job and is still in the generating turn that sent it), the coordinator
|
|
1339
|
+
// cannot immediately receive send_message input. Buffer the event to the pending queue
|
|
1340
|
+
// so it is available via get_pending_mesh_events when the coordinator returns to idle.
|
|
1341
|
+
// Critically: do NOT attempt send_message injection into a generating PTY coordinator for
|
|
1342
|
+
// terminal refine events — injecting text into an active PTY can corrupt the input stream
|
|
1343
|
+
// and leave the coordinator stuck in generating state, unable to process the refine result.
|
|
1344
|
+
const allCoordinatorsGenerating = isRefineTerminalEvent && coordinatorInstances.every((inst) => {
|
|
1345
|
+
const s = inst.getState();
|
|
1346
|
+
const status = readNonEmptyString(s.status).toLowerCase();
|
|
1347
|
+
const activeChatStatus = readNonEmptyString(s.activeChat?.status).toLowerCase();
|
|
1348
|
+
return status === 'generating' || status === 'streaming' || status === 'long_generating'
|
|
1349
|
+
|| activeChatStatus === 'generating' || activeChatStatus === 'streaming';
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
if (!isRefineTerminalEvent || allCoordinatorsGenerating) {
|
|
1336
1353
|
if (queuePendingMeshCoordinatorEvent({
|
|
1337
1354
|
event: args.event,
|
|
1338
1355
|
meshId: args.meshId,
|
|
@@ -1346,10 +1363,23 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
1346
1363
|
coordinatorMessage: messageText,
|
|
1347
1364
|
queuedAt: Date.now(),
|
|
1348
1365
|
})) {
|
|
1349
|
-
|
|
1366
|
+
if (allCoordinatorsGenerating) {
|
|
1367
|
+
LOG.info('MeshEvents', `Queued ${args.event} for generating CLI coordinator (mesh ${args.meshId}) — will be delivered via get_pending_mesh_events when coordinator returns to idle`);
|
|
1368
|
+
} else {
|
|
1369
|
+
LOG.info('MeshEvents', `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
|
|
1370
|
+
}
|
|
1350
1371
|
}
|
|
1351
1372
|
}
|
|
1352
1373
|
|
|
1374
|
+
// When all CLI coordinators are actively generating and a terminal refine event fires,
|
|
1375
|
+
// skip send_message injection entirely. The event is already buffered to the pending queue.
|
|
1376
|
+
// Injecting into a generating PTY coordinator can corrupt its input stream and cause it to
|
|
1377
|
+
// remain stuck in generating state, never processing the refine result.
|
|
1378
|
+
// The coordinator will drain pending events via get_pending_mesh_events on its next idle cycle.
|
|
1379
|
+
if (allCoordinatorsGenerating) {
|
|
1380
|
+
return { success: true, forwarded: 0, bufferedForGeneratingCoordinator: true };
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1353
1383
|
for (const coord of coordinatorInstances) {
|
|
1354
1384
|
const coordState = coord.getState();
|
|
1355
1385
|
LOG.info('MeshEvents', `Forwarding mesh event to coordinator ${coordState.instanceId}`);
|