@integrity-labs/agt-cli 0.28.466 → 0.28.468

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.
@@ -16,6 +16,7 @@ import {
16
16
  diffEnvIntegrations,
17
17
  envOnlyRespawnVars,
18
18
  exchangeApiKey,
19
+ exchangeFailureKind,
19
20
  executeConnectivityProbe,
20
21
  extractCommandNotFound,
21
22
  findMcpServersUsingVars,
@@ -24,6 +25,7 @@ import {
24
25
  getCachedClaudeAuthMode,
25
26
  getHostId,
26
27
  givenUpMcpServerKeys,
28
+ isTransientAuthFailure,
27
29
  liveProxyExtraHeaderVars,
28
30
  liveProxyTokenVars,
29
31
  pinAllowsUrgent,
@@ -42,7 +44,7 @@ import {
42
44
  resolveEffectivePinRaw,
43
45
  safeWriteJsonAtomic,
44
46
  setConfigHash
45
- } from "../chunk-CL24G4D3.js";
47
+ } from "../chunk-Y2XU2XWM.js";
46
48
  import {
47
49
  getProjectDir as getProjectDir2,
48
50
  getReadyTasks,
@@ -2014,6 +2016,30 @@ function isDirectChatMessageExpired(createdAt, nowMs, maxAgeMs) {
2014
2016
  return nowMs - created > maxAgeMs;
2015
2017
  }
2016
2018
 
2019
+ // src/lib/manager/auth-resolve-decision.ts
2020
+ function decideAuthResolveFailureAction(args) {
2021
+ const transient = isTransientAuthFailure(args.error);
2022
+ if (!args.sessionHealthy) {
2023
+ return {
2024
+ action: "skip-spawn",
2025
+ transient,
2026
+ reason: transient ? "transient-fault-no-session" : "auth-verdict-no-session"
2027
+ };
2028
+ }
2029
+ if (transient) {
2030
+ return {
2031
+ action: "preserve-session",
2032
+ transient,
2033
+ reason: "transient-fault-session-preserved"
2034
+ };
2035
+ }
2036
+ return {
2037
+ action: "stop-session",
2038
+ transient,
2039
+ reason: "auth-verdict-session-stopped"
2040
+ };
2041
+ }
2042
+
2017
2043
  // ../../packages/core/dist/host-config/capture.js
2018
2044
  import { createHash as createHash5 } from "crypto";
2019
2045
  var NON_SECRET_ENV_GATES = [
@@ -5370,20 +5396,54 @@ async function startRun(opts) {
5370
5396
  return { run_id: null, kanban_item_id: null };
5371
5397
  }
5372
5398
  }
5399
+ var DEFAULT_FINISH_RETRY_ATTEMPTS = 3;
5400
+ var DEFAULT_FINISH_RETRY_BASE_MS = 1e3;
5401
+ function finishRetryAttempts() {
5402
+ const raw = process.env["AGT_RUNS_FINISH_RETRY_ATTEMPTS"];
5403
+ const parsed = raw ? parseInt(raw, 10) : NaN;
5404
+ if (Number.isFinite(parsed) && parsed >= 0) return Math.min(parsed, 6);
5405
+ return DEFAULT_FINISH_RETRY_ATTEMPTS;
5406
+ }
5407
+ function finishRetryBaseMs() {
5408
+ const raw = process.env["AGT_RUNS_FINISH_RETRY_BASE_MS"];
5409
+ const parsed = raw ? parseInt(raw, 10) : NaN;
5410
+ if (Number.isFinite(parsed) && parsed > 0) return Math.min(parsed, 3e4);
5411
+ return DEFAULT_FINISH_RETRY_BASE_MS;
5412
+ }
5413
+ function isRetryableFinishError(err) {
5414
+ if (err instanceof ApiError) return err.status >= 500 || err.status === 429;
5415
+ return true;
5416
+ }
5373
5417
  async function finishRun(runId, outcome, options = {}) {
5374
- try {
5375
- await api.post("/host/runs/finish", {
5376
- run_id: runId,
5377
- outcome,
5378
- outcome_message: options.outcomeMessage,
5379
- metadata: options.metadata,
5380
- complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
5381
- result: options.result
5382
- });
5383
- } catch (err) {
5384
- const errText = err instanceof Error ? err.message : String(err);
5385
- const errId = createHash9("sha256").update(errText).digest("hex").slice(0, 12);
5386
- log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
5418
+ const maxRetries = finishRetryAttempts();
5419
+ const baseMs = finishRetryBaseMs();
5420
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
5421
+ try {
5422
+ await api.post("/host/runs/finish", {
5423
+ run_id: runId,
5424
+ outcome,
5425
+ outcome_message: options.outcomeMessage,
5426
+ metadata: options.metadata,
5427
+ complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
5428
+ result: options.result
5429
+ });
5430
+ return;
5431
+ } catch (err) {
5432
+ const errText = err instanceof Error ? err.message : String(err);
5433
+ const errId = createHash9("sha256").update(errText).digest("hex").slice(0, 12);
5434
+ const status = err instanceof ApiError ? err.status : 0;
5435
+ if (isRetryableFinishError(err) && attempt < maxRetries) {
5436
+ log(
5437
+ `[runs] finish attempt ${attempt + 1}/${maxRetries + 1} failed for run_id=${runId} outcome=${outcome} status=${status} error_id=${errId} \u2014 retrying`
5438
+ );
5439
+ await new Promise((resolve) => setTimeout(resolve, baseMs * 2 ** attempt));
5440
+ continue;
5441
+ }
5442
+ log(
5443
+ `[runs] finish failed for run_id=${runId} outcome=${outcome} status=${status} error_id=${errId} attempts=${attempt + 1} \u2014 run row left unclosed`
5444
+ );
5445
+ return;
5446
+ }
5387
5447
  }
5388
5448
  }
5389
5449
  var MAX_PRIOR_RUNS = 5;
@@ -9758,7 +9818,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
9758
9818
  }
9759
9819
  return result != null;
9760
9820
  }
