cc-claw 0.20.19 → 0.20.21
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/dist/cli.js +78 -17
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.20.
|
|
36
|
+
VERSION = true ? "0.20.21" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -13694,6 +13694,12 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13694
13694
|
if (contentSilenceTimer) clearTimeout(contentSilenceTimer);
|
|
13695
13695
|
contentSilenceTimer = setTimeout(() => {
|
|
13696
13696
|
if (cancelState.cancelled || timedOut) return;
|
|
13697
|
+
if (pendingTools.size > 0) {
|
|
13698
|
+
const tools2 = Array.from(pendingTools.values()).map((t) => typeof t === "string" ? t : t.name).join(", ");
|
|
13699
|
+
log(`[agent] Content silence timer fired but ${pendingTools.size} tool(s) still running (${tools2}) \u2014 resetting`);
|
|
13700
|
+
resetContentSilenceTimer();
|
|
13701
|
+
return;
|
|
13702
|
+
}
|
|
13697
13703
|
warn(`[agent] Content silence timeout after ${silenceTimeoutMs / 1e3}s for ${adapter.id} \u2014 no content events, killing`);
|
|
13698
13704
|
timedOut = true;
|
|
13699
13705
|
timeoutState.contentSilence = true;
|
|
@@ -18537,7 +18543,8 @@ async function handleMedia(msg, channel) {
|
|
|
18537
18543
|
await channel.sendText(chatId, mLimitMsg, { parseMode: "plain" });
|
|
18538
18544
|
return;
|
|
18539
18545
|
}
|
|
18540
|
-
|
|
18546
|
+
const fileCount = 1 + (msg.extraFiles?.length ?? 0);
|
|
18547
|
+
await channel.sendText(chatId, fileCount > 1 ? `Processing ${fileCount} files...` : "Processing your file...", { parseMode: "plain" });
|
|
18541
18548
|
try {
|
|
18542
18549
|
if (msg.type === "video") {
|
|
18543
18550
|
const fileId = msg.metadata?.fileId ?? fileName;
|
|
@@ -18620,13 +18627,39 @@ Acknowledge receipt. Do NOT analyze the video unless they ask you to.`;
|
|
|
18620
18627
|
if (msg.type === "photo" || isImageExt(ext)) {
|
|
18621
18628
|
const imgExt = msg.type === "photo" ? "jpg" : ext || "jpg";
|
|
18622
18629
|
tempFilePath = await saveMedia(fileBuffer, "img", imgExt);
|
|
18623
|
-
|
|
18630
|
+
const allPaths = [tempFilePath];
|
|
18631
|
+
if (msg.extraFiles?.length) {
|
|
18632
|
+
for (const extra of msg.extraFiles) {
|
|
18633
|
+
try {
|
|
18634
|
+
const extraBuffer = await channel.downloadFile(extra.fileName);
|
|
18635
|
+
const extraExt = extra.mimeType.startsWith("image/") ? extra.mimeType.split("/")[1] || "jpg" : "jpg";
|
|
18636
|
+
const extraPath = await saveMedia(extraBuffer, "img", extraExt);
|
|
18637
|
+
allPaths.push(extraPath);
|
|
18638
|
+
} catch (err) {
|
|
18639
|
+
log(`[media] Failed to download extra file: ${errorMessage(err)}`);
|
|
18640
|
+
}
|
|
18641
|
+
}
|
|
18642
|
+
}
|
|
18643
|
+
if (allPaths.length === 1) {
|
|
18644
|
+
prompt = caption ? `The user sent an image with caption: "${caption}"
|
|
18624
18645
|
|
|
18625
18646
|
The image has been saved to: ${tempFilePath}
|
|
18626
18647
|
|
|
18627
18648
|
Please read the image file and respond to their caption/question.` : `The user sent an image. It has been saved to: ${tempFilePath}
|
|
18628
18649
|
|
|
18629
18650
|
Please read the image file and describe what you see.`;
|
|
18651
|
+
} else {
|
|
18652
|
+
const pathList = allPaths.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
18653
|
+
prompt = caption ? `The user sent ${allPaths.length} images with caption: "${caption}"
|
|
18654
|
+
|
|
18655
|
+
The images have been saved to:
|
|
18656
|
+
${pathList}
|
|
18657
|
+
|
|
18658
|
+
Please read all image files and respond to their caption/question.` : `The user sent ${allPaths.length} images. They have been saved to:
|
|
18659
|
+
${pathList}
|
|
18660
|
+
|
|
18661
|
+
Please read all image files and describe what you see.`;
|
|
18662
|
+
}
|
|
18630
18663
|
} else if (isTextExt(ext)) {
|
|
18631
18664
|
const MAX_TEXT_FILE_BYTES = 1048576;
|
|
18632
18665
|
if (fileBuffer.length > MAX_TEXT_FILE_BYTES) {
|
|
@@ -28009,6 +28042,11 @@ var init_telegram2 = __esm({
|
|
|
28009
28042
|
// messageId → chatId
|
|
28010
28043
|
reactionHandlers = [];
|
|
28011
28044
|
throttle;
|
|
28045
|
+
// ── Media group debouncing ─────────────────────────────────────────
|
|
28046
|
+
// Telegram sends each photo in a media group as a separate update.
|
|
28047
|
+
// We buffer them by media_group_id and emit one merged IncomingMessage.
|
|
28048
|
+
mediaGroupBuffer = /* @__PURE__ */ new Map();
|
|
28049
|
+
static MEDIA_GROUP_DEBOUNCE_MS = 500;
|
|
28012
28050
|
// ── Polling health tracking ─────────────────────────────────────────
|
|
28013
28051
|
/** Timestamp of last update received from Telegram (message, callback, reaction) */
|
|
28014
28052
|
lastUpdateAt = 0;
|
|
@@ -28038,20 +28076,6 @@ var init_telegram2 = __esm({
|
|
|
28038
28076
|
this.allowedChatIds = new Set(ids);
|
|
28039
28077
|
this.bot = new Bot(token);
|
|
28040
28078
|
this.throttle = new TelegramThrottle();
|
|
28041
|
-
this.throttle.setResumeNotifier(async (chatId, pausedSec, queuedCount) => {
|
|
28042
|
-
if (pausedSec > 60) {
|
|
28043
|
-
log(`[telegram] Skipping resume notification (paused ${pausedSec}s \u2014 too long, would risk another 429)`);
|
|
28044
|
-
return;
|
|
28045
|
-
}
|
|
28046
|
-
try {
|
|
28047
|
-
await this.bot.api.sendMessage(
|
|
28048
|
-
numericChatId(chatId),
|
|
28049
|
-
`\u26A0\uFE0F Rate-limited by Telegram for ${pausedSec}s \u2014 delivering ${queuedCount} pending message(s) now.`
|
|
28050
|
-
);
|
|
28051
|
-
} catch (err) {
|
|
28052
|
-
warn("[telegram] Resume notification failed:", err instanceof Error ? err.message : err);
|
|
28053
|
-
}
|
|
28054
|
-
});
|
|
28055
28079
|
}
|
|
28056
28080
|
/** The first ID in ALLOWED_CHAT_ID — used for heartbeat, notifications, etc. */
|
|
28057
28081
|
getPrimaryChatId() {
|
|
@@ -28171,6 +28195,19 @@ var init_telegram2 = __esm({
|
|
|
28171
28195
|
return;
|
|
28172
28196
|
}
|
|
28173
28197
|
log(`[telegram] Processing ${msg.type}: "${msg.text?.slice(0, 50) || msg.command || "(media)"}"`);
|
|
28198
|
+
const mediaGroupId = ctx.message?.media_group_id;
|
|
28199
|
+
if (mediaGroupId && (msg.type === "photo" || msg.type === "video" || msg.type === "document")) {
|
|
28200
|
+
const existing = this.mediaGroupBuffer.get(mediaGroupId);
|
|
28201
|
+
if (existing) {
|
|
28202
|
+
existing.messages.push(msg);
|
|
28203
|
+
clearTimeout(existing.timer);
|
|
28204
|
+
existing.timer = setTimeout(() => this.flushMediaGroup(mediaGroupId), _TelegramChannel.MEDIA_GROUP_DEBOUNCE_MS);
|
|
28205
|
+
} else {
|
|
28206
|
+
const timer = setTimeout(() => this.flushMediaGroup(mediaGroupId), _TelegramChannel.MEDIA_GROUP_DEBOUNCE_MS);
|
|
28207
|
+
this.mediaGroupBuffer.set(mediaGroupId, { messages: [msg], timer, handler, channel: this });
|
|
28208
|
+
}
|
|
28209
|
+
return;
|
|
28210
|
+
}
|
|
28174
28211
|
if (isFastPathMessage(msg)) {
|
|
28175
28212
|
handler(msg, this).catch((err) => {
|
|
28176
28213
|
error("[telegram] Fast-path handler error:", err);
|
|
@@ -28619,6 +28656,30 @@ var init_telegram2 = __esm({
|
|
|
28619
28656
|
}
|
|
28620
28657
|
this.agentMessageIds.set(messageId, chatId);
|
|
28621
28658
|
}
|
|
28659
|
+
/**
|
|
28660
|
+
* Flush a buffered media group — merge all files into a single IncomingMessage
|
|
28661
|
+
* with the first file as primary and the rest in extraFiles.
|
|
28662
|
+
*/
|
|
28663
|
+
flushMediaGroup(groupId) {
|
|
28664
|
+
const group = this.mediaGroupBuffer.get(groupId);
|
|
28665
|
+
if (!group) return;
|
|
28666
|
+
this.mediaGroupBuffer.delete(groupId);
|
|
28667
|
+
const [primary, ...rest] = group.messages;
|
|
28668
|
+
if (rest.length > 0) {
|
|
28669
|
+
primary.extraFiles = rest.map((m) => ({
|
|
28670
|
+
fileName: m.fileName,
|
|
28671
|
+
mimeType: m.mimeType ?? "application/octet-stream"
|
|
28672
|
+
}));
|
|
28673
|
+
if (!primary.caption) {
|
|
28674
|
+
const withCaption = rest.find((m) => m.caption);
|
|
28675
|
+
if (withCaption) primary.caption = withCaption.caption;
|
|
28676
|
+
}
|
|
28677
|
+
log(`[telegram] Media group ${groupId}: merged ${group.messages.length} files into one message`);
|
|
28678
|
+
}
|
|
28679
|
+
group.handler(primary, group.channel).catch((err) => {
|
|
28680
|
+
error("[telegram] Media group handler error:", err);
|
|
28681
|
+
});
|
|
28682
|
+
}
|
|
28622
28683
|
buildIncomingMessage(ctx) {
|
|
28623
28684
|
const chatId = ctx.chat.id.toString();
|
|
28624
28685
|
const messageId = ctx.message?.message_id?.toString() ?? "";
|
package/package.json
CHANGED