@integrity-labs/agt-cli 0.28.442 → 0.28.443

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.
package/dist/bin/agt.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-JIZ6Z6EX.js";
43
+ } from "../chunk-X2LHWP6E.js";
44
44
  import {
45
45
  AnchorSessionClient,
46
46
  CHANNEL_REGISTRY,
@@ -71,7 +71,7 @@ import {
71
71
  requiredMcpWildcard,
72
72
  resolveChannels,
73
73
  serializeManifestForSlackCli
74
- } from "../chunk-XMJ5VMWV.js";
74
+ } from "../chunk-IQX7TMYZ.js";
75
75
  import "../chunk-XWVM4KPK.js";
76
76
 
77
77
  // src/bin/agt.ts
@@ -4830,7 +4830,7 @@ import { execFileSync, execSync } from "child_process";
4830
4830
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4831
4831
  import chalk18 from "chalk";
4832
4832
  import ora16 from "ora";
4833
- var cliVersion = true ? "0.28.442" : "dev";
4833
+ var cliVersion = true ? "0.28.443" : "dev";
4834
4834
  async function fetchLatestVersion() {
4835
4835
  const host2 = getHost();
4836
4836
  if (!host2) return null;
@@ -6002,7 +6002,7 @@ function handleError(err) {
6002
6002
  }
6003
6003
 
6004
6004
  // src/bin/agt.ts
6005
- var cliVersion2 = true ? "0.28.442" : "dev";
6005
+ var cliVersion2 = true ? "0.28.443" : "dev";
6006
6006
  var program = new Command();
6007
6007
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
6008
6008
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -5800,10 +5800,10 @@ async function probeComposioMcpToolCall(config, fetchImpl = fetch) {
5800
5800
  }
5801
5801
  const rpcErrMsg = callRpc && "error" in callRpc ? callRpc["error"]?.message ?? "" : "";
5802
5802
  const result = callRpc?.["result"];
5803
- const contentText = (result?.content ?? []).map((c) => c.text ?? "").join(" ").trim();
5804
- const failed = Boolean(rpcErrMsg) || Boolean(result?.isError) || isComposioFailureEnvelope(contentText);
5803
+ const contentText2 = (result?.content ?? []).map((c) => c.text ?? "").join(" ").trim();
5804
+ const failed = Boolean(rpcErrMsg) || Boolean(result?.isError) || isComposioFailureEnvelope(contentText2);
5805
5805
  if (failed) {
5806
- const failureText = [rpcErrMsg, contentText].filter(Boolean).join(" ");
5806
+ const failureText = [rpcErrMsg, contentText2].filter(Boolean).join(" ");
5807
5807
  const snippet = failureText.length > 200 ? `${failureText.slice(0, 200)}\u2026` : failureText;
5808
5808
  const kind = classifyToolCallFailure(failureText);
5809
5809
  if (kind === "account") {
@@ -8510,6 +8510,109 @@ function resolveUsageLimitUntil(marker, now) {
8510
8510
  return until.getTime() > now.getTime() ? until : null;
8511
8511
  }
8512
8512
 
8513
+ // ../../packages/core/dist/claude-code-usage/rate-limit-classifier.js
8514
+ var UNKNOWN_RATE_LIMIT = Object.freeze({
8515
+ verdict: "unknown",
8516
+ atMs: null,
8517
+ resetsAt: null,
8518
+ text: null
8519
+ });
8520
+ function contentText(record) {
8521
+ const candidates = [record.content];
8522
+ const message = record.message;
8523
+ if (typeof message === "object" && message !== null) {
8524
+ candidates.push(message.content);
8525
+ }
8526
+ const parts = [];
8527
+ for (const candidate of candidates) {
8528
+ if (typeof candidate === "string") {
8529
+ if (candidate)
8530
+ parts.push(candidate);
8531
+ continue;
8532
+ }
8533
+ if (!Array.isArray(candidate))
8534
+ continue;
8535
+ for (const block of candidate) {
8536
+ if (typeof block === "string") {
8537
+ if (block)
8538
+ parts.push(block);
8539
+ continue;
8540
+ }
8541
+ if (typeof block !== "object" || block === null)
8542
+ continue;
8543
+ const text = block.text;
8544
+ if (typeof text === "string" && text)
8545
+ parts.push(text);
8546
+ }
8547
+ }
8548
+ const joined = parts.join("\n").trim();
8549
+ return joined ? joined : null;
8550
+ }
8551
+ function classifyTranscriptLine(line2, startMs, endMs, now) {
8552
+ const trimmed = line2.trim();
8553
+ if (!trimmed)
8554
+ return null;
8555
+ let obj;
8556
+ try {
8557
+ obj = JSON.parse(trimmed);
8558
+ } catch {
8559
+ return null;
8560
+ }
8561
+ if (typeof obj !== "object" || obj === null)
8562
+ return null;
8563
+ const record = obj;
8564
+ if (record.type !== "assistant")
8565
+ return null;
8566
+ const ts = record.timestamp;
8567
+ if (typeof ts !== "string" || !ts)
8568
+ return null;
8569
+ const tsMs = new Date(ts).getTime();
8570
+ if (!Number.isFinite(tsMs) || tsMs < startMs || tsMs > endMs)
8571
+ return null;
8572
+ if (record.error === "rate_limit" || record.apiErrorStatus === 429) {
8573
+ const text = contentText(record);
8574
+ const observation = text ? parseUsageBanner(text, now ?? new Date(endMs)) : null;
8575
+ return { verdict: "capped", atMs: tsMs, resetsAt: observation?.weekResetsAt ?? null, text };
8576
+ }
8577
+ if (record.isApiErrorMessage === true)
8578
+ return null;
8579
+ const message = record.message;
8580
+ if (typeof message !== "object" || message === null)
8581
+ return null;
8582
+ const msg = message;
8583
+ if (msg.model === "<synthetic>")
8584
+ return null;
8585
+ const usage = msg.usage;
8586
+ if (typeof usage !== "object" || usage === null)
8587
+ return null;
8588
+ const u = usage;
8589
+ const spent = Number(u.input_tokens ?? 0) + Number(u.output_tokens ?? 0) + Number(u.cache_creation_input_tokens ?? 0) + Number(u.cache_read_input_tokens ?? 0);
8590
+ if (!Number.isFinite(spent) || spent <= 0)
8591
+ return null;
8592
+ return { verdict: "serving", atMs: tsMs, resetsAt: null, text: null };
8593
+ }
8594
+ function pickNewerClassification(current, next) {
8595
+ if (next.verdict === "unknown")
8596
+ return current;
8597
+ if (current.verdict === "unknown")
8598
+ return next;
8599
+ return next.atMs >= current.atMs ? next : current;
8600
+ }
8601
+ function classifyTranscriptRateLimit(jsonl, startMs, endMs, now) {
8602
+ let newest = UNKNOWN_RATE_LIMIT;
8603
+ for (const line2 of jsonl.split("\n")) {
8604
+ const classified = classifyTranscriptLine(line2, startMs, endMs, now);
8605
+ if (classified)
8606
+ newest = pickNewerClassification(newest, classified);
8607
+ }
8608
+ return newest;
8609
+ }
8610
+
8611
+ // ../../packages/core/dist/claude-code-usage/transcript-location.js
8612
+ function encodeClaudeProjectPath(projectDir) {
8613
+ return "-" + projectDir.replace(/^\//, "").replace(/[/.]/g, "-");
8614
+ }
8615
+
8513
8616
  // ../../packages/core/dist/account-enforcement/marker.js
8514
8617
  var ACCOUNT_ENFORCEMENT_MARKER_FILENAME = "account-enforcement.json";
8515
8618
  var ACCOUNT_ENFORCEMENT_MARKER_VERSION = 1;
@@ -9216,6 +9319,33 @@ var FLAG_REGISTRY = [
9216
9319
  // projectDefinition, so setting it does not roll FLAGS_SCHEMA_VERSION.
9217
9320
  since: "0.28.421"
9218
9321
  },
9322
+ {
9323
+ key: "usage-limit-reactive-notice",
9324
+ 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).",
9325
+ flagType: "enum",
9326
+ allowedValues: ["off", "shadow", "enforce"],
9327
+ // Ships dark: off preserves today's gate byte-for-byte. shadow is a free
9328
+ // local log line (the watch is a transcript read, no model spend), so it is a
9329
+ // cheap soak; enforce changes what reaches the agent, so it is the audited
9330
+ // per-org flip.
9331
+ defaultValue: "off",
9332
+ // Enum override AGT_USAGE_LIMIT_REACTIVE_MODE (off|shadow|enforce), resolved
9333
+ // by resolveUsageLimitReactiveMode in the channel-server bundle.
9334
+ envVar: "AGT_USAGE_LIMIT_REACTIVE_MODE",
9335
+ // enforce sends a message to an agent the host currently believes is capped -
9336
+ // a deliberate availability trade (the refusal is free, but it is still a
9337
+ // dispatch the operator previously suppressed), so flipping toward it is an
9338
+ // audited change (ADR-0022 sensitive-flag confirm).
9339
+ sensitive: true
9340
+ // ENG-8149: `since` is the agt-cli version that first carries
9341
+ // resolveUsageLimitReactiveMode + the MCP watcher. It cannot be known before
9342
+ // this lands (the auto-publish patch-bumps on merge), and an undefined
9343
+ // `since` makes the flip-reach modal claim FULL reach - true for flags that
9344
+ // predate the reach work, wrong for a NEW host-read flag whose reader older
9345
+ // hosts simply do not have. Backfilled by a follow-up commit once the
9346
+ // publishing run reports the version. Excluded from projectDefinition, so
9347
+ // setting it does not roll FLAGS_SCHEMA_VERSION.
9348
+ },
9219
9349
  {
9220
9350
  key: "slack-hot-thread-guard",
9221
9351
  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.",
@@ -12462,9 +12592,7 @@ function rotateDailySession(codeName, now = /* @__PURE__ */ new Date(), timezone
12462
12592
  writeFile(codeName, { current: next, history });
12463
12593
  return next.sessionId;
12464
12594
  }
12465
- function encodeProjectPath(projectDir) {
12466
- return "-" + projectDir.replace(/^\//, "").replace(/[/.]/g, "-");
12467
- }
12595
+ var encodeProjectPath = encodeClaudeProjectPath;
12468
12596
  function sessionFileExists(projectDir, sessionId) {
12469
12597
  const path = join4(
12470
12598
  homedir4(),
@@ -12966,7 +13094,7 @@ function restartEgressSidecar(codeName) {
12966
13094
  }
12967
13095
  }
12968
13096
  function buildDockerRunCommand(args) {
12969
- const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch } = args;
13097
+ const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch, forwardUsageLimitReactive } = args;
12970
13098
  const q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
12971
13099
  const agentDir2 = join5(homeDir, ".augmented", codeName);
12972
13100
  const agentIdDir = join5(homeDir, ".augmented", agentId);
@@ -13003,6 +13131,7 @@ function buildDockerRunCommand(args) {
13003
13131
  if (forwardBlockTurnEndAllMarkers) envArgs.push("-e AGT_BLOCK_TURN_END_ALL_MARKERS_ENABLED");
13004
13132
  if (forwardKanbanWaiting) envArgs.push("-e AGT_KANBAN_WAITING_ENABLED");
13005
13133
  if (forwardNotifyDispatch) envArgs.push("-e AGT_NOTIFY_DISPATCH");
13134
+ if (forwardUsageLimitReactive) envArgs.push("-e AGT_USAGE_LIMIT_REACTIVE_MODE");
13006
13135
  const egressImage = process.env.AGT_EGRESS_IMAGE || "agt-squid:latest";
13007
13136
  const internalNet = `agt-net-${codeName}`;
13008
13137
  const squidName = `agt-squid-${codeName}`;
@@ -13356,6 +13485,12 @@ function spawnSession(config, session) {
13356
13485
  if (config.notifyDispatchMode && config.notifyDispatchMode !== "off" && !process.env["AGT_NOTIFY_DISPATCH"]) {
13357
13486
  tmuxSessionEnvArgs.push("-e", `AGT_NOTIFY_DISPATCH=${config.notifyDispatchMode}`);
13358
13487
  }
13488
+ if (config.usageLimitReactiveMode && config.usageLimitReactiveMode !== "off" && !process.env["AGT_USAGE_LIMIT_REACTIVE_MODE"]) {
13489
+ tmuxSessionEnvArgs.push(
13490
+ "-e",
13491
+ `AGT_USAGE_LIMIT_REACTIVE_MODE=${config.usageLimitReactiveMode}`
13492
+ );
13493
+ }
13359
13494
  const sessionHomeDir = process.env.HOME?.trim() || homedir5();
13360
13495
  let egress;
13361
13496
  if (egressMode(codeName) === "allowlist") {
@@ -13388,7 +13523,13 @@ function spawnSession(config, session) {
13388
13523
  // ENG-7682 (notify Slice 1): forward AGT_NOTIFY_DISPATCH if it will be in
13389
13524
  // the session env (operator-set OR materialized from the flag above).
13390
13525
  forwardNotifyDispatch: !!process.env["AGT_NOTIFY_DISPATCH"] || // feature-gate-allow: registry-flag envVar operator-override precedence, not a new gate
13391
- !!config.notifyDispatchMode && config.notifyDispatchMode !== "off"
13526
+ !!config.notifyDispatchMode && config.notifyDispatchMode !== "off",
13527
+ // ENG-8201: forward AGT_USAGE_LIMIT_REACTIVE_MODE if it will be in the
13528
+ // session env (operator-set OR materialized from the flag above). This
13529
+ // is the ONLY way the flag reaches an isolated agent — the flags-cache
13530
+ // is not in the container's mount set.
13531
+ forwardUsageLimitReactive: !!process.env["AGT_USAGE_LIMIT_REACTIVE_MODE"] || // feature-gate-allow: registry-flag envVar operator-override precedence, not a new gate
13532
+ !!config.usageLimitReactiveMode && config.usageLimitReactiveMode !== "off"
13392
13533
  }) : JSON.stringify(wrapperPath);
13393
13534
  const tmuxEnv = {
13394
13535
  ...process.env,
@@ -14068,6 +14209,9 @@ export {
14068
14209
  serializeUsageLimitMarker,
14069
14210
  parseUsageLimitMarker,
14070
14211
  resolveUsageLimitUntil,
14212
+ UNKNOWN_RATE_LIMIT,
14213
+ pickNewerClassification,
14214
+ classifyTranscriptRateLimit,
14071
14215
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
14072
14216
  serializeAccountEnforcementMarker,
14073
14217
  KANBAN_CHECK_COMMAND,
@@ -14148,4 +14292,4 @@ export {
14148
14292
  stopAllSessionsAndWait,
14149
14293
  getProjectDir
14150
14294
  };
14151
- //# sourceMappingURL=chunk-XMJ5VMWV.js.map
14295
+ //# sourceMappingURL=chunk-IQX7TMYZ.js.map