@integrity-labs/agt-cli 0.28.77 → 0.28.79

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.
@@ -14618,6 +14618,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
14618
14618
  try {
14619
14619
  const raw = JSON.parse(readFileSync2(join2(dir, name), "utf-8"));
14620
14620
  if (raw.discretionary === true) continue;
14621
+ if (typeof raw.seen_at === "string" && raw.seen_at) continue;
14621
14622
  receivedAt = raw.received_at;
14622
14623
  } catch {
14623
14624
  continue;
@@ -15261,34 +15262,36 @@ var defaultClearMarkerFile = (fullPath) => {
15261
15262
  } catch {
15262
15263
  }
15263
15264
  };
15264
- function clearAllSlackPendingMarkersForThread(dir, channel, threadTs, clear = defaultClearMarkerFile) {
15265
+ function applyToMatchingMarkers(dir, prefix, suffix, op) {
15265
15266
  if (!dir) return 0;
15266
- const prefix = `${sanitizeMarkerSegment(channel)}__${sanitizeMarkerSegment(threadTs)}__`;
15267
- let cleared = 0;
15267
+ let applied = 0;
15268
15268
  try {
15269
15269
  for (const f of readdirSync2(dir)) {
15270
- if (!f.startsWith(prefix) || !f.endsWith(".json")) continue;
15271
- clear(join5(dir, f));
15272
- cleared += 1;
15270
+ if (!f.startsWith(prefix) || !f.endsWith(suffix)) continue;
15271
+ op(join5(dir, f));
15272
+ applied += 1;
15273
15273
  }
15274
15274
  } catch {
15275
15275
  }
15276
- return cleared;
15276
+ return applied;
15277
+ }
15278
+ function clearAllSlackPendingMarkersForThread(dir, channel, threadTs, clear = defaultClearMarkerFile) {
15279
+ const prefix = `${sanitizeMarkerSegment(channel)}__${sanitizeMarkerSegment(threadTs)}__`;
15280
+ return applyToMatchingMarkers(dir, prefix, ".json", clear);
15281
+ }
15282
+ function markSeenAllSlackPendingMarkersForThread(dir, channel, threadTs, markSeen) {
15283
+ const prefix = `${sanitizeMarkerSegment(channel)}__${sanitizeMarkerSegment(threadTs)}__`;
15284
+ return applyToMatchingMarkers(dir, prefix, ".json", markSeen);
15277
15285
  }
15278
15286
  function clearSlackPendingMarkerByMessageTs(dir, channel, messageTs, clear = defaultClearMarkerFile) {
15279
- if (!dir) return 0;
15280
15287
  const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
15281
15288
  const messageSuffix = `__${sanitizeMarkerSegment(messageTs)}.json`;
15282
- let cleared = 0;
15283
- try {
15284
- for (const f of readdirSync2(dir)) {
15285
- if (!f.startsWith(channelPrefix) || !f.endsWith(messageSuffix)) continue;
15286
- clear(join5(dir, f));
15287
- cleared += 1;
15288
- }
15289
- } catch {
15290
- }
15291
- return cleared;
15289
+ return applyToMatchingMarkers(dir, channelPrefix, messageSuffix, clear);
15290
+ }
15291
+ function markSeenSlackPendingMarkerByMessageTs(dir, channel, messageTs, markSeen) {
15292
+ const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
15293
+ const messageSuffix = `__${sanitizeMarkerSegment(messageTs)}.json`;
15294
+ return applyToMatchingMarkers(dir, channelPrefix, messageSuffix, markSeen);
15292
15295
  }
15293
15296
  function clearOldestSlackPendingMarkerInChannel(dir, channel, clear = defaultClearMarkerFile) {
15294
15297
  if (!dir) return null;
@@ -15312,6 +15315,59 @@ function clearOldestSlackPendingMarkerInChannel(dir, channel, clear = defaultCle
15312
15315
  }
15313
15316
  }
15314
15317
 
15318
+ // src/channel-progress.ts
15319
+ function channelLiveProgressEnabled() {
15320
+ return resolveHostBooleanFlag({
15321
+ key: "channel-live-progress",
15322
+ envVar: "AGT_CHANNEL_PROGRESS_ENABLED",
15323
+ defaultValue: false
15324
+ });
15325
+ }
15326
+ function decideProgressAction(input) {
15327
+ const { now, heartbeat, target, tracked, freshnessMs, minPendingMs } = input;
15328
+ const fresh = heartbeat != null && now - heartbeat.updatedAtMs <= freshnessMs;
15329
+ const active = target != null && fresh;
15330
+ if (!active) {
15331
+ if (tracked) {
15332
+ return { type: "delete", channel: tracked.channel, threadTs: tracked.threadTs, ts: tracked.ts };
15333
+ }
15334
+ return { type: "none" };
15335
+ }
15336
+ const t = target;
15337
+ const hb = heartbeat;
15338
+ if (tracked && (tracked.threadTs !== t.threadTs || tracked.channel !== t.channel)) {
15339
+ return { type: "delete", channel: tracked.channel, threadTs: tracked.threadTs, ts: tracked.ts };
15340
+ }
15341
+ if (!tracked) {
15342
+ if (now - t.receivedAtMs < minPendingMs) return { type: "none" };
15343
+ return { type: "post", channel: t.channel, threadTs: t.threadTs, step: hb.step };
15344
+ }
15345
+ if (hb.step !== tracked.lastStep) {
15346
+ return { type: "update", channel: t.channel, threadTs: t.threadTs, ts: tracked.ts, step: hb.step };
15347
+ }
15348
+ return { type: "none" };
15349
+ }
15350
+ function progressHeartbeatFreshMs() {
15351
+ const raw = parseInt(process.env.AGT_CHANNEL_PROGRESS_FRESH_MS ?? "", 10);
15352
+ return Number.isFinite(raw) && raw > 0 ? raw : 45e3;
15353
+ }
15354
+ function progressMinPendingMs() {
15355
+ const raw = parseInt(process.env.AGT_CHANNEL_PROGRESS_MIN_PENDING_MS ?? "", 10);
15356
+ return Number.isFinite(raw) && raw > 0 ? raw : 12e3;
15357
+ }
15358
+ function parseProgressHeartbeat(raw) {
15359
+ if (!raw) return null;
15360
+ try {
15361
+ const obj = JSON.parse(raw);
15362
+ const step = typeof obj.step === "string" ? obj.step.trim() : "";
15363
+ const updatedAtMs = typeof obj.updated_at_ms === "number" ? obj.updated_at_ms : NaN;
15364
+ if (!step || !Number.isFinite(updatedAtMs)) return null;
15365
+ return { step, updatedAtMs };
15366
+ } catch {
15367
+ return null;
15368
+ }
15369
+ }
15370
+
15315
15371
  // src/slack-reply-threading.ts
15316
15372
  function isSlackDmChannel(channel) {
15317
15373
  return typeof channel === "string" && channel.startsWith("D");
@@ -16953,6 +17009,18 @@ function rewriteSlackMarkerInPlace(path, marker) {
16953
17009
  }
16954
17010
  }
16955
17011
  }
