@integrity-labs/agt-cli 0.28.1159 → 0.28.1160

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-XZ7QGK2M.js";
43
+ } from "../chunk-Y5W6WEJL.js";
44
44
  import {
45
45
  getProjectDir,
46
46
  isSessionResumeDisabled,
@@ -5513,7 +5513,7 @@ import { execFileSync, execSync } from "child_process";
5513
5513
  import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
5514
5514
  import chalk18 from "chalk";
5515
5515
  import ora16 from "ora";
5516
- var cliVersion = true ? "0.28.1159" : "dev";
5516
+ var cliVersion = true ? "0.28.1160" : "dev";
5517
5517
  async function fetchLatestVersion() {
5518
5518
  const host2 = getHost();
5519
5519
  if (!host2) return null;
@@ -6773,7 +6773,7 @@ function handleError(err) {
6773
6773
  }
6774
6774
 
6775
6775
  // src/bin/agt.ts
6776
- var cliVersion2 = true ? "0.28.1159" : "dev";
6776
+ var cliVersion2 = true ? "0.28.1160" : "dev";
6777
6777
  var program = new Command();
6778
6778
  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");
6779
6779
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -8078,7 +8078,7 @@ function exchangeFailureKind(err) {
8078
8078
  }
8079
8079
 
8080
8080
  // src/lib/api-client.ts
8081
- var agtCliVersion = true ? "0.28.1159" : "dev";
8081
+ var agtCliVersion = true ? "0.28.1160" : "dev";
8082
8082
  var lastConfigHash = null;
8083
8083
  function setConfigHash(hash) {
8084
8084
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -13235,4 +13235,4 @@ export {
13235
13235
  managerInstallSystemUnitCommand,
13236
13236
  managerUninstallSystemUnitCommand
13237
13237
  };
13238
- //# sourceMappingURL=chunk-XZ7QGK2M.js.map
13238
+ //# sourceMappingURL=chunk-Y5W6WEJL.js.map
@@ -77,7 +77,7 @@ import {
77
77
  setConfigHash,
78
78
  shellGuard,
79
79
  tripClass
80
- } from "../chunk-XZ7QGK2M.js";
80
+ } from "../chunk-Y5W6WEJL.js";
81
81
  import {
82
82
  CLAUDE_CODE_SECURITY_FLOOR,
83
83
  SELF_UPDATE_TIMEOUTS,
@@ -4721,6 +4721,26 @@ var SETTLE_MS = 3e4;
4721
4721
  var TRANSCRIPT_MTIME_WINDOW_MS2 = 2 * 24 * 60 * 60 * 1e3;
4722
4722
  var MAX_RUNS_PER_TICK = 20;
4723
4723
  var lastCheckedAt = /* @__PURE__ */ new Map();
4724
+ var sharedWorkList = null;
4725
+ var sharedWorkListInFlight = null;
4726
+ async function unreconciledRunsForHost(api2, nowMs) {
4727
+ if (sharedWorkList && nowMs - sharedWorkList.fetchedAt < MIN_CHECK_INTERVAL_MS4) {
4728
+ return sharedWorkList.runs;
4729
+ }
4730
+ if (sharedWorkListInFlight) return sharedWorkListInFlight;
4731
+ const flight = (async () => {
4732
+ const res = await api2.get("/host/workflow-runs/unreconciled");
4733
+ const runs = res?.runs ?? [];
4734
+ sharedWorkList = { fetchedAt: nowMs, runs };
4735
+ return runs;
4736
+ })();
4737
+ sharedWorkListInFlight = flight;
4738
+ try {
4739
+ return await flight;
4740
+ } finally {
4741
+ if (sharedWorkListInFlight === flight) sharedWorkListInFlight = null;
4742
+ }
4743
+ }
4724
4744
  function emptyTotals() {
4725
4745
  return { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
4726
4746
  }
@@ -4798,8 +4818,7 @@ async function maybeReconcileWorkflowRunTokens(args) {
4798
4818
  lastCheckedAt.set(codeName, nowMs);
4799
4819
  let runs;
4800
4820
  try {
4801
- const res = await api2.get("/host/workflow-runs/unreconciled");
4802
- runs = (res?.runs ?? []).filter(
4821
+ runs = (await unreconciledRunsForHost(api2, nowMs)).filter(
4803
4822
  (r) => r.agent_id === agentId && !!r.finished_at && nowMs - new Date(r.finished_at).getTime() >= SETTLE_MS
4804
4823
  );
4805
4824
  } catch (err) {
@@ -7564,6 +7583,12 @@ function createFetchGate(ttlMs) {
7564
7583
  }
7565
7584
  };
7566
7585
  }
7586
+ function forceGates(gates, agentId) {
7587
+ for (const gate of gates) {
7588
+ if (agentId === void 0) gate.forceAll();
7589
+ else gate.forceNext(agentId);
7590
+ }
7591
+ }
7567
7592
 
7568
7593
  // src/lib/conditional-fetch.ts
7569
7594
  function stillHeld(quoted, live) {
@@ -10585,6 +10610,47 @@ var BlockedPromptCostLedger = class {
10585
10610
  }
10586
10611
  };
10587
10612
 
10613
+ // src/lib/blocked-prompt-report-cadence.ts
10614
+ var BLOCKED_PROMPT_HEARTBEAT_MS = 5 * 6e4;
10615
+ function blockedPromptReportFingerprint(body) {
10616
+ const agents = [...body.agents].map((a) => [
10617
+ a.agent_id,
10618
+ a.state,
10619
+ a.variant ?? "",
10620
+ a.unknown_reason ?? "",
10621
+ a.recovering === true,
10622
+ a.tasks_deferred ?? 0,
10623
+ a.messages_withheld ?? 0
10624
+ ]).sort((x, y) => String(x[0]) < String(y[0]) ? -1 : String(x[0]) > String(y[0]) ? 1 : 0);
10625
+ return JSON.stringify({ agents, complete: body.sweep_complete, omitted: body.omitted_no_session });
10626
+ }
10627
+ var BlockedPromptReportCadence = class {
10628
+ constructor(heartbeatMs = BLOCKED_PROMPT_HEARTBEAT_MS) {
10629
+ this.heartbeatMs = heartbeatMs;
10630
+ }
10631
+ lastDelivered = null;
10632
+ /** Should this poll's report go out? `send` false only for `unchanged`. */
10633
+ decide(body, now, opts) {
10634
+ const fingerprint3 = blockedPromptReportFingerprint(body);
10635
+ const last = this.lastDelivered;
10636
+ let reason;
10637
+ if (opts.hasUnreportedRecovery) reason = "recovery-riding";
10638
+ else if (!last) reason = "first";
10639
+ else if (last.fingerprint !== fingerprint3) reason = "changed";
10640
+ else if (now - last.at >= this.heartbeatMs) reason = "heartbeat";
10641
+ else reason = "unchanged";
10642
+ return { send: reason !== "unchanged", reason, fingerprint: fingerprint3 };
10643
+ }
10644
+ /**
10645
+ * Record what happened to a report that went out. Only a confirmed delivery
10646
+ * advances the baseline; anything else clears it, so the next poll sends
10647
+ * whatever it holds — the self-heal for a lost request.
10648
+ */
10649
+ recordOutcome(fingerprint3, now, delivered) {
10650
+ this.lastDelivered = delivered ? { fingerprint: fingerprint3, at: now } : null;
10651
+ }
10652
+ };
10653
+
10588
10654
  // src/lib/occupancy-gate.ts
10589
10655
  import { closeSync as closeSync3, openSync as openSync3, readdirSync as readdirSync10, readSync as readSync3, statSync as statSync12 } from "fs";
10590
10656
  import { join as join39 } from "path";
@@ -14215,6 +14281,7 @@ var pendingSessionRestarts = /* @__PURE__ */ new Map();
14215
14281
  var lastRestartWasExternal = /* @__PURE__ */ new Map();
14216
14282
  var blockedPromptCost = new BlockedPromptCostLedger();
14217
14283
  var pendingRecoveryCost = /* @__PURE__ */ new Map();
14284
+ var blockedPromptReportCadence = new BlockedPromptReportCadence();
14218
14285
  var computerUseTunnels = new TunnelWatcher();
14219
14286
  var pendingRestartVerifications = /* @__PURE__ */ new Map();
14220
14287
  var agentCreatedAtMs = /* @__PURE__ */ new Map();
@@ -15807,7 +15874,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
15807
15874
  var lastVersionCheckAt = 0;
15808
15875
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
15809
15876
  var lastResponsivenessProbeAt = 0;
15810
- var agtCliVersion = true ? "0.28.1159" : "dev";
15877
+ var agtCliVersion = true ? "0.28.1160" : "dev";
15811
15878
  function resolveBrewPath(execFileSync3) {
15812
15879
  try {
15813
15880
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -17568,14 +17635,17 @@ var pollCycleQuarantinedFetches = 0;
17568
17635
  var pollCycleQuarantinedSkips = 0;
17569
17636
  var pollCycleMemoriesUnchanged = 0;
17570
17637
  var pollCycleMemoriesFull = 0;
17571
- function forceRefreshFetch() {
17572
- refreshFetchGate.forceAll();
17573
- memoriesFetchGate.forceAll();
17574
- secretsFetchGate.forceAll();
17575
- defaultSchedulesFetchGate.forceAll();
17576
- integrationsFetchGate.forceAll();
17577
- quarantinedFetchGate.forceAll();
17578
- forceNextExchange();
17638
+ var DESIRED_STATE_FETCH_GATES = [
17639
+ refreshFetchGate,
17640
+ memoriesFetchGate,
17641
+ secretsFetchGate,
17642
+ defaultSchedulesFetchGate,
17643
+ integrationsFetchGate,
17644
+ quarantinedFetchGate
17645
+ ];
17646
+ function forceRefreshFetch(agentId) {
17647
+ forceGates(DESIRED_STATE_FETCH_GATES, agentId);
17648
+ if (agentId === void 0) forceNextExchange();
17579
17649
  }
17580
17650
  var consecutivePollFailures = 0;
17581
17651
  var consecutiveEmptyAgentPolls = 0;
@@ -18912,20 +18982,28 @@ async function pollCycleInner() {
18912
18982
  `[blocked-prompt-monitor] NOTHING REPORTED: all ${omittedNoSession} active claude-code agent(s) had no live session this poll, so no batch was sent and this host is unassessed. A wedged tmux now reads as 'unknown' rather than 'dead', so reaching here means either a genuine host-wide teardown or no tmux server at all (ENG-10825)`
18913
18983
  );
18914
18984
  }
18915
- if (sweeps.length > 0) {
18985
+ const reportBody = {
18986
+ agents: sweeps,
18987
+ sweep_complete: sweepComplete,
18988
+ // WHY the sweep was partial, not just that it was (obi-wan
18989
+ // `82a0dd7ef253`). `sweep_complete: false` tells the route it may
18990
+ // not close an alert; it does not tell an operator that four agents
18991
+ // on this host have no session and that is why their critical will
18992
+ // not clear. The route writes this onto the alert payload.
18993
+ omitted_no_session: omittedNoSession
18994
+ };
18995
+ const reportDecision = sweeps.length > 0 ? blockedPromptReportCadence.decide(reportBody, Date.now(), {
18996
+ hasUnreportedRecovery: unreportedRecovery.size > 0
18997
+ }) : null;
18998
+ if (reportDecision?.send) {
18999
+ let reportResolved = false;
18916
19000
  const disposition = await api.post(
18917
19001
  "/host/blocked-prompt-report",
18918
- {
18919
- agents: sweeps,
18920
- sweep_complete: sweepComplete,
18921
- // WHY the sweep was partial, not just that it was (obi-wan
18922
- // `82a0dd7ef253`). `sweep_complete: false` tells the route it may
18923
- // not close an alert; it does not tell an operator that four agents
18924
- // on this host have no session and that is why their critical will
18925
- // not clear. The route writes this onto the alert payload.
18926
- omitted_no_session: omittedNoSession
18927
- }
19002
+ // ENG-11720: the SAME object the cadence fingerprinted, so what was
19003
+ // compared and what was sent cannot drift apart.
19004
+ reportBody
18928
19005
  ).then((res) => {
19006
+ reportResolved = true;
18929
19007
  const dropped = res?.dropped_agent_ids ?? [];
18930
19008
  if (dropped.length > 0) {
18931
19009
  log(
@@ -18940,6 +19018,11 @@ async function pollCycleInner() {
18940
19018
  return "withheld";
18941
19019
  });
18942
19020
  settlePendingRecovery(pendingRecoveryCost, unreportedRecovery, disposition);
19021
+ blockedPromptReportCadence.recordOutcome(
19022
+ reportDecision.fingerprint,
19023
+ Date.now(),
19024
+ reportResolved && disposition !== "withheld"
19025
+ );
18943
19026
  if (disposition === "withheld" && unreportedRecovery.size > 0) {
18944
19027
  log(
18945
19028
  `[blocked-prompt-monitor] holding ${unreportedRecovery.size} unreported recovery total(s) for the next poll \u2014 the route did not persist them (ENG-10825)`
@@ -23216,7 +23299,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
23216
23299
  if (restartReasonStampsTiming(restartReason)) {
23217
23300
  noteRestartRequested(codeName, requestedAt);
23218
23301
  }
23219
- triggerEarlyPoll(`config-change reload for '${codeName}' (reason=${restartReason})`);
23302
+ triggerEarlyPoll(`config-change reload for '${codeName}' (reason=${restartReason})`, agentId);
23220
23303
  return;
23221
23304
  }
23222
23305
  if (!hostFlagStore().getBoolean("restart-doorbell")) return;
@@ -23371,7 +23454,7 @@ function ensureRealtimeAssignStarted(agentStates) {
23371
23454
  refreshFetchGate.forceNext(payload.agent_id);
23372
23455
  memoriesFetchGate.forceNext(payload.agent_id);
23373
23456
  log(`[realtime] Agent ${payload.agent_id} assigned`);
23374
- triggerEarlyPoll(`agent ${payload.agent_id} assigned to this host`);
23457
+ triggerEarlyPoll(`agent ${payload.agent_id} assigned to this host`, payload.agent_id);
23375
23458
  markAgentForFreshMemorySync(payload.agent_id);
23376
23459
  },
23377
23460
  onUnassign: (payload) => {
@@ -23503,7 +23586,7 @@ function ensureRealtimeIntegrationContextStarted(agentStates) {
23503
23586
  }
23504
23587
  }
23505
23588
  }
23506
- triggerEarlyPoll(`integration context changed for agent ${payload.agent_id}`);
23589
+ triggerEarlyPoll(`integration context changed for agent ${payload.agent_id}`, payload.agent_id);
23507
23590
  },
23508
23591
  log
23509
23592
  });
@@ -23514,7 +23597,8 @@ function ensureRealtimeIntegrationContextStarted(agentStates) {
23514
23597
  agentIds: [...targetSet],
23515
23598
  onTokenRotated: (agentId, event) => {
23516
23599
  triggerEarlyPoll(
23517
- `integration token rotated for agent ${agentId} (integration ${event.integration_id}, ${event.definition_id})`
23600
+ `integration token rotated for agent ${agentId} (integration ${event.integration_id}, ${event.definition_id})`,
23601
+ agentId
23518
23602
  );
23519
23603
  },
23520
23604
  onSubscribed: (agentId) => {
@@ -23556,7 +23640,7 @@ function ensureRealtimeIntegrationContextStarted(agentStates) {
23556
23640
  log(`[realtime] Integration context subscription failed: ${err.message}`);
23557
23641
  });
23558
23642
  }
23559
- function triggerEarlyPoll(reason) {
23643
+ function triggerEarlyPoll(reason, agentId) {
23560
23644
  if (!running) return;
23561
23645
  if (pollTimer) {
23562
23646
  clearTimeout(pollTimer);
@@ -23564,7 +23648,7 @@ function triggerEarlyPoll(reason) {
23564
23648
  }
23565
23649
  log(`[realtime] Triggering early poll: ${reason}`);
23566
23650
  consecutiveEmptyAgentPolls = 0;
23567
- forceRefreshFetch();
23651
+ forceRefreshFetch(agentId);
23568
23652
  pollTimer = setTimeout(() => {
23569
23653
  void pollCycleAndReschedule(pollCycle, {
23570
23654
  onError: (err) => log(`[poll-loop] early-poll cycle rejected: ${err?.message ?? String(err)}`),