@adhdev/daemon-core 0.9.37 → 0.9.39

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "description": "ADHDev local session host core \u2014 session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1053,6 +1053,17 @@ export class ProviderCliAdapter implements CliAdapter {
1053
1053
  this.resolveStartupState('settled');
1054
1054
  if (this.startupParseGate) return;
1055
1055
 
1056
+ if (!this.isWaitingForResponse && !this.currentTurnScope && !this.activeModal && !this.parseErrorMessage) {
1057
+ const tail = this.settledBuffer || this.recentOutputBuffer;
1058
+ const modal = this.runParseApproval(tail);
1059
+ const lightweightStatus = this.cliScripts?.detectStatus
1060
+ ? this.runDetectStatus(tail)
1061
+ : null;
1062
+ if (!modal && lightweightStatus === 'idle' && this.currentStatus === 'idle') {
1063
+ return;
1064
+ }
1065
+ }
1066
+
1056
1067
  const session = this.runParseSession();
1057
1068
  if (!session) return;
1058
1069
 
@@ -48,6 +48,13 @@ function buildPersistableCliHistorySignature(message: PersistableCliHistoryMessa
48
48
  ].join('|');
49
49
  }
50
50
 
51
+ function hasSamePersistableCliHistoryIdentity(a: PersistableCliHistoryMessage, b: PersistableCliHistoryMessage): boolean {
52
+ return String(a?.role || '') === String(b?.role || '')
53
+ && String(a?.kind || '') === String(b?.kind || '')
54
+ && String(a?.senderName || '') === String(b?.senderName || '')
55
+ && String(a?.content || '') === String(b?.content || '');
56
+ }
57
+
51
58
  export function buildIncrementalHistoryAppendMessages(
52
59
  previousMessages: PersistableCliHistoryMessage[],
53
60
  currentMessages: PersistableCliHistoryMessage[],
@@ -55,20 +62,32 @@ export function buildIncrementalHistoryAppendMessages(
55
62
  if (!Array.isArray(currentMessages) || currentMessages.length === 0) return [];
56
63
  if (!Array.isArray(previousMessages) || previousMessages.length === 0) return currentMessages;
57
64
 
58
- const previousSignatures = previousMessages.map(buildPersistableCliHistorySignature);
59
- const currentSignatures = currentMessages.map(buildPersistableCliHistorySignature);
60
-
65
+ const comparableLength = Math.min(previousMessages.length, currentMessages.length);
61
66
  let sharedPrefixLength = 0;
62
67
  while (
63
- sharedPrefixLength < previousSignatures.length
64
- && sharedPrefixLength < currentSignatures.length
65
- && previousSignatures[sharedPrefixLength] === currentSignatures[sharedPrefixLength]
68
+ sharedPrefixLength < comparableLength
69
+ && hasSamePersistableCliHistoryIdentity(previousMessages[sharedPrefixLength], currentMessages[sharedPrefixLength])
66
70
  ) {
67
71
  sharedPrefixLength += 1;
68
72
  }
69
73
 
70
- if (sharedPrefixLength === currentSignatures.length) return [];
71
- if (sharedPrefixLength === previousSignatures.length) return currentMessages.slice(sharedPrefixLength);
74
+ if (sharedPrefixLength === currentMessages.length) return [];
75
+ if (sharedPrefixLength === previousMessages.length) return currentMessages.slice(sharedPrefixLength);
76
+
77
+ // Rare fallback: preserve the older whitespace-normalized behavior only when
78
+ // the cheap identity check detects a changed prefix. Recomputing normalized
79
+ // signatures for the full transcript on every idle status poll was a CPU
80
+ // hot path for long CLI sessions.
81
+ while (
82
+ sharedPrefixLength < comparableLength
83
+ && buildPersistableCliHistorySignature(previousMessages[sharedPrefixLength])
84
+ === buildPersistableCliHistorySignature(currentMessages[sharedPrefixLength])
85
+ ) {
86
+ sharedPrefixLength += 1;
87
+ }
88
+
89
+ if (sharedPrefixLength === currentMessages.length) return [];
90
+ if (sharedPrefixLength === previousMessages.length) return currentMessages.slice(sharedPrefixLength);
72
91
  return currentMessages;
73
92
  }
74
93
 
@@ -418,13 +437,15 @@ export class CliProviderInstance implements ProviderInstance {
418
437
  }));
419
438
  if (!canonicalBackedHistory && !shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
420
439
  const incrementalMessages = buildIncrementalHistoryAppendMessages(this.lastPersistedHistoryMessages, normalizedMessagesToSave);
421
- this.historyWriter.appendNewMessages(
422
- this.type,
423
- incrementalMessages,
424
- parsedStatus?.title || dirName,
425
- this.instanceId,
426
- this.providerSessionId,
427
- );
440
+ if (incrementalMessages.length > 0) {
441
+ this.historyWriter.appendNewMessages(
442
+ this.type,
443
+ incrementalMessages,
444
+ parsedStatus?.title || dirName,
445
+ this.instanceId,
446
+ this.providerSessionId,
447
+ );
448
+ }
428
449
  }
429
450
  if (!canonicalBackedHistory) {
430
451
  this.lastPersistedHistoryMessages = normalizedMessagesToSave;
@@ -578,8 +599,11 @@ export class CliProviderInstance implements ProviderInstance {
578
599
 
579
600
  private detectStatusTransition(): void {
580
601
  const now = Date.now();
581
- const adapterStatus = this.adapter.getStatus();
582
- const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
602
+ // Status-change handling is a hot path: PTY output can fire it many times
603
+ // during long-running CLI sessions. Keep this path on adapter-owned light
604
+ // state only; rich provider parsing is reserved for getState/read_chat.
605
+ const adapterStatus = this.adapter.getStatus({ allowParse: false });
606
+ const parsedStatus = null;
583
607
  const rawStatus = adapterStatus.status;
584
608
  const autoApproveActive = rawStatus === 'waiting_approval' && this.shouldAutoApprove();
585
609
  // Guard re-entry: onStatusChange can fire multiple times while the modal
@@ -675,7 +699,7 @@ export class CliProviderInstance implements ProviderInstance {
675
699
  this.completedDebouncePending = { chatTitle, duration, timestamp: now };
676
700
  this.completedDebounceTimer = setTimeout(() => {
677
701
  if (this.completedDebouncePending) {
678
- const latestStatus = this.adapter.getStatus();
702
+ const latestStatus = this.adapter.getStatus({ allowParse: false });
679
703
  const latestAutoApproveActive = latestStatus.status === 'waiting_approval' && this.shouldAutoApprove();
680
704
  const latestVisibleStatus = latestAutoApproveActive ? 'generating' : latestStatus.status;
681
705
  if (latestVisibleStatus !== 'idle') {