@adhdev/daemon-core 0.9.82-rc.57 → 0.9.82-rc.59

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.
@@ -0,0 +1,48 @@
1
+ import type { MeshLedgerEntry } from './mesh-ledger.js';
2
+ import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
3
+ export type MeshActiveWorkSource = 'queue' | 'direct';
4
+ export type MeshActiveWorkStatus = 'pending' | 'assigned' | 'generating' | 'idle' | 'failed' | 'awaiting_approval';
5
+ export interface MeshActiveWorkRecord {
6
+ taskId: string;
7
+ source: MeshActiveWorkSource;
8
+ status: MeshActiveWorkStatus;
9
+ nodeId?: string;
10
+ sessionId?: string;
11
+ providerType?: string;
12
+ taskTitle: string;
13
+ taskSummary: string;
14
+ message?: string;
15
+ taskMode?: string;
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ dispatchedAt?: string;
19
+ elapsedMs: number;
20
+ terminal?: boolean;
21
+ terminalKind?: string;
22
+ terminalAt?: string;
23
+ }
24
+ export interface MeshActiveWorkSummary {
25
+ totalActiveCount: number;
26
+ queueActiveCount: number;
27
+ directActiveCount: number;
28
+ awaitingApprovalCount: number;
29
+ generatingCount: number;
30
+ failedCount: number;
31
+ idleCount: number;
32
+ sourceCounts: Record<MeshActiveWorkSource, number>;
33
+ statusCounts: Record<MeshActiveWorkStatus, number>;
34
+ }
35
+ export interface BuildMeshActiveWorkOptions {
36
+ meshId: string;
37
+ queue?: MeshWorkQueueEntry[];
38
+ ledgerEntries?: MeshLedgerEntry[];
39
+ nodes?: any[];
40
+ now?: number;
41
+ /** Include terminal direct rows (idle/failed) for handoff/recent-work surfaces. Defaults false. */
42
+ includeTerminalDirect?: boolean;
43
+ }
44
+ export declare function buildMeshActiveWorkSummary(activeWork: MeshActiveWorkRecord[]): MeshActiveWorkSummary;
45
+ export declare function buildMeshActiveWork(opts: BuildMeshActiveWorkOptions): {
46
+ activeWork: MeshActiveWorkRecord[];
47
+ summary: MeshActiveWorkSummary;
48
+ };
@@ -25,6 +25,40 @@ export interface MeshLedgerEntry {
25
25
  payload: Record<string, unknown>;
26
26
  }
27
27
  export declare function isIntentionalCleanupStopEntry(entry: Pick<MeshLedgerEntry, 'kind' | 'payload'>): boolean;
