@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.
@@ -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
- 'Messages from Telegram arrive as <channel source="telegram" chat_id="..." user="..." user_name="..." message_id="...">.',
14256
- "Inbound attachments: the <channel> tag's `files` attribute is a JSON-serialised array \u2014 JSON.parse it before iterating. If a file entry has a `path`, the image is ALREADY DOWNLOADED locally \u2014 Read that path directly, do NOT call telegram.download_attachment. That tool is only for entries with `file_id` but NO `path` (PDF, docx, voice, audio, video, animations): pass file_id + chat_id verbatim (never paraphrase), then Read the returned path. Single-image messages also get a top-level `image_path` convenience attribute; multi-image messages omit it. Caption (no text) arrives as the channel content. Never tell the user about internal file-handling failures that don't affect the answer.",
14257
- "The user reads Telegram, not this session. Every response goes through telegram.reply with the chat_id from the tag \u2014 clarifying questions, errors, partial answers, status updates, all of it.",
14258
- 'For work >30s follow CLAUDE.md kanban flow: kanban.add, reply with "On it \u2014 tracking here: <kanban URL>", move to in_progress, do the work, reply with the result. Simple lookups skip kanban but still reply.',
14259
- "Address users by user_name; user is the numeric Telegram ID. Reply-to a specific message with reply_to_message_id, otherwise omit.",
14260
- "Resolve ambiguous times against your own Timezone from CLAUDE.md \u2014 do not ask the user.",
14261
- "Reaction taxonomy (use telegram.react sparingly \u2014 prefer a text reply via telegram.reply): \u{1F440} = ack/working on it (already auto-added on inbound, do not duplicate); \u{1F44D} or \u{1F389} = action completed successfully. NEVER react to signal failure \u2014 users misread emoji reactions and don't know why something failed. If an action failed, send a telegram.reply with one sentence explaining what went wrong (no stack traces, no secrets) so the user understands. Free-tier emoji set: \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} (premium-only fail silently)."
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.15.8",
3
+ "version": "0.15.9",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {