@integrity-labs/agt-cli 0.28.1022 → 0.28.1024

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-QXJWRE64.js → chunk-5XPZF6FT.js} +30 -2
  3. package/dist/{chunk-QXJWRE64.js.map → chunk-5XPZF6FT.js.map} +1 -1
  4. package/dist/{chunk-X7SX25N6.js → chunk-BHKFVSIX.js} +10 -8
  5. package/dist/{chunk-X7SX25N6.js.map → chunk-BHKFVSIX.js.map} +1 -1
  6. package/dist/{chunk-5MUSHNVX.js → chunk-HVTL2N3I.js} +4 -4
  7. package/dist/{claude-pair-runtime-KKFNGRSA.js → claude-pair-runtime-IAO6B35F.js} +2 -2
  8. package/dist/lib/manager-worker.js +271 -15
  9. package/dist/lib/manager-worker.js.map +1 -1
  10. package/dist/mcp/direct-chat-channel.js +45 -11
  11. package/dist/mcp/index.js +9 -7
  12. package/dist/mcp/origami.js +9 -7
  13. package/dist/mcp/slack-channel.js +45 -11
  14. package/dist/mcp/telegram-channel.js +45 -11
  15. package/dist/{persistent-session-ZQDNCKQH.js → persistent-session-GZV4EXKD.js} +3 -3
  16. package/dist/{responsiveness-probe-Q27WU2MW.js → responsiveness-probe-WRHLHQCE.js} +3 -3
  17. package/dist/{session-auth-dead-JJUKS75B.js → session-auth-dead-ZKC2HH6T.js} +2 -2
  18. package/package.json +1 -1
  19. /package/dist/{chunk-5MUSHNVX.js.map → chunk-HVTL2N3I.js.map} +0 -0
  20. /package/dist/{claude-pair-runtime-KKFNGRSA.js.map → claude-pair-runtime-IAO6B35F.js.map} +0 -0
  21. /package/dist/{persistent-session-ZQDNCKQH.js.map → persistent-session-GZV4EXKD.js.map} +0 -0
  22. /package/dist/{responsiveness-probe-Q27WU2MW.js.map → responsiveness-probe-WRHLHQCE.js.map} +0 -0
  23. /package/dist/{session-auth-dead-JJUKS75B.js.map → session-auth-dead-ZKC2HH6T.js.map} +0 -0
@@ -32966,13 +32966,18 @@ var OAUTH_PROVIDERS = {
32966
32966
  // existing Xero install must RECONNECT for its token to carry this — the
32967
32967
  // broker posts journals with the agent's own OAuth access_token.
32968
32968
  "accounting.manualjournals",
32969
- // accounting.reports.read → granular read-only replacements
32969
+ // accounting.reports.read → granular read-only replacements, ONE PER REPORT
32970
+ // TOOL THE SERVER REGISTERS (ENG-10961 follow-on). Bank summary, budget
32971
+ // summary and executive summary were requested here with no tool reading
32972
+ // them: #6463 removed the phantom `report-bank-summary` /
32973
+ // `report-budget-summary` names, and nothing ever called the executive
32974
+ // summary. Asking a customer to consent to read access nothing uses is the
32975
+ // least-privilege failure the granular list exists to avoid. Removing a
32976
+ // default can never raise a reconnect prompt - `computeMissingScopes` only
32977
+ // reports defaults the grant LACKS.
32970
32978
  "accounting.reports.balancesheet.read",
32971
32979
  "accounting.reports.profitandloss.read",
32972
32980
  "accounting.reports.trialbalance.read",
32973
- "accounting.reports.budgetsummary.read",
32974
- "accounting.reports.banksummary.read",
32975
- "accounting.reports.executivesummary.read",
32976
32981
  "accounting.reports.aged.read"
32977
32982
  ],
32978
32983
  // Read-only variant (ENG-6170): the `.read` granular scope for every