9761
- function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gateReason = breakerReason) {
9821
+ function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gateReason = breakerReason, runClose) {
9762
9822
  const gate = restartGateFor(codeName, gateReason);
9763
9823
  if (gate !== "bypass" && gate !== "proceed") {
9764
9824
  log(`[maintenance-window] Deferring '${gateReason}' restart for '${codeName}' (${gate})`);
@@ -9771,8 +9831,10 @@ function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gate
9771
9831
  runningMcpServerKeys.delete(codeName);
9772
9832
  runningChannelSecretHashes.delete(codeName);
9773
9833
  sessionLaunchManagedStructure.delete(codeName);
9774
- closeInjectedRunIfOpen(codeName, "cancelled", `session stopped (${breakerReason ?? "deprovision"})`);
9775
- closeScheduledRunsForCode(codeName, "cancelled", `session stopped (${breakerReason ?? "deprovision"})`);
9834
+ const runOutcome = runClose?.outcome ?? "cancelled";
9835
+ const runMessage = runClose ? `session stopped (${runClose.message})` : `session stopped (${breakerReason ?? "deprovision"})`;
9836
+ closeInjectedRunIfOpen(codeName, runOutcome, runMessage);
9837
+ closeScheduledRunsForCode(codeName, runOutcome, runMessage);
9776
9838
  if (breakerReason) {
9777
9839
  recordRestartForBreaker(codeName, breakerReason);
9778
9840
  }
@@ -9976,7 +10038,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9976
10038
  var lastVersionCheckAt = 0;
9977
10039
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9978
10040
  var lastResponsivenessProbeAt = 0;
9979
- var agtCliVersion = true ? "0.28.466" : "dev";
10041
+ var agtCliVersion = true ? "0.28.468" : "dev";
9980
10042
  function resolveBrewPath(execFileSync2) {
9981
10043
  try {
9982
10044
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -14340,9 +14402,38 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
14340
14402
  anthropicApiKeyFingerprint = exchange.anthropicApiKeyFingerprint;
14341
14403
  } catch (err) {
14342
14404
  const msg = err.message;
14405
+ const healthyBefore = isSessionHealthy(codeName);
14406
+ const authFailure = decideAuthResolveFailureAction({ error: err, sessionHealthy: healthyBefore });
14407
+ const kind = exchangeFailureKind(err);
14408
+ if (authFailure.action === "preserve-session") {
14409
+ log(
14410
+ `[persistent-session] Failed to resolve auth for '${codeName}': ${msg} \u2014 transient (${kind}), leaving the healthy session running; retrying next poll`
14411
+ );
14412
+ const healthyAfter = isSessionHealthy(codeName);
14413
+ if (healthyAfter) {
14414
+ return {
14415
+ decision: "skipped-auth-resolve-transient",
14416
+ spawnAttempted: false,
14417
+ sessionHealthyAfter: true,
14418
+ detail: `${authFailure.reason} (${kind}): ${msg}`
14419
+ };
14420
+ }
14421
+ log(
14422
+ `[persistent-session] '${codeName}' session did NOT survive the transient auth failure (${kind}) \u2014 reporting as a skip, next poll respawns`
14423
+ );
14424
+ return {
14425
+ decision: "skipped-auth-resolve-failed",
14426
+ spawnAttempted: false,
14427
+ sessionHealthyAfter: false,
14428
+ detail: `transient-fault-session-lost (${kind}): ${msg}`
14429
+ };
14430
+ }
14343
14431
  log(`[persistent-session] Failed to resolve auth for '${codeName}': ${msg} \u2014 refusing to spawn`);
14344
- if (isSessionHealthy(codeName)) {
14345
- stopPersistentSessionAndForgetMcpBaseline(codeName);
14432
+ if (authFailure.action === "stop-session") {
14433
+ stopPersistentSessionAndForgetMcpBaseline(codeName, void 0, void 0, {
14434
+ outcome: "failed",
14435
+ message: `auth resolve failed (${kind})`
14436
+ });
14346
14437
  agentState.persistentSessionAgents.delete(codeName);
14347
14438
  claudeAuthTupleBySession.delete(codeName);
14348
14439
  }
@@ -14350,7 +14441,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
14350
14441
  decision: "skipped-auth-resolve-failed",
14351
14442
  spawnAttempted: false,
14352
14443
  sessionHealthyAfter: isSessionHealthy(codeName),
14353
- detail: msg
14444
+ detail: `${authFailure.reason} (${kind}): ${msg}`
14354
14445
  };
14355
14446
  }
14356
14447
  if (claudeAuthMode === "openrouter" || openRouterForAgent) {