@integrity-labs/agt-cli 0.27.14 → 0.27.16

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.
@@ -14293,13 +14293,13 @@ var StdioServerTransport = class {
14293
14293
 
14294
14294
  // src/telegram-channel.ts
14295
14295
  import https from "https";
14296
- import { createHash, randomUUID as randomUUID2 } from "crypto";
14296
+ import { createHash, randomUUID } from "crypto";
14297
14297
  import {
14298
14298
  createWriteStream,
14299
14299
  existsSync as existsSync2,
14300
14300
  mkdirSync as mkdirSync3,
14301
- readFileSync as readFileSync2,
14302
- readdirSync,
14301
+ readFileSync as readFileSync3,
14302
+ readdirSync as readdirSync2,
14303
14303
  renameSync as renameSync3,
14304
14304
  statSync,
14305
14305
  unlinkSync as unlinkSync3,
@@ -14307,7 +14307,7 @@ import {
14307
14307
  writeFileSync as writeFileSync3
14308
14308
  } from "fs";
14309
14309
  import { homedir as homedir2 } from "os";
14310
- import { join as join3 } from "path";
14310
+ import { join as join4 } from "path";
14311
14311
 
14312
14312
  // src/channel-attachments.ts
14313
14313
  import { homedir } from "os";
@@ -15386,430 +15386,6 @@ function createInboundContextClient(args) {
15386
15386
  };
15387
15387
  }
15388
15388
 
15389
- // src/progress-tools.ts
15390
- import { randomUUID } from "crypto";
15391
-
15392
- // src/progress-handle.ts
15393
- async function startProgressHandle(opts) {
15394
- const now = opts.now ?? (() => Date.now());
15395
- const setTimer = opts.setTimer ?? ((cb, ms) => setTimeout(cb, ms));
15396
- const clearTimer = opts.clearTimer ?? ((id) => clearTimeout(id));
15397
- const debounceMs = opts.debounceMs ?? 1e3;
15398
- const onPostTerminalUpdate = opts.onPostTerminalUpdate ?? defaultPostTerminalWarn;
15399
- const state = {
15400
- initialLabel: opts.initialLabel,
15401
- mode: opts.initialMode ?? "working",
15402
- lastChangeAt: now()
15403
- };
15404
- let lastFlushAt = 0;
15405
- let pendingTimer = null;
15406
- let inFlight = null;
15407
- let terminal = false;
15408
- async function doFlush() {
15409
- while (inFlight) await inFlight;
15410
- if (pendingTimer != null) {
15411
- clearTimer(pendingTimer);
15412
- pendingTimer = null;
15413
- }
15414
- lastFlushAt = now();
15415
- const promise = opts.flush(snapshotState());
15416
- inFlight = promise.then(
15417
- () => {
15418
- inFlight = null;
15419
- },
15420
- (err) => {
15421
- inFlight = null;
15422
- throw err;
15423
- }
15424
- );
15425
- await inFlight;
15426
- }
15427
- function snapshotState() {
15428
- return {
15429
- initialLabel: state.initialLabel,
15430
- mode: state.mode,
15431
- stepLabel: state.stepLabel,
15432
- stepNumber: state.stepNumber ? { ...state.stepNumber } : void 0,
15433
- detail: state.detail,
15434
- lastChangeAt: state.lastChangeAt,
15435
- terminal: state.terminal ? { ...state.terminal } : void 0
15436
- };
15437
- }
15438
- function applyUpdate(next) {
15439
- if (next.mode !== void 0) state.mode = next.mode;
15440
- if (next.stepLabel !== void 0) state.stepLabel = next.stepLabel;
15441
- if (next.stepNumber !== void 0) state.stepNumber = { ...next.stepNumber };
15442
- if (next.detail !== void 0) state.detail = next.detail;
15443
- state.lastChangeAt = now();
15444
- }
15445
- async function scheduleOrFlush() {
15446
- const elapsed = now() - lastFlushAt;
15447
- if (elapsed >= debounceMs) {
15448
- await doFlush();
15449
- return;
15450
- }
15451
- if (pendingTimer != null) return;
15452
- const remaining = debounceMs - elapsed;
15453
- pendingTimer = setTimer(() => {
15454
- pendingTimer = null;
15455
- void doFlush().catch(() => {
15456
- });
15457
- }, remaining);
15458
- }
15459
- lastFlushAt = now();
15460
- await opts.flush(snapshotState());
15461
- return {
15462
- snapshot: snapshotState,
15463
- async update(next) {
15464
- if (terminal) {
15465
- const peeked = snapshotState();
15466
- Object.assign(peeked, {
15467
- mode: next.mode ?? peeked.mode,
15468
- stepLabel: next.stepLabel ?? peeked.stepLabel,
15469
- stepNumber: next.stepNumber ?? peeked.stepNumber,
15470
- detail: next.detail ?? peeked.detail
15471
- });
15472
- try {
15473
- onPostTerminalUpdate(peeked);
15474
- } catch (err) {
15475
- console.warn(
15476
- "[progress-handle] onPostTerminalUpdate threw \u2014 swallowed to keep update() non-throwing:",
15477
- err
15478
- );
15479
- }
15480
- return;
15481
- }
15482
- applyUpdate(next);
15483
- await scheduleOrFlush();
15484
- },
15485
- async complete(summary) {
15486
- if (terminal) return;
15487
- terminal = true;
15488
- state.terminal = { kind: "completed", message: summary };
15489
- state.lastChangeAt = now();
15490
- await doFlush();
15491
- },
15492
- async fail(reason) {
15493
- if (terminal) return;
15494
- terminal = true;
15495
- state.terminal = { kind: "failed", message: reason };
15496
- state.lastChangeAt = now();
15497
- await doFlush();
15498
- },
15499
- isTerminal: () => terminal
15500
- };
15501
- }
15502
- function defaultPostTerminalWarn(state) {
15503
- console.warn(
15504
- `[progress-handle] update() called after terminal transition (${state.terminal?.kind}) \u2014 ignored.`
15505
- );
15506
- }
15507
-
15508
- // src/progress-tools.ts
15509
- var ProgressToolRegistry = class {
15510
- constructor(opts) {
15511
- this.opts = opts;
15512
- this.toolNames = {
15513
- start: `${opts.prefix}_progress_start`,
15514
- update: `${opts.prefix}_progress_update`,
15515
- complete: `${opts.prefix}_progress_complete`,
15516
- fail: `${opts.prefix}_progress_fail`
15517
- };
15518
- }
15519
- handles = /* @__PURE__ */ new Map();
15520
- toolNames;
15521
- /** Tool definitions to splice into the MCP ListToolsRequest response. */
15522
- getDefinitions() {
15523
- const { surfaceDescription, startArgsSchema } = this.opts;
15524
- const debounceMs = this.opts.debounceMs ?? 1e3;
15525
- const debounceText = debounceMs === 1e3 ? "per second" : `every ${debounceMs}ms`;
15526
- return [
15527
- {
15528
- name: this.toolNames.start,
15529
- description: `Post a "still working" anchor to ${surfaceDescription} and return a progress_id. The anchor message updates in place as you call ${this.toolNames.update} \u2014 operators see one message that ticks forward instead of a flood of new ones. Always end with ${this.toolNames.complete} or ${this.toolNames.fail}; otherwise the anchor lingers as "working" until the stale-state sweep flips it. Opt-in \u2014 only call this for tasks that will take more than a few seconds.`,
15530
- inputSchema: {
15531
- type: "object",
15532
- properties: {
15533
- label: {
15534
- type: "string",
15535
- description: 'Short top-line label for the task (e.g. "diagnose vigil"). Stays constant for the lifetime of this progress anchor.'
15536
- },
15537
- initial_mode: {
15538
- type: "string",
15539
- enum: ["thinking", "working", "waiting"],
15540
- description: 'Initial mode emoji \u2014 defaults to "working".'
15541
- },
15542
- ...startArgsSchema.properties
15543
- },
15544
- required: ["label", ...startArgsSchema.required]
15545
- }
15546
- },
15547
- {
15548
- name: this.toolNames.update,
15549
- description: `Update the progress anchor in place. The state machine debounces calls to one ${surfaceDescription} API call ${debounceText}, so spamming this every step is safe \u2014 only the latest pending state will paint. Any subset of fields can be passed; omitted ones retain their previous value.`,
15550
- inputSchema: {
15551
- type: "object",
15552
- properties: {
15553
- progress_id: { type: "string", description: "The progress_id returned from start." },
15554
- step_label: { type: "string", description: 'Short label for the current step (e.g. "Querying CloudWatch logs").' },
15555
- step_current: { type: "number", description: "1-based step counter \u2014 current." },
15556
- step_total: { type: "number", description: "Total steps if known. Omit for open-ended progress." },
15557
- mode: { type: "string", enum: ["thinking", "working", "waiting"] },
15558
- detail: { type: "string", description: "Free-text detail line under the step label." }
15559
- },
15560
- required: ["progress_id"]
15561
- }
15562
- },
15563
- {
15564
- name: this.toolNames.complete,
15565
- description: `Mark the progress anchor as \u2705 completed. Flushes any pending debounced state before painting the terminal banner \u2014 the operator never sees a stale "Step N" beside the \u2705. After this, the handle is dead; further updates are no-ops.`,
15566
- inputSchema: {
15567
- type: "object",
15568
- properties: {
15569
- progress_id: { type: "string" },
15570
- summary: { type: "string", description: "Optional one-line summary of what was achieved." }
15571
- },
15572
- required: ["progress_id"]
15573
- }
15574
- },
15575
- {
15576
- name: this.toolNames.fail,
15577
- description: `Mark the progress anchor as \u274C failed. Always include a one-sentence reason \u2014 the operator can't tell *why* from emoji alone. Same flush-then-paint behaviour as complete.`,
15578
- inputSchema: {
15579
- type: "object",
15580
- properties: {
15581
- progress_id: { type: "string" },
15582
- reason: { type: "string", description: "One-sentence failure reason." }
15583
- },
15584
- required: ["progress_id", "reason"]
15585
- }
15586
- }
15587
- ];
15588
- }
15589
- /**
15590
- * Dispatch a CallToolRequest. Returns `undefined` if the tool name
15591
- * isn't one of ours (so the caller can keep walking its dispatch
15592
- * chain). Returns a tool result otherwise.
15593
- */
15594
- async handle(name, args) {
15595
- if (name === this.toolNames.start) return this.handleStart(args);
15596
- if (name === this.toolNames.update) return this.handleUpdate(args);
15597
- if (name === this.toolNames.complete) return this.handleComplete(args);
15598
- if (name === this.toolNames.fail) return this.handleFail(args);
15599
- return void 0;
15600
- }
15601
- /** Live count of in-flight handles. Exposed for tests + diagnostics. */
15602
- size() {
15603
- return this.handles.size;
15604
- }
15605
- async handleStart(args) {
15606
- const label = typeof args.label === "string" ? args.label : null;
15607
- if (!label) return errResult(`${this.toolNames.start}: 'label' must be a non-empty string`);
15608
- if ("initial_mode" in args && !isMode(args.initial_mode)) {
15609
- return errResult(
15610
- `${this.toolNames.start}: 'initial_mode' must be one of 'thinking', 'working', or 'waiting'`
15611
- );
15612
- }
15613
- for (const k of this.opts.startArgsSchema.required) {
15614
- if (typeof args[k] !== "string" || args[k].length === 0) {
15615
- return errResult(`${this.toolNames.start}: '${k}' must be a non-empty string`);
15616
- }
15617
- }
15618
- const initialMode = isMode(args.initial_mode) ? args.initial_mode : void 0;
15619
- const progressId = randomUUID();
15620
- try {
15621
- const flush = this.opts.createFlush(args);
15622
- const handle = await startProgressHandle({
15623
- initialLabel: label,
15624
- initialMode,
15625
- flush,
15626
- debounceMs: this.opts.debounceMs ?? 1e3
15627
- });
15628
- this.handles.set(progressId, handle);
15629
- return okResult(JSON.stringify({ progress_id: progressId }));
15630
- } catch (err) {
15631
- return errResult(`${this.toolNames.start} failed: ${err.message ?? String(err)}`);
15632
- }
15633
- }
15634
- async handleUpdate(args) {
15635
- const id = typeof args.progress_id === "string" ? args.progress_id : null;
15636
- if (!id) return errResult(`${this.toolNames.update}: 'progress_id' must be a string`);
15637
- const handle = this.handles.get(id);
15638
- if (!handle) return errResult(`${this.toolNames.update}: unknown progress_id (already terminated, or never started)`);
15639
- if ("mode" in args && !isMode(args.mode)) {
15640
- return errResult(
15641
- `${this.toolNames.update}: 'mode' must be one of 'thinking', 'working', or 'waiting'`
15642
- );
15643
- }
15644
- if (typeof args.step_total === "number" && typeof args.step_current !== "number") {
15645
- return errResult(
15646
- `${this.toolNames.update}: 'step_total' requires 'step_current'`
15647
- );
15648
- }
15649
- const next = {};
15650
- if (typeof args.step_label === "string") next.stepLabel = args.step_label;
15651
- if (typeof args.step_current === "number") {
15652
- next.stepNumber = {
15653
- current: args.step_current,
15654
- ...typeof args.step_total === "number" ? { total: args.step_total } : {}
15655
- };
15656
- }
15657
- if (isMode(args.mode)) next.mode = args.mode;
15658
- if (typeof args.detail === "string") next.detail = args.detail;
15659
- try {
15660
- await handle.update(next);
15661
- return okResult("ok");
15662
- } catch (err) {
15663
- return errResult(`${this.toolNames.update} failed: ${err.message ?? String(err)}`);
15664
- }
15665
- }
15666
- async handleComplete(args) {
15667
- const id = typeof args.progress_id === "string" ? args.progress_id : null;
15668
- if (!id) return errResult(`${this.toolNames.complete}: 'progress_id' must be a string`);
15669
- const handle = this.handles.get(id);
15670
- if (!handle) return errResult(`${this.toolNames.complete}: unknown progress_id`);
15671
- const summary = typeof args.summary === "string" ? args.summary : void 0;
15672
- try {
15673
- await handle.complete(summary);
15674
- this.handles.delete(id);
15675
- return okResult("ok");
15676
- } catch (err) {
15677
- return errResult(`${this.toolNames.complete} failed: ${err.message ?? String(err)}`);
15678
- }
15679
- }
15680
- async handleFail(args) {
15681
- const id = typeof args.progress_id === "string" ? args.progress_id : null;
15682
- if (!id) return errResult(`${this.toolNames.fail}: 'progress_id' must be a string`);
15683
- const reason = typeof args.reason === "string" ? args.reason : null;
15684
- if (!reason) return errResult(`${this.toolNames.fail}: 'reason' must be a non-empty string`);
15685
- const handle = this.handles.get(id);
15686
- if (!handle) return errResult(`${this.toolNames.fail}: unknown progress_id`);
15687
- try {
15688
- await handle.fail(reason);
15689
- this.handles.delete(id);
15690
- return okResult("ok");
15691
- } catch (err) {
15692
- return errResult(`${this.toolNames.fail} failed: ${err.message ?? String(err)}`);
15693
- }
15694
- }
15695
- };
15696
- function okResult(text) {
15697
- return { content: [{ type: "text", text }] };
15698
- }
15699
- function errResult(text) {
15700
- return { content: [{ type: "text", text }], isError: true };
15701
- }
15702
- function isMode(value) {
15703
- return value === "thinking" || value === "working" || value === "waiting";
15704
- }
15705
-
15706
- // src/telegram-progress.ts
15707
- var MODE_EMOJI = {
15708
- thinking: "\u{1F4AD}",
15709
- working: "\u{1F6E0}\uFE0F",
15710
- waiting: "\u23F3"
15711
- };
15712
- var TERMINAL_EMOJI = {
15713
- completed: "\u2705",
15714
- failed: "\u274C"
15715
- };
15716
- function formatWallClock(ms, timeZone) {
15717
- const fmt = new Intl.DateTimeFormat("en-GB", {
15718
- hour: "2-digit",
15719
- minute: "2-digit",
15720
- second: "2-digit",
15721
- hour12: false,
15722
- timeZone,
15723
- timeZoneName: "short"
15724
- });
15725
- const parts = fmt.formatToParts(new Date(ms));
15726
- const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
15727
- return `${get("hour")}:${get("minute")}:${get("second")} ${get("timeZoneName")}`;
15728
- }
15729
- function escapeHtml(s) {
15730
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15731
- }
15732
- function renderProgressHtml(state) {
15733
- const lines = [];
15734
- if (state.terminal) {
15735
- const banner = state.terminal.kind === "completed" ? "Done" : "Failed";
15736
- lines.push(`${TERMINAL_EMOJI[state.terminal.kind]} <b>${banner}</b> \u2014 ${escapeHtml(state.initialLabel)}`);
15737
- if (state.terminal.message) lines.push(escapeHtml(state.terminal.message));
15738
- const verb = state.terminal.kind === "completed" ? "completed" : "failed";
15739
- lines.push(`<i>${verb}: ${formatWallClock(state.lastChangeAt)}</i>`);
15740
- return lines.join("\n");
15741
- }
15742
- lines.push(`${MODE_EMOJI[state.mode]} <b>Working on:</b> ${escapeHtml(state.initialLabel)}`);
15743
- if (state.stepNumber || state.stepLabel) {
15744
- const parts = [];
15745
- if (state.stepNumber) {
15746
- if (state.stepNumber.total != null) {
15747
- parts.push(`<b>Step ${state.stepNumber.current} of ${state.stepNumber.total}</b>`);
15748
- } else {
15749
- parts.push(`<b>Step ${state.stepNumber.current}</b>`);
15750
- }
15751
- }
15752
- if (state.stepLabel) parts.push(escapeHtml(state.stepLabel));
15753
- lines.push(parts.join(": "));
15754
- }
15755
- if (state.detail) lines.push(`<i>${escapeHtml(state.detail)}</i>`);
15756
- lines.push(`<i>last update: ${formatWallClock(state.lastChangeAt)}</i>`);
15757
- return lines.join("\n");
15758
- }
15759
- var TelegramApiError = class extends Error {
15760
- constructor(endpoint, apiError) {
15761
- super(`Telegram ${endpoint} failed: ${apiError}`);
15762
- this.endpoint = endpoint;
15763
- this.apiError = apiError;
15764
- this.name = "TelegramApiError";
15765
- }
15766
- };
15767
- function isMessageNotModified(description) {
15768
- if (!description) return false;
15769
- return /message is not modified/i.test(description);
15770
- }
15771
- function createTelegramProgressFlush(opts) {
15772
- let anchorMessageId = null;
15773
- return async function flush(state) {
15774
- const text = renderProgressHtml(state);
15775
- if (anchorMessageId == null) {
15776
- const body = {
15777
- chat_id: opts.chatId,
15778
- text,
15779
- parse_mode: "HTML",
15780
- disable_notification: true
15781
- };
15782
- if (opts.messageThreadId != null) body.message_thread_id = opts.messageThreadId;
15783
- if (opts.replyToMessageId != null) body.reply_to_message_id = opts.replyToMessageId;
15784
- const res = await opts.callImpl("sendMessage", body);
15785
- if (!res.ok || !res.result?.message_id) {
15786
- throw new TelegramApiError("sendMessage", res.description ?? "missing message_id");
15787
- }
15788
- anchorMessageId = res.result.message_id;
15789
- opts.onAnchorPosted?.(res.result.message_id);
15790
- return;
15791
- }
15792
- try {
15793
- const res = await opts.callImpl("editMessageText", {
15794
- chat_id: opts.chatId,
15795
- message_id: anchorMessageId,
15796
- text,
15797
- parse_mode: "HTML"
15798
- });
15799
- if (!res.ok) {
15800
- if (isMessageNotModified(res.description)) return;
15801
- throw new TelegramApiError("editMessageText", res.description ?? "unknown");
15802
- }
15803
- } catch (err) {
15804
- if (opts.onApiError && err instanceof TelegramApiError) {
15805
- await opts.onApiError(err);
15806
- return;
15807
- }
15808
- throw err;
15809
- }
15810
- };
15811
- }
15812
-
15813
15389
  // src/impersonation.ts
15814
15390
  var ENV_VAR = "AGT_ACT_AS_AGENT_ID";
15815
15391
  var OVERRIDE_ENV_VAR = "ENABLE_IMPERSONATION_CHANNELS";
@@ -15925,13 +15501,109 @@ function readLockHolder(path) {
15925
15501
  }
15926
15502
  }
