@adhdev/daemon-core 0.9.82-rc.127 → 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.
- package/dist/index.js +106 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +108 -16
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/package.json +1 -1
- package/src/commands/chat-commands.ts +97 -8
- package/src/providers/cli-provider-instance.ts +46 -5
|
@@ -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
|
@@ -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;
|
|
@@ -412,9 +413,9 @@ function buildCliMessageSourceProvenance(args: {
|
|
|
412
413
|
ptyMessageCount: ptyMessages.length,
|
|
413
414
|
returnedMessageCount: returnedMessages.length,
|
|
414
415
|
safeMapping: args.safeMapping === true,
|
|
415
|
-
// true when
|
|
416
|
-
//
|
|
417
|
-
ptyMessagesSuppressed: args.selected === 'native-history',
|
|
416
|
+
// true when PTY message bodies are suppressed and must not be treated as
|
|
417
|
+
// chat content. PTY may still contribute status/approval/screen evidence.
|
|
418
|
+
ptyMessagesSuppressed: args.selected === 'native-history' || args.ptyStatusApprovalOnly === true,
|
|
418
419
|
},
|
|
419
420
|
};
|
|
420
421
|
}
|
|
@@ -441,6 +442,58 @@ function buildNativeHistoryFallbackReason(args: {
|
|
|
441
442
|
return 'native_history_not_selected';
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
function isUnsafeNativeTranscriptFallback(reason?: string): boolean {
|
|
446
|
+
const value = String(reason || '').trim();
|
|
447
|
+
return value.startsWith('native_history_unavailable')
|
|
448
|
+
|| value === 'native_history_not_safely_mapped'
|
|
449
|
+
|| value === 'native_history_stale'
|
|
450
|
+
|| value === 'native_history_partial';
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function coerceUnsafeNativeFallbackStatus(status: string, activeModal: unknown): string {
|
|
454
|
+
if (status === 'waiting_approval' && activeModal) return status;
|
|
455
|
+
return 'idle';
|
|
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
|
+
|
|
444
497
|
function supportsCliNativeTranscript(providerType: string, provider?: ProviderModule): boolean {
|
|
445
498
|
if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) return true;
|
|
446
499
|
return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
@@ -1354,6 +1407,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1354
1407
|
let selectedProviderSessionId = providerSessionId;
|
|
1355
1408
|
let selectedTranscriptAuthority = transcriptAuthority;
|
|
1356
1409
|
let selectedCoverage = coverage;
|
|
1410
|
+
let selectedStatus = returnedStatus;
|
|
1357
1411
|
const sessionWorkspace = typeof (h.currentSession as any)?.workspace === 'string'
|
|
1358
1412
|
? (h.currentSession as any).workspace
|
|
1359
1413
|
: typeof adapter.workingDir === 'string'
|
|
@@ -1496,6 +1550,30 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1496
1550
|
safeMapping,
|
|
1497
1551
|
freshEnough,
|
|
1498
1552
|
});
|
|
1553
|
+
const unsafeNativeFallback = adapter.cliType === 'codex-cli'
|
|
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;
|
|
1571
|
+
if (unsafeNativeFallback) {
|
|
1572
|
+
selectedMessages = safeDaemonMessages;
|
|
1573
|
+
selectedTranscriptAuthority = safeDaemonMessages.length > 0 ? 'daemon' : undefined;
|
|
1574
|
+
selectedCoverage = safeDaemonMessages.length > 0 ? 'tail' : undefined;
|
|
1575
|
+
selectedStatus = coerceUnsafeNativeFallbackStatus(returnedStatus, activeModal);
|
|
1576
|
+
}
|
|
1499
1577
|
messageSource = buildCliMessageSourceProvenance({
|
|
1500
1578
|
selected: 'pty-parser',
|
|
1501
1579
|
provider: adapter.cliType,
|
|
@@ -1512,18 +1590,22 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1512
1590
|
unavailableReason,
|
|
1513
1591
|
nativeMessages,
|
|
1514
1592
|
ptyMessages: returnedMessages,
|
|
1515
|
-
returnedMessages,
|
|
1593
|
+
returnedMessages: unsafeNativeFallback ? safeDaemonMessages : returnedMessages,
|
|
1516
1594
|
safeMapping,
|
|
1517
1595
|
freshEnough,
|
|
1518
|
-
ptyStatusApprovalOnly:
|
|
1596
|
+
ptyStatusApprovalOnly: unsafeNativeFallback,
|
|
1519
1597
|
});
|
|
1598
|
+
if (unsafeNativeFallback && exactRuntimeMirrorMessages.length > 0) {
|
|
1599
|
+
(messageSource as any).selectedDaemonSource = 'exact-runtime-mirror';
|
|
1600
|
+
(messageSource as any).transcriptAuthority = 'daemon';
|
|
1601
|
+
}
|
|
1520
1602
|
}
|
|
1521
1603
|
}
|
|
1522
1604
|
}
|
|
1523
1605
|
LOG.debug('Command', `[read_chat] cli-like parsed provider=${adapter.cliType} target=${String(args?.targetSessionId || '')} adapterStatus=${String(adapterStatus.status || '')} parsedStatus=${String(parsedRecord.status || '')} parsedMsgCount=${parsedRecord.messages.length} returnedMsgCount=${returnedMessages.length}`);
|
|
1524
1606
|
return buildReadChatCommandResult({
|
|
1525
1607
|
messages: selectedMessages,
|
|
1526
|
-
status:
|
|
1608
|
+
status: selectedStatus,
|
|
1527
1609
|
activeModal,
|
|
1528
1610
|
messageSource,
|
|
1529
1611
|
transcriptProvenance: messageSource,
|
|
@@ -1532,7 +1614,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1532
1614
|
targetSessionId: String(args?.targetSessionId || ''),
|
|
1533
1615
|
adapterStatus: String(adapterStatus.status || ''),
|
|
1534
1616
|
parsedStatus: String(parsedRecord.status || ''),
|
|
1535
|
-
returnedStatus: String(
|
|
1617
|
+
returnedStatus: String(selectedStatus || ''),
|
|
1536
1618
|
selectedMessageSource: (messageSource as any).selected,
|
|
1537
1619
|
messageSource,
|
|
1538
1620
|
shouldPreferAdapterMessages: supportsCliNativeTranscript(providerType, provider)
|
|
@@ -1541,6 +1623,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1541
1623
|
&& typeof (messageSource as any).fallbackReason === 'string'
|
|
1542
1624
|
&& (messageSource as any).fallbackReason.startsWith('native_history_')
|
|
1543
1625
|
&& (messageSource as any).fallbackReason !== 'native_history_not_checked'
|
|
1626
|
+
&& !isUnsafeNativeTranscriptFallback((messageSource as any).fallbackReason)
|
|
1544
1627
|
&& !(selectedTranscriptAuthority === 'provider' && selectedCoverage === 'full'),
|
|
1545
1628
|
parsedMsgCount: parsedRecord.messages.length,
|
|
1546
1629
|
returnedMsgCount: selectedMessages.length,
|
|
@@ -1931,6 +2014,12 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1931
2014
|
} else {
|
|
1932
2015
|
await adapter.sendMessage(text);
|
|
1933
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
|
+
}
|
|
1934
2023
|
return {
|
|
1935
2024
|
..._logSendSuccess(`${transport}-adapter`, adapter.cliType),
|
|
1936
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() : '';
|