chatccc 0.2.256 → 0.2.257
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 +2 -2
- package/dist/src/cards.js +3 -3
- package/dist/src/index.js +1 -1
- package/dist/src/orchestrator.js +11 -11
- package/dist/src/session-chat-binding.js +2 -2
- package/dist/src/session.js +1 -1
- package/dist/src/wechat-platform.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,7 +157,7 @@ chatccc
|
|
|
157
157
|
|
|
158
158
|
Agent Team 提供本地任务看板和项目主 Agent 入口,但暂不根据任务自动分配 Agent。每个规范化后的本机目录对应一个独立项目和看板,固定包含“头脑风暴、Todo、Doing、Done、搁置”五列,支持新增、编辑、拖动排序和删除任务。页面会自动保存,不需要手动提交。当前按路径识别项目,不检查 Git 仓库或 worktree 关系,任意子目录都可以作为独立项目。
|
|
159
159
|
|
|
160
|
-
打开项目后可以选择 CCC、Claude、Cursor 或 Codex 作为主 Agent。首次设置会创建固定命名为 `主Agent-<目录短名>` 的飞书群和空 Agent Session;后续切换 Agent 或重新关联目录会复用原群,运行中的主 Agent 不允许切换。建群成员取自机器人最近一次收到的飞书私聊;如果尚无私聊记录,网页会提示先给机器人发送任意私聊消息并自动检测。项目群名不会随第一句话或 `/
|
|
160
|
+
打开项目后可以选择 CCC、Claude、Cursor 或 Codex 作为主 Agent。首次设置会创建固定命名为 `主Agent-<目录短名>` 的飞书群和空 Agent Session;后续切换 Agent 或重新关联目录会复用原群,运行中的主 Agent 不允许切换。建群成员取自机器人最近一次收到的飞书私聊;如果尚无私聊记录,网页会提示先给机器人发送任意私聊消息并自动检测。项目群名不会随第一句话或 `/forget` 改变。
|
|
161
161
|
|
|
162
162
|
看板数据默认保存在 `~/.chatccc/agent-team/`,其中 `workspaces.json` 保存最近工作目录索引,`boards/` 保存按稳定 ID 分隔的看板 JSON,`main-agent-bindings/` 单独保存本机飞书群与 Session 绑定。目录移动或重命名后,可从最近目录列表重新关联。数据访问通过仓储接口隔离,后续接入飞书多维表格时可增加双向同步适配器,无需把本机群聊和 Session 状态混入任务同步模型。选择目录、创建群聊和启动 Agent 等操作都由本机 Node 后端执行;当前实现不依赖 Electron。
|
|
163
163
|
|
|
@@ -396,7 +396,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
396
396
|
| `/new cursor` | 创建 Cursor 会话;飞书中会创建新群 |
|
|
397
397
|
| `/new codex` | 创建 Codex 会话;飞书中会创建新群 |
|
|
398
398
|
| `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
|
|
399
|
-
| `/
|
|
399
|
+
| `/forget` | 在当前聊天原地重置会话(忘掉上下文);群聊保留工作目录,飞书私聊固定使用系统用户目录 |
|
|
400
400
|
| `/model` | 查看或切换当前会话的模型 |
|
|
401
401
|
| `/fast` | 查看当前 Codex 会话的 Fast 模式;使用 `/fast on` 或 `/fast off` 切换 |
|
|
402
402
|
| `/stop` | 停止当前回复 |
|
package/dist/src/cards.js
CHANGED
|
@@ -137,7 +137,7 @@ export function buildHelpCard(userText, opts = {}) {
|
|
|
137
137
|
"发送 **/new cursor** 创建新 Cursor 会话",
|
|
138
138
|
"发送 **/new codex** 创建新 Codex 会话",
|
|
139
139
|
"发送 **/new ccc** 创建新 CCC Agent 会话",
|
|
140
|
-
"发送 **/
|
|
140
|
+
"发送 **/forget** 重置当前会话(忘掉上下文,沿用当前工作目录,不切换)",
|
|
141
141
|
"发送 **/plan** 以规划模式提问(只读,不执行写操作)",
|
|
142
142
|
"发送 **/ask** 以问答模式提问(只读,不执行写操作)",
|
|
143
143
|
"发送 **/usage** 查看当前 Agent 的用量或余额",
|
|
@@ -329,8 +329,8 @@ export function buildSessionsCard(sessions, opts = {}) {
|
|
|
329
329
|
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
|
|
330
330
|
{ tag: "hr" },
|
|
331
331
|
{ tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
|
|
332
|
-
? "当前飞书私聊使用专属会话;默认 Agent 变化后,下一条普通消息会自动创建对应 Agent 的新空会话。发送 **/
|
|
333
|
-
: "在会话群内发送 **/
|
|
332
|
+
? "当前飞书私聊使用专属会话;默认 Agent 变化后,下一条普通消息会自动创建对应 Agent 的新空会话。发送 **/forget** 可在私聊中原地重置(忘掉当前上下文)。群聊会话请回到对应群聊继续,私聊不支持 **/session** 切换。"
|
|
333
|
+
: "在会话群内发送 **/forget** 可重置当前会话(忘掉上下文,创建新 Session,保留工作目录和群聊)。\n发送 **/session 数字**(如 `/session 1`)可将当前群聊切换到列表中对应编号的会话。" } },
|
|
334
334
|
{ tag: "hr" },
|
|
335
335
|
{
|
|
336
336
|
tag: "action",
|
package/dist/src/index.js
CHANGED
|
@@ -136,7 +136,7 @@ function parseCardAction(data) {
|
|
|
136
136
|
}
|
|
137
137
|
if (!cmd)
|
|
138
138
|
return null;
|
|
139
|
-
const CMD_MAP = { stop: "/stop", cancel: "/cancel", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", update: "/update", state: "/state", cd: "/cd", sessions: "/sessions",
|
|
139
|
+
const CMD_MAP = { stop: "/stop", cancel: "/cancel", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", update: "/update", state: "/state", cd: "/cd", sessions: "/sessions", forget: "/forget" };
|
|
140
140
|
let text = CMD_MAP[cmd] ?? "";
|
|
141
141
|
if (cmd === "cd" && typeof action.value === "object" && action.value !== null) {
|
|
142
142
|
const path = action.value.path;
|
package/dist/src/orchestrator.js
CHANGED
|
@@ -1021,7 +1021,7 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1021
1021
|
// /new 始终创建独立群聊。飞书私聊自己的常驻 session 不在这里切换。
|
|
1022
1022
|
if (chatType === "p2p" && platform.kind === "wechat") {
|
|
1023
1023
|
// 先解绑旧 session(如果存在),避免旧 session 的 display loop
|
|
1024
|
-
// 继续往同一个 chat 推送内容(/
|
|
1024
|
+
// 继续往同一个 chat 推送内容(/forget 走 switchChatBinding 已有此逻辑,
|
|
1025
1025
|
// 但 /new p2p 之前遗漏了解绑)。
|
|
1026
1026
|
const oldRegistry = await loadSessionRegistryForBinding();
|
|
1027
1027
|
const oldRecord = oldRegistry[chatId];
|
|
@@ -1056,7 +1056,7 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1056
1056
|
`直接在这里发消息即可与 ${toolLabel} 对话。\n\n` +
|
|
1057
1057
|
`发送 **/cd** 切换新建会话的默认目录。\n` +
|
|
1058
1058
|
`发送 **/model** 查看或切换当前会话的模型。${fastHelpAfterModel(tool)}\n` +
|
|
1059
|
-
`发送 **/new** 创建新会话,**/
|
|
1059
|
+
`发送 **/new** 创建新会话,**/forget** 重置当前会话(沿用工作目录)。\n` +
|
|
1060
1060
|
`发送 **/sessions** 查看所有会话状态。\n` +
|
|
1061
1061
|
`发送 \`/git <子命令>\` 在本会话工作目录执行 git,例如 \`/git status\`、\`/git log --oneline -n 5\`。` +
|
|
1062
1062
|
usageHelpLine(tool), "green");
|
|
@@ -1117,7 +1117,7 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1117
1117
|
`直接在这里发消息即可与 ${toolLabel} 对话。\n\n` +
|
|
1118
1118
|
`发送 **/cd** 切换新建会话的默认目录。\n` +
|
|
1119
1119
|
`发送 **/model** 查看或切换当前会话的模型。${fastHelpAfterModel(tool)}\n` +
|
|
1120
|
-
`发送 **/new** 创建新会话,**/
|
|
1120
|
+
`发送 **/new** 创建新会话,**/forget** 重置当前会话(沿用工作目录)。\n` +
|
|
1121
1121
|
`发送 **/sessions** 查看所有会话状态。\n` +
|
|
1122
1122
|
`发送 \`/git <子命令>\` 在本会话工作目录执行 git,例如 \`/git status\`、\`/git log --oneline -n 5\`。` +
|
|
1123
1123
|
usageHelpLine(tool), "green");
|
|
@@ -1413,12 +1413,12 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1413
1413
|
logTrace(tid, "DONE", { outcome: "sessions", ok, count: cardData.length });
|
|
1414
1414
|
return;
|
|
1415
1415
|
}
|
|
1416
|
-
if (isCommandText && textLower === "/
|
|
1417
|
-
logTrace(tid, "BRANCH", { cmd: "/
|
|
1416
|
+
if (isCommandText && textLower === "/forget") {
|
|
1417
|
+
logTrace(tid, "BRANCH", { cmd: "/forget" });
|
|
1418
1418
|
let cwd;
|
|
1419
1419
|
if (isFeishuP2p(platform, chatType)) {
|
|
1420
1420
|
// 飞书私聊不支持切换工作目录。即使 /cd 已为后续 /new 群聊
|
|
1421
|
-
// 保存了其他默认目录,/
|
|
1421
|
+
// 保存了其他默认目录,/forget 仍必须在运行 ChatCCC 的用户目录重建。
|
|
1422
1422
|
cwd = homedir();
|
|
1423
1423
|
}
|
|
1424
1424
|
else {
|
|
@@ -1440,7 +1440,7 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1440
1440
|
}
|
|
1441
1441
|
catch (err) {
|
|
1442
1442
|
logTrace(tid, "DONE", {
|
|
1443
|
-
outcome: "
|
|
1443
|
+
outcome: "forget_session_fail",
|
|
1444
1444
|
error: err.message,
|
|
1445
1445
|
});
|
|
1446
1446
|
await platform.sendCard(chatId, "Error", `Failed to create new session:\n${err.message}`, "red");
|
|
@@ -1463,14 +1463,14 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1463
1463
|
});
|
|
1464
1464
|
if (!switchResult.ok) {
|
|
1465
1465
|
logTrace(tid, "DONE", {
|
|
1466
|
-
outcome: "
|
|
1466
|
+
outcome: "forget_update_chat_fail",
|
|
1467
1467
|
error: switchResult.error?.message,
|
|
1468
1468
|
});
|
|
1469
1469
|
await platform.sendCard(chatId, "Error", `更新群描述失败,会话未切换(新 session 已创建但未启用):\n${switchResult.error?.message}`, "red");
|
|
1470
1470
|
return;
|
|
1471
1471
|
}
|
|
1472
1472
|
if (chatType !== "p2p") {
|
|
1473
|
-
console.log(`[${ts()}] [
|
|
1473
|
+
console.log(`[${ts()}] [FORGET] Group updated: name="${newName}" desc="${descPrefix} ${newSessionId}"`);
|
|
1474
1474
|
}
|
|
1475
1475
|
setChatAvatarForSession(platform, chatId, descriptionTool, "new", newSessionId).catch(() => { });
|
|
1476
1476
|
await platform.sendCard(chatId, `${toolLabel} Session Reset`, `会话已重置为新的 **${toolLabel}** 会话。\n\n` +
|
|
@@ -1479,8 +1479,8 @@ export async function handleCommand(platform, text, chatId, openId, msgTimestamp
|
|
|
1479
1479
|
`直接在这里发消息即可继续对话。\n` +
|
|
1480
1480
|
`发送 **/cd** 可切换新建会话的默认目录。\n` +
|
|
1481
1481
|
`发送 **/model** 查看或切换当前会话的模型。${fastHelpAfterModel(descriptionTool)}`, "green");
|
|
1482
|
-
console.log(`[${ts()}] [
|
|
1483
|
-
logTrace(tid, "DONE", { outcome: "
|
|
1482
|
+
console.log(`[${ts()}] [FORGET] Session ${sessionId} → ${newSessionId} (same cwd=${cwd})`);
|
|
1483
|
+
logTrace(tid, "DONE", { outcome: "forget", newSessionId, cwd });
|
|
1484
1484
|
return;
|
|
1485
1485
|
}
|
|
1486
1486
|
if (isCommandText && textLower === "/deleteg") {
|
|
@@ -30,7 +30,7 @@ export function unbindChatFromSession(sessionId, chatId) {
|
|
|
30
30
|
sessionChatsMap.delete(sessionId);
|
|
31
31
|
}
|
|
32
32
|
// 双保险:lastActiveChatMap 是 sessionId → 最后活跃 chatId 的快照,
|
|
33
|
-
// 若被解绑的 chatId 正好是该 session 的 lastActive(典型场景:/
|
|
33
|
+
// 若被解绑的 chatId 正好是该 session 的 lastActive(典型场景:/forget
|
|
34
34
|
// 把当前群从旧 session 改嫁到新 session),不清理会让旧 session 的
|
|
35
35
|
// display loop 继续向已离开的群推送卡片。
|
|
36
36
|
if (lastActiveChatMap.get(sessionId)?.chatId === chatId) {
|
|
@@ -99,7 +99,7 @@ export function getLastActiveChat(sessionId) {
|
|
|
99
99
|
* display loop 决定推送目标 chat 的纯函数:
|
|
100
100
|
* 仅当 lastActiveChat 仍然绑定到该 session 时才返回,否则返回 undefined。
|
|
101
101
|
*
|
|
102
|
-
* 修复 bug:用户 `/
|
|
102
|
+
* 修复 bug:用户 `/forget` 把当前群从旧 session 改嫁到新 session 后,旧
|
|
103
103
|
* session 的 lastActiveChatMap 残留旧 chatId 快照,display loop 会继续
|
|
104
104
|
* 在该群创建/更新卡片。这里通过交叉验证 sessionChatsMap 把这种悬挂记录
|
|
105
105
|
* 排除掉——loop 拿到 undefined 就走"无活跃群"分支自然停推。
|
package/dist/src/session.js
CHANGED
|
@@ -1622,7 +1622,7 @@ export function startUnifiedDisplayLoop() {
|
|
|
1622
1622
|
continue;
|
|
1623
1623
|
}
|
|
1624
1624
|
// 交叉验证:chat 当前绑定的 session 是否仍是 display 记录的 session。
|
|
1625
|
-
// 若 chat 已被切换到其他 session(如 /
|
|
1625
|
+
// 若 chat 已被切换到其他 session(如 /forget),旧 display 必须停推。
|
|
1626
1626
|
const currentSessionForChat = sessionInfoMap.get(chatId)?.sessionId;
|
|
1627
1627
|
if (currentSessionForChat && currentSessionForChat !== sessionId) {
|
|
1628
1628
|
if (state.status !== "running") {
|
|
@@ -537,7 +537,7 @@ async function handleWechatMessage(message, handler) {
|
|
|
537
537
|
appendChatLog(chatId, chatId, fullText);
|
|
538
538
|
// 用户回复,重置 claw 连发计数
|
|
539
539
|
consecutiveSendCount.set(chatId, 0);
|
|
540
|
-
// WeChat 中所有会话都视为 p2p,/new 复用 p2p 路径(等同飞书 /
|
|
540
|
+
// WeChat 中所有会话都视为 p2p,/new 复用 p2p 路径(等同飞书 /forget 效果)
|
|
541
541
|
// 不 await:避免长 prompt 阻塞后续消息处理(如 /cd、/stop 等命令)
|
|
542
542
|
handler(fullText, chatId, chatId, msgTimestamp, "p2p").catch((err) => {
|
|
543
543
|
platformLog(`消息处理失败: ${err.stack ?? err.message}`);
|