chatccc 0.2.201 → 0.2.202
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/README.md +17 -17
- package/agent-prompts/claude_specific.md +45 -45
- package/agent-prompts/codex_specific.md +2 -2
- package/agent-prompts/cursor_specific.md +13 -13
- package/bin/cccagent.mjs +17 -17
- package/config.sample.json +27 -27
- package/im-skills/feishu-skill/receive-send-file.md +63 -63
- package/im-skills/feishu-skill/receive-send-image.md +24 -24
- package/im-skills/feishu-skill/skill.md +3 -3
- package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
- package/im-skills/wechat-file-skill/send-file.mjs +83 -83
- package/im-skills/wechat-file-skill/skill.md +10 -10
- package/im-skills/wechat-image-skill/skill.md +10 -10
- package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
- package/im-skills/wechat-video-skill/send-video.mjs +79 -79
- package/im-skills/wechat-video-skill/skill.md +10 -10
- package/package.json +1 -1
- package/scripts/postinstall-sharp-check.mjs +58 -58
- package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
- package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
- package/src/__tests__/builtin-chat-session.test.ts +277 -277
- package/src/__tests__/builtin-cli-json.test.ts +39 -39
- package/src/__tests__/builtin-config.test.ts +33 -33
- package/src/__tests__/builtin-context.test.ts +163 -163
- package/src/__tests__/builtin-file-tools.test.ts +224 -224
- package/src/__tests__/builtin-session-select.test.ts +116 -116
- package/src/__tests__/builtin-sigint.test.ts +56 -56
- package/src/__tests__/card-plain-text.test.ts +5 -5
- package/src/__tests__/cardkit.test.ts +60 -60
- package/src/__tests__/cards.test.ts +133 -122
- package/src/__tests__/ccc-adapter.test.ts +114 -114
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
- package/src/__tests__/chatgpt-subscription.test.ts +135 -135
- package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
- package/src/__tests__/claude-adapter.test.ts +592 -592
- package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
- package/src/__tests__/codex-raw-stream-log.test.ts +163 -163
- package/src/__tests__/codex-reset-actions.test.ts +146 -146
- package/src/__tests__/config-reload.test.ts +10 -10
- package/src/__tests__/config-sample.test.ts +18 -18
- package/src/__tests__/cursor-adapter.test.ts +226 -226
- package/src/__tests__/feishu-api.test.ts +60 -60
- package/src/__tests__/feishu-avatar.test.ts +90 -90
- package/src/__tests__/feishu-platform.test.ts +22 -22
- package/src/__tests__/format-message.test.ts +47 -47
- package/src/__tests__/jsonl-stream.test.ts +79 -79
- package/src/__tests__/orchestrator.test.ts +464 -301
- package/src/__tests__/privacy.test.ts +198 -198
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/session.test.ts +46 -44
- package/src/__tests__/shared-prefix.test.ts +36 -36
- package/src/__tests__/sim-platform.test.ts +12 -12
- package/src/__tests__/stream-state.test.ts +42 -42
- package/src/__tests__/web-ui.test.ts +209 -209
- package/src/adapters/ccc-adapter.ts +121 -121
- package/src/adapters/claude-adapter.ts +603 -603
- package/src/adapters/claude-session-meta-store.ts +120 -120
- package/src/adapters/codex-adapter.ts +380 -380
- package/src/adapters/cursor-adapter.ts +673 -673
- package/src/adapters/jsonl-stream.ts +157 -157
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/adapters/resource-monitor.ts +140 -140
- package/src/agent-delegate-task-rpc.ts +153 -153
- package/src/agent-delegate-task.ts +81 -81
- package/src/agent-reload-config-rpc.ts +34 -34
- package/src/agent-stop-stuck.ts +129 -129
- package/src/builtin/cli.ts +473 -473
- package/src/builtin/context.ts +323 -323
- package/src/builtin/file-tools.ts +1072 -1072
- package/src/builtin/index.ts +404 -404
- package/src/builtin/session-select.ts +48 -48
- package/src/builtin/sigint.ts +50 -50
- package/src/cards.ts +222 -210
- package/src/chatgpt-subscription-rpc.ts +27 -27
- package/src/chatgpt-subscription.ts +299 -299
- package/src/chrome-devtools-guard.ts +318 -318
- package/src/codex-reset-actions.ts +184 -184
- package/src/config.ts +125 -125
- package/src/feishu-api.ts +118 -118
- package/src/feishu-platform.ts +20 -20
- package/src/format-message.ts +293 -293
- package/src/litellm-proxy.ts +374 -374
- package/src/orchestrator.ts +348 -329
- package/src/privacy.ts +118 -118
- package/src/runtime-reload.ts +34 -34
- package/src/session-chat-binding.ts +6 -6
- package/src/session-name.ts +8 -8
- package/src/session.ts +177 -169
- package/src/shared-prefix.ts +29 -29
- package/src/sim-platform.ts +20 -20
- package/src/turn-cards.ts +117 -117
- package/src/web-ui.ts +205 -205
package/src/orchestrator.ts
CHANGED
|
@@ -22,24 +22,24 @@ import {
|
|
|
22
22
|
PROJECT_ROOT,
|
|
23
23
|
anthropicConfigDisplay,
|
|
24
24
|
config,
|
|
25
|
-
fileLog,
|
|
26
|
-
getAllEffortsForTool,
|
|
27
|
-
getAllModelsForTool,
|
|
28
|
-
getDefaultEffortForTool,
|
|
29
|
-
getDefaultCwd,
|
|
25
|
+
fileLog,
|
|
26
|
+
getAllEffortsForTool,
|
|
27
|
+
getAllModelsForTool,
|
|
28
|
+
getDefaultEffortForTool,
|
|
29
|
+
getDefaultCwd,
|
|
30
30
|
setDefaultCwd,
|
|
31
31
|
getRecentDirs,
|
|
32
32
|
addRecentDir,
|
|
33
33
|
resolveDefaultAgentTool,
|
|
34
|
-
sessionPrefixForTool,
|
|
35
|
-
toolDisplayName,
|
|
36
|
-
ts,
|
|
37
|
-
type AgentTool,
|
|
38
|
-
} from "./config.ts";
|
|
34
|
+
sessionPrefixForTool,
|
|
35
|
+
toolDisplayName,
|
|
36
|
+
ts,
|
|
37
|
+
type AgentTool,
|
|
38
|
+
} from "./config.ts";
|
|
39
39
|
import {
|
|
40
|
-
buildHelpCard,
|
|
41
|
-
buildEffortCard,
|
|
42
|
-
buildModelCard,
|
|
40
|
+
buildHelpCard,
|
|
41
|
+
buildEffortCard,
|
|
42
|
+
buildModelCard,
|
|
43
43
|
buildStatusCard,
|
|
44
44
|
buildCdContent,
|
|
45
45
|
buildCdCard,
|
|
@@ -54,21 +54,21 @@ import {
|
|
|
54
54
|
runGitCommand,
|
|
55
55
|
} from "./git-command.ts";
|
|
56
56
|
import {
|
|
57
|
-
clearSessionModelOverride,
|
|
58
|
-
clearSessionEffortOverride,
|
|
57
|
+
clearSessionModelOverride,
|
|
58
|
+
clearSessionEffortOverride,
|
|
59
59
|
getSessionStatus,
|
|
60
60
|
getAllSessionsStatus,
|
|
61
61
|
initClaudeSession,
|
|
62
62
|
lastMsgTimestamps,
|
|
63
63
|
resumeAndPrompt,
|
|
64
64
|
sessionInfoMap,
|
|
65
|
-
setSessionModelOverride,
|
|
66
|
-
setSessionEffortOverride,
|
|
65
|
+
setSessionModelOverride,
|
|
66
|
+
setSessionEffortOverride,
|
|
67
67
|
switchChatBinding,
|
|
68
68
|
recordSessionRegistry,
|
|
69
69
|
getAdapterForTool,
|
|
70
|
-
getEffectiveModelForTool,
|
|
71
|
-
getEffectiveEffortForTool,
|
|
70
|
+
getEffectiveModelForTool,
|
|
71
|
+
getEffectiveEffortForTool,
|
|
72
72
|
stopSession,
|
|
73
73
|
loadSessionRegistryForBinding,
|
|
74
74
|
removeSessionRegistryRecord,
|
|
@@ -87,11 +87,10 @@ import {
|
|
|
87
87
|
import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
|
|
88
88
|
import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
|
|
89
89
|
import { getChatGptSubscriptionStatus, type ChatGptSubscriptionResult } from "./chatgpt-subscription.ts";
|
|
90
|
-
import {
|
|
91
|
-
import {
|
|
92
|
-
import {
|
|
93
|
-
|
|
94
|
-
export { type PlatformAdapter } from "./platform-adapter.ts";
|
|
90
|
+
import { applySharedPrefix } from "./shared-prefix.ts";
|
|
91
|
+
import { cwdDisplayName, sessionChatName } from "./session-name.ts";
|
|
92
|
+
import { reloadRuntimeConfig } from "./runtime-reload.ts";
|
|
93
|
+
export { type PlatformAdapter } from "./platform-adapter.ts";
|
|
95
94
|
import type { ChatAvatarUsageHints, PlatformAdapter } from "./platform-adapter.ts";
|
|
96
95
|
import type { CodexUsageSummary } from "./feishu-api.ts";
|
|
97
96
|
|
|
@@ -135,7 +134,7 @@ function formatCodexUsageSummary(usage: CodexUsageSummary, chatGptSubscription:
|
|
|
135
134
|
return `(约 ${parts.join("")}后)`;
|
|
136
135
|
};
|
|
137
136
|
|
|
138
|
-
const formatResetTime = (balance: NonNullable<CodexUsageSummary["fiveHour"]>) => {
|
|
137
|
+
const formatResetTime = (balance: NonNullable<CodexUsageSummary["fiveHour"]>) => {
|
|
139
138
|
if (balance.resetAtEpochSeconds === null) return "暂无数据";
|
|
140
139
|
const date = new Date(balance.resetAtEpochSeconds * 1000);
|
|
141
140
|
const pad = (value: number) => String(value).padStart(2, "0");
|
|
@@ -263,8 +262,8 @@ function formatCodexUsageSummary(usage: CodexUsageSummary, chatGptSubscription:
|
|
|
263
262
|
formatChatGptSubscriptionFailure(),
|
|
264
263
|
formatResetCredits(),
|
|
265
264
|
"",
|
|
266
|
-
usage.fiveHour ? formatWindow("5h", usage.fiveHour) : "",
|
|
267
|
-
usage.weekly ? formatWindow("7天", usage.weekly) : "",
|
|
265
|
+
usage.fiveHour ? formatWindow("5h", usage.fiveHour) : "",
|
|
266
|
+
usage.weekly ? formatWindow("7天", usage.weekly) : "",
|
|
268
267
|
].filter((line, index, arr) => line !== "" || (index > 0 && arr[index - 1] !== "")).join("\n");
|
|
269
268
|
}
|
|
270
269
|
|
|
@@ -325,7 +324,7 @@ function formatCursorUsageSummary(usage: CursorUsageSummary): string {
|
|
|
325
324
|
}
|
|
326
325
|
|
|
327
326
|
function usageHelpLine(tool: string): string {
|
|
328
|
-
if (tool === "codex") return "\n发送 **/usage** 查看 Codex 实际存在的 5h/7天用量窗口,以及查询/使用主动重置卡。";
|
|
327
|
+
if (tool === "codex") return "\n发送 **/usage** 查看 Codex 实际存在的 5h/7天用量窗口,以及查询/使用主动重置卡。";
|
|
329
328
|
if (tool === "cursor") return "\n发送 **/usage** 查看 Cursor 用量。";
|
|
330
329
|
return "";
|
|
331
330
|
}
|
|
@@ -410,42 +409,10 @@ function shouldSendWechatProcessingAck(
|
|
|
410
409
|
return platform.kind === "wechat" && chatType === "p2p" && !isCommandText;
|
|
411
410
|
}
|
|
412
411
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
async function cleanupNonWechatP2pBinding(
|
|
418
|
-
platform: PlatformAdapter,
|
|
419
|
-
chatId: string,
|
|
420
|
-
chatType: string,
|
|
421
|
-
tid: string,
|
|
422
|
-
): Promise<void> {
|
|
423
|
-
if (!isNonWechatP2p(platform, chatType)) return;
|
|
424
|
-
|
|
425
|
-
try {
|
|
426
|
-
const registry = await loadSessionRegistryForBinding();
|
|
427
|
-
const record = registry[chatId];
|
|
428
|
-
if (!record?.sessionId) return;
|
|
429
|
-
|
|
430
|
-
unbindChatFromSession(record.sessionId, chatId);
|
|
431
|
-
displayCards.delete(chatId);
|
|
432
|
-
cancelQueuedMessage(record.sessionId);
|
|
433
|
-
sessionInfoMap.delete(chatId);
|
|
434
|
-
await removeSessionRegistryRecord(chatId);
|
|
435
|
-
logTrace(tid, "BRANCH", {
|
|
436
|
-
reason: "cleanup_non_wechat_p2p_binding",
|
|
437
|
-
chatId,
|
|
438
|
-
oldSessionId: record.sessionId,
|
|
439
|
-
});
|
|
440
|
-
console.log(
|
|
441
|
-
`[${ts()}] [P2P] Removed non-WeChat p2p binding: chat=${chatId} session=${record.sessionId}`,
|
|
442
|
-
);
|
|
443
|
-
} catch (err) {
|
|
444
|
-
console.log(
|
|
445
|
-
`[${ts()}] [INFO] Cannot cleanup p2p registry for ${chatId}: ${(err as Error).message}`,
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
412
|
+
/** 飞书私聊是固定会话容器;显式 /new 才创建独立群聊。 */
|
|
413
|
+
function isFeishuP2p(platform: PlatformAdapter, chatType: string): boolean {
|
|
414
|
+
return chatType === "p2p" && platform.kind === "feishu";
|
|
415
|
+
}
|
|
449
416
|
|
|
450
417
|
/** 检测当前进程是否从 npm 全局安装启动 */
|
|
451
418
|
function isRunningFromGlobalNpm(): boolean {
|
|
@@ -544,32 +511,31 @@ export async function handleCommand(
|
|
|
544
511
|
const promptText = sharedPrefix.text;
|
|
545
512
|
text = sharedPrefix.body;
|
|
546
513
|
const textLower = text.toLowerCase();
|
|
547
|
-
const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
|
|
548
|
-
recordChatPlatform(chatId, platform);
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if (isCommandText && textLower === "/restart") {
|
|
514
|
+
const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
|
|
515
|
+
recordChatPlatform(chatId, platform);
|
|
516
|
+
|
|
517
|
+
if (isCommandText && textLower === "/reload") {
|
|
518
|
+
logTrace(tid, "BRANCH", { cmd: "/reload" });
|
|
519
|
+
try {
|
|
520
|
+
const result = await reloadRuntimeConfig("chat-command");
|
|
521
|
+
await platform.sendText(
|
|
522
|
+
chatId,
|
|
523
|
+
[
|
|
524
|
+
"配置已重新加载。",
|
|
525
|
+
`默认 Agent: ${toolDisplayName(result.defaultAgent)}`,
|
|
526
|
+
`配置文件: ${result.configPath}`,
|
|
527
|
+
"后续新会话会使用最新配置;正在生成的会话不会被中断。",
|
|
528
|
+
].join("\n"),
|
|
529
|
+
).catch(() => {});
|
|
530
|
+
logTrace(tid, "DONE", { outcome: "reload", defaultAgent: result.defaultAgent });
|
|
531
|
+
} catch (err) {
|
|
532
|
+
await platform.sendText(chatId, `配置重载失败:${(err as Error).message}`).catch(() => {});
|
|
533
|
+
logTrace(tid, "DONE", { outcome: "reload_fail", error: (err as Error).message });
|
|
534
|
+
}
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (isCommandText && textLower === "/restart") {
|
|
573
539
|
logTrace(tid, "BRANCH", { cmd: "/restart" });
|
|
574
540
|
await platform.sendText(chatId, "重启中...请几秒后发消息唤醒我").catch(() => {});
|
|
575
541
|
logTrace(tid, "DONE", { outcome: "restart" });
|
|
@@ -763,7 +729,7 @@ export async function handleCommand(
|
|
|
763
729
|
const toolArg = text.slice(5).trim().toLowerCase();
|
|
764
730
|
const tool = toolArg || resolveDefaultAgentTool();
|
|
765
731
|
logTrace(tid, "BRANCH", { cmd: "/new", tool });
|
|
766
|
-
const validTools = ["claude", "cursor", "codex", "ccc"];
|
|
732
|
+
const validTools = ["claude", "cursor", "codex", "ccc"];
|
|
767
733
|
if (!validTools.includes(tool)) {
|
|
768
734
|
logTrace(tid, "DONE", { outcome: "new_invalid_tool", tool });
|
|
769
735
|
await platform.sendCard(
|
|
@@ -815,9 +781,9 @@ export async function handleCommand(
|
|
|
815
781
|
const cwd = sessionCwd;
|
|
816
782
|
const initialName = sessionChatName("新会话", cwd);
|
|
817
783
|
|
|
818
|
-
//
|
|
819
|
-
//
|
|
820
|
-
if (chatType === "p2p" && platform.kind === "wechat") {
|
|
784
|
+
// /new 的平台语义保持不同:微信在当前私聊新建 session;飞书显式
|
|
785
|
+
// /new 始终创建独立群聊。飞书私聊自己的常驻 session 不在这里切换。
|
|
786
|
+
if (chatType === "p2p" && platform.kind === "wechat") {
|
|
821
787
|
// 先解绑旧 session(如果存在),避免旧 session 的 display loop
|
|
822
788
|
// 继续往同一个 chat 推送内容(/newh 走 switchChatBinding 已有此逻辑,
|
|
823
789
|
// 但 /new p2p 之前遗漏了解绑)。
|
|
@@ -837,11 +803,12 @@ export async function handleCommand(
|
|
|
837
803
|
tool,
|
|
838
804
|
});
|
|
839
805
|
await setDefaultCwd(cwd, chatId);
|
|
840
|
-
await recordSessionRegistry({
|
|
841
|
-
chatId,
|
|
842
|
-
sessionId,
|
|
843
|
-
tool,
|
|
844
|
-
|
|
806
|
+
await recordSessionRegistry({
|
|
807
|
+
chatId,
|
|
808
|
+
sessionId,
|
|
809
|
+
tool,
|
|
810
|
+
chatType,
|
|
811
|
+
chatName: initialName,
|
|
845
812
|
turnCount: 0,
|
|
846
813
|
startTime: Date.now(),
|
|
847
814
|
running: false,
|
|
@@ -923,11 +890,12 @@ export async function handleCommand(
|
|
|
923
890
|
// 让新群的默认工作目录继承当前会话的 cwd
|
|
924
891
|
await setDefaultCwd(cwd, newChatId);
|
|
925
892
|
bindChatToSession(sessionId, newChatId);
|
|
926
|
-
await recordSessionRegistry({
|
|
927
|
-
chatId: newChatId,
|
|
928
|
-
sessionId,
|
|
929
|
-
tool,
|
|
930
|
-
|
|
893
|
+
await recordSessionRegistry({
|
|
894
|
+
chatId: newChatId,
|
|
895
|
+
sessionId,
|
|
896
|
+
tool,
|
|
897
|
+
chatType: "group",
|
|
898
|
+
chatName: initialName,
|
|
931
899
|
turnCount: 0,
|
|
932
900
|
startTime: Date.now(),
|
|
933
901
|
running: false,
|
|
@@ -962,7 +930,8 @@ export async function handleCommand(
|
|
|
962
930
|
return;
|
|
963
931
|
}
|
|
964
932
|
|
|
965
|
-
// 检测会话上下文:群聊从 description
|
|
933
|
+
// 检测会话上下文:群聊从 description 获取,飞书/微信私聊都从
|
|
934
|
+
// session-registry 获取。私聊 chatId 是稳定容器,进程重启后仍恢复绑定。
|
|
966
935
|
let sessionId: string | null = null;
|
|
967
936
|
let descriptionTool: string | null = null;
|
|
968
937
|
let toolLabel: string | null = null;
|
|
@@ -988,17 +957,30 @@ export async function handleCommand(
|
|
|
988
957
|
`[${ts()}] [INFO] Cannot get chat info for ${chatId}: ${(err as Error).message}`,
|
|
989
958
|
);
|
|
990
959
|
}
|
|
991
|
-
} else if (platform.kind === "wechat") {
|
|
992
|
-
//
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
960
|
+
} else if (platform.kind === "wechat" || platform.kind === "feishu") {
|
|
961
|
+
// 私聊没有可写的群描述,因此会话绑定只持久化在 session-registry.json。
|
|
962
|
+
try {
|
|
963
|
+
const registry = await loadSessionRegistryForBinding();
|
|
964
|
+
const record = registry[chatId];
|
|
965
|
+
// 旧版飞书曾把私聊视为建群入口,并会清理私聊 registry。没有 p2p
|
|
966
|
+
// 标记的残留记录不能证明它是在固定用户目录创建的,因此只迁移一次:
|
|
967
|
+
// 先解除旧绑定;若本条是普通消息,随后在用户目录创建新的私聊 session。
|
|
968
|
+
if (platform.kind === "feishu" && record?.sessionId && record.chatType !== "p2p") {
|
|
969
|
+
unbindChatFromSession(record.sessionId, chatId);
|
|
970
|
+
displayCards.delete(chatId);
|
|
971
|
+
cancelQueuedMessage(record.sessionId);
|
|
972
|
+
sessionInfoMap.delete(chatId);
|
|
973
|
+
await removeSessionRegistryRecord(chatId);
|
|
974
|
+
logTrace(tid, "BRANCH", {
|
|
975
|
+
reason: "migrate_legacy_feishu_p2p_binding",
|
|
976
|
+
chatId,
|
|
977
|
+
oldSessionId: record.sessionId,
|
|
978
|
+
});
|
|
979
|
+
} else if (record && record.sessionId && record.tool) {
|
|
980
|
+
sessionId = record.sessionId;
|
|
999
981
|
descriptionTool = record.tool;
|
|
1000
982
|
toolLabel = toolDisplayName(descriptionTool);
|
|
1001
|
-
//
|
|
983
|
+
// 确保内存状态在冷启动后恢复;bindChatToSession 是幂等的。
|
|
1002
984
|
if (!sessionInfoMap.has(chatId)) {
|
|
1003
985
|
sessionInfoMap.set(chatId, {
|
|
1004
986
|
sessionId,
|
|
@@ -1008,8 +990,8 @@ export async function handleCommand(
|
|
|
1008
990
|
tool: descriptionTool,
|
|
1009
991
|
});
|
|
1010
992
|
}
|
|
1011
|
-
bindChatToSession(sessionId, chatId);
|
|
1012
|
-
}
|
|
993
|
+
bindChatToSession(sessionId, chatId);
|
|
994
|
+
}
|
|
1013
995
|
} catch (err) {
|
|
1014
996
|
console.log(
|
|
1015
997
|
`[${ts()}] [INFO] Cannot load registry for p2p ${chatId}: ${(err as Error).message}`,
|
|
@@ -1060,11 +1042,11 @@ export async function handleCommand(
|
|
|
1060
1042
|
}
|
|
1061
1043
|
}
|
|
1062
1044
|
|
|
1063
|
-
//
|
|
1064
|
-
if (
|
|
1065
|
-
chatType === "p2p" &&
|
|
1066
|
-
platform.kind === "wechat" &&
|
|
1067
|
-
!isCommandText
|
|
1045
|
+
// P2P:首条非指令消息只更新 registry 中的展示名,不修改私聊信息。
|
|
1046
|
+
if (
|
|
1047
|
+
chatType === "p2p" &&
|
|
1048
|
+
(platform.kind === "wechat" || platform.kind === "feishu") &&
|
|
1049
|
+
!isCommandText
|
|
1068
1050
|
) {
|
|
1069
1051
|
try {
|
|
1070
1052
|
const reg = await loadSessionRegistryForBinding();
|
|
@@ -1093,12 +1075,12 @@ export async function handleCommand(
|
|
|
1093
1075
|
() => {},
|
|
1094
1076
|
);
|
|
1095
1077
|
console.log(
|
|
1096
|
-
`[${ts()}] [RENAME]
|
|
1078
|
+
`[${ts()}] [RENAME] ${platform.kind} P2P → "${newName2}"`,
|
|
1097
1079
|
);
|
|
1098
1080
|
}
|
|
1099
1081
|
} catch (err) {
|
|
1100
1082
|
console.error(
|
|
1101
|
-
`[${ts()}] [RENAME]
|
|
1083
|
+
`[${ts()}] [RENAME] ${platform.kind} P2P failed: ${(err as Error).message}`,
|
|
1102
1084
|
);
|
|
1103
1085
|
}
|
|
1104
1086
|
}
|
|
@@ -1211,10 +1193,11 @@ export async function handleCommand(
|
|
|
1211
1193
|
const allSessions = await getAllSessionsStatus();
|
|
1212
1194
|
const now = Date.now();
|
|
1213
1195
|
const cardData = allSessions.map((s) => ({
|
|
1214
|
-
sessionId: s.sessionId,
|
|
1215
|
-
chatName: s.chatName,
|
|
1216
|
-
chatId: s.chatId,
|
|
1217
|
-
|
|
1196
|
+
sessionId: s.sessionId,
|
|
1197
|
+
chatName: s.chatName,
|
|
1198
|
+
chatId: s.chatId,
|
|
1199
|
+
chatType: s.chatType,
|
|
1200
|
+
active: s.active,
|
|
1218
1201
|
turnCount: s.turnCount,
|
|
1219
1202
|
elapsedSeconds: s.active
|
|
1220
1203
|
? Math.floor((now - s.startTime) / 1000)
|
|
@@ -1222,7 +1205,10 @@ export async function handleCommand(
|
|
|
1222
1205
|
model: s.model,
|
|
1223
1206
|
tool: s.tool,
|
|
1224
1207
|
}));
|
|
1225
|
-
const card = buildSessionsCard(cardData, {
|
|
1208
|
+
const card = buildSessionsCard(cardData, {
|
|
1209
|
+
defaultToolLabel: toolDisplayName(resolveDefaultAgentTool()),
|
|
1210
|
+
fixedPrivateSession: isFeishuP2p(platform, chatType),
|
|
1211
|
+
});
|
|
1226
1212
|
const ok = await platform.sendRawCard(chatId, card);
|
|
1227
1213
|
console.log(
|
|
1228
1214
|
`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${cardData.length}`,
|
|
@@ -1231,16 +1217,22 @@ export async function handleCommand(
|
|
|
1231
1217
|
return;
|
|
1232
1218
|
}
|
|
1233
1219
|
|
|
1234
|
-
if (isCommandText && textLower === "/newh") {
|
|
1235
|
-
logTrace(tid, "BRANCH", { cmd: "/newh" });
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
cwd =
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1220
|
+
if (isCommandText && textLower === "/newh") {
|
|
1221
|
+
logTrace(tid, "BRANCH", { cmd: "/newh" });
|
|
1222
|
+
let cwd: string;
|
|
1223
|
+
if (isFeishuP2p(platform, chatType)) {
|
|
1224
|
+
// 飞书私聊不支持切换工作目录。即使 /cd 已为后续 /new 群聊
|
|
1225
|
+
// 保存了其他默认目录,/newh 仍必须在运行 ChatCCC 的用户目录重建。
|
|
1226
|
+
cwd = homedir();
|
|
1227
|
+
} else {
|
|
1228
|
+
const adapter = getAdapterForTool(descriptionTool, sessionId);
|
|
1229
|
+
try {
|
|
1230
|
+
const info = await adapter.getSessionInfo(sessionId);
|
|
1231
|
+
cwd = info?.cwd ?? (await getDefaultCwd(chatId));
|
|
1232
|
+
} catch {
|
|
1233
|
+
cwd = await getDefaultCwd(chatId);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1244
1236
|
|
|
1245
1237
|
// 第一步:创建新 session(此时尚未碰任何内存绑定,失败可直接返回,
|
|
1246
1238
|
// 旧 session 状态完全保留)。
|
|
@@ -1299,13 +1291,13 @@ export async function handleCommand(
|
|
|
1299
1291
|
.setChatAvatar(chatId, descriptionTool, "new")
|
|
1300
1292
|
.catch(() => {});
|
|
1301
1293
|
|
|
1302
|
-
await platform.sendCard(
|
|
1303
|
-
chatId,
|
|
1304
|
-
`${toolLabel} Session Reset`,
|
|
1305
|
-
`会话已重置为新的 **${toolLabel}** 会话。\n\n` +
|
|
1306
|
-
`**Session ID:** ${newSessionId}\n` +
|
|
1307
|
-
`**工作目录:** \`${cwd}
|
|
1308
|
-
`直接在这里发消息即可继续对话。\n` +
|
|
1294
|
+
await platform.sendCard(
|
|
1295
|
+
chatId,
|
|
1296
|
+
`${toolLabel} Session Reset`,
|
|
1297
|
+
`会话已重置为新的 **${toolLabel}** 会话。\n\n` +
|
|
1298
|
+
`**Session ID:** ${newSessionId}\n` +
|
|
1299
|
+
`**工作目录:** \`${cwd}\`${isFeishuP2p(platform, chatType) ? "(飞书私聊固定使用系统用户目录)" : "(沿用当前会话目录)"}\n\n` +
|
|
1300
|
+
`直接在这里发消息即可继续对话。\n` +
|
|
1309
1301
|
`发送 **/cd** 可切换新建会话的默认目录。\n` +
|
|
1310
1302
|
`发送 **/model** 查看或切换当前会话的模型。`,
|
|
1311
1303
|
"green",
|
|
@@ -1356,9 +1348,22 @@ export async function handleCommand(
|
|
|
1356
1348
|
}
|
|
1357
1349
|
|
|
1358
1350
|
// /session <number>:切换到 /sessions 列表中的指定会话
|
|
1359
|
-
const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
|
|
1360
|
-
if (sessionMatch) {
|
|
1361
|
-
|
|
1351
|
+
const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
|
|
1352
|
+
if (sessionMatch) {
|
|
1353
|
+
// 飞书私聊有自己唯一的常驻 session;历史群聊 session 只能回到对应群聊
|
|
1354
|
+
// 继续,禁止通过 /session 把它们重新绑定进私聊。
|
|
1355
|
+
if (isFeishuP2p(platform, chatType)) {
|
|
1356
|
+
await platform.sendCard(
|
|
1357
|
+
chatId,
|
|
1358
|
+
"/session",
|
|
1359
|
+
"飞书私聊使用固定的专属会话,不能切换到历史群聊会话。请回到对应群聊继续,或发送 /new 新建群聊。",
|
|
1360
|
+
"yellow",
|
|
1361
|
+
);
|
|
1362
|
+
logTrace(tid, "DONE", { outcome: "session_switch_disabled_feishu_p2p" });
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const index = parseInt(sessionMatch[1], 10) - 1;
|
|
1362
1367
|
logTrace(tid, "BRANCH", { cmd: "/session", index: index + 1 });
|
|
1363
1368
|
const allSessions = await getAllSessionsStatus();
|
|
1364
1369
|
const claudeOrdered = allSessions.filter(
|
|
@@ -1555,96 +1560,96 @@ export async function handleCommand(
|
|
|
1555
1560
|
}
|
|
1556
1561
|
|
|
1557
1562
|
// /git <args>:在「当前会话工作目录」执行 git 命令
|
|
1558
|
-
if (isCommandText && textLower === "/effort clear") {
|
|
1559
|
-
logTrace(tid, "BRANCH", { cmd: "/effort clear", sessionId, tool: descriptionTool });
|
|
1560
|
-
const efforts = getAllEffortsForTool(descriptionTool as AgentTool);
|
|
1561
|
-
const toolLabel = toolDisplayName(descriptionTool);
|
|
1562
|
-
if (efforts.length === 0) {
|
|
1563
|
-
const msg = `当前 ${toolLabel} 不支持 effort 切换。`;
|
|
1564
|
-
await (platform.kind === "wechat"
|
|
1565
|
-
? platform.sendText(chatId, msg)
|
|
1566
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1567
|
-
).catch(() => {});
|
|
1568
|
-
logTrace(tid, "DONE", { outcome: "effort_unsupported", tool: descriptionTool });
|
|
1569
|
-
return;
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
clearSessionEffortOverride(sessionId);
|
|
1573
|
-
const defaultEffort = getDefaultEffortForTool(descriptionTool as AgentTool);
|
|
1574
|
-
const msg = `已清除当前 ${toolLabel} 会话的 effort 覆盖,恢复使用: \`${defaultEffort || "(未指定)"}\``;
|
|
1575
|
-
await (platform.kind === "wechat"
|
|
1576
|
-
? platform.sendText(chatId, msg)
|
|
1577
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "green")
|
|
1578
|
-
).catch(() => {});
|
|
1579
|
-
logTrace(tid, "DONE", { outcome: "effort_cleared", sessionId, tool: descriptionTool });
|
|
1580
|
-
return;
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
if (isCommandText && textLower.startsWith("/effort ")) {
|
|
1584
|
-
const effortArg = text.slice(8).trim();
|
|
1585
|
-
if (!effortArg) return;
|
|
1586
|
-
logTrace(tid, "BRANCH", { cmd: "/effort", arg: effortArg, sessionId, tool: descriptionTool });
|
|
1587
|
-
|
|
1588
|
-
const efforts = getAllEffortsForTool(descriptionTool as AgentTool);
|
|
1589
|
-
const toolLabel = toolDisplayName(descriptionTool);
|
|
1590
|
-
if (efforts.length === 0) {
|
|
1591
|
-
const msg = `当前 ${toolLabel} 不支持 effort 切换。`;
|
|
1592
|
-
await (platform.kind === "wechat"
|
|
1593
|
-
? platform.sendText(chatId, msg)
|
|
1594
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1595
|
-
).catch(() => {});
|
|
1596
|
-
logTrace(tid, "DONE", { outcome: "effort_unsupported", tool: descriptionTool });
|
|
1597
|
-
return;
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
const target = findModelMatch(effortArg, efforts);
|
|
1601
|
-
if (!target) {
|
|
1602
|
-
const msg = `未找到匹配 "${effortArg}" 的 effort。当前 ${toolLabel} 可选 effort:\n${efforts.map(e => ` \`${e}\``).join("\n")}`;
|
|
1603
|
-
await (platform.kind === "wechat"
|
|
1604
|
-
? platform.sendText(chatId, msg)
|
|
1605
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1606
|
-
).catch(() => {});
|
|
1607
|
-
logTrace(tid, "DONE", { outcome: "effort_not_found", arg: effortArg, tool: descriptionTool });
|
|
1608
|
-
return;
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
setSessionEffortOverride(sessionId, target);
|
|
1612
|
-
const msg = `已切换当前 ${toolLabel} 会话 effort 为: \`${target}\``;
|
|
1613
|
-
await (platform.kind === "wechat"
|
|
1614
|
-
? platform.sendText(chatId, msg)
|
|
1615
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "green")
|
|
1616
|
-
).catch(() => {});
|
|
1617
|
-
logTrace(tid, "DONE", { outcome: "effort_switched", arg: effortArg, target, sessionId, tool: descriptionTool });
|
|
1618
|
-
return;
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
if (isCommandText && textLower === "/effort") {
|
|
1622
|
-
logTrace(tid, "BRANCH", { cmd: "/effort", sessionId, tool: descriptionTool });
|
|
1623
|
-
const efforts = getAllEffortsForTool(descriptionTool as AgentTool);
|
|
1624
|
-
const currentEffort = getEffectiveEffortForTool(descriptionTool, sessionId);
|
|
1625
|
-
const toolLabel = toolDisplayName(descriptionTool);
|
|
1626
|
-
|
|
1627
|
-
if (efforts.length === 0) {
|
|
1628
|
-
const msg = `当前 ${toolLabel} 不支持 effort 切换。`;
|
|
1629
|
-
await (platform.kind === "wechat"
|
|
1630
|
-
? platform.sendText(chatId, msg)
|
|
1631
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1632
|
-
).catch(() => {});
|
|
1633
|
-
} else if (platform.kind === "wechat") {
|
|
1634
|
-
const lines = [currentEffort ? `当前 effort (${toolLabel}): ${currentEffort}` : `当前 effort (${toolLabel}): 未指定`];
|
|
1635
|
-
lines.push("", "可切换 effort:");
|
|
1636
|
-
for (const e of efforts) lines.push(` ${e}`);
|
|
1637
|
-
lines.push("", "输入 /effort <effort> 切换 effort");
|
|
1638
|
-
await platform.sendText(chatId, lines.join("\n")).catch(() => {});
|
|
1639
|
-
} else {
|
|
1640
|
-
const card = buildEffortCard(currentEffort, efforts, descriptionTool);
|
|
1641
|
-
await platform.sendRawCard(chatId, card);
|
|
1642
|
-
}
|
|
1643
|
-
logTrace(tid, "DONE", { outcome: "effort_query", tool: descriptionTool });
|
|
1644
|
-
return;
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
|
|
1563
|
+
if (isCommandText && textLower === "/effort clear") {
|
|
1564
|
+
logTrace(tid, "BRANCH", { cmd: "/effort clear", sessionId, tool: descriptionTool });
|
|
1565
|
+
const efforts = getAllEffortsForTool(descriptionTool as AgentTool);
|
|
1566
|
+
const toolLabel = toolDisplayName(descriptionTool);
|
|
1567
|
+
if (efforts.length === 0) {
|
|
1568
|
+
const msg = `当前 ${toolLabel} 不支持 effort 切换。`;
|
|
1569
|
+
await (platform.kind === "wechat"
|
|
1570
|
+
? platform.sendText(chatId, msg)
|
|
1571
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1572
|
+
).catch(() => {});
|
|
1573
|
+
logTrace(tid, "DONE", { outcome: "effort_unsupported", tool: descriptionTool });
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
clearSessionEffortOverride(sessionId);
|
|
1578
|
+
const defaultEffort = getDefaultEffortForTool(descriptionTool as AgentTool);
|
|
1579
|
+
const msg = `已清除当前 ${toolLabel} 会话的 effort 覆盖,恢复使用: \`${defaultEffort || "(未指定)"}\``;
|
|
1580
|
+
await (platform.kind === "wechat"
|
|
1581
|
+
? platform.sendText(chatId, msg)
|
|
1582
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "green")
|
|
1583
|
+
).catch(() => {});
|
|
1584
|
+
logTrace(tid, "DONE", { outcome: "effort_cleared", sessionId, tool: descriptionTool });
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
if (isCommandText && textLower.startsWith("/effort ")) {
|
|
1589
|
+
const effortArg = text.slice(8).trim();
|
|
1590
|
+
if (!effortArg) return;
|
|
1591
|
+
logTrace(tid, "BRANCH", { cmd: "/effort", arg: effortArg, sessionId, tool: descriptionTool });
|
|
1592
|
+
|
|
1593
|
+
const efforts = getAllEffortsForTool(descriptionTool as AgentTool);
|
|
1594
|
+
const toolLabel = toolDisplayName(descriptionTool);
|
|
1595
|
+
if (efforts.length === 0) {
|
|
1596
|
+
const msg = `当前 ${toolLabel} 不支持 effort 切换。`;
|
|
1597
|
+
await (platform.kind === "wechat"
|
|
1598
|
+
? platform.sendText(chatId, msg)
|
|
1599
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1600
|
+
).catch(() => {});
|
|
1601
|
+
logTrace(tid, "DONE", { outcome: "effort_unsupported", tool: descriptionTool });
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
const target = findModelMatch(effortArg, efforts);
|
|
1606
|
+
if (!target) {
|
|
1607
|
+
const msg = `未找到匹配 "${effortArg}" 的 effort。当前 ${toolLabel} 可选 effort:\n${efforts.map(e => ` \`${e}\``).join("\n")}`;
|
|
1608
|
+
await (platform.kind === "wechat"
|
|
1609
|
+
? platform.sendText(chatId, msg)
|
|
1610
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1611
|
+
).catch(() => {});
|
|
1612
|
+
logTrace(tid, "DONE", { outcome: "effort_not_found", arg: effortArg, tool: descriptionTool });
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
setSessionEffortOverride(sessionId, target);
|
|
1617
|
+
const msg = `已切换当前 ${toolLabel} 会话 effort 为: \`${target}\``;
|
|
1618
|
+
await (platform.kind === "wechat"
|
|
1619
|
+
? platform.sendText(chatId, msg)
|
|
1620
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "green")
|
|
1621
|
+
).catch(() => {});
|
|
1622
|
+
logTrace(tid, "DONE", { outcome: "effort_switched", arg: effortArg, target, sessionId, tool: descriptionTool });
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
if (isCommandText && textLower === "/effort") {
|
|
1627
|
+
logTrace(tid, "BRANCH", { cmd: "/effort", sessionId, tool: descriptionTool });
|
|
1628
|
+
const efforts = getAllEffortsForTool(descriptionTool as AgentTool);
|
|
1629
|
+
const currentEffort = getEffectiveEffortForTool(descriptionTool, sessionId);
|
|
1630
|
+
const toolLabel = toolDisplayName(descriptionTool);
|
|
1631
|
+
|
|
1632
|
+
if (efforts.length === 0) {
|
|
1633
|
+
const msg = `当前 ${toolLabel} 不支持 effort 切换。`;
|
|
1634
|
+
await (platform.kind === "wechat"
|
|
1635
|
+
? platform.sendText(chatId, msg)
|
|
1636
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1637
|
+
).catch(() => {});
|
|
1638
|
+
} else if (platform.kind === "wechat") {
|
|
1639
|
+
const lines = [currentEffort ? `当前 effort (${toolLabel}): ${currentEffort}` : `当前 effort (${toolLabel}): 未指定`];
|
|
1640
|
+
lines.push("", "可切换 effort:");
|
|
1641
|
+
for (const e of efforts) lines.push(` ${e}`);
|
|
1642
|
+
lines.push("", "输入 /effort <effort> 切换 effort");
|
|
1643
|
+
await platform.sendText(chatId, lines.join("\n")).catch(() => {});
|
|
1644
|
+
} else {
|
|
1645
|
+
const card = buildEffortCard(currentEffort, efforts, descriptionTool);
|
|
1646
|
+
await platform.sendRawCard(chatId, card);
|
|
1647
|
+
}
|
|
1648
|
+
logTrace(tid, "DONE", { outcome: "effort_query", tool: descriptionTool });
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
|
|
1648
1653
|
const args = text === "/git" ? "" : text.slice(5).trim();
|
|
1649
1654
|
logTrace(tid, "BRANCH", { cmd: "/git", args: args || "(none)" });
|
|
1650
1655
|
if (!args) {
|
|
@@ -1802,53 +1807,54 @@ export async function handleCommand(
|
|
|
1802
1807
|
}
|
|
1803
1808
|
|
|
1804
1809
|
// 无会话上下文 → /sessions 仍是有效指令,不触发飞书私聊自动建群。
|
|
1805
|
-
if (isCommandText && textLower === "/effort") {
|
|
1806
|
-
const defaultTool = resolveDefaultAgentTool();
|
|
1807
|
-
const efforts = getAllEffortsForTool(defaultTool);
|
|
1808
|
-
const currentEffort = getDefaultEffortForTool(defaultTool);
|
|
1809
|
-
const toolLabel = toolDisplayName(defaultTool);
|
|
1810
|
-
|
|
1811
|
-
if (efforts.length === 0) {
|
|
1812
|
-
const msg = `当前默认 agent (${toolLabel}) 不支持 effort 切换。`;
|
|
1813
|
-
await (platform.kind === "wechat"
|
|
1814
|
-
? platform.sendText(chatId, msg)
|
|
1815
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1816
|
-
).catch(() => {});
|
|
1817
|
-
} else if (platform.kind === "wechat") {
|
|
1818
|
-
const lines = [currentEffort ? `当前默认 effort (${toolLabel}): ${currentEffort}` : `当前默认 effort (${toolLabel}): 未指定`];
|
|
1819
|
-
lines.push("", "可切换 effort:");
|
|
1820
|
-
for (const e of efforts) lines.push(` ${e}`);
|
|
1821
|
-
lines.push("", "在会话中输入 /effort <effort> 切换 effort");
|
|
1822
|
-
await platform.sendText(chatId, lines.join("\n")).catch(() => {});
|
|
1823
|
-
} else {
|
|
1824
|
-
const card = buildEffortCard(currentEffort, efforts, defaultTool);
|
|
1825
|
-
await platform.sendRawCard(chatId, card);
|
|
1826
|
-
}
|
|
1827
|
-
logTrace(tid, "DONE", { outcome: "effort_query", defaultTool });
|
|
1828
|
-
return;
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
if (isCommandText && textLower.startsWith("/effort ")) {
|
|
1832
|
-
const defaultTool = resolveDefaultAgentTool();
|
|
1833
|
-
const toolLabel = toolDisplayName(defaultTool);
|
|
1834
|
-
const msg = `当前没有绑定会话。请先进入 Claude/Codex 会话,再输入 /effort <effort> 切换当前会话的 effort。当前默认 agent: ${toolLabel}`;
|
|
1835
|
-
await (platform.kind === "wechat"
|
|
1836
|
-
? platform.sendText(chatId, msg)
|
|
1837
|
-
: platform.sendCard(chatId, "Effort 切换", msg, "yellow")
|
|
1838
|
-
).catch(() => {});
|
|
1839
|
-
logTrace(tid, "DONE", { outcome: "effort_no_session", defaultTool });
|
|
1840
|
-
return;
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
if (isCommandText && textLower === "/sessions") {
|
|
1810
|
+
if (isCommandText && textLower === "/effort") {
|
|
1811
|
+
const defaultTool = resolveDefaultAgentTool();
|
|
1812
|
+
const efforts = getAllEffortsForTool(defaultTool);
|
|
1813
|
+
const currentEffort = getDefaultEffortForTool(defaultTool);
|
|
1814
|
+
const toolLabel = toolDisplayName(defaultTool);
|
|
1815
|
+
|
|
1816
|
+
if (efforts.length === 0) {
|
|
1817
|
+
const msg = `当前默认 agent (${toolLabel}) 不支持 effort 切换。`;
|
|
1818
|
+
await (platform.kind === "wechat"
|
|
1819
|
+
? platform.sendText(chatId, msg)
|
|
1820
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "red")
|
|
1821
|
+
).catch(() => {});
|
|
1822
|
+
} else if (platform.kind === "wechat") {
|
|
1823
|
+
const lines = [currentEffort ? `当前默认 effort (${toolLabel}): ${currentEffort}` : `当前默认 effort (${toolLabel}): 未指定`];
|
|
1824
|
+
lines.push("", "可切换 effort:");
|
|
1825
|
+
for (const e of efforts) lines.push(` ${e}`);
|
|
1826
|
+
lines.push("", "在会话中输入 /effort <effort> 切换 effort");
|
|
1827
|
+
await platform.sendText(chatId, lines.join("\n")).catch(() => {});
|
|
1828
|
+
} else {
|
|
1829
|
+
const card = buildEffortCard(currentEffort, efforts, defaultTool);
|
|
1830
|
+
await platform.sendRawCard(chatId, card);
|
|
1831
|
+
}
|
|
1832
|
+
logTrace(tid, "DONE", { outcome: "effort_query", defaultTool });
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
if (isCommandText && textLower.startsWith("/effort ")) {
|
|
1837
|
+
const defaultTool = resolveDefaultAgentTool();
|
|
1838
|
+
const toolLabel = toolDisplayName(defaultTool);
|
|
1839
|
+
const msg = `当前没有绑定会话。请先进入 Claude/Codex 会话,再输入 /effort <effort> 切换当前会话的 effort。当前默认 agent: ${toolLabel}`;
|
|
1840
|
+
await (platform.kind === "wechat"
|
|
1841
|
+
? platform.sendText(chatId, msg)
|
|
1842
|
+
: platform.sendCard(chatId, "Effort 切换", msg, "yellow")
|
|
1843
|
+
).catch(() => {});
|
|
1844
|
+
logTrace(tid, "DONE", { outcome: "effort_no_session", defaultTool });
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
if (isCommandText && textLower === "/sessions") {
|
|
1844
1849
|
logTrace(tid, "BRANCH", { cmd: "/sessions", scope: "global" });
|
|
1845
1850
|
const allSessions = await getAllSessionsStatus();
|
|
1846
1851
|
const now = Date.now();
|
|
1847
1852
|
const cardData = allSessions.map((s) => ({
|
|
1848
|
-
sessionId: s.sessionId,
|
|
1849
|
-
chatName: s.chatName,
|
|
1850
|
-
chatId: s.chatId,
|
|
1851
|
-
|
|
1853
|
+
sessionId: s.sessionId,
|
|
1854
|
+
chatName: s.chatName,
|
|
1855
|
+
chatId: s.chatId,
|
|
1856
|
+
chatType: s.chatType,
|
|
1857
|
+
active: s.active,
|
|
1852
1858
|
turnCount: s.turnCount,
|
|
1853
1859
|
elapsedSeconds: s.active
|
|
1854
1860
|
? Math.floor((now - s.startTime) / 1000)
|
|
@@ -1856,7 +1862,10 @@ export async function handleCommand(
|
|
|
1856
1862
|
model: s.model,
|
|
1857
1863
|
tool: s.tool,
|
|
1858
1864
|
}));
|
|
1859
|
-
const card = buildSessionsCard(cardData, {
|
|
1865
|
+
const card = buildSessionsCard(cardData, {
|
|
1866
|
+
defaultToolLabel: toolDisplayName(resolveDefaultAgentTool()),
|
|
1867
|
+
fixedPrivateSession: isFeishuP2p(platform, chatType),
|
|
1868
|
+
});
|
|
1860
1869
|
const ok = await platform.sendRawCard(chatId, card);
|
|
1861
1870
|
console.log(
|
|
1862
1871
|
`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${cardData.length}`,
|
|
@@ -1865,53 +1874,63 @@ export async function handleCommand(
|
|
|
1865
1874
|
return;
|
|
1866
1875
|
}
|
|
1867
1876
|
|
|
1868
|
-
//
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
const
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
);
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
tool
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
);
|
|
1877
|
+
// 飞书私聊普通消息:首次使用时在当前私聊创建并持久化一个专属 session,
|
|
1878
|
+
// 随后的消息会在上面的 registry 路由中继续该 session。只有显式 /new 才建群。
|
|
1879
|
+
if (isFeishuP2p(platform, chatType) && !isCommandText) {
|
|
1880
|
+
const tool = resolveDefaultAgentTool();
|
|
1881
|
+
const toolLabel = toolDisplayName(tool);
|
|
1882
|
+
// 私聊 cwd 故意不读取 /cd 的 chatId 默认值:/cd 只为之后显式
|
|
1883
|
+
// /new 创建的群聊服务,飞书私聊始终从 ChatCCC 运行账号的用户目录启动。
|
|
1884
|
+
const cwd = homedir();
|
|
1885
|
+
logTrace(tid, "BRANCH", { cmd: "auto_new_feishu_p2p", tool, cwd });
|
|
1886
|
+
|
|
1887
|
+
try {
|
|
1888
|
+
const init = await initClaudeSession(tool, cwd);
|
|
1889
|
+
const sessionId = init.sessionId;
|
|
1890
|
+
const chatName = sessionChatName(text.slice(0, 10) || "私聊会话", cwd);
|
|
1891
|
+
const switchResult = await switchChatBinding({
|
|
1892
|
+
chatId,
|
|
1893
|
+
chatType,
|
|
1894
|
+
oldSessionId: null,
|
|
1895
|
+
newSessionId: sessionId,
|
|
1896
|
+
tool,
|
|
1897
|
+
chatName,
|
|
1898
|
+
newDescription: `${sessionPrefixForTool(tool)} ${sessionId}`,
|
|
1899
|
+
updateChatInfoFn: (cid, name, desc) =>
|
|
1900
|
+
platform.updateChatInfo(cid, name, desc),
|
|
1901
|
+
});
|
|
1902
|
+
if (!switchResult.ok) {
|
|
1903
|
+
throw switchResult.error ?? new Error("Failed to bind Feishu private session");
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
await resumeAndPrompt(
|
|
1907
|
+
sessionId,
|
|
1908
|
+
promptText,
|
|
1909
|
+
platform,
|
|
1910
|
+
chatId,
|
|
1911
|
+
msgTimestamp,
|
|
1912
|
+
tool,
|
|
1913
|
+
tid,
|
|
1914
|
+
);
|
|
1915
|
+
logTrace(tid, "DONE", {
|
|
1916
|
+
outcome: "auto_new_feishu_p2p_prompt_done",
|
|
1917
|
+
chatId,
|
|
1918
|
+
sessionId,
|
|
1919
|
+
tool,
|
|
1920
|
+
cwd,
|
|
1921
|
+
});
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
console.error(`[${ts()}] [AUTO-P2P] FAIL: ${(err as Error).message}`);
|
|
1924
|
+
logTrace(tid, "DONE", {
|
|
1925
|
+
outcome: "auto_new_feishu_p2p_fail",
|
|
1926
|
+
error: (err as Error).message,
|
|
1927
|
+
});
|
|
1928
|
+
await platform.sendCard(
|
|
1929
|
+
chatId,
|
|
1930
|
+
"Error",
|
|
1931
|
+
`Failed to create ${toolLabel} private session:\n${(err as Error).message}`,
|
|
1932
|
+
"red",
|
|
1933
|
+
);
|
|
1915
1934
|
}
|
|
1916
1935
|
return;
|
|
1917
1936
|
}
|