chatccc 0.2.237 → 0.2.239
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
CHANGED
|
@@ -56,6 +56,42 @@ afterEach(() => {
|
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
describe("ChatSession context management", () => {
|
|
59
|
+
it("keeps the generalized evidence gate in the stable system prompt prefix", async () => {
|
|
60
|
+
const { ChatSession } = await import("../builtin/index.ts");
|
|
61
|
+
const dir = await mkdtemp(join(tmpdir(), "deepccc-session-evidence-gate-"));
|
|
62
|
+
await writeFile(join(dir, "AGENTS.md"), "PROJECT GUIDANCE MARKER", "utf-8");
|
|
63
|
+
streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
|
|
64
|
+
|
|
65
|
+
const session = new ChatSession(
|
|
66
|
+
{ apiKey: "sk-test" },
|
|
67
|
+
{
|
|
68
|
+
cwd: dir,
|
|
69
|
+
sessionId: "evidence-gate",
|
|
70
|
+
systemPrompt: "CUSTOM PROMPT MARKER",
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
await collect(session.chat("diagnose a consequential problem"));
|
|
74
|
+
|
|
75
|
+
const system = streamTextMock.mock.calls.at(-1)?.[0].system as string;
|
|
76
|
+
expect(system).toContain("## Evidence-Gated Conclusions");
|
|
77
|
+
expect(system).toContain("source of truth");
|
|
78
|
+
expect(system).toContain("direct observations from inferences");
|
|
79
|
+
expect(system).toContain("plausible alternative explanations");
|
|
80
|
+
expect(system).toContain("runtime behavior for runtime claims");
|
|
81
|
+
expect(system).toContain("state uncertainty");
|
|
82
|
+
expect(system).toContain("Do not repeat checks once decisive evidence exists");
|
|
83
|
+
expect(system).not.toContain("CodesForUnity");
|
|
84
|
+
expect(system.indexOf("## Evidence-Gated Conclusions")).toBeLessThan(
|
|
85
|
+
system.indexOf("PROJECT GUIDANCE MARKER"),
|
|
86
|
+
);
|
|
87
|
+
expect(system.indexOf("## Evidence-Gated Conclusions")).toBeLessThan(
|
|
88
|
+
system.indexOf("Current working directory"),
|
|
89
|
+
);
|
|
90
|
+
expect(system.indexOf("## Evidence-Gated Conclusions")).toBeLessThan(
|
|
91
|
+
system.indexOf("CUSTOM PROMPT MARKER"),
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
59
95
|
it("injects cwd project instruction files before runtime workspace details", async () => {
|
|
60
96
|
const { ChatSession } = await import("../builtin/index.ts");
|
|
61
97
|
const dir = await mkdtemp(join(tmpdir(), "deepccc-session-instructions-"));
|
|
@@ -131,9 +131,11 @@ import {
|
|
|
131
131
|
stopUnifiedDisplayLoop,
|
|
132
132
|
_setProcessAliveForTest,
|
|
133
133
|
_resetProcessAliveForTest,
|
|
134
|
-
_setProcessMonitorIntervalForTest,
|
|
135
|
-
_resetProcessMonitorIntervalForTest,
|
|
136
|
-
|
|
134
|
+
_setProcessMonitorIntervalForTest,
|
|
135
|
+
_resetProcessMonitorIntervalForTest,
|
|
136
|
+
_setAvatarRefreshIntervalForTest,
|
|
137
|
+
_resetAvatarRefreshIntervalForTest,
|
|
138
|
+
_setResponseStallTimeoutForTest,
|
|
137
139
|
_resetResponseStallTimeoutForTest,
|
|
138
140
|
_setResponseStallCheckIntervalForTest,
|
|
139
141
|
_resetResponseStallCheckIntervalForTest,
|
|
@@ -436,7 +438,7 @@ describe("runAgentSession previous final delivery guard", () => {
|
|
|
436
438
|
});
|
|
437
439
|
});
|
|
438
440
|
|
|
439
|
-
describe("runAgentSession process monitor", () => {
|
|
441
|
+
describe("runAgentSession process monitor", () => {
|
|
440
442
|
let registryFile = "";
|
|
441
443
|
let toolsFile = "";
|
|
442
444
|
|
|
@@ -804,7 +806,99 @@ describe("runAgentSession process monitor", () => {
|
|
|
804
806
|
expect(sentTexts[0]).toContain("first prompt");
|
|
805
807
|
expect(sentTexts[1]).toContain("second prompt");
|
|
806
808
|
});
|
|
807
|
-
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
describe("runAgentSession periodic avatar refresh", () => {
|
|
812
|
+
let registryFile = "";
|
|
813
|
+
let toolsFile = "";
|
|
814
|
+
|
|
815
|
+
beforeEach(async () => {
|
|
816
|
+
vi.useFakeTimers();
|
|
817
|
+
resetState();
|
|
818
|
+
resetBindingState();
|
|
819
|
+
mockStreamStates.clear();
|
|
820
|
+
const dir = await mkdtemp(join(tmpdir(), "chatccc-avatar-refresh-"));
|
|
821
|
+
registryFile = join(dir, "session-registry.json");
|
|
822
|
+
toolsFile = join(dir, "session-tools.json");
|
|
823
|
+
_setSessionRegistryFileForTest(registryFile);
|
|
824
|
+
_setSessionToolsFileForTest(toolsFile);
|
|
825
|
+
_setAvatarRefreshIntervalForTest(50);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
afterEach(async () => {
|
|
829
|
+
_resetSessionRegistryFileForTest();
|
|
830
|
+
_resetSessionToolsFileForTest();
|
|
831
|
+
_resetAvatarRefreshIntervalForTest();
|
|
832
|
+
_clearAdapterCacheForTest();
|
|
833
|
+
resetState();
|
|
834
|
+
resetBindingState();
|
|
835
|
+
vi.useRealTimers();
|
|
836
|
+
if (registryFile) await rm(dirname(registryFile), { recursive: true, force: true });
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it.each(["codex", "cursor", "claude", "ccc"])(
|
|
840
|
+
"refreshes the busy avatar for %s every interval and stops after completion",
|
|
841
|
+
async (tool) => {
|
|
842
|
+
const sessionId = `sid-avatar-${tool}`;
|
|
843
|
+
const chatId = `chat-avatar-${tool}`;
|
|
844
|
+
const platform = mockPlatform("feishu");
|
|
845
|
+
setSessionPlatform(platform);
|
|
846
|
+
bindChatToSession(sessionId, chatId);
|
|
847
|
+
recordLastActiveChat(sessionId, chatId);
|
|
848
|
+
|
|
849
|
+
let finishPrompt: (() => void) | undefined;
|
|
850
|
+
const adapter: ToolAdapter = {
|
|
851
|
+
displayName: tool,
|
|
852
|
+
sessionDescPrefix: `${tool} Session:`,
|
|
853
|
+
createSession: async () => ({ sessionId }),
|
|
854
|
+
getSessionInfo: async (sid) => ({ sessionId: sid, cwd: "/tmp" }),
|
|
855
|
+
closeSession: async () => {},
|
|
856
|
+
prompt: async function* () {
|
|
857
|
+
yield { type: "assistant", blocks: [{ type: "text", text: "working" }] };
|
|
858
|
+
await new Promise<void>((resolve) => {
|
|
859
|
+
finishPrompt = resolve;
|
|
860
|
+
});
|
|
861
|
+
yield { type: "assistant", blocks: [{ type: "text", text: "done" }] };
|
|
862
|
+
},
|
|
863
|
+
};
|
|
864
|
+
_setAdapterForToolForTest(tool, adapter);
|
|
865
|
+
|
|
866
|
+
const runPromise = runAgentSession(
|
|
867
|
+
sessionId,
|
|
868
|
+
"prompt",
|
|
869
|
+
platform,
|
|
870
|
+
chatId,
|
|
871
|
+
Date.now(),
|
|
872
|
+
tool,
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
await vi.waitFor(() => {
|
|
876
|
+
expect(vi.mocked(platform.setChatAvatar).mock.calls.some(
|
|
877
|
+
([calledChatId, calledTool, status]) =>
|
|
878
|
+
calledChatId === chatId && calledTool === tool && status === "busy",
|
|
879
|
+
)).toBe(true);
|
|
880
|
+
expect(finishPrompt).toBeTypeOf("function");
|
|
881
|
+
});
|
|
882
|
+
const initialBusyCalls = vi.mocked(platform.setChatAvatar).mock.calls.filter(
|
|
883
|
+
([calledChatId, calledTool, status]) =>
|
|
884
|
+
calledChatId === chatId && calledTool === tool && status === "busy",
|
|
885
|
+
).length;
|
|
886
|
+
|
|
887
|
+
await vi.advanceTimersByTimeAsync(51);
|
|
888
|
+
expect(vi.mocked(platform.setChatAvatar).mock.calls.filter(
|
|
889
|
+
([calledChatId, calledTool, status]) =>
|
|
890
|
+
calledChatId === chatId && calledTool === tool && status === "busy",
|
|
891
|
+
)).toHaveLength(initialBusyCalls + 1);
|
|
892
|
+
|
|
893
|
+
finishPrompt?.();
|
|
894
|
+
await runPromise;
|
|
895
|
+
const callsAfterCompletion = vi.mocked(platform.setChatAvatar).mock.calls.length;
|
|
896
|
+
|
|
897
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
898
|
+
expect(platform.setChatAvatar).toHaveBeenCalledTimes(callsAfterCompletion);
|
|
899
|
+
},
|
|
900
|
+
);
|
|
901
|
+
});
|
|
808
902
|
|
|
809
903
|
describe("runAgentSession response stall watchdog", () => {
|
|
810
904
|
let tempDir = "";
|
package/src/builtin/index.ts
CHANGED
|
@@ -42,9 +42,17 @@ const SYSTEM_PROMPT = [
|
|
|
42
42
|
"- Respond in the user's language unless they ask otherwise.",
|
|
43
43
|
"- Prefer direct, usable answers and concrete actions over long explanations.",
|
|
44
44
|
"- For code tasks, inspect the relevant files before editing and verify with tests or checks when practical.",
|
|
45
|
-
"- Preserve user work. Do not overwrite concurrent changes unless the user explicitly asks.",
|
|
46
|
-
"- Keep immutable platform rules above project guidance and runtime details.",
|
|
47
|
-
|
|
45
|
+
"- Preserve user work. Do not overwrite concurrent changes unless the user explicitly asks.",
|
|
46
|
+
"- Keep immutable platform rules above project guidance and runtime details.",
|
|
47
|
+
"",
|
|
48
|
+
"## Evidence-Gated Conclusions",
|
|
49
|
+
"- Apply this gate before consequential claims or actions that could affect code, data, deployments, or user decisions, and whenever the available evidence is indirect.",
|
|
50
|
+
"- Identify the claim and its authoritative source of truth. Separate direct observations from inferences, and test plausible alternative explanations before choosing one.",
|
|
51
|
+
"- Use the strongest practical decisive check at the same semantic level as the claim: runtime behavior for runtime claims, effective configuration for configuration claims, deployed state for deployment claims, and transformed output for transformation claims.",
|
|
52
|
+
"- Do not treat proxy signals such as names, timestamps, file sizes, line counts, partial samples, or a clean command exit as decisive when a direct check is practical.",
|
|
53
|
+
"- Use definitive language only after the evidence closes the loop. Otherwise state uncertainty, identify the missing evidence, and name the next check.",
|
|
54
|
+
"- Do not repeat checks once decisive evidence exists.",
|
|
55
|
+
].join("\n");
|
|
48
56
|
|
|
49
57
|
const SUMMARY_SYSTEM_PROMPT = [
|
|
50
58
|
"You are DeepCCC's context compactor.",
|
|
@@ -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 由
|