@adhdev/daemon-core 0.9.82-rc.111 → 0.9.82-rc.113

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.
@@ -128,6 +128,8 @@ export declare class CliProviderInstance implements ProviderInstance {
128
128
  private mergeConversationMessages;
129
129
  private formatApprovalRequestMessage;
130
130
  private promoteProviderSessionId;
131
+ private shouldHydrateExistingProviderHistory;
132
+ private shouldSuppressFreshLaunchStartupReplay;
131
133
  private syncCanonicalSavedHistoryIfNeeded;
132
134
  private restorePersistedHistoryFromCurrentSession;
133
135
  private getProbeDirectories;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.111",
3
+ "version": "0.9.82-rc.113",
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",
@@ -17,6 +17,7 @@ export interface ChatTailSubscriptionCursor {
17
17
  export type SessionChatTailCommandResult = Partial<Omit<ReadChatSyncResult, 'activeModal'>> & {
18
18
  success?: boolean
19
19
  activeModal?: unknown
20
+ messagesTail?: unknown
20
21
  }
21
22
 
22
23
  export interface PrepareSessionChatTailUpdateInput {
@@ -102,7 +103,10 @@ export function prepareSessionChatTailUpdate(
102
103
  }
103
104
  }
104
105
 
105
- const fullMessages = normalizeChatMessages(Array.isArray(result.messages) ? result.messages as any[] : [])
106
+ const rawMessages = Array.isArray(result.messages)
107
+ ? result.messages as any[]
108
+ : (Array.isArray(result.messagesTail) ? result.messagesTail as any[] : [])
109
+ const fullMessages = normalizeChatMessages(rawMessages)
106
110
  const messages = fullMessages
107
111
  const title = typeof result.title === 'string' ? result.title : undefined
108
112
  const activeModal = normalizeChatTailActiveModal(result.activeModal)
@@ -1165,16 +1165,55 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
1165
1165
  : typeof (h.currentSession as any)?.workspace === 'string'
1166
1166
  ? (h.currentSession as any).workspace
1167
1167
  : undefined;
1168
- const result = readProviderChatHistory(agentStr, {
1169
- canonicalHistory: provider?.canonicalHistory,
1170
- historySessionId,
1171
- workspace,
1172
- offset: offset || 0,
1173
- limit: limit || 30,
1174
- excludeRecentCount,
1175
- historyBehavior: provider?.historyBehavior,
1176
- scripts: provider?.scripts as any,
1177
- });
1168
+ const exactNativeHistoryScope = Boolean(
1169
+ (typeof args?.targetSessionId === 'string' && args.targetSessionId.trim())
1170
+ || (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
1171
+ || (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
1172
+ );
1173
+ const result = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
1174
+ ? readCliProviderNativeHistory(agentStr, {
1175
+ canonicalHistory: provider?.canonicalHistory,
1176
+ historySessionId,
1177
+ workspace,
1178
+ offset: offset || 0,
1179
+ limit: limit || 30,
1180
+ excludeRecentCount,
1181
+ historyBehavior: provider?.historyBehavior,
1182
+ scripts: provider?.scripts as any,
1183
+ exactSessionScoped: exactNativeHistoryScope,
1184
+ })
1185
+ : readProviderChatHistory(agentStr, {
1186
+ canonicalHistory: provider?.canonicalHistory,
1187
+ historySessionId,
1188
+ workspace,
1189
+ offset: offset || 0,
1190
+ limit: limit || 30,
1191
+ excludeRecentCount,
1192
+ historyBehavior: provider?.historyBehavior,
1193
+ scripts: provider?.scripts as any,
1194
+ });
1195
+ if (supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)) {
1196
+ const lookup = (result as any).lookup === 'workspace' ? 'workspace' : 'session';
1197
+ const messages = Array.isArray((result as any).messages) ? normalizeNativeHistoryMessages(agentStr, (result as any).messages as ChatMessage[]) : [];
1198
+ const historyProviderSessionId = typeof (result as any)?.providerSessionId === 'string'
1199
+ ? (result as any).providerSessionId
1200
+ : readHistorySessionIdFromMessages(messages) || historySessionId;
1201
+ const safeMapping = hasSafeNativeHistoryMapping({
1202
+ historySessionId: lookup === 'workspace' ? undefined : historySessionId,
1203
+ providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
1204
+ workspace,
1205
+ nativeMessages: messages,
1206
+ });
1207
+ if ((result as any).source === 'provider-native' && messages.length > 0 && !safeMapping) {
1208
+ return {
1209
+ success: true,
1210
+ messages: [],
1211
+ hasMore: false,
1212
+ source: 'native-unavailable',
1213
+ agent: agentStr,
1214
+ };
1215
+ }
1216
+ }
1178
1217
  return { success: true, ...result, agent: agentStr };
1179
1218
  } catch (e: any) {
1180
1219
  return { success: false, error: e.message };
@@ -92,12 +92,18 @@ export function buildSessionReadStateKey(sessionId: string, providerSessionId?:
92
92
 
93
93
  export function getSessionSeenAt(state: DaemonState, sessionId: string, providerSessionId?: string | null): number {
94
94
  const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
95
- return state.sessionReads?.[providerKey] || state.sessionReads?.[sessionId] || 0;
95
+ return Math.max(state.sessionReads?.[providerKey] || 0, state.sessionReads?.[sessionId] || 0);
96
96
  }
97
97
 
98
98
  export function getSessionSeenMarker(state: DaemonState, sessionId: string, providerSessionId?: string | null): string {
99
99
  const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
100
- return state.sessionReadMarkers?.[providerKey] || state.sessionReadMarkers?.[sessionId] || '';
100
+ const providerSeenAt = state.sessionReads?.[providerKey] || 0;
101
+ const sessionSeenAt = state.sessionReads?.[sessionId] || 0;
102
+ const providerMarker = state.sessionReadMarkers?.[providerKey] || '';
103
+ const sessionMarker = state.sessionReadMarkers?.[sessionId] || '';
104
+ if (sessionSeenAt > providerSeenAt && sessionMarker) return sessionMarker;
105
+ if (providerSeenAt > sessionSeenAt && providerMarker) return providerMarker;
106
+ return providerMarker || sessionMarker;
101
107
  }
102
108
 
103
109
  export function getSessionNotificationDismissal(state: DaemonState, sessionId: string, providerSessionId?: string | null): string {
@@ -43,6 +43,18 @@ type CompletedDebouncePending = {
43
43
  loggedBlockReason?: string;
44
44
  };
45
45
 
46
+ function isIdleStatus(value: unknown): boolean {
47
+ const status = typeof value === 'string' ? value.trim().toLowerCase() : '';
48
+ return !status || status === 'idle' || status === 'ready';
49
+ }
50
+
51
+ function getMessageTime(message: unknown): number {
52
+ if (!message || typeof message !== 'object') return 0;
53
+ const record = message as { receivedAt?: unknown; timestamp?: unknown };
54
+ const value = Number(record.receivedAt ?? record.timestamp ?? 0);
55
+ return Number.isFinite(value) ? value : 0;
56
+ }
57
+
46
58
  type CompletedFinalizationBlock = {
47
59
  reason: string;
48
60
  terminal?: boolean;
@@ -417,7 +429,7 @@ export class CliProviderInstance implements ProviderInstance {
417
429
  await this.adapter.spawn();
418
430
  await this.enforceFreshSessionLaunchIfNeeded();
419
431
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
420
- if (this.providerSessionId) {
432
+ if (this.providerSessionId && this.shouldHydrateExistingProviderHistory()) {
421
433
  this.restorePersistedHistoryFromCurrentSession();
422
434
  }
423
435
  if (this.providerSessionId && this.launchMode === 'resume') {
@@ -518,26 +530,35 @@ export class CliProviderInstance implements ProviderInstance {
518
530
  this.provider,
519
531
  typeof adapterStatus?.providerSessionId === 'string' ? adapterStatus.providerSessionId : '',
520
532
  );
521
- if (adapterProviderSessionId) {
522
- this.promoteProviderSessionId(adapterProviderSessionId);
523
- }
524
533
  const autoApproveActive = this.maybeAutoApproveStatus(adapterStatus, Date.now());
525
534
  const visibleStatus = parseErrorMessage || parsedStatus?.status === 'error'
526
535
  ? 'error'
527
536
  : (autoApproveActive ? 'generating' : adapterStatus.status);
537
+ const runtime = this.adapter.getRuntimeMetadata();
538
+ this.maybeAppendRuntimeRecoveryMessage(runtime);
539
+ let parsedMessages = Array.isArray(parsedStatus?.messages)
540
+ ? parsedStatus.messages
541
+ : [];
528
542
  const parsedProviderSessionId = normalizeProviderSessionId(
529
543
  this.provider,
530
544
  typeof parsedStatus?.providerSessionId === 'string' ? parsedStatus.providerSessionId : '',
531
545
  );
532
- if (parsedProviderSessionId) {
546
+ const suppressFreshLaunchStartupReplay = this.shouldSuppressFreshLaunchStartupReplay(
547
+ parsedMessages,
548
+ parsedStatus,
549
+ adapterStatus,
550
+ parsedProviderSessionId,
551
+ );
552
+ if (adapterProviderSessionId && !suppressFreshLaunchStartupReplay) {
553
+ this.promoteProviderSessionId(adapterProviderSessionId);
554
+ }
555
+ if (parsedProviderSessionId && !suppressFreshLaunchStartupReplay) {
533
556
  this.promoteProviderSessionId(parsedProviderSessionId);
534
557
  }
535
- const runtime = this.adapter.getRuntimeMetadata();
536
- this.maybeAppendRuntimeRecoveryMessage(runtime);
558
+ if (suppressFreshLaunchStartupReplay) {
559
+ parsedMessages = [];
560
+ }
537
561
  const activeChatId = this.providerSessionId || runtime?.runtimeId || this.instanceId;
538
- let parsedMessages = Array.isArray(parsedStatus?.messages)
539
- ? parsedStatus.messages
540
- : [];
541
562
  const historyMessageCount = Number.isFinite(parsedStatus?.historyMessageCount)
542
563
  ? Math.max(0, Number(parsedStatus.historyMessageCount))
543
564
  : null;
@@ -547,7 +568,9 @@ export class CliProviderInstance implements ProviderInstance {
547
568
  : [];
548
569
  }
549
570
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
550
- const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
571
+ const canonicalBackedHistory = this.shouldHydrateExistingProviderHistory()
572
+ ? this.syncCanonicalSavedHistoryIfNeeded()
573
+ : false;
551
574
  const statusMessages = canonicalBackedHistory && this.lastPersistedHistoryMessages.length > 0
552
575
  ? this.lastPersistedHistoryMessages.map((message) => ({
553
576
  role: message.role,
@@ -600,7 +623,12 @@ export class CliProviderInstance implements ProviderInstance {
600
623
  }
601
624
  }
602
625
 
603
- this.applyProviderResponse(parsedStatus, { phase: 'immediate' });
626
+ this.applyProviderResponse(
627
+ suppressFreshLaunchStartupReplay && parsedStatus && typeof parsedStatus === 'object'
628
+ ? { ...parsedStatus, providerSessionId: undefined }
629
+ : parsedStatus,
630
+ { phase: 'immediate' },
631
+ );
604
632
  const surface = resolveProviderStateSurface({
605
633
  summaryMetadata: this.summaryMetadata as any,
606
634
  controlValues: this.controlValues,
@@ -1466,7 +1494,9 @@ export class CliProviderInstance implements ProviderInstance {
1466
1494
  this.providerSessionId = nextSessionId;
1467
1495
  this.historyWriter.promoteHistorySession(this.type, previousHistorySessionId, nextSessionId);
1468
1496
  this.historyWriter.writeSessionStart(this.type, nextSessionId, this.workingDir, this.instanceId);
1469
- this.restorePersistedHistoryFromCurrentSession();
1497
+ if (this.shouldHydrateExistingProviderHistory()) {
1498
+ this.restorePersistedHistoryFromCurrentSession();
1499
+ }
1470
1500
  this.adapter.updateRuntimeMeta({ providerSessionId: nextSessionId });
1471
1501
  this.onProviderSessionResolved?.({
1472
1502
  instanceId: this.instanceId,
@@ -1479,6 +1509,24 @@ export class CliProviderInstance implements ProviderInstance {
1479
1509
  LOG.info('CLI', `[${this.type}] discovered provider session id: ${nextSessionId}`);
1480
1510
  }
1481
1511
 
1512
+ private shouldHydrateExistingProviderHistory(): boolean {
1513
+ return this.launchMode === 'resume' || this.launchMode === 'manual';
1514
+ }
1515
+
1516
+ private shouldSuppressFreshLaunchStartupReplay(parsedMessages: unknown[], parsedStatus: any, adapterStatus: any, parsedProviderSessionId = ''): boolean {
1517
+ if (this.launchMode !== 'new') return false;
1518
+ if (this.providerSessionId) return false;
1519
+ if (!Array.isArray(parsedMessages) || parsedMessages.length === 0) return false;
1520
+ if (!isIdleStatus(adapterStatus?.status) || !isIdleStatus(parsedStatus?.status)) return false;
1521
+ if (parsedProviderSessionId) return true;
1522
+
1523
+ const newestMessageAt = parsedMessages.reduce<number>((newest, message) => Math.max(newest, getMessageTime(message)), 0);
1524
+
1525
+ // Untimestamped idle parser output during a fresh launch is usually the
1526
+ // provider's last workspace transcript before a new turn exists.
1527
+ return newestMessageAt === 0;
1528
+ }
1529
+
1482
1530
  private syncCanonicalSavedHistoryIfNeeded(): boolean {
1483
1531
  if (!this.providerSessionId) return false;
1484
1532
  const canonicalHistory = this.provider.canonicalHistory;