@integrity-labs/agt-cli 0.28.416 → 0.28.418

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.
@@ -8054,9 +8054,9 @@ var FLAG_REGISTRY = [
8054
8054
  },
8055
8055
  {
8056
8056
  key: "compaction-notice",
8057
- description: 'Compaction courtesy notice (ENG-7339): when a managed Claude Code session compacts its context, the persistent session pauses and stops replying for a stretch, which looks identical to a dead agent from the channel. When ON, the generated PreCompact hook finds the conversation the user is actively on (the last <channel ...> tag in the transcript) and drops a notice into the matching <channel>-notice-outbox; the channel MCP server (alive while the Claude process compacts) posts a short "reorganizing my memory, back shortly" line. The notice path never clears the pending-inbound marker, so the genuine reply the agent still owes after compaction is unaffected. No active channel tag \u21D2 silent (idle agents never broadcast). Boolean gate; ships dark, canary per host before any fleet flip. When OFF the hook writes nothing and the consumer drops any stray notice unsent.',
8057
+ description: 'Compaction courtesy notice (ENG-7339): when a managed Claude Code session compacts its context, the persistent session pauses and stops replying for a stretch, which looks identical to a dead agent from the channel. When ON, the generated PreCompact hook finds the conversation the user is actively on (the last <channel ...> tag in the transcript) and drops a notice into the matching <channel>-notice-outbox; the channel MCP server (alive while the Claude process compacts) posts a short "reorganizing my memory, back shortly" line. The notice path never clears the pending-inbound marker, so the genuine reply the agent still owes after compaction is unaffected. No active channel tag \u21D2 silent (idle agents never broadcast). Default ON (ENG-8040: introduced dark on 2026-07-23 via ENG-7339, never activated in prod; Brad Bond reported Sherlock went silent mid-compaction with no notice). Set AGT_COMPACTION_NOTICE_ENABLED=false or override via the Feature Flags admin page to disable per-host. When OFF the hook writes nothing and the consumer drops any stray notice unsent.',
8058
8058
  flagType: "boolean",
8059
- defaultValue: false,
8059
+ defaultValue: true,
8060
8060
  envVar: "AGT_COMPACTION_NOTICE_ENABLED"
8061
8061
  },