17012
+ function markSlackMarkerSeenInPlace(fullPath) {
17013
+ let marker;
17014
+ try {
17015
+ marker = JSON.parse(readFileSync9(fullPath, "utf-8"));
17016
+ } catch {
17017
+ return;
17018
+ }
17019
+ if (marker.seen_at) return;
17020
+ marker.seen_at = (/* @__PURE__ */ new Date()).toISOString();
17021
+ if (marker.undeliverable) delete marker.undeliverable;
17022
+ rewriteSlackMarkerInPlace(fullPath, marker);
17023
+ }
16956
17024
  function attachSlackReplayPayload(channel, threadTs, messageTs, payload) {
16957
17025
  const path = slackPendingInboundPath(channel, threadTs, messageTs);
16958
17026
  if (!path) return;
@@ -17111,6 +17179,21 @@ function clearSlackMarkerFileWithHeal(fullPath) {
17111
17179
  } catch {
17112
17180
  }
17113
17181
  }
17182
+ function markSlackMarkerSeenWithHeal(fullPath) {
17183
+ let marker = null;
17184
+ try {
17185
+ marker = JSON.parse(readFileSync9(fullPath, "utf-8"));
17186
+ } catch {
17187
+ return;
17188
+ }
17189
+ if (decideRecoveryHeal({
17190
+ wasUndeliverable: marker.undeliverable === true,
17191
+ hasTarget: Boolean(marker.channel && marker.message_ts)
17192
+ }) === "heal") {
17193
+ healSlackUndeliverable(marker.channel, marker.message_ts);
17194
+ }
17195
+ markSlackMarkerSeenInPlace(fullPath);
17196
+ }
17114
17197
  function clearAllSlackPendingMarkersForThread2(channel, threadTs) {
17115
17198
  clearAllSlackPendingMarkersForThread(
17116
17199
  SLACK_PENDING_INBOUND_DIR,
@@ -17134,6 +17217,22 @@ function clearOldestSlackPendingMarkerInChannel2(channel) {
17134
17217
  clearSlackMarkerFileWithHeal
17135
17218
  );
17136
17219
  }
17220
+ function markSeenAllSlackPendingMarkersForThread2(channel, threadTs) {
17221
+ markSeenAllSlackPendingMarkersForThread(
17222
+ SLACK_PENDING_INBOUND_DIR,
17223
+ channel,
17224
+ threadTs,
17225
+ markSlackMarkerSeenWithHeal
17226
+ );
17227
+ }
17228
+ function markSeenSlackPendingMarkerByMessageTs2(channel, messageTs) {
17229
+ markSeenSlackPendingMarkerByMessageTs(
17230
+ SLACK_PENDING_INBOUND_DIR,
17231
+ channel,
17232
+ messageTs,
17233
+ markSlackMarkerSeenWithHeal
17234
+ );
17235
+ }
17137
17236
  function slackNextRetryName(filename) {
17138
17237
  const match = filename.match(/^(.*?)(?:\.retry-(\d+))?\.json$/);
17139
17238
  if (!match) return null;
@@ -17380,6 +17479,160 @@ var slackOrphanSweepTimer = setInterval(() => {
17380
17479
  checkSlackWatchdogGiveUpNotice();
17381
17480
  }, orphanSweepIntervalMs());
17382
17481
  slackOrphanSweepTimer.unref?.();
17482
+ var SLACK_PROGRESS_HEARTBEAT_PATH = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "channel-progress-heartbeat.json") : null;
17483
+ var slackTrackedProgress = null;
17484
+ var slackProgressTickRunning = false;
17485
+ function readSlackProgressHeartbeat() {
17486
+ if (!SLACK_PROGRESS_HEARTBEAT_PATH || !existsSync7(SLACK_PROGRESS_HEARTBEAT_PATH)) return null;
17487
+ try {
17488
+ return parseProgressHeartbeat(readFileSync9(SLACK_PROGRESS_HEARTBEAT_PATH, "utf-8"));
17489
+ } catch {
17490
+ return null;
17491
+ }
17492
+ }
17493
+ function findSlackProgressTarget() {
17494
+ if (!SLACK_PENDING_INBOUND_DIR || !existsSync7(SLACK_PENDING_INBOUND_DIR)) return null;
17495
+ let best = null;
17496
+ let bestMs = Infinity;
17497
+ try {
17498
+ for (const name of readdirSync3(SLACK_PENDING_INBOUND_DIR)) {
17499
+ if (!name.endsWith(".json")) continue;
17500
+ let m;
17501
+ try {
17502
+ m = JSON.parse(readFileSync9(join7(SLACK_PENDING_INBOUND_DIR, name), "utf-8"));
17503
+ } catch {
17504
+ continue;
17505
+ }
17506
+ if (!m.channel || !m.thread_ts) continue;
17507
+ if (m.discretionary === true || m.undeliverable === true) continue;
17508
+ const ms = Date.parse(m.received_at ?? "");
17509
+ if (!Number.isFinite(ms)) continue;
17510
+ if (ms < bestMs) {
17511
+ bestMs = ms;
17512
+ best = { channel: m.channel, threadTs: m.thread_ts, receivedAtMs: ms };
17513
+ }
17514
+ }
17515
+ } catch {
17516
+ return null;
17517
+ }
17518
+ return best;
17519
+ }
17520
+ var SLACK_PROGRESS_PREFIX = "\u23F3";
17521
+ function slackProgressBlocks(step) {
17522
+ const text = `${SLACK_PROGRESS_PREFIX} _Working\u2026 \xB7 ${step}_`;
17523
+ return [{ type: "context", elements: [{ type: "mrkdwn", text }] }];
17524
+ }
17525
+ async function postSlackProgress(channel, threadTs, step) {
17526
+ if (!BOT_TOKEN) return null;
17527
+ try {
17528
+ const res = await fetch("https://slack.com/api/chat.postMessage", {
17529
+ method: "POST",
17530
+ headers: { "Content-Type": "application/json; charset=utf-8", Authorization: `Bearer ${BOT_TOKEN}` },
17531
+ body: JSON.stringify({
17532
+ channel,
17533
+ thread_ts: threadTs,
17534
+ text: `${SLACK_PROGRESS_PREFIX} Working\u2026 \xB7 ${step}`,
17535
+ blocks: slackProgressBlocks(step)
17536
+ }),
17537
+ signal: AbortSignal.timeout(8e3)
17538
+ });
17539
+ const data = await res.json();
17540
+ return data.ok && data.ts ? data.ts : null;
17541
+ } catch {
17542
+ return null;
17543
+ }
17544
+ }
17545
+ async function updateSlackProgress(channel, ts, step) {
17546
+ if (!BOT_TOKEN) return false;
17547
+ try {
17548
+ const res = await fetch("https://slack.com/api/chat.update", {
17549
+ method: "POST",
17550
+ headers: { "Content-Type": "application/json; charset=utf-8", Authorization: `Bearer ${BOT_TOKEN}` },
17551
+ body: JSON.stringify({
17552
+ channel,
17553
+ ts,
17554
+ text: `${SLACK_PROGRESS_PREFIX} Working\u2026 \xB7 ${step}`,
17555
+ blocks: slackProgressBlocks(step)
17556
+ }),
17557
+ signal: AbortSignal.timeout(8e3)
17558
+ });
17559
+ const data = await res.json();
17560
+ return data.ok === true;
17561
+ } catch {
17562
+ return false;
17563
+ }
17564
+ }
17565
+ async function deleteSlackProgress(channel, ts) {
17566
+ if (!BOT_TOKEN) return false;
17567
+ try {
17568
+ const res = await fetch("https://slack.com/api/chat.delete", {
17569
+ method: "POST",
17570
+ headers: { "Content-Type": "application/json; charset=utf-8", Authorization: `Bearer ${BOT_TOKEN}` },
17571
+ body: JSON.stringify({ channel, ts }),
17572
+ signal: AbortSignal.timeout(8e3)
17573
+ });
17574
+ const data = await res.json();
17575
+ return data.ok === true || data.error === "message_not_found";
17576
+ } catch {
17577
+ return false;
17578
+ }
17579
+ }
17580
+ async function slackProgressTick() {
17581
+ if (slackProgressTickRunning) return;
17582
+ slackProgressTickRunning = true;
17583
+ try {
17584
+ if (!channelLiveProgressEnabled()) {
17585
+ if (slackTrackedProgress) {
17586
+ if (await deleteSlackProgress(slackTrackedProgress.channel, slackTrackedProgress.ts)) {
17587
+ slackTrackedProgress = null;
17588
+ }
17589
+ }
17590
+ return;
17591
+ }
17592
+ const action = decideProgressAction({
17593
+ now: Date.now(),
17594
+ heartbeat: readSlackProgressHeartbeat(),
17595
+ target: findSlackProgressTarget(),
17596
+ tracked: slackTrackedProgress,
17597
+ freshnessMs: progressHeartbeatFreshMs(),
17598
+ minPendingMs: progressMinPendingMs()
17599
+ });
17600
+ switch (action.type) {
17601
+ case "post": {
17602
+ const ts = await postSlackProgress(action.channel, action.threadTs, action.step);
17603
+ if (ts) slackTrackedProgress = { channel: action.channel, threadTs: action.threadTs, ts, lastStep: action.step };
17604
+ break;
17605
+ }
17606
+ case "update": {
17607
+ if (await updateSlackProgress(action.channel, action.ts, action.step)) {
17608
+ if (slackTrackedProgress) slackTrackedProgress.lastStep = action.step;
17609
+ }
17610
+ break;
17611
+ }
17612
+ case "delete": {
17613
+ if (await deleteSlackProgress(action.channel, action.ts)) {
17614
+ slackTrackedProgress = null;
17615
+ }
17616
+ break;
17617
+ }
17618
+ case "none":
17619
+ break;
17620
+ }
17621
+ } catch {
17622
+ } finally {
17623
+ slackProgressTickRunning = false;
17624
+ }
17625
+ }
17626
+ function slackProgressPollMs() {
17627
+ const raw = parseInt(process.env.AGT_CHANNEL_PROGRESS_POLL_MS ?? "", 10);
17628
+ return Number.isFinite(raw) && raw >= 2e3 ? raw : 8e3;
17629
+ }
17630
+ if (BOT_TOKEN && SLACK_PENDING_INBOUND_DIR) {
17631
+ const slackProgressTimer = setInterval(() => {
17632
+ void slackProgressTick();
17633
+ }, slackProgressPollMs());
17634
+ slackProgressTimer.unref?.();
17635
+ }
17383
17636
  var lastSlackGiveUpHandledAtMs = null;
17384
17637
  function listPendingSlackConversations() {
17385
17638
  if (!SLACK_PENDING_INBOUND_DIR || !existsSync7(SLACK_PENDING_INBOUND_DIR)) return [];
@@ -17394,6 +17647,7 @@ function listPendingSlackConversations() {
17394
17647
  if (typeof marker.channel !== "string" || !marker.channel) continue;
17395
17648
  if (typeof marker.thread_ts !== "string" || !marker.thread_ts) continue;
17396
17649
  if (marker.discretionary === true) continue;
17650
+ if (typeof marker.seen_at === "string" && marker.seen_at) continue;
17397
17651
  const isThreadReply = typeof marker.message_ts === "string" && marker.message_ts !== marker.thread_ts;
17398
17652
  const key2 = isThreadReply ? `${marker.channel}:${marker.thread_ts}` : marker.channel;
17399
17653
  if (!byKey.has(key2)) {
@@ -17647,28 +17901,12 @@ function clearPendingMessage(channel, threadTs) {
17647
17901
  }
17648
17902
  function noteThreadActivity(channel, threadTs) {
17649
17903
  if (!channel || !threadTs) return;
17650
- clearPendingMessage(channel, threadTs);
17904
+ markSeenAllSlackPendingMarkersForThread2(channel, threadTs);
17651
17905
  }
17652
17906
  function noteThreadActivityByMessageTs(channel, messageTs) {
17653
17907
  if (!channel || !messageTs) return;
17654
- clearPendingMessage(channel, messageTs);
17655
- if (!SLACK_PENDING_INBOUND_DIR) return;
17656
- if (!existsSync7(SLACK_PENDING_INBOUND_DIR)) return;
17657
- let filenames;
17658
- try {
17659
- filenames = readdirSync3(SLACK_PENDING_INBOUND_DIR);
17660
- } catch {
17661
- return;
17662
- }
17663
- const safeChannel = channel.replace(/[^A-Za-z0-9_-]/g, "_");
17664
- const safeMessageTs = messageTs.replace(/[^A-Za-z0-9_-]/g, "_");
17665
- const channelPrefix = `${safeChannel}__`;
17666
- const messageSuffix = `__${safeMessageTs}.json`;
17667
- for (const filename of filenames) {
17668
- if (!filename.startsWith(channelPrefix)) continue;
17669
- if (!filename.endsWith(messageSuffix)) continue;
17670
- clearSlackMarkerFileWithHeal(join7(SLACK_PENDING_INBOUND_DIR, filename));
17671
- }
17908
+ markSeenAllSlackPendingMarkersForThread2(channel, messageTs);
17909
+ markSeenSlackPendingMarkerByMessageTs2(channel, messageTs);
17672
17910
  }
17673
17911
  var RESTART_FLAGS_DIR = join7(homedir3(), ".augmented", "restart-flags");
17674
17912
  function buildAugmentedSlackMetadata() {
@@ -18490,7 +18728,7 @@ var mcp = new Server(
18490
18728
  instructions: [
18491
18729
  // Highest-priority lines first — Claude Code truncates this string at
18492
18730
  // 2048 chars, so anything appended late silently disappears.
18493
- "CRITICAL: every response to a Slack <channel> tag MUST go through slack.reply. Text in your session WITHOUT a slack.reply call never reaches the user \u2014 the message dies inside the agent process.",
18731
+ "CRITICAL: every response to a Slack <channel> tag MUST go through slack.reply \u2014 typed text never reaches the user. Slow work: interim ack (slack.reply interim:true), then your FINAL answer as a separate slack.reply (interim omitted). An ack is not the answer.",
18494
18732
  `Inbound: <channel ... thread_ts="..." message_ts="..." [thread_context="..."]>. Pass channel + message_ts to slack.reply (and thread_ts on threads). thread_context = thread pre-loaded; ground replies ONLY in it, never another channel's.`,
18495
18733
  "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 slack.download_attachment. Use that tool only for entries with `file_id` but NO `path` (PDF, docx, csv): pass file_id + channel verbatim, then Read the returned path. Single-image messages also get a top-level `image_path`. Don't surface internal file-handling errors that don't affect the answer.",
18496
18734
  "Address users by user_name, never by raw user ID. In multi-participant threads the CURRENT speaker is the one on the latest <channel> tag.",
@@ -18528,6 +18766,12 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
18528
18766
  message_ts: {
18529
18767
  type: "string",
18530
18768
  description: "The message_ts of the specific inbound this reply addresses (from the message_ts attribute on the <channel> tag). Used by the pending-inbound cleanup gate so the marker is reliably cleared even for top-level DMs that have no thread_ts."
18769
+ },
18770
+ // ENG-6567: explicit interim/final distinction so the system knows
18771
+ // whether the agent still owes a substantive answer.
18772
+ interim: {
18773
+ type: "boolean",
18774
+ 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 conversation as awaiting your final reply. Your eventual substantive answer MUST be a separate slack.reply with interim omitted/false \u2014 that is what marks the request complete. Omit (default false) for any reply that fully answers the user.'
18531
18775
  }
18532
18776
  },
18533
18777
  required: ["channel", "text"]
@@ -18754,7 +18998,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
18754
18998
  return buildImpersonationRefusal(name);
18755
18999
  }
18756
19000
  if (name === "slack.reply") {
18757
- const { channel, text, thread_ts, message_ts } = args;
19001
+ const { channel, text, thread_ts, message_ts, interim } = args;
18758
19002
  const effectiveThreadTs = resolveReplyThreadTs({
18759
19003
  channel,
18760
19004
  threadTs: thread_ts,
@@ -18818,16 +19062,23 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
18818
19062
  isError: true
18819
19063
  };
18820
19064
  }
18821
- if (channel) {
19065
+ const settlePendingMarker = () => {
19066
+ if (!channel) return;
18822
19067
  if (message_ts) {
18823
- clearSlackPendingMarkerByMessageTs2(channel, message_ts);
18824
- if (thread_ts) clearPendingMessage(channel, thread_ts);
19068
+ if (interim) {
19069
+ markSeenSlackPendingMarkerByMessageTs2(channel, message_ts);
19070
+ if (thread_ts) markSeenAllSlackPendingMarkersForThread2(channel, thread_ts);
19071
+ } else {
19072
+ clearSlackPendingMarkerByMessageTs2(channel, message_ts);
19073
+ if (thread_ts) clearPendingMessage(channel, thread_ts);
19074
+ }
18825
19075
  } else if (thread_ts) {
18826
- clearPendingMessage(channel, thread_ts);
18827
- } else {
19076
+ if (interim) markSeenAllSlackPendingMarkersForThread2(channel, thread_ts);
19077
+ else clearPendingMessage(channel, thread_ts);
19078
+ } else if (!interim) {
18828
19079
  clearOldestSlackPendingMarkerInChannel2(channel);
18829
19080
  }
18830
- }
19081
+ };
18831
19082
  try {
18832
19083
  const res = await fetch("https://slack.com/api/chat.postMessage", {
18833
19084
  method: "POST",
@@ -18851,6 +19102,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
18851
19102
  isError: true
18852
19103
  };
18853
19104
  }
19105
+ settlePendingMarker();
18854
19106
  recordReply(channel, throttleKey, throttleNow, throttleCfg);
18855
19107
  recordActivity("reply");
18856
19108
  if (THREAD_AUTO_FOLLOW !== "off") {
@@ -19290,7 +19542,6 @@ async function handleSendStructured(args) {
19290
19542
  if (typeof text !== "string" || !text) {
19291
19543
  return errResult("text is required (used as fallback for push notifications and unfurls)");
19292
19544
  }
19293
- noteThreadActivity(channel, thread_ts);
19294
19545
  const validation = validateSlackBlocks2(blocks);
19295
19546
  if (!validation.ok) {
19296
19547
  return errResult(`Invalid blocks: ${validation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
@@ -19304,6 +19555,7 @@ async function handleSendStructured(args) {
19304
19555
  if (!result.ok || !result.ts) {
19305
19556
  return errResult(`Slack chat.postMessage failed: ${result.error ?? "unknown"}`);
19306
19557
  }
19558
+ if (thread_ts) clearPendingMessage(channel, thread_ts);
19307
19559
  if (interactiveHostAvailable()) {
19308
19560
  const cfg = {
19309
19561
  apiHost: AGT_HOST,
@@ -16430,6 +16430,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
16430
16430
  try {
16431
16431
  const raw = JSON.parse(readFileSync7(join6(dir, name), "utf-8"));
16432
16432
  if (raw.discretionary === true) continue;
16433
+ if (typeof raw.seen_at === "string" && raw.seen_at) continue;
16433
16434
  receivedAt = raw.received_at;
16434
16435
  } catch {
16435
16436
  continue;
@@ -25,8 +25,8 @@ import {
25
25
  takeZombieDetection,
26
26
  writeDirectChatSessionState,
27
27
  writePersistentClaudeWrapper
28
- } from "./chunk-PEVDRQJA.js";
29
- import "./chunk-XHKMS4UY.js";
28
+ } from "./chunk-XDZFMTY5.js";
29
+ import "./chunk-3KLQA3SC.js";
30
30
  import "./chunk-XWVM4KPK.js";
31
31
  export {
32
32
  SEND_KEYS_ENTER_DELAY_MS,
@@ -56,4 +56,4 @@ export {
56
56
  writeDirectChatSessionState,
57
57
  writePersistentClaudeWrapper
58
58
  };
59
- //# sourceMappingURL=persistent-session-Z3RHQRVC.js.map
59
+ //# sourceMappingURL=persistent-session-U6P5I6TT.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-PEVDRQJA.js";
4
- import "./chunk-XHKMS4UY.js";
3
+ } from "./chunk-XDZFMTY5.js";
4
+ import "./chunk-3KLQA3SC.js";
5
5
  import "./chunk-XWVM4KPK.js";
6
6
 
7
7
  // src/lib/responsiveness-probe.ts
@@ -250,4 +250,4 @@ export {
250
250
  parkPendingInbound,
251
251
  readAndResetChannelDeflections
252
252
  };
253
- //# sourceMappingURL=responsiveness-probe-QWO75M34.js.map
253
+ //# sourceMappingURL=responsiveness-probe-VKIJY4IC.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.77",
3
+ "version": "0.28.79",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {