-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathprogress.js
More file actions
83 lines (68 loc) · 2.81 KB
/
progress.js
File metadata and controls
83 lines (68 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// 微信进度显示模块
// 微信不支持 editMessage,只能发"处理中"+ typing 心跳
import { sendText, sendTyping, cancelTyping } from "./weixin/send.js";
const TOOL_ICONS = {
Read: "📖", Write: "✍️", Edit: "✏️", Bash: "💻",
Glob: "🔍", Grep: "🔎", WebFetch: "🌐", WebSearch: "🔍",
Agent: "🤖", NotebookEdit: "📓",
};
const SILENT_TOOLS = new Set([
"TodoWrite", "TaskCreate", "TaskUpdate", "TaskList", "TaskGet",
]);
const TYPING_INTERVAL_MS = 8000; // typing 心跳间隔
export function createProgressTracker(token, toUserId, contextToken, verboseLevel = 1, backendLabel = "CC") {
let typingInterval = null;
let typingTicket = "";
let entries = [];
let finished = false;
async function start() {
// 微信不能编辑/删除消息,不发文字进度提示(会永久留在聊天里)
// 只用 typing 心跳(会自动消失)
// Typing 心跳
typingInterval = setInterval(() => {
sendTyping(token, toUserId, contextToken, typingTicket).catch(() => {});
}, TYPING_INTERVAL_MS);
// 立刻发一次
sendTyping(token, toUserId, contextToken, typingTicket).catch(() => {});
}
function processEvent(event) {
if (finished || verboseLevel === 0) return;
if (event.type === "progress") {
const toolName = event.toolName || "action";
if (SILENT_TOOLS.has(toolName)) return;
const icon = TOOL_ICONS[toolName] || "🔧";
if (verboseLevel >= 2 && event.input) {
const inp = typeof event.input === "object"
? (event.input.command || event.input.file_path || event.input.description || event.input.pattern || event.input.query || "").slice(0, 60)
: (event.detail || "").slice(0, 60);
entries.push(`${icon} ${toolName}${inp ? ": " + inp : ""}`);
} else {
entries.push(`${icon} ${toolName}`);
}
}
// 微信不能编辑消息,工具进度只记录不实时更新
}
async function finish({ durationMs = 0 } = {}) {
finished = true;
if (typingInterval) {
clearInterval(typingInterval);
typingInterval = null;
}
// 取消 typing 状态
cancelTyping(token, toUserId, contextToken, typingTicket).catch(() => {});
}
function getSummary(durationMs = 0) {
if (entries.length === 0) return "";
const toolCounts = {};
for (const entry of entries) {
const match = entry.match(/^\S+\s+(\w+)/);
if (match && match[1]) toolCounts[match[1]] = (toolCounts[match[1]] || 0) + 1;
}
const toolSummary = Object.entries(toolCounts)
.map(([name, count]) => `${name}${count > 1 ? ` x${count}` : ""}`)
.join(", ");
const durLabel = durationMs > 0 ? ` ${Math.round(durationMs / 1000)}s` : "";
return `✅ Done${durLabel} — ${toolSummary || "no tools"}`;
}
return { start, processEvent, finish, getSummary };
}