chatccc 0.2.112 → 0.2.114
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 +75 -7
- package/src/agent-file-rpc.ts +2 -2
- package/src/agent-image-rpc.ts +2 -2
- package/src/feishu-api.ts +6 -1
- package/src/index.ts +2 -0
- package/src/orchestrator.ts +212 -2
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ const mockStreamStates = new Map<string, { status: "running" | "done" | "stopped
|
|
|
10
10
|
|
|
11
11
|
vi.mock("../im-skills.ts", () => ({
|
|
12
12
|
buildImSkillsPrompt: async () => "",
|
|
13
|
+
buildImSkillsPromptCached: async () => "",
|
|
13
14
|
exportSkillSubDocs: async () => {},
|
|
14
15
|
}));
|
|
15
16
|
|
|
@@ -59,20 +60,21 @@ import {
|
|
|
59
60
|
_setAdapterForToolForTest,
|
|
60
61
|
_setSessionRegistryFileForTest,
|
|
61
62
|
_setSessionToolsFileForTest,
|
|
63
|
+
loadSessionRegistryForBinding,
|
|
62
64
|
recordSessionRegistry,
|
|
63
65
|
resetState,
|
|
64
66
|
} from "../session.ts";
|
|
65
67
|
import { activePrompts, resetBindingState } from "../session-chat-binding.ts";
|
|
66
68
|
|
|
67
|
-
function mockPlatform(): PlatformAdapter {
|
|
69
|
+
function mockPlatform(kind: "wechat" | "feishu" = "wechat"): PlatformAdapter {
|
|
68
70
|
return {
|
|
69
|
-
kind
|
|
71
|
+
kind,
|
|
70
72
|
sendText: vi.fn(async () => true),
|
|
71
73
|
sendCard: vi.fn(async () => true),
|
|
72
74
|
sendRawCard: vi.fn(async () => true),
|
|
73
|
-
createGroup: vi.fn(async () => "
|
|
75
|
+
createGroup: vi.fn(async () => "feishu-group"),
|
|
74
76
|
updateChatInfo: vi.fn(async () => {}),
|
|
75
|
-
getChatInfo: vi.fn(async () => ({ name: "微信会话", description: "" })),
|
|
77
|
+
getChatInfo: vi.fn(async () => ({ name: kind === "wechat" ? "微信会话" : "飞书会话", description: "" })),
|
|
76
78
|
disbandChat: vi.fn(async () => {}),
|
|
77
79
|
setChatAvatar: vi.fn(async () => {}),
|
|
78
80
|
extractSessionInfo: vi.fn(() => null),
|
|
@@ -82,15 +84,15 @@ function mockPlatform(): PlatformAdapter {
|
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
function mockAdapter(): ToolAdapter {
|
|
87
|
+
function mockAdapter(sessionId = "sid-wechat", promptText = "done"): ToolAdapter {
|
|
86
88
|
return {
|
|
87
89
|
displayName: "Claude",
|
|
88
90
|
sessionDescPrefix: "Claude Session:",
|
|
89
|
-
createSession: async () => ({ sessionId
|
|
91
|
+
createSession: vi.fn(async () => ({ sessionId })),
|
|
90
92
|
prompt: async function* () {
|
|
91
93
|
yield {
|
|
92
94
|
type: "assistant",
|
|
93
|
-
blocks: [{ type: "text", text:
|
|
95
|
+
blocks: [{ type: "text", text: promptText }],
|
|
94
96
|
};
|
|
95
97
|
},
|
|
96
98
|
getSessionInfo: async (sessionId: string): Promise<SessionInfo> => ({
|
|
@@ -166,4 +168,70 @@ describe("handleCommand WeChat processing ack", () => {
|
|
|
166
168
|
|
|
167
169
|
expect(platform.sendText).toHaveBeenCalledWith("wx-chat", "生成中...");
|
|
168
170
|
});
|
|
171
|
+
|
|
172
|
+
it("cleans stale Feishu p2p binding, creates a group, and sends the private message as first prompt", async () => {
|
|
173
|
+
const platform = mockPlatform("feishu");
|
|
174
|
+
const prompt = vi.fn(async function* (_sessionId: string, userText: string) {
|
|
175
|
+
yield {
|
|
176
|
+
type: "assistant" as const,
|
|
177
|
+
blocks: [{ type: "text" as const, text: `收到: ${userText}` }],
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
_setAdapterForToolForTest("cursor", {
|
|
181
|
+
displayName: "Claude",
|
|
182
|
+
sessionDescPrefix: "Claude Session:",
|
|
183
|
+
createSession: vi.fn(async () => ({ sessionId: "sid-feishu-new" })),
|
|
184
|
+
prompt,
|
|
185
|
+
getSessionInfo: async (sessionId: string): Promise<SessionInfo> => ({
|
|
186
|
+
sessionId,
|
|
187
|
+
cwd: "F:\\repo",
|
|
188
|
+
}),
|
|
189
|
+
closeSession: async () => {},
|
|
190
|
+
});
|
|
191
|
+
await recordSessionRegistry({
|
|
192
|
+
chatId: "feishu-p2p",
|
|
193
|
+
sessionId: "stale-sid",
|
|
194
|
+
tool: "claude",
|
|
195
|
+
chatName: "旧私聊绑定",
|
|
196
|
+
running: false,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await handleCommand(platform, "帮我看一下日志", "feishu-p2p", "ou-user", Date.now(), "p2p");
|
|
200
|
+
|
|
201
|
+
expect(platform.createGroup).toHaveBeenCalledWith(expect.stringContaining("帮我看一下日志"), ["ou-user"]);
|
|
202
|
+
expect(platform.updateChatInfo).toHaveBeenCalledWith(
|
|
203
|
+
"feishu-group",
|
|
204
|
+
expect.stringContaining("帮我看一下日志"),
|
|
205
|
+
expect.stringContaining("sid-feishu-new"),
|
|
206
|
+
);
|
|
207
|
+
expect(prompt).toHaveBeenCalledWith(
|
|
208
|
+
"sid-feishu-new",
|
|
209
|
+
expect.stringContaining("帮我看一下日志"),
|
|
210
|
+
"F:\\repo",
|
|
211
|
+
expect.any(AbortSignal),
|
|
212
|
+
expect.any(Object),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const registry = await loadSessionRegistryForBinding();
|
|
216
|
+
expect(registry["feishu-p2p"]).toBeUndefined();
|
|
217
|
+
expect(registry["feishu-group"]?.sessionId).toBe("sid-feishu-new");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("cleans stale Feishu p2p binding but keeps valid commands from auto-creating a group", async () => {
|
|
221
|
+
const platform = mockPlatform("feishu");
|
|
222
|
+
await recordSessionRegistry({
|
|
223
|
+
chatId: "feishu-p2p",
|
|
224
|
+
sessionId: "stale-sid",
|
|
225
|
+
tool: "claude",
|
|
226
|
+
chatName: "旧私聊绑定",
|
|
227
|
+
running: false,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
await handleCommand(platform, "/model", "feishu-p2p", "ou-user", Date.now(), "p2p");
|
|
231
|
+
|
|
232
|
+
expect(platform.createGroup).not.toHaveBeenCalled();
|
|
233
|
+
expect(platform.sendRawCard).toHaveBeenCalled();
|
|
234
|
+
const registry = await loadSessionRegistryForBinding();
|
|
235
|
+
expect(registry["feishu-p2p"]).toBeUndefined();
|
|
236
|
+
});
|
|
169
237
|
});
|
package/src/agent-file-rpc.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { extname, isAbsolute, resolve } from "node:path";
|
|
|
3
3
|
import { stat } from "node:fs/promises";
|
|
4
4
|
|
|
5
5
|
import { getTenantAccessToken, sendFileReply, sendTextReply } from "./feishu-platform.ts";
|
|
6
|
-
import { ts } from "./config.ts";
|
|
6
|
+
import { ts, resolveDefaultAgentTool } from "./config.ts";
|
|
7
7
|
import { readUtf8JsonBody } from "./agent-rpc-body.ts";
|
|
8
8
|
import { getAdapterForTool } from "./session.ts";
|
|
9
9
|
import { getChatsForSession } from "./session-chat-binding.ts";
|
|
@@ -77,7 +77,7 @@ export async function handleAgentFileRequest(
|
|
|
77
77
|
try {
|
|
78
78
|
const { getSessionTool } = await import("./session.ts");
|
|
79
79
|
const tool = await getSessionTool(sessionId);
|
|
80
|
-
const adapter = getAdapterForTool(tool ??
|
|
80
|
+
const adapter = getAdapterForTool(tool ?? resolveDefaultAgentTool());
|
|
81
81
|
const info = await adapter.getSessionInfo(sessionId);
|
|
82
82
|
if (!info?.cwd) {
|
|
83
83
|
jsonReply(res, 400, { ok: false, error: "Cannot determine cwd for session" });
|
package/src/agent-image-rpc.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { extname, isAbsolute, resolve } from "node:path";
|
|
|
3
3
|
import { stat } from "node:fs/promises";
|
|
4
4
|
|
|
5
5
|
import { getTenantAccessToken, sendImageReply, sendTextReply } from "./feishu-platform.ts";
|
|
6
|
-
import { ts } from "./config.ts";
|
|
6
|
+
import { ts, resolveDefaultAgentTool } from "./config.ts";
|
|
7
7
|
import { readUtf8JsonBody } from "./agent-rpc-body.ts";
|
|
8
8
|
import { getAdapterForTool } from "./session.ts";
|
|
9
9
|
import { getChatsForSession } from "./session-chat-binding.ts";
|
|
@@ -73,7 +73,7 @@ export async function handleAgentImageRequest(
|
|
|
73
73
|
try {
|
|
74
74
|
const { getSessionTool } = await import("./session.ts");
|
|
75
75
|
const tool = await getSessionTool(sessionId);
|
|
76
|
-
const adapter = getAdapterForTool(tool ??
|
|
76
|
+
const adapter = getAdapterForTool(tool ?? resolveDefaultAgentTool());
|
|
77
77
|
const info = await adapter.getSessionInfo(sessionId);
|
|
78
78
|
if (!info?.cwd) {
|
|
79
79
|
jsonReply(res, 400, { ok: false, error: "Cannot determine cwd for session" });
|
package/src/feishu-api.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
CURSOR_SESSION_PREFIX,
|
|
15
15
|
CODEX_SESSION_PREFIX,
|
|
16
16
|
ts,
|
|
17
|
+
resolveDefaultAgentTool,
|
|
18
|
+
toolDisplayName,
|
|
17
19
|
} from "./config.ts";
|
|
18
20
|
import { applyPrivacy } from "./privacy.ts";
|
|
19
21
|
import { buildHelpCard } from "./cards.ts";
|
|
@@ -950,7 +952,10 @@ export async function sendRestartCard(token: string): Promise<void> {
|
|
|
950
952
|
|
|
951
953
|
console.log(`[${ts()}] [RESTART] Latest active chat: ${latestChatId} (mtime=${new Date(latestTime).toISOString()})`);
|
|
952
954
|
|
|
953
|
-
const restartCard = buildHelpCard("", {
|
|
955
|
+
const restartCard = buildHelpCard("", {
|
|
956
|
+
greeting: "Bot 已启动完成,可以继续使用。",
|
|
957
|
+
defaultToolLabel: toolDisplayName(resolveDefaultAgentTool()),
|
|
958
|
+
});
|
|
954
959
|
await fetch(`${BASE_URL}/im/v1/messages?receive_id_type=chat_id`, {
|
|
955
960
|
method: "POST",
|
|
956
961
|
headers: {
|
package/src/index.ts
CHANGED
|
@@ -109,6 +109,8 @@ import { createWechatAdapter, startWechatPlatform } from "./wechat-platform.ts";
|
|
|
109
109
|
function createFeishuAdapter(): PlatformAdapter {
|
|
110
110
|
const auth = () => getTenantAccessToken();
|
|
111
111
|
return {
|
|
112
|
+
kind: "feishu",
|
|
113
|
+
|
|
112
114
|
// ---- 基础消息 ----
|
|
113
115
|
async sendText(chatId, text) {
|
|
114
116
|
return sendTextReply(await auth(), chatId, text);
|
package/src/orchestrator.ts
CHANGED
|
@@ -121,6 +121,43 @@ function shouldSendWechatProcessingAck(
|
|
|
121
121
|
return platform.kind === "wechat" && chatType === "p2p" && !textLower.startsWith("/");
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
function isNonWechatP2p(platform: PlatformAdapter, chatType: string): boolean {
|
|
125
|
+
return chatType === "p2p" && platform.kind !== "wechat";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function cleanupNonWechatP2pBinding(
|
|
129
|
+
platform: PlatformAdapter,
|
|
130
|
+
chatId: string,
|
|
131
|
+
chatType: string,
|
|
132
|
+
tid: string,
|
|
133
|
+
): Promise<void> {
|
|
134
|
+
if (!isNonWechatP2p(platform, chatType)) return;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const registry = await loadSessionRegistryForBinding();
|
|
138
|
+
const record = registry[chatId];
|
|
139
|
+
if (!record?.sessionId) return;
|
|
140
|
+
|
|
141
|
+
unbindChatFromSession(record.sessionId, chatId);
|
|
142
|
+
displayCards.delete(chatId);
|
|
143
|
+
cancelQueuedMessage(record.sessionId);
|
|
144
|
+
sessionInfoMap.delete(chatId);
|
|
145
|
+
await removeSessionRegistryRecord(chatId);
|
|
146
|
+
logTrace(tid, "BRANCH", {
|
|
147
|
+
reason: "cleanup_non_wechat_p2p_binding",
|
|
148
|
+
chatId,
|
|
149
|
+
oldSessionId: record.sessionId,
|
|
150
|
+
});
|
|
151
|
+
console.log(
|
|
152
|
+
`[${ts()}] [P2P] Removed non-WeChat p2p binding: chat=${chatId} session=${record.sessionId}`,
|
|
153
|
+
);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.log(
|
|
156
|
+
`[${ts()}] [INFO] Cannot cleanup p2p registry for ${chatId}: ${(err as Error).message}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
124
161
|
// ---------------------------------------------------------------------------
|
|
125
162
|
// handleCommand — 平台无关的命令分发
|
|
126
163
|
// ---------------------------------------------------------------------------
|
|
@@ -137,6 +174,7 @@ export async function handleCommand(
|
|
|
137
174
|
const tid = traceId ?? makeTraceId();
|
|
138
175
|
const textLower = text.toLowerCase();
|
|
139
176
|
recordChatPlatform(chatId, platform);
|
|
177
|
+
await cleanupNonWechatP2pBinding(platform, chatId, chatType, tid);
|
|
140
178
|
|
|
141
179
|
if (textLower === "/restart") {
|
|
142
180
|
logTrace(tid, "BRANCH", { cmd: "/restart" });
|
|
@@ -523,8 +561,9 @@ export async function handleCommand(
|
|
|
523
561
|
`[${ts()}] [INFO] Cannot get chat info for ${chatId}: ${(err as Error).message}`,
|
|
524
562
|
);
|
|
525
563
|
}
|
|
526
|
-
} else {
|
|
527
|
-
//
|
|
564
|
+
} else if (platform.kind === "wechat") {
|
|
565
|
+
// 微信私聊:从 session-registry.json 获取绑定的 session。
|
|
566
|
+
// 飞书私聊不再作为持久会话入口,避免历史 registry 残留误路由到旧 session。
|
|
528
567
|
try {
|
|
529
568
|
const registry = await loadSessionRegistryForBinding();
|
|
530
569
|
const record = registry[chatId];
|
|
@@ -1245,6 +1284,177 @@ export async function handleCommand(
|
|
|
1245
1284
|
return;
|
|
1246
1285
|
}
|
|
1247
1286
|
|
|
1287
|
+
// 无会话上下文 → /sessions 仍是有效指令,不触发飞书私聊自动建群。
|
|
1288
|
+
if (textLower === "/sessions") {
|
|
1289
|
+
logTrace(tid, "BRANCH", { cmd: "/sessions", scope: "global" });
|
|
1290
|
+
const allSessions = await getAllSessionsStatus();
|
|
1291
|
+
const now = Date.now();
|
|
1292
|
+
const cardData = allSessions.map((s) => ({
|
|
1293
|
+
sessionId: s.sessionId,
|
|
1294
|
+
chatName: s.chatName,
|
|
1295
|
+
chatId: s.chatId,
|
|
1296
|
+
active: s.active,
|
|
1297
|
+
turnCount: s.turnCount,
|
|
1298
|
+
elapsedSeconds: s.active
|
|
1299
|
+
? Math.floor((now - s.startTime) / 1000)
|
|
1300
|
+
: null,
|
|
1301
|
+
model: s.model,
|
|
1302
|
+
tool: s.tool,
|
|
1303
|
+
}));
|
|
1304
|
+
const card = buildSessionsCard(cardData, { defaultToolLabel: toolDisplayName(resolveDefaultAgentTool()) });
|
|
1305
|
+
const ok = await platform.sendRawCard(chatId, card);
|
|
1306
|
+
console.log(
|
|
1307
|
+
`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${cardData.length}`,
|
|
1308
|
+
);
|
|
1309
|
+
logTrace(tid, "DONE", { outcome: "sessions", ok, count: cardData.length });
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// 飞书私聊普通消息:不再绑定私聊本身,而是自动创建会话群并把私聊内容作为首轮 prompt。
|
|
1314
|
+
if (isNonWechatP2p(platform, chatType) && !textLower.startsWith("/")) {
|
|
1315
|
+
const tool = resolveDefaultAgentTool();
|
|
1316
|
+
const toolLabel = toolDisplayName(tool);
|
|
1317
|
+
logTrace(tid, "BRANCH", { cmd: "auto_new_from_p2p", tool });
|
|
1318
|
+
|
|
1319
|
+
if (!openId) {
|
|
1320
|
+
logTrace(tid, "DONE", { outcome: "auto_new_p2p_no_openid" });
|
|
1321
|
+
await platform.sendCard(
|
|
1322
|
+
chatId,
|
|
1323
|
+
"Error",
|
|
1324
|
+
"Cannot identify sender.",
|
|
1325
|
+
"red",
|
|
1326
|
+
);
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
let sessionId: string;
|
|
1331
|
+
let sessionCwd: string;
|
|
1332
|
+
try {
|
|
1333
|
+
const init = await initClaudeSession(tool, undefined, chatId);
|
|
1334
|
+
sessionId = init.sessionId;
|
|
1335
|
+
sessionCwd = init.cwd;
|
|
1336
|
+
console.log(
|
|
1337
|
+
`[${ts()}] [AUTO-P2P 1/5] ${toolLabel} session created: ${sessionId} → OK`,
|
|
1338
|
+
);
|
|
1339
|
+
} catch (err) {
|
|
1340
|
+
console.error(`[${ts()}] [AUTO-P2P 1/5] FAIL: ${(err as Error).message}`);
|
|
1341
|
+
logTrace(tid, "DONE", {
|
|
1342
|
+
outcome: "auto_new_p2p_session_fail",
|
|
1343
|
+
error: (err as Error).message,
|
|
1344
|
+
});
|
|
1345
|
+
await platform.sendCard(
|
|
1346
|
+
chatId,
|
|
1347
|
+
"Error",
|
|
1348
|
+
`Failed to initialize ${toolLabel} session:\n${(err as Error).message}`,
|
|
1349
|
+
"red",
|
|
1350
|
+
);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
const cwd = sessionCwd;
|
|
1355
|
+
const initialName = sessionChatName(text.slice(0, 10) || "新会话", cwd);
|
|
1356
|
+
let newChatId: string;
|
|
1357
|
+
try {
|
|
1358
|
+
newChatId = await platform.createGroup(initialName, [openId]);
|
|
1359
|
+
console.log(
|
|
1360
|
+
`[${ts()}] [AUTO-P2P 2/5] Created Feishu group: ${newChatId} → OK`,
|
|
1361
|
+
);
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
console.error(`[${ts()}] [AUTO-P2P 2/5] FAIL: ${(err as Error).message}`);
|
|
1364
|
+
logTrace(tid, "DONE", {
|
|
1365
|
+
outcome: "auto_new_p2p_group_fail",
|
|
1366
|
+
error: (err as Error).message,
|
|
1367
|
+
});
|
|
1368
|
+
await platform.sendCard(
|
|
1369
|
+
chatId,
|
|
1370
|
+
"Error",
|
|
1371
|
+
`Failed to create group:\n${(err as Error).message}`,
|
|
1372
|
+
"red",
|
|
1373
|
+
);
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
try {
|
|
1378
|
+
const descPrefix = sessionPrefixForTool(tool);
|
|
1379
|
+
await platform.updateChatInfo(
|
|
1380
|
+
newChatId,
|
|
1381
|
+
initialName,
|
|
1382
|
+
`${descPrefix} ${sessionId}`,
|
|
1383
|
+
);
|
|
1384
|
+
console.log(
|
|
1385
|
+
`[${ts()}] [AUTO-P2P 3/5] Renamed group → name="${initialName}" (${toolLabel}) → OK`,
|
|
1386
|
+
);
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
console.error(`[${ts()}] [AUTO-P2P 3/5] FAIL: ${(err as Error).message}`);
|
|
1389
|
+
logTrace(tid, "DONE", {
|
|
1390
|
+
outcome: "auto_new_p2p_rename_fail",
|
|
1391
|
+
error: (err as Error).message,
|
|
1392
|
+
});
|
|
1393
|
+
await platform.sendCard(
|
|
1394
|
+
chatId,
|
|
1395
|
+
"Error",
|
|
1396
|
+
`Group created but rename failed:\n${(err as Error).message}`,
|
|
1397
|
+
"yellow",
|
|
1398
|
+
);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
await setDefaultCwd(cwd, newChatId);
|
|
1403
|
+
bindChatToSession(sessionId, newChatId);
|
|
1404
|
+
await recordSessionRegistry({
|
|
1405
|
+
chatId: newChatId,
|
|
1406
|
+
sessionId,
|
|
1407
|
+
tool,
|
|
1408
|
+
chatName: initialName,
|
|
1409
|
+
turnCount: 0,
|
|
1410
|
+
startTime: Date.now(),
|
|
1411
|
+
running: false,
|
|
1412
|
+
});
|
|
1413
|
+
await saveSessionTool(sessionId, tool, initialName);
|
|
1414
|
+
|
|
1415
|
+
await platform.sendCard(
|
|
1416
|
+
newChatId,
|
|
1417
|
+
`${toolLabel} Session Ready`,
|
|
1418
|
+
`已根据私聊消息创建 **${toolLabel}** 会话群。\n\n` +
|
|
1419
|
+
`**Session ID:** ${sessionId}\n` +
|
|
1420
|
+
`**工作目录:** \`${cwd}\`\n\n` +
|
|
1421
|
+
`下面会自动把你的私聊消息作为第一句话发送给 ${toolLabel}。\n\n` +
|
|
1422
|
+
`发送 **/model** 查看或切换当前会话的模型。\n` +
|
|
1423
|
+
`发送 **/new** 创建新会话,**/newh** 重置当前会话(沿用工作目录)。`,
|
|
1424
|
+
"green",
|
|
1425
|
+
);
|
|
1426
|
+
platform.setChatAvatar(newChatId, tool, "new").catch(() => {});
|
|
1427
|
+
console.log(`[${ts()}] [AUTO-P2P 4/5] Replied to new group → OK`);
|
|
1428
|
+
|
|
1429
|
+
try {
|
|
1430
|
+
logTrace(tid, "RESUME", { sessionId, tool, trigger: "auto_new_from_p2p" });
|
|
1431
|
+
await resumeAndPrompt(
|
|
1432
|
+
sessionId,
|
|
1433
|
+
text,
|
|
1434
|
+
platform,
|
|
1435
|
+
newChatId,
|
|
1436
|
+
msgTimestamp,
|
|
1437
|
+
tool,
|
|
1438
|
+
tid,
|
|
1439
|
+
);
|
|
1440
|
+
console.log(`[${ts()}] [AUTO-P2P 5/5] First prompt sent → OK`);
|
|
1441
|
+
logTrace(tid, "DONE", { outcome: "auto_new_p2p_prompt_done", newChatId, sessionId, tool });
|
|
1442
|
+
} catch (err) {
|
|
1443
|
+
console.error(`[${ts()}] [AUTO-P2P 5/5] FAIL: ${(err as Error).message}`);
|
|
1444
|
+
logTrace(tid, "DONE", {
|
|
1445
|
+
outcome: "auto_new_p2p_prompt_fail",
|
|
1446
|
+
error: (err as Error).message,
|
|
1447
|
+
});
|
|
1448
|
+
await platform.sendCard(
|
|
1449
|
+
newChatId,
|
|
1450
|
+
"Error",
|
|
1451
|
+
`Failed to send first prompt:\n${(err as Error).message}`,
|
|
1452
|
+
"red",
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1248
1458
|
// 无会话上下文 → help card
|
|
1249
1459
|
logTrace(tid, "SEND", { method: "help_card", chatId });
|
|
1250
1460
|
const card = buildHelpCard(text, { defaultToolLabel: toolDisplayName(resolveDefaultAgentTool()) });
|