@integrity-labs/agt-cli 0.28.80 → 0.28.82

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
@@ -37,7 +37,7 @@ import {
37
37
  success,
38
38
  table,
39
39
  warn
40
- } from "../chunk-JIJ5BGO5.js";
40
+ } from "../chunk-WSRT4VSX.js";
41
41
  import {
42
42
  CHANNEL_REGISTRY,
43
43
  DEPLOYMENT_TEMPLATES,
@@ -4777,7 +4777,7 @@ import { execFileSync, execSync } from "child_process";
4777
4777
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4778
4778
  import chalk18 from "chalk";
4779
4779
  import ora16 from "ora";
4780
- var cliVersion = true ? "0.28.80" : "dev";
4780
+ var cliVersion = true ? "0.28.82" : "dev";
4781
4781
  async function fetchLatestVersion() {
4782
4782
  const host2 = getHost();
4783
4783
  if (!host2) return null;
@@ -5791,7 +5791,7 @@ function handleError(err) {
5791
5791
  }
5792
5792
 
5793
5793
  // src/bin/agt.ts
5794
- var cliVersion2 = true ? "0.28.80" : "dev";
5794
+ var cliVersion2 = true ? "0.28.82" : "dev";
5795
5795
  var program = new Command();
5796
5796
  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");
5797
5797
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -7516,7 +7516,7 @@ function requireHost() {
7516
7516
  }
7517
7517
 
7518
7518
  // src/lib/api-client.ts
7519
- var agtCliVersion = true ? "0.28.80" : "dev";
7519
+ var agtCliVersion = true ? "0.28.82" : "dev";
7520
7520
  var lastConfigHash = null;
7521
7521
  function setConfigHash(hash) {
7522
7522
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -8813,4 +8813,4 @@ export {
8813
8813
  managerInstallSystemUnitCommand,
8814
8814
  managerUninstallSystemUnitCommand
8815
8815
  };
8816
- //# sourceMappingURL=chunk-JIJ5BGO5.js.map
8816
+ //# sourceMappingURL=chunk-WSRT4VSX.js.map
@@ -28,7 +28,7 @@ import {
28
28
  requireHost,
29
29
  safeWriteJsonAtomic,
30
30
  setConfigHash
31
- } from "../chunk-JIJ5BGO5.js";
31
+ } from "../chunk-WSRT4VSX.js";
32
32
  import {
33
33
  getProjectDir as getProjectDir2,
34
34
  getReadyTasks,
@@ -6803,7 +6803,7 @@ var cachedMaintenanceWindow = null;
6803
6803
  var lastVersionCheckAt = 0;
6804
6804
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
6805
6805
  var lastResponsivenessProbeAt = 0;
6806
- var agtCliVersion = true ? "0.28.80" : "dev";
6806
+ var agtCliVersion = true ? "0.28.82" : "dev";
6807
6807
  function resolveBrewPath(execFileSync4) {
6808
6808
  try {
6809
6809
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -18073,15 +18073,6 @@ async function postEphemeralViaResponseUrl(responseUrl, text, logTag) {
18073
18073
  }
18074
18074
  async function forwardOnboardingSlashCommand(opts) {
18075
18075
  const { verb, path, userId, responseUrl, codeName } = opts;
18076
- const allowed = getEffectiveAllowedUsers();
18077
- if (allowed.size > 0 && (!userId || !allowed.has(userId))) {
18078
- await postEphemeralViaResponseUrl(
18079
- responseUrl,
18080
- `\u{1F6AB} \`${verb}\` denied \u2014 your Slack user is not in the allowlist for \`${codeName}\`.`,
18081
- codeName
18082
- );
18083
- return;
18084
- }
18085
18076
  if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
18086
18077
  await postEphemeralViaResponseUrl(
18087
18078
  responseUrl,
@@ -18097,9 +18088,12 @@ async function forwardOnboardingSlashCommand(opts) {
18097
18088
  apiKey: AGT_API_KEY,
18098
18089
  agentId: AGT_AGENT_ID
18099
18090
  };
18100
- const res = await apiCall2(cfg, "POST", `/host${path}`, { agent_id: AGT_AGENT_ID });
18091
+ const res = await apiCall2(cfg, "POST", `/host${path}`, {
18092
+ agent_id: AGT_AGENT_ID,
18093
+ initiator: { channel: "slack", user_id: userId }
18094
+ });
18101
18095
  const data = await res.json();
18102
- const text = data.ok ? `\u{1F504} ${data.message ?? "Onboarding updated."}` : `:x: \`${verb}\` failed${data.error ? `: ${data.error}` : "."}`;
18096
+ const text = data.ok ? `\u{1F504} ${data.message ?? "Onboarding updated."}` : data.message ?? `:x: \`${verb}\` failed${data.error ? `: ${data.error}` : "."}`;
18103
18097
  await postEphemeralViaResponseUrl(responseUrl, text, codeName);
18104
18098
  } catch (err) {
18105
18099
  process.stderr.write(
@@ -16232,6 +16232,12 @@ function markerArrivalMs(fullPath) {
16232
16232
  }
16233
16233
  }
16234
16234
  function clearAllTelegramPendingMarkersForChat(pendingDir, chatId, clearMarkerFile, cutoffMs = Number.POSITIVE_INFINITY) {
16235
+ return applyToChatMarkers(pendingDir, chatId, clearMarkerFile, cutoffMs);
16236
+ }
16237
+ function markSeenAllTelegramPendingMarkersForChat(pendingDir, chatId, markSeen, cutoffMs = Number.POSITIVE_INFINITY) {
16238
+ return applyToChatMarkers(pendingDir, chatId, markSeen, cutoffMs);
16239
+ }
16240
+ function applyToChatMarkers(pendingDir, chatId, op, cutoffMs) {
16235
16241
  const prefix = `${chatId.replace(/[^A-Za-z0-9_-]/g, "_")}__`;
16236
16242
  const bounded = Number.isFinite(cutoffMs);
16237
16243
  let filenames;
@@ -16240,16 +16246,69 @@ function clearAllTelegramPendingMarkersForChat(pendingDir, chatId, clearMarkerFi
16240
16246
  } catch {
16241
16247
  return 0;
16242
16248
  }
16243
- let cleared = 0;
16249
+ let applied = 0;
16244
16250
  for (const filename of filenames) {
16245
16251
  if (!filename.startsWith(prefix)) continue;
16246
16252
  if (!filename.endsWith(".json")) continue;
16247
16253
  const fullPath = join4(pendingDir, filename);
16248
16254
  if (bounded && markerArrivalMs(fullPath) > cutoffMs) continue;
16249
- clearMarkerFile(fullPath);
16250
- cleared++;
16255
+ op(fullPath);
16256
+ applied++;
16257
+ }
16258
+ return applied;
16259
+ }
16260
+
16261
+ // src/channel-progress.ts
16262
+ function channelLiveProgressEnabled() {
16263
+ return resolveHostBooleanFlag({
16264
+ key: "channel-live-progress",
16265
+ envVar: "AGT_CHANNEL_PROGRESS_ENABLED",
16266
+ defaultValue: false
16267
+ });
16268
+ }
16269
+ function decideProgressAction(input) {
16270
+ const { now, heartbeat, target, tracked, freshnessMs, minPendingMs } = input;
16271
+ const fresh = heartbeat != null && now - heartbeat.updatedAtMs <= freshnessMs;
16272
+ const active = target != null && fresh;
16273
+ if (!active) {
16274
+ if (tracked) {
16275
+ return { type: "delete", channel: tracked.channel, threadTs: tracked.threadTs, ts: tracked.ts };
16276
+ }
16277
+ return { type: "none" };
16278
+ }
16279
+ const t = target;
16280
+ const hb = heartbeat;
16281
+ if (tracked && (tracked.threadTs !== t.threadTs || tracked.channel !== t.channel)) {
16282
+ return { type: "delete", channel: tracked.channel, threadTs: tracked.threadTs, ts: tracked.ts };
16283
+ }
16284
+ if (!tracked) {
16285
+ if (now - t.receivedAtMs < minPendingMs) return { type: "none" };
16286
+ return { type: "post", channel: t.channel, threadTs: t.threadTs, step: hb.step };
16287
+ }
16288
+ if (hb.step !== tracked.lastStep) {
16289
+ return { type: "update", channel: t.channel, threadTs: t.threadTs, ts: tracked.ts, step: hb.step };
16290
+ }
16291
+ return { type: "none" };
16292
+ }
16293
+ function progressHeartbeatFreshMs() {
16294
+ const raw = parseInt(process.env.AGT_CHANNEL_PROGRESS_FRESH_MS ?? "", 10);
16295
+ return Number.isFinite(raw) && raw > 0 ? raw : 45e3;
16296
+ }
16297
+ function progressMinPendingMs() {
16298
+ const raw = parseInt(process.env.AGT_CHANNEL_PROGRESS_MIN_PENDING_MS ?? "", 10);
16299
+ return Number.isFinite(raw) && raw > 0 ? raw : 12e3;
16300
+ }
16301
+ function parseProgressHeartbeat(raw) {
16302
+ if (!raw) return null;
16303
+ try {
16304
+ const obj = JSON.parse(raw);
16305
+ const step = typeof obj.step === "string" ? obj.step.trim() : "";
16306
+ const updatedAtMs = typeof obj.updated_at_ms === "number" ? obj.updated_at_ms : NaN;
16307
+ if (!step || !Number.isFinite(updatedAtMs)) return null;
16308
+ return { step, updatedAtMs };
16309
+ } catch {
16310
+ return null;
16251
16311
  }
16252
- return cleared;
16253
16312
  }
16254
16313
 
16255
16314
  // src/mcp-spawn-lock.ts
@@ -17131,7 +17190,8 @@ async function handleOnboardingCommand(opts) {
17131
17190
  agentId: AGT_AGENT_ID
17132
17191
  };
17133
17192
  const res = await apiCall2(cfg, "POST", `/host/onboarding/${opts.mode}`, {
17134
- agent_id: AGT_AGENT_ID
17193
+ agent_id: AGT_AGENT_ID,
17194
+ initiator: { channel: "telegram", user_id: opts.senderId }
17135
17195
  });
17136
17196
  const data = await res.json();
17137
17197
  process.stderr.write(
@@ -17139,7 +17199,7 @@ async function handleOnboardingCommand(opts) {
17139
17199
  `
17140
17200
  );
17141
17201
  await reply(
17142
- data.ok ? `\u{1F504} ${data.message ?? "Onboarding updated."}` : `\u274C ${verb} failed${data.error ? `: ${data.error}` : "."}`
17202
+ data.ok ? `\u{1F504} ${data.message ?? "Onboarding updated."}` : data.message ?? `\u274C ${verb} failed${data.error ? `: ${data.error}` : "."}`
17143
17203
  );
17144
17204
  } catch (err) {
17145
17205
  process.stderr.write(
@@ -17666,6 +17726,33 @@ function clearTelegramMarkerFileWithHeal(fullPath) {
17666
17726
  } catch {
17667
17727
  }
17668
17728
  }
17729
+ function markTelegramMarkerSeenInPlace(fullPath) {
17730
+ let marker;
17731
+ try {
17732
+ marker = JSON.parse(readFileSync8(fullPath, "utf-8"));
17733
+ } catch {
17734
+ return;
17735
+ }
17736
+ if (marker.seen_at) return;
17737
+ marker.seen_at = (/* @__PURE__ */ new Date()).toISOString();
17738
+ if (marker.undeliverable) delete marker.undeliverable;
17739
+ rewriteTelegramMarkerInPlace(fullPath, marker);
17740
+ }
17741
+ function markTelegramMarkerSeenWithHeal(fullPath) {
17742
+ let marker = null;
17743
+ try {
17744
+ marker = JSON.parse(readFileSync8(fullPath, "utf-8"));
17745
+ } catch {
17746
+ return;
17747
+ }
17748
+ if (decideRecoveryHeal({
17749
+ wasUndeliverable: marker.undeliverable === true,
17750
+ hasTarget: Boolean(marker.chat_id)
17751
+ }) === "heal") {
17752
+ notifyBackOnline(marker.chat_id);
17753
+ }
17754
+ markTelegramMarkerSeenInPlace(fullPath);
17755
+ }
17669
17756
  function readPendingInboundMarker(chatId, messageId) {
17670
17757
  const path = pendingInboundPath(chatId, messageId);
17671
17758
  if (!path || !existsSync5(path)) return null;
@@ -17911,6 +17998,143 @@ var orphanSweepTimer = setInterval(() => {
17911
17998
  checkWatchdogGiveUpNotice();
17912
17999
  }, orphanSweepIntervalMs());
17913
18000
  orphanSweepTimer.unref?.();
18001
+ var TELEGRAM_PROGRESS_HEARTBEAT_PATH = AGENT_DIR ? join7(AGENT_DIR, "channel-progress-heartbeat.json") : null;
18002
+ var telegramTrackedProgress = null;
18003
+ var telegramProgressTickRunning = false;
18004
+ function readTelegramProgressHeartbeat() {
18005
+ if (!TELEGRAM_PROGRESS_HEARTBEAT_PATH || !existsSync5(TELEGRAM_PROGRESS_HEARTBEAT_PATH)) return null;
18006
+ try {
18007
+ return parseProgressHeartbeat(readFileSync8(TELEGRAM_PROGRESS_HEARTBEAT_PATH, "utf-8"));
18008
+ } catch {
18009
+ return null;
18010
+ }
18011
+ }
18012
+ function findTelegramProgressTarget() {
18013
+ if (!PENDING_INBOUND_DIR || !existsSync5(PENDING_INBOUND_DIR)) return null;
18014
+ let best = null;
18015
+ let bestMs = Infinity;
18016
+ try {
18017
+ for (const name of readdirSync3(PENDING_INBOUND_DIR)) {
18018
+ if (!name.endsWith(".json")) continue;
18019
+ let m;
18020
+ try {
18021
+ m = JSON.parse(readFileSync8(join7(PENDING_INBOUND_DIR, name), "utf-8"));
18022
+ } catch {
18023
+ continue;
18024
+ }
18025
+ if (!m.chat_id) continue;
18026
+ if (m.undeliverable === true) continue;
18027
+ const ms = Date.parse(m.received_at ?? "");
18028
+ if (!Number.isFinite(ms)) continue;
18029
+ if (ms < bestMs) {
18030
+ bestMs = ms;
18031
+ best = { channel: m.chat_id, threadTs: m.chat_id, receivedAtMs: ms };
18032
+ }
18033
+ }
18034
+ } catch {
18035
+ return null;
18036
+ }
18037
+ return best;
18038
+ }
18039
+ var TELEGRAM_PROGRESS_PREFIX = "\u23F3";
18040
+ function telegramProgressText(step) {
18041
+ return `${TELEGRAM_PROGRESS_PREFIX} Working\u2026 \xB7 ${step}`;
18042
+ }
18043
+ async function postTelegramProgress(chatId, step) {
18044
+ if (!BOT_TOKEN) return null;
18045
+ try {
18046
+ const data = await telegramApiCall(
18047
+ "sendMessage",
18048
+ { chat_id: chatId, text: telegramProgressText(step), disable_notification: true },
18049
+ 8e3
18050
+ );
18051
+ return data.ok && data.result?.message_id != null ? String(data.result.message_id) : null;
18052
+ } catch {
18053
+ return null;
18054
+ }
18055
+ }
18056
+ async function updateTelegramProgress(chatId, messageId, step) {
18057
+ if (!BOT_TOKEN) return false;
18058
+ try {
18059
+ const data = await telegramApiCall(
18060
+ "editMessageText",
18061
+ { chat_id: chatId, message_id: Number(messageId), text: telegramProgressText(step) },
18062
+ 8e3
18063
+ );
18064
+ return data.ok === true || /not modified/i.test(data.description ?? "");
18065
+ } catch {
18066
+ return false;
18067
+ }
18068
+ }
18069
+ async function deleteTelegramProgress(chatId, messageId) {
18070
+ if (!BOT_TOKEN) return false;
18071
+ try {
18072
+ const data = await telegramApiCall(
18073
+ "deleteMessage",
18074
+ { chat_id: chatId, message_id: Number(messageId) },
18075
+ 8e3
18076
+ );
18077
+ return data.ok === true || /message to delete not found|message can't be deleted/i.test(data.description ?? "");
18078
+ } catch {
18079
+ return false;
18080
+ }
18081
+ }
18082
+ async function telegramProgressTick() {
18083
+ if (telegramProgressTickRunning) return;
18084
+ telegramProgressTickRunning = true;
18085
+ try {
18086
+ if (!channelLiveProgressEnabled()) {
18087
+ if (telegramTrackedProgress) {
18088
+ if (await deleteTelegramProgress(telegramTrackedProgress.channel, telegramTrackedProgress.ts)) {
18089
+ telegramTrackedProgress = null;
18090
+ }
18091
+ }
18092
+ return;
18093
+ }
18094
+ const action = decideProgressAction({
18095
+ now: Date.now(),
18096
+ heartbeat: readTelegramProgressHeartbeat(),
18097
+ target: findTelegramProgressTarget(),
18098
+ tracked: telegramTrackedProgress,
18099
+ freshnessMs: progressHeartbeatFreshMs(),
18100
+ minPendingMs: progressMinPendingMs()
18101
+ });
18102
+ switch (action.type) {
18103
+ case "post": {
18104
+ const id = await postTelegramProgress(action.channel, action.step);
18105
+ if (id) telegramTrackedProgress = { channel: action.channel, threadTs: action.threadTs, ts: id, lastStep: action.step };
18106
+ break;
18107
+ }
18108
+ case "update": {
18109
+ if (await updateTelegramProgress(action.channel, action.ts, action.step)) {
18110
+ if (telegramTrackedProgress) telegramTrackedProgress.lastStep = action.step;
18111
+ }
18112
+ break;
18113
+ }
18114
+ case "delete": {
18115
+ if (await deleteTelegramProgress(action.channel, action.ts)) {
18116
+ telegramTrackedProgress = null;
18117
+ }
18118
+ break;
18119
+ }
18120
+ case "none":
18121
+ break;
18122
+ }
18123
+ } catch {
18124
+ } finally {
18125
+ telegramProgressTickRunning = false;
18126
+ }
18127
+ }
18128
+ function telegramProgressPollMs() {
18129
+ const raw = parseInt(process.env.AGT_CHANNEL_PROGRESS_POLL_MS ?? "", 10);
18130
+ return Number.isFinite(raw) && raw >= 2e3 ? raw : 8e3;
18131
+ }
18132
+ if (BOT_TOKEN && PENDING_INBOUND_DIR) {
18133
+ const telegramProgressTimer = setInterval(() => {
18134
+ void telegramProgressTick();
18135
+ }, telegramProgressPollMs());
18136
+ telegramProgressTimer.unref?.();
18137
+ }
17914
18138
  var lastGiveUpHandledAtMs = null;
17915
18139
  function listPendingInboundChatIds() {
17916
18140
  if (!PENDING_INBOUND_DIR || !existsSync5(PENDING_INBOUND_DIR)) return [];
@@ -17922,6 +18146,7 @@ function listPendingInboundChatIds() {
17922
18146
  const marker = JSON.parse(
17923
18147
  readFileSync8(join7(PENDING_INBOUND_DIR, name), "utf8")
17924
18148
  );
18149
+ if (typeof marker.seen_at === "string" && marker.seen_at) continue;
17925
18150
  if (typeof marker.chat_id === "string" && marker.chat_id) chats.add(marker.chat_id);
17926
18151
  } catch {
17927
18152
  }
@@ -18044,9 +18269,18 @@ function clearPendingMessage(chatId, cutoffMs = Number.POSITIVE_INFINITY) {
18044
18269
  cutoffMs
18045
18270
  );
18046
18271
  }
18272
+ function markSeenPendingMessage(chatId, cutoffMs = Number.POSITIVE_INFINITY) {
18273
+ if (!PENDING_INBOUND_DIR || !existsSync5(PENDING_INBOUND_DIR)) return;
18274
+ markSeenAllTelegramPendingMarkersForChat(
18275
+ PENDING_INBOUND_DIR,
18276
+ chatId,
18277
+ markTelegramMarkerSeenWithHeal,
18278
+ cutoffMs
18279
+ );
18280
+ }
18047
18281
  function noteThreadActivity(chatId, cutoffMs = Number.POSITIVE_INFINITY) {
18048
18282
  if (!chatId) return;
18049
- clearPendingMessage(chatId, cutoffMs);
18283
+ markSeenPendingMessage(chatId, cutoffMs);
18050
18284
  }
18051
18285
  var SKIP_REACTION_ON = channelSkipReactionEnabled() && TELEGRAM_SKIP_REACTION.length > 0;
18052
18286
  var mcp = new Server(
@@ -18062,7 +18296,7 @@ var mcp = new Server(
18062
18296
  instructions: [
18063
18297
  // Highest-priority lines first — Claude Code truncates this string at
18064
18298
  // 2048 chars, so anything appended late silently disappears.
18065
- "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.",
18299
+ "CRITICAL: every response to a Telegram <channel> tag MUST go through telegram.reply with the chat_id from the tag \u2014 typed text never reaches the user. Slow work: interim ack (telegram.reply interim:true), then your FINAL answer as a separate telegram.reply (interim omitted). An ack is not the answer.",
18066
18300
  '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.',
18067
18301
  "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.",
18068
18302
  '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.',
@@ -18125,6 +18359,10 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
18125
18359
  reply_to_message_id: {
18126
18360
  type: "string",
18127
18361
  description: "Optional Telegram message_id to quote-reply to"
18362
+ },
18363
+ interim: {
18364
+ type: "boolean",
18365
+ description: 'Set true ONLY when this is an interim acknowledgement (e.g. "On it \u2014 checking now\u2026") and the real answer is still coming. The system keeps tracking this chat as awaiting your final reply. Your eventual substantive answer MUST be a separate telegram.reply with interim omitted/false \u2014 that is what marks the request complete. Omit (default false) for any reply that fully answers the user.'
18128
18366
  }
18129
18367
  },
18130
18368
  required: ["chat_id", "text"]
@@ -18196,7 +18434,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
18196
18434
  return handleChannelRequestInput(args ?? {});
18197
18435
  }
18198
18436
  if (name === "telegram.reply" || name === "telegram.send_message") {
18199
- const { chat_id, text, reply_to_message_id } = args;
18437
+ const { chat_id, text, reply_to_message_id, interim } = args;
18200
18438
  if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chat_id)) {
18201
18439
  return {
18202
18440
  content: [{ type: "text", text: `Chat ${chat_id} is not in TELEGRAM_ALLOWED_CHATS` }],
@@ -18322,7 +18560,8 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
18322
18560
  }
18323
18561
  recordReply(chat_id, "", tgThrottleNow, tgThrottleCfg);
18324
18562
  if (name === "telegram.reply") {
18325
- clearPendingMessage(chat_id, drainCutoffMs);
18563
+ if (interim) markSeenPendingMessage(chat_id, drainCutoffMs);
18564
+ else clearPendingMessage(chat_id, drainCutoffMs);
18326
18565
  }
18327
18566
  return { content: [{ type: "text", text: "sent" }] };
18328
18567
  } catch (err) {
@@ -18972,7 +19211,10 @@ async function pollLoop() {
18972
19211
  await handleOnboardingCommand({
18973
19212
  chatId,
18974
19213
  messageId: String(msg.message_id),
18975
- mode: access.command === "onboard" ? "reset" : "resume"
19214
+ mode: access.command === "onboard" ? "reset" : "resume",
19215
+ // ENG-6578: the manager gate compares the SENDER's user-id (not the
19216
+ // chat id — they differ in groups) to the resolved manager principal.
19217
+ senderId: msg.from?.id != null ? String(msg.from.id) : void 0
18976
19218
  });
18977
19219
  }
18978
19220
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.80",
3
+ "version": "0.28.82",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {