chatccc 0.2.236 → 0.2.238
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/package.json +1 -1
- package/src/__tests__/builtin-session-search.test.ts +34 -0
- package/src/__tests__/restart.test.ts +232 -232
- package/src/__tests__/session.test.ts +99 -5
- package/src/__tests__/web-ui.test.ts +436 -436
- package/src/builtin/session-search.ts +7 -0
- package/src/orchestrator.ts +2543 -2543
- package/src/session-chat-binding.ts +1 -0
- package/src/session.ts +103 -33
|
@@ -111,6 +111,7 @@ export interface ActivePrompt {
|
|
|
111
111
|
processPid?: number;
|
|
112
112
|
processMonitor?: ReturnType<typeof setInterval>;
|
|
113
113
|
responseStallMonitor?: ReturnType<typeof setInterval>;
|
|
114
|
+
avatarRefreshTimer?: ReturnType<typeof setInterval>;
|
|
114
115
|
/** Grace timer that force-closes a stream which stays open after its authoritative final event. */
|
|
115
116
|
finalResponseCloseTimer?: ReturnType<typeof setTimeout>;
|
|
116
117
|
/** Character-count progress observed only while the activity is "responding". */
|
package/src/session.ts
CHANGED
|
@@ -174,8 +174,9 @@ function platformForChat(chatId: string): PlatformAdapter | null {
|
|
|
174
174
|
return chatPlatformMap.get(chatId) ?? platformRef;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
const DEFAULT_PROCESS_MONITOR_INTERVAL_MS = 5000;
|
|
178
|
-
const
|
|
177
|
+
const DEFAULT_PROCESS_MONITOR_INTERVAL_MS = 5000;
|
|
178
|
+
const DEFAULT_AVATAR_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
179
|
+
const DEFAULT_RESPONSE_STALL_TIMEOUT_MS = 3 * 60 * 1000;
|
|
179
180
|
const DEFAULT_RESPONSE_STALL_CHECK_INTERVAL_MS = 5000;
|
|
180
181
|
const DEFAULT_FINAL_RESPONSE_CLOSE_TIMEOUT_MS = 10_000;
|
|
181
182
|
export const RESPONSE_STALL_RECOVERY_PROMPT = "完成了吗?如果没完成继续";
|
|
@@ -184,8 +185,9 @@ export const RESPONSE_STALL_RECOVERY_NOTICE =
|
|
|
184
185
|
export const RESPONSE_STALL_RECOVERY_EXHAUSTED_NOTICE =
|
|
185
186
|
"⚠️ 自动续跑仍连续 3 分钟没有生成新回复,本次不再自动继续。";
|
|
186
187
|
const RESPONSE_STALL_RECOVERY_DELAY_MS = 200;
|
|
187
|
-
let processMonitorIntervalMs = DEFAULT_PROCESS_MONITOR_INTERVAL_MS;
|
|
188
|
-
let
|
|
188
|
+
let processMonitorIntervalMs = DEFAULT_PROCESS_MONITOR_INTERVAL_MS;
|
|
189
|
+
let avatarRefreshIntervalMs = DEFAULT_AVATAR_REFRESH_INTERVAL_MS;
|
|
190
|
+
let responseStallTimeoutMs = DEFAULT_RESPONSE_STALL_TIMEOUT_MS;
|
|
189
191
|
let responseStallCheckIntervalMs = DEFAULT_RESPONSE_STALL_CHECK_INTERVAL_MS;
|
|
190
192
|
let finalResponseCloseTimeoutMs = DEFAULT_FINAL_RESPONSE_CLOSE_TIMEOUT_MS;
|
|
191
193
|
let isProcessAliveImpl = (pid: number): boolean => {
|
|
@@ -216,9 +218,17 @@ export function _setProcessMonitorIntervalForTest(ms: number): void {
|
|
|
216
218
|
processMonitorIntervalMs = ms;
|
|
217
219
|
}
|
|
218
220
|
|
|
219
|
-
export function _resetProcessMonitorIntervalForTest(): void {
|
|
220
|
-
processMonitorIntervalMs = DEFAULT_PROCESS_MONITOR_INTERVAL_MS;
|
|
221
|
-
}
|
|
221
|
+
export function _resetProcessMonitorIntervalForTest(): void {
|
|
222
|
+
processMonitorIntervalMs = DEFAULT_PROCESS_MONITOR_INTERVAL_MS;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function _setAvatarRefreshIntervalForTest(ms: number): void {
|
|
226
|
+
avatarRefreshIntervalMs = ms;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function _resetAvatarRefreshIntervalForTest(): void {
|
|
230
|
+
avatarRefreshIntervalMs = DEFAULT_AVATAR_REFRESH_INTERVAL_MS;
|
|
231
|
+
}
|
|
222
232
|
|
|
223
233
|
export function _setResponseStallTimeoutForTest(ms: number): void {
|
|
224
234
|
responseStallTimeoutMs = ms;
|
|
@@ -251,14 +261,21 @@ function clearPromptProcessMonitor(sessionId: string): void {
|
|
|
251
261
|
prompt.processMonitor = undefined;
|
|
252
262
|
}
|
|
253
263
|
|
|
254
|
-
function clearPromptResponseStallMonitor(sessionId: string): void {
|
|
255
|
-
const prompt = activePrompts.get(sessionId);
|
|
256
|
-
if (!prompt?.responseStallMonitor) return;
|
|
257
|
-
clearInterval(prompt.responseStallMonitor);
|
|
258
|
-
prompt.responseStallMonitor = undefined;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function
|
|
264
|
+
function clearPromptResponseStallMonitor(sessionId: string): void {
|
|
265
|
+
const prompt = activePrompts.get(sessionId);
|
|
266
|
+
if (!prompt?.responseStallMonitor) return;
|
|
267
|
+
clearInterval(prompt.responseStallMonitor);
|
|
268
|
+
prompt.responseStallMonitor = undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function clearPromptAvatarRefreshTimer(sessionId: string): void {
|
|
272
|
+
const prompt = activePrompts.get(sessionId);
|
|
273
|
+
if (!prompt?.avatarRefreshTimer) return;
|
|
274
|
+
clearInterval(prompt.avatarRefreshTimer);
|
|
275
|
+
prompt.avatarRefreshTimer = undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function clearPromptFinalResponseCloseTimer(sessionId: string): void {
|
|
262
279
|
const prompt = activePrompts.get(sessionId);
|
|
263
280
|
if (!prompt?.finalResponseCloseTimer) return;
|
|
264
281
|
clearTimeout(prompt.finalResponseCloseTimer);
|
|
@@ -513,10 +530,11 @@ export function resetState(): void {
|
|
|
513
530
|
clearFeishuMessageLedgerMemory();
|
|
514
531
|
lastMsgTimestamps.clear();
|
|
515
532
|
chatPlatformMap.clear();
|
|
516
|
-
for (const prompt of activePrompts.values()) {
|
|
517
|
-
if (prompt.processMonitor) clearInterval(prompt.processMonitor);
|
|
518
|
-
if (prompt.responseStallMonitor) clearInterval(prompt.responseStallMonitor);
|
|
519
|
-
if (prompt.
|
|
533
|
+
for (const prompt of activePrompts.values()) {
|
|
534
|
+
if (prompt.processMonitor) clearInterval(prompt.processMonitor);
|
|
535
|
+
if (prompt.responseStallMonitor) clearInterval(prompt.responseStallMonitor);
|
|
536
|
+
if (prompt.avatarRefreshTimer) clearInterval(prompt.avatarRefreshTimer);
|
|
537
|
+
if (prompt.finalResponseCloseTimer) clearTimeout(prompt.finalResponseCloseTimer);
|
|
520
538
|
}
|
|
521
539
|
activePrompts.clear();
|
|
522
540
|
displayCards.clear();
|
|
@@ -582,7 +600,7 @@ export function getEffectiveFastModeForTool(tool: string, sessionId?: string): b
|
|
|
582
600
|
return config.codex.fastMode;
|
|
583
601
|
}
|
|
584
602
|
|
|
585
|
-
function setSessionChatAvatar(
|
|
603
|
+
function setSessionChatAvatar(
|
|
586
604
|
platform: PlatformAdapter,
|
|
587
605
|
chatId: string,
|
|
588
606
|
tool: string,
|
|
@@ -591,8 +609,59 @@ function setSessionChatAvatar(
|
|
|
591
609
|
): Promise<void> {
|
|
592
610
|
return getEffectiveFastModeForTool(tool, sessionId)
|
|
593
611
|
? platform.setChatAvatar(chatId, tool, status, { fastMode: true })
|
|
594
|
-
: platform.setChatAvatar(chatId, tool, status);
|
|
595
|
-
}
|
|
612
|
+
: platform.setChatAvatar(chatId, tool, status);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
async function refreshBusySessionAvatar(
|
|
616
|
+
sessionId: string,
|
|
617
|
+
tool: string,
|
|
618
|
+
fallbackPlatform: PlatformAdapter,
|
|
619
|
+
): Promise<void> {
|
|
620
|
+
const chatId = pickDisplayChat(sessionId) ?? getLastActiveChat(sessionId) ?? getChatsForSession(sessionId)[0];
|
|
621
|
+
if (!chatId) return;
|
|
622
|
+
const platform = platformForChat(chatId) ?? fallbackPlatform;
|
|
623
|
+
await setSessionChatAvatar(platform, chatId, tool, "busy", sessionId);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function startPromptAvatarRefresh(
|
|
627
|
+
sessionId: string,
|
|
628
|
+
tool: string,
|
|
629
|
+
fallbackPlatform: PlatformAdapter,
|
|
630
|
+
runningPrompt: NonNullable<ReturnType<typeof activePrompts.get>>,
|
|
631
|
+
): void {
|
|
632
|
+
let refreshInFlight = false;
|
|
633
|
+
const timer = setInterval(() => {
|
|
634
|
+
const current = activePrompts.get(sessionId);
|
|
635
|
+
if (!current || current !== runningPrompt) {
|
|
636
|
+
clearInterval(timer);
|
|
637
|
+
if (runningPrompt.avatarRefreshTimer === timer) {
|
|
638
|
+
runningPrompt.avatarRefreshTimer = undefined;
|
|
639
|
+
}
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (
|
|
643
|
+
refreshInFlight
|
|
644
|
+
|| current.stopped
|
|
645
|
+
|| current.abnormalExit
|
|
646
|
+
|| current.resourceStuck
|
|
647
|
+
|| current.autoEnded
|
|
648
|
+
|| current.finalResponseObserved
|
|
649
|
+
) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
refreshInFlight = true;
|
|
654
|
+
void refreshBusySessionAvatar(sessionId, tool, fallbackPlatform)
|
|
655
|
+
.catch((err) => {
|
|
656
|
+
console.warn(`[${ts()}] [AVATAR] Periodic refresh failed for ${sessionId}: ${(err as Error).message}`);
|
|
657
|
+
})
|
|
658
|
+
.finally(() => {
|
|
659
|
+
refreshInFlight = false;
|
|
660
|
+
});
|
|
661
|
+
}, avatarRefreshIntervalMs);
|
|
662
|
+
timer.unref?.();
|
|
663
|
+
runningPrompt.avatarRefreshTimer = timer;
|
|
664
|
+
}
|
|
596
665
|
|
|
597
666
|
/** 为指定 session 设置模型覆盖(/model <name>) */
|
|
598
667
|
export function setSessionModelOverride(sessionId: string, model: string): void {
|
|
@@ -1435,10 +1504,7 @@ export async function runAgentSession(
|
|
|
1435
1504
|
}
|
|
1436
1505
|
|
|
1437
1506
|
// 设置最后活跃群头像为 busy
|
|
1438
|
-
|
|
1439
|
-
if (activeCid) {
|
|
1440
|
-
setSessionChatAvatar(platform, activeCid, tool, "busy", sessionId).catch(() => {});
|
|
1441
|
-
}
|
|
1507
|
+
refreshBusySessionAvatar(sessionId, tool, platform).catch(() => {});
|
|
1442
1508
|
|
|
1443
1509
|
const state: AccumulatorState = {
|
|
1444
1510
|
accumulatedContent: "",
|
|
@@ -1456,6 +1522,8 @@ export async function runAgentSession(
|
|
|
1456
1522
|
|
|
1457
1523
|
const runningPrompt = activePrompts.get(sessionId);
|
|
1458
1524
|
if (runningPrompt) {
|
|
1525
|
+
startPromptAvatarRefresh(sessionId, tool, platform, runningPrompt);
|
|
1526
|
+
|
|
1459
1527
|
// 在消费第一个事件前建立阶段感知的零字符基线;启动阶段不计时,只有后续
|
|
1460
1528
|
// 收到明确的 responding 状态后才会启动三分钟回复停滞保护。
|
|
1461
1529
|
runningPrompt.responseProgress = observeResponseProgress(
|
|
@@ -1642,9 +1710,10 @@ export async function runAgentSession(
|
|
|
1642
1710
|
const wasAutoEnded = timeoutTriggered && !completedAtTimeoutBoundary;
|
|
1643
1711
|
const wasAutoRecovery = prompt?.autoRecovery ?? false;
|
|
1644
1712
|
const autoEndedAt = wasAutoEnded ? prompt?.autoEndedAt : undefined;
|
|
1645
|
-
clearPromptResponseStallMonitor(sessionId);
|
|
1646
|
-
clearPromptProcessMonitor(sessionId);
|
|
1647
|
-
|
|
1713
|
+
clearPromptResponseStallMonitor(sessionId);
|
|
1714
|
+
clearPromptProcessMonitor(sessionId);
|
|
1715
|
+
clearPromptAvatarRefreshTimer(sessionId);
|
|
1716
|
+
clearPromptFinalResponseCloseTimer(sessionId);
|
|
1648
1717
|
markSessionFinalizing(sessionId);
|
|
1649
1718
|
activePrompts.delete(sessionId);
|
|
1650
1719
|
|
|
@@ -2351,10 +2420,11 @@ export function stopSession(sessionId: string): boolean {
|
|
|
2351
2420
|
}
|
|
2352
2421
|
return false;
|
|
2353
2422
|
}
|
|
2354
|
-
prompt.stopped = true;
|
|
2355
|
-
clearPromptResponseStallMonitor(sessionId);
|
|
2356
|
-
clearPromptProcessMonitor(sessionId);
|
|
2357
|
-
|
|
2423
|
+
prompt.stopped = true;
|
|
2424
|
+
clearPromptResponseStallMonitor(sessionId);
|
|
2425
|
+
clearPromptProcessMonitor(sessionId);
|
|
2426
|
+
clearPromptAvatarRefreshTimer(sessionId);
|
|
2427
|
+
clearPromptFinalResponseCloseTimer(sessionId);
|
|
2358
2428
|
cancelQueuedMessage(sessionId);
|
|
2359
2429
|
|
|
2360
2430
|
// 先发起整棵进程树清理,再触发 close/abort。Windows 上 CLI 由
|