@adhdev/daemon-core 0.9.6 → 0.9.7
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/cli-adapters/provider-cli-adapter.d.ts +7 -8
- package/dist/cli-adapters/provider-cli-shared.d.ts +0 -1
- package/dist/index.js +379 -487
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +379 -487
- package/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +302 -448
- package/src/cli-adapters/provider-cli-parse.ts +47 -2
- package/src/cli-adapters/provider-cli-shared.ts +51 -5
- package/src/commands/chat-commands.ts +11 -13
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
extractPromptRetrySnippet,
|
|
33
33
|
getLastUserPromptText,
|
|
34
34
|
listCliScriptNames,
|
|
35
|
-
looksLikeConfirmOnlyLabel,
|
|
36
35
|
normalizePromptText,
|
|
37
36
|
normalizeScreenSnapshot,
|
|
38
37
|
promptLikelyVisible,
|
|
@@ -94,6 +93,18 @@ interface IdleFinishCandidate {
|
|
|
94
93
|
assistantLength: number;
|
|
95
94
|
}
|
|
96
95
|
|
|
96
|
+
interface SettledEvalContext {
|
|
97
|
+
now: number;
|
|
98
|
+
screenText: string;
|
|
99
|
+
modal: any;
|
|
100
|
+
scriptStatus: string;
|
|
101
|
+
parsedTranscript: any;
|
|
102
|
+
parsedMessages: CliChatMessage[];
|
|
103
|
+
lastParsedAssistant: CliChatMessage | undefined;
|
|
104
|
+
parsedShowsLiveAssistantProgress: boolean;
|
|
105
|
+
prevStatus: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
97
108
|
function normalizeComparableTranscriptText(value: unknown): string {
|
|
98
109
|
return sanitizeTerminalText(String(value || ''))
|
|
99
110
|
.replace(/\s+/g, ' ')
|
|
@@ -625,7 +636,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
625
636
|
const stableMs = this.lastScreenChangeAt ? (now - this.lastScreenChangeAt) : 0;
|
|
626
637
|
if (stableMs < 2000) return;
|
|
627
638
|
|
|
628
|
-
const startupModal = this.
|
|
639
|
+
const startupModal = this.runParseApproval(this.recentOutputBuffer);
|
|
629
640
|
this.startupParseGate = false;
|
|
630
641
|
if (this.startupSettleTimer) {
|
|
631
642
|
clearTimeout(this.startupSettleTimer);
|
|
@@ -689,11 +700,17 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
689
700
|
if (this.currentStatus !== 'waiting_approval') return;
|
|
690
701
|
const tail = this.recentOutputBuffer;
|
|
691
702
|
const screenText = this.terminalScreen.getText() || '';
|
|
692
|
-
const
|
|
693
|
-
const modal = this.runParseApproval(tail) || startupModal;
|
|
703
|
+
const modal = this.runParseApproval(tail);
|
|
694
704
|
const stillWaiting = this.runDetectStatus(tail) === 'waiting_approval' || !!modal;
|
|
695
705
|
if (stillWaiting) {
|
|
696
|
-
|
|
706
|
+
if (!modal) {
|
|
707
|
+
LOG.warn('CLI', `[${this.cliType}] approval timeout check found no actionable modal; keeping approval state fail-closed`);
|
|
708
|
+
this.activeModal = null;
|
|
709
|
+
this.onStatusChange?.();
|
|
710
|
+
this.armApprovalExitTimeout();
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
this.activeModal = modal;
|
|
697
714
|
this.onStatusChange?.();
|
|
698
715
|
this.armApprovalExitTimeout();
|
|
699
716
|
return;
|
|
@@ -706,103 +723,13 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
706
723
|
}, 60000);
|
|
707
724
|
}
|
|
708
725
|
|
|
709
|
-
private looksLikeVisibleIdlePrompt(screenText: string): boolean {
|
|
710
|
-
const text = String(screenText || '');
|
|
711
|
-
if (!text.trim()) return false;
|
|
712
|
-
if (this.cliType === 'codex-cli' && /(^|\n)\s*[❯›>]\s+(?:Find and fix a bug in @filename|Improve documentation in @filename|Use \/skills|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Run \/review on my current changes)(?:\n|$)/im.test(text)) {
|
|
713
|
-
return true;
|
|
714
|
-
}
|
|
715
|
-
return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text)
|
|
716
|
-
|| /⏎\s+send/i.test(text)
|
|
717
|
-
|| /\?\s*for\s*shortcuts/i.test(text)
|
|
718
|
-
|| /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text)
|
|
719
|
-
|| /workspace\s*\(\/directory\)/i.test(text)
|
|
720
|
-
|| /for\s*shortcuts/i.test(text);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
private findLastMatchingLineIndex(lines: string[], predicate: (line: string) => boolean): number {
|
|
724
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
725
|
-
if (predicate(lines[index])) return index;
|
|
726
|
-
}
|
|
727
|
-
return -1;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
private looksLikeClaudeGeneratingLine(line: string): boolean {
|
|
731
|
-
const trimmed = String(line || '').trim();
|
|
732
|
-
if (!trimmed) return false;
|
|
733
|
-
if (/^⏵⏵\s+accept edits on/i.test(trimmed)) return false;
|
|
734
|
-
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
|
|
735
|
-
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
|
|
736
|
-
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
|
|
737
|
-
if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
|
|
738
|
-
return /ctrl\+o to expand/i.test(trimmed)
|
|
739
|
-
|| /\b\d+\s+(?:file|files|pattern|patterns|director(?:y|ies)|match|matches|result|results)\b/i.test(trimmed);
|
|
740
|
-
}
|
|
741
|
-
return false;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
private detectClaudeGeneratingOverride(screenText: string, tail: string): boolean {
|
|
745
|
-
if (this.cliType !== 'claude-cli') return false;
|
|
746
|
-
|
|
747
|
-
const source = sanitizeTerminalText(screenText || tail || '');
|
|
748
|
-
if (!source.trim()) return false;
|
|
749
|
-
|
|
750
|
-
const allLines = source
|
|
751
|
-
.split(/\r\n|\n|\r/g)
|
|
752
|
-
.map(line => line.trim())
|
|
753
|
-
.filter(Boolean);
|
|
754
|
-
if (allLines.length === 0) return false;
|
|
755
|
-
|
|
756
|
-
const recentLines = allLines.slice(-12);
|
|
757
|
-
const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
|
|
758
|
-
const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
|
|
759
|
-
if (activeRegion.length === 0) return false;
|
|
760
|
-
|
|
761
|
-
return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
private refineDetectedStatus(status: string | null, tail: string, screenText?: string): string | null {
|
|
765
|
-
if (this.startupParseGate) {
|
|
766
|
-
return this.getStartupConfirmationModal(screenText || '')
|
|
767
|
-
? 'waiting_approval'
|
|
768
|
-
: 'starting';
|
|
769
|
-
}
|
|
770
|
-
if (status === 'waiting_approval') return status;
|
|
771
|
-
if (this.detectClaudeGeneratingOverride(screenText || '', tail)) return 'generating';
|
|
772
|
-
return status;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
private looksLikeVisibleAssistantCandidate(screenText: string): boolean {
|
|
776
|
-
const lines = sanitizeTerminalText(String(screenText || '')).split(/\r\n|\n|\r/g);
|
|
777
|
-
for (const line of lines) {
|
|
778
|
-
const trimmed = String(line || '').trim();
|
|
779
|
-
if (!trimmed) continue;
|
|
780
|
-
if (/^➜\s+\S+/.test(trimmed)) continue;
|
|
781
|
-
if (/^Update available!/i.test(trimmed)) continue;
|
|
782
|
-
if (/Claude Code v\d/i.test(trimmed)) continue;
|
|
783
|
-
if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
|
|
784
|
-
if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
|
|
785
|
-
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
|
|
786
|
-
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
|
|
787
|
-
const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
|
|
788
|
-
if (!assistantMatch) continue;
|
|
789
|
-
const content = assistantMatch[1].trim();
|
|
790
|
-
if (!content) continue;
|
|
791
|
-
if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
|
|
792
|
-
if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
|
|
793
|
-
return true;
|
|
794
|
-
}
|
|
795
|
-
return false;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
726
|
private shouldRetryFinishResponse(commitResult: { hasAssistant: boolean; assistantContent: string }): boolean {
|
|
799
727
|
if (!this.currentTurnScope) return false;
|
|
800
728
|
if (this.currentStatus === 'waiting_approval' || this.activeModal) return false;
|
|
801
729
|
if (this.finishRetryCount >= ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
|
|
802
730
|
if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
|
|
803
731
|
|
|
804
|
-
|
|
805
|
-
if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
|
|
732
|
+
if (this.runDetectStatus(this.recentOutputBuffer) !== 'idle') return false;
|
|
806
733
|
|
|
807
734
|
const now = Date.now();
|
|
808
735
|
const quietForMs = this.lastNonEmptyOutputAt ? (now - this.lastNonEmptyOutputAt) : Number.MAX_SAFE_INTEGER;
|
|
@@ -831,35 +758,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
831
758
|
return false;
|
|
832
759
|
}
|
|
833
760
|
|
|
834
|
-
private getStartupConfirmationModal(screenText: string): { message: string; buttons: string[] } | null {
|
|
835
|
-
const text = sanitizeTerminalText(String(screenText || ''));
|
|
836
|
-
if (!text.trim()) return null;
|
|
837
|
-
|
|
838
|
-
if (this.cliType === 'claude-cli') {
|
|
839
|
-
const hasTrustPrompt = /Quick safety check/i.test(text)
|
|
840
|
-
|| /Is this a project you trust/i.test(text)
|
|
841
|
-
|| /Do you trust (?:this project|the contents of this directory|the files in this folder)/i.test(text);
|
|
842
|
-
const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text)
|
|
843
|
-
|| /Enter to confirm/i.test(text)
|
|
844
|
-
|| /Esc to (?:cancel|exit)/i.test(text);
|
|
845
|
-
if (hasTrustPrompt || (hasConfirmFooter && /trust/i.test(text))) {
|
|
846
|
-
return {
|
|
847
|
-
message: 'Confirm Claude Code project trust',
|
|
848
|
-
buttons: ['Continue'],
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
return null;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
private shouldResolveModalWithEnter(modal: { message: string; buttons: string[] } | null, buttonIndex: number): boolean {
|
|
857
|
-
if (!modal || buttonIndex !== 0) return false;
|
|
858
|
-
const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
|
|
859
|
-
if (buttons.length !== 1) return false;
|
|
860
|
-
const buttonLabel = String(buttons[0] || '').trim();
|
|
861
|
-
return looksLikeConfirmOnlyLabel(buttonLabel);
|
|
862
|
-
}
|
|
863
761
|
|
|
864
762
|
private async waitForInteractivePrompt(maxWaitMs = 5000): Promise<void> {
|
|
865
763
|
const startedAt = Date.now();
|
|
@@ -868,21 +766,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
868
766
|
while (Date.now() - startedAt < maxWaitMs) {
|
|
869
767
|
this.resolveStartupState('interactive_wait');
|
|
870
768
|
const screenText = this.terminalScreen.getText() || '';
|
|
871
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
872
769
|
const stableMs = this.lastScreenChangeAt ? (Date.now() - this.lastScreenChangeAt) : 0;
|
|
873
770
|
const recentlyOutput = this.lastNonEmptyOutputAt ? (Date.now() - this.lastNonEmptyOutputAt) : Number.MAX_SAFE_INTEGER;
|
|
874
771
|
const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
|
|
875
|
-
const
|
|
876
|
-
const interactiveReady = hasPrompt
|
|
772
|
+
const interactiveReady = status === 'idle'
|
|
877
773
|
&& stableMs >= 700
|
|
878
|
-
&& recentlyOutput >= 350
|
|
879
|
-
&& status !== 'generating';
|
|
774
|
+
&& recentlyOutput >= 350;
|
|
880
775
|
|
|
881
776
|
if (interactiveReady) {
|
|
882
777
|
if (loggedWait) {
|
|
883
778
|
LOG.info(
|
|
884
779
|
'CLI',
|
|
885
|
-
`[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput}
|
|
780
|
+
`[${this.cliType}] Interactive prompt ready after ${Date.now() - startedAt}ms (stableMs=${stableMs}, recentOutputMs=${recentlyOutput})`
|
|
886
781
|
);
|
|
887
782
|
}
|
|
888
783
|
return;
|
|
@@ -892,7 +787,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
892
787
|
loggedWait = true;
|
|
893
788
|
LOG.info(
|
|
894
789
|
'CLI',
|
|
895
|
-
`[${this.cliType}] Waiting for interactive prompt:
|
|
790
|
+
`[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
|
|
896
791
|
);
|
|
897
792
|
}
|
|
898
793
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
@@ -905,17 +800,31 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
905
800
|
);
|
|
906
801
|
}
|
|
907
802
|
|
|
908
|
-
private
|
|
909
|
-
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
803
|
+
private trimLastAssistantEcho(messages: CliChatMessage[], prompt: string | undefined): void {
|
|
804
|
+
if (!prompt) return;
|
|
805
|
+
const last = [...messages].reverse().find((m) => m.role === 'assistant' && typeof m.content === 'string');
|
|
806
|
+
if (last) last.content = trimPromptEchoPrefix(last.content, prompt);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
private clearAllTimers(): void {
|
|
915
810
|
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
916
811
|
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
917
812
|
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
813
|
+
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
918
814
|
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
815
|
+
if (this.settleTimer) { clearTimeout(this.settleTimer); this.settleTimer = null; }
|
|
816
|
+
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
817
|
+
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
818
|
+
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
private clearStaleIdleResponseGuard(reason: string): boolean {
|
|
822
|
+
const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
823
|
+
const isIdle = this.runDetectStatus(this.recentOutputBuffer) === 'idle';
|
|
824
|
+
if (!this.isWaitingForResponse || this.currentStatus !== 'idle' || !isIdle || !!blockingModal) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
this.clearAllTimers();
|
|
919
828
|
this.clearIdleFinishCandidate(reason);
|
|
920
829
|
this.responseBuffer = '';
|
|
921
830
|
this.isWaitingForResponse = false;
|
|
@@ -925,10 +834,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
925
834
|
this.finishRetryCount = 0;
|
|
926
835
|
this.currentTurnScope = null;
|
|
927
836
|
this.activeModal = null;
|
|
928
|
-
this.recordTrace('stale_idle_response_cleared', {
|
|
929
|
-
reason,
|
|
930
|
-
screenText: summarizeCliTraceText(screenText, 240),
|
|
931
|
-
});
|
|
837
|
+
this.recordTrace('stale_idle_response_cleared', { reason });
|
|
932
838
|
return true;
|
|
933
839
|
}
|
|
934
840
|
|
|
@@ -975,7 +881,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
975
881
|
if (this.startupParseGate) {
|
|
976
882
|
return;
|
|
977
883
|
}
|
|
978
|
-
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
979
884
|
const parsedTranscript = this.parseCurrentTranscript(
|
|
980
885
|
this.committedMessages,
|
|
981
886
|
this.responseBuffer,
|
|
@@ -984,14 +889,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
984
889
|
const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button: any) => typeof button === 'string' && button.trim())
|
|
985
890
|
? parsedTranscript.activeModal
|
|
986
891
|
: null;
|
|
987
|
-
const modal = this.runParseApproval(tail) || parsedModal
|
|
988
|
-
const
|
|
989
|
-
// detectStatus is the primary authority for status, but if the parsed transcript
|
|
990
|
-
// already surfaced actionable approval buttons, promote that state so runtime
|
|
991
|
-
// status and resolve_action stay aligned with the visible prompt.
|
|
992
|
-
const scriptStatus = startupModal
|
|
993
|
-
? 'waiting_approval'
|
|
994
|
-
: (parsedModal && parsedTranscript?.status === 'waiting_approval' ? 'waiting_approval' : rawScriptStatus);
|
|
892
|
+
const modal = this.runParseApproval(tail) || parsedModal;
|
|
893
|
+
const scriptStatus = this.runDetectStatus(tail);
|
|
995
894
|
const parsedMessages = Array.isArray(parsedTranscript?.messages)
|
|
996
895
|
? normalizeCliParsedMessages(parsedTranscript.messages, {
|
|
997
896
|
committedMessages: this.committedMessages,
|
|
@@ -1059,246 +958,262 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1059
958
|
if (!scriptStatus) return;
|
|
1060
959
|
|
|
1061
960
|
const prevStatus = this.currentStatus;
|
|
961
|
+
const ctx: SettledEvalContext = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
|
|
1062
962
|
|
|
1063
|
-
|
|
1064
|
-
this.pendingScriptStatus = null;
|
|
1065
|
-
this.pendingScriptStatusSince = 0;
|
|
1066
|
-
if (this.pendingScriptStatusTimer) {
|
|
1067
|
-
clearTimeout(this.pendingScriptStatusTimer);
|
|
1068
|
-
this.pendingScriptStatusTimer = null;
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
const armPendingScriptStatus = (delayMs: number) => {
|
|
1072
|
-
if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
|
|
1073
|
-
this.pendingScriptStatusTimer = setTimeout(() => {
|
|
1074
|
-
this.pendingScriptStatusTimer = null;
|
|
1075
|
-
this.settledBuffer = this.recentOutputBuffer;
|
|
1076
|
-
this.evaluateSettled();
|
|
1077
|
-
}, delayMs);
|
|
1078
|
-
};
|
|
1079
|
-
const shouldDebouncePromotion = (status: string) =>
|
|
1080
|
-
prevStatus === 'idle'
|
|
1081
|
-
&& !this.isWaitingForResponse
|
|
1082
|
-
&& !this.currentTurnScope
|
|
1083
|
-
&& (status === 'generating' || status === 'waiting_approval');
|
|
1084
|
-
|
|
1085
|
-
if (shouldDebouncePromotion(scriptStatus)) {
|
|
1086
|
-
if (this.pendingScriptStatus !== scriptStatus) {
|
|
1087
|
-
this.pendingScriptStatus = scriptStatus as 'generating' | 'waiting_approval';
|
|
1088
|
-
this.pendingScriptStatusSince = now;
|
|
1089
|
-
armPendingScriptStatus(ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
const elapsed = now - this.pendingScriptStatusSince;
|
|
1093
|
-
if (elapsed < ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
1094
|
-
armPendingScriptStatus(ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
} else {
|
|
1098
|
-
clearPendingScriptStatus();
|
|
1099
|
-
}
|
|
963
|
+
if (!this.applyPendingScriptStatusDebounce(ctx)) return;
|
|
1100
964
|
|
|
1101
965
|
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
'CLI',
|
|
1108
|
-
`[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} scriptStatus=${String(scriptStatus || '')} parsedStatus=${String(parsedTranscript?.status || '')} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || '', 120)).slice(0, 160)} visibleIdlePrompt=${String(visibleIdlePrompt)} visibleAssistantCandidate=${String(visibleAssistantCandidate)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
|
|
1109
|
-
);
|
|
1110
|
-
}
|
|
966
|
+
LOG.info(
|
|
967
|
+
'CLI',
|
|
968
|
+
`[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope?.prompt || '').slice(0, 140)} scriptStatus=${String(scriptStatus || '')} parsedStatus=${String(parsedTranscript?.status || '')} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || '', 120)).slice(0, 160)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
|
|
969
|
+
);
|
|
970
|
+
|
|
1111
971
|
const shouldHoldGenerating =
|
|
1112
972
|
scriptStatus === 'idle'
|
|
1113
973
|
&& this.isWaitingForResponse
|
|
1114
974
|
&& !modal
|
|
1115
975
|
&& recentInteractiveActivity
|
|
1116
|
-
&& !(visibleIdlePrompt && visibleAssistantCandidate)
|
|
1117
976
|
&& !(parsedTranscript?.status === 'idle' && !!lastParsedAssistant);
|
|
1118
977
|
|
|
1119
978
|
if (shouldHoldGenerating) {
|
|
1120
|
-
this.
|
|
1121
|
-
this.setStatus('generating', 'recent_activity_hold');
|
|
1122
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1123
|
-
this.idleTimeout = setTimeout(() => {
|
|
1124
|
-
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1125
|
-
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1126
|
-
this.finishResponse();
|
|
1127
|
-
}
|
|
1128
|
-
}, this.timeouts.generatingIdle);
|
|
1129
|
-
this.recordTrace('hold_generating_recent_activity', {
|
|
1130
|
-
scriptStatus,
|
|
1131
|
-
recentInteractiveActivity,
|
|
1132
|
-
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
1133
|
-
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
1134
|
-
holdMs: statusActivityHoldMs,
|
|
1135
|
-
...buildCliTraceParseSnapshot({
|
|
1136
|
-
accumulatedBuffer: this.accumulatedBuffer,
|
|
1137
|
-
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1138
|
-
responseBuffer: this.responseBuffer,
|
|
1139
|
-
partialResponse: this.responseBuffer,
|
|
1140
|
-
scope: this.currentTurnScope,
|
|
1141
|
-
}),
|
|
1142
|
-
});
|
|
1143
|
-
this.onStatusChange?.();
|
|
979
|
+
this.applyHoldGenerating(ctx, recentInteractiveActivity);
|
|
1144
980
|
return;
|
|
1145
981
|
}
|
|
1146
982
|
|
|
1147
983
|
if (scriptStatus === 'waiting_approval') {
|
|
1148
|
-
this.
|
|
1149
|
-
|
|
1150
|
-
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1151
|
-
if ((inCooldown || visibleIdlePrompt) && !modal) {
|
|
1152
|
-
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1153
|
-
this.activeModal = null;
|
|
1154
|
-
if (this.isWaitingForResponse) {
|
|
1155
|
-
this.setStatus('generating', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
1156
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1157
|
-
this.idleTimeout = setTimeout(() => {
|
|
1158
|
-
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1159
|
-
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1160
|
-
this.finishResponse();
|
|
1161
|
-
}
|
|
1162
|
-
}, this.timeouts.generatingIdle);
|
|
1163
|
-
} else {
|
|
1164
|
-
this.setStatus('idle', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
1165
|
-
}
|
|
1166
|
-
this.onStatusChange?.();
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
if (!inCooldown) {
|
|
1170
|
-
this.isWaitingForResponse = true;
|
|
1171
|
-
this.setStatus('waiting_approval', 'script_detect');
|
|
1172
|
-
|
|
1173
|
-
// Use parseApproval script for modal info
|
|
1174
|
-
this.activeModal = modal || { message: 'Approval required', buttons: ['Allow', 'Deny'] };
|
|
1175
|
-
|
|
1176
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1177
|
-
this.armApprovalExitTimeout();
|
|
1178
|
-
this.onStatusChange?.();
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
984
|
+
this.applyWaitingApproval(ctx);
|
|
985
|
+
return;
|
|
1181
986
|
}
|
|
1182
987
|
|
|
1183
988
|
if (scriptStatus === 'generating') {
|
|
1184
|
-
this.
|
|
1185
|
-
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
1186
|
-
const noActiveTurn = !this.currentTurnScope;
|
|
1187
|
-
const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText)
|
|
1188
|
-
|| (/accept edits on/i.test(effectiveScreenText)
|
|
1189
|
-
&& (/Update available!/i.test(screenText)
|
|
1190
|
-
|| /\/effort/i.test(screenText)
|
|
1191
|
-
|| /^.*➜\s+\S+/m.test(effectiveScreenText)));
|
|
1192
|
-
if (prevStatus === 'idle' && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
1193
|
-
return;
|
|
1194
|
-
}
|
|
1195
|
-
if (prevStatus === 'waiting_approval') {
|
|
1196
|
-
// Transitioned out of approval → generating
|
|
1197
|
-
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1198
|
-
this.activeModal = null;
|
|
1199
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
1200
|
-
}
|
|
1201
|
-
if (!this.isWaitingForResponse) {
|
|
1202
|
-
this.isWaitingForResponse = true;
|
|
1203
|
-
this.responseBuffer = '';
|
|
1204
|
-
}
|
|
1205
|
-
this.setStatus('generating', 'script_detect');
|
|
1206
|
-
// Reset idle timeout
|
|
1207
|
-
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1208
|
-
this.idleTimeout = setTimeout(() => {
|
|
1209
|
-
if (this.isWaitingForResponse) {
|
|
1210
|
-
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1211
|
-
this.finishResponse();
|
|
1212
|
-
}
|
|
1213
|
-
}, this.timeouts.generatingIdle);
|
|
1214
|
-
this.onStatusChange?.();
|
|
989
|
+
this.applyGenerating(ctx);
|
|
1215
990
|
return;
|
|
1216
991
|
}
|
|
1217
992
|
|
|
1218
993
|
if (scriptStatus === 'idle') {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
1223
|
-
}
|
|
1224
|
-
if (this.isWaitingForResponse) {
|
|
1225
|
-
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1226
|
-
const quietForMs = this.lastNonEmptyOutputAt ? (now - this.lastNonEmptyOutputAt) : Number.MAX_SAFE_INTEGER;
|
|
1227
|
-
const screenStableMs = this.lastScreenChangeAt ? (now - this.lastScreenChangeAt) : 0;
|
|
1228
|
-
const hasAssistantTurn = !!lastParsedAssistant;
|
|
1229
|
-
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
1230
|
-
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
1231
|
-
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
1232
|
-
const idleStableThresholdMs = idleFinishConfirmMs;
|
|
1233
|
-
const idleReady = visibleIdlePrompt
|
|
1234
|
-
&& !modal
|
|
1235
|
-
&& hasAssistantTurn
|
|
1236
|
-
&& quietForMs >= idleQuietThresholdMs
|
|
1237
|
-
&& screenStableMs >= idleStableThresholdMs;
|
|
1238
|
-
const candidate = this.idleFinishCandidate;
|
|
1239
|
-
const candidateQuiet = !!candidate
|
|
1240
|
-
&& candidate.responseEpoch === this.responseEpoch
|
|
1241
|
-
&& candidate.lastOutputAt === this.lastOutputAt
|
|
1242
|
-
&& candidate.lastScreenChangeAt === this.lastScreenChangeAt
|
|
1243
|
-
&& assistantLength >= candidate.assistantLength
|
|
1244
|
-
&& (now - candidate.armedAt) >= idleFinishConfirmMs;
|
|
1245
|
-
const canFinishImmediately = idleReady && candidateQuiet;
|
|
1246
|
-
|
|
1247
|
-
this.recordTrace('idle_decision', {
|
|
1248
|
-
visibleIdlePrompt,
|
|
1249
|
-
quietForMs,
|
|
1250
|
-
screenStableMs,
|
|
1251
|
-
hasAssistantTurn,
|
|
1252
|
-
assistantLength,
|
|
1253
|
-
hasModal: !!modal,
|
|
1254
|
-
idleQuietThresholdMs,
|
|
1255
|
-
idleStableThresholdMs,
|
|
1256
|
-
idleReady,
|
|
1257
|
-
idleFinishConfirmMs,
|
|
1258
|
-
idleFinishCandidate: candidate,
|
|
1259
|
-
candidateQuiet,
|
|
1260
|
-
canFinishImmediately,
|
|
1261
|
-
submitPendingUntil: this.submitPendingUntil,
|
|
1262
|
-
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
1263
|
-
...buildCliTraceParseSnapshot({
|
|
1264
|
-
accumulatedBuffer: this.accumulatedBuffer,
|
|
1265
|
-
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1266
|
-
responseBuffer: this.responseBuffer,
|
|
1267
|
-
partialResponse: this.responseBuffer,
|
|
1268
|
-
scope: this.currentTurnScope,
|
|
1269
|
-
}),
|
|
1270
|
-
});
|
|
994
|
+
this.applyIdle(ctx, now);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
1271
997
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
998
|
+
// Returns false if the caller should bail out (debounce pending).
|
|
999
|
+
private applyPendingScriptStatusDebounce(ctx: SettledEvalContext): boolean {
|
|
1000
|
+
const { now, scriptStatus, prevStatus } = ctx;
|
|
1001
|
+
const shouldDebounce =
|
|
1002
|
+
prevStatus === 'idle'
|
|
1003
|
+
&& !this.isWaitingForResponse
|
|
1004
|
+
&& !this.currentTurnScope
|
|
1005
|
+
&& (scriptStatus === 'generating' || scriptStatus === 'waiting_approval');
|
|
1278
1006
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1007
|
+
if (!shouldDebounce) {
|
|
1008
|
+
this.pendingScriptStatus = null;
|
|
1009
|
+
this.pendingScriptStatusSince = 0;
|
|
1010
|
+
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
1011
|
+
return true;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const armPending = (delayMs: number) => {
|
|
1015
|
+
if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
|
|
1016
|
+
this.pendingScriptStatusTimer = setTimeout(() => {
|
|
1017
|
+
this.pendingScriptStatusTimer = null;
|
|
1018
|
+
this.settledBuffer = this.recentOutputBuffer;
|
|
1019
|
+
this.evaluateSettled();
|
|
1020
|
+
}, delayMs);
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
if (this.pendingScriptStatus !== scriptStatus) {
|
|
1024
|
+
this.pendingScriptStatus = scriptStatus as 'generating' | 'waiting_approval';
|
|
1025
|
+
this.pendingScriptStatusSince = now;
|
|
1026
|
+
armPending(ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
const elapsed = now - this.pendingScriptStatusSince;
|
|
1030
|
+
if (elapsed < ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
1031
|
+
armPending(ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
return true;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
private applyHoldGenerating(ctx: SettledEvalContext, recentInteractiveActivity: boolean): void {
|
|
1038
|
+
const { scriptStatus } = ctx;
|
|
1039
|
+
this.clearIdleFinishCandidate('hold_generating_recent_activity');
|
|
1040
|
+
this.setStatus('generating', 'recent_activity_hold');
|
|
1041
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1042
|
+
this.idleTimeout = setTimeout(() => {
|
|
1043
|
+
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1044
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1045
|
+
this.finishResponse();
|
|
1046
|
+
}
|
|
1047
|
+
}, this.timeouts.generatingIdle);
|
|
1048
|
+
this.recordTrace('hold_generating_recent_activity', {
|
|
1049
|
+
scriptStatus,
|
|
1050
|
+
recentInteractiveActivity,
|
|
1051
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
1052
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
1053
|
+
holdMs: this.getStatusActivityHoldMs(),
|
|
1054
|
+
...buildCliTraceParseSnapshot({
|
|
1055
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
1056
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1057
|
+
responseBuffer: this.responseBuffer,
|
|
1058
|
+
partialResponse: this.responseBuffer,
|
|
1059
|
+
scope: this.currentTurnScope,
|
|
1060
|
+
}),
|
|
1061
|
+
});
|
|
1062
|
+
this.onStatusChange?.();
|
|
1063
|
+
}
|
|
1287
1064
|
|
|
1065
|
+
private applyWaitingApproval(ctx: SettledEvalContext): void {
|
|
1066
|
+
const { modal } = ctx;
|
|
1067
|
+
this.clearIdleFinishCandidate('waiting_approval');
|
|
1068
|
+
const inCooldown = this.lastApprovalResolvedAt && (Date.now() - this.lastApprovalResolvedAt) < this.timeouts.approvalCooldown;
|
|
1069
|
+
if (inCooldown && !modal) {
|
|
1070
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1071
|
+
this.activeModal = null;
|
|
1072
|
+
if (this.isWaitingForResponse) {
|
|
1073
|
+
this.setStatus('generating', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
1288
1074
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1289
1075
|
this.idleTimeout = setTimeout(() => {
|
|
1290
1076
|
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1291
1077
|
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1292
|
-
this.clearIdleFinishCandidate('idle_timeout_finish');
|
|
1293
1078
|
this.finishResponse();
|
|
1294
1079
|
}
|
|
1295
|
-
}, this.timeouts.
|
|
1296
|
-
} else
|
|
1080
|
+
}, this.timeouts.generatingIdle);
|
|
1081
|
+
} else {
|
|
1082
|
+
this.setStatus('idle', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
1083
|
+
}
|
|
1084
|
+
this.onStatusChange?.();
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
if (!inCooldown) {
|
|
1088
|
+
if (!modal) {
|
|
1089
|
+
LOG.warn('CLI', `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
this.isWaitingForResponse = true;
|
|
1093
|
+
this.setStatus('waiting_approval', 'script_detect');
|
|
1094
|
+
this.activeModal = modal;
|
|
1095
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1096
|
+
this.armApprovalExitTimeout();
|
|
1097
|
+
this.onStatusChange?.();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
private applyGenerating(ctx: SettledEvalContext): void {
|
|
1102
|
+
const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
|
|
1103
|
+
this.clearIdleFinishCandidate('generating');
|
|
1104
|
+
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
1105
|
+
const noActiveTurn = !this.currentTurnScope;
|
|
1106
|
+
const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText)
|
|
1107
|
+
|| (/accept edits on/i.test(effectiveScreenText)
|
|
1108
|
+
&& (/Update available!/i.test(screenText)
|
|
1109
|
+
|| /\/effort/i.test(screenText)
|
|
1110
|
+
|| /^.*➜\s+\S+/m.test(effectiveScreenText)));
|
|
1111
|
+
if (prevStatus === 'idle' && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
if (prevStatus === 'waiting_approval') {
|
|
1115
|
+
// Transitioned out of approval → generating
|
|
1116
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1117
|
+
this.activeModal = null;
|
|
1118
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
1119
|
+
}
|
|
1120
|
+
if (!this.isWaitingForResponse) {
|
|
1121
|
+
this.isWaitingForResponse = true;
|
|
1122
|
+
this.responseBuffer = '';
|
|
1123
|
+
}
|
|
1124
|
+
this.setStatus('generating', 'script_detect');
|
|
1125
|
+
// Reset idle timeout
|
|
1126
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1127
|
+
this.idleTimeout = setTimeout(() => {
|
|
1128
|
+
if (this.isWaitingForResponse) {
|
|
1129
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1130
|
+
this.finishResponse();
|
|
1131
|
+
}
|
|
1132
|
+
}, this.timeouts.generatingIdle);
|
|
1133
|
+
this.onStatusChange?.();
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
private applyIdle(ctx: SettledEvalContext, now: number): void {
|
|
1137
|
+
const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
|
|
1138
|
+
if (prevStatus === 'waiting_approval') {
|
|
1139
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1140
|
+
this.activeModal = null;
|
|
1141
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
1142
|
+
}
|
|
1143
|
+
if (!this.isWaitingForResponse) {
|
|
1144
|
+
if (prevStatus !== 'idle') {
|
|
1297
1145
|
this.clearIdleFinishCandidate('idle_without_response');
|
|
1298
1146
|
this.setStatus('idle', 'script_detect');
|
|
1299
1147
|
this.onStatusChange?.();
|
|
1300
1148
|
}
|
|
1149
|
+
return;
|
|
1301
1150
|
}
|
|
1151
|
+
const quietForMs = this.lastNonEmptyOutputAt ? (now - this.lastNonEmptyOutputAt) : Number.MAX_SAFE_INTEGER;
|
|
1152
|
+
const screenStableMs = this.lastScreenChangeAt ? (now - this.lastScreenChangeAt) : 0;
|
|
1153
|
+
const hasAssistantTurn = !!lastParsedAssistant;
|
|
1154
|
+
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
1155
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
1156
|
+
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
1157
|
+
const idleReady = !modal
|
|
1158
|
+
&& hasAssistantTurn
|
|
1159
|
+
&& quietForMs >= idleQuietThresholdMs
|
|
1160
|
+
&& screenStableMs >= idleFinishConfirmMs;
|
|
1161
|
+
const candidate = this.idleFinishCandidate;
|
|
1162
|
+
const candidateQuiet = !!candidate
|
|
1163
|
+
&& candidate.responseEpoch === this.responseEpoch
|
|
1164
|
+
&& candidate.lastOutputAt === this.lastOutputAt
|
|
1165
|
+
&& candidate.lastScreenChangeAt === this.lastScreenChangeAt
|
|
1166
|
+
&& assistantLength >= candidate.assistantLength
|
|
1167
|
+
&& (now - candidate.armedAt) >= idleFinishConfirmMs;
|
|
1168
|
+
|
|
1169
|
+
this.recordTrace('idle_decision', {
|
|
1170
|
+
quietForMs,
|
|
1171
|
+
screenStableMs,
|
|
1172
|
+
hasAssistantTurn,
|
|
1173
|
+
assistantLength,
|
|
1174
|
+
hasModal: !!modal,
|
|
1175
|
+
idleQuietThresholdMs,
|
|
1176
|
+
idleStableThresholdMs: idleFinishConfirmMs,
|
|
1177
|
+
idleReady,
|
|
1178
|
+
idleFinishConfirmMs,
|
|
1179
|
+
idleFinishCandidate: candidate,
|
|
1180
|
+
candidateQuiet,
|
|
1181
|
+
canFinishImmediately: idleReady && candidateQuiet,
|
|
1182
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
1183
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
1184
|
+
...buildCliTraceParseSnapshot({
|
|
1185
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
1186
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1187
|
+
responseBuffer: this.responseBuffer,
|
|
1188
|
+
partialResponse: this.responseBuffer,
|
|
1189
|
+
scope: this.currentTurnScope,
|
|
1190
|
+
}),
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
if (idleReady && candidateQuiet) {
|
|
1194
|
+
this.clearIdleFinishCandidate('finish_response');
|
|
1195
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1196
|
+
this.finishResponse();
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
if (idleReady) {
|
|
1201
|
+
if (!candidate) {
|
|
1202
|
+
this.armIdleFinishCandidate(assistantLength);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
} else {
|
|
1206
|
+
this.clearIdleFinishCandidate('idle_not_ready');
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1210
|
+
this.idleTimeout = setTimeout(() => {
|
|
1211
|
+
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1212
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1213
|
+
this.clearIdleFinishCandidate('idle_timeout_finish');
|
|
1214
|
+
this.finishResponse();
|
|
1215
|
+
}
|
|
1216
|
+
}, this.timeouts.idleFinish);
|
|
1302
1217
|
}
|
|
1303
1218
|
|
|
1304
1219
|
private finishResponse(): void {
|
|
@@ -1338,12 +1253,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1338
1253
|
}, ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
1339
1254
|
return;
|
|
1340
1255
|
}
|
|
1341
|
-
|
|
1342
|
-
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
1343
|
-
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1344
|
-
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
1345
|
-
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
1346
|
-
|
|
1256
|
+
this.clearAllTimers();
|
|
1347
1257
|
this.responseBuffer = '';
|
|
1348
1258
|
this.isWaitingForResponse = false;
|
|
1349
1259
|
this.responseSettleIgnoreUntil = 0;
|
|
@@ -1356,10 +1266,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1356
1266
|
this.onStatusChange?.();
|
|
1357
1267
|
}
|
|
1358
1268
|
|
|
1359
|
-
private maybeCommitVisibleIdleTranscript(
|
|
1360
|
-
parsed: any,
|
|
1361
|
-
options?: { requireVisibleAssistantCandidate?: boolean; screenText?: string },
|
|
1362
|
-
): boolean {
|
|
1269
|
+
private maybeCommitVisibleIdleTranscript(parsed: any): boolean {
|
|
1363
1270
|
const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
|
|
1364
1271
|
if (!allowImmediateScriptIdleCommit) return false;
|
|
1365
1272
|
if (
|
|
@@ -1374,13 +1281,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1374
1281
|
return false;
|
|
1375
1282
|
}
|
|
1376
1283
|
|
|
1377
|
-
if (options?.requireVisibleAssistantCandidate) {
|
|
1378
|
-
const candidateText = options.screenText || this.terminalScreen.getText() || '';
|
|
1379
|
-
if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
|
|
1380
|
-
return false;
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
1284
|
const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
|
|
1385
1285
|
committedMessages: this.committedMessages,
|
|
1386
1286
|
scope: this.currentTurnScope,
|
|
@@ -1390,18 +1290,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1390
1290
|
if (!visibleAssistant) return false;
|
|
1391
1291
|
|
|
1392
1292
|
this.committedMessages = hydratedForIdleCommit;
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === 'assistant');
|
|
1396
|
-
if (lastAssistantForTrim) {
|
|
1397
|
-
lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
1401
|
-
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
1402
|
-
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1403
|
-
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
1404
|
-
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
1293
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
1294
|
+
this.clearAllTimers();
|
|
1405
1295
|
this.syncMessageViews();
|
|
1406
1296
|
this.responseBuffer = '';
|
|
1407
1297
|
this.isWaitingForResponse = false;
|
|
@@ -1432,13 +1322,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1432
1322
|
scope: this.currentTurnScope,
|
|
1433
1323
|
lastOutputAt: this.lastOutputAt,
|
|
1434
1324
|
});
|
|
1435
|
-
|
|
1436
|
-
if (promptForTrim) {
|
|
1437
|
-
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === 'assistant');
|
|
1438
|
-
if (lastAssistantForTrim) {
|
|
1439
|
-
lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1325
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
1442
1326
|
this.syncMessageViews();
|
|
1443
1327
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === 'assistant');
|
|
1444
1328
|
if (this.currentTurnScope) {
|
|
@@ -1499,7 +1383,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1499
1383
|
screen: buildCliScreenSnapshot(screenText),
|
|
1500
1384
|
tailScreen: buildCliScreenSnapshot(text.slice(-500)),
|
|
1501
1385
|
});
|
|
1502
|
-
return
|
|
1386
|
+
return status;
|
|
1503
1387
|
} catch (e: any) {
|
|
1504
1388
|
LOG.warn('CLI', `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
1505
1389
|
return null;
|
|
@@ -1552,19 +1436,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1552
1436
|
return parsed;
|
|
1553
1437
|
}
|
|
1554
1438
|
|
|
1555
|
-
const
|
|
1556
|
-
const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
|
|
1439
|
+
const visibleModal = this.runParseApproval(recentBuffer);
|
|
1557
1440
|
if (visibleModal) {
|
|
1558
1441
|
return parsed;
|
|
1559
1442
|
}
|
|
1560
1443
|
|
|
1561
1444
|
const detectedStatus = this.runDetectStatus(recentBuffer);
|
|
1562
|
-
const
|
|
1445
|
+
const resolvedStatus = detectedStatus && detectedStatus !== 'waiting_approval'
|
|
1563
1446
|
? detectedStatus
|
|
1564
1447
|
: ((this.isWaitingForResponse || this.currentTurnScope) ? 'generating' : (this.currentStatus === 'waiting_approval' ? 'idle' : this.currentStatus));
|
|
1565
1448
|
return {
|
|
1566
1449
|
...parsed,
|
|
1567
|
-
status:
|
|
1450
|
+
status: resolvedStatus,
|
|
1568
1451
|
activeModal: null,
|
|
1569
1452
|
};
|
|
1570
1453
|
}
|
|
@@ -1572,8 +1455,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1572
1455
|
// ─── Public API (CliAdapter) ───────────────────
|
|
1573
1456
|
|
|
1574
1457
|
getStatus(): CliSessionStatus {
|
|
1575
|
-
const
|
|
1576
|
-
const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
|
|
1458
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
1577
1459
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
1578
1460
|
let effectiveModal = startupModal || this.activeModal;
|
|
1579
1461
|
if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === 'function') {
|
|
@@ -1688,7 +1570,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1688
1570
|
: message.timestamp,
|
|
1689
1571
|
}));
|
|
1690
1572
|
const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === 'assistant' && typeof message.content === 'string' && message.content.trim());
|
|
1691
|
-
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1692
1573
|
const shouldAdoptParsedIdleReplay =
|
|
1693
1574
|
!this.currentTurnScope
|
|
1694
1575
|
&& !this.activeModal
|
|
@@ -1700,7 +1581,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1700
1581
|
this.currentStatus === 'generating'
|
|
1701
1582
|
&& this.isWaitingForResponse
|
|
1702
1583
|
&& parsed.status === 'idle'
|
|
1703
|
-
&&
|
|
1584
|
+
&& this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
1704
1585
|
)
|
|
1705
1586
|
);
|
|
1706
1587
|
if (shouldAdoptParsedIdleReplay) {
|
|
@@ -1856,17 +1737,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1856
1737
|
if (parsed && typeof parsed === 'object') {
|
|
1857
1738
|
Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
|
|
1858
1739
|
}
|
|
1859
|
-
const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === 'string' ? parsed.status : null, input.recentBuffer, input.screenText);
|
|
1860
|
-
if (parsed && refinedStatus && parsed.status !== refinedStatus) {
|
|
1861
|
-
parsed.status = refinedStatus;
|
|
1862
|
-
}
|
|
1863
1740
|
const normalizedParsed = this.suppressStaleParsedApproval(parsed, input.recentBuffer, input.screenText);
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
const lastAssistant = [...normalizedParsed.messages].reverse().find((message: any) => message?.role === 'assistant' && typeof message.content === 'string');
|
|
1867
|
-
if (lastAssistant) {
|
|
1868
|
-
lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
|
|
1869
|
-
}
|
|
1741
|
+
if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
|
|
1742
|
+
this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
|
|
1870
1743
|
}
|
|
1871
1744
|
this.parseErrorMessage = null;
|
|
1872
1745
|
return normalizedParsed;
|
|
@@ -1896,13 +1769,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1896
1769
|
LOG.warn('CLI', `[${this.cliType}] resolveAction error: ${e.message}`);
|
|
1897
1770
|
}
|
|
1898
1771
|
}
|
|
1899
|
-
if (!promptText
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
}
|
|
1903
|
-
if (promptText) {
|
|
1904
|
-
await this.sendMessage(promptText);
|
|
1772
|
+
if (!promptText) {
|
|
1773
|
+
LOG.warn('CLI', `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
|
|
1774
|
+
return;
|
|
1905
1775
|
}
|
|
1776
|
+
await this.sendMessage(promptText);
|
|
1906
1777
|
}
|
|
1907
1778
|
|
|
1908
1779
|
async sendMessage(text: string): Promise<void> {
|
|
@@ -1923,9 +1794,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1923
1794
|
}
|
|
1924
1795
|
if (!this.ready) {
|
|
1925
1796
|
this.resolveStartupState('send_precheck');
|
|
1926
|
-
|
|
1927
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1928
|
-
if (hasPrompt && this.currentStatus === 'idle') {
|
|
1797
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle' && this.currentStatus === 'idle') {
|
|
1929
1798
|
this.ready = true;
|
|
1930
1799
|
this.startupParseGate = false;
|
|
1931
1800
|
LOG.info('CLI', `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
@@ -2038,7 +1907,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2038
1907
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
2039
1908
|
const screenText = this.terminalScreen.getText();
|
|
2040
1909
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
2041
|
-
|
|
1910
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
1911
|
+
if (liveApproval) return;
|
|
1912
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
1913
|
+
if (liveStatus === 'generating' || liveStatus === 'waiting_approval') return;
|
|
2042
1914
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
2043
1915
|
LOG.info('CLI', `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
2044
1916
|
this.recordTrace('submit_write', {
|
|
@@ -2076,6 +1948,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2076
1948
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
2077
1949
|
const screenText = this.terminalScreen.getText();
|
|
2078
1950
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
1951
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
1952
|
+
if (liveApproval) return;
|
|
1953
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
1954
|
+
if (liveStatus === 'generating' || liveStatus === 'waiting_approval') return;
|
|
2079
1955
|
LOG.info('CLI', `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
2080
1956
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
2081
1957
|
this.recordTrace('submit_write', {
|
|
@@ -2223,17 +2099,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2223
2099
|
|
|
2224
2100
|
shutdown(): void {
|
|
2225
2101
|
this.clearIdleFinishCandidate('shutdown');
|
|
2226
|
-
|
|
2227
|
-
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
2228
|
-
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
2229
|
-
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
2230
|
-
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
2231
|
-
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
2232
|
-
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
2233
|
-
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
2102
|
+
this.clearAllTimers();
|
|
2234
2103
|
this.pendingOutputParseBuffer = '';
|
|
2235
2104
|
this.pendingTerminalQueryTail = '';
|
|
2236
|
-
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
2237
2105
|
this.ptyOutputBuffer = '';
|
|
2238
2106
|
this.finishRetryCount = 0;
|
|
2239
2107
|
if (this.ptyProcess) {
|
|
@@ -2252,17 +2120,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2252
2120
|
|
|
2253
2121
|
detach(): void {
|
|
2254
2122
|
this.clearIdleFinishCandidate('detach');
|
|
2255
|
-
|
|
2256
|
-
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
2257
|
-
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
2258
|
-
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
2259
|
-
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
2260
|
-
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
2261
|
-
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
2262
|
-
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
2123
|
+
this.clearAllTimers();
|
|
2263
2124
|
this.pendingOutputParseBuffer = '';
|
|
2264
2125
|
this.pendingTerminalQueryTail = '';
|
|
2265
|
-
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
2266
2126
|
this.ptyOutputBuffer = '';
|
|
2267
2127
|
this.finishRetryCount = 0;
|
|
2268
2128
|
if (this.ptyProcess) {
|
|
@@ -2314,8 +2174,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2314
2174
|
}
|
|
2315
2175
|
|
|
2316
2176
|
resolveModal(buttonIndex: number): void {
|
|
2317
|
-
|
|
2318
|
-
let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
|
|
2177
|
+
let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
2319
2178
|
if (!modal && typeof this.cliScripts?.parseOutput === 'function') {
|
|
2320
2179
|
try {
|
|
2321
2180
|
const parsed = this.getScriptParsedStatus();
|
|
@@ -2350,12 +2209,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2350
2209
|
}
|
|
2351
2210
|
this.setStatus('generating', 'approval_resolved');
|
|
2352
2211
|
this.onStatusChange?.();
|
|
2353
|
-
|
|
2354
|
-
if (startupTrustModal && buttonIndex in this.approvalKeys) {
|
|
2355
|
-
this.ptyProcess.write(`${this.approvalKeys[buttonIndex]}\r`);
|
|
2356
|
-
} else if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
|
|
2357
|
-
this.ptyProcess.write('\r');
|
|
2358
|
-
} else if (buttonIndex in this.approvalKeys) {
|
|
2212
|
+
if (buttonIndex in this.approvalKeys) {
|
|
2359
2213
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
2360
2214
|
} else {
|
|
2361
2215
|
const DOWN = '\x1B[B';
|
|
@@ -2376,7 +2230,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2376
2230
|
|
|
2377
2231
|
getDebugState(): Record<string, any> {
|
|
2378
2232
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
2379
|
-
const startupModal = this.startupParseGate ? this.
|
|
2233
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
2380
2234
|
const effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
2381
2235
|
const effectiveReady = this.ready || !!startupModal;
|
|
2382
2236
|
return {
|