@integrity-labs/agt-cli 0.28.661 → 0.28.662

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-WDCI3FYP.js";
43
+ } from "../chunk-77YTVCXK.js";
44
44
  import {
45
45
  readDirectChatSessionState
46
46
  } from "../chunk-5JAUM7J2.js";
@@ -4909,7 +4909,7 @@ import { execFileSync, execSync } from "child_process";
4909
4909
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4910
4910
  import chalk18 from "chalk";
4911
4911
  import ora16 from "ora";
4912
- var cliVersion = true ? "0.28.661" : "dev";
4912
+ var cliVersion = true ? "0.28.662" : "dev";
4913
4913
  async function fetchLatestVersion() {
4914
4914
  const host2 = getHost();
4915
4915
  if (!host2) return null;
@@ -6089,7 +6089,7 @@ function handleError(err) {
6089
6089
  }
6090
6090
 
6091
6091
  // src/bin/agt.ts
6092
- var cliVersion2 = true ? "0.28.661" : "dev";
6092
+ var cliVersion2 = true ? "0.28.662" : "dev";
6093
6093
  var program = new Command();
6094
6094
  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");
6095
6095
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -6342,7 +6342,7 @@ function exchangeFailureKind(err) {
6342
6342
  }
6343
6343
 
6344
6344
  // src/lib/api-client.ts
6345
- var agtCliVersion = true ? "0.28.661" : "dev";
6345
+ var agtCliVersion = true ? "0.28.662" : "dev";
6346
6346
  var lastConfigHash = null;
6347
6347
  function setConfigHash(hash) {
6348
6348
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -10084,4 +10084,4 @@ export {
10084
10084
  managerInstallSystemUnitCommand,
10085
10085
  managerUninstallSystemUnitCommand
10086
10086
  };
10087
- //# sourceMappingURL=chunk-WDCI3FYP.js.map
10087
+ //# sourceMappingURL=chunk-77YTVCXK.js.map
@@ -55,7 +55,7 @@ import {
55
55
  safeWriteJsonAtomic,
56
56
  setConfigHash,
57
57
  tripClass
58
- } from "../chunk-WDCI3FYP.js";
58
+ } from "../chunk-77YTVCXK.js";
59
59
  import {
60
60
  getProjectDir as getProjectDir2,
61
61
  getReadyTasks,
@@ -12219,6 +12219,7 @@ function resolveAgentFramework(codeName) {
12219
12219
  function clearAgentCaches(agentId, codeName) {
12220
12220
  const channelCacheMutated = clearAgentState(agentId, codeName);
12221
12221
  agentChannelTokens.delete(codeName);
12222
+ forgetChannelSyncState(agentId);
12222
12223
  sharedBusyBuckets.reset(codeName);
12223
12224
  stopOpencodeSlackIngest(codeName, log);
12224
12225
  stopOpencodeTelegramIngest(codeName, log);
@@ -12252,7 +12253,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
12252
12253
  var lastVersionCheckAt = 0;
12253
12254
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
12254
12255
  var lastResponsivenessProbeAt = 0;
12255
- var agtCliVersion = true ? "0.28.661" : "dev";
12256
+ var agtCliVersion = true ? "0.28.662" : "dev";
12256
12257
  function resolveBrewPath(execFileSync3) {
12257
12258
  try {
12258
12259
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -13355,6 +13356,60 @@ function saveChannelHashCache2() {
13355
13356
  }
13356
13357
  var channelWriteFailureCounts = /* @__PURE__ */ new Map();
13357
13358
  var CHANNEL_WRITE_FAILURE_ESCALATE_EVERY = 90;
13359
+ var channelSyncFailures = /* @__PURE__ */ new Map();
13360
+ var channelSyncAttemptedSets = /* @__PURE__ */ new Map();
13361
+ function sameChannelSet(a, b) {
13362
+ if (a === void 0) return false;
13363
+ if (a.size !== b.size) return false;
13364
+ for (const v of b) if (!a.has(v)) return false;
13365
+ return true;
13366
+ }
13367
+ function forgetChannelSyncState(agentId) {
13368
+ channelSyncFailures.delete(agentId);
13369
+ channelSyncAttemptedSets.delete(agentId);
13370
+ }
13371
+ var CHANNEL_SYNC_FREE_ATTEMPTS = 3;
13372
+ var CHANNEL_SYNC_MAX_BACKOFF_MS = 5 * 6e4;
13373
+ var CHANNEL_SYNC_STUCK_AFTER_MS = 10 * 6e4;
13374
+ function channelSyncBackoffMs(attempts) {
13375
+ if (attempts <= CHANNEL_SYNC_FREE_ATTEMPTS) return 0;
13376
+ const step = attempts - CHANNEL_SYNC_FREE_ATTEMPTS;
13377
+ const delay = 1e4 * 2 ** (step - 1);
13378
+ return Math.min(delay, CHANNEL_SYNC_MAX_BACKOFF_MS);
13379
+ }
13380
+ function recordChannelSyncFailure(failures, agentId, reason, now) {
13381
+ const prior = failures.get(agentId);
13382
+ const attempts = (prior?.attempts ?? 0) + 1;
13383
+ const next = {
13384
+ attempts,
13385
+ since: prior?.since ?? now,
13386
+ lastReason: reason,
13387
+ loggedAtAttempt: prior?.loggedAtAttempt ?? 0,
13388
+ nextAttemptAt: now + channelSyncBackoffMs(attempts)
13389
+ };
13390
+ failures.set(agentId, next);
13391
+ return next;
13392
+ }
13393
+ function shouldLogChannelSyncFailure(state8) {
13394
+ if (state8.attempts === 1) return true;
13395
+ if (state8.attempts <= CHANNEL_SYNC_FREE_ATTEMPTS) return false;
13396
+ return state8.attempts > state8.loggedAtAttempt;
13397
+ }
13398
+ function stuckChannelSyncs(failures, now, thresholdMs = CHANNEL_SYNC_STUCK_AFTER_MS) {
13399
+ const out = [];
13400
+ for (const [agentId, state8] of failures) {
13401
+ const stuckForMs = now - state8.since;
13402
+ if (stuckForMs < thresholdMs) continue;
13403
+ out.push({
13404
+ agent_id: agentId,
13405
+ attempts: state8.attempts,
13406
+ stuck_for_seconds: Math.floor(stuckForMs / 1e3),
13407
+ reason: state8.lastReason
13408
+ });
13409
+ }
13410
+ out.sort((a, b) => b.stuck_for_seconds - a.stuck_for_seconds);
13411
+ return out;
13412
+ }
13358
13413
  var persistedDeliveryBaseline = createDeliveryBaselineMaps();
13359
13414
  function loadSenderPolicyBaseline2() {
13360
13415
  loadSenderPolicyBaseline(persistedDeliveryBaseline, channelHashCacheDir(), log);
@@ -13749,6 +13804,22 @@ async function pollCycleInner() {
13749
13804
  // Omitted (undefined) on hosts that haven't run the assertion
13750
13805
  // (non-claude-code frameworks).
13751
13806
  channels_setting_status: lastManagedSettingsStatus ?? void 0,
13807
+ // ENG-9252: agents whose channel credential sync has failed to converge
13808
+ // for longer than CHANNEL_SYNC_STUCK_AFTER_MS. The API opens/closes a
13809
+ // per-agent alert on the edge, exactly as it does for
13810
+ // `channels_setting_status` above.
13811
+ //
13812
+ // This field is the whole point of the ticket. The retry loop it
13813
+ // reports on ran for over three hours on a customer host and raised
13814
+ // NOTHING: non-convergence moves no heartbeat, no session-health signal
13815
+ // and no restart counter, so every projection an operator would consult
13816
+ // read healthy while the manager span. A bounded retry that is still
13817
+ // silent would have fixed the CPU and left the invisibility.
13818
+ //
13819
+ // ALWAYS SENT, including as an empty array — that is what lets the API
13820
+ // close a resolved alert on the edge. Omitting it when empty would make
13821
+ // "recovered" and "this CLI is too old to report" the same wire value.
13822
+ channel_sync_stuck: stuckChannelSyncs(channelSyncFailures, Date.now()),
13752
13823
  // ENG-6251: capability advertisement — the flags schema version this
13753
13824
  // CLI compiled with, so the admin UI can show flip reach across a
13754
13825
  // mixed-CLI fleet (an older CLI sends an older fingerprint).
@@ -14678,6 +14749,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
14678
14749
  const adapter = resolveAgentFramework(agent.code_name);
14679
14750
  let agentDir = join36(adapter.getAgentDir(agent.code_name), "provision");
14680
14751
  if (agent.status === "draft" || agent.status === "paused") {
14752
+ forgetChannelSyncState(agent.agent_id);
14681
14753
  if (previousKnownStatus !== agent.status) {
14682
14754
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
14683
14755
  }
@@ -14881,6 +14953,14 @@ async function processAgent(agent, agentStates, managedToolkits) {
14881
14953
  const previousChannelIds = agentState.knownChannels.get(agent.agent_id);
14882
14954
  const channelsChanged = !previousChannelIds || currentChannelIds.size !== previousChannelIds.size || [...currentChannelIds].some((ch) => !previousChannelIds.has(ch)) || [...previousChannelIds].some((ch) => !currentChannelIds.has(ch));
14883
14955
  let channelConfigConverged = true;
14956
+ let channelConfigFailureReason = "";
14957
+ const attemptedChannelIds = channelSyncAttemptedSets.get(agent.agent_id);
14958
+ const channelSetChangedSinceAttempt = !sameChannelSet(attemptedChannelIds, currentChannelIds);
14959
+ if (attemptedChannelIds !== void 0 && channelSetChangedSinceAttempt && channelSyncFailures.delete(agent.agent_id)) {
14960
+ log(
14961
+ `[channels] channel set changed for '${agent.code_name}' since the last attempt \u2014 clearing the credential-sync backoff and retrying immediately (ENG-9252)`
14962
+ );
14963
+ }
14884
14964
  if (previousChannelIds && channelsChanged && frameworkAdapter.removeChannelCredentials) {
14885
14965
  for (const ch of previousChannelIds) {
14886
14966
  if (!currentChannelIds.has(ch)) {
@@ -14889,6 +14969,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
14889
14969
  log(`Removed ${ch} credentials for '${agent.code_name}'`);
14890
14970
  } catch (err) {
14891
14971
  channelConfigConverged = false;
14972
+ channelConfigFailureReason = `removing ${ch} credentials threw: ${err.message}`;
14892
14973
  log(`Failed to remove ${ch} credentials for '${agent.code_name}': ${err.message}`);
14893
14974
  }
14894
14975
  }
@@ -15152,7 +15233,12 @@ async function processAgent(agent, agentStates, managedToolkits) {
15152
15233
  agentChannelTokens.delete(agent.code_name);
15153
15234
  }
15154
15235
  }
15155
- if (refreshData.channel_configs && frameworkAdapter.writeChannelCredentials) {
15236
+ const channelSyncBackoff = channelSyncFailures.get(agent.agent_id);
15237
+ const channelSyncDeferred = channelSyncBackoff !== void 0 && Date.now() < channelSyncBackoff.nextAttemptAt;
15238
+ if (!channelSyncDeferred) {
15239
+ channelSyncAttemptedSets.set(agent.agent_id, new Set(currentChannelIds));
15240
+ }
15241
+ if (refreshData.channel_configs && frameworkAdapter.writeChannelCredentials && !channelSyncDeferred) {
15156
15242
  if (agent.status === "active") {
15157
15243
  for (const [channelId, entry] of Object.entries(refreshData.channel_configs)) {
15158
15244
  if (quarantinedChannels.has(channelId)) {
@@ -15257,6 +15343,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
15257
15343
  const landed = frameworkAdapter.hasChannelCredentials?.(agent.code_name, channelId) ?? true;
15258
15344
  if (!landed) {
15259
15345
  channelConfigConverged = false;
15346
+ channelConfigFailureReason = `credentials for ${channelId} reported a clean write but did not land on disk (reason=${reason}) \u2014 likely a missing credential field, or the guarded .mcp.json write was rejected by the secret lint / config validator`;
15260
15347
  const attempts = (channelWriteFailureCounts.get(cacheKey) ?? 0) + 1;
15261
15348
  channelWriteFailureCounts.set(cacheKey, attempts);
15262
15349
  if (attempts === 1 || attempts % CHANNEL_WRITE_FAILURE_ESCALATE_EVERY === 0) {
@@ -15274,6 +15361,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
15274
15361
  }
15275
15362
  } catch (err) {
15276
15363
  channelConfigConverged = false;
15364
+ channelConfigFailureReason = `writing ${channelId} credentials threw: ${err.message}`;
15277
15365
  log(`Failed to write channel credentials for '${agent.code_name}/${channelId}': ${err.message}`);
15278
15366
  }
15279
15367
  }
@@ -15287,11 +15375,35 @@ async function processAgent(agent, agentStates, managedToolkits) {
15287
15375
  }
15288
15376
  }
15289
15377
  if (channelConfigConverged) {
15290
- agentState.knownChannels.set(agent.agent_id, currentChannelIds);
15378
+ const ended = channelSyncFailures.get(agent.agent_id);
15379
+ if (ended && !channelSyncDeferred) {
15380
+ channelSyncFailures.delete(agent.agent_id);
15381
+ const stuckForSeconds = Math.floor((Date.now() - ended.since) / 1e3);
15382
+ log(
15383
+ `[channels] Credential sync for '${agent.code_name}' converged after ${ended.attempts} failed attempt(s) over ${stuckForSeconds}s \u2014 recovered (ENG-9252)`
15384
+ );
15385
+ }
15386
+ if (!channelSyncDeferred) {
15387
+ agentState.knownChannels.set(agent.agent_id, currentChannelIds);
15388
+ }
15291
15389
  } else {
15292
- log(`[channels] Credential sync did not converge for '${agent.code_name}' \u2014 leaving diff live for next tick retry`);
15390
+ const state8 = recordChannelSyncFailure(
15391
+ channelSyncFailures,
15392
+ agent.agent_id,
15393
+ channelConfigFailureReason || "reason not recorded",
15394
+ Date.now()
15395
+ );
15396
+ if (shouldLogChannelSyncFailure(state8)) {
15397
+ state8.loggedAtAttempt = state8.attempts;
15398
+ const stuckForSeconds = Math.floor((Date.now() - state8.since) / 1e3);
15399
+ const waitMs = channelSyncBackoffMs(state8.attempts);
15400
+ log(
15401
+ `[channels] Credential sync did not converge for '${agent.code_name}' \u2014 attempt ${state8.attempts}, stuck ${stuckForSeconds}s, next retry in ${Math.round(waitMs / 1e3)}s. Cause: ${state8.lastReason}. Diff left live (ENG-9252).`
15402
+ );
15403
+ }
15293
15404
  }
15294
- const restartDecision = channelConfigConverged ? decideChannelRestart({
15405
+ const channelConfigConvergedThisTick = channelConfigConverged && !channelSyncDeferred;
15406
+ const restartDecision = channelConfigConvergedThisTick ? decideChannelRestart({
15295
15407
  previousChannelIds,
15296
15408
  currentChannelIds,
15297
15409
  sessionMode: refreshData.agent.session_mode,
@@ -15343,7 +15455,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
15343
15455
  writeDmNoticeMarkers
15344
15456
  );
15345
15457
  }
15346
- if (channelConfigConverged) {
15458
+ if (channelConfigConvergedThisTick) {
15347
15459
  const hasSenderPolicyChannel = currentChannelIds.has("slack") || currentChannelIds.has("msteams");
15348
15460
  const senderPolicyForRestartHash = refreshData.sender_policy ?? null;
15349
15461
  const senderPolicyHash = computeSenderPolicyRestartHash(senderPolicyForRestartHash);
@@ -16376,7 +16488,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
16376
16488
  } catch (err) {
16377
16489
  log(`[hot-reload] .mcp.json drift check failed for '${agent.code_name}': ${err.message}`);
16378
16490
  }
16379
- if (channelConfigConverged) {
16491
+ if (channelConfigConvergedThisTick) {
16380
16492
  try {
16381
16493
  checkChannelSecretDriftAndScheduleRestart(agent.code_name, getProjectDir(agent.code_name));
16382
16494
  } catch (err) {
@@ -19314,12 +19426,16 @@ process.on("disconnect", () => {
19314
19426
  });
19315
19427
  export {
19316
19428
  BACK_ONLINE_GREETING_GUIDANCE,
19429
+ CHANNEL_SYNC_FREE_ATTEMPTS,
19430
+ CHANNEL_SYNC_MAX_BACKOFF_MS,
19431
+ CHANNEL_SYNC_STUCK_AFTER_MS,
19317
19432
  DAY_ROLLOVER_FORCE_GRACE_MIN,
19318
19433
  DAY_ROLLOVER_INBOUND_DEFER_MAX_MIN,
19319
19434
  DAY_ROLLOVER_INBOUND_GRACE_SEC,
19320
19435
  UntrustworthyToolkitResponseError,
19321
19436
  applyRestartAcks,
19322
19437
  cancelKanbanNoticeOnDrain,
19438
+ channelSyncBackoffMs,
19323
19439
  classifyLazyDayRolloverReset,
19324
19440
  claudeCodeUpgradeMarkerPath,
19325
19441
  claudeCodeUpgradeThrottled,
@@ -19334,6 +19450,7 @@ export {
19334
19450
  extractCharterSlackPeers,
19335
19451
  extractCharterTelegramPeers,
19336
19452
  fetchManagedToolkits,
19453
+ forgetChannelSyncState,
19337
19454
  hasRevokedResiduals,
19338
19455
  inboundHoldMinutes,
19339
19456
  isOlderSemverTuple,
@@ -19342,10 +19459,13 @@ export {
19342
19459
  maybeInjectKanbanCheck,
19343
19460
  maybeProbeClaudeRuntimeAuth,
19344
19461
  maybeUpgradeClaudeCode,
19462
+ recordChannelSyncFailure,
19345
19463
  refreshSkillsIndexInClaudeMd,
19346
19464
  reorderRestartedFirst,
19347
19465
  resolveManagedMcpServerId,
19348
19466
  restartReasonBindsNewMcp,
19467
+ sameChannelSet,
19468
+ shouldLogChannelSyncFailure,
19349
19469
  shouldReapOnRetag,
19350
19470
  shouldReportRestartToAudit,
19351
19471
  shouldSkipRevokedCleanup,
@@ -19353,6 +19473,7 @@ export {
19353
19473
  stampClaudeCodeUpgradeMarker,
19354
19474
  startManager,
19355
19475
  stopManager,
19476
+ stuckChannelSyncs,
19356
19477
  supersededRuntimeImageIds
19357
19478
  };
19358
19479
  //# sourceMappingURL=manager-worker.js.map