@adhdev/daemon-core 0.6.55 → 0.6.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +14 -0
- package/dist/index.js +313 -276
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/cli-adapters/provider-cli-adapter.ts +230 -22
- package/src/cli-adapters/terminal-screen.ts +66 -239
- package/src/daemon/dev-server.ts +69 -35
- package/src/providers/cli-provider-instance.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.57",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"license": "AGPL-3.0-or-later",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
34
|
+
"@xterm/xterm": "^6.0.0",
|
|
34
35
|
"chalk": "^5.3.0",
|
|
35
36
|
"conf": "^13.0.0",
|
|
36
37
|
"ws": "^8.19.0"
|
|
@@ -68,7 +68,7 @@ export interface CliScripts {
|
|
|
68
68
|
/** Full PTY buffer → ReadChatResult (messages, status, activeModal) */
|
|
69
69
|
parseOutput?: (input: CliScriptInput) => any;
|
|
70
70
|
/** Lightweight status detection (high-frequency polling) → AgentStatus string */
|
|
71
|
-
detectStatus?: (input: { tail: string }) => string | null;
|
|
71
|
+
detectStatus?: (input: { tail: string; screenText?: string; rawBuffer?: string }) => string | null;
|
|
72
72
|
/** Parse approval modal from PTY output → ModalInfo | null */
|
|
73
73
|
parseApproval?: (input: { buffer: string; rawBuffer?: string; tail: string }) => { message: string; buttons: string[] } | null;
|
|
74
74
|
/** Produce a cli-specific prompt from a dashboard action payload */
|
|
@@ -91,6 +91,8 @@ export interface CliProviderModule {
|
|
|
91
91
|
name: string;
|
|
92
92
|
category: 'cli';
|
|
93
93
|
binary: string;
|
|
94
|
+
sendDelayMs?: number;
|
|
95
|
+
sendKey?: string;
|
|
94
96
|
spawn: {
|
|
95
97
|
command: string;
|
|
96
98
|
args: string[];
|
|
@@ -191,6 +193,56 @@ function shSingleQuote(arg: string): string {
|
|
|
191
193
|
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
192
194
|
}
|
|
193
195
|
|
|
196
|
+
function estimatePromptDisplayLines(text: string, cols = 100): number {
|
|
197
|
+
const normalized = String(text || '').replace(/\r/g, '');
|
|
198
|
+
if (!normalized) return 1;
|
|
199
|
+
return normalized
|
|
200
|
+
.split('\n')
|
|
201
|
+
.reduce((sum, line) => sum + Math.max(1, Math.ceil(Math.max(1, line.length) / cols)), 0);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function extractPromptRetrySnippet(text: string): string {
|
|
205
|
+
const lines = String(text || '')
|
|
206
|
+
.replace(/\r/g, '')
|
|
207
|
+
.split('\n')
|
|
208
|
+
.map(line => line.trim())
|
|
209
|
+
.filter(Boolean);
|
|
210
|
+
const candidate = lines[lines.length - 1] || lines[0] || '';
|
|
211
|
+
return candidate.slice(-120);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function normalizePromptText(text: string): string {
|
|
215
|
+
return String(text || '').replace(/\s+/g, ' ').trim();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function compactPromptText(text: string): string {
|
|
219
|
+
return String(text || '').replace(/\s+/g, '').trim();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function promptLikelyVisible(screenText: string, promptSnippet: string): boolean {
|
|
223
|
+
const snippet = normalizePromptText(promptSnippet);
|
|
224
|
+
if (!snippet) return false;
|
|
225
|
+
|
|
226
|
+
const normalizedScreen = normalizePromptText(screenText);
|
|
227
|
+
if (normalizedScreen.includes(snippet)) return true;
|
|
228
|
+
|
|
229
|
+
const compactScreen = compactPromptText(screenText);
|
|
230
|
+
const compactSnippet = compactPromptText(promptSnippet);
|
|
231
|
+
if (compactSnippet && compactScreen.includes(compactSnippet)) return true;
|
|
232
|
+
|
|
233
|
+
const tokens = snippet
|
|
234
|
+
.split(/[^A-Za-z0-9_.:/-]+/)
|
|
235
|
+
.map(token => token.trim())
|
|
236
|
+
.filter(token => token.length >= 4);
|
|
237
|
+
if (tokens.length === 0) return false;
|
|
238
|
+
|
|
239
|
+
const required = Math.min(tokens.length, 3);
|
|
240
|
+
const matched = tokens.filter(token =>
|
|
241
|
+
normalizedScreen.includes(token) || compactScreen.includes(compactPromptText(token)),
|
|
242
|
+
).length;
|
|
243
|
+
return matched >= required;
|
|
244
|
+
}
|
|
245
|
+
|
|
194
246
|
/**
|
|
195
247
|
* Normalize provider.json for auto-implement approval detection.
|
|
196
248
|
* Kept for backward compat with dev-server auto-impl pipeline only.
|
|
@@ -270,6 +322,12 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
270
322
|
// Output settle debounce — fires after PTY output goes quiet
|
|
271
323
|
private settleTimer: NodeJS.Timeout | null = null;
|
|
272
324
|
private settledBuffer: string = '';
|
|
325
|
+
private submitPendingUntil = 0;
|
|
326
|
+
private responseSettleIgnoreUntil = 0;
|
|
327
|
+
private responseEpoch = 0;
|
|
328
|
+
private submitRetryTimer: NodeJS.Timeout | null = null;
|
|
329
|
+
private submitRetryUsed = false;
|
|
330
|
+
private submitRetryPromptSnippet = '';
|
|
273
331
|
|
|
274
332
|
// Resize redraw suppression
|
|
275
333
|
private resizeSuppressUntil: number = 0;
|
|
@@ -302,6 +360,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
302
360
|
|
|
303
361
|
// Provider approval key mapping
|
|
304
362
|
private readonly approvalKeys: Record<number, string>;
|
|
363
|
+
private readonly sendDelayMs: number;
|
|
364
|
+
private readonly sendKey: string;
|
|
305
365
|
|
|
306
366
|
constructor(provider: CliProviderModule, workingDir: string, private extraArgs: string[] = []) {
|
|
307
367
|
this.provider = provider;
|
|
@@ -325,6 +385,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
325
385
|
|
|
326
386
|
const rawKeys = (provider as any).approvalKeys;
|
|
327
387
|
this.approvalKeys = (rawKeys && typeof rawKeys === 'object') ? rawKeys : {};
|
|
388
|
+
this.sendDelayMs = typeof (provider as any).sendDelayMs === 'number' ? Math.max(0, (provider as any).sendDelayMs) : 0;
|
|
389
|
+
this.sendKey = typeof (provider as any).sendKey === 'string' && (provider as any).sendKey.length > 0
|
|
390
|
+
? (provider as any).sendKey
|
|
391
|
+
: '\r';
|
|
328
392
|
|
|
329
393
|
// Scripts are required — loaded by ProviderLoader via compatibility array
|
|
330
394
|
this.cliScripts = (provider as any).scripts || {};
|
|
@@ -450,7 +514,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
450
514
|
this.startupParseGate = true;
|
|
451
515
|
this.startupBuffer = '';
|
|
452
516
|
this.terminalScreen.reset(40, 120);
|
|
453
|
-
this.ready =
|
|
517
|
+
this.ready = false;
|
|
454
518
|
this.setStatus('idle', 'pty_ready');
|
|
455
519
|
this.onStatusChange?.();
|
|
456
520
|
}
|
|
@@ -460,6 +524,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
460
524
|
private handleOutput(rawData: string): void {
|
|
461
525
|
if (Date.now() < this.resizeSuppressUntil) return;
|
|
462
526
|
|
|
527
|
+
if (rawData.includes('\x1b[6n') || rawData.includes('\x1b[?6n')) {
|
|
528
|
+
// Some TUIs probe cursor position during startup; node-pty does not answer automatically.
|
|
529
|
+
this.ptyProcess?.write('\x1b[1;1R');
|
|
530
|
+
}
|
|
531
|
+
|
|
463
532
|
this.terminalScreen.write(rawData);
|
|
464
533
|
const cleanData = stripAnsi(rawData);
|
|
465
534
|
|
|
@@ -507,7 +576,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
507
576
|
|
|
508
577
|
if (isReady) {
|
|
509
578
|
this.startupParseGate = false;
|
|
579
|
+
this.ready = true;
|
|
510
580
|
LOG.info('CLI', `[${this.cliType}] Startup gate end (${elapsed}ms, scriptStatus=${scriptStatus})`);
|
|
581
|
+
this.onStatusChange?.();
|
|
511
582
|
} else {
|
|
512
583
|
return;
|
|
513
584
|
}
|
|
@@ -519,16 +590,49 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
519
590
|
|
|
520
591
|
private scheduleSettle(): void {
|
|
521
592
|
if (this.settleTimer) clearTimeout(this.settleTimer);
|
|
593
|
+
const settleEpoch = this.responseEpoch;
|
|
594
|
+
const delay = Math.max(
|
|
595
|
+
this.timeouts.outputSettle,
|
|
596
|
+
this.submitPendingUntil > Date.now()
|
|
597
|
+
? (this.submitPendingUntil - Date.now()) + this.timeouts.outputSettle
|
|
598
|
+
: 0,
|
|
599
|
+
);
|
|
522
600
|
this.settleTimer = setTimeout(() => {
|
|
523
601
|
this.settleTimer = null;
|
|
602
|
+
if (settleEpoch !== this.responseEpoch) return;
|
|
524
603
|
this.settledBuffer = this.recentOutputBuffer;
|
|
525
604
|
this.evaluateSettled();
|
|
526
|
-
},
|
|
605
|
+
}, delay);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private armApprovalExitTimeout(): void {
|
|
609
|
+
if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
|
|
610
|
+
this.approvalExitTimeout = setTimeout(() => {
|
|
611
|
+
if (this.currentStatus !== 'waiting_approval') return;
|
|
612
|
+
const tail = this.recentOutputBuffer;
|
|
613
|
+
const modal = this.runParseApproval(tail);
|
|
614
|
+
const stillWaiting = this.runDetectStatus(tail) === 'waiting_approval' || !!modal;
|
|
615
|
+
if (stillWaiting) {
|
|
616
|
+
this.activeModal = modal || this.activeModal || { message: 'Approval required', buttons: ['Allow', 'Deny'] };
|
|
617
|
+
this.onStatusChange?.();
|
|
618
|
+
this.armApprovalExitTimeout();
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
LOG.warn('CLI', `[${this.cliType}] Approval timeout — auto-clearing`);
|
|
622
|
+
this.activeModal = null;
|
|
623
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
624
|
+
this.setStatus('idle', 'approval_timeout');
|
|
625
|
+
this.onStatusChange?.();
|
|
626
|
+
}, 60000);
|
|
527
627
|
}
|
|
528
628
|
|
|
529
629
|
private evaluateSettled(): void {
|
|
630
|
+
if (this.submitPendingUntil > Date.now()) return;
|
|
631
|
+
if (this.responseSettleIgnoreUntil > Date.now()) return;
|
|
530
632
|
const tail = this.settledBuffer;
|
|
531
|
-
const
|
|
633
|
+
const modal = this.runParseApproval(tail);
|
|
634
|
+
const rawScriptStatus = this.runDetectStatus(tail);
|
|
635
|
+
const scriptStatus = rawScriptStatus === 'waiting_approval' || modal ? 'waiting_approval' : rawScriptStatus;
|
|
532
636
|
if (!scriptStatus) return;
|
|
533
637
|
|
|
534
638
|
const prevStatus = this.currentStatus;
|
|
@@ -540,20 +644,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
540
644
|
this.setStatus('waiting_approval', 'script_detect');
|
|
541
645
|
|
|
542
646
|
// Use parseApproval script for modal info
|
|
543
|
-
const modal = this.runParseApproval(tail);
|
|
544
647
|
this.activeModal = modal || { message: 'Approval required', buttons: ['Allow', 'Deny'] };
|
|
545
648
|
|
|
546
649
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
547
|
-
|
|
548
|
-
this.approvalExitTimeout = setTimeout(() => {
|
|
549
|
-
if (this.currentStatus === 'waiting_approval') {
|
|
550
|
-
LOG.warn('CLI', `[${this.cliType}] Approval timeout — auto-clearing`);
|
|
551
|
-
this.activeModal = null;
|
|
552
|
-
this.lastApprovalResolvedAt = Date.now();
|
|
553
|
-
this.setStatus('idle', 'approval_timeout');
|
|
554
|
-
this.onStatusChange?.();
|
|
555
|
-
}
|
|
556
|
-
}, 60000);
|
|
650
|
+
this.armApprovalExitTimeout();
|
|
557
651
|
this.onStatusChange?.();
|
|
558
652
|
return;
|
|
559
653
|
}
|
|
@@ -587,7 +681,12 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
587
681
|
this.lastApprovalResolvedAt = Date.now();
|
|
588
682
|
}
|
|
589
683
|
if (this.isWaitingForResponse) {
|
|
590
|
-
this.
|
|
684
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
685
|
+
this.idleTimeout = setTimeout(() => {
|
|
686
|
+
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
687
|
+
this.finishResponse();
|
|
688
|
+
}
|
|
689
|
+
}, this.timeouts.idleFinish);
|
|
591
690
|
} else if (prevStatus !== 'idle') {
|
|
592
691
|
this.setStatus('idle', 'script_detect');
|
|
593
692
|
this.onStatusChange?.();
|
|
@@ -596,12 +695,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
596
695
|
}
|
|
597
696
|
|
|
598
697
|
private finishResponse(): void {
|
|
698
|
+
if (this.submitPendingUntil > Date.now()) return;
|
|
699
|
+
if (this.responseSettleIgnoreUntil > Date.now()) return;
|
|
599
700
|
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
600
701
|
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
601
702
|
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
703
|
+
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
602
704
|
|
|
603
705
|
this.responseBuffer = '';
|
|
604
706
|
this.isWaitingForResponse = false;
|
|
707
|
+
this.responseSettleIgnoreUntil = 0;
|
|
708
|
+
this.submitRetryUsed = false;
|
|
709
|
+
this.submitRetryPromptSnippet = '';
|
|
605
710
|
this.activeModal = null;
|
|
606
711
|
this.setStatus('idle', 'response_finished');
|
|
607
712
|
this.onStatusChange?.();
|
|
@@ -612,7 +717,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
612
717
|
private runDetectStatus(text: string): string | null {
|
|
613
718
|
if (!this.cliScripts?.detectStatus) return null;
|
|
614
719
|
try {
|
|
615
|
-
return this.cliScripts.detectStatus({
|
|
720
|
+
return this.cliScripts.detectStatus({
|
|
721
|
+
tail: text.slice(-500),
|
|
722
|
+
screenText: this.terminalScreen.getText(),
|
|
723
|
+
rawBuffer: this.accumulatedRawBuffer,
|
|
724
|
+
});
|
|
616
725
|
} catch (e: any) {
|
|
617
726
|
LOG.warn('CLI', `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
618
727
|
return null;
|
|
@@ -720,6 +829,12 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
720
829
|
|
|
721
830
|
async sendMessage(text: string): Promise<void> {
|
|
722
831
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
832
|
+
if (this.startupParseGate) {
|
|
833
|
+
const deadline = Date.now() + 10000;
|
|
834
|
+
while (this.startupParseGate && Date.now() < deadline) {
|
|
835
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
836
|
+
}
|
|
837
|
+
}
|
|
723
838
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
724
839
|
if (this.isWaitingForResponse) return;
|
|
725
840
|
|
|
@@ -727,14 +842,87 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
727
842
|
this.structuredMessages.push({ role: 'user', content: text, timestamp: Date.now() });
|
|
728
843
|
this.isWaitingForResponse = true;
|
|
729
844
|
this.responseBuffer = '';
|
|
845
|
+
this.submitRetryUsed = false;
|
|
846
|
+
this.submitRetryPromptSnippet = extractPromptRetrySnippet(text);
|
|
847
|
+
const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet);
|
|
848
|
+
if (this.submitRetryTimer) {
|
|
849
|
+
clearTimeout(this.submitRetryTimer);
|
|
850
|
+
this.submitRetryTimer = null;
|
|
851
|
+
}
|
|
852
|
+
const estimatedLines = estimatePromptDisplayLines(text);
|
|
853
|
+
const submitDelayMs = this.sendDelayMs + Math.min(2000, Math.max(0, estimatedLines - 1) * 350);
|
|
854
|
+
const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5000, estimatedLines * 500));
|
|
855
|
+
const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
|
|
856
|
+
if (this.settleTimer) {
|
|
857
|
+
clearTimeout(this.settleTimer);
|
|
858
|
+
this.settleTimer = null;
|
|
859
|
+
}
|
|
860
|
+
this.responseEpoch += 1;
|
|
861
|
+
this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
|
|
730
862
|
this.setStatus('generating', 'sendMessage');
|
|
731
863
|
this.onStatusChange?.();
|
|
864
|
+
if (submitDelayMs > 0) {
|
|
865
|
+
this.submitPendingUntil = Date.now() + submitDelayMs;
|
|
866
|
+
}
|
|
867
|
+
this.ptyProcess.write(text);
|
|
868
|
+
|
|
869
|
+
const submit = () => {
|
|
870
|
+
if (!this.ptyProcess) return;
|
|
871
|
+
this.submitPendingUntil = 0;
|
|
872
|
+
this.ptyProcess.write(this.sendKey);
|
|
873
|
+
const retrySubmitIfStuck = (attempt: number) => {
|
|
874
|
+
this.submitRetryTimer = null;
|
|
875
|
+
if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
|
|
876
|
+
if (this.currentStatus !== 'generating') return;
|
|
877
|
+
if ((this.responseBuffer || '').trim()) return;
|
|
878
|
+
const screenText = this.terminalScreen.getText();
|
|
879
|
+
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
880
|
+
if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText)) return;
|
|
881
|
+
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
882
|
+
LOG.info('CLI', `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
|
|
883
|
+
this.ptyProcess.write(this.sendKey);
|
|
884
|
+
if (attempt >= 3) {
|
|
885
|
+
this.submitRetryUsed = true;
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
|
|
889
|
+
};
|
|
890
|
+
this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
|
|
891
|
+
this.responseTimeout = setTimeout(() => {
|
|
892
|
+
if (this.isWaitingForResponse) this.finishResponse();
|
|
893
|
+
}, this.timeouts.maxResponse);
|
|
894
|
+
};
|
|
895
|
+
const submitStartedAt = Date.now();
|
|
896
|
+
let lastNormalizedScreen = '';
|
|
897
|
+
let lastScreenChangeAt = submitStartedAt;
|
|
898
|
+
const waitForEchoAndSubmit = () => {
|
|
899
|
+
if (!this.ptyProcess) return;
|
|
900
|
+
const now = Date.now();
|
|
901
|
+
const elapsed = now - submitStartedAt;
|
|
902
|
+
const screenText = this.terminalScreen.getText();
|
|
903
|
+
const normalizedScreen = normalizePromptText(screenText);
|
|
904
|
+
if (normalizedScreen !== lastNormalizedScreen) {
|
|
905
|
+
lastNormalizedScreen = normalizedScreen;
|
|
906
|
+
lastScreenChangeAt = now;
|
|
907
|
+
}
|
|
908
|
+
const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
|
|
909
|
+
|
|
910
|
+
if (echoVisible) {
|
|
911
|
+
const screenSettled = (now - lastScreenChangeAt) >= 500;
|
|
912
|
+
if (elapsed >= submitDelayMs && screenSettled) {
|
|
913
|
+
submit();
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
732
917
|
|
|
733
|
-
|
|
918
|
+
if (elapsed >= maxEchoWaitMs) {
|
|
919
|
+
submit();
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
734
922
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
923
|
+
setTimeout(waitForEchoAndSubmit, 50);
|
|
924
|
+
};
|
|
925
|
+
waitForEchoAndSubmit();
|
|
738
926
|
}
|
|
739
927
|
|
|
740
928
|
getPartialResponse(): string {
|
|
@@ -747,6 +935,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
747
935
|
shutdown(): void {
|
|
748
936
|
if (this.settleTimer) { clearTimeout(this.settleTimer); this.settleTimer = null; }
|
|
749
937
|
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
938
|
+
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
750
939
|
if (this.ptyProcess) {
|
|
751
940
|
this.ptyProcess.write('\x03');
|
|
752
941
|
setTimeout(() => {
|
|
@@ -766,6 +955,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
766
955
|
this.structuredMessages = [];
|
|
767
956
|
this.accumulatedBuffer = '';
|
|
768
957
|
this.accumulatedRawBuffer = '';
|
|
958
|
+
this.submitRetryUsed = false;
|
|
959
|
+
this.submitRetryPromptSnippet = '';
|
|
769
960
|
this.terminalScreen.reset();
|
|
770
961
|
this.onStatusChange?.();
|
|
771
962
|
}
|
|
@@ -778,7 +969,16 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
778
969
|
}
|
|
779
970
|
|
|
780
971
|
resolveModal(buttonIndex: number): void {
|
|
781
|
-
if (!this.ptyProcess || this.currentStatus !== 'waiting_approval') return;
|
|
972
|
+
if (!this.ptyProcess || (this.currentStatus !== 'waiting_approval' && !this.activeModal)) return;
|
|
973
|
+
this.activeModal = null;
|
|
974
|
+
this.lastApprovalResolvedAt = Date.now();
|
|
975
|
+
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
976
|
+
if (this.approvalExitTimeout) {
|
|
977
|
+
clearTimeout(this.approvalExitTimeout);
|
|
978
|
+
this.approvalExitTimeout = null;
|
|
979
|
+
}
|
|
980
|
+
this.setStatus('generating', 'approval_resolved');
|
|
981
|
+
this.onStatusChange?.();
|
|
782
982
|
if (buttonIndex in this.approvalKeys) {
|
|
783
983
|
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
784
984
|
} else {
|
|
@@ -810,13 +1010,21 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
810
1010
|
messages: this.messages.slice(-20),
|
|
811
1011
|
structuredMessages: this.structuredMessages.slice(-20),
|
|
812
1012
|
messageCount: this.messages.length,
|
|
1013
|
+
screenText: this.terminalScreen.getText().slice(-4000),
|
|
813
1014
|
startupBuffer: this.startupBuffer.slice(-4000),
|
|
814
1015
|
recentOutputBuffer: this.recentOutputBuffer.slice(-500),
|
|
815
1016
|
settledBuffer: this.settledBuffer.slice(-500),
|
|
816
1017
|
accumulatedBufferLength: this.accumulatedBuffer.length,
|
|
1018
|
+
accumulatedRawBufferLength: this.accumulatedRawBuffer.length,
|
|
1019
|
+
rawBufferPreview: this.accumulatedRawBuffer.slice(-1000),
|
|
1020
|
+
responseBuffer: this.responseBuffer.slice(-1000),
|
|
817
1021
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
818
1022
|
activeModal: this.activeModal,
|
|
819
1023
|
lastApprovalResolvedAt: this.lastApprovalResolvedAt,
|
|
1024
|
+
sendDelayMs: this.sendDelayMs,
|
|
1025
|
+
sendKey: this.sendKey,
|
|
1026
|
+
submitPendingUntil: this.submitPendingUntil,
|
|
1027
|
+
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
820
1028
|
resizeSuppressUntil: this.resizeSuppressUntil,
|
|
821
1029
|
hasCliScripts: this.hasCliScripts(),
|
|
822
1030
|
scriptNames: Object.keys(this.cliScripts).filter(k => typeof (this.cliScripts as any)[k] === 'function'),
|