@adhdev/daemon-core 0.9.77-rc.50 → 0.9.77-rc.51

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.
@@ -8,6 +8,10 @@ export interface PendingMeshCoordinatorEvent {
8
8
  }
9
9
  /** Drain and return all pending coordinator events, clearing the queue. */
10
10
  export declare function drainPendingMeshCoordinatorEvents(): PendingMeshCoordinatorEvent[];
11
+ /** Peek at pending coordinator events without draining (non-destructive). */
12
+ export declare function getPendingMeshCoordinatorEvents(): readonly PendingMeshCoordinatorEvent[];
13
+ /** Explicitly clear all pending coordinator events. */
14
+ export declare function clearPendingMeshCoordinatorEvents(): void;
11
15
  export declare function tryAssignQueueTask(components: DaemonComponents, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
12
16
  /**
13
17
  * Triggers a queue check for all nodes in the mesh.
@@ -32,6 +32,8 @@ export interface MeshWorkQueueEntry {
32
32
  sessionId?: string;
33
33
  updatedAt: string;
34
34
  };
35
+ /** ISO timestamp when the task was dispatched (assigned) to a node/session. Used for precise matching on completion. */
36
+ dispatchTimestamp?: string;
35
37
  createdAt: string;
36
38
  updatedAt: string;
37
39
  }
@@ -1,4 +1,5 @@
1
1
  import type { ChatMessage } from '../types.js';
2
+ export declare function extractFinalSummaryFromMessages(messages: ChatMessage[] | null | undefined, maxChars?: number): string;
2
3
  export declare const BUILTIN_CHAT_MESSAGE_KINDS: readonly ["standard", "thought", "tool", "terminal", "system"];
3
4
  export type BuiltinChatMessageKind = typeof BUILTIN_CHAT_MESSAGE_KINDS[number];
