@adhdev/daemon-core 0.9.82-rc.69 → 0.9.82-rc.70

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.69",
3
+ "version": "0.9.82-rc.70",
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",
@@ -761,6 +761,17 @@ export class ProviderCliAdapter implements CliAdapter {
761
761
  if (stableMs < 2000) return;
762
762
 
763
763
  const startupModal = this.runParseApproval(this.recentOutputBuffer);
764
+ const startupStatus = this.runDetectStatus(screenText || this.recentOutputBuffer);
765
+ if (!startupModal && startupStatus !== 'idle') {
766
+ this.recordTrace('startup_settle_deferred', {
767
+ trigger,
768
+ startupStatus,
769
+ stableMs,
770
+ screenText: summarizeCliTraceText(screenText, 500),
771
+ });
772
+ this.scheduleStartupSettleCheck();
773
+ return;
774
+ }
764
775
  this.startupParseGate = false;
765
776
  if (this.startupSettleTimer) {
766
777
  clearTimeout(this.startupSettleTimer);
@@ -956,6 +967,38 @@ export class ProviderCliAdapter implements CliAdapter {
956
967
  return true;
957
968
  }
958
969
 
970
+ private clearParsedIdleResponseGuard(reason: string, parsedStatus: any): boolean {
971
+ const parsedRawStatus = typeof parsedStatus?.status === 'string' ? parsedStatus.status.trim() : '';
972
+ const parsedModal = parsedStatus?.activeModal ?? parsedStatus?.modal ?? null;
973
+ const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
974
+ if (
975
+ !this.isWaitingForResponse
976
+ || parsedRawStatus !== 'idle'
977
+ || !!parsedModal
978
+ || !!blockingModal
979
+ || !this.parsedStatusHasFinalAssistantMessage(parsedStatus)
980
+ ) {
981
+ return false;
982
+ }
983
+ this.clearAllTimers();
984
+ this.clearIdleFinishCandidate(reason);
985
+ this.responseBuffer = '';
986
+ this.isWaitingForResponse = false;
987
+ this.responseSettleIgnoreUntil = 0;
988
+ this.submitRetryUsed = false;
989
+ this.submitRetryPromptSnippet = '';
990
+ this.finishRetryCount = 0;
991
+ this.currentTurnScope = null;
992
+ this.activeModal = null;
993
+ this.setStatus('idle', reason);
994
+ this.recordTrace('parsed_idle_response_cleared', {
995
+ reason,
996
+ parsedStatus: parsedRawStatus,
997
+ parsedMessageCount: Array.isArray(parsedStatus?.messages) ? parsedStatus.messages.length : 0,
998
+ });
999
+ return true;
1000
+ }
1001
+
959
1002
  private hasMeaningfulResponseBuffer(promptSnippet: string): boolean {
960
1003
  const raw = String(this.responseBuffer || '').trim();
961
1004
  if (!raw) return false;
@@ -1489,6 +1532,7 @@ export class ProviderCliAdapter implements CliAdapter {
1489
1532
  accumulatedRawBuffer: this.accumulatedRawBuffer,
1490
1533
  recentOutputBuffer: this.recentOutputBuffer,
1491
1534
  terminalScreenText: parseScreenText,
1535
+ workingDir: this.workingDir,
1492
1536
  baseMessages: [],
1493
1537
  partialResponse: this.responseBuffer,
1494
1538
  isWaitingForResponse: this.isWaitingForResponse,
@@ -1552,6 +1596,15 @@ export class ProviderCliAdapter implements CliAdapter {
1552
1596
  return !!(startupModal || this.activeModal);
1553
1597
  }
1554
1598
 
1599
+ private parsedStatusHasFinalAssistantMessage(parsed: any): boolean {
1600
+ const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
1601
+ const lastAssistant = [...messages].reverse().find((message: any) => {
1602
+ if (!message || message.role !== 'assistant') return false;
1603
+ return typeof message.content === 'string' && message.content.trim().length > 0;
1604
+ });
1605
+ return !!lastAssistant;
1606
+ }
1607
+
1555
1608
  private projectEffectiveStatus(startupModal: { message: string; buttons: string[] } | null = null): CliSessionStatus['status'] {
1556
1609
  if (this.parseErrorMessage) return 'error';
1557
1610
  if (this.hasActionableApproval(startupModal)) return 'waiting_approval';
@@ -1564,8 +1617,14 @@ export class ProviderCliAdapter implements CliAdapter {
1564
1617
  getStatus(options: { allowParse?: boolean } = {}): CliSessionStatus {
1565
1618
  const allowParse = options.allowParse !== false;
1566
1619
  const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
1620
+ const startupDetectedStatus = allowParse && this.startupParseGate && !startupModal
1621
+ ? this.runDetectStatus(this.recentOutputBuffer || this.terminalScreen.getText())
1622
+ : null;
1567
1623
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
1568
1624
  let effectiveModal = startupModal || this.activeModal;
1625
+ if (startupDetectedStatus === 'waiting_approval') {
1626
+ effectiveStatus = 'waiting_approval';
1627
+ }
1569
1628
  if (allowParse && !startupModal && !effectiveModal) {
1570
1629
  const parsed = this.getFreshParsedStatusCache();
1571
1630
  const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons)
@@ -1575,6 +1634,18 @@ export class ProviderCliAdapter implements CliAdapter {
1575
1634
  if (parsed?.status === 'waiting_approval' && parsedModal) {
1576
1635
  effectiveStatus = 'waiting_approval';
1577
1636
  effectiveModal = parsedModal;
1637
+ } else if (
1638
+ effectiveStatus === 'idle'
1639
+ && parsed?.status === 'generating'
1640
+ && !this.parsedStatusHasFinalAssistantMessage(parsed)
1641
+ ) {
1642
+ effectiveStatus = 'generating';
1643
+ } else if (
1644
+ effectiveStatus === 'generating'
1645
+ && parsed?.status === 'idle'
1646
+ && this.parsedStatusHasFinalAssistantMessage(parsed)
1647
+ ) {
1648
+ effectiveStatus = 'idle';
1578
1649
  }
1579
1650
  }
1580
1651
  const bufferState = this.getBufferState();
@@ -1665,6 +1736,7 @@ export class ProviderCliAdapter implements CliAdapter {
1665
1736
  accumulatedRawBuffer: this.accumulatedRawBuffer,
1666
1737
  recentOutputBuffer: this.recentOutputBuffer,
1667
1738
  terminalScreenText: this.getParseScreenText(this.terminalScreen.getText()),
1739
+ workingDir: this.workingDir,
1668
1740
  baseMessages: [],
1669
1741
  partialResponse: this.responseBuffer,
1670
1742
  isWaitingForResponse: this.isWaitingForResponse,
@@ -1979,7 +2051,10 @@ export class ProviderCliAdapter implements CliAdapter {
1979
2051
  }
1980
2052
  }
1981
2053
  if (this.isWaitingForResponse && !allowInputDuringGeneration) {
1982
- if (!this.clearStaleIdleResponseGuard('send_message_guard')) {
2054
+ if (
2055
+ !this.clearStaleIdleResponseGuard('send_message_guard')
2056
+ && !this.clearParsedIdleResponseGuard('send_message_parsed_idle_guard', parsedStatusBeforeSend)
2057
+ ) {
1983
2058
  throw new Error(`${this.cliName} is still processing the previous prompt`);
1984
2059
  }
1985
2060
  }
@@ -2369,10 +2444,23 @@ export class ProviderCliAdapter implements CliAdapter {
2369
2444
  getDebugState(): Record<string, any> {
2370
2445
  const screenText = sanitizeTerminalText(this.terminalScreen.getText());
2371
2446
  const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
2372
- const effectiveStatus = this.projectEffectiveStatus(startupModal);
2373
- const effectiveReady = this.ready || !!startupModal;
2447
+ const startupDetectedStatus = this.startupParseGate && !startupModal
2448
+ ? this.runDetectStatus(this.recentOutputBuffer || screenText)
2449
+ : null;
2450
+ const effectiveReady = this.ready || !!startupModal || startupDetectedStatus === 'waiting_approval';
2374
2451
  const parsedDebugState = this.getParsedDebugState();
2375
2452
  const parsedMessages = Array.isArray(parsedDebugState?.messages) ? parsedDebugState.messages : [];
2453
+ let effectiveStatus = this.projectEffectiveStatus(startupModal);
2454
+ if (startupDetectedStatus === 'waiting_approval') {
2455
+ effectiveStatus = 'waiting_approval';
2456
+ }
2457
+ if (
2458
+ effectiveStatus === 'idle'
2459
+ && parsedDebugState?.status === 'generating'
2460
+ && !this.parsedStatusHasFinalAssistantMessage(parsedDebugState)
2461
+ ) {
2462
+ effectiveStatus = 'generating';
2463
+ }
2376
2464
  return {
2377
2465
  type: this.cliType,
2378
2466
  name: this.cliName,
@@ -15,6 +15,7 @@ export declare function buildCliParseInput(options: {
15
15
  accumulatedRawBuffer: string;
16
16
  recentOutputBuffer: string;
17
17
  terminalScreenText: string;
18
+ workingDir?: string;
18
19
  baseMessages: CliChatMessage[];
19
20
  partialResponse: string;
20
21
  isWaitingForResponse?: boolean;
@@ -35,6 +35,7 @@ export function buildCliParseInput(options: {
35
35
  accumulatedRawBuffer: string;
36
36
  recentOutputBuffer: string;
37
37
  terminalScreenText: string;
38
+ workingDir?: string;
38
39
  baseMessages: CliChatMessage[];
39
40
  partialResponse: string;
40
41
  isWaitingForResponse?: boolean;
@@ -46,6 +47,7 @@ export function buildCliParseInput(options: {
46
47
  accumulatedRawBuffer,
47
48
  recentOutputBuffer,
48
49
  terminalScreenText,
50
+ workingDir,
49
51
  baseMessages,
50
52
  partialResponse,
51
53
  isWaitingForResponse,
@@ -66,6 +68,8 @@ export function buildCliParseInput(options: {
66
68
  rawBuffer,
67
69
  recentBuffer,
68
70
  screenText,
71
+ workspace: workingDir,
72
+ workingDir,
69
73
  screen: buildCliScreenSnapshot(screenText),
70
74
  bufferScreen: buildCliScreenSnapshot(buffer),
71
75
  recentScreen: buildCliScreenSnapshot(recentBuffer),
@@ -55,6 +55,8 @@ export interface CliScriptInput {
55
55
  rawBuffer: string;
56
56
  recentBuffer: string;
57
57
  screenText: string;
58
+ workspace?: string;
59
+ workingDir?: string;
58
60
  screen: CliScreenSnapshot;
59
61
  bufferScreen: CliScreenSnapshot;
60
62
  recentScreen: CliScreenSnapshot;
@@ -94,6 +94,8 @@ export interface CliScriptInput {
94
94
  rawBuffer: string;
95
95
  recentBuffer: string;
96
96
  screenText: string;
97
+ workspace?: string;
98
+ workingDir?: string;
97
99
  screen: CliScreenSnapshot;
98
100
  bufferScreen: CliScreenSnapshot;
99
101
  recentScreen: CliScreenSnapshot;
@@ -408,12 +408,19 @@ export class DaemonCommandHandler implements CommandHelpers {
408
408
  'invoke_provider_script',
409
409
  ]);
410
410
 
411
+ // read_chat and get_chat_debug_bundle can serve historical transcript data even
412
+ // when the live session record is gone (stopped/destroyed). Allow the fallback
413
+ // when the provider type is known and any session identity hint is present:
414
+ // an explicit providerSessionId/historySessionId, or the targetSessionId itself
415
+ // (which getHistorySessionId already uses as a fallback history key).
416
+ const isReadOrDebugCmd = cmd === 'read_chat' || cmd === 'get_chat_debug_bundle';
411
417
  const allowsInactiveReadChatFallback =
412
- cmd === 'read_chat'
418
+ isReadOrDebugCmd
413
419
  && !!this._currentRoute.providerType
414
420
  && (
415
421
  (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim().length > 0)
416
422
  || (typeof args?.historySessionId === 'string' && args.historySessionId.trim().length > 0)
423
+ || (typeof args?.targetSessionId === 'string' && args.targetSessionId.trim().length > 0)
417
424
  );
418
425
 
419
426
  if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd) && !allowsInactiveReadChatFallback) {
@@ -1387,22 +1387,23 @@ function callProviderNativeHistoryRead(
1387
1387
  agentType: string,
1388
1388
  canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
1389
1389
  scripts: ProviderNativeHistoryScripts | undefined,
1390
- historySessionId: string,
1390
+ historySessionId: string | undefined,
1391
1391
  workspace?: string,
1392
1392
  ): ProviderNativeHistoryReadResult | null {
1393
1393
  const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, 'readSession');
1394
1394
  if (!fn) return null;
1395
+ const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
1395
1396
  const result = fn({
1396
1397
  agentType,
1397
- sessionId: historySessionId,
1398
- historySessionId,
1398
+ sessionId: normalizedSessionId,
1399
+ historySessionId: normalizedSessionId,
1399
1400
  workspace,
1400
1401
  format: canonicalHistory?.format,
1401
1402
  watchPath: canonicalHistory?.watchPath,
1402
- args: { sessionId: historySessionId, historySessionId, workspace },
1403
+ args: { sessionId: normalizedSessionId, historySessionId: normalizedSessionId, workspace },
1403
1404
  });
1404
1405
  if (!result || typeof result !== 'object') return null;
1405
- const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, (result as any).messages || (result as any).records);
1406
+ const records = normalizeProviderNativeHistoryRecords(agentType, normalizedSessionId, (result as any).messages || (result as any).records);
1406
1407
  if (records.length === 0) return null;
1407
1408
  return {
1408
1409
  records,
@@ -1419,7 +1420,8 @@ function buildNativeHistoryReadResult(
1419
1420
  workspace?: string,
1420
1421
  ): ProviderNativeHistoryReadResult | null {
1421
1422
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
1422
- if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
1423
+ const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
1424
+ if (!canonicalHistory || (!normalizedSessionId && !normalizedWorkspace) || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
1423
1425
  return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
1424
1426
  }
1425
1427
 
@@ -1478,7 +1480,7 @@ export function readProviderChatHistory(
1478
1480
  scripts?: ProviderNativeHistoryScripts;
1479
1481
  } = {},
1480
1482
  ): { messages: HistoryMessage[]; hasMore: boolean; source: 'provider-native' | 'adhdev-mirror' | 'native-unavailable'; sourcePath?: string; sourceMtimeMs?: number } {
1481
- if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
1483
+ if (isNativeSourceCanonicalHistory(options.canonicalHistory) && (options.historySessionId || options.workspace)) {
1482
1484
  const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace);
1483
1485
  if (!nativeResult) return { messages: [], hasMore: false, source: 'native-unavailable' };
1484
1486
  return {
@@ -802,7 +802,16 @@ export async function handleCliSend(ctx: DevServerContext, req: http.IncomingMes
802
802
  }
803
803
 
804
804
  try {
805
- ctx.instanceManager!.sendEvent(target.instanceId, 'send_message', { text });
805
+ if (target.category === 'cli') {
806
+ const bundle = getCliTargetBundle(ctx, type, instanceId);
807
+ if (!bundle) {
808
+ ctx.json(res, 404, { error: `No running CLI adapter found for: ${type || instanceId}` });
809
+ return;
810
+ }
811
+ await bundle.adapter.sendMessage(text);
812
+ } else {
813
+ ctx.instanceManager!.sendEvent(target.instanceId, 'send_message', { text });
814
+ }
806
815
  ctx.json(res, 200, { sent: true, type: target.type, instanceId: target.instanceId });
807
816
  } catch (e: any) {
808
817
  ctx.json(res, 500, { error: `Send failed: ${e.message}` });