chatccc 0.2.138 → 0.2.140
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 +0 -6
- package/agent-prompts/claude_specific.md +1 -13
- package/agent-prompts/codex_specific.md +0 -1
- package/agent-prompts/cursor_specific.md +0 -1
- package/package.json +1 -1
- package/src/__tests__/claude-adapter.test.ts +31 -1
- package/src/adapters/adapter-interface.ts +0 -2
- package/src/adapters/claude-adapter.ts +64 -19
- package/src/orchestrator.ts +14 -31
- package/src/session.ts +10 -7
package/README.md
CHANGED
|
@@ -311,12 +311,6 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
311
311
|
| `/sessions` | 查看所有会话状态 |
|
|
312
312
|
| `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
|
|
313
313
|
| `/restart` | 重启机器人进程 |
|
|
314
|
-
| `/plan [内容]` | 以 plan 模式传递给 Agent,**不跳过权限检查**(无 `--dangerously-skip-permissions`) |
|
|
315
|
-
| `/ask [内容]` | 以 plan 模式传递给 Agent,**不跳过权限检查**(同 `/plan`) |
|
|
316
|
-
|
|
317
|
-
> **`/plan` 与 `/ask`**:这两个指令不会被 ChatCCC 当作内置命令处理,而是把用户原话(含指令前缀)完整传给 Agent。与普通消息的区别是:调用时不添加 `--dangerously-skip-permissions`,改为 `--permission-mode plan`,Agent 会先在权限沙盒内运行,遇到命令执行等操作时需要确认。适合需要安全审查的任务场景。
|
|
318
|
-
>
|
|
319
|
-
> **群名**:`/plan` / `/ask` 消息如果是群内第一条消息,也会触发群名更新。为美观起见,群名会自动去除 `/plan` 或 `/ask` 前缀。
|
|
320
314
|
|
|
321
315
|
> **模型切换**:`/model` 查看可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。当前仅 Claude Code 支持模型切换(需同时填写 `claude.model` 和 `claude.subagentModel`),Cursor / Codex 切换正在开发中。
|
|
322
316
|
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
# Claude-Specific Injection Prompt
|
|
2
|
-
|
|
3
|
-
Use this prompt as the Claude Agent SDK injection prompt for this project.
|
|
4
|
-
|
|
5
|
-
Project workspace:
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
f:\users\weizhangjian\feishuclauderprivate
|
|
9
|
-
```
|
|
10
|
-
|
|
11
1
|
## Repeated Successful Command Guard
|
|
12
2
|
|
|
13
3
|
When working in this project through Claude Agent SDK, repeated successful shell commands are a completion signal, not a reason to keep using tools.
|
|
@@ -50,6 +40,4 @@ Never execute the same successful shell command three times for the same task in
|
|
|
50
40
|
|
|
51
41
|
If the same command has already succeeded twice in a row, the next assistant action must be a final response to the user, not another tool call.
|
|
52
42
|
|
|
53
|
-
Successful repeated command execution is a terminal condition. Prefer a concise final response over further tool calls.
|
|
54
|
-
|
|
55
|
-
如果计划写好了等待你的审阅,将计划文件通过当前聊天软件发给用户
|
|
43
|
+
Successful repeated command execution is a terminal condition. Prefer a concise final response over further tool calls.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
如果计划写好了等待你的审阅,将计划文件通过当前聊天软件发给用户
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
如果计划写好了等待你的审阅,将计划文件通过当前聊天软件发给用户
|
package/package.json
CHANGED
|
@@ -467,7 +467,37 @@ describe("buildClaudePromptText", () => {
|
|
|
467
467
|
|
|
468
468
|
describe("buildSdkEnv", () => {
|
|
469
469
|
it("returns undefined when no SDK env override is configured", () => {
|
|
470
|
-
|
|
470
|
+
const originalPath = process.env.PATH;
|
|
471
|
+
try {
|
|
472
|
+
process.env.PATH = process.platform === "win32"
|
|
473
|
+
? "C:\\Program Files\\Git\\usr\\bin;C:\\Windows\\System32"
|
|
474
|
+
: "/usr/bin:/bin";
|
|
475
|
+
expect(buildSdkEnv("", " ", undefined)).toBeUndefined();
|
|
476
|
+
} finally {
|
|
477
|
+
process.env.PATH = originalPath;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it("prefers Git Bash before WindowsApps for Claude SDK subprocesses on Windows", () => {
|
|
482
|
+
if (process.platform !== "win32") return;
|
|
483
|
+
const originalPath = process.env.PATH;
|
|
484
|
+
try {
|
|
485
|
+
process.env.PATH = [
|
|
486
|
+
"C:\\Users\\weizhangjian\\AppData\\Local\\Microsoft\\WindowsApps",
|
|
487
|
+
"C:\\Program Files\\Git\\usr\\bin",
|
|
488
|
+
"%PATH%",
|
|
489
|
+
"C:\\Windows\\System32",
|
|
490
|
+
].join(";");
|
|
491
|
+
|
|
492
|
+
const env = buildSdkEnv("", "", "");
|
|
493
|
+
|
|
494
|
+
expect(env).toBeDefined();
|
|
495
|
+
const parts = env!.PATH!.split(";");
|
|
496
|
+
expect(parts[0]).toBe("C:\\Program Files\\Git\\usr\\bin");
|
|
497
|
+
expect(parts).not.toContain("%PATH%");
|
|
498
|
+
} finally {
|
|
499
|
+
process.env.PATH = originalPath;
|
|
500
|
+
}
|
|
471
501
|
});
|
|
472
502
|
|
|
473
503
|
it("sets requested SDK env overrides and removes conflicting Claude auth/model vars", () => {
|
|
@@ -128,8 +128,6 @@ export interface ToolPromptOptions {
|
|
|
128
128
|
onProcessStart?: (info: ToolProcessInfo) => void;
|
|
129
129
|
/** Called when the adapter leaves the prompt process scope normally or by abort. */
|
|
130
130
|
onProcessExit?: (info: ToolProcessInfo) => void;
|
|
131
|
-
/** 是否为 /plan 或 /ask 模式:不开启 dangerous skip permissions */
|
|
132
|
-
planMode?: boolean;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
// ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { delimiter, dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
6
|
import {
|
|
@@ -82,23 +82,73 @@ export function buildSdkEnv(
|
|
|
82
82
|
const apiKeyTrim = (apiKey ?? "").trim();
|
|
83
83
|
const baseUrlTrim = (baseUrl ?? "").trim();
|
|
84
84
|
|
|
85
|
-
if (!subagentModelTrim && !apiKeyTrim && !baseUrlTrim) return undefined;
|
|
86
|
-
|
|
87
85
|
const env: Record<string, string | undefined> = { ...process.env };
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
let mutated = preferGitBashOnWindows(env);
|
|
87
|
+
|
|
88
|
+
if (subagentModelTrim || apiKeyTrim || baseUrlTrim) {
|
|
89
|
+
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
90
|
+
delete env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
91
|
+
delete env.ANTHROPIC_MODEL;
|
|
92
|
+
delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
93
|
+
delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
94
|
+
delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
95
|
+
delete env.CLAUDE_CODE_EFFORT_LEVEL;
|
|
96
|
+
delete env.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
97
|
+
mutated = true;
|
|
98
|
+
}
|
|
96
99
|
|
|
97
100
|
if (subagentModelTrim) env.CLAUDE_CODE_SUBAGENT_MODEL = subagentModelTrim;
|
|
98
101
|
if (apiKeyTrim) env.ANTHROPIC_API_KEY = apiKeyTrim;
|
|
99
102
|
if (baseUrlTrim) env.ANTHROPIC_BASE_URL = baseUrlTrim;
|
|
100
103
|
|
|
101
|
-
return env;
|
|
104
|
+
return mutated ? env : undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function preferGitBashOnWindows(env: Record<string, string | undefined>): boolean {
|
|
108
|
+
if (process.platform !== "win32") return false;
|
|
109
|
+
|
|
110
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
111
|
+
const rawPath = env[pathKey];
|
|
112
|
+
if (!rawPath) return false;
|
|
113
|
+
|
|
114
|
+
const parts = rawPath.split(delimiter).filter((part) => part && part !== "%PATH%");
|
|
115
|
+
const preferred = findPreferredGitBashPath(parts);
|
|
116
|
+
if (!preferred) {
|
|
117
|
+
const nextPath = parts.join(delimiter);
|
|
118
|
+
if (nextPath !== rawPath) {
|
|
119
|
+
env[pathKey] = nextPath;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const preferredLower = preferred.toLowerCase();
|
|
126
|
+
const reordered = [
|
|
127
|
+
preferred,
|
|
128
|
+
...parts.filter((part) => part.toLowerCase() !== preferredLower),
|
|
129
|
+
];
|
|
130
|
+
const nextPath = reordered.join(delimiter);
|
|
131
|
+
if (nextPath === rawPath) return false;
|
|
132
|
+
env[pathKey] = nextPath;
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function findPreferredGitBashPath(pathParts: string[]): string | undefined {
|
|
137
|
+
const programFilesGit = join(
|
|
138
|
+
process.env.ProgramFiles ?? "C:\\Program Files",
|
|
139
|
+
"Git",
|
|
140
|
+
"usr",
|
|
141
|
+
"bin",
|
|
142
|
+
);
|
|
143
|
+
if (existsSync(join(programFilesGit, "bash.exe"))) return programFilesGit;
|
|
144
|
+
|
|
145
|
+
return pathParts.find((part) => {
|
|
146
|
+
if (!/(\\|\/)(Git)(\\|\/)usr(\\|\/)bin$/i.test(part) &&
|
|
147
|
+
!/(\\|\/)Fork(\\|\/)gitInstance(\\|\/)[^\\/]+(\\|\/)bin$/i.test(part)) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return existsSync(join(part, "bash.exe"));
|
|
151
|
+
});
|
|
102
152
|
}
|
|
103
153
|
|
|
104
154
|
function readMcpServersConfig(): Record<string, unknown> | undefined {
|
|
@@ -243,7 +293,6 @@ function buildSdkOptions(args: {
|
|
|
243
293
|
apiKey?: string;
|
|
244
294
|
baseUrl?: string;
|
|
245
295
|
maxTurn: number;
|
|
246
|
-
planMode?: boolean;
|
|
247
296
|
abortController?: AbortController;
|
|
248
297
|
}): ClaudeSdkSessionOptions {
|
|
249
298
|
const {
|
|
@@ -255,7 +304,6 @@ function buildSdkOptions(args: {
|
|
|
255
304
|
apiKey,
|
|
256
305
|
baseUrl,
|
|
257
306
|
maxTurn,
|
|
258
|
-
planMode,
|
|
259
307
|
abortController,
|
|
260
308
|
} = args;
|
|
261
309
|
|
|
@@ -263,7 +311,7 @@ function buildSdkOptions(args: {
|
|
|
263
311
|
cwd,
|
|
264
312
|
abortController,
|
|
265
313
|
settingSources: ["user", "project", "local"] as SettingSource[],
|
|
266
|
-
permissionMode:
|
|
314
|
+
permissionMode: "bypassPermissions",
|
|
267
315
|
autoCompactEnabled: true,
|
|
268
316
|
maxTurns: maxTurn,
|
|
269
317
|
skills: "all",
|
|
@@ -275,9 +323,7 @@ function buildSdkOptions(args: {
|
|
|
275
323
|
},
|
|
276
324
|
};
|
|
277
325
|
|
|
278
|
-
|
|
279
|
-
options.allowDangerouslySkipPermissions = true;
|
|
280
|
-
}
|
|
326
|
+
options.allowDangerouslySkipPermissions = true;
|
|
281
327
|
if (!isEmpty(model)) {
|
|
282
328
|
options.model = model;
|
|
283
329
|
}
|
|
@@ -410,7 +456,6 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
410
456
|
apiKey: this.apiKey,
|
|
411
457
|
baseUrl: this.baseUrl,
|
|
412
458
|
maxTurn: this.maxTurn,
|
|
413
|
-
planMode: options?.planMode,
|
|
414
459
|
abortController,
|
|
415
460
|
})),
|
|
416
461
|
);
|
package/src/orchestrator.ts
CHANGED
|
@@ -94,21 +94,6 @@ export function sessionChatName(left: string, cwd: string): string {
|
|
|
94
94
|
return `${left}-${cwdDisplayName(cwd)}`;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
/** 判断用户消息是否以 /plan 或 /ask 开头(忽略大小写和尾部空格) */
|
|
98
|
-
function isPlanOrAsk(text: string): boolean {
|
|
99
|
-
const t = text.toLowerCase().trim();
|
|
100
|
-
return t === "/plan" || t.startsWith("/plan ") || t === "/ask" || t.startsWith("/ask ");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** 剥离 /plan 或 /ask 前缀,用于群名等展示场景 */
|
|
104
|
-
function stripPlanOrAskPrefix(text: string): string {
|
|
105
|
-
if (isPlanOrAsk(text)) {
|
|
106
|
-
const match = text.match(/^\/(?:plan|ask)\s*(.*)/i);
|
|
107
|
-
return (match?.[1] ?? "").trim() || text.trim();
|
|
108
|
-
}
|
|
109
|
-
return text;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
97
|
/** 模型模糊匹配:精确匹配优先,否则找子串匹配(模型名越短越优先) */
|
|
113
98
|
function findModelMatch(input: string, models: string[]): string | null {
|
|
114
99
|
if (models.length === 0) return null;
|
|
@@ -134,7 +119,7 @@ function shouldSendWechatProcessingAck(
|
|
|
134
119
|
chatType: string,
|
|
135
120
|
text: string,
|
|
136
121
|
): boolean {
|
|
137
|
-
return platform.kind === "wechat" && chatType === "p2p" &&
|
|
122
|
+
return platform.kind === "wechat" && chatType === "p2p" && !textLower.startsWith("/");
|
|
138
123
|
}
|
|
139
124
|
|
|
140
125
|
function isNonWechatP2p(platform: PlatformAdapter, chatType: string): boolean {
|
|
@@ -616,10 +601,10 @@ export async function handleCommand(
|
|
|
616
601
|
if (
|
|
617
602
|
chatType !== "p2p" &&
|
|
618
603
|
isUntitledSessionChatName(chatInfo!.name) &&
|
|
619
|
-
|
|
604
|
+
!textLower.startsWith("/")
|
|
620
605
|
) {
|
|
621
606
|
const MAX_PREFIX = 10;
|
|
622
|
-
const prefix =
|
|
607
|
+
const prefix = text.slice(0, MAX_PREFIX);
|
|
623
608
|
const adapter = getAdapterForTool(descriptionTool, sessionId);
|
|
624
609
|
const info = await adapter
|
|
625
610
|
.getSessionInfo(sessionId)
|
|
@@ -651,7 +636,7 @@ export async function handleCommand(
|
|
|
651
636
|
if (
|
|
652
637
|
chatType === "p2p" &&
|
|
653
638
|
platform.kind === "wechat" &&
|
|
654
|
-
|
|
639
|
+
!textLower.startsWith("/")
|
|
655
640
|
) {
|
|
656
641
|
try {
|
|
657
642
|
const reg = await loadSessionRegistryForBinding();
|
|
@@ -1250,11 +1235,10 @@ export async function handleCommand(
|
|
|
1250
1235
|
text,
|
|
1251
1236
|
platform,
|
|
1252
1237
|
chatId,
|
|
1253
|
-
msgTimestamp,
|
|
1254
|
-
descriptionTool,
|
|
1255
|
-
tid,
|
|
1256
|
-
|
|
1257
|
-
);
|
|
1238
|
+
msgTimestamp,
|
|
1239
|
+
descriptionTool,
|
|
1240
|
+
tid,
|
|
1241
|
+
);
|
|
1258
1242
|
logTrace(tid, "DONE", { outcome: "resume_done", sessionId });
|
|
1259
1243
|
console.log(`[${ts()}] [RESUME] Session ${sessionId} done`);
|
|
1260
1244
|
} catch (err) {
|
|
@@ -1328,7 +1312,7 @@ export async function handleCommand(
|
|
|
1328
1312
|
}
|
|
1329
1313
|
|
|
1330
1314
|
// 飞书私聊普通消息:不再绑定私聊本身,而是自动创建会话群并把私聊内容作为首轮 prompt。
|
|
1331
|
-
if (isNonWechatP2p(platform, chatType) &&
|
|
1315
|
+
if (isNonWechatP2p(platform, chatType) && !textLower.startsWith("/")) {
|
|
1332
1316
|
const tool = resolveDefaultAgentTool();
|
|
1333
1317
|
const toolLabel = toolDisplayName(tool);
|
|
1334
1318
|
logTrace(tid, "BRANCH", { cmd: "auto_new_from_p2p", tool });
|
|
@@ -1369,7 +1353,7 @@ export async function handleCommand(
|
|
|
1369
1353
|
}
|
|
1370
1354
|
|
|
1371
1355
|
const cwd = sessionCwd;
|
|
1372
|
-
const initialName = sessionChatName(
|
|
1356
|
+
const initialName = sessionChatName(text.slice(0, 10) || "新会话", cwd);
|
|
1373
1357
|
let newChatId: string;
|
|
1374
1358
|
try {
|
|
1375
1359
|
newChatId = await platform.createGroup(initialName, [openId]);
|
|
@@ -1450,11 +1434,10 @@ export async function handleCommand(
|
|
|
1450
1434
|
text,
|
|
1451
1435
|
platform,
|
|
1452
1436
|
newChatId,
|
|
1453
|
-
msgTimestamp,
|
|
1454
|
-
tool,
|
|
1455
|
-
tid,
|
|
1456
|
-
|
|
1457
|
-
);
|
|
1437
|
+
msgTimestamp,
|
|
1438
|
+
tool,
|
|
1439
|
+
tid,
|
|
1440
|
+
);
|
|
1458
1441
|
console.log(`[${ts()}] [AUTO-P2P 5/5] First prompt sent → OK`);
|
|
1459
1442
|
logTrace(tid, "DONE", { outcome: "auto_new_p2p_prompt_done", newChatId, sessionId, tool });
|
|
1460
1443
|
} catch (err) {
|
package/src/session.ts
CHANGED
|
@@ -834,9 +834,8 @@ export async function resumeAndPrompt(
|
|
|
834
834
|
msgTimestamp: number,
|
|
835
835
|
tool: string,
|
|
836
836
|
traceId?: string,
|
|
837
|
-
planMode?: boolean,
|
|
838
837
|
): Promise<void> {
|
|
839
|
-
return runAgentSession(sessionId, userText, platform, chatId, msgTimestamp, tool, traceId
|
|
838
|
+
return runAgentSession(sessionId, userText, platform, chatId, msgTimestamp, tool, traceId);
|
|
840
839
|
}
|
|
841
840
|
|
|
842
841
|
// ---------------------------------------------------------------------------
|
|
@@ -851,7 +850,6 @@ export async function runAgentSession(
|
|
|
851
850
|
msgTimestamp: number,
|
|
852
851
|
tool: string,
|
|
853
852
|
traceId?: string,
|
|
854
|
-
planMode?: boolean,
|
|
855
853
|
): Promise<void> {
|
|
856
854
|
const tid = traceId ?? "";
|
|
857
855
|
|
|
@@ -1015,7 +1013,9 @@ export async function runAgentSession(
|
|
|
1015
1013
|
const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(prevState.status);
|
|
1016
1014
|
const cardContent = truncateContent(prevState.accumulatedContent + prevState.finalReply) || " ";
|
|
1017
1015
|
const doneCard = buildProgressCard(cardContent, { showStop: false, headerTitle, headerTemplate });
|
|
1018
|
-
await pp.cardUpdate(display.cardId, doneCard, nextSeq).catch(
|
|
1016
|
+
await pp.cardUpdate(display.cardId, doneCard, nextSeq).catch(err => {
|
|
1017
|
+
console.error(`[${ts()}] [DISPLAY] prevState final cardUpdate failed: ${(err as Error).message}`);
|
|
1018
|
+
});
|
|
1019
1019
|
// cardUpdate IO 期间统一 loop 可能也已处理此 display → 删前检查引用
|
|
1020
1020
|
const stillOursAfterUpdate = displayCards.get(displayChatId) === display;
|
|
1021
1021
|
displayCards.delete(displayChatId);
|
|
@@ -1110,7 +1110,6 @@ export async function runAgentSession(
|
|
|
1110
1110
|
clearPromptProcessMonitor(sessionId);
|
|
1111
1111
|
if (exitInfo.pid !== undefined) unregisterProcess(exitInfo.pid);
|
|
1112
1112
|
},
|
|
1113
|
-
planMode,
|
|
1114
1113
|
})) {
|
|
1115
1114
|
for (const block of unifiedMsg.blocks) {
|
|
1116
1115
|
accumulateBlockContent(block, state, toolCallMap);
|
|
@@ -1347,7 +1346,9 @@ export function startUnifiedDisplayLoop(): void {
|
|
|
1347
1346
|
const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(state.status);
|
|
1348
1347
|
const cardContent = truncateContent(state.accumulatedContent + state.finalReply) || " ";
|
|
1349
1348
|
const doneCard = buildProgressCard(cardContent, { showStop: false, headerTitle, headerTemplate });
|
|
1350
|
-
await p.cardUpdate(display.cardId, doneCard, nextSeq).catch(
|
|
1349
|
+
await p.cardUpdate(display.cardId, doneCard, nextSeq).catch(err => {
|
|
1350
|
+
console.error(`[${ts()}] [DISPLAY] terminal cardUpdate failed: ${(err as Error).message}`);
|
|
1351
|
+
});
|
|
1351
1352
|
|
|
1352
1353
|
// 若 session 仍在 activePrompts 中,说明 runAgentSession 的 finally
|
|
1353
1354
|
// 还没执行,当前 stream state 可能是 stopSession fire-and-forget
|
|
@@ -1422,7 +1423,9 @@ export function startUnifiedDisplayLoop(): void {
|
|
|
1422
1423
|
const oldSeqBase = display.sequence;
|
|
1423
1424
|
const oldContent = state.accumulatedContent + state.finalReply;
|
|
1424
1425
|
const oldCard = buildProgressCard(truncateContent(oldContent) || " ", { showStop: false, headerTitle: "生成中(上轮)" });
|
|
1425
|
-
await p.cardUpdate(display.cardId, oldCard, oldSeqBase + 1).catch(
|
|
1426
|
+
await p.cardUpdate(display.cardId, oldCard, oldSeqBase + 1).catch(err => {
|
|
1427
|
+
console.error(`[${ts()}] [DISPLAY] rotation old cardUpdate failed: ${(err as Error).message}`);
|
|
1428
|
+
});
|
|
1426
1429
|
markCardDone(sessionId, display.turnCount, display.cardId).catch(() => {});
|
|
1427
1430
|
const newCardId = await p.cardCreate(buildProgressCard(
|
|
1428
1431
|
"",
|