@integrity-labs/agt-cli 0.27.92 → 0.27.94

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 CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  success,
29
29
  table,
30
30
  warn
31
- } from "../chunk-FXXCD523.js";
31
+ } from "../chunk-2ITGJXXM.js";
32
32
  import {
33
33
  CHANNEL_REGISTRY,
34
34
  DEPLOYMENT_TEMPLATES,
@@ -4934,7 +4934,7 @@ import { execFileSync, execSync } from "child_process";
4934
4934
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4935
4935
  import chalk18 from "chalk";
4936
4936
  import ora16 from "ora";
4937
- var cliVersion = true ? "0.27.92" : "dev";
4937
+ var cliVersion = true ? "0.27.94" : "dev";
4938
4938
  async function fetchLatestVersion() {
4939
4939
  const host2 = getHost();
4940
4940
  if (!host2) return null;
@@ -5857,7 +5857,7 @@ function handleError(err) {
5857
5857
  }
5858
5858
 
5859
5859
  // src/bin/agt.ts
5860
- var cliVersion2 = true ? "0.27.92" : "dev";
5860
+ var cliVersion2 = true ? "0.27.94" : "dev";
5861
5861
  var program = new Command();
5862
5862
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
5863
5863
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -7578,4 +7578,4 @@ export {
7578
7578
  managerInstallSystemUnitCommand,
7579
7579
  managerUninstallSystemUnitCommand
7580
7580
  };
7581
- //# sourceMappingURL=chunk-FXXCD523.js.map
7581
+ //# sourceMappingURL=chunk-2ITGJXXM.js.map
@@ -16,7 +16,7 @@ import {
16
16
  provisionStopHook,
17
17
  requireHost,
18
18
  safeWriteJsonAtomic
19
- } from "../chunk-FXXCD523.js";
19
+ } from "../chunk-2ITGJXXM.js";
20
20
  import {
21
21
  getProjectDir as getProjectDir2,
22
22
  getReadyTasks,
@@ -3996,7 +3996,7 @@ var cachedMaintenanceWindow = null;
3996
3996
  var lastVersionCheckAt = 0;
3997
3997
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
3998
3998
  var lastResponsivenessProbeAt = 0;
3999
- var agtCliVersion = true ? "0.27.92" : "dev";
3999
+ var agtCliVersion = true ? "0.27.94" : "dev";
4000
4000
  function resolveBrewPath(execFileSync4) {
4001
4001
  try {
4002
4002
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -6986,13 +6986,10 @@ __export(slack_block_kit_runtime_exports, {
6986
6986
  DEFAULT_ASK_USER_LIMITS: () => DEFAULT_ASK_USER_LIMITS,
6987
6987
  apiCall: () => apiCall,
6988
6988
  buildAskUserBlocks: () => buildAskUserBlocks,
6989
- buildRatingActionsBlock: () => buildRatingActionsBlock,
6990
6989
  createPendingInteraction: () => createPendingInteraction,
6991
6990
  decodeActionId: () => decodeActionId,
6992
- defaultRatingLabels: () => defaultRatingLabels,
6993
6991
  encodeActionId: () => encodeActionId,
6994
6992
  generateOptionToken: () => generateOptionToken,
6995
- ratingValuesForScale: () => ratingValuesForScale,
6996
6993
  recordSlackDelivery: () => recordSlackDelivery,
6997
6994
  resolveInteractive: () => resolveInteractive,
6998
6995
  updatePendingInteractionMessageTs: () => updatePendingInteractionMessageTs,
@@ -7159,22 +7156,6 @@ function buildAskUserBlocks(opts) {
7159
7156
  }
7160
7157
  ];
7161
7158
  }
7162
- function ratingValuesForScale(scale) {
7163
- return scale === "1-5" ? [1, 2, 3, 4, 5] : [-1, 0, 1];
7164
- }
7165
- function defaultRatingLabels(scale) {
7166
- return scale === "1-5" ? ["1", "2", "3", "4", "5"] : ["\u{1F44E}", "\u{1F914}", "\u{1F44D}"];
7167
- }
7168
- function buildRatingActionsBlock(opts) {
7169
- return {
7170
- type: "actions",
7171
- elements: opts.options.map(({ label, token }) => ({
7172
- type: "button",
7173
- text: { type: "plain_text", text: label },
7174
- action_id: encodeActionId(opts.callbackId, token)
7175
- }))
7176
- };
7177
- }
7178
7159
  async function resolveInteractive(cfg, input) {
7179
7160
  const res = await apiCall(cfg, "POST", "/host/interactive/resolve", {
7180
7161
  agent_id: cfg.agentId,
@@ -17212,7 +17193,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
17212
17193
  ...blockKitToolsAvailable() ? [
17213
17194
  {
17214
17195
  name: "slack.send_structured",
17215
- description: 'Send a Slack message built from Block Kit blocks (structured layout: headers, sections, dividers, context, action buttons). Use when plain text isn\'t enough \u2014 status reports, confirmations with multiple fields, calls-to-action with buttons. Pass a `text` fallback for push notifications. Returns { message_ts, permalink }. Does NOT block waiting for user interactions; for that use slack.ask_user. Pass `interactive: { type: "rate_run", run_id }` to append a 1-5 rating button row that records the score against this scheduled-task run (ENG-4572).',
17196
+ description: "Send a Slack message built from Block Kit blocks (structured layout: headers, sections, dividers, context, action buttons). Use when plain text isn't enough \u2014 status reports, confirmations with multiple fields, calls-to-action with buttons. Pass a `text` fallback for push notifications. Returns { message_ts, permalink }. Does NOT block waiting for user interactions; for that use slack.ask_user.",
17216
17197
  inputSchema: {
17217
17198
  type: "object",
17218
17199
  properties: {
@@ -17222,18 +17203,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
17222
17203
  description: "Array of Block Kit blocks. Supported block types: header, section, divider, context, actions. Supported interactive elements (inside actions blocks): button. Hard limits: 50 blocks per message, 5 elements per actions block, 3000 chars per section text, 75 chars per button label."
17223
17204
  },
17224
17205
  text: { type: "string", description: "Plain-text fallback for push notifications and unfurls. Required." },
17225
- thread_ts: { type: "string", description: "Thread timestamp for threaded replies (optional)" },
17226
- interactive: {
17227
- type: "object",
17228
- description: `Optional interactive affordance appended to the message. Currently supports type="rate_run" \u2014 appends a 1-5 button row that updates this run's rating when tapped. Use after a scheduled-task delivery so users can rate the firing.`,
17229
- properties: {
17230
- type: { type: "string", enum: ["rate_run"], description: 'Affordance kind. Only "rate_run" is supported today.' },
17231
- run_id: { type: "string", description: "The runs.run_id this rating belongs to (returned by /host/runs/start)." },
17232
- scale: { type: "string", enum: ["1-5", "sentiment"], description: 'Rating scale. Default "1-5" (1..5 buttons). "sentiment" emits \u{1F44E}/\u{1F914}/\u{1F44D} mapping to -1/0/+1 \u2014 reserved for future Telegram parity.' },
17233
- expires_in_seconds: { type: "number", description: "How long the rating row stays tappable. Default 86400 (24h). The pending row times out after this; later taps are ignored." }
17234
- },
17235
- required: ["type", "run_id"]
17236
- }
17206
+ thread_ts: { type: "string", description: "Thread timestamp for threaded replies (optional)" }
17237
17207
  },
17238
17208
  required: ["channel", "blocks", "text"]
17239
17209
  }
@@ -17908,7 +17878,7 @@ async function handleSendStructured(args) {
17908
17878
  }
17909
17879
  const runtime = await Promise.resolve().then(() => (init_slack_block_kit_runtime(), slack_block_kit_runtime_exports));
17910
17880
  const { validateSlackBlocks: validateSlackBlocks2 } = runtime;
17911
- const { channel, blocks, text, thread_ts, interactive } = args;
17881
+ const { channel, blocks, text, thread_ts } = args;
17912
17882
  if (typeof channel !== "string" || !channel) {
17913
17883
  return errResult("channel is required");
17914
17884
  }
@@ -17920,97 +17890,16 @@ async function handleSendStructured(args) {
17920
17890
  if (!validation.ok) {
17921
17891
  return errResult(`Invalid blocks: ${validation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
17922
17892
  }
17923
- let effectiveBlocks = blocks;
17924
- let ratingCallbackId = null;
17925
- let ratingExpiresAt = null;
17926
- let ratingTokenised = [];
17927
- if (interactive) {
17928
- if (interactive.type !== "rate_run") {
17929
- return errResult(`Unsupported interactive.type '${interactive.type}'. Supported: rate_run.`);
17930
- }
17931
- if (typeof interactive.run_id !== "string" || !interactive.run_id) {
17932
- return errResult("interactive.run_id is required when interactive is set");
17933
- }
17934
- if (!interactiveHostAvailable()) {
17935
- return errResult(
17936
- "interactive=rate_run requires Block Kit plus host MCP wiring (AGT_HOST, AGT_API_KEY, AGT_AGENT_ID)."
17937
- );
17938
- }
17939
- const scale = interactive.scale === "sentiment" ? "sentiment" : "1-5";
17940
- const requestedTtl = typeof interactive.expires_in_seconds === "number" ? interactive.expires_in_seconds : 86400;
17941
- const ttl = Math.max(60, Math.min(7 * 86400, requestedTtl));
17942
- const { randomUUID: rndUUID } = await import("crypto");
17943
- ratingCallbackId = rndUUID();
17944
- ratingExpiresAt = new Date(Date.now() + ttl * 1e3);
17945
- const values = runtime.ratingValuesForScale(scale);
17946
- const labels = runtime.defaultRatingLabels(scale);
17947
- ratingTokenised = values.map((v, i) => ({
17948
- token: runtime.generateOptionToken(),
17949
- value: String(v),
17950
- label: labels[i] ?? String(v)
17951
- }));
17952
- const ratingBlock = runtime.buildRatingActionsBlock({
17953
- callbackId: ratingCallbackId,
17954
- options: ratingTokenised.map(({ label, token }) => ({
17955
- // The numeric value is carried by token → options mapping in the
17956
- // pending_interaction row, not by the button itself, so this
17957
- // cast-back to RatingOption only needs label + token + a placeholder.
17958
- value: 0,
17959
- label,
17960
- token
17961
- }))
17962
- });
17963
- effectiveBlocks = [...blocks, ratingBlock];
17964
- const finalValidation = runtime.validateSlackBlocks(effectiveBlocks);
17965
- if (!finalValidation.ok) {
17966
- return errResult(
17967
- `Invalid blocks (after appending rating row): ${finalValidation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`
17968
- );
17969
- }
17970
- const cfg = {
17971
- apiHost: AGT_HOST,
17972
- apiKey: AGT_API_KEY,
17973
- agentId: AGT_AGENT_ID
17974
- };
17975
- try {
17976
- await runtime.createPendingInteraction(cfg, {
17977
- callbackId: ratingCallbackId,
17978
- channelId: channel,
17979
- messageTs: "",
17980
- threadTs: thread_ts,
17981
- options: ratingTokenised,
17982
- expiresAt: ratingExpiresAt,
17983
- kind: "rate_run",
17984
- kindData: { run_id: interactive.run_id, scale }
17985
- });
17986
- } catch (err) {
17987
- return errResult(`Failed to register rating row: ${err.message}`);
17988
- }
17989
- }
17990
17893
  const result = await postSlackMessageWithTs({
17991
17894
  channel,
17992
- blocks: effectiveBlocks,
17895
+ blocks,
17993
17896
  text,
17994
17897
  ...thread_ts ? { thread_ts } : {}
17995
17898
  });
17996
17899
  if (!result.ok || !result.ts) {
17997
17900
  return errResult(`Slack chat.postMessage failed: ${result.error ?? "unknown"}`);
17998
17901
  }
17999
- if (ratingCallbackId) {
18000
- try {
18001
- await runtime.updatePendingInteractionMessageTs(
18002
- { apiHost: AGT_HOST, apiKey: AGT_API_KEY, agentId: AGT_AGENT_ID },
18003
- ratingCallbackId,
18004
- result.ts
18005
- );
18006
- } catch (err) {
18007
- process.stderr.write(
18008
- `slack-channel(${AGENT_CODE_NAME}): updatePendingInteractionMessageTs failed for rating ${hashId(ratingCallbackId)}: ${err.message}
18009
- `
18010
- );
18011
- }
18012
- }
18013
- if (interactiveHostAvailable() && !ratingCallbackId) {
17902
+ if (interactiveHostAvailable()) {
18014
17903
  const cfg = {
18015
17904
  apiHost: AGT_HOST,
18016
17905
  apiKey: AGT_API_KEY,
@@ -18040,8 +17929,7 @@ async function handleSendStructured(args) {
18040
17929
  type: "text",
18041
17930
  text: JSON.stringify({
18042
17931
  message_ts: result.ts,
18043
- permalink: permalink ?? "",
18044
- ...ratingCallbackId ? { rating_callback_id: ratingCallbackId } : {}
17932
+ permalink: permalink ?? ""
18045
17933
  })
18046
17934
  }
18047
17935
  ]
@@ -6986,13 +6986,10 @@ __export(slack_block_kit_runtime_exports, {
6986
6986
  DEFAULT_ASK_USER_LIMITS: () => DEFAULT_ASK_USER_LIMITS,
6987
6987
  apiCall: () => apiCall,
6988
6988
  buildAskUserBlocks: () => buildAskUserBlocks,
6989
- buildRatingActionsBlock: () => buildRatingActionsBlock,
6990
6989
  createPendingInteraction: () => createPendingInteraction,
6991
6990
  decodeActionId: () => decodeActionId,
6992
- defaultRatingLabels: () => defaultRatingLabels,
6993
6991
  encodeActionId: () => encodeActionId,
6994
6992
  generateOptionToken: () => generateOptionToken,
6995
- ratingValuesForScale: () => ratingValuesForScale,
6996
6993
  recordSlackDelivery: () => recordSlackDelivery,
6997
6994
  resolveInteractive: () => resolveInteractive,
6998
6995
  updatePendingInteractionMessageTs: () => updatePendingInteractionMessageTs,
@@ -7159,22 +7156,6 @@ function buildAskUserBlocks(opts) {
7159
7156
  }
7160
7157
  ];
7161
7158
  }
7162
- function ratingValuesForScale(scale) {
7163
- return scale === "1-5" ? [1, 2, 3, 4, 5] : [-1, 0, 1];
7164
- }
7165
- function defaultRatingLabels(scale) {
7166
- return scale === "1-5" ? ["1", "2", "3", "4", "5"] : ["\u{1F44E}", "\u{1F914}", "\u{1F44D}"];
7167
- }
7168
- function buildRatingActionsBlock(opts) {
7169
- return {
7170
- type: "actions",
7171
- elements: opts.options.map(({ label, token }) => ({
7172
- type: "button",
7173
- text: { type: "plain_text", text: label },
7174
- action_id: encodeActionId(opts.callbackId, token)
7175
- }))
7176
- };
7177
- }
7178
7159
  async function resolveInteractive(cfg, input) {
7179
7160
  const res = await apiCall(cfg, "POST", "/host/interactive/resolve", {
7180
7161
  agent_id: cfg.agentId,
@@ -14545,23 +14526,33 @@ var DEFAULT_THROTTLE = {
14545
14526
  windowMs: 12e4,
14546
14527
  ringSize: 8
14547
14528
  };
14548
- function decideReplyThrottle(input) {
14529
+ var DEFAULT_REPLY_QUEUE_DEPTH = 2;
14530
+ function planReplyDelivery(input) {
14549
14531
  const cfg = input.config ?? DEFAULT_THROTTLE;
14532
+ const maxDepth = input.maxQueueDepth ?? DEFAULT_REPLY_QUEUE_DEPTH;
14550
14533
  const cutoff = input.now - cfg.windowMs;
14551
- const inWindow = input.recentReplyTimestamps.filter((t) => t >= cutoff);
14552
- if (inWindow.length >= cfg.threshold) {
14553
- return {
14554
- allow: false,
14555
- recentCount: inWindow.length,
14556
- reason: "throttle_threshold_exceeded"
14557
- };
14534
+ const inWindow = input.recentReplyTimestamps.filter((t) => t >= cutoff).sort((a, b) => a - b);
14535
+ const occupancy = inWindow.length + input.queuedCount;
14536
+ if (occupancy < cfg.threshold) {
14537
+ return { action: "send", recentCount: inWindow.length, reason: "under_threshold" };
14538
+ }
14539
+ if (input.queuedCount >= maxDepth) {
14540
+ return { action: "refuse", recentCount: inWindow.length, reason: "queue_full" };
14558
14541
  }
14542
+ const idx = Math.min(input.queuedCount, inWindow.length - 1);
14543
+ const opensAt = (inWindow[idx] ?? input.now) + cfg.windowMs;
14559
14544
  return {
14560
- allow: true,
14545
+ action: "queue",
14546
+ deliverAtMs: Math.max(opensAt, input.now),
14561
14547
  recentCount: inWindow.length,
14562
- reason: "under_threshold"
14548
+ reason: "queued_for_slot"
14563
14549
  };
14564
14550
  }
14551
+ function relaxedThrottleConfig(cfg, env = process.env, envVar = "TELEGRAM_PRIVATE_REPLY_THROTTLE_COUNT") {
14552
+ const raw = Number(env[envVar]);
14553
+ const threshold = Number.isFinite(raw) && raw > 0 ? raw : cfg.threshold * 2;
14554
+ return { ...cfg, threshold, ringSize: Math.max(cfg.ringSize, threshold + 2) };
14555
+ }
14565
14556
  var buffers = /* @__PURE__ */ new Map();
14566
14557
  function key(channelId, threadKey) {
14567
14558
  return `${channelId}${threadKey}`;
@@ -16320,6 +16311,54 @@ var orphanSweepTimer = setInterval(() => {
16320
16311
  sweepTelegramStaleMarkers(sessionAlive ? channelOrphanMarkerMs() : STALE_MARKER_MS);
16321
16312
  }, orphanSweepIntervalMs());
16322
16313
  orphanSweepTimer.unref?.();
16314
+ var queuedReplyCounts = /* @__PURE__ */ new Map();
16315
+ function _resetQueuedReplyCountsForTests() {
16316
+ queuedReplyCounts.clear();
16317
+ }
16318
+ async function deliverQueuedReply(p) {
16319
+ const remaining = (queuedReplyCounts.get(p.chatId) ?? 1) - 1;
16320
+ if (remaining <= 0) queuedReplyCounts.delete(p.chatId);
16321
+ else queuedReplyCounts.set(p.chatId, remaining);
16322
+ try {
16323
+ const killed = await isThreadKilled({
16324
+ channelType: "telegram",
16325
+ channelId: p.chatId,
16326
+ threadTs: "",
16327
+ agtHost: AGT_HOST,
16328
+ agtApiKey: AGT_API_KEY
16329
+ });
16330
+ if (killed) {
16331
+ process.stderr.write(
16332
+ `telegram-channel(${AGENT_CODE_NAME}): reply_queue_dropped reason=killed chat=${redactId(p.chatId)}
16333
+ `
16334
+ );
16335
+ return;
16336
+ }
16337
+ const body = { chat_id: p.chatId, text: p.text };
16338
+ if (p.replyToMessageId) body.reply_to_message_id = Number(p.replyToMessageId);
16339
+ const data = await telegramApiCall("sendMessage", body, 15e3);
16340
+ if (!data.ok) {
16341
+ process.stderr.write(
16342
+ `telegram-channel(${AGENT_CODE_NAME}): reply_queue_failed chat=${redactId(p.chatId)} error=${data.description ?? "unknown"}
16343
+ `
16344
+ );
16345
+ return;
16346
+ }
16347
+ recordReply(p.chatId, "", Date.now(), p.throttleCfg);
16348
+ if (p.isReplyTool) {
16349
+ clearPendingMessage(p.chatId, p.replyToMessageId);
16350
+ }
16351
+ process.stderr.write(
16352
+ `telegram-channel(${AGENT_CODE_NAME}): reply_queue_delivered chat=${redactId(p.chatId)}
16353
+ `
16354
+ );
16355
+ } catch (err) {
16356
+ process.stderr.write(
16357
+ `telegram-channel(${AGENT_CODE_NAME}): reply_queue_failed chat=${redactId(p.chatId)} error=${err.message}
16358
+ `
16359
+ );
16360
+ }
16361
+ }
16323
16362
  function clearPendingMessage(chatId, messageId) {
16324
16363
  if (messageId) {
16325
16364
  clearPendingInboundMarker(chatId, messageId);
@@ -16553,28 +16592,56 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
16553
16592
  isError: true
16554
16593
  };
16555
16594
  }
16556
- const tgThrottleCfg = configFromEnv();
16595
+ const isPrivateChat = !chat_id.startsWith("-");
16596
+ const tgThrottleCfg = isPrivateChat ? relaxedThrottleConfig(configFromEnv()) : configFromEnv();
16557
16597
  const tgThrottleNow = Date.now();
16558
- const tgThrottleDecision = decideReplyThrottle({
16598
+ const tgPlan = planReplyDelivery({
16559
16599
  recentReplyTimestamps: getRecentReplies(chat_id, "", tgThrottleNow, tgThrottleCfg),
16600
+ queuedCount: queuedReplyCounts.get(chat_id) ?? 0,
16560
16601
  now: tgThrottleNow,
16561
16602
  config: tgThrottleCfg
16562
16603
  });
16563
- if (!tgThrottleDecision.allow) {
16604
+ if (tgPlan.action === "refuse") {
16564
16605
  process.stderr.write(
16565
- `telegram-channel(${AGENT_CODE_NAME}): reply_throttled chat=${redactId(chat_id)} count=${tgThrottleDecision.recentCount} window_ms=${tgThrottleCfg.windowMs} threshold=${tgThrottleCfg.threshold}
16606
+ `telegram-channel(${AGENT_CODE_NAME}): reply_throttled chat=${redactId(chat_id)} count=${tgPlan.recentCount} queued=${queuedReplyCounts.get(chat_id) ?? 0} window_ms=${tgThrottleCfg.windowMs} threshold=${tgThrottleCfg.threshold}
16566
16607
  `
16567
16608
  );
16568
16609
  return {
16569
16610
  content: [
16570
16611
  {
16571
16612
  type: "text",
16572
- text: `Reply throttled: ${tgThrottleDecision.recentCount} replies from this agent to this chat within the last ${Math.round(tgThrottleCfg.windowMs / 1e3)}s (threshold ${tgThrottleCfg.threshold}). The chat is paused for this agent until a human posts in it. Stop attempting to reply.`
16613
+ text: `Reply throttled: ${tgPlan.recentCount} replies from this agent to this chat within the last ${Math.round(tgThrottleCfg.windowMs / 1e3)}s (threshold ${tgThrottleCfg.threshold}) and the delivery queue is full. The chat is paused for this agent until a human posts in it. Stop attempting to reply.`
16573
16614
  }
16574
16615
  ],
16575
16616
  isError: true
16576
16617
  };
16577
16618
  }
16619
+ if (tgPlan.action === "queue" && tgPlan.deliverAtMs !== void 0) {
16620
+ const delayMs = Math.max(0, tgPlan.deliverAtMs - tgThrottleNow);
16621
+ queuedReplyCounts.set(chat_id, (queuedReplyCounts.get(chat_id) ?? 0) + 1);
16622
+ process.stderr.write(
16623
+ `telegram-channel(${AGENT_CODE_NAME}): reply_queued chat=${redactId(chat_id)} delay_ms=${delayMs} count=${tgPlan.recentCount} threshold=${tgThrottleCfg.threshold}
16624
+ `
16625
+ );
16626
+ const timer = setTimeout(() => {
16627
+ void deliverQueuedReply({
16628
+ chatId: chat_id,
16629
+ text,
16630
+ replyToMessageId: reply_to_message_id,
16631
+ isReplyTool: name === "telegram.reply",
16632
+ throttleCfg: tgThrottleCfg
16633
+ });
16634
+ }, delayMs);
16635
+ timer.unref();
16636
+ return {
16637
+ content: [
16638
+ {
16639
+ type: "text",
16640
+ text: `Reply queued, not yet sent: the reply-rate window is full (${tgPlan.recentCount} recent sends, threshold ${tgThrottleCfg.threshold}). It will be delivered automatically in ~${Math.ceil(delayMs / 1e3)}s. Do not re-send this message; avoid further sends to this chat until it delivers.`
16641
+ }
16642
+ ]
16643
+ };
16644
+ }
16578
16645
  try {
16579
16646
  const body = { chat_id, text };
16580
16647
  if (reply_to_message_id) body.reply_to_message_id = Number(reply_to_message_id);
@@ -17403,5 +17470,6 @@ pollLoop().catch((err) => {
17403
17470
  });
17404
17471
  export {
17405
17472
  __resetBackOnlineNoticeThrottle,
17406
- __resetUndeliverableNoticeThrottle
17473
+ __resetUndeliverableNoticeThrottle,
17474
+ _resetQueuedReplyCountsForTests
17407
17475
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.92",
3
+ "version": "0.27.94",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {