chatccc 0.2.73 → 0.2.75
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__/orchestrator.test.ts +5 -4
- package/src/__tests__/platform-startup.test.ts +19 -0
- package/src/cards.ts +41 -0
- package/src/index.ts +43 -16
- package/src/orchestrator.ts +42 -13
- package/src/platform-startup.ts +16 -0
- package/src/session-chat-binding.ts +52 -0
- package/src/session.ts +14 -0
package/package.json
CHANGED
|
@@ -142,13 +142,14 @@ describe("handleCommand WeChat processing ack", () => {
|
|
|
142
142
|
|
|
143
143
|
await handleCommand(platform, "继续说明", "wx-chat", "wx-user", Date.now(), "p2p");
|
|
144
144
|
|
|
145
|
+
// 不再发"生成中"卡片,改为入队文本通知
|
|
145
146
|
expect(platform.sendText).not.toHaveBeenCalledWith("wx-chat", "生成中...");
|
|
146
|
-
expect(platform.
|
|
147
|
+
expect(platform.sendText).toHaveBeenCalledWith(
|
|
147
148
|
"wx-chat",
|
|
148
|
-
"
|
|
149
|
-
"该会话正在生成回复中,请等待完成后再发送新消息。也可以发送 /stop 结束,已完成的步骤不会丢失。",
|
|
150
|
-
"yellow",
|
|
149
|
+
"当前会话正在生成中,你的消息已进入缓存队列,生成完成后会立即处理。发送 /cancel 可取消缓存。",
|
|
151
150
|
);
|
|
151
|
+
// sendCard 不再被调用(WeChat 用 sendText)
|
|
152
|
+
expect(platform.sendCard).not.toHaveBeenCalled();
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
it("sends the WeChat processing ack after the busy check for normal prompts", async () => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { buildPlatformStartupPlan } from "../platform-startup.ts";
|
|
4
|
+
|
|
5
|
+
describe("buildPlatformStartupPlan", () => {
|
|
6
|
+
it("starts WeChat iLink without requiring Feishu when Feishu is disabled", () => {
|
|
7
|
+
expect(buildPlatformStartupPlan({ feishuEnabled: false, ilinkEnabled: true })).toEqual({
|
|
8
|
+
startFeishu: false,
|
|
9
|
+
startIlink: true,
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("can start both platforms when both are enabled", () => {
|
|
14
|
+
expect(buildPlatformStartupPlan({ feishuEnabled: true, ilinkEnabled: true })).toEqual({
|
|
15
|
+
startFeishu: true,
|
|
16
|
+
startIlink: true,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
package/src/cards.ts
CHANGED
|
@@ -346,6 +346,47 @@ export function buildSessionsCard(sessions: Array<{
|
|
|
346
346
|
});
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
// 队列缓存卡片(消息已进入缓存队列,带取消按钮)
|
|
350
|
+
export function buildQueuedCard(text: string): string {
|
|
351
|
+
const preview = text.length > 100 ? text.slice(0, 100) + "…" : text;
|
|
352
|
+
return JSON.stringify({
|
|
353
|
+
config: { wide_screen_mode: true },
|
|
354
|
+
header: { template: "blue", title: { content: "消息已进入缓存队列", tag: "plain_text" } },
|
|
355
|
+
elements: [
|
|
356
|
+
{ tag: "div", text: { tag: "lark_md", content: `当前会话正在生成中,你的消息已进入缓存队列,生成完成后会立即处理。\n\n> ${preview}` } },
|
|
357
|
+
{ tag: "hr" },
|
|
358
|
+
{
|
|
359
|
+
tag: "action",
|
|
360
|
+
actions: [{
|
|
361
|
+
tag: "button",
|
|
362
|
+
text: { tag: "plain_text", content: "取消缓存(/cancel)" },
|
|
363
|
+
type: "danger",
|
|
364
|
+
value: { action: "cancel" },
|
|
365
|
+
}],
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 队列满卡片(提示 /stop 或 /cancel)
|
|
372
|
+
export function buildQueueFullCard(): string {
|
|
373
|
+
return JSON.stringify({
|
|
374
|
+
config: { wide_screen_mode: true },
|
|
375
|
+
header: { template: "yellow", title: { content: "消息队列已满", tag: "plain_text" } },
|
|
376
|
+
elements: [
|
|
377
|
+
{ tag: "div", text: { tag: "lark_md", content: "当前缓存队列中已有消息等待处理,请等待或发送指令:\n- **/stop** — 停止当前生成\n- **/cancel** — 取消队列中的消息" } },
|
|
378
|
+
{ tag: "hr" },
|
|
379
|
+
{
|
|
380
|
+
tag: "action",
|
|
381
|
+
actions: [
|
|
382
|
+
{ tag: "button", text: { tag: "plain_text", content: "取消缓存(/cancel)" }, type: "danger", value: { action: "cancel" } },
|
|
383
|
+
{ tag: "button", text: { tag: "plain_text", content: "停止生成(/stop)" }, type: "default", value: { action: "stop" } },
|
|
384
|
+
],
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
349
390
|
// 状态卡片(带关闭按钮)
|
|
350
391
|
export function buildStatusCard(statusText: string, template = "blue"): string {
|
|
351
392
|
return JSON.stringify({
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import WebSocket from "ws";
|
|
|
29
29
|
|
|
30
30
|
import { appendStartupTrace, attachRelayWebSocket, ensureSingleInstance, freeRelayListenPort, installCrashLogging, waitForPortFree } from "./shared.ts";
|
|
31
31
|
import { createUiRouter, setExtraApiHandler, setReloadConfigHook, startSetupMode } from "./web-ui.ts";
|
|
32
|
+
import { buildPlatformStartupPlan } from "./platform-startup.ts";
|
|
32
33
|
import { makeTraceId, logTrace } from "./trace.ts";
|
|
33
34
|
import {
|
|
34
35
|
CHATCCC_PORT,
|
|
@@ -94,6 +95,7 @@ import {
|
|
|
94
95
|
} from "./session.ts";
|
|
95
96
|
import {
|
|
96
97
|
rebuildSessionChatsFromRegistry,
|
|
98
|
+
setQueueConsumer,
|
|
97
99
|
} from "./session-chat-binding.ts";
|
|
98
100
|
import { fixStaleStreamStates } from "./stream-state.ts";
|
|
99
101
|
import { handleCommand, type PlatformAdapter } from "./orchestrator.ts";
|
|
@@ -155,6 +157,13 @@ const feishuPlatform = createFeishuAdapter();
|
|
|
155
157
|
const wechatPlatform = createWechatAdapter();
|
|
156
158
|
setSessionPlatform(feishuPlatform);
|
|
157
159
|
|
|
160
|
+
// 注册队列消费回调:session 生成完成后自动处理缓存消息
|
|
161
|
+
setQueueConsumer((platform, msg) => {
|
|
162
|
+
handleCommand(platform, msg.text, msg.chatId, msg.openId, msg.msgTimestamp, msg.chatType, msg.traceId).catch(err =>
|
|
163
|
+
console.error(`[${ts()}] Queue consume failed: ${(err as Error).message}`)
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
158
167
|
// ---------------------------------------------------------------------------
|
|
159
168
|
// Event types
|
|
160
169
|
// ---------------------------------------------------------------------------
|
|
@@ -275,7 +284,7 @@ function parseCardAction(data: unknown): CardActionResult | null {
|
|
|
275
284
|
}
|
|
276
285
|
if (!cmd) return null;
|
|
277
286
|
|
|
278
|
-
const CMD_MAP: Record<string, string> = { stop: "/stop", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", status: "/status", cd: "/cd", sessions: "/sessions", newh: "/newh" };
|
|
287
|
+
const CMD_MAP: Record<string, string> = { stop: "/stop", cancel: "/cancel", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", status: "/status", cd: "/cd", sessions: "/sessions", newh: "/newh" };
|
|
279
288
|
let text = CMD_MAP[cmd] ?? "";
|
|
280
289
|
if (cmd === "cd" && typeof action.value === "object" && action.value !== null) {
|
|
281
290
|
const path = (action.value as Record<string, string>).path;
|
|
@@ -668,6 +677,36 @@ async function startWechatSupervisor(): Promise<void> {
|
|
|
668
677
|
console.log("[WX] 微信 iLink 平台已停止。");
|
|
669
678
|
}
|
|
670
679
|
|
|
680
|
+
async function startConfiguredPlatforms(
|
|
681
|
+
httpServer: Server,
|
|
682
|
+
options: { failOnFeishuError: boolean },
|
|
683
|
+
): Promise<void> {
|
|
684
|
+
const plan = buildPlatformStartupPlan({
|
|
685
|
+
feishuEnabled: FEISHU_ENABLED,
|
|
686
|
+
ilinkEnabled: ILINK_ENABLED,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
if (plan.startFeishu) {
|
|
690
|
+
try {
|
|
691
|
+
await startBotService({ httpServer, port: CHATCCC_PORT });
|
|
692
|
+
} catch (err) {
|
|
693
|
+
if (options.failOnFeishuError) throw err;
|
|
694
|
+
console.error(`\n[飞书] 启动失败: ${(err as Error).message}`);
|
|
695
|
+
console.error("[飞书] 微信等其他平台不受影响,将继续启动。\n");
|
|
696
|
+
}
|
|
697
|
+
} else {
|
|
698
|
+
console.log("[飞书] 平台未启用,跳过飞书启动。");
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (plan.startIlink) {
|
|
702
|
+
startWechatSupervisor().catch((err) =>
|
|
703
|
+
console.error(`[WX] 微信 supervisor 异常退出: ${(err as Error).message}`),
|
|
704
|
+
);
|
|
705
|
+
} else {
|
|
706
|
+
console.log("[WX] 微信 iLink 未启用,跳过微信启动。");
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
671
710
|
// ---------------------------------------------------------------------------
|
|
672
711
|
// Main
|
|
673
712
|
// ---------------------------------------------------------------------------
|
|
@@ -784,11 +823,11 @@ async function main(): Promise<void> {
|
|
|
784
823
|
appIdMaskAfterReload: maskAppId(APP_ID),
|
|
785
824
|
});
|
|
786
825
|
try {
|
|
787
|
-
await
|
|
826
|
+
await startConfiguredPlatforms(httpServer, { failOnFeishuError: true });
|
|
788
827
|
installShutdownHandlers(httpServer);
|
|
789
828
|
return { ok: true };
|
|
790
829
|
} catch (err) {
|
|
791
|
-
appendStartupTrace("setup-activate:
|
|
830
|
+
appendStartupTrace("setup-activate: startConfiguredPlatforms failed", {
|
|
792
831
|
message: (err as Error).message,
|
|
793
832
|
});
|
|
794
833
|
return { ok: false, error: (err as Error).message };
|
|
@@ -822,19 +861,7 @@ async function main(): Promise<void> {
|
|
|
822
861
|
process.exit(1);
|
|
823
862
|
});
|
|
824
863
|
|
|
825
|
-
|
|
826
|
-
try {
|
|
827
|
-
await startBotService({ httpServer, port: CHATCCC_PORT });
|
|
828
|
-
} catch (err) {
|
|
829
|
-
console.error(`\n[飞书] 启动失败: ${(err as Error).message}`);
|
|
830
|
-
console.error("[飞书] 微信等其他平台不受影响,将继续启动。\n");
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// 启动微信 iLink 平台(后台运行,不阻塞飞书)
|
|
835
|
-
startWechatSupervisor().catch((err) =>
|
|
836
|
-
console.error(`[WX] 微信 supervisor 异常退出: ${(err as Error).message}`),
|
|
837
|
-
);
|
|
864
|
+
await startConfiguredPlatforms(httpServer, { failOnFeishuError: false });
|
|
838
865
|
|
|
839
866
|
installShutdownHandlers(httpServer);
|
|
840
867
|
}
|
package/src/orchestrator.ts
CHANGED
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
buildCdContent,
|
|
33
33
|
buildCdCard,
|
|
34
34
|
buildSessionsCard,
|
|
35
|
+
buildQueuedCard,
|
|
36
|
+
buildQueueFullCard,
|
|
35
37
|
} from "./cards.ts";
|
|
36
38
|
import {
|
|
37
39
|
formatGitResult,
|
|
@@ -60,6 +62,8 @@ import {
|
|
|
60
62
|
isSessionRunning,
|
|
61
63
|
displayCards,
|
|
62
64
|
recordLastActiveChat,
|
|
65
|
+
enqueueMessage,
|
|
66
|
+
cancelQueuedMessage,
|
|
63
67
|
} from "./session-chat-binding.ts";
|
|
64
68
|
export { type PlatformAdapter } from "./platform-adapter.ts";
|
|
65
69
|
import type { PlatformAdapter } from "./platform-adapter.ts";
|
|
@@ -306,6 +310,7 @@ export async function handleCommand(
|
|
|
306
310
|
if (oldRecord?.sessionId && oldRecord.sessionId !== sessionId) {
|
|
307
311
|
unbindChatFromSession(oldRecord.sessionId, chatId);
|
|
308
312
|
displayCards.delete(chatId);
|
|
313
|
+
cancelQueuedMessage(oldRecord.sessionId);
|
|
309
314
|
}
|
|
310
315
|
bindChatToSession(sessionId, chatId);
|
|
311
316
|
sessionInfoMap.set(chatId, {
|
|
@@ -590,6 +595,19 @@ export async function handleCommand(
|
|
|
590
595
|
return;
|
|
591
596
|
}
|
|
592
597
|
|
|
598
|
+
if (textLower === "/cancel") {
|
|
599
|
+
logTrace(tid, "BRANCH", { cmd: "/cancel" });
|
|
600
|
+
if (cancelQueuedMessage(sessionId)) {
|
|
601
|
+
console.log(`[${ts()}] [CANCEL] Queue cancelled for session=${sessionId}`);
|
|
602
|
+
await platform.sendText(chatId, "已取消缓存队列中的消息。").catch(() => {});
|
|
603
|
+
logTrace(tid, "DONE", { outcome: "cancelled" });
|
|
604
|
+
} else {
|
|
605
|
+
await platform.sendText(chatId, "当前缓存队列中没有消息。").catch(() => {});
|
|
606
|
+
logTrace(tid, "DONE", { outcome: "cancel_no_queue" });
|
|
607
|
+
}
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
593
611
|
if (textLower === "/status") {
|
|
594
612
|
logTrace(tid, "BRANCH", { cmd: "/status" });
|
|
595
613
|
const status = await getSessionStatus(chatId);
|
|
@@ -980,21 +998,32 @@ export async function handleCommand(
|
|
|
980
998
|
return;
|
|
981
999
|
}
|
|
982
1000
|
|
|
983
|
-
// 并发检查:同一 session 只能有一个活跃 prompt
|
|
1001
|
+
// 并发检查:同一 session 只能有一个活跃 prompt,多余消息进入队列
|
|
984
1002
|
if (isSessionRunning(sessionId)) {
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
sessionId,
|
|
1003
|
+
const queued = enqueueMessage(sessionId, {
|
|
1004
|
+
text, chatId, openId, msgTimestamp, chatType, traceId: tid,
|
|
988
1005
|
});
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
"
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1006
|
+
if (queued) {
|
|
1007
|
+
logTrace(tid, "QUEUED", { sessionId });
|
|
1008
|
+
console.log(
|
|
1009
|
+
`[${ts()}] [QUEUED] Session ${sessionId} is busy, message from chat ${chatId} enqueued`,
|
|
1010
|
+
);
|
|
1011
|
+
if (platform.kind === "wechat") {
|
|
1012
|
+
await platform.sendText(chatId, "当前会话正在生成中,你的消息已进入缓存队列,生成完成后会立即处理。发送 /cancel 可取消缓存。").catch(() => {});
|
|
1013
|
+
} else {
|
|
1014
|
+
await platform.sendRawCard(chatId, buildQueuedCard(text)).catch(() => {});
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
logTrace(tid, "QUEUE_FULL", { sessionId });
|
|
1018
|
+
console.log(
|
|
1019
|
+
`[${ts()}] [QUEUE_FULL] Session ${sessionId} queue full, rejecting message from chat ${chatId}`,
|
|
1020
|
+
);
|
|
1021
|
+
if (platform.kind === "wechat") {
|
|
1022
|
+
await platform.sendText(chatId, "当前缓存队列中已有消息等待处理,请等待或发送 /stop(停止生成)或 /cancel(取消缓存)。").catch(() => {});
|
|
1023
|
+
} else {
|
|
1024
|
+
await platform.sendRawCard(chatId, buildQueueFullCard()).catch(() => {});
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
998
1027
|
return;
|
|
999
1028
|
}
|
|
1000
1029
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface PlatformStartupPlanInput {
|
|
2
|
+
feishuEnabled: boolean;
|
|
3
|
+
ilinkEnabled: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface PlatformStartupPlan {
|
|
7
|
+
startFeishu: boolean;
|
|
8
|
+
startIlink: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildPlatformStartupPlan(input: PlatformStartupPlanInput): PlatformStartupPlan {
|
|
12
|
+
return {
|
|
13
|
+
startFeishu: input.feishuEnabled,
|
|
14
|
+
startIlink: input.ilinkEnabled,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// 由 session.ts 在初始化时调用 rebuildSessionChatsFromRegistry 重建
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
|
|
8
|
+
import type { PlatformAdapter } from "./platform-adapter.ts";
|
|
9
|
+
|
|
8
10
|
const sessionChatsMap = new Map<string, Set<string>>();
|
|
9
11
|
|
|
10
12
|
/** 从 registry 数据重建映射(由 session.ts 调用,避免循环依赖) */
|
|
@@ -123,10 +125,60 @@ export const displayCards = new Map<string, DisplayCardState>();
|
|
|
123
125
|
/** displayLoops: sessionId → 展示循环的 stop 函数 */
|
|
124
126
|
export const displayLoops = new Map<string, () => void>();
|
|
125
127
|
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// queuedMessages: sessionId → 缓存消息(生成中排队,队列最大长度 1)
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
export interface QueuedMessage {
|
|
133
|
+
text: string;
|
|
134
|
+
chatId: string;
|
|
135
|
+
openId: string;
|
|
136
|
+
msgTimestamp: number;
|
|
137
|
+
chatType: string;
|
|
138
|
+
traceId?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const queuedMessages = new Map<string, QueuedMessage>();
|
|
142
|
+
|
|
143
|
+
export function enqueueMessage(sessionId: string, msg: QueuedMessage): boolean {
|
|
144
|
+
if (queuedMessages.has(sessionId)) return false;
|
|
145
|
+
queuedMessages.set(sessionId, msg);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function dequeueMessage(sessionId: string): QueuedMessage | undefined {
|
|
150
|
+
const msg = queuedMessages.get(sessionId);
|
|
151
|
+
queuedMessages.delete(sessionId);
|
|
152
|
+
return msg;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function cancelQueuedMessage(sessionId: string): boolean {
|
|
156
|
+
return queuedMessages.delete(sessionId);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function hasQueuedMessage(sessionId: string): boolean {
|
|
160
|
+
return queuedMessages.has(sessionId);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// 队列消费回调(由 index.ts 注入,避免 session.ts → orchestrator.ts 循环依赖)
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
let onConsumeQueuedMessage: ((platform: PlatformAdapter, msg: QueuedMessage) => void) | null = null;
|
|
168
|
+
|
|
169
|
+
export function setQueueConsumer(fn: (platform: PlatformAdapter, msg: QueuedMessage) => void): void {
|
|
170
|
+
onConsumeQueuedMessage = fn;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function consumeQueuedMessage(platform: PlatformAdapter, msg: QueuedMessage): void {
|
|
174
|
+
onConsumeQueuedMessage?.(platform, msg);
|
|
175
|
+
}
|
|
176
|
+
|
|
126
177
|
export function resetBindingState(): void {
|
|
127
178
|
sessionChatsMap.clear();
|
|
128
179
|
lastActiveChatMap.clear();
|
|
129
180
|
activePrompts.clear();
|
|
181
|
+
queuedMessages.clear();
|
|
130
182
|
displayCards.clear();
|
|
131
183
|
for (const stop of displayLoops.values()) stop();
|
|
132
184
|
displayLoops.clear();
|
package/src/session.ts
CHANGED
|
@@ -50,6 +50,9 @@ import {
|
|
|
50
50
|
recordLastActiveChat,
|
|
51
51
|
getLastActiveChat,
|
|
52
52
|
pickDisplayChat,
|
|
53
|
+
dequeueMessage,
|
|
54
|
+
consumeQueuedMessage,
|
|
55
|
+
cancelQueuedMessage,
|
|
53
56
|
} from "./session-chat-binding.ts";
|
|
54
57
|
|
|
55
58
|
// ---------------------------------------------------------------------------
|
|
@@ -575,6 +578,7 @@ export async function switchChatBinding(args: SwitchChatBindingArgs): Promise<Sw
|
|
|
575
578
|
if (oldSessionId) {
|
|
576
579
|
unbindChatFromSession(oldSessionId, chatId);
|
|
577
580
|
displayCards.delete(chatId);
|
|
581
|
+
cancelQueuedMessage(oldSessionId);
|
|
578
582
|
}
|
|
579
583
|
bindChatToSession(newSessionId, chatId);
|
|
580
584
|
recordLastActiveChat(newSessionId, chatId);
|
|
@@ -854,6 +858,15 @@ export async function runAgentSession(
|
|
|
854
858
|
const wasStopped = prompt?.stopped ?? false;
|
|
855
859
|
activePrompts.delete(sessionId);
|
|
856
860
|
|
|
861
|
+
// 消费队列中的缓存消息(异步,不阻塞后续清理)
|
|
862
|
+
const queued = dequeueMessage(sessionId);
|
|
863
|
+
if (queued) {
|
|
864
|
+
console.log(`[${ts()}] [QUEUE] Consuming queued message for session ${sessionId}: "${queued.text.slice(0, 50)}"`);
|
|
865
|
+
setImmediate(() => {
|
|
866
|
+
consumeQueuedMessage(platform, queued);
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
|
|
857
870
|
// 写最终状态
|
|
858
871
|
const finalStatus = wasStopped ? "stopped" : "done";
|
|
859
872
|
const finalReply = pickFinalReply(state).trim();
|
|
@@ -1184,6 +1197,7 @@ export function stopSession(sessionId: string): boolean {
|
|
|
1184
1197
|
const prompt = activePrompts.get(sessionId);
|
|
1185
1198
|
if (!prompt) return false;
|
|
1186
1199
|
prompt.stopped = true;
|
|
1200
|
+
cancelQueuedMessage(sessionId);
|
|
1187
1201
|
prompt.controller.abort();
|
|
1188
1202
|
console.log(`[${ts()}] [STOP] Session ${sessionId} aborted`);
|
|
1189
1203
|
return true;
|