cc-claw 0.18.3 → 0.18.4
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 +160 -42
- 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.18.
|
|
36
|
+
VERSION = true ? "0.18.4" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -11154,6 +11154,7 @@ var init_detect = __esm({
|
|
|
11154
11154
|
// src/agent.ts
|
|
11155
11155
|
var agent_exports = {};
|
|
11156
11156
|
__export(agent_exports, {
|
|
11157
|
+
CONTENT_SILENCE_TIMEOUT_ERROR: () => CONTENT_SILENCE_TIMEOUT_ERROR,
|
|
11157
11158
|
FIRST_RESPONSE_TIMEOUT_ERROR: () => FIRST_RESPONSE_TIMEOUT_ERROR,
|
|
11158
11159
|
FREE_SLOTS_EXHAUSTED: () => FREE_SLOTS_EXHAUSTED,
|
|
11159
11160
|
askAgent: () => askAgent,
|
|
@@ -11277,6 +11278,20 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11277
11278
|
}
|
|
11278
11279
|
}, frTimeoutMs);
|
|
11279
11280
|
}
|
|
11281
|
+
let contentSilenceTimer;
|
|
11282
|
+
const silenceTimeoutMs = CONTENT_SILENCE_TIMEOUT_MS;
|
|
11283
|
+
function resetContentSilenceTimer() {
|
|
11284
|
+
if (silenceTimeoutMs <= 0) return;
|
|
11285
|
+
if (contentSilenceTimer) clearTimeout(contentSilenceTimer);
|
|
11286
|
+
contentSilenceTimer = setTimeout(() => {
|
|
11287
|
+
if (cancelState.cancelled || timedOut) return;
|
|
11288
|
+
warn(`[agent] Content silence timeout after ${silenceTimeoutMs / 1e3}s for ${adapter.id} \u2014 no content events, killing`);
|
|
11289
|
+
timedOut = true;
|
|
11290
|
+
cancelState.__contentSilenceTimeout = true;
|
|
11291
|
+
killProcessGroup(proc, "SIGTERM");
|
|
11292
|
+
sigkillTimer = setTimeout(() => killProcessGroup(proc, "SIGKILL"), 3e3);
|
|
11293
|
+
}, silenceTimeoutMs);
|
|
11294
|
+
}
|
|
11280
11295
|
rl2.on("line", (line) => {
|
|
11281
11296
|
if (!line.trim()) return;
|
|
11282
11297
|
if (firstLine) {
|
|
@@ -11299,6 +11314,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11299
11314
|
case "init":
|
|
11300
11315
|
log(`[agent] Session init at ${elapsed()}`);
|
|
11301
11316
|
if (ev.sessionId) sessionId = ev.sessionId;
|
|
11317
|
+
resetContentSilenceTimer();
|
|
11302
11318
|
break;
|
|
11303
11319
|
case "text":
|
|
11304
11320
|
if (!gotModelContent) {
|
|
@@ -11308,6 +11324,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11308
11324
|
firstResponseTimer = void 0;
|
|
11309
11325
|
}
|
|
11310
11326
|
}
|
|
11327
|
+
resetContentSilenceTimer();
|
|
11311
11328
|
if (ev.text) {
|
|
11312
11329
|
accumulatedText = appendTextChunk(accumulatedText, ev.text);
|
|
11313
11330
|
if (opts?.onStream) opts.onStream(ev.text);
|
|
@@ -11321,6 +11338,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11321
11338
|
firstResponseTimer = void 0;
|
|
11322
11339
|
}
|
|
11323
11340
|
}
|
|
11341
|
+
resetContentSilenceTimer();
|
|
11324
11342
|
if (ev.text) {
|
|
11325
11343
|
accumulatedThinking = appendTextChunk(accumulatedThinking, ev.text);
|
|
11326
11344
|
if (opts?.onThinking) opts.onThinking(ev.text);
|
|
@@ -11334,6 +11352,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11334
11352
|
firstResponseTimer = void 0;
|
|
11335
11353
|
}
|
|
11336
11354
|
}
|
|
11355
|
+
resetContentSilenceTimer();
|
|
11337
11356
|
sawToolEvents = true;
|
|
11338
11357
|
if (opts?.onToolAction && ev.toolName) {
|
|
11339
11358
|
const toolInput = ev.toolInput ?? {};
|
|
@@ -11360,6 +11379,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11360
11379
|
}
|
|
11361
11380
|
break;
|
|
11362
11381
|
case "tool_end":
|
|
11382
|
+
resetContentSilenceTimer();
|
|
11363
11383
|
if (opts?.onToolAction) {
|
|
11364
11384
|
const pending = ev.toolId ? pendingTools.get(ev.toolId) : void 0;
|
|
11365
11385
|
if (pending) {
|
|
@@ -11413,6 +11433,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11413
11433
|
proc.on("error", (err) => {
|
|
11414
11434
|
clearTimeout(spawnTimeout);
|
|
11415
11435
|
if (firstResponseTimer) clearTimeout(firstResponseTimer);
|
|
11436
|
+
if (contentSilenceTimer) clearTimeout(contentSilenceTimer);
|
|
11416
11437
|
if (sigkillTimer) clearTimeout(sigkillTimer);
|
|
11417
11438
|
rl2.close();
|
|
11418
11439
|
cancelState.process = void 0;
|
|
@@ -11424,6 +11445,10 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11424
11445
|
clearTimeout(firstResponseTimer);
|
|
11425
11446
|
firstResponseTimer = void 0;
|
|
11426
11447
|
}
|
|
11448
|
+
if (contentSilenceTimer) {
|
|
11449
|
+
clearTimeout(contentSilenceTimer);
|
|
11450
|
+
contentSilenceTimer = void 0;
|
|
11451
|
+
}
|
|
11427
11452
|
if (sigkillTimer) {
|
|
11428
11453
|
clearTimeout(sigkillTimer);
|
|
11429
11454
|
sigkillTimer = void 0;
|
|
@@ -11444,6 +11469,11 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
11444
11469
|
reject(new Error(`${FIRST_RESPONSE_TIMEOUT_ERROR}: No response from ${adapter.id} within ${frTimeoutMs / 1e3}s${stderr ? ` \u2014 ${stderr.slice(-300)}` : ""}`));
|
|
11445
11470
|
return;
|
|
11446
11471
|
}
|
|
11472
|
+
if (cancelState.__contentSilenceTimeout) {
|
|
11473
|
+
delete cancelState.__contentSilenceTimeout;
|
|
11474
|
+
reject(new Error(`${CONTENT_SILENCE_TIMEOUT_ERROR}: ${adapter.id} produced no content events for ${silenceTimeoutMs / 1e3}s after init`));
|
|
11475
|
+
return;
|
|
11476
|
+
}
|
|
11447
11477
|
let msg = `Spawn timeout after ${effectiveTimeout / 1e3}s`;
|
|
11448
11478
|
if (pendingTools.size > 0) {
|
|
11449
11479
|
const tools2 = Array.from(pendingTools.values()).map((t) => typeof t === "string" ? t : t.name).join(", ");
|
|
@@ -11796,6 +11826,26 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
11796
11826
|
})();
|
|
11797
11827
|
result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, retryOpts);
|
|
11798
11828
|
}
|
|
11829
|
+
} else if (errMsg.startsWith(CONTENT_SILENCE_TIMEOUT_ERROR) && existingSessionId) {
|
|
11830
|
+
warn(`[agent] Content silence on ${adapter.id} \u2014 clearing session ${existingSessionId} and retrying fresh`);
|
|
11831
|
+
clearSession(chatId);
|
|
11832
|
+
if (useGeminiRotation) {
|
|
11833
|
+
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11834
|
+
const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
|
|
11835
|
+
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
|
|
11836
|
+
} else if (useBackendRotation) {
|
|
11837
|
+
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11838
|
+
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
|
|
11839
|
+
} else {
|
|
11840
|
+
const retryOpts = (() => {
|
|
11841
|
+
if (adapter.id !== "gemini") return spawnOpts;
|
|
11842
|
+
const geminiAdapter = adapter;
|
|
11843
|
+
const { env, slot } = geminiAdapter.resolveSlotEnv(chatId);
|
|
11844
|
+
if (slot) return { ...spawnOpts, envOverride: env };
|
|
11845
|
+
return spawnOpts;
|
|
11846
|
+
})();
|
|
11847
|
+
result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, retryOpts);
|
|
11848
|
+
}
|
|
11799
11849
|
} else {
|
|
11800
11850
|
if (!isSyntheticChatId(chatId)) {
|
|
11801
11851
|
try {
|
|
@@ -11900,7 +11950,7 @@ function injectMcpConfig(adapterId, args, mcpConfigPath) {
|
|
|
11900
11950
|
if (!flag) return args;
|
|
11901
11951
|
return [...args, ...flag, mcpConfigPath];
|
|
11902
11952
|
}
|
|
11903
|
-
var activeChats, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
|
|
11953
|
+
var activeChats, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_ERROR, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
|
|
11904
11954
|
var init_agent = __esm({
|
|
11905
11955
|
"src/agent.ts"() {
|
|
11906
11956
|
"use strict";
|
|
@@ -11925,6 +11975,8 @@ var init_agent = __esm({
|
|
|
11925
11975
|
chatLocks = /* @__PURE__ */ new Map();
|
|
11926
11976
|
SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
11927
11977
|
FIRST_RESPONSE_TIMEOUT_MS = parseInt(process.env.GEMINI_FIRST_RESPONSE_TIMEOUT_MS ?? "30000", 10);
|
|
11978
|
+
CONTENT_SILENCE_TIMEOUT_MS = parseInt(process.env.CONTENT_SILENCE_TIMEOUT_MS ?? "90000", 10);
|
|
11979
|
+
CONTENT_SILENCE_TIMEOUT_ERROR = "CONTENT_SILENCE_TIMEOUT";
|
|
11928
11980
|
FIRST_RESPONSE_TIMEOUT_ERROR = "FIRST_RESPONSE_TIMEOUT";
|
|
11929
11981
|
FREE_SLOTS_EXHAUSTED = "FREE_SLOTS_EXHAUSTED";
|
|
11930
11982
|
GEMINI_FALLBACK_CHAIN = {
|
|
@@ -19820,7 +19872,14 @@ var init_commands = __esm({
|
|
|
19820
19872
|
|
|
19821
19873
|
// src/router/callbacks.ts
|
|
19822
19874
|
import { readFile as readFile7 } from "fs/promises";
|
|
19823
|
-
async function handleCallback(chatId, data, channel) {
|
|
19875
|
+
async function handleCallback(chatId, data, channel, messageId) {
|
|
19876
|
+
const replaceWithText = async (text) => {
|
|
19877
|
+
if (messageId && channel.editText) {
|
|
19878
|
+
await channel.editText(chatId, messageId, text, "plain");
|
|
19879
|
+
} else {
|
|
19880
|
+
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
19881
|
+
}
|
|
19882
|
+
};
|
|
19824
19883
|
if (data.startsWith("menu:")) {
|
|
19825
19884
|
const action = data.slice(5);
|
|
19826
19885
|
const synth = { chatId, messageId: "", text: "", senderName: "User", type: "command", source: "telegram", command: "", commandArgs: "" };
|
|
@@ -20056,7 +20115,7 @@ ${PERM_MODES[chosen]}`,
|
|
|
20056
20115
|
return;
|
|
20057
20116
|
}
|
|
20058
20117
|
removePendingPlan(chatId);
|
|
20059
|
-
await
|
|
20118
|
+
await replaceWithText("\u2705 Approved. Executing...");
|
|
20060
20119
|
bypassBusyCheck.add(chatId);
|
|
20061
20120
|
const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router(), router_exports));
|
|
20062
20121
|
const overrideMsg = `[SYSTEM: Planning mode disabled. Execution APPROVED. Please execute the plan.]
|
|
@@ -20549,14 +20608,14 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
20549
20608
|
}
|
|
20550
20609
|
} else if (action === "discard") {
|
|
20551
20610
|
pendingInterrupts.delete(targetChatId);
|
|
20552
|
-
await
|
|
20611
|
+
await replaceWithText("\u{1F5D1} Message discarded.");
|
|
20553
20612
|
} else {
|
|
20554
20613
|
await channel.sendText(chatId, "Message already processed or expired.", { parseMode: "plain" });
|
|
20555
20614
|
}
|
|
20556
20615
|
} else if (data.startsWith("sq:cancel:")) {
|
|
20557
20616
|
const sqId = data.slice("sq:cancel:".length);
|
|
20558
20617
|
stopAgent(sqId);
|
|
20559
|
-
await
|
|
20618
|
+
await replaceWithText("\u{1F5FA} Side quest cancelled.");
|
|
20560
20619
|
} else if (data.startsWith("fallback:")) {
|
|
20561
20620
|
const parts = data.split(":");
|
|
20562
20621
|
const targetBackend = parts[1];
|
|
@@ -21183,13 +21242,14 @@ function makeLiveStatus(chatId, channel, modelLabel, verboseLevel, showThinking)
|
|
|
21183
21242
|
};
|
|
21184
21243
|
return { liveStatus, toolCb };
|
|
21185
21244
|
}
|
|
21186
|
-
var
|
|
21245
|
+
var FLUSH_INTERVAL_DM_MS, FLUSH_INTERVAL_GROUP_MS, MAX_THINKING_CHARS, TRIM_THRESHOLD, MAX_ENTRIES, LiveStatusMessage;
|
|
21187
21246
|
var init_live_status = __esm({
|
|
21188
21247
|
"src/router/live-status.ts"() {
|
|
21189
21248
|
"use strict";
|
|
21190
21249
|
init_log();
|
|
21191
21250
|
init_helpers();
|
|
21192
|
-
|
|
21251
|
+
FLUSH_INTERVAL_DM_MS = 1e3;
|
|
21252
|
+
FLUSH_INTERVAL_GROUP_MS = 3e3;
|
|
21193
21253
|
MAX_THINKING_CHARS = 800;
|
|
21194
21254
|
TRIM_THRESHOLD = 3500;
|
|
21195
21255
|
MAX_ENTRIES = 200;
|
|
@@ -21211,6 +21271,11 @@ var init_live_status = __esm({
|
|
|
21211
21271
|
nextFlushAllowedAt = 0;
|
|
21212
21272
|
/** Tracks whether entries have been trimmed at least once (for display hint). */
|
|
21213
21273
|
hasTrimmed = false;
|
|
21274
|
+
/** Resolve flush interval based on chat type (group chats are rate-limited more aggressively). */
|
|
21275
|
+
get flushIntervalMs() {
|
|
21276
|
+
const numericId = parseInt(this.chatId);
|
|
21277
|
+
return numericId < 0 ? FLUSH_INTERVAL_GROUP_MS : FLUSH_INTERVAL_DM_MS;
|
|
21278
|
+
}
|
|
21214
21279
|
/** Send the initial status message. Must be called before adding entries. */
|
|
21215
21280
|
async init() {
|
|
21216
21281
|
if (!this.channel.sendTextReturningId) return;
|
|
@@ -21219,7 +21284,7 @@ var init_live_status = __esm({
|
|
|
21219
21284
|
this.messageId = await this.channel.sendTextReturningId(this.chatId, initial, "plain") ?? null;
|
|
21220
21285
|
if (this.messageId) {
|
|
21221
21286
|
this.flushTimer = setInterval(() => this.flush().catch(() => {
|
|
21222
|
-
}),
|
|
21287
|
+
}), this.flushIntervalMs);
|
|
21223
21288
|
}
|
|
21224
21289
|
} catch (err) {
|
|
21225
21290
|
log(`[live-status] init failed: ${err}`);
|
|
@@ -22932,7 +22997,26 @@ ${body.replace(/<[^>]*>/g, "").trim()}</code>
|
|
|
22932
22997
|
});
|
|
22933
22998
|
|
|
22934
22999
|
// src/channels/telegram.ts
|
|
22935
|
-
import { Bot, InlineKeyboard, InputFile } from "grammy";
|
|
23000
|
+
import { Bot, GrammyError, InlineKeyboard, InputFile } from "grammy";
|
|
23001
|
+
async function withRetry(label2, fn) {
|
|
23002
|
+
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
23003
|
+
try {
|
|
23004
|
+
return await fn();
|
|
23005
|
+
} catch (err) {
|
|
23006
|
+
if (err instanceof GrammyError && err.error_code === 429) {
|
|
23007
|
+
const retrySec = Math.min(err.parameters?.retry_after ?? FALLBACK_RETRY_SEC, MAX_RETRY_WAIT_SEC);
|
|
23008
|
+
if (attempt < MAX_RETRIES2) {
|
|
23009
|
+
warn(`[telegram] 429 on ${label2} (attempt ${attempt + 1}/${MAX_RETRIES2}) \u2014 retrying in ${retrySec}s`);
|
|
23010
|
+
await new Promise((r) => setTimeout(r, retrySec * 1e3));
|
|
23011
|
+
continue;
|
|
23012
|
+
}
|
|
23013
|
+
warn(`[telegram] 429 on ${label2} \u2014 exhausted ${MAX_RETRIES2} retries, giving up`);
|
|
23014
|
+
}
|
|
23015
|
+
throw err;
|
|
23016
|
+
}
|
|
23017
|
+
}
|
|
23018
|
+
throw new Error(`withRetry: unreachable`);
|
|
23019
|
+
}
|
|
22936
23020
|
function isFastPathMessage(msg) {
|
|
22937
23021
|
if (msg.type === "command" && msg.command && FAST_PATH_COMMANDS.has(msg.command)) {
|
|
22938
23022
|
return true;
|
|
@@ -22952,13 +23036,16 @@ function numericChatId(chatId) {
|
|
|
22952
23036
|
const raw = chatId.includes(":") ? chatId.split(":").pop() : chatId;
|
|
22953
23037
|
return parseInt(raw);
|
|
22954
23038
|
}
|
|
22955
|
-
var FAST_PATH_COMMANDS, TelegramChannel;
|
|
23039
|
+
var MAX_RETRIES2, FALLBACK_RETRY_SEC, MAX_RETRY_WAIT_SEC, FAST_PATH_COMMANDS, TelegramChannel;
|
|
22956
23040
|
var init_telegram2 = __esm({
|
|
22957
23041
|
"src/channels/telegram.ts"() {
|
|
22958
23042
|
"use strict";
|
|
22959
23043
|
init_telegram();
|
|
22960
23044
|
init_log();
|
|
22961
23045
|
init_store5();
|
|
23046
|
+
MAX_RETRIES2 = 3;
|
|
23047
|
+
FALLBACK_RETRY_SEC = 3;
|
|
23048
|
+
MAX_RETRY_WAIT_SEC = 30;
|
|
22962
23049
|
FAST_PATH_COMMANDS = /* @__PURE__ */ new Set(["stop", "status", "new", "newchat"]);
|
|
22963
23050
|
TelegramChannel = class {
|
|
22964
23051
|
name = "telegram";
|
|
@@ -23109,11 +23196,12 @@ var init_telegram2 = __esm({
|
|
|
23109
23196
|
return;
|
|
23110
23197
|
}
|
|
23111
23198
|
const data = ctx.callbackQuery.data;
|
|
23199
|
+
const messageId = ctx.callbackQuery.message?.message_id?.toString();
|
|
23112
23200
|
ctx.answerCallbackQuery().catch(() => {
|
|
23113
23201
|
});
|
|
23114
23202
|
(async () => {
|
|
23115
23203
|
for (const handler2 of this.callbackHandlers) {
|
|
23116
|
-
await handler2(chatId, data, this);
|
|
23204
|
+
await handler2(chatId, data, this, messageId);
|
|
23117
23205
|
}
|
|
23118
23206
|
})().catch((err) => {
|
|
23119
23207
|
error("[telegram] Callback handler error:", err);
|
|
@@ -23166,7 +23254,10 @@ var init_telegram2 = __esm({
|
|
|
23166
23254
|
if (parseMode === "plain") {
|
|
23167
23255
|
const plainChunks = splitMessage(text);
|
|
23168
23256
|
for (const chunk of plainChunks) {
|
|
23169
|
-
const sent = await
|
|
23257
|
+
const sent = await withRetry(
|
|
23258
|
+
"sendText:plain",
|
|
23259
|
+
() => this.bot.api.sendMessage(numericChatId(chatId), chunk, { ...threadOpts, ...replyOpts })
|
|
23260
|
+
);
|
|
23170
23261
|
this.trackAgentMessage(sent.message_id, chatId);
|
|
23171
23262
|
}
|
|
23172
23263
|
return;
|
|
@@ -23175,32 +23266,44 @@ var init_telegram2 = __esm({
|
|
|
23175
23266
|
const chunks = splitMessage(formatted);
|
|
23176
23267
|
for (const chunk of chunks) {
|
|
23177
23268
|
try {
|
|
23178
|
-
const sent = await
|
|
23179
|
-
|
|
23180
|
-
|
|
23181
|
-
|
|
23182
|
-
|
|
23269
|
+
const sent = await withRetry(
|
|
23270
|
+
"sendText:html",
|
|
23271
|
+
() => this.bot.api.sendMessage(numericChatId(chatId), chunk, {
|
|
23272
|
+
parse_mode: "HTML",
|
|
23273
|
+
...threadOpts,
|
|
23274
|
+
...replyOpts
|
|
23275
|
+
})
|
|
23276
|
+
);
|
|
23183
23277
|
this.trackAgentMessage(sent.message_id, chatId);
|
|
23184
23278
|
} catch {
|
|
23185
|
-
const sent = await
|
|
23186
|
-
|
|
23187
|
-
|
|
23188
|
-
|
|
23279
|
+
const sent = await withRetry(
|
|
23280
|
+
"sendText:fallback",
|
|
23281
|
+
() => this.bot.api.sendMessage(
|
|
23282
|
+
numericChatId(chatId),
|
|
23283
|
+
chunk.replace(/<[^>]+>/g, ""),
|
|
23284
|
+
{ ...threadOpts, ...replyOpts }
|
|
23285
|
+
)
|
|
23189
23286
|
);
|
|
23190
23287
|
this.trackAgentMessage(sent.message_id, chatId);
|
|
23191
23288
|
}
|
|
23192
23289
|
}
|
|
23193
23290
|
}
|
|
23194
23291
|
async sendVoice(chatId, audioBuffer, fileName) {
|
|
23195
|
-
await
|
|
23196
|
-
|
|
23197
|
-
|
|
23292
|
+
await withRetry(
|
|
23293
|
+
"sendVoice",
|
|
23294
|
+
() => this.bot.api.sendVoice(
|
|
23295
|
+
numericChatId(chatId),
|
|
23296
|
+
new InputFile(audioBuffer, fileName ?? "response.ogg")
|
|
23297
|
+
)
|
|
23198
23298
|
);
|
|
23199
23299
|
}
|
|
23200
23300
|
async sendFile(chatId, buffer, fileName) {
|
|
23201
|
-
await
|
|
23202
|
-
|
|
23203
|
-
|
|
23301
|
+
await withRetry(
|
|
23302
|
+
"sendFile",
|
|
23303
|
+
() => this.bot.api.sendDocument(
|
|
23304
|
+
numericChatId(chatId),
|
|
23305
|
+
new InputFile(buffer, fileName)
|
|
23306
|
+
)
|
|
23204
23307
|
);
|
|
23205
23308
|
}
|
|
23206
23309
|
async downloadFile(fileId) {
|
|
@@ -23213,7 +23316,10 @@ var init_telegram2 = __esm({
|
|
|
23213
23316
|
try {
|
|
23214
23317
|
const formatted = parseMode === "html" ? text : parseMode === "plain" ? text : formatForTelegram(text);
|
|
23215
23318
|
const opts = parseMode === "plain" ? {} : { parse_mode: "HTML" };
|
|
23216
|
-
const msg = await
|
|
23319
|
+
const msg = await withRetry(
|
|
23320
|
+
"sendTextReturningId",
|
|
23321
|
+
() => this.bot.api.sendMessage(numericChatId(chatId), formatted, opts)
|
|
23322
|
+
);
|
|
23217
23323
|
return msg.message_id.toString();
|
|
23218
23324
|
} catch {
|
|
23219
23325
|
return void 0;
|
|
@@ -23222,16 +23328,22 @@ var init_telegram2 = __esm({
|
|
|
23222
23328
|
async editText(chatId, messageId, text, parseMode) {
|
|
23223
23329
|
const formatted = parseMode === "html" ? text : formatForTelegram(text);
|
|
23224
23330
|
try {
|
|
23225
|
-
await
|
|
23226
|
-
|
|
23227
|
-
|
|
23331
|
+
await withRetry(
|
|
23332
|
+
"editText:html",
|
|
23333
|
+
() => this.bot.api.editMessageText(numericChatId(chatId), parseInt(messageId), formatted, {
|
|
23334
|
+
parse_mode: "HTML"
|
|
23335
|
+
})
|
|
23336
|
+
);
|
|
23228
23337
|
return true;
|
|
23229
23338
|
} catch {
|
|
23230
23339
|
try {
|
|
23231
|
-
await
|
|
23232
|
-
|
|
23233
|
-
|
|
23234
|
-
|
|
23340
|
+
await withRetry(
|
|
23341
|
+
"editText:fallback",
|
|
23342
|
+
() => this.bot.api.editMessageText(
|
|
23343
|
+
numericChatId(chatId),
|
|
23344
|
+
parseInt(messageId),
|
|
23345
|
+
formatted.replace(/<[^>]+>/g, "")
|
|
23346
|
+
)
|
|
23235
23347
|
);
|
|
23236
23348
|
return true;
|
|
23237
23349
|
} catch {
|
|
@@ -23261,16 +23373,22 @@ var init_telegram2 = __esm({
|
|
|
23261
23373
|
const MAX_KEYBOARD_TEXT = 4e3;
|
|
23262
23374
|
const safeText = text.length > MAX_KEYBOARD_TEXT ? text.slice(0, MAX_KEYBOARD_TEXT) + "\n\n\u2026(truncated)" : text;
|
|
23263
23375
|
try {
|
|
23264
|
-
const msg = await
|
|
23265
|
-
|
|
23266
|
-
|
|
23376
|
+
const msg = await withRetry(
|
|
23377
|
+
"sendKeyboard",
|
|
23378
|
+
() => this.bot.api.sendMessage(numericChatId(chatId), safeText, {
|
|
23379
|
+
reply_markup: keyboard
|
|
23380
|
+
})
|
|
23381
|
+
);
|
|
23267
23382
|
return msg.message_id.toString();
|
|
23268
23383
|
} catch (err) {
|
|
23269
23384
|
error(`[telegram] sendKeyboard failed (chat=${chatId}, textLen=${text.length}):`, err);
|
|
23270
23385
|
try {
|
|
23271
|
-
const fallbackMsg = await
|
|
23272
|
-
|
|
23273
|
-
|
|
23386
|
+
const fallbackMsg = await withRetry(
|
|
23387
|
+
"sendKeyboard:fallback",
|
|
23388
|
+
() => this.bot.api.sendMessage(numericChatId(chatId), "\u2B06\uFE0F (see above for details)", {
|
|
23389
|
+
reply_markup: keyboard
|
|
23390
|
+
})
|
|
23391
|
+
);
|
|
23274
23392
|
return fallbackMsg.message_id.toString();
|
|
23275
23393
|
} catch (fallbackErr) {
|
|
23276
23394
|
error(`[telegram] sendKeyboard fallback also failed:`, fallbackErr);
|
package/package.json
CHANGED