15927
15503
 
15504
+ // src/ack-reaction.ts
15505
+ import { readdirSync, readFileSync as readFileSync2 } from "fs";
15506
+ import { join as join3 } from "path";
15507
+ var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
15508
+ var ACK_STARTUP_GRACE_MS = 6e4;
15509
+ function decideAckReaction(i) {
15510
+ if (!i.hasTarget) return "none";
15511
+ if (!i.integrationReady) return "undeliverable";
15512
+ if (i.tmux === "dead") return "undeliverable";
15513
+ if (!i.withinStartupGrace && i.claude === "dead") return "undeliverable";
15514
+ const threshold = i.pendingStaleThresholdMs ?? REPLY_WEDGED_THRESHOLD_MS;
15515
+ if (i.oldestPendingAgeMs != null && i.oldestPendingAgeMs > threshold) {
15516
+ return "undeliverable";
15517
+ }
15518
+ return "ack";
15519
+ }
15520
+ function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
15521
+ if (!dir) return null;
15522
+ let names;
15523
+ try {
15524
+ names = readdirSync(dir);
15525
+ } catch {
15526
+ return null;
15527
+ }
15528
+ let oldest = null;
15529
+ for (const name of names) {
15530
+ if (!name.endsWith(".json")) continue;
15531
+ let receivedAt;
15532
+ try {
15533
+ const raw = JSON.parse(readFileSync2(join3(dir, name), "utf-8"));
15534
+ receivedAt = raw.received_at;
15535
+ } catch {
15536
+ continue;
15537
+ }
15538
+ if (!receivedAt) continue;
15539
+ const t = Date.parse(receivedAt);
15540
+ if (Number.isNaN(t)) continue;
15541
+ const age = now - t;
15542
+ if (age < 0) continue;
15543
+ if (oldest == null || age > oldest) oldest = age;
15544
+ }
15545
+ return oldest;
15546
+ }
15547
+
15548
+ // src/session-probe-runtime.ts
15549
+ import { execFileSync } from "child_process";
15550
+ function agentTmuxSessionName(codeName) {
15551
+ return `agt-${codeName}`;
15552
+ }
15553
+ function escapePgrepRegex(value) {
15554
+ return value.replace(/[.[\]{}()*+?^$|\\]/g, "\\$&");
15555
+ }
15556
+ function probeClaudeProcessInTmux(tmuxSession) {
15557
+ const escapedSession = escapePgrepRegex(tmuxSession);
15558
+ const pattern = `(^|[[:space:]])--name ${escapedSession}([[:space:]]|$)`;
15559
+ try {
15560
+ const out = execFileSync("pgrep", ["-f", "--", pattern], {
15561
+ encoding: "utf-8",
15562
+ timeout: 3e3
15563
+ }).trim();
15564
+ return out.length > 0 ? "alive" : "dead";
15565
+ } catch (err) {
15566
+ const e = err;
15567
+ if (e?.code === "ENOENT") return "unknown";
15568
+ return e?.status === 1 ? "dead" : "unknown";
15569
+ }
15570
+ }
15571
+ function probeTmuxSession(tmuxSession) {
15572
+ try {
15573
+ execFileSync("tmux", ["has-session", "-t", tmuxSession], {
15574
+ stdio: "ignore",
15575
+ timeout: 3e3
15576
+ });
15577
+ return "alive";
15578
+ } catch (err) {
15579
+ const e = err;
15580
+ if (e?.code === "ENOENT") return "unknown";
15581
+ return "dead";
15582
+ }
15583
+ }
15584
+ function probeAgentSession(codeName) {
15585
+ const session = agentTmuxSessionName(codeName);
15586
+ const tmux = probeTmuxSession(session);
15587
+ const claude = tmux === "alive" ? probeClaudeProcessInTmux(session) : tmux;
15588
+ return { tmux, claude };
15589
+ }
15590
+ var probeCache = /* @__PURE__ */ new Map();
15591
+ var SESSION_PROBE_TTL_MS = 15e3;
15592
+ function probeAgentSessionCached(codeName, ttlMs = SESSION_PROBE_TTL_MS, now = Date.now()) {
15593
+ const cached2 = probeCache.get(codeName);
15594
+ if (cached2 && now - cached2.at < ttlMs) return cached2.value;
15595
+ const value = probeAgentSession(codeName);
15596
+ probeCache.set(codeName, { at: now, value });
15597
+ return value;
15598
+ }
15599
+
15928
15600
  // src/telegram-channel.ts
