@adhdev/daemon-core 0.9.82-rc.11 → 0.9.82-rc.111
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 +21 -0
- 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 +66 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +6001 -1225
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5967 -1212
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +60 -0
- package/dist/mesh/mesh-events.d.ts +26 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/refine-config.d.ts +176 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/repo-mesh-types.d.ts +45 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/cli-adapters/provider-cli-adapter.ts +453 -17
- 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 +626 -20
- package/src/commands/cli-manager.ts +129 -1
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +2820 -437
- package/src/config/chat-history.ts +37 -9
- package/src/config/mesh-config.ts +245 -1
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/index.ts +30 -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 +255 -0
- package/src/mesh/mesh-events.ts +389 -47
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/refine-config.ts +356 -0
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +143 -18
- 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 +50 -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,
|
|
@@ -1925,6 +2211,104 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1925
2211
|
}
|
|
1926
2212
|
|
|
1927
2213
|
async sendMessage(text: string): Promise<void> {
|
|
2214
|
+
await this.sendMessageNow(text, true);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
private enqueuePendingOutboundMessage(text: string, reason: string): void {
|
|
2218
|
+
const content = String(text || '');
|
|
2219
|
+
const duplicate = this.pendingOutboundQueue.some((message) => message.content === content);
|
|
2220
|
+
if (duplicate) {
|
|
2221
|
+
this.recordTrace('send_message_queued_duplicate_suppressed', {
|
|
2222
|
+
reason,
|
|
2223
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2224
|
+
text: summarizeCliTraceText(content, 500),
|
|
2225
|
+
});
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
const queuedAt = Date.now();
|
|
2229
|
+
const message: PendingOutboundMessage = {
|
|
2230
|
+
id: `${queuedAt}:${this.pendingOutboundQueue.length}:${Math.random().toString(36).slice(2, 10)}`,
|
|
2231
|
+
role: 'user',
|
|
2232
|
+
content,
|
|
2233
|
+
queuedAt,
|
|
2234
|
+
source: 'sendMessage',
|
|
2235
|
+
};
|
|
2236
|
+
this.pendingOutboundQueue.push(message);
|
|
2237
|
+
this.recordTrace('send_message_queued', {
|
|
2238
|
+
reason,
|
|
2239
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2240
|
+
queuedAt,
|
|
2241
|
+
text: summarizeCliTraceText(content, 500),
|
|
2242
|
+
});
|
|
2243
|
+
LOG.info('CLI', `[${this.cliType}] queued outbound message while busy (${reason}); queue=${this.pendingOutboundQueue.length}`);
|
|
2244
|
+
this.onStatusChange?.();
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
private shouldQueuePendingOutboundMessage(parsedStatusBeforeSend: any | null = null): string | null {
|
|
2248
|
+
if (this.provider.allowInputDuringGeneration === true) return null;
|
|
2249
|
+
if (this.hasActionableApproval()) return null;
|
|
2250
|
+
const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === 'string'
|
|
2251
|
+
? String(parsedStatusBeforeSend.status)
|
|
2252
|
+
: '';
|
|
2253
|
+
if (parsedSessionStatus === 'idle' && this.parsedStatusHasFinalAssistantMessage(parsedStatusBeforeSend)) return null;
|
|
2254
|
+
if (this.currentStatus === 'generating') return 'current_status_generating';
|
|
2255
|
+
if (parsedSessionStatus === 'generating' || parsedSessionStatus === 'long_generating') {
|
|
2256
|
+
const parsedModal = parsedStatusBeforeSend?.activeModal ?? parsedStatusBeforeSend?.modal ?? null;
|
|
2257
|
+
const parsedHasActionableModal = Boolean(
|
|
2258
|
+
parsedModal
|
|
2259
|
+
&& Array.isArray(parsedModal.buttons)
|
|
2260
|
+
&& parsedModal.buttons.some((candidate: unknown) => typeof candidate === 'string' && candidate.trim()),
|
|
2261
|
+
);
|
|
2262
|
+
const terminalLooksIdle = this.currentStatus === 'idle'
|
|
2263
|
+
&& this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
2264
|
+
&& !this.isWaitingForResponse
|
|
2265
|
+
&& !this.currentTurnScope
|
|
2266
|
+
&& !this.hasActionableApproval()
|
|
2267
|
+
&& !parsedHasActionableModal;
|
|
2268
|
+
return terminalLooksIdle ? null : `parsed_status_${parsedSessionStatus}`;
|
|
2269
|
+
}
|
|
2270
|
+
if (this.isWaitingForResponse && this.currentTurnScope) return 'active_turn_in_progress';
|
|
2271
|
+
return null;
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
private schedulePendingOutboundFlush(delayMs = 0): void {
|
|
2275
|
+
if (this.pendingOutboundFlushTimer) clearTimeout(this.pendingOutboundFlushTimer);
|
|
2276
|
+
this.pendingOutboundFlushTimer = setTimeout(() => {
|
|
2277
|
+
this.pendingOutboundFlushTimer = null;
|
|
2278
|
+
void this.flushPendingOutboundQueue();
|
|
2279
|
+
}, Math.max(0, delayMs));
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
private async flushPendingOutboundQueue(): Promise<void> {
|
|
2283
|
+
if (this.pendingOutboundFlushInFlight || this.pendingOutboundQueue.length === 0) return;
|
|
2284
|
+
if (this.currentStatus !== 'idle' || this.isWaitingForResponse || this.hasActionableApproval()) return;
|
|
2285
|
+
this.pendingOutboundFlushInFlight = true;
|
|
2286
|
+
try {
|
|
2287
|
+
while (this.pendingOutboundQueue.length > 0) {
|
|
2288
|
+
if (this.currentStatus !== 'idle' || this.isWaitingForResponse || this.hasActionableApproval()) break;
|
|
2289
|
+
const next = this.pendingOutboundQueue[0];
|
|
2290
|
+
this.recordTrace('send_message_queue_flush', {
|
|
2291
|
+
id: next.id,
|
|
2292
|
+
queuedAt: next.queuedAt,
|
|
2293
|
+
queueLength: this.pendingOutboundQueue.length,
|
|
2294
|
+
text: summarizeCliTraceText(next.content, 500),
|
|
2295
|
+
});
|
|
2296
|
+
try {
|
|
2297
|
+
await this.sendMessageNow(next.content, false);
|
|
2298
|
+
this.pendingOutboundQueue.shift();
|
|
2299
|
+
this.onStatusChange?.();
|
|
2300
|
+
} catch (error: any) {
|
|
2301
|
+
LOG.warn('CLI', `[${this.cliType}] queued outbound flush failed: ${error?.message || error}`);
|
|
2302
|
+
this.schedulePendingOutboundFlush(1000);
|
|
2303
|
+
break;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
} finally {
|
|
2307
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
private async sendMessageNow(text: string, allowQueue: boolean): Promise<void> {
|
|
1928
2312
|
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
1929
2313
|
const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
|
|
1930
2314
|
const allowInterventionPrompt = allowInputDuringGeneration
|
|
@@ -1937,27 +2321,33 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1937
2321
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1938
2322
|
}
|
|
1939
2323
|
}
|
|
2324
|
+
const parsedStatusBeforeSend = !allowInputDuringGeneration
|
|
2325
|
+
? (() => {
|
|
2326
|
+
try {
|
|
2327
|
+
return this.getScriptParsedStatus?.() || null;
|
|
2328
|
+
} catch {
|
|
2329
|
+
return null;
|
|
2330
|
+
}
|
|
2331
|
+
})()
|
|
2332
|
+
: null;
|
|
2333
|
+
const queueReason = this.shouldQueuePendingOutboundMessage(parsedStatusBeforeSend);
|
|
2334
|
+
if (allowQueue && queueReason) {
|
|
2335
|
+
this.enqueuePendingOutboundMessage(text, queueReason);
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
1940
2338
|
if (!allowInterventionPrompt) {
|
|
1941
2339
|
await this.waitForInteractivePrompt();
|
|
1942
2340
|
}
|
|
1943
2341
|
if (!this.ready) {
|
|
1944
2342
|
this.resolveStartupState('send_precheck');
|
|
1945
|
-
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle'
|
|
2343
|
+
if (this.runDetectStatus(this.recentOutputBuffer) === 'idle') {
|
|
1946
2344
|
this.ready = true;
|
|
1947
2345
|
this.startupParseGate = false;
|
|
2346
|
+
this.setStatus('idle', 'send_message_idle_prompt_recovery');
|
|
1948
2347
|
LOG.info('CLI', `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
1949
2348
|
}
|
|
1950
2349
|
}
|
|
1951
2350
|
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
2351
|
const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === 'string'
|
|
1962
2352
|
? String(parsedStatusBeforeSend.status)
|
|
1963
2353
|
: '';
|
|
@@ -1975,11 +2365,22 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1975
2365
|
&& !this.hasActionableApproval()
|
|
1976
2366
|
&& !parsedHasActionableModal;
|
|
1977
2367
|
if (!terminalLooksIdle) {
|
|
2368
|
+
if (allowQueue) {
|
|
2369
|
+
this.enqueuePendingOutboundMessage(text, `parsed_status_${parsedSessionStatus}`);
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
1978
2372
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1979
2373
|
}
|
|
1980
2374
|
}
|
|
1981
2375
|
if (this.isWaitingForResponse && !allowInputDuringGeneration) {
|
|
1982
|
-
if (
|
|
2376
|
+
if (
|
|
2377
|
+
!this.clearStaleIdleResponseGuard('send_message_guard')
|
|
2378
|
+
&& !this.clearParsedIdleResponseGuard('send_message_parsed_idle_guard', parsedStatusBeforeSend)
|
|
2379
|
+
) {
|
|
2380
|
+
if (allowQueue) {
|
|
2381
|
+
this.enqueuePendingOutboundMessage(text, 'waiting_for_response');
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
1983
2384
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1984
2385
|
}
|
|
1985
2386
|
}
|
|
@@ -2230,6 +2631,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2230
2631
|
this.pendingTerminalQueryTail = '';
|
|
2231
2632
|
this.ptyOutputChunks = [];
|
|
2232
2633
|
this.finishRetryCount = 0;
|
|
2634
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2635
|
+
this.pendingOutboundQueue = [];
|
|
2636
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2233
2637
|
if (this.ptyProcess) {
|
|
2234
2638
|
this.ptyProcess.write('\x03');
|
|
2235
2639
|
setTimeout(() => {
|
|
@@ -2251,6 +2655,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2251
2655
|
this.pendingTerminalQueryTail = '';
|
|
2252
2656
|
this.ptyOutputChunks = [];
|
|
2253
2657
|
this.finishRetryCount = 0;
|
|
2658
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2659
|
+
this.pendingOutboundQueue = [];
|
|
2660
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2254
2661
|
if (this.ptyProcess) {
|
|
2255
2662
|
try {
|
|
2256
2663
|
if (typeof this.ptyProcess.detach === 'function') {
|
|
@@ -2281,6 +2688,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2281
2688
|
this.ptyOutputChunks = [];
|
|
2282
2689
|
if (this.finishRetryTimer) { clearTimeout(this.finishRetryTimer); this.finishRetryTimer = null; }
|
|
2283
2690
|
this.finishRetryCount = 0;
|
|
2691
|
+
if (this.pendingOutboundFlushTimer) { clearTimeout(this.pendingOutboundFlushTimer); this.pendingOutboundFlushTimer = null; }
|
|
2692
|
+
this.pendingOutboundQueue = [];
|
|
2693
|
+
this.pendingOutboundFlushInFlight = false;
|
|
2284
2694
|
this.resetTerminalScreen();
|
|
2285
2695
|
this.ptyProcess?.clearBuffer?.();
|
|
2286
2696
|
this.onStatusChange?.();
|
|
@@ -2369,10 +2779,26 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2369
2779
|
getDebugState(): Record<string, any> {
|
|
2370
2780
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
2371
2781
|
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2782
|
+
const startupDetectedStatus = this.startupParseGate && !startupModal
|
|
2783
|
+
? this.runDetectStatus(this.recentOutputBuffer || screenText)
|
|
2784
|
+
: null;
|
|
2785
|
+
const effectiveReady = this.ready || !!startupModal || startupDetectedStatus === 'waiting_approval';
|
|
2374
2786
|
const parsedDebugState = this.getParsedDebugState();
|
|
2375
2787
|
const parsedMessages = Array.isArray(parsedDebugState?.messages) ? parsedDebugState.messages : [];
|
|
2788
|
+
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
2789
|
+
if (parsedDebugState?.status === 'error') {
|
|
2790
|
+
effectiveStatus = 'error';
|
|
2791
|
+
}
|
|
2792
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
2793
|
+
effectiveStatus = 'waiting_approval';
|
|
2794
|
+
}
|
|
2795
|
+
if (
|
|
2796
|
+
effectiveStatus === 'idle'
|
|
2797
|
+
&& parsedDebugState?.status === 'generating'
|
|
2798
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsedDebugState)
|
|
2799
|
+
) {
|
|
2800
|
+
effectiveStatus = 'generating';
|
|
2801
|
+
}
|
|
2376
2802
|
return {
|
|
2377
2803
|
type: this.cliType,
|
|
2378
2804
|
name: this.cliName,
|
|
@@ -2394,6 +2820,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2394
2820
|
providerSessionId: parsedDebugState.providerSessionId,
|
|
2395
2821
|
transcriptAuthority: parsedDebugState.transcriptAuthority,
|
|
2396
2822
|
coverage: parsedDebugState.coverage,
|
|
2823
|
+
errorMessage: parsedDebugState.errorMessage,
|
|
2824
|
+
errorReason: parsedDebugState.errorReason,
|
|
2397
2825
|
activeModal: parsedDebugState.activeModal,
|
|
2398
2826
|
messageCount: parsedMessages.length,
|
|
2399
2827
|
} : null,
|
|
@@ -2407,6 +2835,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2407
2835
|
rawBufferPreview: this.accumulatedRawBuffer.slice(-1000),
|
|
2408
2836
|
sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1000),
|
|
2409
2837
|
responseBuffer: this.responseBuffer.slice(-1000),
|
|
2838
|
+
pendingOutboundQueue: this.pendingOutboundQueue.map((message) => ({
|
|
2839
|
+
id: message.id,
|
|
2840
|
+
role: message.role,
|
|
2841
|
+
content: message.content,
|
|
2842
|
+
queuedAt: message.queuedAt,
|
|
2843
|
+
source: message.source,
|
|
2844
|
+
})),
|
|
2845
|
+
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
2410
2846
|
lastOutputAt: this.lastOutputAt,
|
|
2411
2847
|
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
2412
2848
|
lastScreenChangeAt: this.lastScreenChangeAt,
|