4
5
  export type ChatMessageKind = BuiltinChatMessageKind | (string & {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.77-rc.50",
3
+ "version": "0.9.77-rc.51",
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",
package/src/index.ts CHANGED
@@ -160,7 +160,8 @@ export { enqueueTask, getQueue, claimNextTask, updateTaskStatus, updateSessionTa
160
160
  export type { MeshWorkQueueEntry, MeshTaskStatus, MeshWorkQueueStats } from './mesh/mesh-work-queue.js';
161
161
 
162
162
  // ── Mesh Events ──
163
- export { triggerMeshQueue } from './mesh/mesh-events.js';
163
+ export { triggerMeshQueue, drainPendingMeshCoordinatorEvents, getPendingMeshCoordinatorEvents, clearPendingMeshCoordinatorEvents } from './mesh/mesh-events.js';
164
+ export type { PendingMeshCoordinatorEvent } from './mesh/mesh-events.js';
164
165
 
165
166
  // ── Mesh P2P Relay Failure Classification ──
166
167
  export {
@@ -44,6 +44,16 @@ export function drainPendingMeshCoordinatorEvents(): PendingMeshCoordinatorEvent
44
44
  return pendingMeshCoordinatorEvents.splice(0);
45
45
  }
46
46
 
47
+ /** Peek at pending coordinator events without draining (non-destructive). */
48
+ export function getPendingMeshCoordinatorEvents(): readonly PendingMeshCoordinatorEvent[] {
49
+ return pendingMeshCoordinatorEvents.slice();
50
+ }
51
+
52
+ /** Explicitly clear all pending coordinator events. */
53
+ export function clearPendingMeshCoordinatorEvents(): void {
54
+ pendingMeshCoordinatorEvents.splice(0);
55
+ }
56
+
47
57
  function readNonEmptyString(value: unknown): string {
48
58
  return typeof value === 'string' && value.trim() ? value.trim() : '';
49
59
  }
@@ -39,6 +39,8 @@ export interface MeshWorkQueueEntry {
39
39
  sessionId?: string;
40
40
  updatedAt: string;
41
41
  };
42
+ /** ISO timestamp when the task was dispatched (assigned) to a node/session. Used for precise matching on completion. */
43
+ dispatchTimestamp?: string;
42
44
  createdAt: string;
43
45
  updatedAt: string;
44
46
  }
@@ -132,6 +134,7 @@ export function claimNextTask(meshId: string, nodeId: string, sessionId: string)
132
134
  entry.status = 'assigned';
133
135
  entry.assignedNodeId = nodeId;
134
136
  entry.assignedSessionId = sessionId;
137
+ entry.dispatchTimestamp = new Date().toISOString();
135
138
  entry.updatedAt = new Date().toISOString();
136
139
 
137
140
  writeQueue(meshId, queue);
@@ -243,17 +246,27 @@ export function updateSessionTaskStatus(
243
246
  status: MeshTaskStatus,
244
247
  ): MeshWorkQueueEntry | null {
245
248
  const queue = readQueue(meshId);
246
- // Find the most recently assigned task for this session that isn't already terminal
247
- // (In case multiple tasks were assigned to the same session over time, though rare)
249
+ // Collect all assigned tasks for this session, then pick the one with the
250
+ // most recent dispatchTimestamp (or updatedAt fallback for legacy entries).
251
+ // This prevents completing the wrong task when multiple tasks were assigned
252
+ // to the same session in rapid succession.
253
+ let bestIdx = -1;
254
+ let bestTime = 0;
248
255
  for (let i = queue.length - 1; i >= 0; i--) {
249
256
  if (queue[i].assignedSessionId === sessionId && queue[i].status === 'assigned') {
250
- queue[i].status = status;
251
- queue[i].updatedAt = new Date().toISOString();
252
- writeQueue(meshId, queue);
253
- return queue[i];
257
+ const time = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
258
+ if (time > bestTime) {
259
+ bestTime = time;
260
+ bestIdx = i;
261
+ }
254
262
  }
255
263
  }
256
- return null;
264
+ if (bestIdx === -1) return null;
265
+
266
+ queue[bestIdx].status = status;
267
+ queue[bestIdx].updatedAt = new Date().toISOString();
268
+ writeQueue(meshId, queue);
269
+ return queue[bestIdx];
257
270
  }
258
271
 
259
272
  export interface MeshWorkQueueStats {
@@ -61,6 +61,7 @@ import {
61
61
  buildToolChatMessage,
62
62
  buildUserChatMessage,
63
63
  normalizeChatMessages,
64
+ extractFinalSummaryFromMessages,
64
65
  } from './chat-message-normalization.js';
65
66
  import { LOG } from '../logging/logger.js';
66
67
  import type { ChatMessage } from '../types.js';
@@ -1507,7 +1508,7 @@ export class AcpProviderInstance implements ProviderInstance {
1507
1508
  });
1508
1509
  } else if (newStatus === 'idle' && (this.lastStatus === 'generating' || this.lastStatus === 'waiting_approval')) {
1509
1510
  const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1000) : 0;
1510
- this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now });
1511
+ this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now, finalSummary: extractFinalSummaryFromMessages(this.messages) });
1511
1512
  this.generatingStartedAt = 0;
1512
1513
  } else if (newStatus === 'stopped') {
1513
1514
  this.pushEvent({ event: 'agent:stopped', chatTitle, timestamp: now });
@@ -1,4 +1,36 @@
1
1
  import type { ChatMessage } from '../types.js';
2
+ import { flattenContent } from './contracts.js';
3
+
4
+ export function extractFinalSummaryFromMessages(
5
+ messages: ChatMessage[] | null | undefined,
6
+ maxChars: number = 500,
7
+ ): string {
8
+ if (!Array.isArray(messages) || messages.length === 0) return '';
9
+
10
+ // Find last user-facing assistant message
11
+ for (let i = messages.length - 1; i >= 0; i--) {
12
+ const msg = messages[i];
13
+ if (!msg) continue;
14
+ const classification = classifyChatMessageVisibility(msg);
15
+ if (classification.isUserFacing && (msg.role === 'assistant' || msg.role === 'model')) {
16
+ const text = flattenContent(msg.content).trim();
17
+ if (text) return text.slice(0, maxChars);
18
+ }
19
+ }
20
+
21
+ // Fallback: last user-facing message of any role
22
+ for (let i = messages.length - 1; i >= 0; i--) {
23
+ const msg = messages[i];
24
+ if (!msg) continue;
25
+ const classification = classifyChatMessageVisibility(msg);
26
+ if (classification.isUserFacing) {
27
+ const text = flattenContent(msg.content).trim();
28
+ if (text) return text.slice(0, maxChars);
29
+ }
30
+ }
31
+
32
+ return '';
33
+ }
2
34
 
3
35
  export const BUILTIN_CHAT_MESSAGE_KINDS = ['standard', 'thought', 'tool', 'terminal', 'system'] as const;
4
36
 
@@ -25,7 +25,7 @@ import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.
25
25
  import { getCliScriptCommand, parseCliScriptResult } from './cli-script-results.js';
26
26
  import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
27
27
  import { normalizeProviderSessionId } from './provider-session-id.js';
28
- import { buildChatMessage, buildRuntimeSystemChatMessage, isUserFacingChatMessage, normalizeChatMessages, resolveChatMessageKind } from './chat-message-normalization.js';
28
+ import { buildChatMessage, buildRuntimeSystemChatMessage, isUserFacingChatMessage, normalizeChatMessages, resolveChatMessageKind, extractFinalSummaryFromMessages } from './chat-message-normalization.js';
29
29
 
30
30
  type PersistableCliHistoryMessage = {
31
31
  role: string;
@@ -841,6 +841,7 @@ export class CliProviderInstance implements ProviderInstance {
841
841
  chatTitle: pending.chatTitle,
842
842
  duration: pending.duration,
843
843
  timestamp: pending.timestamp,
844
+ finalSummary: extractFinalSummaryFromMessages(this.adapter?.getScriptParsedStatus()?.messages),
844
845
  });
845
846
  this.completedDebouncePending = null;
846
847
  this.completedDebounceTimer = null;
@@ -12,7 +12,7 @@ import { buildPersistedProviderEffectMessage, normalizeProviderEffects } from '.
12
12
  import { ChatHistoryWriter } from '../config/chat-history.js';
13
13
  import type { ChatMessage } from '../types.js';
14
14
  import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
15
- import { buildChatMessage, buildRuntimeSystemChatMessage, normalizeChatMessages } from './chat-message-normalization.js';
15
+ import { buildChatMessage, buildRuntimeSystemChatMessage, normalizeChatMessages, extractFinalSummaryFromMessages } from './chat-message-normalization.js';
16
16
  import { getProviderSessionCapabilities, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE } from './open-panel-support.js';
17
17
 
18
18
  export class ExtensionProviderInstance implements ProviderInstance {
@@ -234,6 +234,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
234
234
  agentType: this.type,
235
235
  agentName: this.agentName || this.provider.name,
236
236
  extensionId: this.extensionId || this.type,
237
+ finalSummary: extractFinalSummaryFromMessages(data?.messages),
237
238
  });
238
239
  this.generatingStartedAt = 0;
239
240
  }
@@ -22,7 +22,7 @@ import { validateReadChatResultPayload } from './read-chat-contract.js';
22
22
  import type { ChatMessage } from '../types.js';
23
23
  import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.js';
24
24
  import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
25
- import { buildChatMessage, buildRuntimeSystemChatMessage, normalizeChatMessages } from './chat-message-normalization.js';
25
+ import { buildChatMessage, buildRuntimeSystemChatMessage, normalizeChatMessages, extractFinalSummaryFromMessages } from './chat-message-normalization.js';
26
26
  import { getProviderSessionCapabilities, IDE_PROVIDER_SESSION_CAPABILITIES_BASE } from './open-panel-support.js';
27
27
 
28
28
  type ReadChatModal = {
@@ -470,7 +470,7 @@ export class IdeProviderInstance implements ProviderInstance {
470
470
  } else if (agentStatus === 'idle' && (lastStatus === 'generating' || lastStatus === 'waiting_approval')) {
471
471
  const startedAt = this.generatingStartedAt.get(agentKey);
472
472
  const duration = startedAt ? Math.round((now - startedAt) / 1000) : 0;
473
- this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now, ideType: this.type });
473
+ this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now, ideType: this.type, finalSummary: extractFinalSummaryFromMessages(chatData?.messages) });
474
474
  this.generatingStartedAt.delete(agentKey);
475
475
  }
476
476