@@ -32995,9 +33000,6 @@ var OAUTH_PROVIDERS = {
32995
33000
  "accounting.reports.balancesheet.read",
32996
33001
  "accounting.reports.profitandloss.read",
32997
33002
  "accounting.reports.trialbalance.read",
32998
- "accounting.reports.budgetsummary.read",
32999
- "accounting.reports.banksummary.read",
33000
- "accounting.reports.executivesummary.read",
33001
33003
  "accounting.reports.aged.read"
33002
33004
  ],
33003
33005
  supportsRefresh: true,
@@ -39284,13 +39286,17 @@ function decideBusyAck(i) {
39284
39286
  const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
39285
39287
  if (i.pendingAgeMs < threshold) return false;
39286
39288
  if (!i.sessionAlive) return false;
39289
+ if (i.liveStateMarker) return true;
39287
39290
  const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
39288
39291
  return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
39289
39292
  }
39290
- function busyAckNoticeText(usageLimit) {
39293
+ function busyAckNoticeText(usageLimit, blockedOnPrompt) {
39291
39294
  if (usageLimit) return giveUpNoticeText("usage_limit", usageLimit.resetsHint);
39292
- return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
39295
+ if (blockedOnPrompt) return BLOCKED_ON_PROMPT_NOTICE;
39296
+ return BUSY_NOTICE;
39293
39297
  }
39298
+ var BUSY_NOTICE = "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
39299
+ var BLOCKED_ON_PROMPT_NOTICE = "\u26A0\uFE0F I'm stuck on a prompt I can't answer myself, so I can't get to this right now. This isn't me ignoring you - it needs someone on our side to clear it before I can continue.";
39294
39300
  var GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
39295
39301
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
39296
39302
  function readGiveUpSignal(path, now = Date.now()) {
@@ -39327,6 +39333,28 @@ function readUsageLimitForBusyAck(agentDir, now = Date.now()) {
39327
39333
  if (now - lastSeenMs > USAGE_LIMIT_LIVE_WINDOW_MS) return null;
39328
39334
  return { resetsHint: sig.resetsHint };
39329
39335
  }
39336
+ var BLOCKED_PROMPT_SIGNAL_FILENAME = "blocked-prompt.json";
39337
+ var BLOCKED_PROMPT_KINDS = ["login-picker", "usage-limit-unanswerable", "unclaimed-modal"];
39338
+ var BLOCKED_PROMPT_LIVE_WINDOW_MS = 3 * 6e4;
39339
+ function readBlockedPromptForBusyAck(agentDir, now = Date.now()) {
39340
+ if (!agentDir) return null;
39341
+ let raw;
39342
+ try {
39343
+ raw = JSON.parse(readFileSync7(join7(agentDir, BLOCKED_PROMPT_SIGNAL_FILENAME), "utf8"));
39344
+ } catch {
39345
+ return null;
39346
+ }
39347
+ if (typeof raw !== "object" || raw === null) return null;
39348
+ const kind = raw.kind;
39349
+ if (typeof kind !== "string" || !BLOCKED_PROMPT_KINDS.includes(kind)) {
39350
+ return null;
39351
+ }
39352
+ if (typeof raw.last_seen_at !== "string") return null;
39353
+ const t = Date.parse(raw.last_seen_at);
39354
+ if (!Number.isFinite(t) || t > now) return null;
39355
+ if (now - t > BLOCKED_PROMPT_LIVE_WINDOW_MS) return null;
39356
+ return { kind };
39357
+ }
39330
39358
  function giveUpNoticeText(reason = null, resetsHint = null) {
39331
39359
  if (reason === "usage_limit") {
39332
39360
  return resetsHint ? `\u23F3 I've reached my usage limit for now, so I can't reply until ${resetsHint}. Your message is queued \u2014 I'll pick it up then, no need to resend.` : "\u23F3 I've reached my usage limit for now, so I can't reply just yet. Your message is queued \u2014 I'll pick it up as soon as it lifts, no need to resend.";
@@ -39738,7 +39766,8 @@ function probeTmuxSession(tmuxSession) {
39738
39766
  return "alive";
39739
39767
  } catch (err) {
39740
39768
  const e = err;
39741
- if (e?.code === "ENOENT") return "unknown";
39769
+ const timedOut = e?.code === "ETIMEDOUT" || e?.error?.code === "ETIMEDOUT";
39770
+ if (e?.code === "ENOENT" || e?.error?.code === "ENOENT" || timedOut) return "unknown";
39742
39771
  return "dead";
39743
39772
  }
39744
39773
  }
@@ -41310,7 +41339,10 @@ function postDirectChatBusyAckNotice(sessionId) {
41310
41339
  void (async () => {
41311
41340
  const ok = await postDirectChatNoticeMessage(
41312
41341
  sessionId,
41313
- busyAckNoticeText(readUsageLimitForBusyAck(DIRECT_CHAT_CODE_NAME_AGENT_DIR))
41342
+ busyAckNoticeText(
41343
+ readUsageLimitForBusyAck(DIRECT_CHAT_CODE_NAME_AGENT_DIR),
41344
+ readBlockedPromptForBusyAck(DIRECT_CHAT_CODE_NAME_AGENT_DIR)
41345
+ )
41314
41346
  );
41315
41347
  if (!ok) {
41316
41348
  lastBusyAckNoticeAt.delete(sessionId);
@@ -41405,6 +41437,8 @@ function scheduleDirectChatBusyAck(sessionId, messageId, arrivedWhileBusy) {
41405
41437
  pendingAgeMs,
41406
41438
  sessionAlive,
41407
41439
  paneLogFreshAgeMs,
41440
+ // ENG-10972: a live watchdog marker stands in for pane freshness (see BusyAckInputs).
41441
+ liveStateMarker: Boolean(readUsageLimitForBusyAck(DIRECT_CHAT_CODE_NAME_AGENT_DIR) || readBlockedPromptForBusyAck(DIRECT_CHAT_CODE_NAME_AGENT_DIR)),
41408
41442
  thresholdMs
41409
41443
  })) {
41410
41444
  recordChannelDeflection(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "direct-chat", "busy");
package/dist/mcp/index.js CHANGED
@@ -25548,13 +25548,18 @@ var OAUTH_PROVIDERS = {
25548
25548
  // existing Xero install must RECONNECT for its token to carry this — the
25549
25549
  // broker posts journals with the agent's own OAuth access_token.
25550
25550
  "accounting.manualjournals",
25551
- // accounting.reports.read → granular read-only replacements
25551
+ // accounting.reports.read → granular read-only replacements, ONE PER REPORT
25552
+ // TOOL THE SERVER REGISTERS (ENG-10961 follow-on). Bank summary, budget
25553
+ // summary and executive summary were requested here with no tool reading
25554
+ // them: #6463 removed the phantom `report-bank-summary` /
25555
+ // `report-budget-summary` names, and nothing ever called the executive
25556
+ // summary. Asking a customer to consent to read access nothing uses is the
25557
+ // least-privilege failure the granular list exists to avoid. Removing a
25558
+ // default can never raise a reconnect prompt - `computeMissingScopes` only
25559
+ // reports defaults the grant LACKS.
25552
25560
  "accounting.reports.balancesheet.read",
25553
25561
  "accounting.reports.profitandloss.read",
25554
25562
  "accounting.reports.trialbalance.read",
25555
- "accounting.reports.budgetsummary.read",
25556
- "accounting.reports.banksummary.read",
25557
- "accounting.reports.executivesummary.read",
25558
25563
  "accounting.reports.aged.read"
25559
25564
  ],
25560
25565
  // Read-only variant (ENG-6170): the `.read` granular scope for every
@@ -25577,9 +25582,6 @@ var OAUTH_PROVIDERS = {
25577
25582
  "accounting.reports.balancesheet.read",
25578
25583
  "accounting.reports.profitandloss.read",
25579
25584
  "accounting.reports.trialbalance.read",
25580
- "accounting.reports.budgetsummary.read",
25581
- "accounting.reports.banksummary.read",
25582
- "accounting.reports.executivesummary.read",
25583
25585
  "accounting.reports.aged.read"
25584
25586
  ],
25585
25587
  supportsRefresh: true,
@@ -39699,13 +39699,18 @@ var OAUTH_PROVIDERS = {
39699
39699
  // existing Xero install must RECONNECT for its token to carry this — the
39700
39700
  // broker posts journals with the agent's own OAuth access_token.
39701
39701
  "accounting.manualjournals",
39702
- // accounting.reports.read → granular read-only replacements
39702
+ // accounting.reports.read → granular read-only replacements, ONE PER REPORT
39703
+ // TOOL THE SERVER REGISTERS (ENG-10961 follow-on). Bank summary, budget
39704
+ // summary and executive summary were requested here with no tool reading
39705
+ // them: #6463 removed the phantom `report-bank-summary` /
39706
+ // `report-budget-summary` names, and nothing ever called the executive
39707
+ // summary. Asking a customer to consent to read access nothing uses is the
39708
+ // least-privilege failure the granular list exists to avoid. Removing a
39709
+ // default can never raise a reconnect prompt - `computeMissingScopes` only
39710
+ // reports defaults the grant LACKS.
39703
39711
  "accounting.reports.balancesheet.read",
39704
39712
  "accounting.reports.profitandloss.read",
39705
39713
  "accounting.reports.trialbalance.read",
39706
- "accounting.reports.budgetsummary.read",
39707
- "accounting.reports.banksummary.read",
39708
- "accounting.reports.executivesummary.read",
39709
39714
  "accounting.reports.aged.read"
39710
39715
  ],
39711
39716
  // Read-only variant (ENG-6170): the `.read` granular scope for every
@@ -39728,9 +39733,6 @@ var OAUTH_PROVIDERS = {
39728
39733
  "accounting.reports.balancesheet.read",
39729
39734
  "accounting.reports.profitandloss.read",
39730
39735
  "accounting.reports.trialbalance.read",
39731
- "accounting.reports.budgetsummary.read",
39732
- "accounting.reports.banksummary.read",
39733
- "accounting.reports.executivesummary.read",
39734
39736
  "accounting.reports.aged.read"
39735
39737
  ],
39736
39738
  supportsRefresh: true,
@@ -33916,13 +33916,18 @@ var OAUTH_PROVIDERS = {
33916
33916
  // existing Xero install must RECONNECT for its token to carry this — the
33917
33917
  // broker posts journals with the agent's own OAuth access_token.
33918
33918
  "accounting.manualjournals",
33919
- // accounting.reports.read → granular read-only replacements
33919
+ // accounting.reports.read → granular read-only replacements, ONE PER REPORT
33920
+ // TOOL THE SERVER REGISTERS (ENG-10961 follow-on). Bank summary, budget
33921
+ // summary and executive summary were requested here with no tool reading
33922
+ // them: #6463 removed the phantom `report-bank-summary` /
33923
+ // `report-budget-summary` names, and nothing ever called the executive
33924
+ // summary. Asking a customer to consent to read access nothing uses is the
33925
+ // least-privilege failure the granular list exists to avoid. Removing a
33926
+ // default can never raise a reconnect prompt - `computeMissingScopes` only
33927
+ // reports defaults the grant LACKS.
33920
33928
  "accounting.reports.balancesheet.read",
33921
33929
  "accounting.reports.profitandloss.read",
33922
33930
  "accounting.reports.trialbalance.read",
33923
- "accounting.reports.budgetsummary.read",
33924
- "accounting.reports.banksummary.read",
33925
- "accounting.reports.executivesummary.read",
33926
33931
  "accounting.reports.aged.read"
33927
33932
  ],
33928
33933
  // Read-only variant (ENG-6170): the `.read` granular scope for every
@@ -33945,9 +33950,6 @@ var OAUTH_PROVIDERS = {
33945
33950
  "accounting.reports.balancesheet.read",
33946
33951
  "accounting.reports.profitandloss.read",
33947
33952
  "accounting.reports.trialbalance.read",
33948
- "accounting.reports.budgetsummary.read",
33949
- "accounting.reports.banksummary.read",
33950
- "accounting.reports.executivesummary.read",
33951
33953
  "accounting.reports.aged.read"
33952
33954
  ],
33953
33955
  supportsRefresh: true,
@@ -39964,13 +39966,17 @@ function decideBusyAck(i) {
39964
39966
  const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
39965
39967
  if (i.pendingAgeMs < threshold) return false;
39966
39968
  if (!i.sessionAlive) return false;
39969
+ if (i.liveStateMarker) return true;
39967
39970
  const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
39968
39971
  return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
39969
39972
  }
39970
- function busyAckNoticeText(usageLimit) {
39973
+ function busyAckNoticeText(usageLimit, blockedOnPrompt) {
39971
39974
  if (usageLimit) return giveUpNoticeText("usage_limit", usageLimit.resetsHint);
39972
- return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
39975
+ if (blockedOnPrompt) return BLOCKED_ON_PROMPT_NOTICE;
39976
+ return BUSY_NOTICE;
39973
39977
  }
39978
+ var BUSY_NOTICE = "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
39979
+ var BLOCKED_ON_PROMPT_NOTICE = "\u26A0\uFE0F I'm stuck on a prompt I can't answer myself, so I can't get to this right now. This isn't me ignoring you - it needs someone on our side to clear it before I can continue.";
39974
39980
  var GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
39975
39981
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
39976
39982
  function readGiveUpSignal(path, now = Date.now()) {
@@ -40007,6 +40013,28 @@ function readUsageLimitForBusyAck(agentDir, now = Date.now()) {
40007
40013
  if (now - lastSeenMs > USAGE_LIMIT_LIVE_WINDOW_MS) return null;
40008
40014
  return { resetsHint: sig.resetsHint };
40009
40015
  }
40016
+ var BLOCKED_PROMPT_SIGNAL_FILENAME = "blocked-prompt.json";
40017
+ var BLOCKED_PROMPT_KINDS = ["login-picker", "usage-limit-unanswerable", "unclaimed-modal"];
40018
+ var BLOCKED_PROMPT_LIVE_WINDOW_MS = 3 * 6e4;
40019
+ function readBlockedPromptForBusyAck(agentDir, now = Date.now()) {
40020
+ if (!agentDir) return null;
40021
+ let raw;
40022
+ try {
40023
+ raw = JSON.parse(readFileSync3(join3(agentDir, BLOCKED_PROMPT_SIGNAL_FILENAME), "utf8"));
40024
+ } catch {
40025
+ return null;
40026
+ }
40027
+ if (typeof raw !== "object" || raw === null) return null;
40028
+ const kind = raw.kind;
40029
+ if (typeof kind !== "string" || !BLOCKED_PROMPT_KINDS.includes(kind)) {
40030
+ return null;
40031
+ }
40032
+ if (typeof raw.last_seen_at !== "string") return null;
40033
+ const t = Date.parse(raw.last_seen_at);
40034
+ if (!Number.isFinite(t) || t > now) return null;
40035
+ if (now - t > BLOCKED_PROMPT_LIVE_WINDOW_MS) return null;
40036
+ return { kind };
40037
+ }
40010
40038
  function decideGiveUpNotice(input) {
40011
40039
  const maxAge = input.maxAgeMs ?? GIVE_UP_SIGNAL_MAX_AGE_MS;
40012
40040
  if (input.signalAtMs == null) return false;
@@ -40192,7 +40220,8 @@ function probeTmuxSession(tmuxSession) {
40192
40220
  return "alive";
40193
40221
  } catch (err) {
40194
40222
  const e = err;
40195
- if (e?.code === "ENOENT") return "unknown";
40223
+ const timedOut = e?.code === "ETIMEDOUT" || e?.error?.code === "ETIMEDOUT";
40224
+ if (e?.code === "ENOENT" || e?.error?.code === "ENOENT" || timedOut) return "unknown";
40196
40225
  return "dead";
40197
40226
  }
40198
40227
  }
@@ -45457,7 +45486,10 @@ function postBusyAckNotice(channel, threadTs, isThreadReply) {
45457
45486
  body: JSON.stringify({
45458
45487
  channel,
45459
45488
  // ENG-10972: say RATE-LIMITED rather than "busy" when that is the truth.
45460
- text: busyAckNoticeText(readUsageLimitForBusyAck(SLACK_AGENT_DIR)),
45489
+ text: busyAckNoticeText(
45490
+ readUsageLimitForBusyAck(SLACK_AGENT_DIR),
45491
+ readBlockedPromptForBusyAck(SLACK_AGENT_DIR)
45492
+ ),
45461
45493
  ...threadTs ? { thread_ts: threadTs } : {}
45462
45494
  }),
45463
45495
  // Bound the request so a hung Slack call can't dangle to the platform
@@ -45490,6 +45522,8 @@ function scheduleBusyAck(channel, threadTs, messageTs, isThreadReply, arrivedWhi
45490
45522
  pendingAgeMs: Math.max(0, Date.now() - Date.parse(marker.received_at)),
45491
45523
  sessionAlive: probe.tmux === "alive" && probe.claude === "alive",
45492
45524
  paneLogFreshAgeMs,
45525
+ // ENG-10972: a live watchdog marker stands in for pane freshness (see BusyAckInputs).
45526
+ liveStateMarker: Boolean(readUsageLimitForBusyAck(SLACK_AGENT_DIR) || readBlockedPromptForBusyAck(SLACK_AGENT_DIR)),
45493
45527
  thresholdMs
45494
45528
  });
45495
45529
  if (post) {
@@ -34025,13 +34025,18 @@ var OAUTH_PROVIDERS = {
34025
34025
  // existing Xero install must RECONNECT for its token to carry this — the
34026
34026
  // broker posts journals with the agent's own OAuth access_token.
34027
34027
  "accounting.manualjournals",
34028
- // accounting.reports.read → granular read-only replacements
34028
+ // accounting.reports.read → granular read-only replacements, ONE PER REPORT
34029
+ // TOOL THE SERVER REGISTERS (ENG-10961 follow-on). Bank summary, budget
34030
+ // summary and executive summary were requested here with no tool reading
34031
+ // them: #6463 removed the phantom `report-bank-summary` /
34032
+ // `report-budget-summary` names, and nothing ever called the executive
34033
+ // summary. Asking a customer to consent to read access nothing uses is the
34034
+ // least-privilege failure the granular list exists to avoid. Removing a
34035
+ // default can never raise a reconnect prompt - `computeMissingScopes` only
34036
+ // reports defaults the grant LACKS.
34029
34037
  "accounting.reports.balancesheet.read",
34030
34038
  "accounting.reports.profitandloss.read",
34031
34039
  "accounting.reports.trialbalance.read",
34032
- "accounting.reports.budgetsummary.read",
34033
- "accounting.reports.banksummary.read",
34034
- "accounting.reports.executivesummary.read",
34035
34040
  "accounting.reports.aged.read"
34036
34041
  ],
34037
34042
  // Read-only variant (ENG-6170): the `.read` granular scope for every
@@ -34054,9 +34059,6 @@ var OAUTH_PROVIDERS = {
34054
34059
  "accounting.reports.balancesheet.read",
34055
34060
  "accounting.reports.profitandloss.read",
34056
34061
  "accounting.reports.trialbalance.read",
34057
- "accounting.reports.budgetsummary.read",
34058
- "accounting.reports.banksummary.read",
34059
- "accounting.reports.executivesummary.read",
34060
34062
  "accounting.reports.aged.read"
34061
34063
  ],
34062
34064
  supportsRefresh: true,
@@ -40696,7 +40698,8 @@ function probeTmuxSession(tmuxSession) {
40696
40698
  return "alive";
40697
40699
  } catch (err) {
40698
40700
  const e = err;
40699
- if (e?.code === "ENOENT") return "unknown";
40701
+ const timedOut = e?.code === "ETIMEDOUT" || e?.error?.code === "ETIMEDOUT";
40702
+ if (e?.code === "ENOENT" || e?.error?.code === "ENOENT" || timedOut) return "unknown";
40700
40703
  return "dead";
40701
40704
  }
40702
40705
  }
@@ -42686,13 +42689,17 @@ function decideBusyAck(i) {
42686
42689
  const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
42687
42690
  if (i.pendingAgeMs < threshold) return false;
42688
42691
  if (!i.sessionAlive) return false;
42692
+ if (i.liveStateMarker) return true;
42689
42693
  const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
42690
42694
  return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
42691
42695
  }
42692
- function busyAckNoticeText(usageLimit) {
42696
+ function busyAckNoticeText(usageLimit, blockedOnPrompt) {
42693
42697
  if (usageLimit) return giveUpNoticeText("usage_limit", usageLimit.resetsHint);
42694
- return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
42698
+ if (blockedOnPrompt) return BLOCKED_ON_PROMPT_NOTICE;
42699
+ return BUSY_NOTICE;
42695
42700
  }
42701
+ var BUSY_NOTICE = "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
42702
+ var BLOCKED_ON_PROMPT_NOTICE = "\u26A0\uFE0F I'm stuck on a prompt I can't answer myself, so I can't get to this right now. This isn't me ignoring you - it needs someone on our side to clear it before I can continue.";
42696
42703
  var GIVE_UP_SIGNAL_FILENAME = "watchdog-give-up.json";
42697
42704
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
42698
42705
  function readGiveUpSignal(path, now = Date.now()) {
@@ -42729,6 +42736,28 @@ function readUsageLimitForBusyAck(agentDir, now = Date.now()) {
42729
42736
  if (now - lastSeenMs > USAGE_LIMIT_LIVE_WINDOW_MS) return null;
42730
42737
  return { resetsHint: sig.resetsHint };
42731
42738
  }
42739
+ var BLOCKED_PROMPT_SIGNAL_FILENAME = "blocked-prompt.json";
42740
+ var BLOCKED_PROMPT_KINDS = ["login-picker", "usage-limit-unanswerable", "unclaimed-modal"];
42741
+ var BLOCKED_PROMPT_LIVE_WINDOW_MS = 3 * 6e4;
42742
+ function readBlockedPromptForBusyAck(agentDir, now = Date.now()) {
42743
+ if (!agentDir) return null;
42744
+ let raw;
42745
+ try {
42746
+ raw = JSON.parse(readFileSync13(join15(agentDir, BLOCKED_PROMPT_SIGNAL_FILENAME), "utf8"));
42747
+ } catch {
42748
+ return null;
42749
+ }
42750
+ if (typeof raw !== "object" || raw === null) return null;
42751
+ const kind = raw.kind;
42752
+ if (typeof kind !== "string" || !BLOCKED_PROMPT_KINDS.includes(kind)) {
42753
+ return null;
42754
+ }
42755
+ if (typeof raw.last_seen_at !== "string") return null;
42756
+ const t = Date.parse(raw.last_seen_at);
42757
+ if (!Number.isFinite(t) || t > now) return null;
42758
+ if (now - t > BLOCKED_PROMPT_LIVE_WINDOW_MS) return null;
42759
+ return { kind };
42760
+ }
42732
42761
  function decideGiveUpNotice(input) {
42733
42762
  const maxAge = input.maxAgeMs ?? GIVE_UP_SIGNAL_MAX_AGE_MS;
42734
42763
  if (input.signalAtMs == null) return false;
@@ -43563,7 +43592,10 @@ function postBusyAckNotice(chatId, messageId) {
43563
43592
  {
43564
43593
  chat_id: chatId,
43565
43594
  // ENG-10972: say RATE-LIMITED rather than "busy" when that is the truth.
43566
- text: busyAckNoticeText(readUsageLimitForBusyAck(TELEGRAM_AGENT_DIR)),
43595
+ text: busyAckNoticeText(
43596
+ readUsageLimitForBusyAck(TELEGRAM_AGENT_DIR),
43597
+ readBlockedPromptForBusyAck(TELEGRAM_AGENT_DIR)
43598
+ ),
43567
43599
  reply_to_message_id: Number(messageId),
43568
43600
  allow_sending_without_reply: true
43569
43601
  },
@@ -43606,6 +43638,8 @@ function scheduleBusyAck(chatId, messageId, arrivedWhileBusy) {
43606
43638
  pendingAgeMs: Math.max(0, Date.now() - Date.parse(marker.received_at)),
43607
43639
  sessionAlive: probe.tmux === "alive" && probe.claude === "alive",
43608
43640
  paneLogFreshAgeMs,
43641
+ // ENG-10972: a live watchdog marker stands in for pane freshness (see BusyAckInputs).
43642
+ liveStateMarker: Boolean(readUsageLimitForBusyAck(TELEGRAM_AGENT_DIR) || readBlockedPromptForBusyAck(TELEGRAM_AGENT_DIR)),
43609
43643
  thresholdMs
43610
43644
  });
43611
43645
  if (post) {
@@ -65,8 +65,8 @@ import {
65
65
  writeDirectChatSessionState,
66
66
  writeEgressAllowlist,
67
67
  writePersistentClaudeWrapper
68
- } from "./chunk-QXJWRE64.js";
69
- import "./chunk-X7SX25N6.js";
68
+ } from "./chunk-5XPZF6FT.js";
69
+ import "./chunk-BHKFVSIX.js";
70
70
  import "./chunk-DHWNVVX4.js";
71
71
  import "./chunk-XWVM4KPK.js";
72
72
  import "./chunk-EL5MWGGH.js";
@@ -139,4 +139,4 @@ export {
139
139
  writeEgressAllowlist,
140
140
  writePersistentClaudeWrapper
141
141
  };
142
- //# sourceMappingURL=persistent-session-ZQDNCKQH.js.map
142
+ //# sourceMappingURL=persistent-session-GZV4EXKD.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-QXJWRE64.js";
4
- import "./chunk-X7SX25N6.js";
3
+ } from "./chunk-5XPZF6FT.js";
4
+ import "./chunk-BHKFVSIX.js";
5
5
  import "./chunk-DHWNVVX4.js";
6
6
  import "./chunk-XWVM4KPK.js";
7
7
  import "./chunk-EL5MWGGH.js";
@@ -802,4 +802,4 @@ export {
802
802
  readAndResetSlackReplyBindingClassifications,
803
803
  readAndResetSlackReplyTargetClassifications
804
804
  };
805
- //# sourceMappingURL=responsiveness-probe-Q27WU2MW.js.map
805
+ //# sourceMappingURL=responsiveness-probe-WRHLHQCE.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionTranscriptDir
3
- } from "./chunk-X7SX25N6.js";
3
+ } from "./chunk-BHKFVSIX.js";
4
4
  import "./chunk-DGUM43GV.js";
5
5
 
6
6
  // src/lib/session-auth-dead.ts
@@ -204,4 +204,4 @@ export {
204
204
  decideSessionAuthState,
205
205
  probeSessionAuth
206
206
  };
207
- //# sourceMappingURL=session-auth-dead-JJUKS75B.js.map
207
+ //# sourceMappingURL=session-auth-dead-ZKC2HH6T.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.1022",
3
+ "version": "0.28.1024",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {