@adhdev/daemon-core 0.9.82-rc.13 → 0.9.82-rc.130
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/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +4 -1
- package/dist/cli-adapters/provider-cli-adapter.d.ts +25 -1
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +14 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/chat-history.d.ts +4 -0
- package/dist/config/mesh-config.d.ts +68 -1
- package/dist/git/git-commands.d.ts +5 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.js +7461 -1380
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7417 -1364
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +73 -0
- package/dist/mesh/mesh-events.d.ts +54 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-refine-status.d.ts +27 -0
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +115 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +6 -1
- package/dist/repo-mesh-types.d.ts +62 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/chat/subscription-updates.ts +5 -1
- package/src/cli-adapter-types.ts +2 -1
- package/src/cli-adapters/provider-cli-adapter.ts +473 -18
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +32 -10
- package/src/commands/chat-commands.ts +960 -40
- package/src/commands/cli-manager.ts +138 -2
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +3238 -423
- package/src/config/chat-history.ts +37 -9
- package/src/config/mesh-config.ts +249 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +17 -5
- package/src/index.ts +41 -4
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +31 -8
- package/src/mesh/mesh-active-work.ts +295 -0
- package/src/mesh/mesh-events.ts +595 -48
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-refine-status.ts +145 -0
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +234 -0
- package/src/providers/approval-utils.ts +12 -5
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +289 -36
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +67 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -110,6 +110,14 @@ interface SendMessageCompletion {
|
|
|
110
110
|
rejectOnce: (error: unknown) => void;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
interface PendingOutboundMessage {
|
|
114
|
+
id: string;
|
|
115
|
+
role: 'user';
|
|
116
|
+
content: string;
|
|
117
|
+
queuedAt: number;
|
|
118
|
+
source: 'sendMessage';
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
export function appendBoundedText(current: string, chunk: string, maxChars: number): string {
|
|
114
122
|
if (!chunk) return current.length <= maxChars ? current : current.slice(-maxChars);
|
|
115
123
|
if (maxChars <= 0) return '';
|
|
@@ -137,6 +145,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
137
145
|
private isWaitingForResponse = false;
|
|
138
146
|
private activeModal: { message: string; buttons: string[] } | null = null;
|
|
139
147
|
private parseErrorMessage: string | null = null;
|
|
148
|
+
private providerSessionId: string | null = null;
|
|
149
|
+
private providerErrorMessage: string | null = null;
|
|
150
|
+
private providerErrorReason: string | null = null;
|
|
140
151
|
private responseTimeout: NodeJS.Timeout | null = null;
|
|
141
152
|
private idleTimeout: NodeJS.Timeout | null = null;
|
|
142
153
|
private ready = false;
|
|
@@ -186,6 +197,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
186
197
|
private idleFinishCandidate: IdleFinishCandidate | null = null;
|
|
187
198
|
private finishRetryTimer: NodeJS.Timeout | null = null;
|
|
188
199
|
private finishRetryCount = 0;
|
|
200
|
+
private pendingOutboundQueue: PendingOutboundMessage[] = [];
|
|
201
|
+
private pendingOutboundFlushTimer: NodeJS.Timeout | null = null;
|
|
202
|
+
private pendingOutboundFlushInFlight = false;
|
|
203
|
+
private providerErrorRetryTimer: NodeJS.Timeout | null = null;
|
|
204
|
+
private providerErrorRetryKey = '';
|
|
189
205
|
|
|
190
206
|
// Resize redraw suppression
|
|
191
207
|
private resizeSuppressUntil: number = 0;
|
|
@@ -761,6 +777,17 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
761
777
|
if (stableMs < 2000) return;
|
|
762
778
|
|
|
763
779
|
const startupModal = this.runParseApproval(this.recentOutputBuffer);
|
|
780
|
+
const startupStatus = this.runDetectStatus(screenText || this.recentOutputBuffer);
|
|
781
|
+
if (!startupModal && startupStatus !== 'idle') {
|
|
782
|
+
this.recordTrace('startup_settle_deferred', {
|
|
783
|
+
trigger,
|
|
784
|
+
startupStatus,
|
|
785
|
+
stableMs,
|
|
786
|
+
screenText: summarizeCliTraceText(screenText, 500),
|
|
787
|
+
});
|
|
788
|
+
this.scheduleStartupSettleCheck();
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
764
791
|
this.startupParseGate = false;
|
|
765
792
|
if (this.startupSettleTimer) {
|
|
766
793
|
clearTimeout(this.startupSettleTimer);
|
|
@@ -934,6 +961,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
934
961
|
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
935
962
|
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
936
963
|
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
964
|
+
if (this.providerErrorRetryTimer) { clearTimeout(this.providerErrorRetryTimer); this.providerErrorRetryTimer = null; }
|
|
965
|
+
this.providerErrorRetryKey = '';
|
|
937
966
|
}
|
|
938
967
|
|
|
939
968
|
private clearStaleIdleResponseGuard(reason: string): boolean {
|
|
@@ -956,6 +985,38 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
956
985
|
return true;
|
|
957
986
|
}
|
|
958
987
|
|
|
988
|
+
private clearParsedIdleResponseGuard(reason: string, parsedStatus: any): boolean {
|
|
989
|
+
const parsedRawStatus = typeof parsedStatus?.status === 'string' ? parsedStatus.status.trim() : '';
|
|
990
|
+
const parsedModal = parsedStatus?.activeModal ?? parsedStatus?.modal ?? null;
|
|
991
|
+
const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
992
|
+
if (
|
|
993
|
+
!this.isWaitingForResponse
|
|
994
|
+
|| parsedRawStatus !== 'idle'
|
|
995
|
+
|| !!parsedModal
|
|
996
|
+
|| !!blockingModal
|
|
997
|
+
|| !this.parsedStatusHasFinalAssistantMessage(parsedStatus)
|
|
998
|
+
) {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
this.clearAllTimers();
|
|
1002
|
+
this.clearIdleFinishCandidate(reason);
|
|
1003
|
+
this.responseBuffer = '';
|
|
1004
|
+
this.isWaitingForResponse = false;
|
|
1005
|
+
this.responseSettleIgnoreUntil = 0;
|
|
1006
|
+
this.submitRetryUsed = false;
|
|
1007
|
+
this.submitRetryPromptSnippet = '';
|
|
1008
|
+
this.finishRetryCount = 0;
|
|
1009
|
+
this.currentTurnScope = null;
|
|
1010
|
+
this.activeModal = null;
|
|
1011
|
+
this.setStatus('idle', reason);
|
|
1012
|
+
this.recordTrace('parsed_idle_response_cleared', {
|
|
1013
|
+
reason,
|
|
1014
|
+
parsedStatus: parsedRawStatus,
|
|
1015
|
+
parsedMessageCount: Array.isArray(parsedStatus?.messages) ? parsedStatus.messages.length : 0,
|
|
1016
|
+
});
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
959
1020
|
private hasMeaningfulResponseBuffer(promptSnippet: string): boolean {
|
|
960
1021
|
const raw = String(this.responseBuffer || '').trim();
|
|
961
1022
|
if (!raw) return false;
|
|
@@ -1097,6 +1158,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1097
1158
|
&& !(parsedStatus === 'idle' && !!lastParsedAssistant);
|
|
1098
1159
|
|
|
1099
1160
|
if (shouldHoldGenerating) { this.applyHoldGenerating(ctx, recentInteractiveActivity); return; }
|
|
1161
|
+
if (status === 'error') {
|
|
1162
|
+
if (this.maybeScheduleProviderErrorRetry(ctx, session)) return;
|
|
1163
|
+
this.applyError(ctx, session);
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1100
1166
|
if (status === 'waiting_approval') { this.applyWaitingApproval(ctx); return; }
|
|
1101
1167
|
if (status === 'generating') { this.applyGenerating(ctx); return; }
|
|
1102
1168
|
if (status === 'idle') { this.applyIdle(ctx, now); }
|
|
@@ -1240,6 +1306,107 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1240
1306
|
this.onStatusChange?.();
|
|
1241
1307
|
}
|
|
1242
1308
|
|
|
1309
|
+
private applyError(ctx: SettledEvalContext, session: ParsedSession): void {
|
|
1310
|
+
this.clearIdleFinishCandidate('provider_error');
|
|
1311
|
+
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
1312
|
+
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
1313
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1314
|
+
this.isWaitingForResponse = false;
|
|
1315
|
+
this.responseSettleIgnoreUntil = 0;
|
|
1316
|
+
this.submitRetryUsed = false;
|
|
1317
|
+
this.submitRetryPromptSnippet = '';
|
|
1318
|
+
this.finishRetryCount = 0;
|
|
1319
|
+
this.currentTurnScope = null;
|
|
1320
|
+
this.activeModal = null;
|
|
1321
|
+
this.providerErrorMessage = typeof session.errorMessage === 'string' && session.errorMessage.trim()
|
|
1322
|
+
? session.errorMessage.trim()
|
|
1323
|
+
: 'Provider reported an error';
|
|
1324
|
+
this.providerErrorReason = typeof session.errorReason === 'string' && session.errorReason.trim()
|
|
1325
|
+
? session.errorReason.trim()
|
|
1326
|
+
: 'provider_error';
|
|
1327
|
+
this.setStatus('error', this.providerErrorReason);
|
|
1328
|
+
this.recordTrace('provider_error', {
|
|
1329
|
+
errorMessage: this.providerErrorMessage,
|
|
1330
|
+
errorReason: this.providerErrorReason,
|
|
1331
|
+
parsedStatus: ctx.parsedStatus || ctx.status,
|
|
1332
|
+
messageCount: ctx.parsedMessages.length,
|
|
1333
|
+
...buildCliTraceParseSnapshot({
|
|
1334
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
1335
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1336
|
+
responseBuffer: this.responseBuffer,
|
|
1337
|
+
partialResponse: this.responseBuffer,
|
|
1338
|
+
scope: this.currentTurnScope,
|
|
1339
|
+
}),
|
|
1340
|
+
});
|
|
1341
|
+
this.onStatusChange?.();
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
private maybeScheduleProviderErrorRetry(ctx: SettledEvalContext, session: ParsedSession): boolean {
|
|
1345
|
+
const retryPrompt = typeof (session as any).retryPrompt === 'string'
|
|
1346
|
+
? String((session as any).retryPrompt).trim()
|
|
1347
|
+
: '';
|
|
1348
|
+
const retryDelayMs = typeof (session as any).retryDelayMs === 'number'
|
|
1349
|
+
? Number((session as any).retryDelayMs)
|
|
1350
|
+
: NaN;
|
|
1351
|
+
if (!retryPrompt || !Number.isFinite(retryDelayMs) || retryDelayMs < 0) return false;
|
|
1352
|
+
if (!this.ptyProcess) return false;
|
|
1353
|
+
|
|
1354
|
+
const retryAttempt = typeof (session as any).retryAttempt === 'number'
|
|
1355
|
+
? Number((session as any).retryAttempt)
|
|
1356
|
+
: 0;
|
|
1357
|
+
const retryMaxAttempts = typeof (session as any).retryMaxAttempts === 'number'
|
|
1358
|
+
? Number((session as any).retryMaxAttempts)
|
|
1359
|
+
: 0;
|
|
1360
|
+
const errorReason = typeof session.errorReason === 'string' && session.errorReason.trim()
|
|
1361
|
+
? session.errorReason.trim()
|
|
1362
|
+
: 'provider_error';
|
|
1363
|
+
const retryKey = `${errorReason}:${retryAttempt}:${retryPrompt}`;
|
|
1364
|
+
if (this.providerErrorRetryTimer && this.providerErrorRetryKey === retryKey) return true;
|
|
1365
|
+
|
|
1366
|
+
if (this.providerErrorRetryTimer) clearTimeout(this.providerErrorRetryTimer);
|
|
1367
|
+
this.providerErrorRetryKey = retryKey;
|
|
1368
|
+
this.clearIdleFinishCandidate('provider_error_retry');
|
|
1369
|
+
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
1370
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1371
|
+
this.providerErrorMessage = typeof session.errorMessage === 'string' && session.errorMessage.trim()
|
|
1372
|
+
? session.errorMessage.trim()
|
|
1373
|
+
: 'Provider reported an error';
|
|
1374
|
+
this.providerErrorReason = errorReason;
|
|
1375
|
+
this.activeModal = null;
|
|
1376
|
+
this.responseSettleIgnoreUntil = Date.now() + retryDelayMs + this.timeouts.outputSettle + 400;
|
|
1377
|
+
this.setStatus('generating', 'provider_error_retry_scheduled');
|
|
1378
|
+
this.recordTrace('provider_error_retry_scheduled', {
|
|
1379
|
+
retryPrompt,
|
|
1380
|
+
retryDelayMs,
|
|
1381
|
+
retryAttempt,
|
|
1382
|
+
retryMaxAttempts,
|
|
1383
|
+
errorReason,
|
|
1384
|
+
parsedStatus: ctx.parsedStatus || ctx.status,
|
|
1385
|
+
});
|
|
1386
|
+
this.onStatusChange?.();
|
|
1387
|
+
this.providerErrorRetryTimer = setTimeout(() => {
|
|
1388
|
+
this.providerErrorRetryTimer = null;
|
|
1389
|
+
this.providerErrorRetryKey = '';
|
|
1390
|
+
if (!this.ptyProcess) return;
|
|
1391
|
+
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
1392
|
+
this.submitRetryUsed = false;
|
|
1393
|
+
this.recordTrace('provider_error_retry_write', {
|
|
1394
|
+
retryPrompt,
|
|
1395
|
+
retryAttempt,
|
|
1396
|
+
retryMaxAttempts,
|
|
1397
|
+
errorReason,
|
|
1398
|
+
});
|
|
1399
|
+
this.ptyProcess.write(`${retryPrompt}\r`);
|
|
1400
|
+
if (this.settleTimer) clearTimeout(this.settleTimer);
|
|
1401
|
+
this.settleTimer = setTimeout(() => {
|
|
1402
|
+
this.settleTimer = null;
|
|
1403
|
+
this.settledBuffer = this.recentOutputBuffer;
|
|
1404
|
+
this.evaluateSettled();
|
|
1405
|
+
}, this.timeouts.outputSettle + 150);
|
|
1406
|
+
}, retryDelayMs);
|
|
1407
|
+
return true;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1243
1410
|
private applyIdle(ctx: SettledEvalContext, now: number): void {
|
|
1244
1411
|
const { modal, lastParsedAssistant, prevStatus } = ctx;
|
|
1245
1412
|
if (prevStatus === 'waiting_approval') {
|
|
@@ -1318,6 +1485,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1318
1485
|
this.idleTimeout = setTimeout(() => {
|
|
1319
1486
|
if (this.isWaitingForResponse && !this.hasActionableApproval()) {
|
|
1320
1487
|
if (this.shouldDeferIdleTimeoutFinish()) return;
|
|
1488
|
+
const parsed = this.runParseSession();
|
|
1489
|
+
if (this.shouldKeepCodexTurnOpenForFinish(parsed)) {
|
|
1490
|
+
this.rescheduleCodexFinishCheck('codex_idle_timeout_not_final');
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1321
1493
|
this.clearIdleFinishCandidate('idle_timeout_finish');
|
|
1322
1494
|
this.finishResponse();
|
|
1323
1495
|
}
|
|
@@ -1327,6 +1499,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1327
1499
|
private finishResponse(): void {
|
|
1328
1500
|
if (this.submitPendingUntil > Date.now()) return;
|
|
1329
1501
|
if (this.responseSettleIgnoreUntil > Date.now()) return;
|
|
1502
|
+
const parsedBeforeFinish = this.runParseSession();
|
|
1503
|
+
if (this.shouldKeepCodexTurnOpenForFinish(parsedBeforeFinish)) {
|
|
1504
|
+
this.rescheduleCodexFinishCheck('codex_finish_not_final');
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1330
1507
|
this.clearIdleFinishCandidate('finish_response_enter');
|
|
1331
1508
|
this.recordTrace('finish_response', {
|
|
1332
1509
|
...buildCliTraceParseSnapshot({
|
|
@@ -1372,6 +1549,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1372
1549
|
this.activeModal = null;
|
|
1373
1550
|
this.setStatus('idle', 'response_finished');
|
|
1374
1551
|
this.onStatusChange?.();
|
|
1552
|
+
this.schedulePendingOutboundFlush();
|
|
1375
1553
|
}
|
|
1376
1554
|
|
|
1377
1555
|
private maybeCommitVisibleIdleTranscript(session: ParsedSession, parsedMessages: CliChatMessage[]): boolean {
|
|
@@ -1402,6 +1580,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1402
1580
|
this.activeModal = null;
|
|
1403
1581
|
this.setStatus('idle', 'script_idle_commit');
|
|
1404
1582
|
this.onStatusChange?.();
|
|
1583
|
+
this.schedulePendingOutboundFlush();
|
|
1405
1584
|
this.recordTrace('script_idle_commit', {
|
|
1406
1585
|
messageCount: parsedMessages.length,
|
|
1407
1586
|
lastAssistant: summarizeCliTraceText(visibleAssistant.content, 320),
|
|
@@ -1489,6 +1668,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1489
1668
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1490
1669
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1491
1670
|
terminalScreenText: parseScreenText,
|
|
1671
|
+
workingDir: this.workingDir,
|
|
1492
1672
|
baseMessages: [],
|
|
1493
1673
|
partialResponse: this.responseBuffer,
|
|
1494
1674
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
@@ -1500,6 +1680,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1500
1680
|
{ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) },
|
|
1501
1681
|
);
|
|
1502
1682
|
this.parseErrorMessage = null;
|
|
1683
|
+
if (session && typeof session === 'object') this.applyParsedSessionMetadata(session);
|
|
1503
1684
|
return session && typeof session === 'object' ? session : null;
|
|
1504
1685
|
} catch (e: any) {
|
|
1505
1686
|
const message = e?.message || String(e);
|
|
@@ -1552,6 +1733,74 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1552
1733
|
return !!(startupModal || this.activeModal);
|
|
1553
1734
|
}
|
|
1554
1735
|
|
|
1736
|
+
private parsedStatusHasFinalAssistantMessage(parsed: any): boolean {
|
|
1737
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
1738
|
+
const lastAssistant = [...messages].reverse().find((message: any) => {
|
|
1739
|
+
if (!message || message.role !== 'assistant') return false;
|
|
1740
|
+
return typeof message.content === 'string' && message.content.trim().length > 0;
|
|
1741
|
+
});
|
|
1742
|
+
return !!lastAssistant;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
private applyParsedSessionMetadata(parsed: any): void {
|
|
1746
|
+
const providerSessionId = typeof parsed?.providerSessionId === 'string' && parsed.providerSessionId.trim()
|
|
1747
|
+
? parsed.providerSessionId.trim()
|
|
1748
|
+
: '';
|
|
1749
|
+
if (providerSessionId) {
|
|
1750
|
+
this.providerSessionId = providerSessionId;
|
|
1751
|
+
this.updateRuntimeMeta({ providerSessionId });
|
|
1752
|
+
}
|
|
1753
|
+
this.providerErrorMessage = typeof parsed?.errorMessage === 'string' && parsed.errorMessage.trim()
|
|
1754
|
+
? parsed.errorMessage.trim()
|
|
1755
|
+
: null;
|
|
1756
|
+
this.providerErrorReason = typeof parsed?.errorReason === 'string' && parsed.errorReason.trim()
|
|
1757
|
+
? parsed.errorReason.trim()
|
|
1758
|
+
: null;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
private parsedStatusHasFinalStandardAssistantMessage(parsed: any): boolean {
|
|
1762
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
1763
|
+
const lastAssistant = [...messages].reverse().find((message: any) => {
|
|
1764
|
+
if (!message || message.role !== 'assistant') return false;
|
|
1765
|
+
return typeof message.content === 'string' && message.content.trim().length > 0;
|
|
1766
|
+
});
|
|
1767
|
+
if (!lastAssistant) return false;
|
|
1768
|
+
const kind = typeof lastAssistant.kind === 'string' && lastAssistant.kind.trim()
|
|
1769
|
+
? lastAssistant.kind.trim()
|
|
1770
|
+
: 'standard';
|
|
1771
|
+
return kind === 'standard' && lastAssistant.meta?.streaming !== true;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
private shouldKeepCodexTurnOpenForFinish(parsed: any): boolean {
|
|
1775
|
+
if (this.cliType !== 'codex-cli') return false;
|
|
1776
|
+
if (!this.isWaitingForResponse || !this.currentTurnScope || this.hasActionableApproval()) return false;
|
|
1777
|
+
const parsedStatus = typeof parsed?.status === 'string' ? parsed.status.trim() : '';
|
|
1778
|
+
if (parsedStatus !== 'idle') return true;
|
|
1779
|
+
if (parsed?.activeModal || parsed?.modal) return true;
|
|
1780
|
+
return !this.parsedStatusHasFinalStandardAssistantMessage(parsed);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
private rescheduleCodexFinishCheck(reason: string): void {
|
|
1784
|
+
this.clearIdleFinishCandidate(reason);
|
|
1785
|
+
this.setStatus('generating', reason);
|
|
1786
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1787
|
+
this.idleTimeout = setTimeout(() => {
|
|
1788
|
+
if (!this.isWaitingForResponse || this.hasActionableApproval()) return;
|
|
1789
|
+
this.settledBuffer = this.recentOutputBuffer;
|
|
1790
|
+
this.evaluateSettled();
|
|
1791
|
+
}, this.getIdleFinishConfirmMs());
|
|
1792
|
+
this.recordTrace('codex_finish_deferred', {
|
|
1793
|
+
reason,
|
|
1794
|
+
...buildCliTraceParseSnapshot({
|
|
1795
|
+
accumulatedBuffer: this.accumulatedBuffer,
|
|
1796
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1797
|
+
responseBuffer: this.responseBuffer,
|
|
1798
|
+
partialResponse: this.responseBuffer,
|
|
1799
|
+
scope: this.currentTurnScope,
|
|
1800
|
+
}),
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1555
1804
|
private projectEffectiveStatus(startupModal: { message: string; buttons: string[] } | null = null): CliSessionStatus['status'] {
|
|
1556
1805
|
if (this.parseErrorMessage) return 'error';
|
|
1557
1806
|
if (this.hasActionableApproval(startupModal)) return 'waiting_approval';
|
|
@@ -1564,8 +1813,16 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1564
1813
|
getStatus(options: { allowParse?: boolean } = {}): CliSessionStatus {
|
|
1565
1814
|
const allowParse = options.allowParse !== false;
|
|
1566
1815
|
const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
1816
|
+
const startupDetectedStatus = allowParse && this.startupParseGate && !startupModal
|
|
1817
|
+
? this.runDetectStatus(this.recentOutputBuffer || this.terminalScreen.getText())
|
|
1818
|
+
: null;
|
|
1567
1819
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
1568
1820
|
let effectiveModal = startupModal || this.activeModal;
|
|
1821
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
1822
|
+
effectiveStatus = 'waiting_approval';
|
|
1823
|
+
} else if (startupDetectedStatus === 'idle' && !startupModal && !effectiveModal) {
|
|
1824
|
+
effectiveStatus = 'idle';
|
|
1825
|
+
}
|
|
1569
1826
|
if (allowParse && !startupModal && !effectiveModal) {
|
|
1570
1827
|
const parsed = this.getFreshParsedStatusCache();
|
|
1571
1828
|
const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons)
|
|
@@ -1575,6 +1832,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1575
1832
|
if (parsed?.status === 'waiting_approval' && parsedModal) {
|
|
1576
1833
|
effectiveStatus = 'waiting_approval';
|
|
1577
1834
|
effectiveModal = parsedModal;
|
|
1835
|
+
} else if (
|
|
1836
|
+
effectiveStatus === 'idle'
|
|
1837
|
+
&& parsed?.status === 'generating'
|
|
1838
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsed)
|
|
1839
|
+
) {
|
|
1840
|
+
effectiveStatus = 'generating';
|
|
1841
|
+
} else if (
|
|
1842
|
+
effectiveStatus === 'generating'
|
|
1843
|
+
&& parsed?.status === 'idle'
|
|
1844
|
+
&& this.parsedStatusHasFinalAssistantMessage(parsed)
|
|
1845
|
+
) {
|
|
1846
|
+
effectiveStatus = 'idle';
|
|
1578
1847
|
}
|
|
1579
1848
|
}
|
|
1580
1849
|
const bufferState = this.getBufferState();
|
|
@@ -1583,8 +1852,17 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1583
1852
|
messages: [],
|
|
1584
1853
|
workingDir: this.workingDir,
|
|
1585
1854
|
activeModal: effectiveModal,
|
|
1586
|
-
|
|
1587
|
-
|
|
1855
|
+
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
1856
|
+
pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
|
|
1857
|
+
id: message.id,
|
|
1858
|
+
role: message.role,
|
|
1859
|
+
content: message.content,
|
|
1860
|
+
queuedAt: message.queuedAt,
|
|
1861
|
+
source: message.source,
|
|
1862
|
+
})),
|
|
1863
|
+
errorMessage: this.parseErrorMessage || this.providerErrorMessage || undefined,
|
|
1864
|
+
errorReason: this.parseErrorMessage ? 'parse_error' : (this.providerErrorReason || undefined),
|
|
1865
|
+
providerSessionId: this.providerSessionId || undefined,
|
|
1588
1866
|
...(bufferState ? { bufferState } : {}),
|
|
1589
1867
|
};
|
|
1590
1868
|
}
|
|
@@ -1600,7 +1878,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1600
1878
|
const cached = this.parsedStatusCache;
|
|
1601
1879
|
const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
|
|
1602
1880
|
if (
|
|
1603
|
-
|
|
1881
|
+
!this.providerOwnsTranscript()
|
|
1882
|
+
&& cached
|
|
1604
1883
|
&& cached.responseBuffer === this.responseBuffer
|
|
1605
1884
|
&& cached.currentTurnScope === this.currentTurnScope
|
|
1606
1885
|
&& cached.recentOutputBuffer === this.recentOutputBuffer
|
|
@@ -1630,7 +1909,13 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1630
1909
|
lastOutputAt: this.lastOutputAt,
|
|
1631
1910
|
}),
|
|
1632
1911
|
activeModal,
|
|
1633
|
-
providerSessionId: typeof (parsed as any).providerSessionId === 'string' ? (parsed as any).providerSessionId : undefined,
|
|
1912
|
+
providerSessionId: this.providerSessionId || (typeof (parsed as any).providerSessionId === 'string' ? (parsed as any).providerSessionId : undefined),
|
|
1913
|
+
errorMessage: typeof (parsed as any).errorMessage === 'string' && (parsed as any).errorMessage.trim()
|
|
1914
|
+
? (parsed as any).errorMessage.trim()
|
|
1915
|
+
: undefined,
|
|
1916
|
+
errorReason: typeof (parsed as any).errorReason === 'string' && (parsed as any).errorReason.trim()
|
|
1917
|
+
? (parsed as any).errorReason.trim()
|
|
1918
|
+
: undefined,
|
|
1634
1919
|
...(bufferState ? { bufferState } : {}),
|
|
1635
1920
|
...((parsed as any).transcriptAuthority === 'provider' || (parsed as any).transcriptAuthority === 'daemon'
|
|
1636
1921
|
? { transcriptAuthority: (parsed as any).transcriptAuthority }
|
|
@@ -1665,6 +1950,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1665
1950
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1666
1951
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1667
1952
|
terminalScreenText: this.getParseScreenText(this.terminalScreen.getText()),
|
|
1953
|
+
workingDir: this.workingDir,
|
|
1668
1954
|
baseMessages: [],
|
|
1669
1955
|
partialResponse: this.responseBuffer,
|
|
1670
1956
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
@@ -1924,7 +2210,124 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1924
2210
|
), 50);
|
|
1925
2211
|
}
|
|
1926
2212
|
|
|
1927
|
-
async sendMessage(text: string): Promise<void> {
|
|
2213
|
+
async sendMessage(text: string, options: { force?: boolean } = {}): Promise<void> {
|
|
2214
|
+
if (options.force === true) {
|
|
2215
|
+
await this.forceSendMessage(text);
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
await this.sendMessageNow(text, true);
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
async forceSendMessage(text: string): Promise<void> {
|
|
2222
|
+
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
2223
|
+
const content = String(text || '');
|
|
2224
|
+
if (!content.trim()) return;
|
|
2225
|
+
this.recordTrace('force_send_message', {
|
|
2226
|
+
text: summarizeCliTraceText(content, 500),
|
|
2227
|
+
status: this.currentStatus,
|
|
2228
|
+
isWaitingForResponse: this.isWaitingForResponse,
|
|
2229
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2230
|
+
});
|
|
2231
|
+
LOG.info('CLI', `[${this.cliType}] force-sending prompt while status=${this.currentStatus}`);
|
|
2232
|
+
await this.writeToPty(content + this.sendKey);
|
|
2233
|
+
this.onStatusChange?.();
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
private enqueuePendingOutboundMessage(text: string, reason: string): void {
|
|
2237
|
+
const content = String(text || '');
|
|
2238
|
+
const duplicate = this.pendingOutboundQueue.some((message) => message.content === content);
|
|
2239
|
+
if (duplicate) {
|
|
2240
|
+
this.recordTrace('send_message_queued_duplicate_suppressed', {
|
|
2241
|
+
reason,
|
|
2242
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2243
|
+
text: summarizeCliTraceText(content, 500),
|
|
2244
|
+
});
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
const queuedAt = Date.now();
|
|
2248
|
+
const message: PendingOutboundMessage = {
|
|
2249
|
+
id: `${queuedAt}:${this.pendingOutboundQueue.length}:${Math.random().toString(36).slice(2, 10)}`,
|
|
2250
|
+
role: 'user',
|
|
2251
|
+
content,
|
|
2252
|
+
queuedAt,
|
|
2253
|
+
source: 'sendMessage',
|
|
2254
|
+
};
|
|
2255
|
+
this.pendingOutboundQueue.push(message);
|
|
2256
|
+
this.recordTrace('send_message_queued', {
|
|
2257
|
+
reason,
|
|
2258
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2259
|
+
queuedAt,
|
|
2260
|
+
text: summarizeCliTraceText(content, 500),
|
|
2261
|
+
});
|
|
2262
|
+
LOG.info('CLI', `[${this.cliType}] queued outbound message while busy (${reason}); queue=${this.pendingOutboundQueue.length}`);
|
|
2263
|
+
this.onStatusChange?.();
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
private shouldQueuePendingOutboundMessage(parsedStatusBeforeSend: any | null = null): string | null {
|
|
2267
|
+
if (this.provider.allowInputDuringGeneration === true) return null;
|
|
2268
|
+
if (this.hasActionableApproval()) return null;
|
|
2269
|
+
const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === 'string'
|
|
2270
|
+
? String(parsedStatusBeforeSend.status)
|
|
2271
|
+
: '';
|
|
2272
|
+
if (parsedSessionStatus === 'idle' && this.parsedStatusHasFinalAssistantMessage(parsedStatusBeforeSend)) return null;
|
|
2273
|
+
if (this.currentStatus === 'generating') return 'current_status_generating';
|
|
2274
|
+
if (parsedSessionStatus === 'generating' || parsedSessionStatus === 'long_generating') {
|
|
2275
|
+
const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
|
|
2276
|
+
const parsedHasActionableModal = Boolean(
|
|
2277
|
+
parsedModal
|
|
2278
|
+
&& Array.isArray(parsedModal.buttons)
|
|
2279
|
+
&& parsedModal.buttons.some((candidate: unknown) => typeof candidate === 'string' && candidate.trim()),
|
|
2280
|
+
);
|
|
2281
|
+
const terminalLooksIdle = this.currentStatus === 'idle'
|
|
2282
|
+
&& this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
2283
|
+
&& !this.isWaitingForResponse
|
|
2284
|
+
&& !this.currentTurnScope
|
|
2285
|
+
&& !this.hasActionableApproval()
|
|
2286
|
+
&& !parsedHasActionableModal;
|
|
2287
|
+
return terminalLooksIdle ? null : `parsed_status_${parsedSessionStatus}`;
|
|
2288
|
+
}
|
|
2289
|
+
if (this.isWaitingForResponse && this.currentTurnScope) return 'active_turn_in_progress';
|
|
2290
|
+
return null;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
private schedulePendingOutboundFlush(delayMs = 0): void {
|
|
2294
|
+
if (this.pendingOutboundFlushTimer) clearTimeout(this.pendingOutboundFlushTimer);
|
|
2295
|
+
this.pendingOutboundFlushTimer = setTimeout(() => {
|
|
2296
|
+
this.pendingOutboundFlushTimer = null;
|
|
2297
|
+
void this.flushPendingOutboundQueue();
|
|
2298
|
+
}, Math.max(0, delayMs));
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
private async flushPendingOutboundQueue(): Promise<void> {
|
|
2302
|
+
if (this.pendingOutboundFlushInFlight || this.pendingOutboundQueue.length === 0) return;
|
|
2303
|
+
if (this.currentStatus !== 'idle' || this.isWaitingForResponse || this.hasActionableApproval()) return;
|
|
2304
|
+
this.pendingOutboundFlushInFlight = true;
|
|
2305
|
+
try {
|
|
2306
|
+
while (this.pendingOutboundQueue.length > 0) {
|
|
2307
|
+
if (this.currentStatus !== 'idle' || this.isWaitingForResponse || this.hasActionableApproval()) break;
|
|
2308
|
+
const next = this.pendingOutboundQueue[0];
|
|
2309
|
+
this.recordTrace('send_message_queue_flush', {
|
|
2310
|
+
id: next.id,
|
|
2311
|
+
queuedAt: next.queuedAt,
|
|
2312
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2313
|
+
text: summarizeCliTraceText(next.content, 500),
|
|
2314
|
+
});
|
|
2315
|
+
try {
|
|
2316
|
+
await this.sendMessageNow(next.content, false);
|
|
2317
|
+
this.pendingOutboundQueue.shift();
|
|
2318
|
+
this.onStatusChange?.();
|
|
2319
|
+
} catch (error: any) {
|
|
2320
|
+
LOG.warn('CLI', `[${this.cliType}] queued outbound flush failed: ${error?.message || error}`);
|
|
2321
|
+
this.schedulePendingOutboundFlush(1000);
|
|
2322
|
+
break;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
} finally {
|
|
2326
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
private async sendMessageNow(text: string, allowQueue: boolean): Promise<void> {
|
|
1928
2331
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
1929
2332
|
const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
|
|
1930
2333
|
const allowInterventionPrompt = allowInputDuringGeneration
|
|
@@ -1937,27 +2340,33 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1937
2340
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1938
2341
|
}
|
|
1939
2342
|
}
|
|
2343
|
+
const parsedStatusBeforeSend = !allowInputDuringGeneration
|
|
2344
|
+
? (() => {
|
|
2345
|
+
try {
|
|
2346
|
+
return this.getScriptParsedStatus?.() || null;
|
|
2347
|
+
} catch {
|
|
2348
|
+
return null;
|
|
2349
|
+
}
|
|
2350
|
+
})()
|
|
2351
|
+
: null;
|
|
2352
|
+
const queueReason = this.shouldQueuePendingOutboundMessage(parsedStatusBeforeSend);
|
|
2353
|
+
if (allowQueue && queueReason) {
|
|
2354
|
+
this.enqueuePendingOutboundMessage(text, queueReason);
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
1940
2357
|
if (!allowInterventionPrompt) {
|
|
1941
2358
|
await this.waitForInteractivePrompt();
|
|
1942
2359
|
}
|
|
1943
2360
|
if (!this.ready) {
|
|
1944
2361
|
this.resolveStartupState('send_precheck');
|
|
1945
|
-
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
2362
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle') {
|
|
1946
2363
|
this.ready = true;
|
|
1947
2364
|
this.startupParseGate = false;
|
|
2365
|
+
this.setStatus('idle', 'send_message_idle_prompt_recovery');
|
|
1948
2366
|
LOG.info('CLI', `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
1949
2367
|
}
|
|
1950
2368
|
}
|
|
1951
2369
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
1952
|
-
const parsedStatusBeforeSend = !allowInputDuringGeneration
|
|
1953
|
-
? (() => {
|
|
1954
|
-
try {
|
|
1955
|
-
return this.getScriptParsedStatus?.() || null;
|
|
1956
|
-
} catch {
|
|
1957
|
-
return null;
|
|
1958
|
-
}
|
|
1959
|
-
})()
|
|
1960
|
-
: null;
|
|
1961
2370
|
const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === 'string'
|
|
1962
2371
|
? String(parsedStatusBeforeSend.status)
|
|
1963
2372
|
: '';
|
|
@@ -1975,11 +2384,22 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1975
2384
|
&& !this.hasActionableApproval()
|
|
1976
2385
|
&& !parsedHasActionableModal;
|
|
1977
2386
|
if (!terminalLooksIdle) {
|
|
2387
|
+
if (allowQueue) {
|
|
2388
|
+
this.enqueuePendingOutboundMessage(text, `parsed_status_${parsedSessionStatus}`);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
1978
2391
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1979
2392
|
}
|
|
1980
2393
|
}
|
|
1981
2394
|
if (this.isWaitingForResponse && !allowInputDuringGeneration) {
|
|
1982
|
-
if (
|
|
2395
|
+
if (
|
|
2396
|
+
!this.clearStaleIdleResponseGuard('send_message_guard')
|
|
2397
|
+
&& !this.clearParsedIdleResponseGuard('send_message_parsed_idle_guard', parsedStatusBeforeSend)
|
|
2398
|
+
) {
|
|
2399
|
+
if (allowQueue) {
|
|
2400
|
+
this.enqueuePendingOutboundMessage(text, 'waiting_for_response');
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
1983
2403
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1984
2404
|
}
|
|
1985
2405
|
}
|
|
@@ -2230,6 +2650,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2230
2650
|
this.pendingTerminalQueryTail = '';
|
|
2231
2651
|
this.ptyOutputChunks = [];
|
|
2232
2652
|
this.finishRetryCount = 0;
|
|
2653
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2654
|
+
this.pendingOutboundQueue = [];
|
|
2655
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2233
2656
|
if (this.ptyProcess) {
|
|
2234
2657
|
this.ptyProcess.write('\x03');
|
|
2235
2658
|
setTimeout(() => {
|
|
@@ -2251,6 +2674,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2251
2674
|
this.pendingTerminalQueryTail = '';
|
|
2252
2675
|
this.ptyOutputChunks = [];
|
|
2253
2676
|
this.finishRetryCount = 0;
|
|
2677
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2678
|
+
this.pendingOutboundQueue = [];
|
|
2679
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2254
2680
|
if (this.ptyProcess) {
|
|
2255
2681
|
try {
|
|
2256
2682
|
if (typeof this.ptyProcess.detach === 'function') {
|
|
@@ -2281,6 +2707,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2281
2707
|
this.ptyOutputChunks = [];
|
|
2282
2708
|
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
2283
2709
|
this.finishRetryCount = 0;
|
|
2710
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2711
|
+
this.pendingOutboundQueue = [];
|
|
2712
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2284
2713
|
this.resetTerminalScreen();
|
|
2285
2714
|
this.ptyProcess?.clearBuffer?.();
|
|
2286
2715
|
this.onStatusChange?.();
|
|
@@ -2369,10 +2798,26 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2369
2798
|
getDebugState(): Record<string, any> {
|
|
2370
2799
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
2371
2800
|
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2801
|
+
const startupDetectedStatus = this.startupParseGate && !startupModal
|
|
2802
|
+
? this.runDetectStatus(this.recentOutputBuffer || screenText)
|
|
2803
|
+
: null;
|
|
2804
|
+
const effectiveReady = this.ready || !!startupModal || startupDetectedStatus === 'waiting_approval';
|
|
2374
2805
|
const parsedDebugState = this.getParsedDebugState();
|
|
2375
2806
|
const parsedMessages = Array.isArray(parsedDebugState?.messages) ? parsedDebugState.messages : [];
|
|
2807
|
+
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
2808
|
+
if (parsedDebugState?.status === 'error') {
|
|
2809
|
+
effectiveStatus = 'error';
|
|
2810
|
+
}
|
|
2811
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
2812
|
+
effectiveStatus = 'waiting_approval';
|
|
2813
|
+
}
|
|
2814
|
+
if (
|
|
2815
|
+
effectiveStatus === 'idle'
|
|
2816
|
+
&& parsedDebugState?.status === 'generating'
|
|
2817
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsedDebugState)
|
|
2818
|
+
) {
|
|
2819
|
+
effectiveStatus = 'generating';
|
|
2820
|
+
}
|
|
2376
2821
|
return {
|
|
2377
2822
|
type: this.cliType,
|
|
2378
2823
|
name: this.cliName,
|
|
@@ -2394,6 +2839,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2394
2839
|
providerSessionId: parsedDebugState.providerSessionId,
|
|
2395
2840
|
transcriptAuthority: parsedDebugState.transcriptAuthority,
|
|
2396
2841
|
coverage: parsedDebugState.coverage,
|
|
2842
|
+
errorMessage: parsedDebugState.errorMessage,
|
|
2843
|
+
errorReason: parsedDebugState.errorReason,
|
|
2397
2844
|
activeModal: parsedDebugState.activeModal,
|
|
2398
2845
|
messageCount: parsedMessages.length,
|
|
2399
2846
|
} : null,
|
|
@@ -2407,6 +2854,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2407
2854
|
rawBufferPreview: this.accumulatedRawBuffer.slice(-1000),
|
|
2408
2855
|
sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1000),
|
|
2409
2856
|
responseBuffer: this.responseBuffer.slice(-1000),
|
|
2857
|
+
pendingOutboundQueue: this.pendingOutboundQueue.map((message) => ({
|
|
2858
|
+
id: message.id,
|
|
2859
|
+
role: message.role,
|
|
2860
|
+
content: message.content,
|
|
2861
|
+
queuedAt: message.queuedAt,
|
|
2862
|
+
source: message.source,
|
|
2863
|
+
})),
|
|
2864
|
+
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
2410
2865
|
lastOutputAt: this.lastOutputAt,
|
|
2411
2866
|
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2412
2867
|
lastScreenChangeAt: this.lastScreenChangeAt,
|