@adhdev/daemon-core 0.9.82-rc.1 → 0.9.82-rc.100
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/boot/daemon-lifecycle.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +10 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +10 -0
- package/dist/commands/router.d.ts +24 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/git/git-commands.d.ts +1 -0
- package/dist/git/git-status.d.ts +5 -0
- package/dist/git/git-types.d.ts +10 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +6068 -1214
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6033 -1201
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +60 -0
- package/dist/mesh/mesh-events.d.ts +29 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/refine-config.d.ts +176 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/repo-mesh-types.d.ts +167 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +4 -0
- package/src/cli-adapters/provider-cli-adapter.ts +255 -14
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +28 -10
- package/src/commands/chat-commands.ts +570 -20
- package/src/commands/cli-manager.ts +129 -1
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +3095 -406
- package/src/config/chat-history.ts +9 -7
- package/src/config/mesh-config.ts +245 -1
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +3 -3
- package/src/git/git-status.ts +97 -6
- package/src/git/git-summary.ts +3 -0
- package/src/git/git-types.ts +11 -0
- package/src/index.ts +39 -5
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +31 -8
- package/src/mesh/mesh-active-work.ts +255 -0
- package/src/mesh/mesh-events.ts +400 -47
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/refine-config.ts +356 -0
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +93 -14
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +182 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -110,6 +110,14 @@ interface SendMessageCompletion {
|
|
|
110
110
|
rejectOnce: (error: unknown) => void;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
interface PendingOutboundMessage {
|
|
114
|
+
id: string;
|
|
115
|
+
role: 'user';
|
|
116
|
+
content: string;
|
|
117
|
+
queuedAt: number;
|
|
118
|
+
source: 'sendMessage';
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
export function appendBoundedText(current: string, chunk: string, maxChars: number): string {
|
|
114
122
|
if (!chunk) return current.length <= maxChars ? current : current.slice(-maxChars);
|
|
115
123
|
if (maxChars <= 0) return '';
|
|
@@ -186,6 +194,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
186
194
|
private idleFinishCandidate: IdleFinishCandidate | null = null;
|
|
187
195
|
private finishRetryTimer: NodeJS.Timeout | null = null;
|
|
188
196
|
private finishRetryCount = 0;
|
|
197
|
+
private pendingOutboundQueue: PendingOutboundMessage[] = [];
|
|
198
|
+
private pendingOutboundFlushTimer: NodeJS.Timeout | null = null;
|
|
199
|
+
private pendingOutboundFlushInFlight = false;
|
|
189
200
|
|
|
190
201
|
// Resize redraw suppression
|
|
191
202
|
private resizeSuppressUntil: number = 0;
|
|
@@ -761,6 +772,17 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
761
772
|
if (stableMs < 2000) return;
|
|
762
773
|
|
|
763
774
|
const startupModal = this.runParseApproval(this.recentOutputBuffer);
|
|
775
|
+
const startupStatus = this.runDetectStatus(screenText || this.recentOutputBuffer);
|
|
776
|
+
if (!startupModal && startupStatus !== 'idle') {
|
|
777
|
+
this.recordTrace('startup_settle_deferred', {
|
|
778
|
+
trigger,
|
|
779
|
+
startupStatus,
|
|
780
|
+
stableMs,
|
|
781
|
+
screenText: summarizeCliTraceText(screenText, 500),
|
|
782
|
+
});
|
|
783
|
+
this.scheduleStartupSettleCheck();
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
764
786
|
this.startupParseGate = false;
|
|
765
787
|
if (this.startupSettleTimer) {
|
|
766
788
|
clearTimeout(this.startupSettleTimer);
|
|
@@ -956,6 +978,38 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
956
978
|
return true;
|
|
957
979
|
}
|
|
958
980
|
|
|
981
|
+
private clearParsedIdleResponseGuard(reason: string, parsedStatus: any): boolean {
|
|
982
|
+
const parsedRawStatus = typeof parsedStatus?.status === 'string' ? parsedStatus.status.trim() : '';
|
|
983
|
+
const parsedModal = parsedStatus?.activeModal ?? parsedStatus?.modal ?? null;
|
|
984
|
+
const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
985
|
+
if (
|
|
986
|
+
!this.isWaitingForResponse
|
|
987
|
+
|| parsedRawStatus !== 'idle'
|
|
988
|
+
|| !!parsedModal
|
|
989
|
+
|| !!blockingModal
|
|
990
|
+
|| !this.parsedStatusHasFinalAssistantMessage(parsedStatus)
|
|
991
|
+
) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
this.clearAllTimers();
|
|
995
|
+
this.clearIdleFinishCandidate(reason);
|
|
996
|
+
this.responseBuffer = '';
|
|
997
|
+
this.isWaitingForResponse = false;
|
|
998
|
+
this.responseSettleIgnoreUntil = 0;
|
|
999
|
+
this.submitRetryUsed = false;
|
|
1000
|
+
this.submitRetryPromptSnippet = '';
|
|
1001
|
+
this.finishRetryCount = 0;
|
|
1002
|
+
this.currentTurnScope = null;
|
|
1003
|
+
this.activeModal = null;
|
|
1004
|
+
this.setStatus('idle', reason);
|
|
1005
|
+
this.recordTrace('parsed_idle_response_cleared', {
|
|
1006
|
+
reason,
|
|
1007
|
+
parsedStatus: parsedRawStatus,
|
|
1008
|
+
parsedMessageCount: Array.isArray(parsedStatus?.messages) ? parsedStatus.messages.length : 0,
|
|
1009
|
+
});
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
959
1013
|
private hasMeaningfulResponseBuffer(promptSnippet: string): boolean {
|
|
960
1014
|
const raw = String(this.responseBuffer || '').trim();
|
|
961
1015
|
if (!raw) return false;
|
|
@@ -1372,6 +1426,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1372
1426
|
this.activeModal = null;
|
|
1373
1427
|
this.setStatus('idle', 'response_finished');
|
|
1374
1428
|
this.onStatusChange?.();
|
|
1429
|
+
this.schedulePendingOutboundFlush();
|
|
1375
1430
|
}
|
|
1376
1431
|
|
|
1377
1432
|
private maybeCommitVisibleIdleTranscript(session: ParsedSession, parsedMessages: CliChatMessage[]): boolean {
|
|
@@ -1402,6 +1457,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1402
1457
|
this.activeModal = null;
|
|
1403
1458
|
this.setStatus('idle', 'script_idle_commit');
|
|
1404
1459
|
this.onStatusChange?.();
|
|
1460
|
+
this.schedulePendingOutboundFlush();
|
|
1405
1461
|
this.recordTrace('script_idle_commit', {
|
|
1406
1462
|
messageCount: parsedMessages.length,
|
|
1407
1463
|
lastAssistant: summarizeCliTraceText(visibleAssistant.content, 320),
|
|
@@ -1489,6 +1545,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1489
1545
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1490
1546
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1491
1547
|
terminalScreenText: parseScreenText,
|
|
1548
|
+
workingDir: this.workingDir,
|
|
1492
1549
|
baseMessages: [],
|
|
1493
1550
|
partialResponse: this.responseBuffer,
|
|
1494
1551
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
@@ -1552,6 +1609,15 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1552
1609
|
return !!(startupModal || this.activeModal);
|
|
1553
1610
|
}
|
|
1554
1611
|
|
|
1612
|
+
private parsedStatusHasFinalAssistantMessage(parsed: any): boolean {
|
|
1613
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
1614
|
+
const lastAssistant = [...messages].reverse().find((message: any) => {
|
|
1615
|
+
if (!message || message.role !== 'assistant') return false;
|
|
1616
|
+
return typeof message.content === 'string' && message.content.trim().length > 0;
|
|
1617
|
+
});
|
|
1618
|
+
return !!lastAssistant;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1555
1621
|
private projectEffectiveStatus(startupModal: { message: string; buttons: string[] } | null = null): CliSessionStatus['status'] {
|
|
1556
1622
|
if (this.parseErrorMessage) return 'error';
|
|
1557
1623
|
if (this.hasActionableApproval(startupModal)) return 'waiting_approval';
|
|
@@ -1564,8 +1630,16 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1564
1630
|
getStatus(options: { allowParse?: boolean } = {}): CliSessionStatus {
|
|
1565
1631
|
const allowParse = options.allowParse !== false;
|
|
1566
1632
|
const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
1633
|
+
const startupDetectedStatus = allowParse && this.startupParseGate && !startupModal
|
|
1634
|
+
? this.runDetectStatus(this.recentOutputBuffer || this.terminalScreen.getText())
|
|
1635
|
+
: null;
|
|
1567
1636
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
1568
1637
|
let effectiveModal = startupModal || this.activeModal;
|
|
1638
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
1639
|
+
effectiveStatus = 'waiting_approval';
|
|
1640
|
+
} else if (startupDetectedStatus === 'idle' && !startupModal && !effectiveModal) {
|
|
1641
|
+
effectiveStatus = 'idle';
|
|
1642
|
+
}
|
|
1569
1643
|
if (allowParse && !startupModal && !effectiveModal) {
|
|
1570
1644
|
const parsed = this.getFreshParsedStatusCache();
|
|
1571
1645
|
const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons)
|
|
@@ -1575,6 +1649,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1575
1649
|
if (parsed?.status === 'waiting_approval' && parsedModal) {
|
|
1576
1650
|
effectiveStatus = 'waiting_approval';
|
|
1577
1651
|
effectiveModal = parsedModal;
|
|
1652
|
+
} else if (
|
|
1653
|
+
effectiveStatus === 'idle'
|
|
1654
|
+
&& parsed?.status === 'generating'
|
|
1655
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsed)
|
|
1656
|
+
) {
|
|
1657
|
+
effectiveStatus = 'generating';
|
|
1658
|
+
} else if (
|
|
1659
|
+
effectiveStatus === 'generating'
|
|
1660
|
+
&& parsed?.status === 'idle'
|
|
1661
|
+
&& this.parsedStatusHasFinalAssistantMessage(parsed)
|
|
1662
|
+
) {
|
|
1663
|
+
effectiveStatus = 'idle';
|
|
1578
1664
|
}
|
|
1579
1665
|
}
|
|
1580
1666
|
const bufferState = this.getBufferState();
|
|
@@ -1583,6 +1669,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1583
1669
|
messages: [],
|
|
1584
1670
|
workingDir: this.workingDir,
|
|
1585
1671
|
activeModal: effectiveModal,
|
|
1672
|
+
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
1673
|
+
pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
|
|
1674
|
+
id: message.id,
|
|
1675
|
+
role: message.role,
|
|
1676
|
+
content: message.content,
|
|
1677
|
+
queuedAt: message.queuedAt,
|
|
1678
|
+
source: message.source,
|
|
1679
|
+
})),
|
|
1586
1680
|
errorMessage: this.parseErrorMessage || undefined,
|
|
1587
1681
|
errorReason: this.parseErrorMessage ? 'parse_error' : undefined,
|
|
1588
1682
|
...(bufferState ? { bufferState } : {}),
|
|
@@ -1600,7 +1694,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1600
1694
|
const cached = this.parsedStatusCache;
|
|
1601
1695
|
const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
|
|
1602
1696
|
if (
|
|
1603
|
-
|
|
1697
|
+
!this.providerOwnsTranscript()
|
|
1698
|
+
&& cached
|
|
1604
1699
|
&& cached.responseBuffer === this.responseBuffer
|
|
1605
1700
|
&& cached.currentTurnScope === this.currentTurnScope
|
|
1606
1701
|
&& cached.recentOutputBuffer === this.recentOutputBuffer
|
|
@@ -1665,6 +1760,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1665
1760
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1666
1761
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1667
1762
|
terminalScreenText: this.getParseScreenText(this.terminalScreen.getText()),
|
|
1763
|
+
workingDir: this.workingDir,
|
|
1668
1764
|
baseMessages: [],
|
|
1669
1765
|
partialResponse: this.responseBuffer,
|
|
1670
1766
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
@@ -1925,6 +2021,104 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1925
2021
|
}
|
|
1926
2022
|
|
|
1927
2023
|
async sendMessage(text: string): Promise<void> {
|
|
2024
|
+
await this.sendMessageNow(text, true);
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
private enqueuePendingOutboundMessage(text: string, reason: string): void {
|
|
2028
|
+
const content = String(text || '');
|
|
2029
|
+
const duplicate = this.pendingOutboundQueue.some((message) => message.content === content);
|
|
2030
|
+
if (duplicate) {
|
|
2031
|
+
this.recordTrace('send_message_queued_duplicate_suppressed', {
|
|
2032
|
+
reason,
|
|
2033
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2034
|
+
text: summarizeCliTraceText(content, 500),
|
|
2035
|
+
});
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
const queuedAt = Date.now();
|
|
2039
|
+
const message: PendingOutboundMessage = {
|
|
2040
|
+
id: `${queuedAt}:${this.pendingOutboundQueue.length}:${Math.random().toString(36).slice(2, 10)}`,
|
|
2041
|
+
role: 'user',
|
|
2042
|
+
content,
|
|
2043
|
+
queuedAt,
|
|
2044
|
+
source: 'sendMessage',
|
|
2045
|
+
};
|
|
2046
|
+
this.pendingOutboundQueue.push(message);
|
|
2047
|
+
this.recordTrace('send_message_queued', {
|
|
2048
|
+
reason,
|
|
2049
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2050
|
+
queuedAt,
|
|
2051
|
+
text: summarizeCliTraceText(content, 500),
|
|
2052
|
+
});
|
|
2053
|
+
LOG.info('CLI', `[${this.cliType}] queued outbound message while busy (${reason}); queue=${this.pendingOutboundQueue.length}`);
|
|
2054
|
+
this.onStatusChange?.();
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
private shouldQueuePendingOutboundMessage(parsedStatusBeforeSend: any | null = null): string | null {
|
|
2058
|
+
if (this.provider.allowInputDuringGeneration === true) return null;
|
|
2059
|
+
if (this.hasActionableApproval()) return null;
|
|
2060
|
+
const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === 'string'
|
|
2061
|
+
? String(parsedStatusBeforeSend.status)
|
|
2062
|
+
: '';
|
|
2063
|
+
if (parsedSessionStatus === 'idle' && this.parsedStatusHasFinalAssistantMessage(parsedStatusBeforeSend)) return null;
|
|
2064
|
+
if (this.currentStatus === 'generating') return 'current_status_generating';
|
|
2065
|
+
if (parsedSessionStatus === 'generating' || parsedSessionStatus === 'long_generating') {
|
|
2066
|
+
const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
|
|
2067
|
+
const parsedHasActionableModal = Boolean(
|
|
2068
|
+
parsedModal
|
|
2069
|
+
&& Array.isArray(parsedModal.buttons)
|
|
2070
|
+
&& parsedModal.buttons.some((candidate: unknown) => typeof candidate === 'string' && candidate.trim()),
|
|
2071
|
+
);
|
|
2072
|
+
const terminalLooksIdle = this.currentStatus === 'idle'
|
|
2073
|
+
&& this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
2074
|
+
&& !this.isWaitingForResponse
|
|
2075
|
+
&& !this.currentTurnScope
|
|
2076
|
+
&& !this.hasActionableApproval()
|
|
2077
|
+
&& !parsedHasActionableModal;
|
|
2078
|
+
return terminalLooksIdle ? null : `parsed_status_${parsedSessionStatus}`;
|
|
2079
|
+
}
|
|
2080
|
+
if (this.isWaitingForResponse && this.currentTurnScope) return 'active_turn_in_progress';
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
private schedulePendingOutboundFlush(delayMs = 0): void {
|
|
2085
|
+
if (this.pendingOutboundFlushTimer) clearTimeout(this.pendingOutboundFlushTimer);
|
|
2086
|
+
this.pendingOutboundFlushTimer = setTimeout(() => {
|
|
2087
|
+
this.pendingOutboundFlushTimer = null;
|
|
2088
|
+
void this.flushPendingOutboundQueue();
|
|
2089
|
+
}, Math.max(0, delayMs));
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
private async flushPendingOutboundQueue(): Promise<void> {
|
|
2093
|
+
if (this.pendingOutboundFlushInFlight || this.pendingOutboundQueue.length === 0) return;
|
|
2094
|
+
if (this.currentStatus !== 'idle' || this.isWaitingForResponse || this.hasActionableApproval()) return;
|
|
2095
|
+
this.pendingOutboundFlushInFlight = true;
|
|
2096
|
+
try {
|
|
2097
|
+
while (this.pendingOutboundQueue.length > 0) {
|
|
2098
|
+
if (this.currentStatus !== 'idle' || this.isWaitingForResponse || this.hasActionableApproval()) break;
|
|
2099
|
+
const next = this.pendingOutboundQueue[0];
|
|
2100
|
+
this.recordTrace('send_message_queue_flush', {
|
|
2101
|
+
id: next.id,
|
|
2102
|
+
queuedAt: next.queuedAt,
|
|
2103
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2104
|
+
text: summarizeCliTraceText(next.content, 500),
|
|
2105
|
+
});
|
|
2106
|
+
try {
|
|
2107
|
+
await this.sendMessageNow(next.content, false);
|
|
2108
|
+
this.pendingOutboundQueue.shift();
|
|
2109
|
+
this.onStatusChange?.();
|
|
2110
|
+
} catch (error: any) {
|
|
2111
|
+
LOG.warn('CLI', `[${this.cliType}] queued outbound flush failed: ${error?.message || error}`);
|
|
2112
|
+
this.schedulePendingOutboundFlush(1000);
|
|
2113
|
+
break;
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
} finally {
|
|
2117
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
private async sendMessageNow(text: string, allowQueue: boolean): Promise<void> {
|
|
1928
2122
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
1929
2123
|
const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
|
|
1930
2124
|
const allowInterventionPrompt = allowInputDuringGeneration
|
|
@@ -1937,27 +2131,33 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1937
2131
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1938
2132
|
}
|
|
1939
2133
|
}
|
|
2134
|
+
const parsedStatusBeforeSend = !allowInputDuringGeneration
|
|
2135
|
+
? (() => {
|
|
2136
|
+
try {
|
|
2137
|
+
return this.getScriptParsedStatus?.() || null;
|
|
2138
|
+
} catch {
|
|
2139
|
+
return null;
|
|
2140
|
+
}
|
|
2141
|
+
})()
|
|
2142
|
+
: null;
|
|
2143
|
+
const queueReason = this.shouldQueuePendingOutboundMessage(parsedStatusBeforeSend);
|
|
2144
|
+
if (allowQueue && queueReason) {
|
|
2145
|
+
this.enqueuePendingOutboundMessage(text, queueReason);
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
1940
2148
|
if (!allowInterventionPrompt) {
|
|
1941
2149
|
await this.waitForInteractivePrompt();
|
|
1942
2150
|
}
|
|
1943
2151
|
if (!this.ready) {
|
|
1944
2152
|
this.resolveStartupState('send_precheck');
|
|
1945
|
-
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
2153
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle') {
|
|
1946
2154
|
this.ready = true;
|
|
1947
2155
|
this.startupParseGate = false;
|
|
2156
|
+
this.setStatus('idle', 'send_message_idle_prompt_recovery');
|
|
1948
2157
|
LOG.info('CLI', `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
1949
2158
|
}
|
|
1950
2159
|
}
|
|
1951
2160
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
1952
|
-
const parsedStatusBeforeSend = !allowInputDuringGeneration
|
|
1953
|
-
? (() => {
|
|
1954
|
-
try {
|
|
1955
|
-
return this.getScriptParsedStatus?.() || null;
|
|
1956
|
-
} catch {
|
|
1957
|
-
return null;
|
|
1958
|
-
}
|
|
1959
|
-
})()
|
|
1960
|
-
: null;
|
|
1961
2161
|
const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === 'string'
|
|
1962
2162
|
? String(parsedStatusBeforeSend.status)
|
|
1963
2163
|
: '';
|
|
@@ -1975,11 +2175,22 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1975
2175
|
&& !this.hasActionableApproval()
|
|
1976
2176
|
&& !parsedHasActionableModal;
|
|
1977
2177
|
if (!terminalLooksIdle) {
|
|
2178
|
+
if (allowQueue) {
|
|
2179
|
+
this.enqueuePendingOutboundMessage(text, `parsed_status_${parsedSessionStatus}`);
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
1978
2182
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1979
2183
|
}
|
|
1980
2184
|
}
|
|
1981
2185
|
if (this.isWaitingForResponse && !allowInputDuringGeneration) {
|
|
1982
|
-
if (
|
|
2186
|
+
if (
|
|
2187
|
+
!this.clearStaleIdleResponseGuard('send_message_guard')
|
|
2188
|
+
&& !this.clearParsedIdleResponseGuard('send_message_parsed_idle_guard', parsedStatusBeforeSend)
|
|
2189
|
+
) {
|
|
2190
|
+
if (allowQueue) {
|
|
2191
|
+
this.enqueuePendingOutboundMessage(text, 'waiting_for_response');
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
1983
2194
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1984
2195
|
}
|
|
1985
2196
|
}
|
|
@@ -2230,6 +2441,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2230
2441
|
this.pendingTerminalQueryTail = '';
|
|
2231
2442
|
this.ptyOutputChunks = [];
|
|
2232
2443
|
this.finishRetryCount = 0;
|
|
2444
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2445
|
+
this.pendingOutboundQueue = [];
|
|
2446
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2233
2447
|
if (this.ptyProcess) {
|
|
2234
2448
|
this.ptyProcess.write('\x03');
|
|
2235
2449
|
setTimeout(() => {
|
|
@@ -2251,6 +2465,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2251
2465
|
this.pendingTerminalQueryTail = '';
|
|
2252
2466
|
this.ptyOutputChunks = [];
|
|
2253
2467
|
this.finishRetryCount = 0;
|
|
2468
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2469
|
+
this.pendingOutboundQueue = [];
|
|
2470
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2254
2471
|
if (this.ptyProcess) {
|
|
2255
2472
|
try {
|
|
2256
2473
|
if (typeof this.ptyProcess.detach === 'function') {
|
|
@@ -2281,6 +2498,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2281
2498
|
this.ptyOutputChunks = [];
|
|
2282
2499
|
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
2283
2500
|
this.finishRetryCount = 0;
|
|
2501
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2502
|
+
this.pendingOutboundQueue = [];
|
|
2503
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2284
2504
|
this.resetTerminalScreen();
|
|
2285
2505
|
this.ptyProcess?.clearBuffer?.();
|
|
2286
2506
|
this.onStatusChange?.();
|
|
@@ -2369,10 +2589,23 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2369
2589
|
getDebugState(): Record<string, any> {
|
|
2370
2590
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
2371
2591
|
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2592
|
+
const startupDetectedStatus = this.startupParseGate && !startupModal
|
|
2593
|
+
? this.runDetectStatus(this.recentOutputBuffer || screenText)
|
|
2594
|
+
: null;
|
|
2595
|
+
const effectiveReady = this.ready || !!startupModal || startupDetectedStatus === 'waiting_approval';
|
|
2374
2596
|
const parsedDebugState = this.getParsedDebugState();
|
|
2375
2597
|
const parsedMessages = Array.isArray(parsedDebugState?.messages) ? parsedDebugState.messages : [];
|
|
2598
|
+
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
2599
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
2600
|
+
effectiveStatus = 'waiting_approval';
|
|
2601
|
+
}
|
|
2602
|
+
if (
|
|
2603
|
+
effectiveStatus === 'idle'
|
|
2604
|
+
&& parsedDebugState?.status === 'generating'
|
|
2605
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsedDebugState)
|
|
2606
|
+
) {
|
|
2607
|
+
effectiveStatus = 'generating';
|
|
2608
|
+
}
|
|
2376
2609
|
return {
|
|
2377
2610
|
type: this.cliType,
|
|
2378
2611
|
name: this.cliName,
|
|
@@ -2407,6 +2640,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2407
2640
|
rawBufferPreview: this.accumulatedRawBuffer.slice(-1000),
|
|
2408
2641
|
sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1000),
|
|
2409
2642
|
responseBuffer: this.responseBuffer.slice(-1000),
|
|
2643
|
+
pendingOutboundQueue: this.pendingOutboundQueue.map((message) => ({
|
|
2644
|
+
id: message.id,
|
|
2645
|
+
role: message.role,
|
|
2646
|
+
content: message.content,
|
|
2647
|
+
queuedAt: message.queuedAt,
|
|
2648
|
+
source: message.source,
|
|
2649
|
+
})),
|
|
2650
|
+
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
2410
2651
|
lastOutputAt: this.lastOutputAt,
|
|
2411
2652
|
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2412
2653
|
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
@@ -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),
|
|
@@ -36,7 +36,9 @@ export function resolveCliSpawnPlan(options: {
|
|
|
36
36
|
: spawnConfig.command;
|
|
37
37
|
const binaryPath = findBinary(configuredCommand);
|
|
38
38
|
const isWin = os.platform() === 'win32';
|
|
39
|
-
const allArgs = [...spawnConfig.args, ...extraArgs]
|
|
39
|
+
const allArgs = [...spawnConfig.args, ...extraArgs].map((arg) =>
|
|
40
|
+
typeof arg === 'string' ? arg.replace(/\{\{workingDir\}\}/g, workingDir) : arg,
|
|
41
|
+
);
|
|
40
42
|
|
|
41
43
|
let shellCmd: string;
|
|
42
44
|
let shellArgs: string[];
|
|
@@ -28,6 +28,14 @@ export interface CliSessionStatus {
|
|
|
28
28
|
messages: CliChatMessage[];
|
|
29
29
|
workingDir: string;
|
|
30
30
|
activeModal: { message: string; buttons: string[] } | null;
|
|
31
|
+
pendingOutboundCount?: number;
|
|
32
|
+
pendingOutboundMessages?: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
role: 'user';
|
|
35
|
+
content: string;
|
|
36
|
+
queuedAt: number;
|
|
37
|
+
source: string;
|
|
38
|
+
}>;
|
|
31
39
|
errorMessage?: string;
|
|
32
40
|
errorReason?: string;
|
|
33
41
|
bufferState?: {
|
|
@@ -94,6 +102,8 @@ export interface CliScriptInput {
|
|
|
94
102
|
rawBuffer: string;
|
|
95
103
|
recentBuffer: string;
|
|
96
104
|
screenText: string;
|
|
105
|
+
workspace?: string;
|
|
106
|
+
workingDir?: string;
|
|
97
107
|
screen: CliScreenSnapshot;
|
|
98
108
|
bufferScreen: CliScreenSnapshot;
|
|
99
109
|
recentScreen: CliScreenSnapshot;
|
|
@@ -484,17 +494,25 @@ export function findBinary(name: string): string {
|
|
|
484
494
|
return path.isAbsolute(expanded) ? expanded : path.resolve(expanded);
|
|
485
495
|
}
|
|
486
496
|
const isWin = os.platform() === 'win32';
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
+
const paths = (process.env.PATH || '').split(path.delimiter);
|
|
498
|
+
const exes = isWin ? ['.exe', '.cmd', '.bat', ''] : [''];
|
|
499
|
+
|
|
500
|
+
for (const p of paths) {
|
|
501
|
+
if (!p) continue;
|
|
502
|
+
for (const ext of exes) {
|
|
503
|
+
const fullPath = path.join(p, trimmed + ext);
|
|
504
|
+
try {
|
|
505
|
+
const fs = require('fs');
|
|
506
|
+
if (fs.existsSync(fullPath)) {
|
|
507
|
+
const stat = fs.statSync(fullPath);
|
|
508
|
+
if (stat.isFile() && (isWin || (stat.mode & 0o111))) {
|
|
509
|
+
return fullPath;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} catch { }
|
|
513
|
+
}
|
|
497
514
|
}
|
|
515
|
+
return isWin ? `${trimmed}.cmd` : trimmed;
|
|
498
516
|
}
|
|
499
517
|
|
|
500
518
|
export function isScriptBinary(binaryPath: string): boolean {
|