8062
8062
  {
@@ -8230,6 +8230,21 @@ var FLAG_REGISTRY = [
8230
8230
  // AGT_CONV_EVAL_BACKEND keeps that value until the env var is retired.
8231
8231
  envVar: "AGT_CONV_EVAL_BACKEND"
8232
8232
  },
8233
+ {
8234
+ key: "synthetic-probe-enabled",
8235
+ description: 'Global gate for the agent synthetic-liveness cron (ENG-8074). When OFF, the cron exits after reading the flag \u2014 no probes sent, no metrics emitted, no CloudWatch alarms fired, near-zero DB footprint. Ships dark (OFF) because the probe cron was generating false positives (probe_timeout on angie for 7+ days while healthy, ENG-8048) and had open cron_invocation_errors alarms (since 2026-07-23). Flip ON per-environment via the admin Feature Flags page once the reliability issues are resolved. The existing AUGMENTED_SYNTHETIC_PROBE_ENABLED env var is the declared envVar for this flag (ADR-0022): set it to "true"/"1" to force-enable (env takes precedence over the DB stage value), or "false"/"0" to force-disable without a DB lookup. Evaluated as a global stage value by the central cron (no per-org targeting). Boolean gate.',
8236
+ flagType: "boolean",
8237
+ // Safe/dark default: false = probe off. This encodes the ENG-8074 intent (disable
8238
+ // in prod until reliability issues are resolved) and is the fail-safe direction for
8239
+ // a flag-DB read error — a hiccup degrades to keeping the cron dormant rather than
8240
+ // silently re-arming a broken probe fleet-wide.
8241
+ // The env var AUGMENTED_SYNTHETIC_PROBE_ENABLED was the pre-flag emergency kill switch
8242
+ // (read as `!== 'false'`, so unset = probe ON). The flag inverts this: unset env var
8243
+ // now resolves to the flag default (OFF). Set AUGMENTED_SYNTHETIC_PROBE_ENABLED=true to
8244
+ // explicitly re-arm the probe via env, or flip the flag ON from the admin UI.
8245
+ defaultValue: false,
8246
+ envVar: "AUGMENTED_SYNTHETIC_PROBE_ENABLED"
8247
+ },
8233
8248
  {
8234
8249
  key: "synthetic-probe-on-metered-hosts",
8235
8250
  description: "Synthetic liveness probing of OpenRouter-metered agents (ENG-7235). On a host whose claude_auth_mode='openrouter' every model call is per-token cost, so the hourly synthetic probe (a full inbound\u2192session\u2192model\u2192outbound round-trip whose only output is a probe_ack) is real spend for zero deliverable - unlike a Max-subscription host where it is effectively free. When OFF (default) the synthetic-probe cron skips OpenRouter-mode agents entirely; their liveness falls back to the zero-token signals (pane-activity ENG-5399 + manager heartbeat + inbound-loop liveness ENG-5614). When ON, metered agents are probed like every other agent (the pre-ENG-7235 behaviour) - the explicit opt-in for an operator who wants the end-to-end check and accepts the cost. Subscription / api_key hosts are unaffected either way. Evaluated as a global stage value by the central cron (no per-org targeting in v1). Boolean gate.",
@@ -10295,12 +10310,66 @@ var TurnOutcomeTracker = class {
10295
10310
  }
10296
10311
  };
10297
10312
 
10313
+ // src/lib/opencode-activity-tracker.ts
10314
+ var OpencodeActivityTracker = class {
10315
+ state = /* @__PURE__ */ new Map();
10316
+ entry(codeName) {
10317
+ let cur = this.state.get(codeName);
10318
+ if (!cur) {
10319
+ cur = { inFlight: 0, lastActiveAt: null };
10320
+ this.state.set(codeName, cur);
10321
+ }
10322
+ return cur;
10323
+ }
10324
+ /** A turn has been dispatched to the serve. Call BEFORE awaiting it. */
10325
+ beginTurn(codeName) {
10326
+ this.entry(codeName).inFlight += 1;
10327
+ }
10328
+ /**
10329
+ * A dispatched turn has resolved. Call in a `finally`, so a throw cannot
10330
+ * strand the agent permanently "busy" — a leaked in-flight count would pin
10331
+ * the age at 0 forever and bill the agent around the clock.
10332
+ *
10333
+ * `counted` is false for a gate decline: it occupied the agent for the
10334
+ * microseconds the gate took, and nothing more.
10335
+ */
10336
+ endTurn(codeName, counted, now = Date.now()) {
10337
+ const cur = this.entry(codeName);
10338
+ cur.inFlight = Math.max(0, cur.inFlight - 1);
10339
+ if (counted) cur.lastActiveAt = now;
10340
+ }
10341
+ /**
10342
+ * Seconds since this agent was last doing work, or null if it never has been
10343
+ * in this manager generation.
10344
+ *
10345
+ * 0 while any turn is in flight — see the interval rationale above. Null is
10346
+ * "no signal", NOT "idle": the API omits the field entirely so a mixed-version
10347
+ * fleet cannot have a silent old CLI read as a busy agent (or vice versa).
10348
+ */
10349
+ activityAgeSeconds(codeName, now = Date.now()) {
10350
+ const cur = this.state.get(codeName);
10351
+ if (!cur) return null;
10352
+ if (cur.inFlight > 0) return 0;
10353
+ if (cur.lastActiveAt == null) return null;
10354
+ return Math.max(0, Math.floor((now - cur.lastActiveAt) / 1e3));
10355
+ }
10356
+ /**
10357
+ * Drop all state for an agent, so a fresh serve is not born holding the dead
10358
+ * one's in-flight count. Called from the same teardown path that resets turn
10359
+ * health.
10360
+ */
10361
+ reset(codeName) {
10362
+ this.state.delete(codeName);
10363
+ }
10364
+ };
10365
+
10298
10366
  // src/lib/opencode-session.ts
10299
10367
  var OPENCODE_BIN = process.env["AGT_OPENCODE_BIN"]?.trim() || "opencode";
10300
10368
  var OPENCODE_RUN_TIMEOUT_MS = Number(process.env["AGT_OPENCODE_RUN_TIMEOUT_MS"]) || 18e4;
10301
10369
  var sessions = /* @__PURE__ */ new Map();
10302
10370
  var loggers = /* @__PURE__ */ new Map();
10303
10371
  var turnOutcomeTracker = new TurnOutcomeTracker();
10372
+ var activityTracker = new OpencodeActivityTracker();
10304
10373
  var bridges = /* @__PURE__ */ new Map();
10305
10374
  function opencodeTmuxSession(codeName) {
10306
10375
  return `agt-oc-${codeName}`;
@@ -10641,18 +10710,28 @@ async function injectOpencodeMessage(codeName, msg, opts = {}) {
10641
10710
  return { status: "declined", reason: "server_not_running" };
10642
10711
  }
10643
10712
  const bridge = getBridge(codeName, session.port, session.password);
10713
+ activityTracker.beginTurn(codeName);
10714
+ let occupied = true;
10644
10715
  try {
10645
10716
  const result = await bridge.handleInbound(msg, { gate: opts.gate, awaitReply: opts.awaitReply });
10646
10717
  const outcome = result.status === "declined" ? "declined" : result.status === "replied" && result.reply ? "replied" : opts.awaitReply === false ? "admitted" : "no_reply";
10718
+ occupied = outcome !== "declined";
10647
10719
  noteOpencodeTurnOutcome(codeName, outcome, session);
10648
10720
  return result;
10649
10721
  } catch (err) {
10650
10722
  noteOpencodeTurnOutcome(codeName, "failed", session);
10651
10723
  throw err;
10724
+ } finally {
10725
+ if (isLiveOpencodeSession(codeName, session)) {
10726
+ activityTracker.endTurn(codeName, occupied);
10727
+ }
10652
10728
  }
10653
10729
  }
10730
+ function isLiveOpencodeSession(codeName, startedOn) {
10731
+ return sessions.get(codeName) === startedOn && startedOn.status === "running";
10732
+ }
10654
10733
  function noteOpencodeTurnOutcome(codeName, outcome, startedOn) {
10655
- if (sessions.get(codeName) !== startedOn || startedOn.status !== "running") return;
10734
+ if (!isLiveOpencodeSession(codeName, startedOn)) return;
10656
10735
  const { health, shouldWarn, recovered } = turnOutcomeTracker.record(codeName, outcome);
10657
10736
  const log2 = loggers.get(codeName);
10658
10737
  if (!log2) return;
@@ -10748,6 +10827,7 @@ function stopOpencodeSession(codeName, log2) {
10748
10827
  stopTranscriptRefresher(codeName);
10749
10828
  bridges.delete(codeName);
10750
10829
  turnOutcomeTracker.reset(codeName);
10830
+ activityTracker.reset(codeName);
10751
10831
  loggers.delete(codeName);
10752
10832
  const session = sessions.get(codeName);
10753
10833
  if (session) {
@@ -10762,6 +10842,9 @@ function getOpencodeSessionState(codeName) {
10762
10842
  function getOpencodeTurnHealth(codeName) {
10763
10843
  return turnOutcomeTracker.get(codeName);
10764
10844
  }
10845
+ function getOpencodeActivityAgeSeconds(codeName, now = Date.now()) {
10846
+ return activityTracker.activityAgeSeconds(codeName, now);
10847
+ }
10765
10848
  function stripUndefined(env2) {
10766
10849
  const out = {};
10767
10850
  for (const [k, v] of Object.entries(env2)) if (v !== void 0) out[k] = v;
@@ -12558,6 +12641,8 @@ export {
12558
12641
  injectOpencodeMessage,
12559
12642
  stopOpencodeSession,
12560
12643
  getOpencodeSessionState,
12644
+ getOpencodeTurnHealth,
12645
+ getOpencodeActivityAgeSeconds,
12561
12646
  sessionTranscriptDir,
12562
12647
  transcriptActivityAgeSeconds,
12563
12648
  subagentActivityAgeSeconds,
@@ -12606,4 +12691,4 @@ export {
12606
12691
  stopAllSessionsAndWait,
12607
12692
  getProjectDir
12608
12693
  };
12609
- //# sourceMappingURL=chunk-QZUKHEMO.js.map
12694
+ //# sourceMappingURL=chunk-IE7ZJKF3.js.map