@integrity-labs/agt-cli 0.28.829 → 0.28.831

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.
Files changed (23) hide show
  1. package/dist/bin/agt.js +5 -5
  2. package/dist/{chunk-GJY3KZG6.js → chunk-4ZPOP7EW.js} +36 -11
  3. package/dist/{chunk-GJY3KZG6.js.map → chunk-4ZPOP7EW.js.map} +1 -1
  4. package/dist/{chunk-6HTCP6D5.js → chunk-N4QVPPYS.js} +2 -2
  5. package/dist/{chunk-E75YCJTE.js → chunk-PODKITVQ.js} +4 -4
  6. package/dist/{claude-pair-runtime-UTDZXJKO.js → claude-pair-runtime-PUWAIK73.js} +2 -2
  7. package/dist/lib/manager-worker.js +13 -13
  8. package/dist/mcp/augmented-admin.js +1 -1
  9. package/dist/mcp/direct-chat-channel.js +88 -13
  10. package/dist/mcp/index.js +35 -10
  11. package/dist/mcp/origami.js +35 -10
  12. package/dist/mcp/slack-channel.js +115 -13
  13. package/dist/mcp/telegram-channel.js +107 -13
  14. package/dist/{persistent-session-6YTEDS3H.js → persistent-session-W23U7GQU.js} +3 -3
  15. package/dist/{responsiveness-probe-5B2WA5G3.js → responsiveness-probe-IRQKIEBW.js} +3 -3
  16. package/dist/{session-auth-dead-LFNU6AUU.js → session-auth-dead-VDDXVJTX.js} +2 -2
  17. package/package.json +1 -1
  18. /package/dist/{chunk-6HTCP6D5.js.map → chunk-N4QVPPYS.js.map} +0 -0
  19. /package/dist/{chunk-E75YCJTE.js.map → chunk-PODKITVQ.js.map} +0 -0
  20. /package/dist/{claude-pair-runtime-UTDZXJKO.js.map → claude-pair-runtime-PUWAIK73.js.map} +0 -0
  21. /package/dist/{persistent-session-6YTEDS3H.js.map → persistent-session-W23U7GQU.js.map} +0 -0
  22. /package/dist/{responsiveness-probe-5B2WA5G3.js.map → responsiveness-probe-IRQKIEBW.js.map} +0 -0
  23. /package/dist/{session-auth-dead-LFNU6AUU.js.map → session-auth-dead-VDDXVJTX.js.map} +0 -0
@@ -36911,7 +36911,7 @@ var FLAG_REGISTRY = [
36911
36911
  },
36912
36912
  {
36913
36913
  key: "auto-resume-enabled",
36914
- description: "DO NOT ARM YET (ENG-8395): this flag has no `since` set until a follow-up bisects it from the published agt-cli dist, and an unset `since` makes the flip-reach estimate report FULL reach for a reader that is host-side \u2014 it will tell you a flip reached hosts that have no code to honour it. Arm only once that follow-up has landed. Bounded BLIND auto-resume for circuit-breaker pauses (ENG-6088): at most one unattended resume per trip cycle, fired once the trip driver has been quiet for the configured window and only for provisioning-reload trips (the ENG-7577 class gate \u2014 a crash-class trip still needs an operator). Superseded wherever `resume-reconciler` is on: that path proves the dependency recovered instead of assuming it, and the blind timer is its flag-off fallback. Boolean gate; ships dark. ENG-8395 promoted this from a raw host env var because AGT_AUTO_RESUME_ENABLED lives in the host env file, so its grain is per-HOST \u2014 one flip arms every agent that host runs (agt-aws-1 carries 14), which made the canary-then-fleet rollout its own documentation prescribes impossible. As a registry flag it resolves per org/team/host/AGENT, so one agent can genuinely be cut over first. Read host-side by the manager (agt-cli), not centrally.",
36914
+ description: "Bounded BLIND auto-resume for circuit-breaker pauses (ENG-6088): at most one unattended resume per trip cycle, fired once the trip driver has been quiet for the configured window and only for provisioning-reload trips (the ENG-7577 class gate \u2014 a crash-class trip still needs an operator). Superseded wherever `resume-reconciler` is on: that path proves the dependency recovered instead of assuming it, and the blind timer is its flag-off fallback. Boolean gate; ships dark. ENG-8395 promoted this from a raw host env var because AGT_AUTO_RESUME_ENABLED lives in the host env file, so its grain is per-HOST \u2014 one flip arms every agent that host runs (agt-aws-1 carries 14), which made the canary-then-fleet rollout its own documentation prescribes impossible. As a registry flag it resolves per org/team/host/AGENT, so one agent can genuinely be cut over first. Read host-side by the manager (agt-cli), not centrally.",
36915
36915
  flagType: "boolean",
36916
36916
  // Declared safe value is `false` — identical to today's behaviour on a host
36917
36917
  // that has never set the env var. Fail-safe direction on a flag-DB read
@@ -36928,16 +36928,41 @@ var FLAG_REGISTRY = [
36928
36928
  // An unattended resume is a real availability change — the agent starts
36929
36929
  // spawning sessions again with no human in the loop — so arming it is a
36930
36930
  // deliberate, confirmed write (ADR-0022 sensitive-flag confirm).
36931
- sensitive: true
36932
- // ENG-8395 AC5: `since` is deliberately UNSET at merge. It must be bisected
36933
- // from the PUBLISHED agt-cli dist once the artifact carrying the host-side
36934
- // reader exists, never guessed auto-resume.ts is host-side, so an
36935
- // under-`since` host has no consumer for this flag and a guessed value makes
36936
- // the flip-reach modal promise reach it does not have (the defect caught on
36937
- // ENG-8344 / PR #4014). Precedent for the bisect method: slack-hot-thread-guard
36938
- // = 0.28.339, slack-scheduled-channel-guard = 0.28.421. Setting it later is
36939
- // safe: `since` is excluded from projectDefinition, so it does not roll
36931
+ sensitive: true,
36932
+ // ENG-10034: bisected from the PUBLISHED agt-cli dists, not from git history —
36933
+ // auto-resume.ts is host-side, so what matters is the first ARTIFACT a host
36934
+ // could be running that contains a consumer for this flag.
36935
+ //
36936
+ // METHOD, so it can be re-run rather than trusted: download each candidate
36937
+ // tarball from the npm registry and grep its contents for the flag-specific
36938
+ // wiring. Binary search over the 22 versions published either side of
36939
+ // ENG-8395's merge gave a clean one-release boundary, eight minutes wide:
36940
+ //
36941
+ // 0.28.506 2026-08-03T22:29:22Z readAutoResumeEnvOverride ✗ AUTO_RESUME_FLAG_KEY ✗ 'auto-resume-enabled' ✗
36942
+ // 0.28.507 2026-08-03T22:37:00Z readAutoResumeEnvOverride ✓ AUTO_RESUME_FLAG_KEY ✓ 'auto-resume-enabled' ✓
36943
+ //
36944
+ // All three symbols are checked rather than one. `getBooleanForAgent` alone
36945
+ // would have been the wrong probe — it is present in BOTH builds, because the
36946
+ // generic resolver long predates this flag. A `since` bisected on a symbol the
36947
+ // flag does not depend on is a number that looks rigorous and is not.
36948
+ //
36949
+ // ENG-8395 AC5 left this unset on purpose and said why: an unset `since` makes
36950
+ // the flip-reach estimate report FULL reach for a host-side reader, promising
36951
+ // reach it does not have (the defect caught on ENG-8344 / PR #4014). The
36952
+ // follow-up it was waiting on was never filed, so the flag sat un-armable for
36953
+ // a month and ENG-6088's bounded auto-resume stayed dark fleet-wide — a
36954
+ // customer agent sat breaker-paused 2h08m on 2026-09-05 with the recovery path
36955
+ // silently disabled. That is what an administrative blocker recorded only in a
36956
+ // code comment costs.
36957
+ //
36958
+ // Same method as its precedents: slack-hot-thread-guard = 0.28.339,
36959
+ // slack-scheduled-channel-guard = 0.28.421 (ENG-8149). Setting it now is safe:
36960
+ // `since` is excluded from projectDefinition, so it does not roll
36940
36961
  // FLAGS_SCHEMA_VERSION.
36962
+ //
36963
+ // This does NOT arm the flag. defaultValue stays false and `sensitive` stays
36964
+ // true; arming is a separate, deliberate decision that belongs to ENG-6066.
36965
+ since: "0.28.507"
36941
36966
  },
36942
36967
  {
36943
36968
  key: "host-poll-rate-alarm",
@@ -38389,10 +38414,19 @@ function undeliverableNoticeEligible(shouldEngage) {
38389
38414
  function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
38390
38415
  return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
38391
38416
  }
38392
- function undeliverableNoticeText(replay = channelReplayEnabled()) {
38393
- return noticePromiseIsHonest(replay) ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
38417
+ function undeliverableNoticeText(replay) {
38418
+ const honest = noticePromiseIsHonest(replay);
38419
+ return honest ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
38420
+ }
38421
+ function undeliverableNoticeTextForRepeat(i) {
38422
+ if (i.priorNotices <= 0) return undeliverableNoticeText(i.replay);
38423
+ if (i.priorNotices === 1) {
38424
+ return noticePromiseIsHonest(i.replay) ? "\u23F3 Still stuck on this one \u2014 the automatic pickup I mentioned has not happened. Please resend when you can." : "\u23F3 Still stuck on this one, and I still have not picked it up. Resending is the reliable way through.";
38425
+ }
38426
+ return null;
38394
38427
  }
38395
38428
  function noticePromiseIsHonest(replay) {
38429
+ if (replay === void 0) return false;
38396
38430
  if (typeof replay === "boolean") return replay;
38397
38431
  return markerReplayable({
38398
38432
  enabled: replay.enabled ?? channelReplayEnabled(),
@@ -38427,6 +38461,13 @@ var UndeliverableNoticeScheduler = class {
38427
38461
  }
38428
38462
  /** decision-time stamps, keyed by conversation. Shared with other one-per-window system notices. */
38429
38463
  stamps = /* @__PURE__ */ new Map();
38464
+ /**
38465
+ * ENG-9413 AC4 — how many notices have actually been POSTED for this
38466
+ * conversation. Distinct from `stamps`, which is a decision-time throttle and
38467
+ * is dropped by `cancel()`: a notice that was scheduled and cancelled was
38468
+ * never seen by anyone and must not count toward escalation.
38469
+ */
38470
+ sent = /* @__PURE__ */ new Map();
38430
38471
  /** scheduled-but-unsent notices, keyed by conversation. */
38431
38472
  timers = /* @__PURE__ */ new Map();
38432
38473
  /** is a notice scheduled but not yet sent for this conversation? */
@@ -38437,6 +38478,30 @@ var UndeliverableNoticeScheduler = class {
38437
38478
  stampedAt(key2) {
38438
38479
  return this.stamps.get(key2);
38439
38480
  }
38481
+ /**
38482
+ * ENG-9413 AC4 — notices already POSTED for this conversation. Read by the
38483
+ * channel's send callback to pick escalated copy, or to say nothing at all.
38484
+ * The count is incremented AFTER `send` runs, so inside the callback this is
38485
+ * the number of PRIOR notices.
38486
+ */
38487
+ noticeCount(key2) {
38488
+ return this.sent.get(key2) ?? 0;
38489
+ }
38490
+ /**
38491
+ * ENG-9413 AC4 — record that a notice was CONFIRMED delivered.
38492
+ *
38493
+ * Called by the channel, not by `dispatch`, and that is the correction.
38494
+ * `dispatch`'s `send` is `() => void`: it returns before the Slack or Telegram
38495
+ * request completes, so incrementing there counted posts that never landed. A
38496
+ * failed post would then advance the escalation — the user's SECOND notice
38497
+ * would arrive worded as though they had already seen a first, and their third
38498
+ * would be suppressed entirely, on the strength of notices that were never
38499
+ * delivered. Raised in review on this PR; direct chat already got this right
38500
+ * by incrementing only on `ok`, and the shared scheduler now matches it.
38501
+ */
38502
+ noteDelivered(key2) {
38503
+ this.sent.set(key2, this.noticeCount(key2) + 1);
38504
+ }
38440
38505
  /**
38441
38506
  * ENG-6025: claim this conversation's one-notice-per-window slot for a
38442
38507
  * DIFFERENT system notice (the watchdog give-up line). Returns false when the
@@ -38508,6 +38573,7 @@ var UndeliverableNoticeScheduler = class {
38508
38573
  for (const timer of this.timers.values()) clearTimeout(timer);
38509
38574
  this.timers.clear();
38510
38575
  this.stamps.clear();
38576
+ this.sent.clear();
38511
38577
  }
38512
38578
  clearTimer(key2) {
38513
38579
  const timer = this.timers.get(key2);
@@ -38681,6 +38747,13 @@ function channelReplayEnabled() {
38681
38747
  defaultValue: true
38682
38748
  });
38683
38749
  }
38750
+ function combineMarkerReplayFacts(markers) {
38751
+ if (markers.length === 0) return null;
38752
+ return {
38753
+ hasPayload: markers.every((m) => m.hasPayload),
38754
+ discretionary: markers.some((m) => m.discretionary === true)
38755
+ };
38756
+ }
38684
38757
  function markerReplayable(i) {
38685
38758
  if (!i.enabled) return false;
38686
38759
  if (!i.hasPayload) return false;
@@ -43799,8 +43872,36 @@ function postUndeliverableNotice(channel, threadTs, isThreadReply, cause = "unkn
43799
43872
  send: () => sendUndeliverableNotice(channel, threadTs, conversationKey)
43800
43873
  });
43801
43874
  }
43875
+ function slackConversationReplayFacts(channel, threadTs) {
43876
+ if (!threadTs || !SLACK_PENDING_INBOUND_DIR) return null;
43877
+ const found = [];
43878
+ try {
43879
+ markSeenAllSlackPendingMarkersForThread(
43880
+ SLACK_PENDING_INBOUND_DIR,
43881
+ channel,
43882
+ threadTs,
43883
+ (fullPath) => {
43884
+ try {
43885
+ const marker = JSON.parse(readFileSync18(fullPath, "utf-8"));
43886
+ found.push({ hasPayload: Boolean(marker.payload), discretionary: marker.discretionary });
43887
+ } catch {
43888
+ found.push({ hasPayload: false });
43889
+ }
43890
+ }
43891
+ );
43892
+ } catch {
43893
+ return null;
43894
+ }
43895
+ const facts = combineMarkerReplayFacts(found);
43896
+ return facts ? { enabled: channelReplayEnabled(), ...facts } : null;
43897
+ }
43802
43898
  function sendUndeliverableNotice(channel, threadTs, conversationKey) {
43803
43899
  if (!BOT_TOKEN || !channel) return;
43900
+ const text = undeliverableNoticeTextForRepeat({
43901
+ replay: slackConversationReplayFacts(channel, threadTs) ?? void 0,
43902
+ priorNotices: slackUndeliverableNotices.noticeCount(conversationKey)
43903
+ });
43904
+ if (text === null) return;
43804
43905
  fetch("https://slack.com/api/chat.postMessage", {
43805
43906
  method: "POST",
43806
43907
  headers: {
@@ -43809,12 +43910,13 @@ function sendUndeliverableNotice(channel, threadTs, conversationKey) {
43809
43910
  },
43810
43911
  body: JSON.stringify({
43811
43912
  channel,
43812
- text: undeliverableNoticeText(),
43913
+ text,
43813
43914
  ...threadTs ? { thread_ts: threadTs } : {}
43814
43915
  })
43815
43916
  }).then((res) => res.json()).then((data) => {
43816
43917
  if (data?.ok === true && typeof data.ts === "string" && data.ts) {
43817
43918
  lastSlackUndeliverableNoticeInfo.set(conversationKey, { channel, ts: data.ts });
43919
+ slackUndeliverableNotices.noteDelivered(conversationKey);
43818
43920
  }
43819
43921
  }).catch(() => {
43820
43922
  });
@@ -37043,7 +37043,7 @@ var FLAG_REGISTRY = [
37043
37043
  },
37044
37044
  {
37045
37045
  key: "auto-resume-enabled",
37046
- description: "DO NOT ARM YET (ENG-8395): this flag has no `since` set until a follow-up bisects it from the published agt-cli dist, and an unset `since` makes the flip-reach estimate report FULL reach for a reader that is host-side \u2014 it will tell you a flip reached hosts that have no code to honour it. Arm only once that follow-up has landed. Bounded BLIND auto-resume for circuit-breaker pauses (ENG-6088): at most one unattended resume per trip cycle, fired once the trip driver has been quiet for the configured window and only for provisioning-reload trips (the ENG-7577 class gate \u2014 a crash-class trip still needs an operator). Superseded wherever `resume-reconciler` is on: that path proves the dependency recovered instead of assuming it, and the blind timer is its flag-off fallback. Boolean gate; ships dark. ENG-8395 promoted this from a raw host env var because AGT_AUTO_RESUME_ENABLED lives in the host env file, so its grain is per-HOST \u2014 one flip arms every agent that host runs (agt-aws-1 carries 14), which made the canary-then-fleet rollout its own documentation prescribes impossible. As a registry flag it resolves per org/team/host/AGENT, so one agent can genuinely be cut over first. Read host-side by the manager (agt-cli), not centrally.",
37046
+ description: "Bounded BLIND auto-resume for circuit-breaker pauses (ENG-6088): at most one unattended resume per trip cycle, fired once the trip driver has been quiet for the configured window and only for provisioning-reload trips (the ENG-7577 class gate \u2014 a crash-class trip still needs an operator). Superseded wherever `resume-reconciler` is on: that path proves the dependency recovered instead of assuming it, and the blind timer is its flag-off fallback. Boolean gate; ships dark. ENG-8395 promoted this from a raw host env var because AGT_AUTO_RESUME_ENABLED lives in the host env file, so its grain is per-HOST \u2014 one flip arms every agent that host runs (agt-aws-1 carries 14), which made the canary-then-fleet rollout its own documentation prescribes impossible. As a registry flag it resolves per org/team/host/AGENT, so one agent can genuinely be cut over first. Read host-side by the manager (agt-cli), not centrally.",
37047
37047
  flagType: "boolean",
37048
37048
  // Declared safe value is `false` — identical to today's behaviour on a host
37049
37049
  // that has never set the env var. Fail-safe direction on a flag-DB read
@@ -37060,16 +37060,41 @@ var FLAG_REGISTRY = [
37060
37060
  // An unattended resume is a real availability change — the agent starts
37061
37061
  // spawning sessions again with no human in the loop — so arming it is a
37062
37062
  // deliberate, confirmed write (ADR-0022 sensitive-flag confirm).
37063
- sensitive: true
37064
- // ENG-8395 AC5: `since` is deliberately UNSET at merge. It must be bisected
37065
- // from the PUBLISHED agt-cli dist once the artifact carrying the host-side
37066
- // reader exists, never guessed auto-resume.ts is host-side, so an
37067
- // under-`since` host has no consumer for this flag and a guessed value makes
37068
- // the flip-reach modal promise reach it does not have (the defect caught on
37069
- // ENG-8344 / PR #4014). Precedent for the bisect method: slack-hot-thread-guard
37070
- // = 0.28.339, slack-scheduled-channel-guard = 0.28.421. Setting it later is
37071
- // safe: `since` is excluded from projectDefinition, so it does not roll
37063
+ sensitive: true,
37064
+ // ENG-10034: bisected from the PUBLISHED agt-cli dists, not from git history —
37065
+ // auto-resume.ts is host-side, so what matters is the first ARTIFACT a host
37066
+ // could be running that contains a consumer for this flag.
37067
+ //
37068
+ // METHOD, so it can be re-run rather than trusted: download each candidate
37069
+ // tarball from the npm registry and grep its contents for the flag-specific
37070
+ // wiring. Binary search over the 22 versions published either side of
37071
+ // ENG-8395's merge gave a clean one-release boundary, eight minutes wide:
37072
+ //
37073
+ // 0.28.506 2026-08-03T22:29:22Z readAutoResumeEnvOverride ✗ AUTO_RESUME_FLAG_KEY ✗ 'auto-resume-enabled' ✗
37074
+ // 0.28.507 2026-08-03T22:37:00Z readAutoResumeEnvOverride ✓ AUTO_RESUME_FLAG_KEY ✓ 'auto-resume-enabled' ✓
37075
+ //
37076
+ // All three symbols are checked rather than one. `getBooleanForAgent` alone
37077
+ // would have been the wrong probe — it is present in BOTH builds, because the
37078
+ // generic resolver long predates this flag. A `since` bisected on a symbol the
37079
+ // flag does not depend on is a number that looks rigorous and is not.
37080
+ //
37081
+ // ENG-8395 AC5 left this unset on purpose and said why: an unset `since` makes
37082
+ // the flip-reach estimate report FULL reach for a host-side reader, promising
37083
+ // reach it does not have (the defect caught on ENG-8344 / PR #4014). The
37084
+ // follow-up it was waiting on was never filed, so the flag sat un-armable for
37085
+ // a month and ENG-6088's bounded auto-resume stayed dark fleet-wide — a
37086
+ // customer agent sat breaker-paused 2h08m on 2026-09-05 with the recovery path
37087
+ // silently disabled. That is what an administrative blocker recorded only in a
37088
+ // code comment costs.
37089
+ //
37090
+ // Same method as its precedents: slack-hot-thread-guard = 0.28.339,
37091
+ // slack-scheduled-channel-guard = 0.28.421 (ENG-8149). Setting it now is safe:
37092
+ // `since` is excluded from projectDefinition, so it does not roll
37072
37093
  // FLAGS_SCHEMA_VERSION.
37094
+ //
37095
+ // This does NOT arm the flag. defaultValue stays false and `sensitive` stays
37096
+ // true; arming is a separate, deliberate decision that belongs to ENG-6066.
37097
+ since: "0.28.507"
37073
37098
  },
37074
37099
  {
37075
37100
  key: "host-poll-rate-alarm",
@@ -41127,10 +41152,19 @@ var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
41127
41152
  function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
41128
41153
  return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
41129
41154
  }
41130
- function undeliverableNoticeText(replay = channelReplayEnabled()) {
41131
- return noticePromiseIsHonest(replay) ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
41155
+ function undeliverableNoticeText(replay) {
41156
+ const honest = noticePromiseIsHonest(replay);
41157
+ return honest ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
41158
+ }
41159
+ function undeliverableNoticeTextForRepeat(i) {
41160
+ if (i.priorNotices <= 0) return undeliverableNoticeText(i.replay);
41161
+ if (i.priorNotices === 1) {
41162
+ return noticePromiseIsHonest(i.replay) ? "\u23F3 Still stuck on this one \u2014 the automatic pickup I mentioned has not happened. Please resend when you can." : "\u23F3 Still stuck on this one, and I still have not picked it up. Resending is the reliable way through.";
41163
+ }
41164
+ return null;
41132
41165
  }
41133
41166
  function noticePromiseIsHonest(replay) {
41167
+ if (replay === void 0) return false;
41134
41168
  if (typeof replay === "boolean") return replay;
41135
41169
  return markerReplayable({
41136
41170
  enabled: replay.enabled ?? channelReplayEnabled(),
@@ -41165,6 +41199,13 @@ var UndeliverableNoticeScheduler = class {
41165
41199
  }
41166
41200
  /** decision-time stamps, keyed by conversation. Shared with other one-per-window system notices. */
41167
41201
  stamps = /* @__PURE__ */ new Map();
41202
+ /**
41203
+ * ENG-9413 AC4 — how many notices have actually been POSTED for this
41204
+ * conversation. Distinct from `stamps`, which is a decision-time throttle and
41205
+ * is dropped by `cancel()`: a notice that was scheduled and cancelled was
41206
+ * never seen by anyone and must not count toward escalation.
41207
+ */
41208
+ sent = /* @__PURE__ */ new Map();
41168
41209
  /** scheduled-but-unsent notices, keyed by conversation. */
41169
41210
  timers = /* @__PURE__ */ new Map();
41170
41211
  /** is a notice scheduled but not yet sent for this conversation? */
@@ -41175,6 +41216,30 @@ var UndeliverableNoticeScheduler = class {
41175
41216
  stampedAt(key2) {
41176
41217
  return this.stamps.get(key2);
41177
41218
  }
41219
+ /**
41220
+ * ENG-9413 AC4 — notices already POSTED for this conversation. Read by the
41221
+ * channel's send callback to pick escalated copy, or to say nothing at all.
41222
+ * The count is incremented AFTER `send` runs, so inside the callback this is
41223
+ * the number of PRIOR notices.
41224
+ */
41225
+ noticeCount(key2) {
41226
+ return this.sent.get(key2) ?? 0;
41227
+ }
41228
+ /**
41229
+ * ENG-9413 AC4 — record that a notice was CONFIRMED delivered.
41230
+ *
41231
+ * Called by the channel, not by `dispatch`, and that is the correction.
41232
+ * `dispatch`'s `send` is `() => void`: it returns before the Slack or Telegram
41233
+ * request completes, so incrementing there counted posts that never landed. A
41234
+ * failed post would then advance the escalation — the user's SECOND notice
41235
+ * would arrive worded as though they had already seen a first, and their third
41236
+ * would be suppressed entirely, on the strength of notices that were never
41237
+ * delivered. Raised in review on this PR; direct chat already got this right
41238
+ * by incrementing only on `ok`, and the shared scheduler now matches it.
41239
+ */
41240
+ noteDelivered(key2) {
41241
+ this.sent.set(key2, this.noticeCount(key2) + 1);
41242
+ }
41178
41243
  /**
41179
41244
  * ENG-6025: claim this conversation's one-notice-per-window slot for a
41180
41245
  * DIFFERENT system notice (the watchdog give-up line). Returns false when the
@@ -41246,6 +41311,7 @@ var UndeliverableNoticeScheduler = class {
41246
41311
  for (const timer of this.timers.values()) clearTimeout(timer);
41247
41312
  this.timers.clear();
41248
41313
  this.stamps.clear();
41314
+ this.sent.clear();
41249
41315
  }
41250
41316
  clearTimer(key2) {
41251
41317
  const timer = this.timers.get(key2);
@@ -41412,6 +41478,13 @@ function channelReplayEnabled() {
41412
41478
  defaultValue: true
41413
41479
  });
41414
41480
  }
41481
+ function combineMarkerReplayFacts(markers) {
41482
+ if (markers.length === 0) return null;
41483
+ return {
41484
+ hasPayload: markers.every((m) => m.hasPayload),
41485
+ discretionary: markers.some((m) => m.discretionary === true)
41486
+ };
41487
+ }
41415
41488
  function markerReplayable(i) {
41416
41489
  if (!i.enabled) return false;
41417
41490
  if (!i.hasPayload) return false;
@@ -42039,19 +42112,40 @@ function notifyUndeliverable(chatId, messageId, cause = "unknown") {
42039
42112
  send: () => void sendUndeliverableNotice(chatId, messageId)
42040
42113
  });
42041
42114
  }
42115
+ function telegramMarkerReplayFacts(chatId, messageId) {
42116
+ const path = pendingInboundPath(chatId, messageId);
42117
+ if (!path || !existsSync9(path)) return null;
42118
+ try {
42119
+ const marker = JSON.parse(readFileSync14(path, "utf-8"));
42120
+ const facts = combineMarkerReplayFacts([
42121
+ { hasPayload: Boolean(marker.payload), discretionary: marker.discretionary }
42122
+ ]);
42123
+ return facts ? { enabled: channelReplayEnabled(), ...facts } : null;
42124
+ } catch {
42125
+ return null;
42126
+ }
42127
+ }
42042
42128
  async function sendUndeliverableNotice(chatId, messageId) {
42129
+ const text = undeliverableNoticeTextForRepeat({
42130
+ replay: telegramMarkerReplayFacts(String(chatId), String(messageId)) ?? void 0,
42131
+ priorNotices: telegramUndeliverableNotices.noticeCount(String(chatId))
42132
+ });
42133
+ if (text === null) return;
42043
42134
  try {
42044
42135
  const resp = await telegramApiCall(
42045
42136
  "sendMessage",
42046
42137
  {
42047
42138
  chat_id: chatId,
42048
42139
  // ENG-6025: shared copy with the Slack undeliverable notice.
42049
- text: undeliverableNoticeText(),
42140
+ text,
42050
42141
  reply_to_message_id: Number(messageId),
42051
42142
  allow_sending_without_reply: true
42052
42143
  },
42053
42144
  1e4
42054
42145
  );
42146
+ if (resp.ok) {
42147
+ telegramUndeliverableNotices.noteDelivered(String(chatId));
42148
+ }
42055
42149
  if (!resp.ok) {
42056
42150
  process.stderr.write(
42057
42151
  `telegram-channel(${AGENT_CODE_NAME}): undeliverable notice failed (chat=${redactId(chatId)}): ${resp.description ?? "unknown"}
@@ -49,8 +49,8 @@ import {
49
49
  writeDirectChatSessionState,
50
50
  writeEgressAllowlist,
51
51
  writePersistentClaudeWrapper
52
- } from "./chunk-6HTCP6D5.js";
53
- import "./chunk-GJY3KZG6.js";
52
+ } from "./chunk-N4QVPPYS.js";
53
+ import "./chunk-4ZPOP7EW.js";
54
54
  import "./chunk-XWVM4KPK.js";
55
55
  export {
56
56
  EGRESS_BASELINE_DOMAINS,
@@ -104,4 +104,4 @@ export {
104
104
  writeEgressAllowlist,
105
105
  writePersistentClaudeWrapper
106
106
  };
107
- //# sourceMappingURL=persistent-session-6YTEDS3H.js.map
107
+ //# sourceMappingURL=persistent-session-W23U7GQU.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-6HTCP6D5.js";
4
- import "./chunk-GJY3KZG6.js";
3
+ } from "./chunk-N4QVPPYS.js";
4
+ import "./chunk-4ZPOP7EW.js";
5
5
  import "./chunk-XWVM4KPK.js";
6
6
 
7
7
  // src/lib/responsiveness-probe.ts
@@ -739,4 +739,4 @@ export {
739
739
  readAndResetSlackReplyBindingClassifications,
740
740
  readAndResetSlackReplyTargetClassifications
741
741
  };
742
- //# sourceMappingURL=responsiveness-probe-5B2WA5G3.js.map
742
+ //# sourceMappingURL=responsiveness-probe-IRQKIEBW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionTranscriptDir
3
- } from "./chunk-GJY3KZG6.js";
3
+ } from "./chunk-4ZPOP7EW.js";
4
4
 
5
5
  // src/lib/session-auth-dead.ts
6
6
  import { closeSync, openSync, readSync, readdirSync, statSync } from "fs";
@@ -203,4 +203,4 @@ export {
203
203
  decideSessionAuthState,
204
204
  probeSessionAuth
205
205
  };
206
- //# sourceMappingURL=session-auth-dead-LFNU6AUU.js.map
206
+ //# sourceMappingURL=session-auth-dead-VDDXVJTX.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.829",
3
+ "version": "0.28.831",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {