@adhdev/daemon-core 0.9.82-rc.90 → 0.9.82-rc.92

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.90",
3
+ "version": "0.9.82-rc.92",
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",
@@ -50,6 +50,16 @@ function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: stri
50
50
  return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
51
51
  }
52
52
 
53
+ function getExplicitHistorySessionId(args: any): string | undefined {
54
+ const explicit = typeof args?.historySessionId === 'string' ? args.historySessionId.trim() : '';
55
+ if (explicit) return explicit;
56
+
57
+ const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
58
+ if (explicitProviderSessionId) return explicitProviderSessionId;
59
+
60
+ return undefined;
61
+ }
62
+
53
63
  function getTargetInstance(h: CommandHelpers, args: any): ApprovalSelectableInstance | null {
54
64
  const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
55
65
  const sessionId = targetSessionId || h.currentSession?.sessionId || '';
@@ -141,16 +151,17 @@ async function waitOnceForFreshHermesCliStart(adapter: CliAdapter, log: (msg: st
141
151
  }
142
152
 
143
153
  function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
144
- const explicit = typeof args?.historySessionId === 'string' ? args.historySessionId.trim() : '';
154
+ const explicit = getExplicitHistorySessionId(args);
145
155
  if (explicit) return explicit;
146
156
 
147
- const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
148
- if (explicitProviderSessionId) return explicitProviderSessionId;
149
-
150
157
  const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
151
158
  if (!targetSessionId) return undefined;
152
159
 
153
- const instance = h.ctx.instanceManager?.getInstance(targetSessionId);
160
+ const session = h.ctx.sessionRegistry?.get(targetSessionId) as any;
161
+ const registeredProviderSessionId = typeof session?.providerSessionId === 'string' ? session.providerSessionId.trim() : '';
162
+ if (registeredProviderSessionId) return registeredProviderSessionId;
163
+
164
+ const instance = getTargetInstance(h, args);
154
165
  const state = instance?.getState?.();
155
166
  const providerSessionId = typeof state?.providerSessionId === 'string' ? state.providerSessionId.trim() : '';
156
167
  if (providerSessionId) return providerSessionId;
@@ -166,6 +177,23 @@ function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
166
177
  return targetSessionId;
167
178
  }
168
179
 
180
+ function resolveCliNativeHistorySessionId(args: any, currentHistorySessionId: string | undefined, parsedProviderSessionId: string | undefined): string | undefined {
181
+ const explicit = getExplicitHistorySessionId(args);
182
+ if (explicit) return explicit;
183
+
184
+ const parsed = typeof parsedProviderSessionId === 'string' ? parsedProviderSessionId.trim() : '';
185
+ const current = typeof currentHistorySessionId === 'string' ? currentHistorySessionId.trim() : '';
186
+ const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
187
+
188
+ // getHistorySessionId falls back to the runtime session id when no native
189
+ // handle has been registered yet. For live CLI adapters the parser may
190
+ // already know the provider-native handle; prefer it over the runtime id so
191
+ // exact native reads do not miss the worker transcript and fall back to PTY
192
+ // or same-workspace history.
193
+ if (parsed && (!current || current === targetSessionId)) return parsed;
194
+ return current || parsed || undefined;
195
+ }
196
+
169
197
  function getInteractionId(args: any): string | undefined {
170
198
  return typeof args?._interactionId === 'string' && args._interactionId.trim()
171
199
  ? args._interactionId.trim()
@@ -368,11 +396,38 @@ function supportsCliNativeTranscript(providerType: string, provider?: ProviderMo
368
396
  return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
369
397
  }
370
398
 
399
+ function getComparableVisibleText(message: ChatMessage | undefined): string {
400
+ if (!message) return '';
401
+ const role = String((message as any).role || '').trim().toLowerCase();
402
+ if (role !== 'user' && role !== 'assistant') return '';
403
+ const kind = String((message as any).kind || 'standard').trim().toLowerCase();
404
+ if (kind && kind !== 'standard') return '';
405
+ const content = flattenContent((message as any).content).replace(/\s+/g, ' ').trim();
406
+ return content;
407
+ }
408
+
409
+ function hasOverlappingVisibleConversationText(nativeMessages: ChatMessage[], ptyMessages: ChatMessage[]): boolean {
410
+ const nativeTexts = nativeMessages.map(getComparableVisibleText).filter(Boolean);
411
+ const ptyTexts = ptyMessages.map(getComparableVisibleText).filter(Boolean);
412
+ if (nativeTexts.length === 0 || ptyTexts.length === 0) return false;
413
+ for (const nativeText of nativeTexts) {
414
+ for (const ptyText of ptyTexts) {
415
+ if (nativeText === ptyText) return true;
416
+ const shorter = nativeText.length <= ptyText.length ? nativeText : ptyText;
417
+ const longer = nativeText.length <= ptyText.length ? ptyText : nativeText;
418
+ if (shorter.length >= 32 && longer.includes(shorter)) return true;
419
+ }
420
+ }
421
+ return false;
422
+ }
423
+
371
424
  function hasSafeNativeHistoryMapping(args: {
372
425
  historySessionId?: string;
373
426
  providerSessionId?: string;
374
427
  workspace?: string;
375
428
  nativeMessages: ChatMessage[];
429
+ ptyMessages?: ChatMessage[];
430
+ requireWorkspaceContentOverlap?: boolean;
376
431
  }): boolean {
377
432
  const explicitSessionId = String(args.historySessionId || args.providerSessionId || '').trim();
378
433
  if (explicitSessionId) {
@@ -384,7 +439,10 @@ function hasSafeNativeHistoryMapping(args: {
384
439
  }
385
440
  const workspace = String(args.workspace || '').trim();
386
441
  if (!workspace) return false;
387
- return args.nativeMessages.some((message: any) => String(message?.workspace || '').trim() === workspace);
442
+ const workspaceMatches = args.nativeMessages.some((message: any) => String(message?.workspace || '').trim() === workspace);
443
+ if (!workspaceMatches) return false;
444
+ if (!args.requireWorkspaceContentOverlap) return true;
445
+ return hasOverlappingVisibleConversationText(args.nativeMessages, args.ptyMessages || []);
388
446
  }
389
447
 
390
448
  function readCliProviderNativeHistory(agentStr: string, args: {
@@ -396,6 +454,7 @@ function readCliProviderNativeHistory(agentStr: string, args: {
396
454
  excludeRecentCount: number;
397
455
  historyBehavior?: ProviderModule['historyBehavior'];
398
456
  scripts?: ProviderScripts;
457
+ exactSessionScoped?: boolean;
399
458
  }): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
400
459
  const sessionHistory = readProviderChatHistory(agentStr, {
401
460
  canonicalHistory: args.canonicalHistory,
@@ -407,8 +466,12 @@ function readCliProviderNativeHistory(agentStr: string, args: {
407
466
  historyBehavior: args.historyBehavior,
408
467
  scripts: args.scripts as any,
409
468
  });
410
- if ((sessionHistory as any).source !== 'native-unavailable' || !args.historySessionId || !args.workspace) {
411
- return { ...(sessionHistory as any), lookup: 'session' };
469
+ // Exact runtime/provider transcript reads must not silently fall back to the
470
+ // workspace's active or most recent native transcript: multiple Hermes/Gemini/
471
+ // Codex sessions can run in the same workspace, and workspace fallback can make
472
+ // read_chat/completion evidence point at a different runtime's prompt.
473
+ if ((sessionHistory as any).source !== 'native-unavailable' || args.exactSessionScoped || !args.historySessionId || !args.workspace) {
474
+ return { ...(sessionHistory as any), lookup: args.historySessionId ? 'session' : 'workspace' };
412
475
  }
413
476
  const workspaceHistory = readProviderChatHistory(agentStr, {
414
477
  canonicalHistory: args.canonicalHistory,
@@ -440,6 +503,22 @@ function shouldPreserveReadChatPayloadField(key: string): boolean {
440
503
  return key === 'messageSource' || key === 'transcriptProvenance';
441
504
  }
442
505
 
506
+ function updateMessageSourceReturnedCount(value: unknown, returnedMessageCount: number): unknown {
507
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
508
+ const record = value as Record<string, unknown>;
509
+ const coverage = record.coverage && typeof record.coverage === 'object' && !Array.isArray(record.coverage)
510
+ ? record.coverage as Record<string, unknown>
511
+ : undefined;
512
+ if (!coverage) return value;
513
+ return {
514
+ ...record,
515
+ coverage: {
516
+ ...coverage,
517
+ returnedMessageCount,
518
+ },
519
+ };
520
+ }
521
+
443
522
  function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
444
523
  const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
445
524
  if (unitKey) return `read_chat:${unitKey}`;
@@ -589,6 +668,13 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
589
668
  const visibleMessages = filterUserFacingChatMessages(messages);
590
669
  const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
591
670
  const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
671
+ const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
672
+ if (preservedPayloadFields.messageSource) {
673
+ preservedPayloadFields.messageSource = updateMessageSourceReturnedCount(preservedPayloadFields.messageSource, sync.messages.length);
674
+ }
675
+ if (preservedPayloadFields.transcriptProvenance) {
676
+ preservedPayloadFields.transcriptProvenance = updateMessageSourceReturnedCount(preservedPayloadFields.transcriptProvenance, sync.messages.length);
677
+ }
592
678
  const returnedDebugReadChat = debugReadChat
593
679
  ? {
594
680
  ...debugReadChat,
@@ -603,7 +689,7 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
603
689
  return {
604
690
  success: true,
605
691
  ...validatedPayload,
606
- ...Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key))),
692
+ ...preservedPayloadFields,
607
693
  messages: sync.messages,
608
694
  totalMessages: sync.totalMessages,
609
695
  ...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
@@ -1163,17 +1249,27 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1163
1249
  returnedMessages.length,
1164
1250
  200,
1165
1251
  );
1252
+ const nativeHistorySessionId = resolveCliNativeHistorySessionId(args, historySessionId, providerSessionId);
1253
+ const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
1254
+ const exactNativeHistoryScope = Boolean(
1255
+ (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
1256
+ || (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
1257
+ || providerSessionId
1258
+ || (nativeHistorySessionId && nativeHistorySessionId !== targetSessionId)
1259
+ || ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
1260
+ );
1166
1261
  let nativeHistory: (ReturnType<typeof readProviderChatHistory> & { lookup?: 'session' | 'workspace' }) | null = null;
1167
1262
  try {
1168
1263
  nativeHistory = readCliProviderNativeHistory(agentStr, {
1169
1264
  canonicalHistory: provider?.canonicalHistory,
1170
- historySessionId,
1265
+ historySessionId: nativeHistorySessionId,
1171
1266
  workspace,
1172
1267
  offset: 0,
1173
1268
  limit: nativeHistoryLimit,
1174
1269
  excludeRecentCount: 0,
1175
1270
  historyBehavior: provider?.historyBehavior,
1176
1271
  scripts: provider?.scripts as any,
1272
+ exactSessionScoped: exactNativeHistoryScope,
1177
1273
  });
1178
1274
  } catch (error: any) {
1179
1275
  const fallbackReason = `native_history_error:${error?.message || String(error)}`;
@@ -1194,13 +1290,15 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1194
1290
  : [];
1195
1291
  const historyProviderSessionId = typeof (nativeHistory as any)?.providerSessionId === 'string'
1196
1292
  ? (nativeHistory as any).providerSessionId
1197
- : readHistorySessionIdFromMessages(nativeMessages) || historySessionId;
1293
+ : readHistorySessionIdFromMessages(nativeMessages) || nativeHistorySessionId || historySessionId;
1198
1294
  const lookup = (nativeHistory as any).lookup === 'workspace' ? 'workspace' : 'session';
1199
1295
  const safeMapping = hasSafeNativeHistoryMapping({
1200
- historySessionId: lookup === 'workspace' ? undefined : historySessionId,
1201
- providerSessionId: lookup === 'workspace' ? undefined : providerSessionId,
1296
+ historySessionId: lookup === 'workspace' ? undefined : nativeHistorySessionId,
1297
+ providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId || providerSessionId,
1202
1298
  workspace,
1203
1299
  nativeMessages,
1300
+ ptyMessages: returnedMessages,
1301
+ requireWorkspaceContentOverlap: lookup === 'workspace' && !exactNativeHistoryScope,
1204
1302
  });
1205
1303
  const freshEnough = isNativeHistoryFreshEnough({
1206
1304
  sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
@@ -1215,7 +1313,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1215
1313
  messageSource = buildCliMessageSourceProvenance({
1216
1314
  selected: 'native-history',
1217
1315
  provider: adapter.cliType,
1218
- nativeHandle: selectedProviderSessionId || historySessionId,
1316
+ nativeHandle: selectedProviderSessionId || nativeHistorySessionId || historySessionId,
1219
1317
  nativeSource: (nativeHistory as any).source,
1220
1318
  sourcePath: (nativeHistory as any).sourcePath,
1221
1319
  sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
@@ -1238,7 +1336,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1238
1336
  messageSource = buildCliMessageSourceProvenance({
1239
1337
  selected: 'pty-parser',
1240
1338
  provider: adapter.cliType,
1241
- nativeHandle: historyProviderSessionId || historySessionId,
1339
+ nativeHandle: historyProviderSessionId || nativeHistorySessionId || historySessionId,
1242
1340
  fallbackReason,
1243
1341
  nativeSource: (nativeHistory as any).source,
1244
1342
  sourcePath: (nativeHistory as any).sourcePath,
@@ -1286,6 +1384,11 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1286
1384
  : typeof (h.currentSession as any)?.workspace === 'string'
1287
1385
  ? (h.currentSession as any).workspace
1288
1386
  : undefined;
1387
+ const exactNativeHistoryScope = Boolean(
1388
+ (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
1389
+ || (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
1390
+ || ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
1391
+ );
1289
1392
  const history = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
1290
1393
  ? readCliProviderNativeHistory(agentStr, {
1291
1394
  canonicalHistory: provider?.canonicalHistory,
@@ -1296,6 +1399,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1296
1399
  excludeRecentCount: 0,
1297
1400
  historyBehavior: provider?.historyBehavior,
1298
1401
  scripts: provider?.scripts as any,
1402
+ exactSessionScoped: exactNativeHistoryScope,
1299
1403
  })
1300
1404
  : readProviderChatHistory(agentStr, {
1301
1405
  canonicalHistory: provider?.canonicalHistory,
@@ -80,7 +80,19 @@ function sessionStatusFromNodes(nodes: any[] | undefined, nodeId?: string, sessi
80
80
  if (!node) return { staleReason: 'direct task node is no longer in the live mesh' };
81
81
  if (!sessionId) return {};
82
82
  const candidates: any[] = [];
83
- 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]) {
83
+ for (const value of [
84
+ node.sessions,
85
+ node.activeSessions,
86
+ node.active_sessions,
87
+ node.activeSessionDetails,
88
+ node.active_session_details,
89
+ node.sessionDetails,
90
+ node.session_details,
91
+ node.lastProbe?.sessions,
92
+ node.last_probe?.sessions,
93
+ node.lastProbe?.status?.sessions,
94
+ node.last_probe?.status?.sessions,
95
+ ]) {
84
96
  if (Array.isArray(value)) candidates.push(...value);
85
97
  }
86
98
  for (const value of [node.activeSession, node.active_session, node.currentSession, node.current_session, node.runtimeSession, node.runtime_session, node.session]) {
@@ -20,17 +20,10 @@ export function extractFinalSummaryFromMessages(
20
20
  }
21
21
  }
22
22
 
23
- // Fallback: last user-facing message of any role
24
- for (let i = messages.length - 1; i >= 0; i--) {
25
- const msg = messages[i];
26
- if (!msg) continue;
27
- const classification = classifyChatMessageVisibility(msg);
28
- if (classification.isUserFacing) {
29
- const text = flattenContent(msg.content).trim();
30
- if (text) return text.slice(0, maxChars);
31
- }
32
- }
33
-
23
+ // Completion summaries must describe the assistant/model result. If no
24
+ // user-facing assistant/model message exists yet (for example, only the
25
+ // dispatched user prompt is visible), return empty instead of echoing the
26
+ // prompt as a misleading finalSummary.
34
27
  return '';
35
28
  }
36
29
 
@@ -521,6 +521,7 @@ export class CliProviderInstance implements ProviderInstance {
521
521
  }
522
522
  const runtime = this.adapter.getRuntimeMetadata();
523
523
  this.maybeAppendRuntimeRecoveryMessage(runtime);
524
+ const activeChatId = this.providerSessionId || runtime?.runtimeId || this.instanceId;
524
525
  let parsedMessages = Array.isArray(parsedStatus?.messages)
525
526
  ? parsedStatus.messages
526
527
  : [];
@@ -597,7 +598,7 @@ export class CliProviderInstance implements ProviderInstance {
597
598
  status: visibleStatus,
598
599
  mode: this.presentationMode,
599
600
  activeChat: {
600
- id: `${this.type}_${this.workingDir}`,
601
+ id: activeChatId,
601
602
  title: parsedStatus?.title || dirName,
602
603
  status: activeChatStatus,
603
604
  messages: mergedMessages,