28
+ export type MeshWorkerResultStatus = 'completed' | 'failed' | 'blocked' | 'partial' | 'unknown';
29
+ export type MeshProcessArtifactKind = 'process' | 'log' | 'port' | 'window' | 'session' | 'file' | 'url' | 'other';
30
+ export interface MeshValidationResultArtifact {
31
+ command?: string;
32
+ status: 'passed' | 'failed' | 'skipped' | 'unknown';
33
+ durationMs?: number;
34
+ outputPath?: string;
35
+ summary?: string;
36
+ }
37
+ export interface MeshProcessArtifact {
38
+ kind: MeshProcessArtifactKind;
39
+ id?: string;
40
+ label?: string;
41
+ locator?: string;
42
+ pid?: number;
43
+ port?: number;
44
+ url?: string;
45
+ path?: string;
46
+ sessionId?: string;
47
+ keepRunning?: boolean;
48
+ metadata?: Record<string, unknown>;
49
+ }
50
+ export interface MeshWorkerResultArtifact {
51
+ status: MeshWorkerResultStatus;
52
+ classification?: string;
53
+ changedFiles: string[];
54
+ validationResults: MeshValidationResultArtifact[];
55
+ gitStatus?: Record<string, unknown>;
56
+ processArtifacts: MeshProcessArtifact[];
57
+ errors: string[];
58
+ nextAction?: string;
59
+ requiresUserAction: boolean;
60
+ source: 'explicit_metadata' | 'final_summary_json' | 'default';
61
+ }
28
62
  export interface MeshTaskCompletionEvidence {
29
63
  source: 'agent_status_event';
30
64
  event: 'agent:generating_completed' | 'agent:ready';
@@ -38,6 +72,7 @@ export interface MeshTaskCompletionEvidence {
38
72
  providerSessionId?: string;
39
73
  finalSummaryAvailable: boolean;
40
74
  };
75
+ workerResult: MeshWorkerResultArtifact;
41
76
  git: {
42
77
  status: 'deferred';
43
78
  reason: string;
@@ -59,6 +94,7 @@ export interface BuildTaskCompletionEvidenceOptions {
59
94
  providerType?: string;
60
95
  providerSessionId?: string;
61
96
  finalSummary?: string;
97
+ workerResult?: Record<string, unknown>;
62
98
  completedAt?: string;
63
99
  }
64
100
  export interface MeshLedgerSummary {
@@ -115,6 +151,7 @@ export interface AppendRemoteLedgerResult {
115
151
  }
116
152
  export declare const MAX_LEDGER_SLICE_LIMIT = 500;
117
153
  export declare function getLedgerDir(): string;
154
+ export declare function normalizeMeshWorkerResult(input?: Record<string, unknown>, source?: MeshWorkerResultArtifact['source']): MeshWorkerResultArtifact;
118
155
  export declare function buildTaskCompletionEvidence(opts: BuildTaskCompletionEvidenceOptions): MeshTaskCompletionEvidence;
119
156
  /**
120
157
  * Append a new entry to the mesh ledger.
@@ -2,13 +2,24 @@ import type { RepoMeshDaemonRole } from '../repo-mesh-types.js';
2
2
  export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
3
3
  export type MeshActiveTaskStatus = Extract<MeshTaskStatus, 'pending' | 'assigned'>;
4
4
  export type MeshHistoricalTaskStatus = Extract<MeshTaskStatus, 'completed' | 'failed' | 'cancelled'>;
5
+ export type MeshTaskMode = 'code_change' | 'validation' | 'live_debug_readonly' | 'launch_app' | 'convergence';
5
6
  export declare const ACTIVE_MESH_QUEUE_STATUSES: MeshActiveTaskStatus[];
6
7
  export declare const HISTORICAL_MESH_QUEUE_STATUSES: MeshHistoricalTaskStatus[];
8
+ export declare const MESH_TASK_MODES: MeshTaskMode[];
9
+ export interface MeshTaskModeValidationResult {
10
+ valid: boolean;
11
+ taskMode?: MeshTaskMode;
12
+ violations: string[];
13
+ allowedOperations?: string[];
14
+ }
15
+ export declare function normalizeMeshTaskMode(value: unknown): MeshTaskMode | undefined;
16
+ export declare function validateMeshTaskModeRequest(mode: unknown, message: string): MeshTaskModeValidationResult;
7
17
  export interface MeshWorkQueueEntry {
8
18
  id: string;
9
19
  meshId: string;
10
20
  message: string;
11
21
  status: MeshTaskStatus;
22
+ taskMode?: MeshTaskMode;
12
23
  /** If specified, only this node can claim the task (used by legacy mesh_send_task) */
13
24
  targetNodeId?: string;
14
25
  /** If specified, only this runtime session can claim the task */
@@ -47,6 +58,7 @@ export interface MeshQueueMutationOptions {
47
58
  export declare function enqueueTask(meshId: string, message: string, opts?: {
48
59
  targetNodeId?: string;
49
60
  targetSessionId?: string;
61
+ taskMode?: MeshTaskMode | string;
50
62
  } & MeshQueueMutationOptions): MeshWorkQueueEntry;
51
63
  /**
52
64
  * Get all tasks in the queue, optionally filtered by status.
@@ -101,6 +101,7 @@ export declare class CliProviderInstance implements ProviderInstance {
101
101
  private completedDebouncePending;
102
102
  private enforceFreshSessionLaunchIfNeeded;
103
103
  private completionHasFinalAssistantMessage;
104
+ private buildCompletedFinalizationDiagnostic;
104
105
  private hasAdapterPendingResponse;
105
106
  private shouldSuppressStaleParsedBusyStatus;
106
107
  private getCompletedFinalizationBlockReason;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.57",
3
+ "version": "0.9.82-rc.59",
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",
@@ -36,7 +36,9 @@ export function resolveCliSpawnPlan(options: {
36
36
  : spawnConfig.command;
37
37
  const binaryPath = findBinary(configuredCommand);
38
38
  const isWin = os.platform() === 'win32';
39
- const allArgs = [...spawnConfig.args, ...extraArgs];
39
+ const allArgs = [...spawnConfig.args, ...extraArgs].map((arg) =>
40
+ typeof arg === 'string' ? arg.replace(/\{\{workingDir\}\}/g, workingDir) : arg,
41
+ );
40
42
 
41
43
  let shellCmd: string;
42
44
  let shellArgs: string[];
@@ -460,6 +460,11 @@ function reconcileInlineMeshCache(cached: any, incoming: any): any {
460
460
  const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
461
461
  if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
462
462
 
463
+ const cachedUpdatedAt = Date.parse(readStringValue(cached.updatedAt, cached.updated_at) || '');
464
+ const incomingUpdatedAt = Date.parse(readStringValue(incoming.updatedAt, incoming.updated_at) || '');
465
+ const preserveCachedMembership = Number.isFinite(cachedUpdatedAt)
466
+ && (!Number.isFinite(incomingUpdatedAt) || cachedUpdatedAt > incomingUpdatedAt);
467
+
463
468
  const cachedById = new Map<string, any>();
464
469
  for (const node of cachedNodes) {
465
470
  const nodeId = readInlineMeshNodeId(node);
@@ -469,12 +474,13 @@ function reconcileInlineMeshCache(cached: any, incoming: any): any {
469
474
  const nodes = incomingNodes.map((incomingNode: any) => {
470
475
  const nodeId = readInlineMeshNodeId(incomingNode);
471
476
  const cachedNode = nodeId ? cachedById.get(nodeId) : undefined;
477
+ if (!cachedNode && preserveCachedMembership) return null;
472
478
  if (!cachedNode) return incomingNode;
473
479
  if (hasInlineMeshTransientNodeState(incomingNode)) {
474
480
  return { ...cachedNode, ...incomingNode };
475
481
  }
476
482
  return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
477
- });
483
+ }).filter(Boolean);
478
484
 
479
485
  return {
480
486
  ...cached,
package/src/index.ts CHANGED
@@ -178,16 +178,18 @@ export { syncMeshes } from './mesh/mesh-sync.js';
178
178
  export type { MeshSyncTransport, MeshSyncResult, RemoteMeshRecord } from './mesh/mesh-sync.js';
179
179
 
180
180
  // ── Mesh Task Ledger ──
181
- export { appendLedgerEntry, appendRemoteLedgerEntries, readLedgerEntries, readLedgerSlice, getLedgerSummary, getLedgerDir, getSessionRecoveryContext, MAX_LEDGER_SLICE_LIMIT } from './mesh/mesh-ledger.js';
182
- export type { AppendRemoteLedgerResult, MeshLedgerEntry, MeshLedgerKind, MeshLedgerSlice, MeshLedgerSummary, ReadLedgerOptions, ReadLedgerSliceOptions, SessionRecoveryContext } from './mesh/mesh-ledger.js';
181
+ export { appendLedgerEntry, appendRemoteLedgerEntries, buildTaskCompletionEvidence, normalizeMeshWorkerResult, readLedgerEntries, readLedgerSlice, getLedgerSummary, getLedgerDir, getSessionRecoveryContext, MAX_LEDGER_SLICE_LIMIT } from './mesh/mesh-ledger.js';
182
+ export type { AppendRemoteLedgerResult, MeshLedgerEntry, MeshLedgerKind, MeshLedgerSlice, MeshLedgerSummary, ReadLedgerOptions, ReadLedgerSliceOptions, SessionRecoveryContext, MeshTaskCompletionEvidence, MeshWorkerResultArtifact, MeshProcessArtifact, MeshValidationResultArtifact } from './mesh/mesh-ledger.js';
183
183
  export { fastForwardMeshNode } from './mesh/mesh-fast-forward.js';
184
184
  export type { MeshFastForwardNodeArgs, MeshFastForwardPlannedStep, MeshFastForwardResult } from './mesh/mesh-fast-forward.js';
185
185
  export { buildMeshLedgerReconciliationEvidence, buildMeshLedgerReplicaEvidence } from './mesh/mesh-ledger-reconciliation.js';
186
186
  export type { MeshLedgerReconciliationEvidence, MeshLedgerReplicaEvidence, MeshLedgerReplicaStatus } from './mesh/mesh-ledger-reconciliation.js';
187
187
 
188
188
  // ── Mesh Work Queue (GUPP) ──
189
- export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask, getMeshQueueStats } from './mesh/mesh-work-queue.js';
190
- export type { MeshWorkQueueEntry, MeshTaskStatus, MeshWorkQueueStats, MeshQueueMutationOptions } from './mesh/mesh-work-queue.js';
189
+ export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTaskStatus, cancelTask, requeueTask, getMeshQueueStats, normalizeMeshTaskMode, validateMeshTaskModeRequest } from './mesh/mesh-work-queue.js';
190
+ export type { MeshWorkQueueEntry, MeshTaskStatus, MeshTaskMode, MeshWorkQueueStats, MeshQueueMutationOptions, MeshTaskModeValidationResult } from './mesh/mesh-work-queue.js';
191
+ export { buildMeshActiveWork, buildMeshActiveWorkSummary } from './mesh/mesh-active-work.js';
192
+ export type { MeshActiveWorkRecord, MeshActiveWorkStatus, MeshActiveWorkSummary, MeshActiveWorkSource } from './mesh/mesh-active-work.js';
191
193
 
192
194
  // ── Mesh Host Ownership ──
193
195
  export { buildMeshHostRequiredFailure, createDefaultMeshHostMetadata, isMeshHostOwner, normalizeMeshDaemonRole, requireMeshHostQueueOwner, resolveMeshHostStatus } from './mesh/mesh-host-ownership.js';
@@ -0,0 +1,205 @@
1
+ import type { MeshLedgerEntry } from './mesh-ledger.js';
2
+ import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
3
+
4
+ export type MeshActiveWorkSource = 'queue' | 'direct';
5
+ export type MeshActiveWorkStatus = 'pending' | 'assigned' | 'generating' | 'idle' | 'failed' | 'awaiting_approval';
6
+
7
+ export interface MeshActiveWorkRecord {
8
+ taskId: string;
9
+ source: MeshActiveWorkSource;
10
+ status: MeshActiveWorkStatus;
11
+ nodeId?: string;
12
+ sessionId?: string;
13
+ providerType?: string;
14
+ taskTitle: string;
15
+ taskSummary: string;
16
+ message?: string;
17
+ taskMode?: string;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ dispatchedAt?: string;
21
+ elapsedMs: number;
22
+ terminal?: boolean;
23
+ terminalKind?: string;
24
+ terminalAt?: string;
25
+ }
26
+
27
+ export interface MeshActiveWorkSummary {
28
+ totalActiveCount: number;
29
+ queueActiveCount: number;
30
+ directActiveCount: number;
31
+ awaitingApprovalCount: number;
32
+ generatingCount: number;
33
+ failedCount: number;
34
+ idleCount: number;
35
+ sourceCounts: Record<MeshActiveWorkSource, number>;
36
+ statusCounts: Record<MeshActiveWorkStatus, number>;
37
+ }
38
+
39
+ export interface BuildMeshActiveWorkOptions {
40
+ meshId: string;
41
+ queue?: MeshWorkQueueEntry[];
42
+ ledgerEntries?: MeshLedgerEntry[];
43
+ nodes?: any[];
44
+ now?: number;
45
+ /** Include terminal direct rows (idle/failed) for handoff/recent-work surfaces. Defaults false. */
46
+ includeTerminalDirect?: boolean;
47
+ }
48
+
49
+ const DIRECT_DISPATCH_VIA = new Set(['p2p_direct', 'local_direct', 'mesh_send_task']);
50
+ const TERMINAL_LEDGER_KINDS = new Set(['task_completed', 'task_failed', 'task_stalled']);
51
+
52
+ function readString(value: unknown): string | undefined {
53
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
54
+ }
55
+
56
+ function summarizeMessage(message: string): { title: string; summary: string } {
57
+ const oneLine = message.replace(/\s+/g, ' ').trim();
58
+ const title = oneLine.length > 96 ? `${oneLine.slice(0, 93)}...` : oneLine;
59
+ return { title: title || '(untitled task)', summary: oneLine };
60
+ }
61
+
62
+ function elapsedSince(value: string | undefined, now: number): number {
63
+ const started = value ? new Date(value).getTime() : Number.NaN;
64
+ return Number.isFinite(started) ? Math.max(0, now - started) : 0;
65
+ }
66
+
67
+ function sessionStatusFromNodes(nodes: any[] | undefined, nodeId?: string, sessionId?: string): MeshActiveWorkStatus | undefined {
68
+ if (!nodeId || !sessionId || !Array.isArray(nodes)) return undefined;
69
+ const node = nodes.find(item => readString(item?.id) === nodeId || readString(item?.nodeId) === nodeId || readString(item?.node_id) === nodeId);
70
+ if (!node) return undefined;
71
+ const candidates: any[] = [];
72
+ for (const value of [node.sessions, node.activeSessions, node.active_sessions, node.lastProbe?.sessions, node.last_probe?.sessions, node.lastProbe?.status?.sessions, node.last_probe?.status?.sessions]) {
73
+ if (Array.isArray(value)) candidates.push(...value);
74
+ }
75
+ for (const value of [node.activeSession, node.active_session, node.currentSession, node.current_session, node.runtimeSession, node.runtime_session, node.session]) {
76
+ if (value && typeof value === 'object') candidates.push(value);
77
+ }
78
+ const session = candidates.find(item => {
79
+ const id = readString(item?.id) || readString(item?.sessionId) || readString(item?.session_id) || readString(item?.runtimeSessionId) || readString(item?.instanceId);
80
+ return id === sessionId;
81
+ });
82
+ if (!session) return undefined;
83
+ const raw = `${readString(session.status) || ''} ${readString(session.lifecycle) || ''} ${readString(session.state) || ''} ${readString(session.activeChat?.status) || ''}`.toLowerCase();
84
+ if (raw.includes('approval')) return 'awaiting_approval';
85
+ if (raw.includes('generating') || raw.includes('running') || raw.includes('busy')) return 'generating';
86
+ if (raw.includes('failed') || raw.includes('stopped') || raw.includes('terminated') || raw.includes('exited')) return 'failed';
87
+ if (raw.includes('idle') || raw.includes('waiting_input') || raw.includes('ready')) return 'idle';
88
+ return undefined;
89
+ }
90
+
91
+ function isDirectDispatch(entry: MeshLedgerEntry): boolean {
92
+ if (entry.kind !== 'task_dispatched') return false;
93
+ const payload = entry.payload || {};
94
+ if (payload.source === 'direct') return true;
95
+ const via = readString(payload.via);
96
+ return Boolean(via && DIRECT_DISPATCH_VIA.has(via) && payload.source !== 'queue');
97
+ }
98
+
99
+ function directDispatchTaskId(entry: MeshLedgerEntry): string {
100
+ return readString(entry.payload?.taskId) || entry.id;
101
+ }
102
+
103
+ function terminalMatchesDispatch(terminal: MeshLedgerEntry, dispatch: MeshLedgerEntry, taskId: string): boolean {
104
+ const terminalTaskId = readString(terminal.payload?.taskId);
105
+ if (terminalTaskId && terminalTaskId === taskId) return true;
106
+ if (terminalTaskId && terminalTaskId !== taskId) return false;
107
+ if (dispatch.sessionId && terminal.sessionId === dispatch.sessionId) return true;
108
+ return Boolean(dispatch.nodeId && terminal.nodeId === dispatch.nodeId && !dispatch.sessionId);
109
+ }
110
+
111
+ function statusFromTerminal(entry: MeshLedgerEntry): MeshActiveWorkStatus {
112
+ if (entry.kind === 'task_approval_needed') return 'awaiting_approval';
113
+ if (entry.kind === 'task_completed') return 'idle';
114
+ return 'failed';
115
+ }
116
+
117
+ export function buildMeshActiveWorkSummary(activeWork: MeshActiveWorkRecord[]): MeshActiveWorkSummary {
118
+ const statusCounts: Record<MeshActiveWorkStatus, number> = {
119
+ pending: 0,
120
+ assigned: 0,
121
+ generating: 0,
122
+ idle: 0,
123
+ failed: 0,
124
+ awaiting_approval: 0,
125
+ };
126
+ const sourceCounts: Record<MeshActiveWorkSource, number> = { queue: 0, direct: 0 };
127
+ for (const item of activeWork) {
128
+ sourceCounts[item.source] += 1;
129
+ statusCounts[item.status] += 1;
130
+ }
131
+ return {
132
+ totalActiveCount: activeWork.length,
133
+ queueActiveCount: sourceCounts.queue,
134
+ directActiveCount: sourceCounts.direct,
135
+ awaitingApprovalCount: statusCounts.awaiting_approval,
136
+ generatingCount: statusCounts.generating,
137
+ failedCount: statusCounts.failed,
138
+ idleCount: statusCounts.idle,
139
+ sourceCounts,
140
+ statusCounts,
141
+ };
142
+ }
143
+
144
+ export function buildMeshActiveWork(opts: BuildMeshActiveWorkOptions): { activeWork: MeshActiveWorkRecord[]; summary: MeshActiveWorkSummary } {
145
+ const now = opts.now ?? Date.now();
146
+ const records: MeshActiveWorkRecord[] = [];
147
+
148
+ for (const task of opts.queue || []) {
149
+ if (task.status !== 'pending' && task.status !== 'assigned') continue;
150
+ const { title, summary } = summarizeMessage(task.message || '');
151
+ records.push({
152
+ taskId: task.id,
153
+ source: 'queue',
154
+ status: task.status,
155
+ nodeId: task.assignedNodeId || task.targetNodeId,
156
+ sessionId: task.assignedSessionId || task.targetSessionId,
157
+ taskTitle: title,
158
+ taskSummary: summary,
159
+ message: task.message,
160
+ taskMode: task.taskMode,
161
+ createdAt: task.createdAt,
162
+ updatedAt: task.updatedAt,
163
+ dispatchedAt: task.dispatchTimestamp,
164
+ elapsedMs: elapsedSince(task.dispatchTimestamp || task.createdAt, now),
165
+ });
166
+ }
167
+
168
+ const ledgerEntries = (opts.ledgerEntries || []).slice().sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
169
+ const terminals = ledgerEntries.filter(entry => TERMINAL_LEDGER_KINDS.has(entry.kind) || entry.kind === 'task_approval_needed');
170
+ for (const dispatch of ledgerEntries.filter(isDirectDispatch)) {
171
+ const taskId = directDispatchTaskId(dispatch);
172
+ const terminal = terminals
173
+ .filter(entry => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime())
174
+ .find(entry => terminalMatchesDispatch(entry, dispatch, taskId));
175
+ const terminalStatus = terminal ? statusFromTerminal(terminal) : undefined;
176
+ const liveStatus = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
177
+ const status = terminalStatus || liveStatus || 'assigned';
178
+ const terminalRow = Boolean(terminal && terminal.kind !== 'task_approval_needed');
179
+ if (terminalRow && opts.includeTerminalDirect !== true) continue;
180
+ const message = readString(dispatch.payload?.message) || readString(dispatch.payload?.summary) || '';
181
+ const { title, summary } = summarizeMessage(message);
182
+ records.push({
183
+ taskId,
184
+ source: 'direct',
185
+ status,
186
+ nodeId: dispatch.nodeId,
187
+ sessionId: dispatch.sessionId,
188
+ providerType: dispatch.providerType || readString(dispatch.payload?.providerType),
189
+ taskTitle: readString(dispatch.payload?.taskTitle) || title,
190
+ taskSummary: readString(dispatch.payload?.taskSummary) || summary,
191
+ message,
192
+ taskMode: readString(dispatch.payload?.taskMode),
193
+ createdAt: dispatch.timestamp,
194
+ updatedAt: terminal?.timestamp || dispatch.timestamp,
195
+ dispatchedAt: dispatch.timestamp,
196
+ elapsedMs: elapsedSince(dispatch.timestamp, now),
197
+ terminal: terminalRow,
198
+ terminalKind: terminal?.kind,
199
+ terminalAt: terminal?.timestamp,
200
+ });
201
+ }
202
+
203
+ records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
204
+ return { activeWork: records, summary: buildMeshActiveWorkSummary(records) };
205
+ }
@@ -25,6 +25,10 @@ interface RemoteIdleSession {
25
25
  const REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1000; // 5 minutes
26
26
  const remoteIdleSessions = new Map<string, RemoteIdleSession>(); // key: `${nodeId}:${sessionId}`
27
27
 
28
+ function readWorkerResultMetadata(event: Record<string, unknown>): Record<string, unknown> | undefined {
29
+ return readRecord(event.workerResult) || readRecord(event.meshWorkerResult) || readRecord(event.structuredResult);
30
+ }
31
+
28
32
  function sweepExpiredRemoteIdleSessions(): void {
29
33
  const now = Date.now();
30
34
  for (const [key, session] of remoteIdleSessions) {
@@ -107,6 +111,12 @@ function readNonEmptyString(value: unknown): string {
107
111
  return typeof value === 'string' && value.trim() ? value.trim() : '';
108
112
  }
109
113
 
114
+ function readRecord(value: unknown): Record<string, unknown> | undefined {
115
+ return value && typeof value === 'object' && !Array.isArray(value)
116
+ ? value as Record<string, unknown>
117
+ : undefined;
118
+ }
119
+
110
120
  function resolveEventSessionId(event: Record<string, unknown>, fallback?: unknown): string {
111
121
  return readNonEmptyString(event.targetSessionId)
112
122
  || readNonEmptyString(event.sessionId)
@@ -135,10 +145,21 @@ function isMeshCoordinatorEvent(eventName: unknown): eventName is string {
135
145
  }
136
146
 
137
147
  function formatCompletionMetadata(event: Record<string, unknown>): string {
148
+ const completionDiagnostic = event.completionDiagnostic && typeof event.completionDiagnostic === 'object'
149
+ ? event.completionDiagnostic as Record<string, unknown>
150
+ : null;
151
+ const diagnosticReason = completionDiagnostic
152
+ ? readNonEmptyString(completionDiagnostic.blockReason) || 'present'
153
+ : '';
154
+ const finalAssistantPresent = typeof completionDiagnostic?.finalAssistantPresent === 'boolean'
155
+ ? String(completionDiagnostic.finalAssistantPresent)
156
+ : '';
138
157
  const parts = [
139
158
  readNonEmptyString(event.targetSessionId) ? `session_id=${readNonEmptyString(event.targetSessionId)}` : '',
140
159
  readNonEmptyString(event.providerType) ? `provider=${readNonEmptyString(event.providerType)}` : '',
141
160
  readNonEmptyString(event.providerSessionId) ? `provider_session_id=${readNonEmptyString(event.providerSessionId)}` : '',
161
+ diagnosticReason ? `completion_diagnostic=${diagnosticReason}` : '',
162
+ finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : '',
142
163
  ].filter(Boolean);
143
164
  return parts.length > 0 ? ` (${parts.join('; ')})` : '';
144
165
  }
@@ -748,6 +769,9 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
748
769
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
749
770
  const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
750
771
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
772
+ const providerSessionId = readNonEmptyString(args.metadataEvent.providerSessionId) || undefined;
773
+ const finalSummary = readNonEmptyString(args.metadataEvent.finalSummary) || undefined;
774
+ const workerResult = readWorkerResultMetadata(args.metadataEvent);
751
775
  const completedTask = sessionId
752
776
  ? updateSessionTaskStatus(args.meshId, sessionId, 'completed')
753
777
  : null;
@@ -764,15 +788,17 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
764
788
  nodeLabel: args.nodeLabel,
765
789
  taskId: completedTask.id,
766
790
  completedViaReady: true,
767
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
768
- finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || undefined,
791
+ providerSessionId,
792
+ finalSummary,
793
+ workerResult,
769
794
  evidence: buildTaskCompletionEvidence({
770
795
  event: 'agent:ready',
771
796
  nodeId,
772
797
  sessionId,
773
798
  providerType: providerType || undefined,
774
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
775
- finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || undefined,
799
+ providerSessionId,
800
+ finalSummary,
801
+ workerResult,
776
802
  }),
777
803
  },
778
804
  });
@@ -815,14 +841,18 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
815
841
  const ledgerNodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined;
816
842
  const ledgerSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId) || undefined;
817
843
  const ledgerProviderType = readNonEmptyString(args.metadataEvent.providerType) || undefined;
844
+ const providerSessionId = readNonEmptyString(args.metadataEvent.providerSessionId) || undefined;
845
+ const finalSummary = readNonEmptyString(args.metadataEvent.finalSummary) || undefined;
846
+ const workerResult = readWorkerResultMetadata(args.metadataEvent);
818
847
  const completionEvidence = ledgerKind === 'task_completed' && ledgerNodeId && ledgerSessionId
819
848
  ? buildTaskCompletionEvidence({
820
849
  event: 'agent:generating_completed',
821
850
  nodeId: ledgerNodeId,
822
851
  sessionId: ledgerSessionId,
823
852
  providerType: ledgerProviderType,
824
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
825
- finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || undefined,
853
+ providerSessionId,
854
+ finalSummary,
855
+ workerResult,
826
856
  })
827
857
  : undefined;
828
858
  appendLedgerEntry(args.meshId, {
@@ -834,8 +864,12 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
834
864
  event: args.event,
835
865
  nodeLabel: args.nodeLabel,
836
866
  taskId: completedTaskForLedger?.id || undefined,
837
- providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || undefined,
838
- finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || undefined,
867
+ providerSessionId,
868
+ finalSummary,
869
+ workerResult,
870
+ completionDiagnostic: args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === 'object'
871
+ ? args.metadataEvent.completionDiagnostic
872
+ : undefined,
839
873
  evidence: completionEvidence,
840
874
  },
841
875
  });