@integrity-labs/agt-cli 0.28.502 → 0.28.504

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.
@@ -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-ZW5HH2SC.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-AB4SAH5B.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-5GY4G7MD.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-E3KMHKQQ.js.map
@@ -52,7 +52,7 @@ import {
52
52
  safeWriteJsonAtomic,
53
53
  setConfigHash,
54
54
  tripClass
55
- } from "../chunk-KS762LYV.js";
55
+ } from "../chunk-GXEXHVNW.js";
56
56
  import {
57
57
  getProjectDir as getProjectDir2,
58
58
  getReadyTasks,
@@ -90,7 +90,7 @@ import {
90
90
  classifyTranscriptRateLimit,
91
91
  coerceOnboardingState,
92
92
  creditWatchdogGiveUpCount,
93
- cursorShortfallKey,
93
+ cursorAdvanceCounterKey,
94
94
  describeOnboardingChannel,
95
95
  egressMode,
96
96
  estimateActiveTasksTokens,
@@ -172,7 +172,7 @@ import {
172
172
  toOpencodeModel,
173
173
  transcriptActivityAgeSeconds,
174
174
  writeEgressAllowlist
175
- } from "../chunk-HMBECQIY.js";
175
+ } from "../chunk-HEUDF73X.js";
176
176
  import {
177
177
  reapOrphanChannelMcps
178
178
  } from "../chunk-XWVM4KPK.js";
@@ -1908,9 +1908,12 @@ function collectEnvGates(env) {
1908
1908
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
1909
1909
  import { join as join6 } from "path";
1910
1910
  var CURSOR_SHORTFALL_COUNTER_SUFFIX = "-cursor-advance-classifications.json";
1911
- function recordCursorAdvanceShortfall(agentDir, source, classification) {
1911
+ function recordCursorAdvanceOutcome(agentDir, source, route, verdict) {
1912
1912
  if (!agentDir)
1913
1913
  return;
1914
+ const key = cursorAdvanceCounterKey(route, verdict);
1915
+ if (key === null)
1916
+ return;
1914
1917
  const path = join6(agentDir, `${source}${CURSOR_SHORTFALL_COUNTER_SUFFIX}`);
1915
1918
  const counts = {};
1916
1919
  try {
@@ -1923,7 +1926,6 @@ function recordCursorAdvanceShortfall(agentDir, source, classification) {
1923
1926
  }
1924
1927
  } catch {
1925
1928
  }
1926
- const key = cursorShortfallKey(classification.route, classification.reason, classification.partial);
1927
1929
  counts[key] = (counts[key] ?? 0) + 1;
1928
1930
  try {
1929
1931
  writeFileSync4(path, JSON.stringify(counts), { mode: 384 });
@@ -3663,13 +3665,34 @@ function bundleFingerprint(files) {
3663
3665
 
3664
3666
  // src/lib/channel-config-hash.ts
3665
3667
  import { createHash as createHash7 } from "crypto";
3668
+ var DERIVED_PRINCIPAL_ID_LIST_KEYS = [
3669
+ "allowed_users",
3670
+ "diagnostic_chat_ids",
3671
+ "ping_allowed_users",
3672
+ "ping_allowed_chat_ids"
3673
+ ];
3674
+ function normalizeChannelConfigForHash(config2) {
3675
+ if (config2 === null || typeof config2 !== "object" || Array.isArray(config2)) return config2;
3676
+ const src = config2;
3677
+ let normalized = null;
3678
+ for (const key of DERIVED_PRINCIPAL_ID_LIST_KEYS) {
3679
+ const list = src[key];
3680
+ if (!Array.isArray(list)) continue;
3681
+ normalized ??= { ...src };
3682
+ normalized[key] = [...list].sort();
3683
+ }
3684
+ return normalized ?? config2;
3685
+ }
3666
3686
  var CHANNEL_WRITE_VERSION = 9;
3667
3687
  function computeChannelConfigHash(input) {
3668
3688
  return createHash7("sha256").update(
3669
3689
  canonicalJson({
3670
3690
  writeVersion: CHANNEL_WRITE_VERSION,
3671
3691
  cliVersion: input.cliVersion,
3672
- config: input.config,
3692
+ // ENG-8399: value-blind on the server-derived principal id SETS so a
3693
+ // reorder of the same principals can't oscillate this hash into a
3694
+ // false rewrite (and, downstream, a session respawn).
3695
+ config: normalizeChannelConfigForHash(input.config),
3673
3696
  team: input.team,
3674
3697
  peers: input.peers,
3675
3698
  sessionMode: input.sessionMode ?? null,
@@ -8900,7 +8923,7 @@ function hasRevokedResiduals(state7) {
8900
8923
  var pendingSessionRestarts = /* @__PURE__ */ new Map();
8901
8924
  var lastRestartWasExternal = /* @__PURE__ */ new Map();
8902
8925
  var pendingRestartVerifications = /* @__PURE__ */ new Map();
8903
- var restartBreaker = new RestartBreaker();
8926
+ var restartBreaker = new RestartBreaker({ runtimeKey: (codeName) => agentRuntimeKey(codeName) });
8904
8927
  {
8905
8928
  const clamp = restartBreaker.getQuarantineOrderingClamp();
8906
8929
  if (clamp) {
@@ -9013,9 +9036,9 @@ function maybeClearStaleTrip(agent) {
9013
9036
  log(
9014
9037
  `[circuit-breaker] agent=${codeName} cleared STALE trip \u2014 authoritative status=active while a trip was held (age=${Math.round(decision.ageMs / 6e4)}min, trippedAt=${new Date(trip.trippedAt).toISOString()}); the pause this trip caused is no longer in force (ENG-7577)`
9015
9038
  );
9016
- const marker = autoResumeMarkers.get(codeName);
9039
+ const marker = getAutoResumeMarker(codeName);
9017
9040
  const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
9018
- if (!isSelfResume) autoResumeMarkers.delete(codeName);
9041
+ if (!isSelfResume) deleteAutoResumeMarker(codeName);
9019
9042
  state6 = {
9020
9043
  ...state6,
9021
9044
  circuitBreakerTrips: restartBreaker.serialize(),
@@ -9024,6 +9047,30 @@ function maybeClearStaleTrip(agent) {
9024
9047
  send({ type: "state-update", state: state6 });
9025
9048
  }
9026
9049
  var autoResumeMarkers = /* @__PURE__ */ new Map();
9050
+ function autoResumeMarkerKey(codeName) {
9051
+ return agentRuntimeKey(codeName);
9052
+ }
9053
+ function getAutoResumeMarker(codeName) {
9054
+ const key = autoResumeMarkerKey(codeName);
9055
+ const hit = autoResumeMarkers.get(key);
9056
+ if (hit || key === codeName) return hit;
9057
+ const legacy = autoResumeMarkers.get(codeName);
9058
+ if (!legacy) return void 0;
9059
+ autoResumeMarkers.delete(codeName);
9060
+ autoResumeMarkers.set(key, legacy);
9061
+ return legacy;
9062
+ }
9063
+ function setAutoResumeMarker(codeName, rec) {
9064
+ const key = autoResumeMarkerKey(codeName);
9065
+ if (key !== codeName) autoResumeMarkers.delete(codeName);
9066
+ autoResumeMarkers.set(key, rec);
9067
+ }
9068
+ function deleteAutoResumeMarker(codeName) {
9069
+ const key = autoResumeMarkerKey(codeName);
9070
+ let removed = autoResumeMarkers.delete(key);
9071
+ if (key !== codeName && autoResumeMarkers.delete(codeName)) removed = true;
9072
+ return removed;
9073
+ }
9027
9074
  var autoResumeInFlight = /* @__PURE__ */ new Set();
9028
9075
  var AUTO_RESUME_SELF_WINDOW_MS = 12e4;
9029
9076
  var autoResumeLoggedSkips = /* @__PURE__ */ new Map();
@@ -9044,7 +9091,7 @@ function maybeAutoResume(agent) {
9044
9091
  if (trip && autoResumeStandDowns.has(`${codeName}:${trip.trippedAt}`)) return;
9045
9092
  const decision = decideAutoResume({
9046
9093
  trip,
9047
- marker: autoResumeMarkers.get(codeName),
9094
+ marker: getAutoResumeMarker(codeName),
9048
9095
  config: readAutoResumeConfig(),
9049
9096
  now: Date.now()
9050
9097
  });
@@ -9071,7 +9118,7 @@ function maybeAutoResume(agent) {
9071
9118
  }).then((res) => {
9072
9119
  autoResumeInFlight.delete(codeName);
9073
9120
  if (res.resumed) {
9074
- autoResumeMarkers.set(codeName, { trippedAt, autoResumedAt: Date.now() });
9121
+ setAutoResumeMarker(codeName, { trippedAt, autoResumedAt: Date.now() });
9075
9122
  restartBreaker.clear(codeName);
9076
9123
  reportedTrips.delete(codeName);
9077
9124
  dependencyRecoveryLedger.clear(codeName);
@@ -9148,7 +9195,7 @@ async function maybeResumeReconcile(agent) {
9148
9195
  const mcpPresent = deriveMcpPresent(declaredKeys, givenUpMcpServerKeys(codeName));
9149
9196
  const decision = decideResumeReconcile({
9150
9197
  trip,
9151
- marker: autoResumeMarkers.get(codeName),
9198
+ marker: getAutoResumeMarker(codeName),
9152
9199
  health: { ...serverHealth, mcpPresent },
9153
9200
  config: config2,
9154
9201
  now: Date.now()
@@ -9185,7 +9232,7 @@ async function maybeResumeReconcile(agent) {
9185
9232
  }
9186
9233
  );
9187
9234
  if (res.resumed) {
9188
- autoResumeMarkers.set(codeName, { trippedAt, autoResumedAt: Date.now() });
9235
+ setAutoResumeMarker(codeName, { trippedAt, autoResumedAt: Date.now() });
9189
9236
  restartBreaker.clear(codeName);
9190
9237
  reportedTrips.delete(codeName);
9191
9238
  dependencyRecoveryLedger.clear(codeName);
@@ -9954,7 +10001,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9954
10001
  var lastVersionCheckAt = 0;
9955
10002
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9956
10003
  var lastResponsivenessProbeAt = 0;
9957
- var agtCliVersion = true ? "0.28.502" : "dev";
10004
+ var agtCliVersion = true ? "0.28.504" : "dev";
9958
10005
  function resolveBrewPath(execFileSync2) {
9959
10006
  try {
9960
10007
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -10189,6 +10236,9 @@ async function ensureIsolationImage(imageUri) {
10189
10236
  return;
10190
10237
  }
10191
10238
  log(`[isolation-image] ${localTag} now points at ${imageUri}`);
10239
+ if (shouldReapOnRetag(hostFlagStore().getString("docker-hygiene"))) {
10240
+ await reapSupersededRuntimeImages(imageUri, localTag);
10241
+ }
10192
10242
  isolationImageEnsured.add(imageUri);
10193
10243
  isolationImageRetryAfter.delete(imageUri);
10194
10244
  isolationImageFailureCount.delete(imageUri);
@@ -10196,6 +10246,47 @@ async function ensureIsolationImage(imageUri) {
10196
10246
  isolationImageInFlight.delete(imageUri);
10197
10247
  }
10198
10248
  }
10249
+ function supersededRuntimeImageIds(currentId, repoImageIds) {
10250
+ const cur = currentId.trim();
10251
+ const out = [];
10252
+ const seen = /* @__PURE__ */ new Set();
10253
+ for (const raw of repoImageIds) {
10254
+ const id = raw.trim();
10255
+ if (!id || id === cur || seen.has(id)) continue;
10256
+ seen.add(id);
10257
+ out.push(id);
10258
+ }
10259
+ return out;
10260
+ }
10261
+ function shouldReapOnRetag(mode) {
10262
+ return mode === "reap-superseded" || mode === "full";
10263
+ }
10264
+ async function reapSupersededRuntimeImages(imageUri, localTag) {
10265
+ try {
10266
+ const repo = imageUri.replace(/@sha256:[a-f0-9]+$/i, "").replace(/:[^/:]+$/, "");
10267
+ const idRes = await runAsync("docker", ["image", "inspect", "--format", "{{.Id}}", localTag], { timeout: 15e3 });
10268
+ if (idRes.code !== 0) {
10269
+ log(`[docker-gc] could not resolve ${localTag} id, skipping reap: ${idRes.stderr.trim().slice(0, 160)}`);
10270
+ return;
10271
+ }
10272
+ const currentId = idRes.stdout.trim();
10273
+ const lsRes = await runAsync("docker", ["images", "--no-trunc", "--format", "{{.ID}}", repo], { timeout: 15e3 });
10274
+ if (lsRes.code !== 0) {
10275
+ log(`[docker-gc] could not list ${repo} images, skipping reap: ${lsRes.stderr.trim().slice(0, 160)}`);
10276
+ return;
10277
+ }
10278
+ const targets = supersededRuntimeImageIds(currentId, lsRes.stdout.split("\n"));
10279
+ let reaped = 0;
10280
+ for (const id of targets) {
10281
+ const rm = await runAsync("docker", ["rmi", id], { timeout: 3e4 });
10282
+ if (rm.code === 0) reaped++;
10283
+ else log(`[docker-gc] skip ${id.slice(0, 19)}: ${(rm.stderr || rm.stdout).trim().slice(0, 120)}`);
10284
+ }
10285
+ if (reaped > 0) log(`[docker-gc] reaped ${reaped} superseded ${repo} image(s)`);
10286
+ } catch (err) {
10287
+ log(`[docker-gc] reap failed: ${isolationImageErrDetail(err)}`);
10288
+ }
10289
+ }
10199
10290
  function runAsync(cmd, args, opts) {
10200
10291
  return new Promise((resolve, reject) => {
10201
10292
  import("child_process").then(({ spawn }) => {
@@ -11117,7 +11208,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
11117
11208
  if (codeNames.length === 0) return;
11118
11209
  void (async () => {
11119
11210
  try {
11120
- const { collectDiagnostics } = await import("../persistent-session-ZW5HH2SC.js");
11211
+ const { collectDiagnostics } = await import("../persistent-session-AB4SAH5B.js");
11121
11212
  await api.post("/host/heartbeat", {
11122
11213
  host_id: hostId,
11123
11214
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor)
@@ -11224,7 +11315,7 @@ async function pollCycle() {
11224
11315
  }
11225
11316
  try {
11226
11317
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
11227
- const { collectDiagnostics } = await import("../persistent-session-ZW5HH2SC.js");
11318
+ const { collectDiagnostics } = await import("../persistent-session-AB4SAH5B.js");
11228
11319
  const diagCodeNames = [...agentState.persistentSessionAgents];
11229
11320
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor) : void 0;
11230
11321
  let tailscaleHostname;
@@ -11339,7 +11430,7 @@ async function pollCycle() {
11339
11430
  collectResponsivenessProbes,
11340
11431
  collectPanelessActivityProbes,
11341
11432
  getResponsivenessIntervalMs
11342
- } = await import("../responsiveness-probe-RAHKBJEE.js");
11433
+ } = await import("../responsiveness-probe-O4SCSCKC.js");
11343
11434
  const probeIntervalMs = getResponsivenessIntervalMs();
11344
11435
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
11345
11436
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -11403,7 +11494,7 @@ async function pollCycle() {
11403
11494
  collectResponsivenessProbes,
11404
11495
  livePendingInboundOldestAgeSeconds,
11405
11496
  parkPendingInbound
11406
- } = await import("../responsiveness-probe-RAHKBJEE.js");
11497
+ } = await import("../responsiveness-probe-O4SCSCKC.js");
11407
11498
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
11408
11499
  const wedgeNow = /* @__PURE__ */ new Date();
11409
11500
  const liveAgents = agentState.persistentSessionAgents;
@@ -12095,9 +12186,9 @@ async function processAgent(agent, agentStates) {
12095
12186
  log(`[circuit-breaker] Cleared trip for '${agent.code_name}' on operator resume (paused \u2192 active)`);
12096
12187
  }
12097
12188
  if (previousStatus === "paused" && agent.status === "active") {
12098
- const marker = autoResumeMarkers.get(agent.code_name);
12189
+ const marker = getAutoResumeMarker(agent.code_name);
12099
12190
  const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
12100
- if (!isSelfResume && autoResumeMarkers.delete(agent.code_name)) {
12191
+ if (!isSelfResume && deleteAutoResumeMarker(agent.code_name)) {
12101
12192
  state6 = {
12102
12193
  ...state6,
12103
12194
  circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
@@ -14895,7 +14986,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
14895
14986
  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}`));
14896
14987
  void (async () => {
14897
14988
  try {
14898
- const { collectDiagnostics } = await import("../persistent-session-ZW5HH2SC.js");
14989
+ const { collectDiagnostics } = await import("../persistent-session-AB4SAH5B.js");
14899
14990
  await api.post("/host/heartbeat", {
14900
14991
  host_id: hostId,
14901
14992
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
@@ -14945,7 +15036,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
14945
15036
  }
14946
15037
  try {
14947
15038
  const hostId = await getHostId();
14948
- const { collectDiagnostics } = await import("../persistent-session-ZW5HH2SC.js");
15039
+ const { collectDiagnostics } = await import("../persistent-session-AB4SAH5B.js");
14949
15040
  await api.post("/host/heartbeat", {
14950
15041
  host_id: hostId,
14951
15042
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
@@ -15187,6 +15278,7 @@ async function processDirectChatMessageOpencode(agent, msg) {
15187
15278
  return;
15188
15279
  }
15189
15280
  if (result.status === "replied" && result.reply) {
15281
+ let recorded = false;
15190
15282
  try {
15191
15283
  const body = await api.post("/host/direct-chat/reply", {
15192
15284
  agent_id: agent.agentId,
@@ -15199,6 +15291,13 @@ async function processDirectChatMessageOpencode(agent, msg) {
15199
15291
  httpOk: true,
15200
15292
  body
15201
15293
  });
15294
+ recordCursorAdvanceOutcome(
15295
+ dirname8(paneLogPath(agent.codeName)),
15296
+ "direct-chat-manager",
15297
+ "reply",
15298
+ verdict
15299
+ );
15300
+ recorded = true;
15202
15301
  if (verdict.outcome === "failed") {
15203
15302
  log(
15204
15303
  `[direct-chat] opencode reply REFUSED for '${agent.codeName}' (msg=${msg.id}): ${verdict.error} \u2014 leaving for redelivery`
@@ -15218,14 +15317,17 @@ async function processDirectChatMessageOpencode(agent, msg) {
15218
15317
  cleared: []
15219
15318
  })
15220
15319
  );
15221
- recordCursorAdvanceShortfall(dirname8(paneLogPath(agent.codeName)), "direct-chat-manager", {
15222
- route: "reply",
15223
- reason: verdict.reason,
15224
- partial: verdict.partial
15225
- });
15226
15320
  }
15227
15321
  log(`[direct-chat] opencode replied for '${agent.codeName}' (msg=${msg.id}, ${result.reply.length} chars)`);
15228
15322
  } catch (err) {
15323
+ if (!recorded) {
15324
+ recordCursorAdvanceOutcome(
15325
+ dirname8(paneLogPath(agent.codeName)),
15326
+ "direct-chat-manager",
15327
+ "reply",
15328
+ { outcome: "failed", error: err.message, expected: 1 }
15329
+ );
15330
+ }
15229
15331
  log(`[direct-chat] opencode reply POST failed for '${agent.codeName}' (msg=${msg.id}): ${err.message} \u2014 leaving for redelivery`);
15230
15332
  }
15231
15333
  return;
@@ -15452,7 +15554,7 @@ async function processClaudePairSessions(agents) {
15452
15554
  killPairSession,
15453
15555
  pairTmuxSession,
15454
15556
  finalizeClaudePairOnboarding
15455
- } = await import("../claude-pair-runtime-5GY4G7MD.js");
15557
+ } = await import("../claude-pair-runtime-E3KMHKQQ.js");
15456
15558
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
15457
15559
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
15458
15560
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -16392,11 +16494,13 @@ export {
16392
16494
  reorderRestartedFirst,
16393
16495
  resolveManagedMcpServerId,
16394
16496
  restartReasonBindsNewMcp,
16497
+ shouldReapOnRetag,
16395
16498
  shouldReportRestartToAudit,
16396
16499
  shouldSkipRevokedCleanup,
16397
16500
  shouldUpdateOnLatest,
16398
16501
  stampClaudeCodeUpgradeMarker,
16399
16502
  startManager,
16400
- stopManager
16503
+ stopManager,
16504
+ supersededRuntimeImageIds
16401
16505
  };
16402
16506
  //# sourceMappingURL=manager-worker.js.map