15929
15601
  function redactId(id) {
15930
15602
  return createHash("sha256").update(String(id)).digest("hex").slice(0, 8);
15931
15603
  }
15932
15604
  var BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
15933
15605
  var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
15934
- var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join3(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
15606
+ var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join4(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
15935
15607
  var AGT_HOST = process.env.AGT_HOST ?? null;
15936
15608
  var AGT_API_KEY = process.env.AGT_API_KEY ?? null;
15937
15609
  var AGT_AGENT_ID = process.env.AGT_AGENT_ID ?? null;
@@ -16015,9 +15687,9 @@ if (!BOT_TOKEN) {
16015
15687
  var stderrLogStream = null;
16016
15688
  if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
16017
15689
  try {
16018
- const logDir = join3(homedir2(), ".augmented", AGENT_CODE_NAME);
15690
+ const logDir = join4(homedir2(), ".augmented", AGENT_CODE_NAME);
16019
15691
  mkdirSync3(logDir, { recursive: true });
16020
- stderrLogStream = createWriteStream(join3(logDir, "telegram-channel-stderr.log"), {
15692
+ stderrLogStream = createWriteStream(join4(logDir, "telegram-channel-stderr.log"), {
16021
15693
  flags: "a",
16022
15694
  mode: 384
16023
15695
  });
@@ -16098,7 +15770,42 @@ async function setMessageReaction(chatId, messageId, emoji2) {
16098
15770
  );
16099
15771
  }
16100
15772
  }
16101
- var RESTART_FLAGS_DIR = join3(homedir2(), ".augmented", "restart-flags");
15773
+ var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
15774
+ var lastUndeliverableNoticeAt = /* @__PURE__ */ new Map();
15775
+ async function notifyUndeliverable(chatId, messageId) {
15776
+ const key2 = String(chatId);
15777
+ const now = Date.now();
15778
+ const last = lastUndeliverableNoticeAt.get(key2);
15779
+ if (last != null && now - last < UNDELIVERABLE_NOTICE_THROTTLE_MS) return;
15780
+ lastUndeliverableNoticeAt.set(key2, now);
15781
+ try {
15782
+ const resp = await telegramApiCall(
15783
+ "sendMessage",
15784
+ {
15785
+ chat_id: chatId,
15786
+ text: "\u26A0\uFE0F I can't respond right now \u2014 I'll follow up once I'm back.",
15787
+ reply_to_message_id: Number(messageId),
15788
+ allow_sending_without_reply: true
15789
+ },
15790
+ 1e4
15791
+ );
15792
+ if (!resp.ok) {
15793
+ process.stderr.write(
15794
+ `telegram-channel(${AGENT_CODE_NAME}): undeliverable notice failed (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
15795
+ `
15796
+ );
15797
+ }
15798
+ } catch (err) {
15799
+ process.stderr.write(
15800
+ `telegram-channel(${AGENT_CODE_NAME}): undeliverable notice error: ${err.message}
15801
+ `
15802
+ );
15803
+ }
15804
+ }
15805
+ function __resetUndeliverableNoticeThrottle() {
15806
+ lastUndeliverableNoticeAt.clear();
15807
+ }
15808
+ var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
16102
15809
  function buildTelegramHelpMessage(codeName) {
16103
15810
  return [
16104
15811
  `\u{1F916} *Available commands for \`${codeName}\`*`,
@@ -16138,14 +15845,14 @@ async function handleRestartCommand(opts) {
16138
15845
  if (!existsSync2(RESTART_FLAGS_DIR)) {
16139
15846
  mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
16140
15847
  }
16141
- const flagPath = join3(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
15848
+ const flagPath = join4(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
16142
15849
  const flag = {
16143
15850
  codeName: AGENT_CODE_NAME,
16144
15851
  source: "telegram",
16145
15852
  ts: Date.now(),
16146
15853
  reply: { chat_id: opts.chatId, message_id: opts.messageId }
16147
15854
  };
16148
- const tmpPath = `${flagPath}.${process.pid}.${randomUUID2()}.tmp`;
15855
+ const tmpPath = `${flagPath}.${process.pid}.${randomUUID()}.tmp`;
16149
15856
  writeFileSync3(tmpPath, JSON.stringify(flag) + "\n", "utf8");
16150
15857
  renameSync3(tmpPath, flagPath);
16151
15858
  process.stderr.write(
@@ -16254,16 +15961,16 @@ async function classifyRestartCommand(text) {
16254
15961
  if (!ours) return "verification_failed";
16255
15962
  return target === ours ? "act" : "ignore";
16256
15963
  }
16257
- var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join3(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16258
- var PENDING_INBOUND_DIR = AGENT_DIR ? join3(AGENT_DIR, "telegram-pending-inbound") : null;
16259
- var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join3(AGENT_DIR, "telegram-recovery-outbox") : null;
15964
+ var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join4(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
15965
+ var PENDING_INBOUND_DIR = AGENT_DIR ? join4(AGENT_DIR, "telegram-pending-inbound") : null;
15966
+ var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join4(AGENT_DIR, "telegram-recovery-outbox") : null;
16260
15967
  function safeMarkerName(chatId, messageId) {
16261
15968
  const safe = (s) => s.replace(/[^A-Za-z0-9_-]/g, "_");
16262
15969
  return `${safe(chatId)}__${safe(messageId)}.json`;
16263
15970
  }
16264
15971
  function pendingInboundPath(chatId, messageId) {
16265
15972
  if (!PENDING_INBOUND_DIR) return null;
16266
- return join3(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
15973
+ return join4(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
16267
15974
  }
16268
15975
  function writePendingInboundMarker(chatId, messageId, chatType) {
16269
15976
  const path = pendingInboundPath(chatId, messageId);
@@ -16307,10 +16014,10 @@ function nextRetryName(filename) {
16307
16014
  async function processRecoveryOutboxFile(filename) {
16308
16015
  if (!RECOVERY_OUTBOX_DIR) return;
16309
16016
  if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
16310
- const fullPath = join3(RECOVERY_OUTBOX_DIR, filename);
16017
+ const fullPath = join4(RECOVERY_OUTBOX_DIR, filename);
16311
16018
  let payload;
16312
16019
  try {
16313
- const raw = readFileSync2(fullPath, "utf-8");
16020
+ const raw = readFileSync3(fullPath, "utf-8");
16314
16021
  payload = JSON.parse(raw);
16315
16022
  } catch (err) {
16316
16023
  process.stderr.write(
@@ -16373,7 +16080,7 @@ async function processRecoveryOutboxFile(filename) {
16373
16080
  const next = nextRetryName(filename);
16374
16081
  if (next) {
16375
16082
  try {
16376
- renameSync3(fullPath, join3(RECOVERY_OUTBOX_DIR, next.next));
16083
+ renameSync3(fullPath, join4(RECOVERY_OUTBOX_DIR, next.next));
16377
16084
  if (next.attempt >= MAX_RECOVERY_ATTEMPTS) {
16378
16085
  process.stderr.write(
16379
16086
  `telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
@@ -16404,7 +16111,7 @@ function scanRecoveryRetries() {
16404
16111
  if (!RECOVERY_OUTBOX_DIR) return;
16405
16112
  let entries;
16406
16113
  try {
16407
- entries = readdirSync(RECOVERY_OUTBOX_DIR);
16114
+ entries = readdirSync2(RECOVERY_OUTBOX_DIR);
16408
16115
  } catch {
16409
16116
  return;
16410
16117
  }
@@ -16413,7 +16120,7 @@ function scanRecoveryRetries() {
16413
16120
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
16414
16121
  let mtimeMs;
16415
16122
  try {
16416
- mtimeMs = statSync(join3(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
16123
+ mtimeMs = statSync(join4(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
16417
16124
  } catch {
16418
16125
  continue;
16419
16126
  }
@@ -16434,7 +16141,7 @@ function startRecoveryOutboxWatcher() {
16434
16141
  return;
16435
16142
  }
16436
16143
  try {
16437
- for (const f of readdirSync(RECOVERY_OUTBOX_DIR)) {
16144
+ for (const f of readdirSync2(RECOVERY_OUTBOX_DIR)) {
16438
16145
  if (isFirstAttemptOutboxFile(f)) void processRecoveryOutboxFile(f);
16439
16146
  }
16440
16147
  } catch {
@@ -16443,7 +16150,7 @@ function startRecoveryOutboxWatcher() {
16443
16150
  const watcher = watch(RECOVERY_OUTBOX_DIR, (event, filename) => {
16444
16151
  if (event !== "rename" || !filename) return;
16445
16152
  if (!isFirstAttemptOutboxFile(filename)) return;
16446
- if (existsSync2(join3(RECOVERY_OUTBOX_DIR, filename))) {
16153
+ if (existsSync2(join4(RECOVERY_OUTBOX_DIR, filename))) {
16447
16154
  void processRecoveryOutboxFile(filename);
16448
16155
  }
16449
16156
  });
@@ -16467,7 +16174,7 @@ function sweepTelegramStaleMarkersOnBoot() {
16467
16174
  if (!existsSync2(PENDING_INBOUND_DIR)) return;
16468
16175
  let filenames;
16469
16176
  try {
16470
- filenames = readdirSync(PENDING_INBOUND_DIR);
16177
+ filenames = readdirSync2(PENDING_INBOUND_DIR);
16471
16178
  } catch (err) {
16472
16179
  process.stderr.write(
16473
16180
  `telegram-channel(${AGENT_CODE_NAME}): stale-marker readdir failed: ${err.message}
@@ -16480,10 +16187,10 @@ function sweepTelegramStaleMarkersOnBoot() {
16480
16187
  for (const filename of filenames) {
16481
16188
  if (!filename.endsWith(".json")) continue;
16482
16189
  if (filename.endsWith(".tmp")) continue;
16483
- const fullPath = join3(PENDING_INBOUND_DIR, filename);
16190
+ const fullPath = join4(PENDING_INBOUND_DIR, filename);
16484
16191
  let marker;
16485
16192
  try {
16486
- marker = JSON.parse(readFileSync2(fullPath, "utf-8"));
16193
+ marker = JSON.parse(readFileSync3(fullPath, "utf-8"));
16487
16194
  } catch (err) {
16488
16195
  process.stderr.write(
16489
16196
  `telegram-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactId(filename)}: ${err.message}
@@ -16532,7 +16239,7 @@ function clearPendingMessage(chatId, messageId) {
16532
16239
  const prefix = `${safeChatId}__`;
16533
16240
  let filenames;
16534
16241
  try {
16535
- filenames = readdirSync(PENDING_INBOUND_DIR);
16242
+ filenames = readdirSync2(PENDING_INBOUND_DIR);
16536
16243
  } catch {
16537
16244
  return;
16538
16245
  }
@@ -16540,7 +16247,7 @@ function clearPendingMessage(chatId, messageId) {
16540
16247
  if (!filename.startsWith(prefix)) continue;
16541
16248
  if (!filename.endsWith(".json")) continue;
16542
16249
  try {
16543
- unlinkSync3(join3(PENDING_INBOUND_DIR, filename));
16250
+ unlinkSync3(join4(PENDING_INBOUND_DIR, filename));
16544
16251
  } catch {
16545
16252
  }
16546
16253
  }
@@ -16567,46 +16274,12 @@ var mcp = new Server(
16567
16274
  "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.",
16568
16275
  '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.',
16569
16276
  "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.",
16570
- "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}."
16277
+ `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 of YOUR work. 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}. (If a message arrived while you were offline you may see a system-posted "can't respond right now" notice \u2014 that's the runtime, not you; don't repeat or apologise for it.)`
16571
16278
  ].join(" ")
16572
16279
  }
16573
16280
  );
16574
- var TELEGRAM_PROGRESS_TIMEOUT_MS = 5e3;
16575
- var progressRegistry = new ProgressToolRegistry({
16576
- prefix: "telegram",
16577
- surfaceDescription: "a Telegram chat",
16578
- startArgsSchema: {
16579
- properties: {
16580
- chat_id: {
16581
- type: "string",
16582
- description: "Telegram chat id (from the `chat_id` attribute on the inbound <channel> tag). Numeric ids are passed as strings; the API accepts both."
16583
- },
16584
- message_thread_id: {
16585
- type: "string",
16586
- description: "Forum topic id (`message_thread_id`) \u2014 pass this if the inbound arrived in a forum topic so the anchor lands in the same topic. Omit for normal chats."
16587
- },
16588
- reply_to_message_id: {
16589
- type: "string",
16590
- description: "Optional message id to thread the anchor under (typically the inbound message_id from the <channel> tag)."
16591
- }
16592
- },
16593
- required: ["chat_id"]
16594
- },
16595
- createFlush: (args) => {
16596
- const chatId = args.chat_id;
16597
- const threadId = typeof args.message_thread_id === "string" && args.message_thread_id.length > 0 ? Number(args.message_thread_id) : void 0;
16598
- const replyTo = typeof args.reply_to_message_id === "string" && args.reply_to_message_id.length > 0 ? Number(args.reply_to_message_id) : void 0;
16599
- return createTelegramProgressFlush({
16600
- chatId,
16601
- messageThreadId: Number.isFinite(threadId) ? threadId : void 0,
16602
- replyToMessageId: Number.isFinite(replyTo) ? replyTo : void 0,
16603
- callImpl: (method, body) => telegramApiCall(method, body, TELEGRAM_PROGRESS_TIMEOUT_MS)
16604
- });
16605
- }
16606
- });
16607
16281
  mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
16608
16282
  tools: [
16609
- ...progressRegistry.getDefinitions(),
16610
16283
  {
16611
16284
  name: "channel_request_input",
16612
16285
  description: "Generic Yes/No or multi-choice picker. Renders as a Telegram inline keyboard; the tool blocks until the user taps an option (or the timeout elapses) and returns the resolved value. Use this when you need a structured pick rather than freeform text \u2014 e.g. confirming a draft action or disambiguating an ambiguous fork.",
@@ -16698,7 +16371,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
16698
16371
  },
16699
16372
  {
16700
16373
  name: "telegram.react",
16701
- description: "Add an emoji reaction to a Telegram message. Use sparingly \u2014 prefer a text reply via telegram.reply. Reaction taxonomy: \u{1F44D} or \u{1F389} = action completed successfully. NEVER react to signal failure \u2014 users can't tell why it failed from a reaction. On failure, call telegram.reply with one sentence explaining what went wrong instead. \u{1F440} is already auto-applied on inbound; do not duplicate. Only free-tier emoji reactions are available to bots (Premium-only emoji fail silently). Pass an empty string or omit emoji to clear the bot's reaction on that message.",
16374
+ description: `Add an emoji reaction to a Telegram message. Use sparingly \u2014 prefer a text reply via telegram.reply. Reaction taxonomy: \u{1F44D} or \u{1F389} = action completed successfully. NEVER react to signal failure of your work \u2014 users can't tell why it failed from a reaction. On failure, call telegram.reply with one sentence explaining what went wrong instead. \u{1F440} is already auto-applied on inbound; do not duplicate. Only free-tier emoji reactions are available to bots (Premium-only emoji fail silently). Pass an empty string or omit emoji to clear the bot's reaction on that message. (Note: when a message arrives while the agent is offline/wedged, the runtime \u2014 not you \u2014 posts a brief "can't respond right now" notice; you never need to add a failure reaction for that.)`,
16702
16375
  inputSchema: {
16703
16376
  type: "object",
16704
16377
  properties: {
@@ -16725,8 +16398,6 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
16725
16398
  if (isImpersonating() && !channelsEnabledOverride() && TELEGRAM_EGRESS_TOOLS.has(name)) {
16726
16399
  return buildImpersonationRefusal(name);
16727
16400
  }
16728
- const progressResult = await progressRegistry.handle(name, args ?? {});
16729
- if (progressResult !== void 0) return progressResult;
16730
16401
  if (name === "channel_request_input") {
16731
16402
  return handleChannelRequestInput(args ?? {});
16732
16403
  }
@@ -16926,12 +16597,12 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
16926
16597
  function channelRequestInputAvailable() {
16927
16598
  return Boolean(AGT_HOST && AGT_API_KEY && AGT_AGENT_ID);
16928
16599
  }
16929
- function errResult2(text) {
16600
+ function errResult(text) {
16930
16601
  return { content: [{ type: "text", text }], isError: true };
16931
16602
  }
16932
16603
  async function handleChannelRequestInput(args) {
16933
16604
  if (!channelRequestInputAvailable()) {
16934
- return errResult2(
16605
+ return errResult(
16935
16606
  "channel_request_input is disabled \u2014 missing AGT_HOST / AGT_API_KEY / AGT_AGENT_ID env wiring."
16936
16607
  );
16937
16608
  }
@@ -16939,12 +16610,12 @@ async function handleChannelRequestInput(args) {
16939
16610
  const { apiCall: apiCall2, waitForResolution: waitForResolution2, validateAskUserOptions: validateAskUserOptions2 } = runtime;
16940
16611
  const { thread_id, prompt_text, options, schema, request_id, timeout_seconds } = args;
16941
16612
  if (typeof thread_id !== "string" || !thread_id) {
16942
- return errResult2(
16613
+ return errResult(
16943
16614
  "thread_id is required (Telegram shape: `<chat_id>` or `<chat_id>:<reply_to_message_id>`)"
16944
16615
  );
16945
16616
  }
16946
16617
  if (typeof prompt_text !== "string" || !prompt_text) {
16947
- return errResult2("prompt_text is required");
16618
+ return errResult("prompt_text is required");
16948
16619
  }
16949
16620
  const optsValidation = validateAskUserOptions2(options, {
16950
16621
  maxOptions: 4,
@@ -16952,27 +16623,27 @@ async function handleChannelRequestInput(args) {
16952
16623
  maxValueChars: 2e3
16953
16624
  });
16954
16625
  if (!optsValidation.ok) {
16955
- return errResult2(
16626
+ return errResult(
16956
16627
  `Invalid options: ${optsValidation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`
16957
16628
  );
16958
16629
  }
16959
16630
  const optionsArr = options;
16960
16631
  if (optionsArr.length < 2) {
16961
- return errResult2(
16632
+ return errResult(
16962
16633
  "options must contain at least 2 entries (this is a picker, not a notification)"
16963
16634
  );
16964
16635
  }
16965
16636
  if (!schema || typeof schema.type !== "string") {
16966
- return errResult2("schema is required with shape { type: 'yes_no' | 'choice' }");
16637
+ return errResult("schema is required with shape { type: 'yes_no' | 'choice' }");
16967
16638
  }
16968
16639
  if (schema.type !== "yes_no" && schema.type !== "choice") {
16969
- return errResult2("schema.type must be 'yes_no' or 'choice'");
16640
+ return errResult("schema.type must be 'yes_no' or 'choice'");
16970
16641
  }
16971
16642
  if (schema.type === "yes_no" && optionsArr.length !== 2) {
16972
- return errResult2("schema.type='yes_no' requires exactly 2 options");
16643
+ return errResult("schema.type='yes_no' requires exactly 2 options");
16973
16644
  }
16974
16645
  if (request_id !== void 0 && (typeof request_id !== "string" || !request_id)) {
16975
- return errResult2("request_id must be a non-empty string when provided");
16646
+ return errResult("request_id must be a non-empty string when provided");
16976
16647
  }
16977
16648
  const requestedTimeout = typeof timeout_seconds === "number" ? timeout_seconds : 300;
16978
16649
  const timeoutSec = Math.max(10, Math.min(3600, requestedTimeout));
@@ -16994,10 +16665,10 @@ async function handleChannelRequestInput(args) {
16994
16665
  dispatchPayload = await res.json().catch(() => ({}));
16995
16666
  if (!res.ok || !dispatchPayload.ok || !dispatchPayload.callback_id) {
16996
16667
  const reason = dispatchPayload.reason ?? `http_${res.status}`;
16997
- return errResult2(`channel_request_input dispatch failed: ${reason}`);
16668
+ return errResult(`channel_request_input dispatch failed: ${reason}`);
16998
16669
  }
16999
16670
  } catch (err) {
17000
- return errResult2(
16671
+ return errResult(
17001
16672
  `channel_request_input dispatch failed: ${err.message}`
17002
16673
  );
17003
16674
  }
@@ -17419,7 +17090,20 @@ async function pollLoop() {
17419
17090
  }
17420
17091
  }
17421
17092
  const messageId = String(msg.message_id);
17422
- void setMessageReaction(chatId, messageId, ACK_EMOJI);
17093
+ const ackProbe = process.env.TMUX && AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
17094
+ const ackDecision = decideAckReaction({
17095
+ hasTarget: Boolean(chatId && messageId),
17096
+ integrationReady: Boolean(BOT_TOKEN),
17097
+ tmux: ackProbe.tmux,
17098
+ claude: ackProbe.claude,
17099
+ withinStartupGrace: process.uptime() * 1e3 < ACK_STARTUP_GRACE_MS,
17100
+ oldestPendingAgeMs: oldestPendingMarkerAgeMs(PENDING_INBOUND_DIR)
17101
+ });
17102
+ if (ackDecision === "ack") {
17103
+ void setMessageReaction(chatId, messageId, ACK_EMOJI);
17104
+ } else if (ackDecision === "undeliverable") {
17105
+ void notifyUndeliverable(chatId, messageId);
17106
+ }
17423
17107
  trackPendingMessage(chatId, messageId, msg.chat.type);
17424
17108
  const fileMeta = [];
17425
17109
  for (const attachment of classifiedAttachments) {
@@ -17546,3 +17230,6 @@ pollLoop().catch((err) => {
17546
17230
  );
17547
17231
  process.exit(1);
17548
17232
  });
17233
+ export {
17234
+ __resetUndeliverableNoticeThrottle
17235
+ };