@integrity-labs/agt-cli 0.15.8 → 0.15.9
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/bin/agt.js +3 -3
- package/dist/{chunk-32D5TUSD.js → chunk-C6UBNLUC.js} +117 -5
- package/dist/{chunk-32D5TUSD.js.map → chunk-C6UBNLUC.js.map} +1 -1
- package/dist/lib/manager-worker.js +348 -177
- package/dist/lib/manager-worker.js.map +1 -1
- package/mcp/slack-channel.js +388 -11
- package/mcp/telegram-channel.js +386 -8
- package/package.json +1 -1
package/mcp/telegram-channel.js
CHANGED
|
@@ -13863,7 +13863,21 @@ var StdioServerTransport = class {
|
|
|
13863
13863
|
|
|
13864
13864
|
// src/telegram-channel.ts
|
|
13865
13865
|
import https from "https";
|
|
13866
|
-
import { createHash } from "crypto";
|
|
13866
|
+
import { createHash, randomUUID } from "crypto";
|
|
13867
|
+
import {
|
|
13868
|
+
createWriteStream,
|
|
13869
|
+
existsSync,
|
|
13870
|
+
mkdirSync as mkdirSync2,
|
|
13871
|
+
readFileSync,
|
|
13872
|
+
readdirSync,
|
|
13873
|
+
renameSync as renameSync2,
|
|
13874
|
+
statSync,
|
|
13875
|
+
unlinkSync as unlinkSync2,
|
|
13876
|
+
watch,
|
|
13877
|
+
writeFileSync as writeFileSync2
|
|
13878
|
+
} from "fs";
|
|
13879
|
+
import { homedir as homedir2 } from "os";
|
|
13880
|
+
import { join as join2 } from "path";
|
|
13867
13881
|
|
|
13868
13882
|
// src/channel-attachments.ts
|
|
13869
13883
|
import { homedir } from "os";
|
|
@@ -14110,6 +14124,28 @@ if (!BOT_TOKEN) {
|
|
|
14110
14124
|
);
|
|
14111
14125
|
process.exit(1);
|
|
14112
14126
|
}
|
|
14127
|
+
var stderrLogStream = null;
|
|
14128
|
+
if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
|
|
14129
|
+
try {
|
|
14130
|
+
const logDir = join2(homedir2(), ".augmented", AGENT_CODE_NAME);
|
|
14131
|
+
mkdirSync2(logDir, { recursive: true });
|
|
14132
|
+
stderrLogStream = createWriteStream(join2(logDir, "telegram-channel-stderr.log"), {
|
|
14133
|
+
flags: "a",
|
|
14134
|
+
mode: 384
|
|
14135
|
+
});
|
|
14136
|
+
const origWrite = process.stderr.write.bind(process.stderr);
|
|
14137
|
+
process.stderr.write = (chunk, ...rest) => {
|
|
14138
|
+
try {
|
|
14139
|
+
const buf = typeof chunk === "string" ? chunk : String(chunk);
|
|
14140
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
14141
|
+
stderrLogStream?.write(`[${ts}] ${buf}${buf.endsWith("\n") ? "" : "\n"}`);
|
|
14142
|
+
} catch {
|
|
14143
|
+
}
|
|
14144
|
+
return origWrite(chunk, ...rest);
|
|
14145
|
+
};
|
|
14146
|
+
} catch {
|
|
14147
|
+
}
|
|
14148
|
+
}
|
|
14113
14149
|
function telegramApiCall(method, body, timeoutMs) {
|
|
14114
14150
|
return new Promise((resolve2, reject) => {
|
|
14115
14151
|
const postData = JSON.stringify(body);
|
|
@@ -14175,13 +14211,320 @@ async function setMessageReaction(chatId, messageId, emoji2) {
|
|
|
14175
14211
|
);
|
|
14176
14212
|
}
|
|
14177
14213
|
}
|
|
14214
|
+
var RESTART_FLAGS_DIR = join2(homedir2(), ".augmented", "restart-flags");
|
|
14215
|
+
async function handleRestartCommand(opts) {
|
|
14216
|
+
try {
|
|
14217
|
+
if (!existsSync(RESTART_FLAGS_DIR)) {
|
|
14218
|
+
mkdirSync2(RESTART_FLAGS_DIR, { recursive: true });
|
|
14219
|
+
}
|
|
14220
|
+
const flagPath = join2(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
|
|
14221
|
+
const flag = {
|
|
14222
|
+
codeName: AGENT_CODE_NAME,
|
|
14223
|
+
source: "telegram",
|
|
14224
|
+
ts: Date.now(),
|
|
14225
|
+
reply: { chat_id: opts.chatId, message_id: opts.messageId }
|
|
14226
|
+
};
|
|
14227
|
+
const tmpPath = `${flagPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
14228
|
+
writeFileSync2(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
14229
|
+
renameSync2(tmpPath, flagPath);
|
|
14230
|
+
process.stderr.write(
|
|
14231
|
+
`telegram-channel(${AGENT_CODE_NAME}): /restart queued from chat ${redactId(opts.chatId)}
|
|
14232
|
+
`
|
|
14233
|
+
);
|
|
14234
|
+
try {
|
|
14235
|
+
const ack = await telegramApiCall(
|
|
14236
|
+
"sendMessage",
|
|
14237
|
+
{
|
|
14238
|
+
chat_id: opts.chatId,
|
|
14239
|
+
text: `\u23F3 Restart queued for \`${AGENT_CODE_NAME}\`. The session will reconnect in a few seconds.`,
|
|
14240
|
+
reply_to_message_id: Number(opts.messageId),
|
|
14241
|
+
parse_mode: "Markdown"
|
|
14242
|
+
},
|
|
14243
|
+
1e4
|
|
14244
|
+
);
|
|
14245
|
+
if (!ack.ok) {
|
|
14246
|
+
process.stderr.write(
|
|
14247
|
+
`telegram-channel(${AGENT_CODE_NAME}): /restart ack rejected by Telegram (chat ${redactId(opts.chatId)}): ${ack.description ?? "unknown"}
|
|
14248
|
+
`
|
|
14249
|
+
);
|
|
14250
|
+
}
|
|
14251
|
+
} catch (err) {
|
|
14252
|
+
process.stderr.write(
|
|
14253
|
+
`telegram-channel(${AGENT_CODE_NAME}): /restart ack send failed: ${redactAugmentedPaths(err.message)}
|
|
14254
|
+
`
|
|
14255
|
+
);
|
|
14256
|
+
}
|
|
14257
|
+
} catch (err) {
|
|
14258
|
+
process.stderr.write(
|
|
14259
|
+
`telegram-channel(${AGENT_CODE_NAME}): /restart flag write failed: ${redactAugmentedPaths(err.message)}
|
|
14260
|
+
`
|
|
14261
|
+
);
|
|
14262
|
+
try {
|
|
14263
|
+
const errAck = await telegramApiCall(
|
|
14264
|
+
"sendMessage",
|
|
14265
|
+
{
|
|
14266
|
+
chat_id: opts.chatId,
|
|
14267
|
+
text: `\u274C Failed to queue restart for ${AGENT_CODE_NAME}. Please try again in a few seconds.`,
|
|
14268
|
+
reply_to_message_id: Number(opts.messageId)
|
|
14269
|
+
},
|
|
14270
|
+
1e4
|
|
14271
|
+
);
|
|
14272
|
+
if (!errAck.ok) {
|
|
14273
|
+
process.stderr.write(
|
|
14274
|
+
`telegram-channel(${AGENT_CODE_NAME}): /restart error-ack rejected by Telegram (chat ${redactId(opts.chatId)}): ${errAck.description ?? "unknown"}
|
|
14275
|
+
`
|
|
14276
|
+
);
|
|
14277
|
+
}
|
|
14278
|
+
} catch {
|
|
14279
|
+
}
|
|
14280
|
+
}
|
|
14281
|
+
}
|
|
14282
|
+
var cachedBotUsername = null;
|
|
14283
|
+
async function resolveBotUsername() {
|
|
14284
|
+
if (cachedBotUsername !== null) return cachedBotUsername;
|
|
14285
|
+
try {
|
|
14286
|
+
const resp = await telegramApiCall("getMe", {}, 5e3);
|
|
14287
|
+
if (resp.ok && resp.result && typeof resp.result.username === "string") {
|
|
14288
|
+
cachedBotUsername = resp.result.username.toLowerCase();
|
|
14289
|
+
return cachedBotUsername;
|
|
14290
|
+
}
|
|
14291
|
+
process.stderr.write(
|
|
14292
|
+
`telegram-channel(${AGENT_CODE_NAME}): getMe rejected: ${resp.description ?? "unknown"} \u2014 will retry on next /restart@<bot>
|
|
14293
|
+
`
|
|
14294
|
+
);
|
|
14295
|
+
} catch (err) {
|
|
14296
|
+
process.stderr.write(
|
|
14297
|
+
`telegram-channel(${AGENT_CODE_NAME}): getMe failed: ${redactAugmentedPaths(err.message)} \u2014 will retry on next /restart@<bot>
|
|
14298
|
+
`
|
|
14299
|
+
);
|
|
14300
|
+
}
|
|
14301
|
+
return null;
|
|
14302
|
+
}
|
|
14303
|
+
var RESTART_SYNTAX_RE = /^\/restart(?:@([A-Za-z0-9_]{1,64}))?(?:\s|$)/;
|
|
14304
|
+
function isRestartSyntax(text) {
|
|
14305
|
+
return RESTART_SYNTAX_RE.test(text);
|
|
14306
|
+
}
|
|
14307
|
+
async function classifyRestartCommand(text) {
|
|
14308
|
+
const m = RESTART_SYNTAX_RE.exec(text);
|
|
14309
|
+
if (!m) return "ignore";
|
|
14310
|
+
const target = m[1]?.toLowerCase();
|
|
14311
|
+
if (!target) return "act";
|
|
14312
|
+
const ours = await resolveBotUsername();
|
|
14313
|
+
if (!ours) return "verification_failed";
|
|
14314
|
+
return target === ours ? "act" : "ignore";
|
|
14315
|
+
}
|
|
14178
14316
|
var pendingMessages = /* @__PURE__ */ new Map();
|
|
14317
|
+
var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join2(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
14318
|
+
var PENDING_INBOUND_DIR = AGENT_DIR ? join2(AGENT_DIR, "telegram-pending-inbound") : null;
|
|
14319
|
+
var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join2(AGENT_DIR, "telegram-recovery-outbox") : null;
|
|
14320
|
+
function safeMarkerName(chatId, messageId) {
|
|
14321
|
+
const safe = (s) => s.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
14322
|
+
return `${safe(chatId)}__${safe(messageId)}.json`;
|
|
14323
|
+
}
|
|
14324
|
+
function pendingInboundPath(chatId, messageId) {
|
|
14325
|
+
if (!PENDING_INBOUND_DIR) return null;
|
|
14326
|
+
return join2(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
|
|
14327
|
+
}
|
|
14328
|
+
function writePendingInboundMarker(chatId, messageId, chatType) {
|
|
14329
|
+
const path = pendingInboundPath(chatId, messageId);
|
|
14330
|
+
if (!path || !PENDING_INBOUND_DIR) return;
|
|
14331
|
+
const marker = {
|
|
14332
|
+
chat_id: chatId,
|
|
14333
|
+
message_id: messageId,
|
|
14334
|
+
chat_type: chatType,
|
|
14335
|
+
received_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
14336
|
+
};
|
|
14337
|
+
try {
|
|
14338
|
+
mkdirSync2(PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
|
|
14339
|
+
writeFileSync2(path, JSON.stringify(marker), { mode: 384 });
|
|
14340
|
+
} catch (err) {
|
|
14341
|
+
process.stderr.write(
|
|
14342
|
+
`telegram-channel(${AGENT_CODE_NAME}): pending-inbound marker write failed: ${err.message}
|
|
14343
|
+
`
|
|
14344
|
+
);
|
|
14345
|
+
}
|
|
14346
|
+
}
|
|
14347
|
+
function clearPendingInboundMarker(chatId, messageId) {
|
|
14348
|
+
const path = pendingInboundPath(chatId, messageId);
|
|
14349
|
+
if (!path) return;
|
|
14350
|
+
try {
|
|
14351
|
+
if (existsSync(path)) unlinkSync2(path);
|
|
14352
|
+
} catch {
|
|
14353
|
+
}
|
|
14354
|
+
}
|
|
14355
|
+
var MAX_RECOVERY_ATTEMPTS = 3;
|
|
14356
|
+
function nextRetryName(filename) {
|
|
14357
|
+
const match = filename.match(/^(.*?)(?:\.retry-(\d+))?\.json$/);
|
|
14358
|
+
if (!match) return null;
|
|
14359
|
+
const base = match[1];
|
|
14360
|
+
const prev = match[2] ? parseInt(match[2], 10) : 0;
|
|
14361
|
+
const attempt = prev + 1;
|
|
14362
|
+
if (attempt >= MAX_RECOVERY_ATTEMPTS) {
|
|
14363
|
+
return { next: `${base}.retry-${attempt}.poison.json`, attempt };
|
|
14364
|
+
}
|
|
14365
|
+
return { next: `${base}.retry-${attempt}.json`, attempt };
|
|
14366
|
+
}
|
|
14367
|
+
async function processRecoveryOutboxFile(filename) {
|
|
14368
|
+
if (!RECOVERY_OUTBOX_DIR) return;
|
|
14369
|
+
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
|
|
14370
|
+
const fullPath = join2(RECOVERY_OUTBOX_DIR, filename);
|
|
14371
|
+
let payload;
|
|
14372
|
+
try {
|
|
14373
|
+
const raw = readFileSync(fullPath, "utf-8");
|
|
14374
|
+
payload = JSON.parse(raw);
|
|
14375
|
+
} catch (err) {
|
|
14376
|
+
process.stderr.write(
|
|
14377
|
+
`telegram-channel(${AGENT_CODE_NAME}): recovery outbox parse failed (${filename}): ${err.message}
|
|
14378
|
+
`
|
|
14379
|
+
);
|
|
14380
|
+
try {
|
|
14381
|
+
renameSync2(fullPath, `${fullPath}.parse-error.poison`);
|
|
14382
|
+
} catch {
|
|
14383
|
+
}
|
|
14384
|
+
return;
|
|
14385
|
+
}
|
|
14386
|
+
if (!payload.chat_id || !payload.text) {
|
|
14387
|
+
process.stderr.write(
|
|
14388
|
+
`telegram-channel(${AGENT_CODE_NAME}): recovery outbox malformed (${filename}): missing chat_id or text
|
|
14389
|
+
`
|
|
14390
|
+
);
|
|
14391
|
+
try {
|
|
14392
|
+
renameSync2(fullPath, `${fullPath}.malformed.poison`);
|
|
14393
|
+
} catch {
|
|
14394
|
+
}
|
|
14395
|
+
return;
|
|
14396
|
+
}
|
|
14397
|
+
const text = `[recovered reply] ${payload.text}`;
|
|
14398
|
+
const body = {
|
|
14399
|
+
chat_id: payload.chat_id,
|
|
14400
|
+
text,
|
|
14401
|
+
allow_sending_without_reply: true
|
|
14402
|
+
};
|
|
14403
|
+
if (payload.message_id) body.reply_to_message_id = Number(payload.message_id);
|
|
14404
|
+
let sendSucceeded = false;
|
|
14405
|
+
try {
|
|
14406
|
+
const resp = await telegramApiCall("sendMessage", body, 15e3);
|
|
14407
|
+
if (resp.ok) {
|
|
14408
|
+
sendSucceeded = true;
|
|
14409
|
+
process.stderr.write(
|
|
14410
|
+
`telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery sent (chat=${redactId(payload.chat_id)} msg=${redactId(payload.message_id ?? "")})
|
|
14411
|
+
`
|
|
14412
|
+
);
|
|
14413
|
+
if (payload.message_id) clearPendingInboundMarker(payload.chat_id, payload.message_id);
|
|
14414
|
+
} else {
|
|
14415
|
+
process.stderr.write(
|
|
14416
|
+
`telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery failed (chat=${redactId(payload.chat_id)}): ${resp.description ?? "unknown"}
|
|
14417
|
+
`
|
|
14418
|
+
);
|
|
14419
|
+
}
|
|
14420
|
+
} catch (err) {
|
|
14421
|
+
process.stderr.write(
|
|
14422
|
+
`telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery error (chat=${redactId(payload.chat_id)}): ${err.message}
|
|
14423
|
+
`
|
|
14424
|
+
);
|
|
14425
|
+
}
|
|
14426
|
+
if (sendSucceeded) {
|
|
14427
|
+
try {
|
|
14428
|
+
unlinkSync2(fullPath);
|
|
14429
|
+
} catch {
|
|
14430
|
+
}
|
|
14431
|
+
return;
|
|
14432
|
+
}
|
|
14433
|
+
const next = nextRetryName(filename);
|
|
14434
|
+
if (next) {
|
|
14435
|
+
try {
|
|
14436
|
+
renameSync2(fullPath, join2(RECOVERY_OUTBOX_DIR, next.next));
|
|
14437
|
+
if (next.attempt >= MAX_RECOVERY_ATTEMPTS) {
|
|
14438
|
+
process.stderr.write(
|
|
14439
|
+
`telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
|
|
14440
|
+
`
|
|
14441
|
+
);
|
|
14442
|
+
}
|
|
14443
|
+
} catch {
|
|
14444
|
+
}
|
|
14445
|
+
}
|
|
14446
|
+
}
|
|
14447
|
+
function isFirstAttemptOutboxFile(filename) {
|
|
14448
|
+
if (!filename.endsWith(".json")) return false;
|
|
14449
|
+
if (filename.includes(".retry-")) return false;
|
|
14450
|
+
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return false;
|
|
14451
|
+
if (filename.startsWith(".")) return false;
|
|
14452
|
+
return true;
|
|
14453
|
+
}
|
|
14454
|
+
var RETRY_SCAN_INTERVAL_MS = 6e4;
|
|
14455
|
+
var RETRY_BACKOFF_BASE_MS = 6e4;
|
|
14456
|
+
function shouldRetryNow(filename, mtimeMs) {
|
|
14457
|
+
const match = filename.match(/\.retry-(\d+)\.json$/);
|
|
14458
|
+
if (!match) return false;
|
|
14459
|
+
const attempt = parseInt(match[1], 10);
|
|
14460
|
+
const required2 = attempt * RETRY_BACKOFF_BASE_MS;
|
|
14461
|
+
return Date.now() - mtimeMs >= required2;
|
|
14462
|
+
}
|
|
14463
|
+
function scanRecoveryRetries() {
|
|
14464
|
+
if (!RECOVERY_OUTBOX_DIR) return;
|
|
14465
|
+
let entries;
|
|
14466
|
+
try {
|
|
14467
|
+
entries = readdirSync(RECOVERY_OUTBOX_DIR);
|
|
14468
|
+
} catch {
|
|
14469
|
+
return;
|
|
14470
|
+
}
|
|
14471
|
+
for (const f of entries) {
|
|
14472
|
+
if (!f.endsWith(".json")) continue;
|
|
14473
|
+
if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
|
|
14474
|
+
let mtimeMs;
|
|
14475
|
+
try {
|
|
14476
|
+
mtimeMs = statSync(join2(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
|
|
14477
|
+
} catch {
|
|
14478
|
+
continue;
|
|
14479
|
+
}
|
|
14480
|
+
if (shouldRetryNow(f, mtimeMs)) {
|
|
14481
|
+
void processRecoveryOutboxFile(f);
|
|
14482
|
+
}
|
|
14483
|
+
}
|
|
14484
|
+
}
|
|
14485
|
+
function startRecoveryOutboxWatcher() {
|
|
14486
|
+
if (!RECOVERY_OUTBOX_DIR) return;
|
|
14487
|
+
try {
|
|
14488
|
+
mkdirSync2(RECOVERY_OUTBOX_DIR, { recursive: true, mode: 448 });
|
|
14489
|
+
} catch (err) {
|
|
14490
|
+
process.stderr.write(
|
|
14491
|
+
`telegram-channel(${AGENT_CODE_NAME}): recovery outbox mkdir failed: ${err.message}
|
|
14492
|
+
`
|
|
14493
|
+
);
|
|
14494
|
+
return;
|
|
14495
|
+
}
|
|
14496
|
+
try {
|
|
14497
|
+
for (const f of readdirSync(RECOVERY_OUTBOX_DIR)) {
|
|
14498
|
+
if (isFirstAttemptOutboxFile(f)) void processRecoveryOutboxFile(f);
|
|
14499
|
+
}
|
|
14500
|
+
} catch {
|
|
14501
|
+
}
|
|
14502
|
+
try {
|
|
14503
|
+
const watcher = watch(RECOVERY_OUTBOX_DIR, (event, filename) => {
|
|
14504
|
+
if (event !== "rename" || !filename) return;
|
|
14505
|
+
if (!isFirstAttemptOutboxFile(filename)) return;
|
|
14506
|
+
if (existsSync(join2(RECOVERY_OUTBOX_DIR, filename))) {
|
|
14507
|
+
void processRecoveryOutboxFile(filename);
|
|
14508
|
+
}
|
|
14509
|
+
});
|
|
14510
|
+
watcher.unref?.();
|
|
14511
|
+
} catch (err) {
|
|
14512
|
+
process.stderr.write(
|
|
14513
|
+
`telegram-channel(${AGENT_CODE_NAME}): recovery outbox watch failed: ${err.message}
|
|
14514
|
+
`
|
|
14515
|
+
);
|
|
14516
|
+
}
|
|
14517
|
+
const retryTimer = setInterval(scanRecoveryRetries, RETRY_SCAN_INTERVAL_MS);
|
|
14518
|
+
retryTimer.unref?.();
|
|
14519
|
+
}
|
|
14520
|
+
startRecoveryOutboxWatcher();
|
|
14179
14521
|
function trackPendingMessage(chatId, messageId, chatType) {
|
|
14180
14522
|
const key = `${chatId}:${messageId}`;
|
|
14181
14523
|
const existing = pendingMessages.get(key);
|
|
14182
14524
|
if (existing) clearTimeout(existing.timer);
|
|
14183
14525
|
const timer = setTimeout(() => {
|
|
14184
14526
|
pendingMessages.delete(key);
|
|
14527
|
+
clearPendingInboundMarker(chatId, messageId);
|
|
14185
14528
|
void setMessageReaction(chatId, messageId, null);
|
|
14186
14529
|
void telegramApiCall(
|
|
14187
14530
|
"sendMessage",
|
|
@@ -14212,6 +14555,7 @@ function trackPendingMessage(chatId, messageId, chatType) {
|
|
|
14212
14555
|
}, RESPONSE_TIMEOUT_MS);
|
|
14213
14556
|
timer.unref?.();
|
|
14214
14557
|
pendingMessages.set(key, { timer, chatType });
|
|
14558
|
+
writePendingInboundMarker(chatId, messageId, chatType);
|
|
14215
14559
|
}
|
|
14216
14560
|
function clearPendingMessage(chatId, messageId) {
|
|
14217
14561
|
if (messageId) {
|
|
@@ -14220,6 +14564,7 @@ function clearPendingMessage(chatId, messageId) {
|
|
|
14220
14564
|
if (entry) {
|
|
14221
14565
|
clearTimeout(entry.timer);
|
|
14222
14566
|
pendingMessages.delete(key);
|
|
14567
|
+
clearPendingInboundMarker(chatId, messageId);
|
|
14223
14568
|
}
|
|
14224
14569
|
return;
|
|
14225
14570
|
}
|
|
@@ -14230,6 +14575,8 @@ function clearPendingMessage(chatId, messageId) {
|
|
|
14230
14575
|
if (entry.chatType !== "private") continue;
|
|
14231
14576
|
clearTimeout(entry.timer);
|
|
14232
14577
|
pendingMessages.delete(key);
|
|
14578
|
+
const msgId = key.slice(prefix.length);
|
|
14579
|
+
clearPendingInboundMarker(chatId, msgId);
|
|
14233
14580
|
clearedPrivate = true;
|
|
14234
14581
|
}
|
|
14235
14582
|
if (clearedPrivate) return;
|
|
@@ -14238,6 +14585,8 @@ function clearPendingMessage(chatId, messageId) {
|
|
|
14238
14585
|
if (entry.chatType === "private") continue;
|
|
14239
14586
|
clearTimeout(entry.timer);
|
|
14240
14587
|
pendingMessages.delete(key);
|
|
14588
|
+
const msgId = key.slice(prefix.length);
|
|
14589
|
+
clearPendingInboundMarker(chatId, msgId);
|
|
14241
14590
|
return;
|
|
14242
14591
|
}
|
|
14243
14592
|
}
|
|
@@ -14252,13 +14601,14 @@ var mcp = new Server(
|
|
|
14252
14601
|
// Attachments rules live near the top because they get chopped otherwise
|
|
14253
14602
|
// and the agent silently loses attachment-handling guidance.
|
|
14254
14603
|
instructions: [
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
"
|
|
14258
|
-
'
|
|
14259
|
-
"
|
|
14260
|
-
|
|
14261
|
-
"
|
|
14604
|
+
// Highest-priority lines first — Claude Code truncates this string at
|
|
14605
|
+
// 2048 chars, so anything appended late silently disappears.
|
|
14606
|
+
"CRITICAL: every response to a Telegram <channel> tag MUST go through telegram.reply with the chat_id from the tag. Text in your session WITHOUT a telegram.reply call never reaches the user.",
|
|
14607
|
+
'Messages from Telegram arrive as <channel source="telegram" chat_id="..." user="..." user_name="..." message_id="...">. Pass reply_to_message_id from the tag so the response lands as a quote-reply in busy chats.',
|
|
14608
|
+
"Inbound attachments: <channel> `files` is a JSON-serialised array \u2014 JSON.parse it. If an entry has `path`, the image is already downloaded \u2014 Read it directly, do NOT call telegram.download_attachment. Use that tool only for entries with `file_id` but NO `path` (PDF, docx, voice, audio, video, animations): pass file_id + chat_id verbatim, then Read the returned path. Single-image messages also get a top-level `image_path`. Caption arrives as channel content. Don't surface internal file-handling errors that don't affect the answer.",
|
|
14609
|
+
'For work >30s follow CLAUDE.md kanban flow: kanban.add \u2192 reply "On it \u2014 tracking here: <kanban URL>" \u2192 move to in_progress \u2192 do the work \u2192 reply with the result. Simple lookups skip kanban but still reply.',
|
|
14610
|
+
"Address users by user_name; user is the numeric Telegram ID. Resolve ambiguous times against your own Timezone from CLAUDE.md \u2014 do not ask.",
|
|
14611
|
+
"Reaction taxonomy (use telegram.react sparingly \u2014 prefer telegram.reply): \u{1F440} = ack (already auto-added on inbound, do not duplicate); \u{1F44D} or \u{1F389} = success. NEVER react to signal failure. On failure, telegram.reply with one sentence explaining what went wrong. Free-tier emoji: \u{1F44D} \u{1F44E} \u2764 \u{1F525} \u{1F389} \u{1F914} \u{1F92F} \u{1F64F} \u{1F44C} \u{1F440} \u{1F4AF} \u270D \u{1FAE1} \u{1F192} \u{1F973} \u{1F494}."
|
|
14262
14612
|
].join(" ")
|
|
14263
14613
|
}
|
|
14264
14614
|
);
|
|
@@ -14570,6 +14920,34 @@ async function pollLoop() {
|
|
|
14570
14920
|
if (content.length === 0 && classifiedAttachments.length === 0) continue;
|
|
14571
14921
|
const chatId = String(msg.chat.id);
|
|
14572
14922
|
if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId)) continue;
|
|
14923
|
+
const trimmedContent = content.trim();
|
|
14924
|
+
if (isRestartSyntax(trimmedContent)) {
|
|
14925
|
+
const disposition = await classifyRestartCommand(trimmedContent);
|
|
14926
|
+
if (disposition === "act") {
|
|
14927
|
+
await handleRestartCommand({
|
|
14928
|
+
chatId,
|
|
14929
|
+
messageId: String(msg.message_id)
|
|
14930
|
+
});
|
|
14931
|
+
} else if (disposition === "verification_failed") {
|
|
14932
|
+
try {
|
|
14933
|
+
await telegramApiCall(
|
|
14934
|
+
"sendMessage",
|
|
14935
|
+
{
|
|
14936
|
+
chat_id: chatId,
|
|
14937
|
+
text: `\u274C Couldn't verify the restart target. Please retry /restart in a few seconds.`,
|
|
14938
|
+
reply_to_message_id: Number(msg.message_id)
|
|
14939
|
+
},
|
|
14940
|
+
1e4
|
|
14941
|
+
);
|
|
14942
|
+
} catch (err) {
|
|
14943
|
+
process.stderr.write(
|
|
14944
|
+
`telegram-channel(${AGENT_CODE_NAME}): /restart verification-failed ack send failed: ${redactAugmentedPaths(err.message)}
|
|
14945
|
+
`
|
|
14946
|
+
);
|
|
14947
|
+
}
|
|
14948
|
+
}
|
|
14949
|
+
continue;
|
|
14950
|
+
}
|
|
14573
14951
|
const userId = msg.from?.id != null ? String(msg.from.id) : "unknown";
|
|
14574
14952
|
const userName = msg.from?.username || [msg.from?.first_name, msg.from?.last_name].filter(Boolean).join(" ").trim() || userId;
|
|
14575
14953
|
const messageId = String(msg.message_id);
|