@integrity-labs/agt-cli 0.28.896 → 0.28.897

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.
@@ -19,7 +19,7 @@ import {
19
19
  rotateDailySession,
20
20
  sessionFileExists,
21
21
  todayLocalIso
22
- } from "./chunk-5464FJVN.js";
22
+ } from "./chunk-HQKGVPIV.js";
23
23
  import {
24
24
  reapOrphanChannelMcps
25
25
  } from "./chunk-XWVM4KPK.js";
@@ -5730,4 +5730,4 @@ export {
5730
5730
  stopAllSessionsAndWait,
5731
5731
  getProjectDir
5732
5732
  };
5733
- //# sourceMappingURL=chunk-RAEBKUW2.js.map
5733
+ //# sourceMappingURL=chunk-IVJVMR3S.js.map
@@ -16,7 +16,7 @@ import {
16
16
  parseEnvIntegrations,
17
17
  shellQuote,
18
18
  summarizeUnanswerablePane
19
- } from "./chunk-RAEBKUW2.js";
19
+ } from "./chunk-IVJVMR3S.js";
20
20
  import {
21
21
  BIND_FAILURE_QUARANTINE_THRESHOLD,
22
22
  INTEGRATIONS_SECTION_END,
@@ -76,7 +76,7 @@ import {
76
76
  sessionTranscriptDir,
77
77
  worseConnectivityOutcome,
78
78
  wrapScheduledTaskPrompt
79
- } from "./chunk-5464FJVN.js";
79
+ } from "./chunk-HQKGVPIV.js";
80
80
  import {
81
81
  parsePsRows
82
82
  } from "./chunk-XWVM4KPK.js";
@@ -6841,7 +6841,7 @@ function exchangeFailureKind(err) {
6841
6841
  }
6842
6842
 
6843
6843
  // src/lib/api-client.ts
6844
- var agtCliVersion = true ? "0.28.896" : "dev";
6844
+ var agtCliVersion = true ? "0.28.897" : "dev";
6845
6845
  var lastConfigHash = null;
6846
6846
  function setConfigHash(hash) {
6847
6847
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -11451,4 +11451,4 @@ export {
11451
11451
  managerInstallSystemUnitCommand,
11452
11452
  managerUninstallSystemUnitCommand
11453
11453
  };
11454
- //# sourceMappingURL=chunk-HKG6EMAM.js.map
11454
+ //# sourceMappingURL=chunk-SYTV34UM.js.map
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-5HSYQBF7.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-6QB3XNY2.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-3MK5MUPN.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-CUFG7UQD.js.map
@@ -63,7 +63,7 @@ import {
63
63
  safeWriteJsonAtomic,
64
64
  setConfigHash,
65
65
  tripClass
66
- } from "../chunk-HKG6EMAM.js";
66
+ } from "../chunk-SYTV34UM.js";
67
67
  import {
68
68
  getProjectDir as getProjectDir2,
69
69
  getReadyTasks,
@@ -136,7 +136,7 @@ import {
136
136
  takeZombieDetection,
137
137
  toOpencodeModel,
138
138
  writeEgressAllowlist
139
- } from "../chunk-RAEBKUW2.js";
139
+ } from "../chunk-IVJVMR3S.js";
140
140
  import {
141
141
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
142
142
  AnchorSessionClient,
@@ -231,7 +231,7 @@ import {
231
231
  subagentActivityAgeSeconds,
232
232
  sumTranscriptUsageInWindow,
233
233
  transcriptActivityAgeSeconds
234
- } from "../chunk-5464FJVN.js";
234
+ } from "../chunk-HQKGVPIV.js";
235
235
  import {
236
236
  reapOrphanChannelMcps
237
237
  } from "../chunk-XWVM4KPK.js";
