@adhdev/daemon-core 0.9.82-rc.128 → 0.9.82-rc.129

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.
@@ -97,6 +97,7 @@ export declare class CliProviderInstance implements ProviderInstance {
97
97
  getSessionModalState(): SessionModalState;
98
98
  updateSettings(newSettings: Record<string, any>): void;
99
99
  onEvent(event: string, data?: any): void;
100
+ recordAcknowledgedUserInput(input: InputEnvelope | string): void;
100
101
  dispose(): void;
101
102
  private completedDebounceTimer;
102
103
  private completedDebouncePending;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.128",
3
+ "version": "0.9.82-rc.129",
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",
@@ -14,7 +14,7 @@ import { assertProviderSupportsDeclaredInput, assertTextOnlyInput } from '../pro
14
14
  import { validateReadChatResultPayload } from '../providers/read-chat-contract.js';
15
15
  import { pickApprovalButton } from '../providers/approval-utils.js';
16
16
  import type { ProviderInstance } from '../providers/provider-instance.js';
17
- import { isNativeSourceCanonicalHistory, readProviderChatHistory } from '../config/chat-history.js';
17
+ import { isNativeSourceCanonicalHistory, readChatHistory, readProviderChatHistory } from '../config/chat-history.js';
18
18
  import { LOG, getRecentLogs } from '../logging/logger.js';
19
19
  import { getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
20
20
  import { buildChatMessageSignature, hashSignatureParts } from '../chat/chat-signatures.js';
@@ -40,6 +40,7 @@ interface ApprovalSelectableInstance extends ProviderInstance {
40
40
 
41
41
  interface RuntimeChatMessageMerger extends ProviderInstance {
42
42
  mergeRuntimeChatMessages?(messages: ChatMessage[]): ChatMessage[];
43
+ recordAcknowledgedUserInput?(input: InputEnvelope | string): void;
43
44
  }
44
45
 
45
46
  type LegacyStringScript = (params?: Record<string, unknown> | string) => string;
@@ -454,6 +455,45 @@ function coerceUnsafeNativeFallbackStatus(status: string, activeModal: unknown):
454
455
  return 'idle';
455
456
  }
456
457
 
458
+ function isRuntimeInputAckMessage(message: ChatMessage | undefined): boolean {
459
+ if (!message || typeof message !== 'object') return false;
460
+ const role = String((message as any).role || '').trim().toLowerCase();
461
+ if (role !== 'user' && role !== 'human') return false;
462
+ const meta = (message as any).meta;
463
+ return !!meta && typeof meta === 'object' && !Array.isArray(meta) && meta.runtimeInputAck === true;
464
+ }
465
+
466
+ function selectRuntimeInputAckMessages(messages: ChatMessage[]): ChatMessage[] {
467
+ return messages.filter((message) => isRuntimeInputAckMessage(message));
468
+ }
469
+
470
+ function readExactRuntimeMirrorMessages(args: {
471
+ providerType: string;
472
+ targetSessionId?: string;
473
+ currentSessionId?: string;
474
+ tailLimit: number;
475
+ historyBehavior?: ProviderModule['historyBehavior'];
476
+ }): ChatMessage[] {
477
+ const targetSessionId = String(args.targetSessionId || '').trim();
478
+ const currentSessionId = String(args.currentSessionId || '').trim();
479
+ if (!targetSessionId || targetSessionId !== currentSessionId) return [];
480
+
481
+ const history = readChatHistory(
482
+ args.providerType,
483
+ 0,
484
+ Math.max(args.tailLimit || 0, 200),
485
+ targetSessionId,
486
+ 0,
487
+ args.historyBehavior,
488
+ );
489
+ return normalizeChatMessages((history.messages || []) as ChatMessage[])
490
+ .filter((message) => {
491
+ const historySessionId = String((message as any).historySessionId || '').trim();
492
+ const instanceId = String((message as any).instanceId || '').trim();
493
+ return historySessionId === targetSessionId || instanceId === targetSessionId;
494
+ });
495
+ }
496
+
457
497
  function supportsCliNativeTranscript(providerType: string, provider?: ProviderModule): boolean {
458
498
  if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) return true;
459
499
  return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
@@ -1512,10 +1552,26 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1512
1552
  });
1513
1553
  const unsafeNativeFallback = adapter.cliType === 'codex-cli'
1514
1554
  && isUnsafeNativeTranscriptFallback(fallbackReason);
1555
+ const safeRuntimeAckMessages = unsafeNativeFallback
1556
+ ? selectRuntimeInputAckMessages(returnedMessages)
1557
+ : [];
1558
+ const exactRuntimeMirrorMessages = unsafeNativeFallback
1559
+ && safeRuntimeAckMessages.length === 0
1560
+ ? readExactRuntimeMirrorMessages({
1561
+ providerType,
1562
+ targetSessionId: typeof args?.targetSessionId === 'string' ? args.targetSessionId : undefined,
1563
+ currentSessionId: typeof (h.currentSession as any)?.sessionId === 'string' ? (h.currentSession as any).sessionId : undefined,
1564
+ tailLimit: nativeHistoryLimit,
1565
+ historyBehavior: provider?.historyBehavior,
1566
+ })
1567
+ : [];
1568
+ const safeDaemonMessages = safeRuntimeAckMessages.length > 0
1569
+ ? safeRuntimeAckMessages
1570
+ : exactRuntimeMirrorMessages;
1515
1571
  if (unsafeNativeFallback) {
1516
- selectedMessages = [];
1517
- selectedTranscriptAuthority = undefined;
1518
- selectedCoverage = undefined;
1572
+ selectedMessages = safeDaemonMessages;
1573
+ selectedTranscriptAuthority = safeDaemonMessages.length > 0 ? 'daemon' : undefined;
1574
+ selectedCoverage = safeDaemonMessages.length > 0 ? 'tail' : undefined;
1519
1575
  selectedStatus = coerceUnsafeNativeFallbackStatus(returnedStatus, activeModal);
1520
1576
  }
1521
1577
  messageSource = buildCliMessageSourceProvenance({
@@ -1534,11 +1590,15 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1534
1590
  unavailableReason,
1535
1591
  nativeMessages,
1536
1592
  ptyMessages: returnedMessages,
1537
- returnedMessages: unsafeNativeFallback ? [] : returnedMessages,
1593
+ returnedMessages: unsafeNativeFallback ? safeDaemonMessages : returnedMessages,
1538
1594
  safeMapping,
1539
1595
  freshEnough,
1540
1596
  ptyStatusApprovalOnly: unsafeNativeFallback,
1541
1597
  });
1598
+ if (unsafeNativeFallback && exactRuntimeMirrorMessages.length > 0) {
1599
+ (messageSource as any).selectedDaemonSource = 'exact-runtime-mirror';
1600
+ (messageSource as any).transcriptAuthority = 'daemon';
1601
+ }
1542
1602
  }
1543
1603
  }
1544
1604
  }
@@ -1954,6 +2014,12 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
1954
2014
  } else {
1955
2015
  await adapter.sendMessage(text);
1956
2016
  }
2017
+ const target = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
2018
+ if (target?.category === 'cli'
2019
+ && target.type === adapter.cliType
2020
+ && typeof target.recordAcknowledgedUserInput === 'function') {
2021
+ target.recordAcknowledgedUserInput(input);
2022
+ }
1957
2023
  return {
1958
2024
  ..._logSendSuccess(`${transport}-adapter`, adapter.cliType),
1959
2025
  ...(forceSend ? { forceSent: true } : {}),
@@ -752,6 +752,34 @@ export class CliProviderInstance implements ProviderInstance {
752
752
  }
753
753
  }
754
754
 
755
+ recordAcknowledgedUserInput(input: InputEnvelope | string): void {
756
+ const content = typeof input === 'string'
757
+ ? input.trim()
758
+ : buildCliStructuredInputPrompt(input).trim();
759
+ if (!content) return;
760
+
761
+ const receivedAt = Date.now();
762
+ const dedupKey = `user_input_ack:${crypto
763
+ .createHash('sha256')
764
+ .update(`${this.instanceId}:${content}:${receivedAt}`)
765
+ .digest('hex')
766
+ .slice(0, 24)}`;
767
+ this.appendRuntimeMessage(buildChatMessage({
768
+ role: 'user',
769
+ senderName: 'User',
770
+ kind: 'standard',
771
+ content,
772
+ receivedAt,
773
+ timestamp: receivedAt,
774
+ source: 'runtime_input_ack',
775
+ meta: {
776
+ runtimeInputAck: true,
777
+ provider: this.type,
778
+ workspace: this.workingDir,
779
+ },
780
+ } as ChatMessage), dedupKey);
781
+ }
782
+
755
783
  dispose(): void {
756
784
  this.adapter.shutdown();
757
785
  this.monitor.reset();
@@ -1450,12 +1478,28 @@ export class CliProviderInstance implements ProviderInstance {
1450
1478
  index,
1451
1479
  source: 'parsed',
1452
1480
  }));
1481
+ const getRole = (message: ChatMessage): string => typeof message.role === 'string'
1482
+ ? message.role.trim().toLowerCase()
1483
+ : '';
1453
1484
  const runtimeEntries: MergeEntry[] = this.runtimeMessages.map((entry, index) => ({
1454
1485
  message: entry.message,
1455
1486
  index: parsedMessages.length + index,
1456
- source: 'runtime',
1487
+ source: 'runtime' as const,
1457
1488
  runtimeKey: entry.key,
1458
- }));
1489
+ })).filter((entry) => {
1490
+ const meta = entry.message.meta && typeof entry.message.meta === 'object' && !Array.isArray(entry.message.meta)
1491
+ ? entry.message.meta as Record<string, unknown>
1492
+ : {};
1493
+ if (meta.runtimeInputAck !== true) return true;
1494
+ const runtimeText = flattenContent(entry.message.content).replace(/\s+/g, ' ').trim();
1495
+ if (!runtimeText) return false;
1496
+ return !parsedEntries.some((parsedEntry) => {
1497
+ const parsedRole = getRole(parsedEntry.message);
1498
+ if (parsedRole !== 'user' && parsedRole !== 'human') return false;
1499
+ const parsedText = flattenContent(parsedEntry.message.content).replace(/\s+/g, ' ').trim();
1500
+ return parsedText === runtimeText;
1501
+ });
1502
+ });
1459
1503
  const getTime = (message: ChatMessage): number => {
1460
1504
  const value = typeof message.receivedAt === 'number'
1461
1505
  ? message.receivedAt
@@ -1465,9 +1509,6 @@ export class CliProviderInstance implements ProviderInstance {
1465
1509
  return Number.isFinite(value) && value > 0 ? value : 0;
1466
1510
  };
1467
1511
 
1468
- const getRole = (message: ChatMessage): string => typeof message.role === 'string'
1469
- ? message.role.trim().toLowerCase()
1470
- : '';
1471
1512
  const isRuntimeOverlay = (entry: MergeEntry): boolean => {
1472
1513
  if (entry.source !== 'runtime') return false;
1473
1514
  const key = typeof entry.runtimeKey === 'string' ? entry.runtimeKey.trim().toLowerCase() : '';