@adhdev/daemon-core 0.9.6 → 0.9.8
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 -486
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +379 -486
- 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 +303 -446
- 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,11 @@ 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
|
|
892
|
+
const modal = this.runParseApproval(tail) || parsedModal;
|
|
988
893
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
989
|
-
|
|
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
|
|
894
|
+
const scriptStatus = parsedTranscript?.status === 'waiting_approval' && modal
|
|
993
895
|
? 'waiting_approval'
|
|
994
|
-
:
|
|
896
|
+
: rawScriptStatus;
|
|
995
897
|
const parsedMessages = Array.isArray(parsedTranscript?.messages)
|
|
996
898
|
? normalizeCliParsedMessages(parsedTranscript.messages, {
|
|
997
899
|
committedMessages: this.committedMessages,
|
|
@@ -1059,246 +961,262 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1059
961
|
if (!scriptStatus) return;
|
|
1060
962
|
|
|
1061
963
|
const prevStatus = this.currentStatus;
|
|
964
|
+
const ctx: SettledEvalContext = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
|
|
1062
965
|
|
|
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
|
-
}
|
|
966
|
+
if (!this.applyPendingScriptStatusDebounce(ctx)) return;
|
|
1100
967
|
|
|
1101
968
|
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
|
-
}
|
|
969
|
+
LOG.info(
|
|
970
|
+
'CLI',
|
|
971
|
+
`[${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)}`
|
|
972
|
+
);
|
|
973
|
+
|
|
1111
974
|
const shouldHoldGenerating =
|
|
1112
975
|
scriptStatus === 'idle'
|
|
1113
976
|
&& this.isWaitingForResponse
|
|
1114
977
|
&& !modal
|
|
1115
978
|
&& recentInteractiveActivity
|
|
1116
|
-
&& !(visibleIdlePrompt && visibleAssistantCandidate)
|
|
1117
979
|
&& !(parsedTranscript?.status === 'idle' && !!lastParsedAssistant);
|
|
1118
980
|
|
|
1119
981
|
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?.();
|
|
982
|
+
this.applyHoldGenerating(ctx, recentInteractiveActivity);
|
|
1144
983
|
return;
|
|
1145
984
|
}
|
|
1146
985
|
|
|
1147
986
|
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
|
-
}
|
|
987
|
+
this.applyWaitingApproval(ctx);
|
|
988
|
+
return;
|
|
1181
989
|
}
|
|
1182
990
|
|
|
1183
991
|
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?.();
|
|
992
|
+
this.applyGenerating(ctx);
|
|
1215
993
|
return;
|
|
1216
994
|
}
|
|
1217
995
|
|
|
1218
996
|
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
|
-
});
|
|
997
|
+
this.applyIdle(ctx, now);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1271
1000
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1001
|
+
// Returns false if the caller should bail out (debounce pending).
|
|
1002
|
+
private applyPendingScriptStatusDebounce(ctx: SettledEvalContext): boolean {
|
|
1003
|
+
const { now, scriptStatus, prevStatus } = ctx;
|
|
1004
|
+
const shouldDebounce =
|
|
1005
|
+
prevStatus === 'idle'
|
|
1006
|
+
&& !this.isWaitingForResponse
|
|
1007
|
+
&& !this.currentTurnScope
|
|
1008
|
+
&& (scriptStatus === 'generating' || scriptStatus === 'waiting_approval');
|
|
1278
1009
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1010
|
+
if (!shouldDebounce) {
|
|
1011
|
+
this.pendingScriptStatus = null;
|
|
1012
|
+
this.pendingScriptStatusSince = 0;
|
|
1013
|
+
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const armPending = (delayMs: number) => {
|
|
1018
|
+
if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
|
|
1019
|
+
this.pendingScriptStatusTimer = setTimeout(() => {
|
|
1020
|
+
this.pendingScriptStatusTimer = null;
|
|
1021
|
+
this.settledBuffer = this.recentOutputBuffer;
|
|
1022
|
+
this.evaluateSettled();
|
|
1023
|
+
}, delayMs);
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
if (this.pendingScriptStatus !== scriptStatus) {
|
|
1027
|
+
this.pendingScriptStatus = scriptStatus as 'generating' | 'waiting_approval';
|
|
1028
|
+
this.pendingScriptStatusSince = now;
|
|
1029
|
+
armPending(ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
|
|
1030
|
+
return false;
|
|
1031
|
+
}
|
|
1032
|
+
const elapsed = now - this.pendingScriptStatusSince;
|
|
1033
|
+
if (elapsed < ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
|
|
1034
|
+
armPending(ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
private applyHoldGenerating(ctx: SettledEvalContext, recentInteractiveActivity: boolean): void {
|
|
1041
|
+
const { scriptStatus } = ctx;
|
|
1042
|
+
this.clearIdleFinishCandidate('hold_generating_recent_activity');
|
|
1043
|
+
this.setStatus('generating', 'recent_activity_hold');
|
|
1044
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1045
|
+
this.idleTimeout = setTimeout(() => {
|
|
1046
|
+
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1047
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1048
|
+
this.finishResponse();
|
|
1049
|
+
}
|
|
1050
|
+
}, this.timeouts.generatingIdle);
|
|
1051
|
+
this.recordTrace('hold_generating_recent_activity', {
|
|
1052
|
+
scriptStatus,
|
|
1053
|
+
recentInteractiveActivity,
|
|
1054
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
1055
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
1056
|
+
holdMs: this.getStatusActivityHoldMs(),
|
|
1057
|
+
...buildCliTraceParseSnapshot({
|
|
1058
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
1059
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1060
|
+
responseBuffer: this.responseBuffer,
|
|
1061
|
+
partialResponse: this.responseBuffer,
|
|
1062
|
+
scope: this.currentTurnScope,
|
|
1063
|
+
}),
|
|
1064
|
+
});
|
|
1065
|
+
this.onStatusChange?.();
|
|
1066
|
+
}
|
|
1287
1067
|
|
|
1068
|
+
private applyWaitingApproval(ctx: SettledEvalContext): void {
|
|
1069
|
+
const { modal } = ctx;
|
|
1070
|
+
this.clearIdleFinishCandidate('waiting_approval');
|
|
1071
|
+
const inCooldown = this.lastApprovalResolvedAt && (Date.now() - this.lastApprovalResolvedAt) < this.timeouts.approvalCooldown;
|
|
1072
|
+
if (inCooldown && !modal) {
|
|
1073
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1074
|
+
this.activeModal = null;
|
|
1075
|
+
if (this.isWaitingForResponse) {
|
|
1076
|
+
this.setStatus('generating', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
1288
1077
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1289
1078
|
this.idleTimeout = setTimeout(() => {
|
|
1290
1079
|
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1291
1080
|
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1292
|
-
this.clearIdleFinishCandidate('idle_timeout_finish');
|
|
1293
1081
|
this.finishResponse();
|
|
1294
1082
|
}
|
|
1295
|
-
}, this.timeouts.
|
|
1296
|
-
} else
|
|
1083
|
+
}, this.timeouts.generatingIdle);
|
|
1084
|
+
} else {
|
|
1085
|
+
this.setStatus('idle', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
1086
|
+
}
|
|
1087
|
+
this.onStatusChange?.();
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (!inCooldown) {
|
|
1091
|
+
if (!modal) {
|
|
1092
|
+
LOG.warn('CLI', `[${this.cliType}] detectStatus reported waiting_approval without parseApproval modal; ignoring non-actionable approval state`);
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
this.isWaitingForResponse = true;
|
|
1096
|
+
this.setStatus('waiting_approval', 'script_detect');
|
|
1097
|
+
this.activeModal = modal;
|
|
1098
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1099
|
+
this.armApprovalExitTimeout();
|
|
1100
|
+
this.onStatusChange?.();
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
private applyGenerating(ctx: SettledEvalContext): void {
|
|
1105
|
+
const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
|
|
1106
|
+
this.clearIdleFinishCandidate('generating');
|
|
1107
|
+
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
1108
|
+
const noActiveTurn = !this.currentTurnScope;
|
|
1109
|
+
const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText)
|
|
1110
|
+
|| (/accept edits on/i.test(effectiveScreenText)
|
|
1111
|
+
&& (/Update available!/i.test(screenText)
|
|
1112
|
+
|| /\/effort/i.test(screenText)
|
|
1113
|
+
|| /^.*➜\s+\S+/m.test(effectiveScreenText)));
|
|
1114
|
+
if (prevStatus === 'idle' && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
if (prevStatus === 'waiting_approval') {
|
|
1118
|
+
// Transitioned out of approval → generating
|
|
1119
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1120
|
+
this.activeModal = null;
|
|
1121
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
1122
|
+
}
|
|
1123
|
+
if (!this.isWaitingForResponse) {
|
|
1124
|
+
this.isWaitingForResponse = true;
|
|
1125
|
+
this.responseBuffer = '';
|
|
1126
|
+
}
|
|
1127
|
+
this.setStatus('generating', 'script_detect');
|
|
1128
|
+
// Reset idle timeout
|
|
1129
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1130
|
+
this.idleTimeout = setTimeout(() => {
|
|
1131
|
+
if (this.isWaitingForResponse) {
|
|
1132
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1133
|
+
this.finishResponse();
|
|
1134
|
+
}
|
|
1135
|
+
}, this.timeouts.generatingIdle);
|
|
1136
|
+
this.onStatusChange?.();
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
private applyIdle(ctx: SettledEvalContext, now: number): void {
|
|
1140
|
+
const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
|
|
1141
|
+
if (prevStatus === 'waiting_approval') {
|
|
1142
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1143
|
+
this.activeModal = null;
|
|
1144
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
1145
|
+
}
|
|
1146
|
+
if (!this.isWaitingForResponse) {
|
|
1147
|
+
if (prevStatus !== 'idle') {
|
|
1297
1148
|
this.clearIdleFinishCandidate('idle_without_response');
|
|
1298
1149
|
this.setStatus('idle', 'script_detect');
|
|
1299
1150
|
this.onStatusChange?.();
|
|
1300
1151
|
}
|
|
1152
|
+
return;
|
|
1301
1153
|
}
|
|
1154
|
+
const quietForMs = this.lastNonEmptyOutputAt ? (now - this.lastNonEmptyOutputAt) : Number.MAX_SAFE_INTEGER;
|
|
1155
|
+
const screenStableMs = this.lastScreenChangeAt ? (now - this.lastScreenChangeAt) : 0;
|
|
1156
|
+
const hasAssistantTurn = !!lastParsedAssistant;
|
|
1157
|
+
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
1158
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
1159
|
+
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
1160
|
+
const idleReady = !modal
|
|
1161
|
+
&& hasAssistantTurn
|
|
1162
|
+
&& quietForMs >= idleQuietThresholdMs
|
|
1163
|
+
&& screenStableMs >= idleFinishConfirmMs;
|
|
1164
|
+
const candidate = this.idleFinishCandidate;
|
|
1165
|
+
const candidateQuiet = !!candidate
|
|
1166
|
+
&& candidate.responseEpoch === this.responseEpoch
|
|
1167
|
+
&& candidate.lastOutputAt === this.lastOutputAt
|
|
1168
|
+
&& candidate.lastScreenChangeAt === this.lastScreenChangeAt
|
|
1169
|
+
&& assistantLength >= candidate.assistantLength
|
|
1170
|
+
&& (now - candidate.armedAt) >= idleFinishConfirmMs;
|
|
1171
|
+
|
|
1172
|
+
this.recordTrace('idle_decision', {
|
|
1173
|
+
quietForMs,
|
|
1174
|
+
screenStableMs,
|
|
1175
|
+
hasAssistantTurn,
|
|
1176
|
+
assistantLength,
|
|
1177
|
+
hasModal: !!modal,
|
|
1178
|
+
idleQuietThresholdMs,
|
|
1179
|
+
idleStableThresholdMs: idleFinishConfirmMs,
|
|
1180
|
+
idleReady,
|
|
1181
|
+
idleFinishConfirmMs,
|
|
1182
|
+
idleFinishCandidate: candidate,
|
|
1183
|
+
candidateQuiet,
|
|
1184
|
+
canFinishImmediately: idleReady && candidateQuiet,
|
|
1185
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
1186
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
1187
|
+
...buildCliTraceParseSnapshot({
|
|
1188
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
1189
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1190
|
+
responseBuffer: this.responseBuffer,
|
|
1191
|
+
partialResponse: this.responseBuffer,
|
|
1192
|
+
scope: this.currentTurnScope,
|
|
1193
|
+
}),
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
if (idleReady && candidateQuiet) {
|
|
1197
|
+
this.clearIdleFinishCandidate('finish_response');
|
|
1198
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1199
|
+
this.finishResponse();
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (idleReady) {
|
|
1204
|
+
if (!candidate) {
|
|
1205
|
+
this.armIdleFinishCandidate(assistantLength);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
} else {
|
|
1209
|
+
this.clearIdleFinishCandidate('idle_not_ready');
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1213
|
+
this.idleTimeout = setTimeout(() => {
|
|
1214
|
+
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
1215
|
+
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1216
|
+
this.clearIdleFinishCandidate('idle_timeout_finish');
|
|
1217
|
+
this.finishResponse();
|
|
1218
|
+
}
|
|
1219
|
+
}, this.timeouts.idleFinish);
|
|
1302
1220
|
}
|
|
1303
1221
|
|
|
1304
1222
|
private finishResponse(): void {
|
|
@@ -1338,12 +1256,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1338
1256
|
}, ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
|
|
1339
1257
|
return;
|
|
1340
1258
|
}
|
|
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
|
-
|
|
1259
|
+
this.clearAllTimers();
|
|
1347
1260
|
this.responseBuffer = '';
|
|
1348
1261
|
this.isWaitingForResponse = false;
|
|
1349
1262
|
this.responseSettleIgnoreUntil = 0;
|
|
@@ -1356,10 +1269,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1356
1269
|
this.onStatusChange?.();
|
|
1357
1270
|
}
|
|
1358
1271
|
|
|
1359
|
-
private maybeCommitVisibleIdleTranscript(
|
|
1360
|
-
parsed: any,
|
|
1361
|
-
options?: { requireVisibleAssistantCandidate?: boolean; screenText?: string },
|
|
1362
|
-
): boolean {
|
|
1272
|
+
private maybeCommitVisibleIdleTranscript(parsed: any): boolean {
|
|
1363
1273
|
const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
|
|
1364
1274
|
if (!allowImmediateScriptIdleCommit) return false;
|
|
1365
1275
|
if (
|
|
@@ -1374,13 +1284,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1374
1284
|
return false;
|
|
1375
1285
|
}
|
|
1376
1286
|
|
|
1377
|
-
if (options?.requireVisibleAssistantCandidate) {
|
|
1378
|
-
const candidateText = options.screenText || this.terminalScreen.getText() || '';
|
|
1379
|
-
if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
|
|
1380
|
-
return false;
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
1287
|
const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
|
|
1385
1288
|
committedMessages: this.committedMessages,
|
|
1386
1289
|
scope: this.currentTurnScope,
|
|
@@ -1390,18 +1293,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1390
1293
|
if (!visibleAssistant) return false;
|
|
1391
1294
|
|
|
1392
1295
|
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; }
|
|
1296
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
1297
|
+
this.clearAllTimers();
|
|
1405
1298
|
this.syncMessageViews();
|
|
1406
1299
|
this.responseBuffer = '';
|
|
1407
1300
|
this.isWaitingForResponse = false;
|
|
@@ -1432,13 +1325,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1432
1325
|
scope: this.currentTurnScope,
|
|
1433
1326
|
lastOutputAt: this.lastOutputAt,
|
|
1434
1327
|
});
|
|
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
|
-
}
|
|
1328
|
+
this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
|
|
1442
1329
|
this.syncMessageViews();
|
|
1443
1330
|
const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === 'assistant');
|
|
1444
1331
|
if (this.currentTurnScope) {
|
|
@@ -1499,7 +1386,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1499
1386
|
screen: buildCliScreenSnapshot(screenText),
|
|
1500
1387
|
tailScreen: buildCliScreenSnapshot(text.slice(-500)),
|
|
1501
1388
|
});
|
|
1502
|
-
return
|
|
1389
|
+
return status;
|
|
1503
1390
|
} catch (e: any) {
|
|
1504
1391
|
LOG.warn('CLI', `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
1505
1392
|
return null;
|
|
@@ -1552,19 +1439,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1552
1439
|
return parsed;
|
|
1553
1440
|
}
|
|
1554
1441
|
|
|
1555
|
-
const
|
|
1556
|
-
const visibleModal = this.runParseApproval(recentBuffer) || startupModal;
|
|
1442
|
+
const visibleModal = this.runParseApproval(recentBuffer);
|
|
1557
1443
|
if (visibleModal) {
|
|
1558
1444
|
return parsed;
|
|
1559
1445
|
}
|
|
1560
1446
|
|
|
1561
1447
|
const detectedStatus = this.runDetectStatus(recentBuffer);
|
|
1562
|
-
const
|
|
1448
|
+
const resolvedStatus = detectedStatus && detectedStatus !== 'waiting_approval'
|
|
1563
1449
|
? detectedStatus
|
|
1564
1450
|
: ((this.isWaitingForResponse || this.currentTurnScope) ? 'generating' : (this.currentStatus === 'waiting_approval' ? 'idle' : this.currentStatus));
|
|
1565
1451
|
return {
|
|
1566
1452
|
...parsed,
|
|
1567
|
-
status:
|
|
1453
|
+
status: resolvedStatus,
|
|
1568
1454
|
activeModal: null,
|
|
1569
1455
|
};
|
|
1570
1456
|
}
|
|
@@ -1572,8 +1458,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1572
1458
|
// ─── Public API (CliAdapter) ───────────────────
|
|
1573
1459
|
|
|
1574
1460
|
getStatus(): CliSessionStatus {
|
|
1575
|
-
const
|
|
1576
|
-
const startupModal = this.startupParseGate ? this.getStartupConfirmationModal(screenText) : null;
|
|
1461
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
1577
1462
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
1578
1463
|
let effectiveModal = startupModal || this.activeModal;
|
|
1579
1464
|
if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === 'function') {
|
|
@@ -1688,7 +1573,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1688
1573
|
: message.timestamp,
|
|
1689
1574
|
}));
|
|
1690
1575
|
const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === 'assistant' && typeof message.content === 'string' && message.content.trim());
|
|
1691
|
-
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1692
1576
|
const shouldAdoptParsedIdleReplay =
|
|
1693
1577
|
!this.currentTurnScope
|
|
1694
1578
|
&& !this.activeModal
|
|
@@ -1700,7 +1584,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1700
1584
|
this.currentStatus === 'generating'
|
|
1701
1585
|
&& this.isWaitingForResponse
|
|
1702
1586
|
&& parsed.status === 'idle'
|
|
1703
|
-
&&
|
|
1587
|
+
&& this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
1704
1588
|
)
|
|
1705
1589
|
);
|
|
1706
1590
|
if (shouldAdoptParsedIdleReplay) {
|
|
@@ -1856,17 +1740,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1856
1740
|
if (parsed && typeof parsed === 'object') {
|
|
1857
1741
|
Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
|
|
1858
1742
|
}
|
|
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
1743
|
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
|
-
}
|
|
1744
|
+
if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
|
|
1745
|
+
this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
|
|
1870
1746
|
}
|
|
1871
1747
|
this.parseErrorMessage = null;
|
|
1872
1748
|
return normalizedParsed;
|
|
@@ -1896,13 +1772,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1896
1772
|
LOG.warn('CLI', `[${this.cliType}] resolveAction error: ${e.message}`);
|
|
1897
1773
|
}
|
|
1898
1774
|
}
|
|
1899
|
-
if (!promptText
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
}
|
|
1903
|
-
if (promptText) {
|
|
1904
|
-
await this.sendMessage(promptText);
|
|
1775
|
+
if (!promptText) {
|
|
1776
|
+
LOG.warn('CLI', `[${this.cliType}] resolveAction skipped: provider script did not supply a prompt`);
|
|
1777
|
+
return;
|
|
1905
1778
|
}
|
|
1779
|
+
await this.sendMessage(promptText);
|
|
1906
1780
|
}
|
|
1907
1781
|
|
|
1908
1782
|
async sendMessage(text: string): Promise<void> {
|
|
@@ -1923,9 +1797,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1923
1797
|
}
|
|
1924
1798
|
if (!this.ready) {
|
|
1925
1799
|
this.resolveStartupState('send_precheck');
|
|
1926
|
-
|
|
1927
|
-
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1928
|
-
if (hasPrompt && this.currentStatus === 'idle') {
|
|
1800
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle' && this.currentStatus === 'idle') {
|
|
1929
1801
|
this.ready = true;
|
|
1930
1802
|
this.startupParseGate = false;
|
|
1931
1803
|
LOG.info('CLI', `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
@@ -2038,7 +1910,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2038
1910
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
2039
1911
|
const screenText = this.terminalScreen.getText();
|
|
2040
1912
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
2041
|
-
|
|
1913
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
1914
|
+
if (liveApproval) return;
|
|
1915
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
1916
|
+
if (liveStatus === 'generating' || liveStatus === 'waiting_approval') return;
|
|
2042
1917
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
2043
1918
|
LOG.info('CLI', `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
2044
1919
|
this.recordTrace('submit_write', {
|
|
@@ -2076,6 +1951,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2076
1951
|
if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
|
|
2077
1952
|
const screenText = this.terminalScreen.getText();
|
|
2078
1953
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
1954
|
+
const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
|
|
1955
|
+
if (liveApproval) return;
|
|
1956
|
+
const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
|
|
1957
|
+
if (liveStatus === 'generating' || liveStatus === 'waiting_approval') return;
|
|
2079
1958
|
LOG.info('CLI', `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
|
|
2080
1959
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
2081
1960
|
this.recordTrace('submit_write', {
|
|
@@ -2223,17 +2102,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2223
2102
|
|
|
2224
2103
|
shutdown(): void {
|
|
2225
2104
|
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; }
|
|
2105
|
+
this.clearAllTimers();
|
|
2234
2106
|
this.pendingOutputParseBuffer = '';
|
|
2235
2107
|
this.pendingTerminalQueryTail = '';
|
|
2236
|
-
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
2237
2108
|
this.ptyOutputBuffer = '';
|
|
2238
2109
|
this.finishRetryCount = 0;
|
|
2239
2110
|
if (this.ptyProcess) {
|
|
@@ -2252,17 +2123,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2252
2123
|
|
|
2253
2124
|
detach(): void {
|
|
2254
2125
|
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; }
|
|
2126
|
+
this.clearAllTimers();
|
|
2263
2127
|
this.pendingOutputParseBuffer = '';
|
|
2264
2128
|
this.pendingTerminalQueryTail = '';
|
|
2265
|
-
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
2266
2129
|
this.ptyOutputBuffer = '';
|
|
2267
2130
|
this.finishRetryCount = 0;
|
|
2268
2131
|
if (this.ptyProcess) {
|
|
@@ -2314,8 +2177,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2314
2177
|
}
|
|
2315
2178
|
|
|
2316
2179
|
resolveModal(buttonIndex: number): void {
|
|
2317
|
-
|
|
2318
|
-
let modal = this.activeModal || this.getStartupConfirmationModal(screenText);
|
|
2180
|
+
let modal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
2319
2181
|
if (!modal && typeof this.cliScripts?.parseOutput === 'function') {
|
|
2320
2182
|
try {
|
|
2321
2183
|
const parsed = this.getScriptParsedStatus();
|
|
@@ -2350,12 +2212,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2350
2212
|
}
|
|
2351
2213
|
this.setStatus('generating', 'approval_resolved');
|
|
2352
2214
|
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) {
|
|
2215
|
+
if (buttonIndex in this.approvalKeys) {
|
|
2359
2216
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
2360
2217
|
} else {
|
|
2361
2218
|
const DOWN = '\x1B[B';
|
|
@@ -2376,7 +2233,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2376
2233
|
|
|
2377
2234
|
getDebugState(): Record<string, any> {
|
|
2378
2235
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
2379
|
-
const startupModal = this.startupParseGate ? this.
|
|
2236
|
+
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
2380
2237
|
const effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
2381
2238
|
const effectiveReady = this.ready || !!startupModal;
|
|
2382
2239
|
return {
|