@@ -13003,7 +13003,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
13003
13003
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
13004
13004
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
13005
13005
  async function channelInboundActivityAgeSecondsFor(codeName) {
13006
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-ZSB3DLLI.js");
13006
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-ULU4DHEO.js");
13007
13007
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
13008
13008
  if (newest === null) return null;
13009
13009
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -13671,7 +13671,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
13671
13671
  var lastVersionCheckAt = 0;
13672
13672
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
13673
13673
  var lastResponsivenessProbeAt = 0;
13674
- var agtCliVersion = true ? "0.28.896" : "dev";
13674
+ var agtCliVersion = true ? "0.28.897" : "dev";
13675
13675
  function resolveBrewPath(execFileSync2) {
13676
13676
  try {
13677
13677
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15261,7 +15261,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
15261
15261
  if (codeNames.length === 0) return;
15262
15262
  void (async () => {
15263
15263
  try {
15264
- const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
15264
+ const { collectDiagnostics } = await import("../persistent-session-6QB3XNY2.js");
15265
15265
  await api.post("/host/heartbeat", {
15266
15266
  host_id: hostId,
15267
15267
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -15403,7 +15403,7 @@ async function pollCycleInner() {
15403
15403
  }
15404
15404
  try {
15405
15405
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
15406
- const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
15406
+ const { collectDiagnostics } = await import("../persistent-session-6QB3XNY2.js");
15407
15407
  const diagCodeNames = [...agentState.persistentSessionAgents];
15408
15408
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
15409
15409
  let tailscaleHostname;
@@ -15572,7 +15572,7 @@ async function pollCycleInner() {
15572
15572
  collectPanelessActivityProbes,
15573
15573
  getResponsivenessIntervalMs,
15574
15574
  occupancyQualificationClassifications
15575
- } = await import("../responsiveness-probe-ZSB3DLLI.js");
15575
+ } = await import("../responsiveness-probe-ULU4DHEO.js");
15576
15576
  const probeIntervalMs = getResponsivenessIntervalMs();
15577
15577
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
15578
15578
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -15689,7 +15689,7 @@ async function pollCycleInner() {
15689
15689
  collectResponsivenessProbes,
15690
15690
  livePendingInboundOldestAgeSeconds,
15691
15691
  parkPendingInbound
15692
- } = await import("../responsiveness-probe-ZSB3DLLI.js");
15692
+ } = await import("../responsiveness-probe-ULU4DHEO.js");
15693
15693
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
15694
15694
  const wedgeNow = /* @__PURE__ */ new Date();
15695
15695
  const liveAgents = agentState.persistentSessionAgents;
@@ -15845,7 +15845,7 @@ async function pollCycleInner() {
15845
15845
  }
15846
15846
  try {
15847
15847
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
15848
- const { probeSessionAuth } = await import("../session-auth-dead-J7SQAYRR.js");
15848
+ const { probeSessionAuth } = await import("../session-auth-dead-UXZ5DDKQ.js");
15849
15849
  const observations = [];
15850
15850
  const pendingCacheCommits = [];
15851
15851
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -20103,7 +20103,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20103
20103
  try {
20104
20104
  const freshId = rotateSessionForWedge(codeName);
20105
20105
  consecutiveWedgeCycles.delete(codeName);
20106
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-ZSB3DLLI.js");
20106
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-ULU4DHEO.js");
20107
20107
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
20108
20108
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
20109
20109
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -20146,7 +20146,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20146
20146
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
20147
20147
  void (async () => {
20148
20148
  try {
20149
- const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
20149
+ const { collectDiagnostics } = await import("../persistent-session-6QB3XNY2.js");
20150
20150
  await api.post("/host/heartbeat", {
20151
20151
  host_id: hostId,
20152
20152
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20197,7 +20197,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
20197
20197
  }
20198
20198
  try {
20199
20199
  const hostId = await getHostId();
20200
- const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
20200
+ const { collectDiagnostics } = await import("../persistent-session-6QB3XNY2.js");
20201
20201
  await api.post("/host/heartbeat", {
20202
20202
  host_id: hostId,
20203
20203
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20839,7 +20839,7 @@ async function processClaudePairSessions(agents) {
20839
20839
  killPairSession,
20840
20840
  pairTmuxSession,
20841
20841
  finalizeClaudePairOnboarding
20842
- } = await import("../claude-pair-runtime-3MK5MUPN.js");
20842
+ } = await import("../claude-pair-runtime-CUFG7UQD.js");
20843
20843
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
20844
20844
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
20845
20845
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -35085,12 +35085,15 @@ function parseResetDateTime(humanDate, now) {
35085
35085
 
35086
35086
  // ../core/dist/claude-code-usage/banner-parser.js
35087
35087
  var SESSION_QUALIFIER = /^(?:session|\d{1,2}\s*-?\s*hours?|\d{1,2}h)$/i;
35088
+ var SPEND_QUALIFIER = /^(?:individual\s+)?spend$/i;
35088
35089
  function classifyLimitScope(qualifier) {
35089
35090
  const q = (qualifier ?? "").trim();
35090
35091
  if (!q)
35091
35092
  return "weekly";
35092
35093
  if (SESSION_QUALIFIER.test(q))
35093
35094
  return "session";
35095
+ if (SPEND_QUALIFIER.test(q))
35096
+ return "spend";
35094
35097
  if (/^weekly$/i.test(q))
35095
35098
  return "weekly";
35096
35099
  return "unknown";
@@ -35127,7 +35130,58 @@ var BANNER_PATTERNS = [
35127
35130
  // bounding the count, keeps it linear.
35128
35131
  `${SUBJECT}hit\\s+your\\s+(?<qualifier>[a-z0-9]+(?:[\\s-]+[a-z0-9]+){0,3}\\s+)?limit${SEP}resets\\s+(?<reset>${RESET_DATE})`,
35129
35132
  "i"
35130
- )
35133
+ ),
35134
+ // ENG-10365 — the Claude Team seat's INDIVIDUAL SPEND LIMIT.
35135
+ //
35136
+ // WHY THE TWO PATTERNS ABOVE CANNOT MATCH IT. Both require `limit` to be
35137
+ // followed immediately by a separator and then `resets`. The spend banner puts
35138
+ // its remedy between the two:
35139
+ //
35140
+ // You've hit your individual spend limit · run /usage-credits to ask your
35141
+ // admin for a higher limit · your weekly limit resets Sep 12, 5pm (UTC)
35142
+ // Continuing automatically at Sep 12, 5pm · esc to cancel
35143
+ //
35144
+ // so the adjacency fails and `parseUsageBanner` returned null — verified
35145
+ // against origin/main on the verbatim pane capture in ENG-10365 before this
35146
+ // pattern was written. Null means NO observation is POSTed, which means the
35147
+ // `claude_code_usage_observations` row never exists, which means
35148
+ // `usage-limit-monitor` never hears of the agent at all. cindy ran zero-second
35149
+ // turns for ~32 hours with `heartbeat_verdict fresh` and no usage alert of any
35150
+ // kind, because the whole chain starts here.
35151
+ //
35152
+ // THE GAP IS BOUNDED AND LAZY, deliberately. The real banner's gap is ~78
35153
+ // characters; 160 leaves 2x headroom for phrasing drift and for the pane's own
35154
+ // line wrap (which lands mid-sentence, as above, so `[\s\S]` and not `.`).
35155
+ // Lazy + bounded keeps it linear on the pane.log tails this parser runs over —
35156
+ // the same reason the saturated pattern refuses the naive `(a+)+` shape.
35157
+ //
35158
+ // WHAT IT INHERITS BY MATCHING AT ALL. Everything downstream keys on the
35159
+ // `usage_limit_reached` ALERT KIND rather than on the scope, so one matching
35160
+ // banner reaches the roster's `usage_limit_alert_open` flag (ENG-9634),
35161
+ // `resolveCappedAgents`, and both monitors that suppress on a capped agent.
35162
+ // That is why this fix is a regex and not a subsystem.
35163
+ //
35164
+ // AND A SECOND CONSUMER, which is worth stating precisely because it is easy
35165
+ // to overclaim. `rate-limit-classifier.ts:160` calls this parser to read the
35166
+ // RESET TIME out of a `rate_limit` / 429 transcript refusal, and
35167
+ // `readUsageCapUntil` (agent-serving-probe.ts:296) returns null — i.e. does NOT
35168
+ // defer — when that reset comes back null. So a parse failure here has always
35169
+ // disarmed the ENG-8197 host-side dispatch deferral for this banner, which
35170
+ // would explain cindy being dispatched into hourly while visibly blocked.
35171
+ //
35172
+ // NOT VERIFIED, and deliberately not claimed: whether a spend refusal is
35173
+ // written to the transcript as `error: 'rate_limit'` / `apiErrorStatus: 429`
35174
+ // at all. The classifier's verdict keys on those fields, not on this text. If
35175
+ // it is, the deferral starts working as a side effect of this fix; if it is
35176
+ // not, ENG-8197 is still blind to the spend ceiling and that is a separate
35177
+ // ticket. Nothing here depends on which.
35178
+ //
35179
+ // KNOWN LIMIT, stated rather than papered over: a spend banner that carries no
35180
+ // `resets` clause at all still yields null. `week_resets_at` drives the
35181
+ // monitor's auto-close, and synthesising an instant we never read is the exact
35182
+ // confident-and-false reporting ENG-8901 exists to stop. Every spend banner
35183
+ // observed so far carries one.
35184
+ new RegExp(`${SUBJECT}hit\\s+your\\s+(?<qualifier>(?:individual\\s+)?spend)\\s+limit[\\s\\S]{0,160}?resets\\s+(?<reset>${RESET_DATE})`, "i")
35131
35185
  ];
35132
35186
  function parseUsageBanner(text, now = /* @__PURE__ */ new Date()) {
35133
35187
  let bestIndex = -1;
package/dist/mcp/index.js CHANGED
@@ -27729,7 +27729,58 @@ var BANNER_PATTERNS = [
27729
27729
  // bounding the count, keeps it linear.
27730
27730
  `${SUBJECT}hit\\s+your\\s+(?<qualifier>[a-z0-9]+(?:[\\s-]+[a-z0-9]+){0,3}\\s+)?limit${SEP}resets\\s+(?<reset>${RESET_DATE})`,
27731
27731
  "i"
27732
- )
27732
+ ),
27733
+ // ENG-10365 — the Claude Team seat's INDIVIDUAL SPEND LIMIT.
27734
+ //
27735
+ // WHY THE TWO PATTERNS ABOVE CANNOT MATCH IT. Both require `limit` to be
27736
+ // followed immediately by a separator and then `resets`. The spend banner puts
27737
+ // its remedy between the two:
27738
+ //
27739
+ // You've hit your individual spend limit · run /usage-credits to ask your
27740
+ // admin for a higher limit · your weekly limit resets Sep 12, 5pm (UTC)
27741
+ // Continuing automatically at Sep 12, 5pm · esc to cancel
27742
+ //
27743
+ // so the adjacency fails and `parseUsageBanner` returned null — verified
27744
+ // against origin/main on the verbatim pane capture in ENG-10365 before this
27745
+ // pattern was written. Null means NO observation is POSTed, which means the
27746
+ // `claude_code_usage_observations` row never exists, which means
27747
+ // `usage-limit-monitor` never hears of the agent at all. cindy ran zero-second
27748
+ // turns for ~32 hours with `heartbeat_verdict fresh` and no usage alert of any
27749
+ // kind, because the whole chain starts here.
27750
+ //
27751
+ // THE GAP IS BOUNDED AND LAZY, deliberately. The real banner's gap is ~78
27752
+ // characters; 160 leaves 2x headroom for phrasing drift and for the pane's own
27753
+ // line wrap (which lands mid-sentence, as above, so `[\s\S]` and not `.`).
27754
+ // Lazy + bounded keeps it linear on the pane.log tails this parser runs over —
27755
+ // the same reason the saturated pattern refuses the naive `(a+)+` shape.
27756
+ //
27757
+ // WHAT IT INHERITS BY MATCHING AT ALL. Everything downstream keys on the
27758
+ // `usage_limit_reached` ALERT KIND rather than on the scope, so one matching
27759
+ // banner reaches the roster's `usage_limit_alert_open` flag (ENG-9634),
27760
+ // `resolveCappedAgents`, and both monitors that suppress on a capped agent.
27761
+ // That is why this fix is a regex and not a subsystem.
27762
+ //
27763
+ // AND A SECOND CONSUMER, which is worth stating precisely because it is easy
27764
+ // to overclaim. `rate-limit-classifier.ts:160` calls this parser to read the
27765
+ // RESET TIME out of a `rate_limit` / 429 transcript refusal, and
27766
+ // `readUsageCapUntil` (agent-serving-probe.ts:296) returns null — i.e. does NOT
27767
+ // defer — when that reset comes back null. So a parse failure here has always
27768
+ // disarmed the ENG-8197 host-side dispatch deferral for this banner, which
27769
+ // would explain cindy being dispatched into hourly while visibly blocked.
27770
+ //
27771
+ // NOT VERIFIED, and deliberately not claimed: whether a spend refusal is
27772
+ // written to the transcript as `error: 'rate_limit'` / `apiErrorStatus: 429`
27773
+ // at all. The classifier's verdict keys on those fields, not on this text. If
27774
+ // it is, the deferral starts working as a side effect of this fix; if it is
27775
+ // not, ENG-8197 is still blind to the spend ceiling and that is a separate
27776
+ // ticket. Nothing here depends on which.
27777
+ //
27778
+ // KNOWN LIMIT, stated rather than papered over: a spend banner that carries no
27779
+ // `resets` clause at all still yields null. `week_resets_at` drives the
27780
+ // monitor's auto-close, and synthesising an instant we never read is the exact
27781
+ // confident-and-false reporting ENG-8901 exists to stop. Every spend banner
27782
+ // observed so far carries one.
27783
+ new RegExp(`${SUBJECT}hit\\s+your\\s+(?<qualifier>(?:individual\\s+)?spend)\\s+limit[\\s\\S]{0,160}?resets\\s+(?<reset>${RESET_DATE})`, "i")
27733
27784
  ];
27734
27785
 
27735
27786
  // ../core/dist/claude-code-usage/run-marker.js
@@ -41678,7 +41678,58 @@ var BANNER_PATTERNS = [
41678
41678
  // bounding the count, keeps it linear.
41679
41679
  `${SUBJECT}hit\\s+your\\s+(?<qualifier>[a-z0-9]+(?:[\\s-]+[a-z0-9]+){0,3}\\s+)?limit${SEP}resets\\s+(?<reset>${RESET_DATE})`,
41680
41680
  "i"
41681
- )
41681
+ ),
41682
+ // ENG-10365 — the Claude Team seat's INDIVIDUAL SPEND LIMIT.
41683
+ //
41684
+ // WHY THE TWO PATTERNS ABOVE CANNOT MATCH IT. Both require `limit` to be
41685
+ // followed immediately by a separator and then `resets`. The spend banner puts
41686
+ // its remedy between the two:
41687
+ //
41688
+ // You've hit your individual spend limit · run /usage-credits to ask your
41689
+ // admin for a higher limit · your weekly limit resets Sep 12, 5pm (UTC)
41690
+ // Continuing automatically at Sep 12, 5pm · esc to cancel
41691
+ //
41692
+ // so the adjacency fails and `parseUsageBanner` returned null — verified
41693
+ // against origin/main on the verbatim pane capture in ENG-10365 before this
41694
+ // pattern was written. Null means NO observation is POSTed, which means the
41695
+ // `claude_code_usage_observations` row never exists, which means
41696
+ // `usage-limit-monitor` never hears of the agent at all. cindy ran zero-second
41697
+ // turns for ~32 hours with `heartbeat_verdict fresh` and no usage alert of any
41698
+ // kind, because the whole chain starts here.
41699
+ //
41700
+ // THE GAP IS BOUNDED AND LAZY, deliberately. The real banner's gap is ~78
41701
+ // characters; 160 leaves 2x headroom for phrasing drift and for the pane's own
41702
+ // line wrap (which lands mid-sentence, as above, so `[\s\S]` and not `.`).
41703
+ // Lazy + bounded keeps it linear on the pane.log tails this parser runs over —
41704
+ // the same reason the saturated pattern refuses the naive `(a+)+` shape.
41705
+ //
41706
+ // WHAT IT INHERITS BY MATCHING AT ALL. Everything downstream keys on the
41707
+ // `usage_limit_reached` ALERT KIND rather than on the scope, so one matching
41708
+ // banner reaches the roster's `usage_limit_alert_open` flag (ENG-9634),
41709
+ // `resolveCappedAgents`, and both monitors that suppress on a capped agent.
41710
+ // That is why this fix is a regex and not a subsystem.
41711
+ //
41712
+ // AND A SECOND CONSUMER, which is worth stating precisely because it is easy
41713
+ // to overclaim. `rate-limit-classifier.ts:160` calls this parser to read the
41714
+ // RESET TIME out of a `rate_limit` / 429 transcript refusal, and
41715
+ // `readUsageCapUntil` (agent-serving-probe.ts:296) returns null — i.e. does NOT
41716
+ // defer — when that reset comes back null. So a parse failure here has always
41717
+ // disarmed the ENG-8197 host-side dispatch deferral for this banner, which
41718
+ // would explain cindy being dispatched into hourly while visibly blocked.
41719
+ //
41720
+ // NOT VERIFIED, and deliberately not claimed: whether a spend refusal is
41721
+ // written to the transcript as `error: 'rate_limit'` / `apiErrorStatus: 429`
41722
+ // at all. The classifier's verdict keys on those fields, not on this text. If
41723
+ // it is, the deferral starts working as a side effect of this fix; if it is
41724
+ // not, ENG-8197 is still blind to the spend ceiling and that is a separate
41725
+ // ticket. Nothing here depends on which.
41726
+ //
41727
+ // KNOWN LIMIT, stated rather than papered over: a spend banner that carries no
41728
+ // `resets` clause at all still yields null. `week_resets_at` drives the
41729
+ // monitor's auto-close, and synthesising an instant we never read is the exact
41730
+ // confident-and-false reporting ENG-8901 exists to stop. Every spend banner
41731
+ // observed so far carries one.
41732
+ new RegExp(`${SUBJECT}hit\\s+your\\s+(?<qualifier>(?:individual\\s+)?spend)\\s+limit[\\s\\S]{0,160}?resets\\s+(?<reset>${RESET_DATE})`, "i")
41682
41733
  ];
41683
41734
 
41684
41735
  // ../core/dist/claude-code-usage/run-marker.js
@@ -35942,12 +35942,15 @@ function parseResetDateTime(humanDate, now) {
35942
35942
 
35943
35943
  // ../core/dist/claude-code-usage/banner-parser.js
35944
35944
  var SESSION_QUALIFIER = /^(?:session|\d{1,2}\s*-?\s*hours?|\d{1,2}h)$/i;
35945
+ var SPEND_QUALIFIER = /^(?:individual\s+)?spend$/i;
35945
35946
  function classifyLimitScope(qualifier) {
35946
35947
  const q = (qualifier ?? "").trim();
35947
35948
  if (!q)
35948
35949
  return "weekly";
35949
35950
  if (SESSION_QUALIFIER.test(q))
35950
35951
  return "session";
35952
+ if (SPEND_QUALIFIER.test(q))
35953
+ return "spend";
35951
35954
  if (/^weekly$/i.test(q))
35952
35955
  return "weekly";
35953
35956
  return "unknown";
@@ -35984,7 +35987,58 @@ var BANNER_PATTERNS = [
35984
35987
  // bounding the count, keeps it linear.
35985
35988
  `${SUBJECT}hit\\s+your\\s+(?<qualifier>[a-z0-9]+(?:[\\s-]+[a-z0-9]+){0,3}\\s+)?limit${SEP}resets\\s+(?<reset>${RESET_DATE})`,
35986
35989
  "i"
35987
- )
35990
+ ),
35991
+ // ENG-10365 — the Claude Team seat's INDIVIDUAL SPEND LIMIT.
35992
+ //
35993
+ // WHY THE TWO PATTERNS ABOVE CANNOT MATCH IT. Both require `limit` to be
35994
+ // followed immediately by a separator and then `resets`. The spend banner puts
35995
+ // its remedy between the two:
35996
+ //
35997
+ // You've hit your individual spend limit · run /usage-credits to ask your
35998
+ // admin for a higher limit · your weekly limit resets Sep 12, 5pm (UTC)
35999
+ // Continuing automatically at Sep 12, 5pm · esc to cancel
36000
+ //
36001
+ // so the adjacency fails and `parseUsageBanner` returned null — verified
36002
+ // against origin/main on the verbatim pane capture in ENG-10365 before this
36003
+ // pattern was written. Null means NO observation is POSTed, which means the
36004
+ // `claude_code_usage_observations` row never exists, which means
36005
+ // `usage-limit-monitor` never hears of the agent at all. cindy ran zero-second
36006
+ // turns for ~32 hours with `heartbeat_verdict fresh` and no usage alert of any
36007
+ // kind, because the whole chain starts here.
36008
+ //
36009
+ // THE GAP IS BOUNDED AND LAZY, deliberately. The real banner's gap is ~78
36010
+ // characters; 160 leaves 2x headroom for phrasing drift and for the pane's own
36011
+ // line wrap (which lands mid-sentence, as above, so `[\s\S]` and not `.`).
36012
+ // Lazy + bounded keeps it linear on the pane.log tails this parser runs over —
36013
+ // the same reason the saturated pattern refuses the naive `(a+)+` shape.
36014
+ //
36015
+ // WHAT IT INHERITS BY MATCHING AT ALL. Everything downstream keys on the
36016
+ // `usage_limit_reached` ALERT KIND rather than on the scope, so one matching
36017
+ // banner reaches the roster's `usage_limit_alert_open` flag (ENG-9634),
36018
+ // `resolveCappedAgents`, and both monitors that suppress on a capped agent.
36019
+ // That is why this fix is a regex and not a subsystem.
36020
+ //
36021
+ // AND A SECOND CONSUMER, which is worth stating precisely because it is easy
36022
+ // to overclaim. `rate-limit-classifier.ts:160` calls this parser to read the
36023
+ // RESET TIME out of a `rate_limit` / 429 transcript refusal, and
36024
+ // `readUsageCapUntil` (agent-serving-probe.ts:296) returns null — i.e. does NOT
36025
+ // defer — when that reset comes back null. So a parse failure here has always
36026
+ // disarmed the ENG-8197 host-side dispatch deferral for this banner, which
36027
+ // would explain cindy being dispatched into hourly while visibly blocked.
36028
+ //
36029
+ // NOT VERIFIED, and deliberately not claimed: whether a spend refusal is
36030
+ // written to the transcript as `error: 'rate_limit'` / `apiErrorStatus: 429`
36031
+ // at all. The classifier's verdict keys on those fields, not on this text. If
36032
+ // it is, the deferral starts working as a side effect of this fix; if it is
36033
+ // not, ENG-8197 is still blind to the spend ceiling and that is a separate
36034
+ // ticket. Nothing here depends on which.
36035
+ //
36036
+ // KNOWN LIMIT, stated rather than papered over: a spend banner that carries no
36037
+ // `resets` clause at all still yields null. `week_resets_at` drives the
36038
+ // monitor's auto-close, and synthesising an instant we never read is the exact
36039
+ // confident-and-false reporting ENG-8901 exists to stop. Every spend banner
36040
+ // observed so far carries one.
36041
+ new RegExp(`${SUBJECT}hit\\s+your\\s+(?<qualifier>(?:individual\\s+)?spend)\\s+limit[\\s\\S]{0,160}?resets\\s+(?<reset>${RESET_DATE})`, "i")
35988
36042
  ];
35989
36043
  function parseUsageBanner(text, now = /* @__PURE__ */ new Date()) {
35990
36044
  let bestIndex = -1;
@@ -35992,12 +35992,15 @@ function parseResetDateTime(humanDate, now) {
35992
35992
 
35993
35993
  // ../core/dist/claude-code-usage/banner-parser.js
35994
35994
  var SESSION_QUALIFIER = /^(?:session|\d{1,2}\s*-?\s*hours?|\d{1,2}h)$/i;
35995
+ var SPEND_QUALIFIER = /^(?:individual\s+)?spend$/i;
35995
35996
  function classifyLimitScope(qualifier) {
35996
35997
  const q = (qualifier ?? "").trim();
35997
35998
  if (!q)
35998
35999
  return "weekly";
35999
36000
  if (SESSION_QUALIFIER.test(q))
36000
36001
  return "session";
36002
+ if (SPEND_QUALIFIER.test(q))
36003
+ return "spend";
36001
36004
  if (/^weekly$/i.test(q))
36002
36005
  return "weekly";
36003
36006
  return "unknown";
@@ -36034,7 +36037,58 @@ var BANNER_PATTERNS = [
36034
36037
  // bounding the count, keeps it linear.
36035
36038
  `${SUBJECT}hit\\s+your\\s+(?<qualifier>[a-z0-9]+(?:[\\s-]+[a-z0-9]+){0,3}\\s+)?limit${SEP}resets\\s+(?<reset>${RESET_DATE})`,
36036
36039
  "i"
36037
- )
36040
+ ),
36041
+ // ENG-10365 — the Claude Team seat's INDIVIDUAL SPEND LIMIT.
36042
+ //
36043
+ // WHY THE TWO PATTERNS ABOVE CANNOT MATCH IT. Both require `limit` to be
36044
+ // followed immediately by a separator and then `resets`. The spend banner puts
36045
+ // its remedy between the two:
36046
+ //
36047
+ // You've hit your individual spend limit · run /usage-credits to ask your
36048
+ // admin for a higher limit · your weekly limit resets Sep 12, 5pm (UTC)
36049
+ // Continuing automatically at Sep 12, 5pm · esc to cancel
36050
+ //
36051
+ // so the adjacency fails and `parseUsageBanner` returned null — verified
36052
+ // against origin/main on the verbatim pane capture in ENG-10365 before this
36053
+ // pattern was written. Null means NO observation is POSTed, which means the
36054
+ // `claude_code_usage_observations` row never exists, which means
36055
+ // `usage-limit-monitor` never hears of the agent at all. cindy ran zero-second
36056
+ // turns for ~32 hours with `heartbeat_verdict fresh` and no usage alert of any
36057
+ // kind, because the whole chain starts here.
36058
+ //
36059
+ // THE GAP IS BOUNDED AND LAZY, deliberately. The real banner's gap is ~78
36060
+ // characters; 160 leaves 2x headroom for phrasing drift and for the pane's own
36061
+ // line wrap (which lands mid-sentence, as above, so `[\s\S]` and not `.`).
36062
+ // Lazy + bounded keeps it linear on the pane.log tails this parser runs over —
36063
+ // the same reason the saturated pattern refuses the naive `(a+)+` shape.
36064
+ //
36065
+ // WHAT IT INHERITS BY MATCHING AT ALL. Everything downstream keys on the
36066
+ // `usage_limit_reached` ALERT KIND rather than on the scope, so one matching
36067
+ // banner reaches the roster's `usage_limit_alert_open` flag (ENG-9634),
36068
+ // `resolveCappedAgents`, and both monitors that suppress on a capped agent.
36069
+ // That is why this fix is a regex and not a subsystem.
36070
+ //
36071
+ // AND A SECOND CONSUMER, which is worth stating precisely because it is easy
36072
+ // to overclaim. `rate-limit-classifier.ts:160` calls this parser to read the
36073
+ // RESET TIME out of a `rate_limit` / 429 transcript refusal, and
36074
+ // `readUsageCapUntil` (agent-serving-probe.ts:296) returns null — i.e. does NOT
36075
+ // defer — when that reset comes back null. So a parse failure here has always
36076
+ // disarmed the ENG-8197 host-side dispatch deferral for this banner, which
36077
+ // would explain cindy being dispatched into hourly while visibly blocked.
36078
+ //
36079
+ // NOT VERIFIED, and deliberately not claimed: whether a spend refusal is
36080
+ // written to the transcript as `error: 'rate_limit'` / `apiErrorStatus: 429`
36081
+ // at all. The classifier's verdict keys on those fields, not on this text. If
36082
+ // it is, the deferral starts working as a side effect of this fix; if it is
36083
+ // not, ENG-8197 is still blind to the spend ceiling and that is a separate
36084
+ // ticket. Nothing here depends on which.
36085
+ //
36086
+ // KNOWN LIMIT, stated rather than papered over: a spend banner that carries no
36087
+ // `resets` clause at all still yields null. `week_resets_at` drives the
36088
+ // monitor's auto-close, and synthesising an instant we never read is the exact
36089
+ // confident-and-false reporting ENG-8901 exists to stop. Every spend banner
36090
+ // observed so far carries one.
36091
+ new RegExp(`${SUBJECT}hit\\s+your\\s+(?<qualifier>(?:individual\\s+)?spend)\\s+limit[\\s\\S]{0,160}?resets\\s+(?<reset>${RESET_DATE})`, "i")
36038
36092
  ];
36039
36093
  function parseUsageBanner(text, now = /* @__PURE__ */ new Date()) {
36040
36094
  let bestIndex = -1;
@@ -62,8 +62,8 @@ import {
62
62
  writeDirectChatSessionState,
63
63
  writeEgressAllowlist,
64
64
  writePersistentClaudeWrapper
65
- } from "./chunk-RAEBKUW2.js";
66
- import "./chunk-5464FJVN.js";
65
+ } from "./chunk-IVJVMR3S.js";
66
+ import "./chunk-HQKGVPIV.js";
67
67
  import "./chunk-XWVM4KPK.js";
68
68
  export {
69
69
  CLAUDE_HOST_WIDE,
@@ -130,4 +130,4 @@ export {
130
130
  writeEgressAllowlist,
131
131
  writePersistentClaudeWrapper
132
132
  };
133
- //# sourceMappingURL=persistent-session-5HSYQBF7.js.map
133
+ //# sourceMappingURL=persistent-session-6QB3XNY2.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-RAEBKUW2.js";
4
- import "./chunk-5464FJVN.js";
3
+ } from "./chunk-IVJVMR3S.js";
4
+ import "./chunk-HQKGVPIV.js";
5
5
  import "./chunk-XWVM4KPK.js";
6
6
 
7
7
  // src/lib/responsiveness-probe.ts
@@ -764,4 +764,4 @@ export {
764
764
  readAndResetSlackReplyBindingClassifications,
765
765
  readAndResetSlackReplyTargetClassifications
766
766
  };
767
- //# sourceMappingURL=responsiveness-probe-ZSB3DLLI.js.map
767
+ //# sourceMappingURL=responsiveness-probe-ULU4DHEO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionTranscriptDir
3
- } from "./chunk-5464FJVN.js";
3
+ } from "./chunk-HQKGVPIV.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-J7SQAYRR.js.map
206
+ //# sourceMappingURL=session-auth-dead-UXZ5DDKQ.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.896",
3
+ "version": "0.28.897",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {