@integrity-labs/agt-cli 0.28.442 → 0.28.444

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.
@@ -39805,6 +39805,14 @@ var BANNER_PATTERNS = [
39805
39805
  ];
39806
39806
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
39807
39807
 
39808
+ // ../core/dist/claude-code-usage/rate-limit-classifier.js
39809
+ var UNKNOWN_RATE_LIMIT = Object.freeze({
39810
+ verdict: "unknown",
39811
+ atMs: null,
39812
+ resetsAt: null,
39813
+ text: null
39814
+ });
39815
+
39808
39816
  // ../core/dist/kanban/state-machine.js
39809
39817
  var KANBAN_STATUSES = [
39810
39818
  "backlog",
@@ -40427,6 +40435,33 @@ var FLAG_REGISTRY = [
40427
40435
  // projectDefinition, so setting it does not roll FLAGS_SCHEMA_VERSION.
40428
40436
  since: "0.28.421"
40429
40437
  },
40438
+ {
40439
+ key: "usage-limit-reactive-notice",
40440
+ description: "Report a Claude Code usage cap REACTIVELY instead of predicting it (ENG-8201). Today the manager guesses from the agent transcript that the next turn will be refused, writes a marker, and every channel MCP refuses to dispatch on the strength of that guess - so a wrong guess is a swallowed message, and because the notice is throttled per (channel, sender) while the DROP is not, the usual symptom is total silence rather than a wrong reply. A refused turn actually costs nothing (rejected in under a second, zero tokens) and Claude Code records it with the reset time in it, so there is no need to guess: dispatch, and report the refusal if one comes back. off = today behaviour (pre-dispatch marker gate, no watcher). shadow = still gate on the marker, but ALSO watch dispatched messages and log the refusal that would have been reported - measures the true-positive rate with no user-visible change. enforce = stop reading the marker before dispatch; every admitted human message reaches the agent and a refusal is answered in-thread with the reset time from the error itself. Read live from the heartbeat flags-cache (or the env override).",
40441
+ flagType: "enum",
40442
+ allowedValues: ["off", "shadow", "enforce"],
40443
+ // Ships dark: off preserves today's gate byte-for-byte. shadow is a free
40444
+ // local log line (the watch is a transcript read, no model spend), so it is a
40445
+ // cheap soak; enforce changes what reaches the agent, so it is the audited
40446
+ // per-org flip.
40447
+ defaultValue: "off",
40448
+ // Enum override AGT_USAGE_LIMIT_REACTIVE_MODE (off|shadow|enforce), resolved
40449
+ // by resolveUsageLimitReactiveMode in the channel-server bundle.
40450
+ envVar: "AGT_USAGE_LIMIT_REACTIVE_MODE",
40451
+ // enforce sends a message to an agent the host currently believes is capped -
40452
+ // a deliberate availability trade (the refusal is free, but it is still a
40453
+ // dispatch the operator previously suppressed), so flipping toward it is an
40454
+ // audited change (ADR-0022 sensitive-flag confirm).
40455
+ sensitive: true
40456
+ // ENG-8149: `since` is the agt-cli version that first carries
40457
+ // resolveUsageLimitReactiveMode + the MCP watcher. It cannot be known before
40458
+ // this lands (the auto-publish patch-bumps on merge), and an undefined
40459
+ // `since` makes the flip-reach modal claim FULL reach - true for flags that
40460
+ // predate the reach work, wrong for a NEW host-read flag whose reader older
40461
+ // hosts simply do not have. Backfilled by a follow-up commit once the
40462
+ // publishing run reports the version. Excluded from projectDefinition, so
40463
+ // setting it does not roll FLAGS_SCHEMA_VERSION.
40464
+ },
40430
40465
  {
40431
40466
  key: "slack-hot-thread-guard",
40432
40467
  description: "Server-side hot-thread guard on the slack.reply surface (ENG-7462). Prevents an agent posting a NEW top-level Slack message when it meant to reply inside the thread it is already working in - a prompt/memory rule proved insufficient (the agent had the rule and still slipped). When a reply would otherwise post to channel ROOT (no thread_ts / message_ts / inbound_id, no active kanban card) and the agent has a recent active thread in that channel (its last bot-posted thread, from the persisted trackedThreads cache, within a freshness window), the reply is redirected into that thread. proactive:true no longer implies channel root; posting at root becomes a deliberate action (the to_channel_root flag, or the thread_ts:null sentinel). off = guard never runs, replies with no coords root exactly as today (ships dark). shadow = compute + log the would-redirect but STILL post to root (measure the fire rate before acting). enforce = apply the redirect (a soft-block: redirect + inform, never a hard rejection). Read live from the heartbeat flags-cache (or the env override); enforce is a deliberate per-org flip after a shadow soak.",
@@ -33772,6 +33772,14 @@ function buildUsageLimitReplyText(limitedUntil) {
33772
33772
  return `Your agent has hit its Claude Code usage limit until ${formatUtcClock(limitedUntil)}. It'll pick back up once the limit resets \u2014 please try again after then.`;
33773
33773
  }
33774
33774
 
33775
+ // ../core/dist/claude-code-usage/rate-limit-classifier.js
33776
+ var UNKNOWN_RATE_LIMIT = Object.freeze({
33777
+ verdict: "unknown",
33778
+ atMs: null,
33779
+ resetsAt: null,
33780
+ text: null
33781
+ });
33782
+
33775
33783
  // ../core/dist/account-enforcement/marker.js
33776
33784
  var ACCOUNT_ENFORCEMENT_MARKER_FILENAME = "account-enforcement.json";
33777
33785
  var ACCOUNT_ENFORCEMENT_MARKER_VERSION = 1;
@@ -34416,6 +34424,33 @@ var FLAG_REGISTRY = [
34416
34424
  // projectDefinition, so setting it does not roll FLAGS_SCHEMA_VERSION.
34417
34425
  since: "0.28.421"
34418
34426
  },
34427
+ {
34428
+ key: "usage-limit-reactive-notice",
34429
+ description: "Report a Claude Code usage cap REACTIVELY instead of predicting it (ENG-8201). Today the manager guesses from the agent transcript that the next turn will be refused, writes a marker, and every channel MCP refuses to dispatch on the strength of that guess - so a wrong guess is a swallowed message, and because the notice is throttled per (channel, sender) while the DROP is not, the usual symptom is total silence rather than a wrong reply. A refused turn actually costs nothing (rejected in under a second, zero tokens) and Claude Code records it with the reset time in it, so there is no need to guess: dispatch, and report the refusal if one comes back. off = today behaviour (pre-dispatch marker gate, no watcher). shadow = still gate on the marker, but ALSO watch dispatched messages and log the refusal that would have been reported - measures the true-positive rate with no user-visible change. enforce = stop reading the marker before dispatch; every admitted human message reaches the agent and a refusal is answered in-thread with the reset time from the error itself. Read live from the heartbeat flags-cache (or the env override).",
34430
+ flagType: "enum",
34431
+ allowedValues: ["off", "shadow", "enforce"],
34432
+ // Ships dark: off preserves today's gate byte-for-byte. shadow is a free
34433
+ // local log line (the watch is a transcript read, no model spend), so it is a
34434
+ // cheap soak; enforce changes what reaches the agent, so it is the audited
34435
+ // per-org flip.
34436
+ defaultValue: "off",
34437
+ // Enum override AGT_USAGE_LIMIT_REACTIVE_MODE (off|shadow|enforce), resolved
34438
+ // by resolveUsageLimitReactiveMode in the channel-server bundle.
34439
+ envVar: "AGT_USAGE_LIMIT_REACTIVE_MODE",
34440
+ // enforce sends a message to an agent the host currently believes is capped -
34441
+ // a deliberate availability trade (the refusal is free, but it is still a
34442
+ // dispatch the operator previously suppressed), so flipping toward it is an
34443
+ // audited change (ADR-0022 sensitive-flag confirm).
34444
+ sensitive: true
34445
+ // ENG-8149: `since` is the agt-cli version that first carries
34446
+ // resolveUsageLimitReactiveMode + the MCP watcher. It cannot be known before
34447
+ // this lands (the auto-publish patch-bumps on merge), and an undefined
34448
+ // `since` makes the flip-reach modal claim FULL reach - true for flags that
34449
+ // predate the reach work, wrong for a NEW host-read flag whose reader older
34450
+ // hosts simply do not have. Backfilled by a follow-up commit once the
34451
+ // publishing run reports the version. Excluded from projectDefinition, so
34452
+ // setting it does not roll FLAGS_SCHEMA_VERSION.
34453
+ },
34419
34454
  {
34420
34455
  key: "slack-hot-thread-guard",
34421
34456
  description: "Server-side hot-thread guard on the slack.reply surface (ENG-7462). Prevents an agent posting a NEW top-level Slack message when it meant to reply inside the thread it is already working in - a prompt/memory rule proved insufficient (the agent had the rule and still slipped). When a reply would otherwise post to channel ROOT (no thread_ts / message_ts / inbound_id, no active kanban card) and the agent has a recent active thread in that channel (its last bot-posted thread, from the persisted trackedThreads cache, within a freshness window), the reply is redirected into that thread. proactive:true no longer implies channel root; posting at root becomes a deliberate action (the to_channel_root flag, or the thread_ts:null sentinel). off = guard never runs, replies with no coords root exactly as today (ships dark). shadow = compute + log the would-redirect but STILL post to root (measure the fire rate before acting). enforce = apply the redirect (a soft-block: redirect + inform, never a hard rejection). Read live from the heartbeat flags-cache (or the env override); enforce is a deliberate per-org flip after a shadow soak.",
@@ -35424,6 +35459,15 @@ function shouldReplayMarker(i) {
35424
35459
  }
35425
35460
  return true;
35426
35461
  }
35462
+ function markerGcThresholdMs(i) {
35463
+ if (i.discretionary) {
35464
+ return i.discretionaryThresholdMs != null ? Math.min(i.thresholdMs, i.discretionaryThresholdMs) : i.thresholdMs;
35465
+ }
35466
+ if (i.replayEnabled && i.hasPayload && i.replayCount === 0) {
35467
+ return Math.max(i.thresholdMs, i.hardCeilingMs);
35468
+ }
35469
+ return i.thresholdMs;
35470
+ }
35427
35471
  function isMarkerGenuinelyAged(receivedAt, nowMs, thresholdMs) {
35428
35472
  const t = Date.parse(receivedAt ?? "");
35429
35473
  return Number.isFinite(t) && t <= nowMs && nowMs - t >= thresholdMs;
@@ -40046,6 +40090,7 @@ function sweepSlackStaleMarkers(thresholdMs) {
40046
40090
  return;
40047
40091
  }
40048
40092
  const now = Date.now();
40093
+ const replayEnabled = channelReplayEnabled();
40049
40094
  let cleared = 0;
40050
40095
  for (const filename of filenames) {
40051
40096
  if (!filename.endsWith(".json")) continue;
@@ -40067,7 +40112,15 @@ function sweepSlackStaleMarkers(thresholdMs) {
40067
40112
  continue;
40068
40113
  }
40069
40114
  const { channel, thread_ts, message_ts, received_at } = marker;
40070
- const effectiveThresholdMs = marker.discretionary ? Math.min(thresholdMs, DISCRETIONARY_MARKER_MS) : thresholdMs;
40115
+ const effectiveThresholdMs = markerGcThresholdMs({
40116
+ thresholdMs,
40117
+ hardCeilingMs: STALE_MARKER_MS,
40118
+ discretionaryThresholdMs: DISCRETIONARY_MARKER_MS,
40119
+ discretionary: marker.discretionary,
40120
+ hasPayload: Boolean(marker.payload),
40121
+ replayCount: marker.replay_count ?? 0,
40122
+ replayEnabled
40123
+ });
40071
40124
  const structurallyBroken = !channel || !thread_ts || !message_ts;
40072
40125
  const timeStale = isPendingMarkerStale(received_at, now, effectiveThresholdMs);
40073
40126
  if (structurallyBroken || timeStale) {
@@ -34074,6 +34074,14 @@ function buildUsageLimitReplyText(limitedUntil) {
34074
34074
  return `Your agent has hit its Claude Code usage limit until ${formatUtcClock(limitedUntil)}. It'll pick back up once the limit resets \u2014 please try again after then.`;
34075
34075
  }
34076
34076
 
34077
+ // ../core/dist/claude-code-usage/rate-limit-classifier.js
34078
+ var UNKNOWN_RATE_LIMIT = Object.freeze({
34079
+ verdict: "unknown",
34080
+ atMs: null,
34081
+ resetsAt: null,
34082
+ text: null
34083
+ });
34084
+
34077
34085
  // ../core/dist/account-enforcement/marker.js
34078
34086
  var ACCOUNT_ENFORCEMENT_MARKER_FILENAME = "account-enforcement.json";
34079
34087
  var ACCOUNT_ENFORCEMENT_MARKER_VERSION = 1;
@@ -34718,6 +34726,33 @@ var FLAG_REGISTRY = [
34718
34726
  // projectDefinition, so setting it does not roll FLAGS_SCHEMA_VERSION.
34719
34727
  since: "0.28.421"
34720
34728
  },
34729
+ {
34730
+ key: "usage-limit-reactive-notice",
34731
+ description: "Report a Claude Code usage cap REACTIVELY instead of predicting it (ENG-8201). Today the manager guesses from the agent transcript that the next turn will be refused, writes a marker, and every channel MCP refuses to dispatch on the strength of that guess - so a wrong guess is a swallowed message, and because the notice is throttled per (channel, sender) while the DROP is not, the usual symptom is total silence rather than a wrong reply. A refused turn actually costs nothing (rejected in under a second, zero tokens) and Claude Code records it with the reset time in it, so there is no need to guess: dispatch, and report the refusal if one comes back. off = today behaviour (pre-dispatch marker gate, no watcher). shadow = still gate on the marker, but ALSO watch dispatched messages and log the refusal that would have been reported - measures the true-positive rate with no user-visible change. enforce = stop reading the marker before dispatch; every admitted human message reaches the agent and a refusal is answered in-thread with the reset time from the error itself. Read live from the heartbeat flags-cache (or the env override).",
34732
+ flagType: "enum",
34733
+ allowedValues: ["off", "shadow", "enforce"],
34734
+ // Ships dark: off preserves today's gate byte-for-byte. shadow is a free
34735
+ // local log line (the watch is a transcript read, no model spend), so it is a
34736
+ // cheap soak; enforce changes what reaches the agent, so it is the audited
34737
+ // per-org flip.
34738
+ defaultValue: "off",
34739
+ // Enum override AGT_USAGE_LIMIT_REACTIVE_MODE (off|shadow|enforce), resolved
34740
+ // by resolveUsageLimitReactiveMode in the channel-server bundle.
34741
+ envVar: "AGT_USAGE_LIMIT_REACTIVE_MODE",
34742
+ // enforce sends a message to an agent the host currently believes is capped -
34743
+ // a deliberate availability trade (the refusal is free, but it is still a
34744
+ // dispatch the operator previously suppressed), so flipping toward it is an
34745
+ // audited change (ADR-0022 sensitive-flag confirm).
34746
+ sensitive: true
34747
+ // ENG-8149: `since` is the agt-cli version that first carries
34748
+ // resolveUsageLimitReactiveMode + the MCP watcher. It cannot be known before
34749
+ // this lands (the auto-publish patch-bumps on merge), and an undefined
34750
+ // `since` makes the flip-reach modal claim FULL reach - true for flags that
34751
+ // predate the reach work, wrong for a NEW host-read flag whose reader older
34752
+ // hosts simply do not have. Backfilled by a follow-up commit once the
34753
+ // publishing run reports the version. Excluded from projectDefinition, so
34754
+ // setting it does not roll FLAGS_SCHEMA_VERSION.
34755
+ },
34721
34756
  {
34722
34757
  key: "slack-hot-thread-guard",
34723
34758
  description: "Server-side hot-thread guard on the slack.reply surface (ENG-7462). Prevents an agent posting a NEW top-level Slack message when it meant to reply inside the thread it is already working in - a prompt/memory rule proved insufficient (the agent had the rule and still slipped). When a reply would otherwise post to channel ROOT (no thread_ts / message_ts / inbound_id, no active kanban card) and the agent has a recent active thread in that channel (its last bot-posted thread, from the persisted trackedThreads cache, within a freshness window), the reply is redirected into that thread. proactive:true no longer implies channel root; posting at root becomes a deliberate action (the to_channel_root flag, or the thread_ts:null sentinel). off = guard never runs, replies with no coords root exactly as today (ships dark). shadow = compute + log the would-redirect but STILL post to root (measure the fire rate before acting). enforce = apply the redirect (a soft-block: redirect + inform, never a hard rejection). Read live from the heartbeat flags-cache (or the env override); enforce is a deliberate per-org flip after a shadow soak.",
@@ -37870,6 +37905,15 @@ function shouldReplayMarker(i) {
37870
37905
  }
37871
37906
  return true;
37872
37907
  }
37908
+ function markerGcThresholdMs(i) {
37909
+ if (i.discretionary) {
37910
+ return i.discretionaryThresholdMs != null ? Math.min(i.thresholdMs, i.discretionaryThresholdMs) : i.thresholdMs;
37911
+ }
37912
+ if (i.replayEnabled && i.hasPayload && i.replayCount === 0) {
37913
+ return Math.max(i.thresholdMs, i.hardCeilingMs);
37914
+ }
37915
+ return i.thresholdMs;
37916
+ }
37873
37917
  function isMarkerGenuinelyAged(receivedAt, nowMs, thresholdMs) {
37874
37918
  const t = Date.parse(receivedAt ?? "");
37875
37919
  return Number.isFinite(t) && t <= nowMs && nowMs - t >= thresholdMs;
@@ -39816,6 +39860,7 @@ function sweepTelegramStaleMarkers(thresholdMs) {
39816
39860
  return;
39817
39861
  }
39818
39862
  const now = Date.now();
39863
+ const replayEnabled = channelReplayEnabled();
39819
39864
  let cleared = 0;
39820
39865
  for (const filename of filenames) {
39821
39866
  if (!filename.endsWith(".json")) continue;
@@ -39837,10 +39882,17 @@ function sweepTelegramStaleMarkers(thresholdMs) {
39837
39882
  continue;
39838
39883
  }
39839
39884
  const { chat_id, message_id, received_at } = marker;
39885
+ const effectiveThresholdMs = markerGcThresholdMs({
39886
+ thresholdMs,
39887
+ hardCeilingMs: STALE_MARKER_MS,
39888
+ hasPayload: Boolean(marker.payload),
39889
+ replayCount: marker.replay_count ?? 0,
39890
+ replayEnabled
39891
+ });
39840
39892
  const structurallyBroken = !chat_id || !message_id;
39841
- const timeStale = isPendingMarkerStale(received_at, now, thresholdMs);
39893
+ const timeStale = isPendingMarkerStale(received_at, now, effectiveThresholdMs);
39842
39894
  if (structurallyBroken || timeStale) {
39843
- if (!structurallyBroken && isMarkerGenuinelyAged(received_at, now, thresholdMs) && marker.payload) {
39895
+ if (!structurallyBroken && isMarkerGenuinelyAged(received_at, now, effectiveThresholdMs) && marker.payload) {
39844
39896
  recordChannelDeflection(
39845
39897
  AGENT_DIR,
39846
39898
  "telegram",
@@ -36,7 +36,7 @@ import {
36
36
  writeDirectChatSessionState,
37
37
  writeEgressAllowlist,
38
38
  writePersistentClaudeWrapper
39
- } from "./chunk-XMJ5VMWV.js";
39
+ } from "./chunk-IQX7TMYZ.js";
40
40
  import "./chunk-XWVM4KPK.js";
41
41
  export {
42
42
  EGRESS_BASELINE_DOMAINS,
@@ -77,4 +77,4 @@ export {
77
77
  writeEgressAllowlist,
78
78
  writePersistentClaudeWrapper
79
79
  };
80
- //# sourceMappingURL=persistent-session-VMWQYV5B.js.map
80
+ //# sourceMappingURL=persistent-session-6SCIPYQI.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-XMJ5VMWV.js";
3
+ } from "./chunk-IQX7TMYZ.js";
4
4
  import "./chunk-XWVM4KPK.js";
5
5
 
6
6
  // src/lib/responsiveness-probe.ts
@@ -471,4 +471,4 @@ export {
471
471
  readAndResetSlackReplyBindingClassifications,
472
472
  readAndResetSlackReplyTargetClassifications
473
473
  };
474
- //# sourceMappingURL=responsiveness-probe-WMJEL7IZ.js.map
474
+ //# sourceMappingURL=responsiveness-probe-WHDWK62P.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.442",
3
+ "version": "0.28.444",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {