@integrity-labs/agt-cli 0.27.107 → 0.27.108
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
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
success,
|
|
29
29
|
table,
|
|
30
30
|
warn
|
|
31
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-N4YEE4O7.js";
|
|
32
32
|
import {
|
|
33
33
|
CHANNEL_REGISTRY,
|
|
34
34
|
DEPLOYMENT_TEMPLATES,
|
|
@@ -4934,7 +4934,7 @@ import { execFileSync, execSync } from "child_process";
|
|
|
4934
4934
|
import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
|
|
4935
4935
|
import chalk18 from "chalk";
|
|
4936
4936
|
import ora16 from "ora";
|
|
4937
|
-
var cliVersion = true ? "0.27.
|
|
4937
|
+
var cliVersion = true ? "0.27.108" : "dev";
|
|
4938
4938
|
async function fetchLatestVersion() {
|
|
4939
4939
|
const host2 = getHost();
|
|
4940
4940
|
if (!host2) return null;
|
|
@@ -5857,7 +5857,7 @@ function handleError(err) {
|
|
|
5857
5857
|
}
|
|
5858
5858
|
|
|
5859
5859
|
// src/bin/agt.ts
|
|
5860
|
-
var cliVersion2 = true ? "0.27.
|
|
5860
|
+
var cliVersion2 = true ? "0.27.108" : "dev";
|
|
5861
5861
|
var program = new Command();
|
|
5862
5862
|
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");
|
|
5863
5863
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
provisionStopHook,
|
|
18
18
|
requireHost,
|
|
19
19
|
safeWriteJsonAtomic
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-N4YEE4O7.js";
|
|
21
21
|
import {
|
|
22
22
|
getProjectDir as getProjectDir2,
|
|
23
23
|
getReadyTasks,
|
|
@@ -968,6 +968,42 @@ function formatStatusMessage(events, windowMs) {
|
|
|
968
968
|
return `Circuit breaker tripped: ${events.length} restarts in ${windowLabel} (${breakdown}); most recent=${last.reason} at ${new Date(last.at).toISOString()}`;
|
|
969
969
|
}
|
|
970
970
|
|
|
971
|
+
// src/lib/auto-resume.ts
|
|
972
|
+
var DEFAULT_QUIET_MS = 18e5;
|
|
973
|
+
var DEFAULT_BACKOFF_RESET_MS = 864e5;
|
|
974
|
+
function readAutoResumeConfig(env = process.env) {
|
|
975
|
+
const raw = (env["AGT_AUTO_RESUME_ENABLED"] ?? "").trim().toLowerCase();
|
|
976
|
+
return {
|
|
977
|
+
enabled: raw === "1" || raw === "true" || raw === "yes" || raw === "on",
|
|
978
|
+
quietMs: readEnvNumber("AGT_AUTO_RESUME_QUIET_MS", DEFAULT_QUIET_MS),
|
|
979
|
+
backoffResetMs: readEnvNumber("AGT_AUTO_RESUME_BACKOFF_RESET_MS", DEFAULT_BACKOFF_RESET_MS)
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function decideAutoResume(opts) {
|
|
983
|
+
const { trip, marker, config: config2, now } = opts;
|
|
984
|
+
if (!config2.enabled) return { action: "skip", reason: "disabled" };
|
|
985
|
+
if (!trip) return { action: "skip", reason: "no-trip" };
|
|
986
|
+
if (marker && now - marker.autoResumedAt < config2.backoffResetMs) {
|
|
987
|
+
return { action: "skip", reason: "already-auto-resumed" };
|
|
988
|
+
}
|
|
989
|
+
const lastEventAt = trip.eventsAtTrip.length > 0 ? Math.max(trip.trippedAt, ...trip.eventsAtTrip.map((e) => e.at)) : trip.trippedAt;
|
|
990
|
+
const quietFor = now - lastEventAt;
|
|
991
|
+
if (quietFor < config2.quietMs) {
|
|
992
|
+
return { action: "skip", reason: "not-quiet-yet", remainingMs: config2.quietMs - quietFor };
|
|
993
|
+
}
|
|
994
|
+
return { action: "resume", quietMs: quietFor };
|
|
995
|
+
}
|
|
996
|
+
function hydrateAutoResumeMarkers(saved) {
|
|
997
|
+
const map = /* @__PURE__ */ new Map();
|
|
998
|
+
if (!saved) return map;
|
|
999
|
+
for (const [codeName, rec] of Object.entries(saved)) {
|
|
1000
|
+
if (rec && typeof rec.trippedAt === "number" && typeof rec.autoResumedAt === "number") {
|
|
1001
|
+
map.set(codeName, rec);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return map;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
971
1007
|
// src/lib/model-change-respawn.ts
|
|
972
1008
|
function shouldRespawnForModelChange(input) {
|
|
973
1009
|
const { previousModel, primaryModel, framework, sessionHealthy } = input;
|
|
@@ -3730,6 +3766,64 @@ function maybeReportCurrentTrip(codeName) {
|
|
|
3730
3766
|
log(`[circuit-breaker] Failed to report trip for '${codeName}' to API: ${err.message}`);
|
|
3731
3767
|
});
|
|
3732
3768
|
}
|
|
3769
|
+
var autoResumeMarkers = /* @__PURE__ */ new Map();
|
|
3770
|
+
var autoResumeInFlight = /* @__PURE__ */ new Set();
|
|
3771
|
+
var AUTO_RESUME_SELF_WINDOW_MS = 12e4;
|
|
3772
|
+
var autoResumeLoggedSkips = /* @__PURE__ */ new Map();
|
|
3773
|
+
var autoResumeStandDowns = /* @__PURE__ */ new Set();
|
|
3774
|
+
function maybeAutoResume(agent) {
|
|
3775
|
+
const codeName = agent.code_name;
|
|
3776
|
+
if (autoResumeInFlight.has(codeName)) return;
|
|
3777
|
+
const trip = restartBreaker.getTrip(codeName);
|
|
3778
|
+
if (trip && autoResumeStandDowns.has(`${codeName}:${trip.trippedAt}`)) return;
|
|
3779
|
+
const decision = decideAutoResume({
|
|
3780
|
+
trip,
|
|
3781
|
+
marker: autoResumeMarkers.get(codeName),
|
|
3782
|
+
config: readAutoResumeConfig(),
|
|
3783
|
+
now: Date.now()
|
|
3784
|
+
});
|
|
3785
|
+
if (decision.action === "skip") {
|
|
3786
|
+
if ((decision.reason === "already-auto-resumed" || decision.reason === "not-quiet-yet") && trip) {
|
|
3787
|
+
const key = `${codeName}:${trip.trippedAt}`;
|
|
3788
|
+
if (autoResumeLoggedSkips.get(key) !== decision.reason) {
|
|
3789
|
+
autoResumeLoggedSkips.set(key, decision.reason);
|
|
3790
|
+
const detail = decision.reason === "not-quiet-yet" ? ` (~${Math.ceil((decision.remainingMs ?? 0) / 6e4)}min remaining)` : " \u2014 credit spent for this cycle; operator resume re-arms it";
|
|
3791
|
+
log(`[auto-resume] agent=${codeName} decision=skip reason=${decision.reason}${detail} (ENG-6088)`);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
const trippedAt = trip.trippedAt;
|
|
3797
|
+
log(
|
|
3798
|
+
`[auto-resume] agent=${codeName} decision=resume quiet=${Math.round(decision.quietMs / 6e4)}min trippedAt=${new Date(trippedAt).toISOString()} (ENG-6088)`
|
|
3799
|
+
);
|
|
3800
|
+
autoResumeInFlight.add(codeName);
|
|
3801
|
+
api.post("/host/circuit-breaker/auto-resume", {
|
|
3802
|
+
agent_id: agent.agent_id,
|
|
3803
|
+
quiet_ms: decision.quietMs,
|
|
3804
|
+
tripped_at: new Date(trippedAt).toISOString()
|
|
3805
|
+
}).then((res) => {
|
|
3806
|
+
autoResumeInFlight.delete(codeName);
|
|
3807
|
+
if (res.resumed) {
|
|
3808
|
+
autoResumeMarkers.set(codeName, { trippedAt, autoResumedAt: Date.now() });
|
|
3809
|
+
restartBreaker.clear(codeName);
|
|
3810
|
+
reportedTrips.delete(codeName);
|
|
3811
|
+
state5 = {
|
|
3812
|
+
...state5,
|
|
3813
|
+
circuitBreakerTrips: restartBreaker.serialize(),
|
|
3814
|
+
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
3815
|
+
};
|
|
3816
|
+
send({ type: "state-update", state: state5 });
|
|
3817
|
+
log(`[auto-resume] agent=${codeName} resumed \u2014 re-trip within backoff window will stay paused (ENG-6088)`);
|
|
3818
|
+
} else {
|
|
3819
|
+
autoResumeStandDowns.add(`${codeName}:${trippedAt}`);
|
|
3820
|
+
log(`[auto-resume] agent=${codeName} not applied (reason=${res.reason ?? "unknown"}) \u2014 standing down for this trip (ENG-6088)`);
|
|
3821
|
+
}
|
|
3822
|
+
}).catch((err) => {
|
|
3823
|
+
autoResumeInFlight.delete(codeName);
|
|
3824
|
+
log(`[auto-resume] agent=${codeName} POST failed (will retry next poll): ${err.message}`);
|
|
3825
|
+
});
|
|
3826
|
+
}
|
|
3733
3827
|
function scheduleSessionRestart(codeName, delayMs, reason, breakerReason = "hot-reload-mcp") {
|
|
3734
3828
|
const existing = pendingSessionRestarts.get(codeName);
|
|
3735
3829
|
if (existing) {
|
|
@@ -4039,7 +4133,7 @@ var cachedMaintenanceWindow = null;
|
|
|
4039
4133
|
var lastVersionCheckAt = 0;
|
|
4040
4134
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
4041
4135
|
var lastResponsivenessProbeAt = 0;
|
|
4042
|
-
var agtCliVersion = true ? "0.27.
|
|
4136
|
+
var agtCliVersion = true ? "0.27.108" : "dev";
|
|
4043
4137
|
function resolveBrewPath(execFileSync4) {
|
|
4044
4138
|
try {
|
|
4045
4139
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -5615,7 +5709,9 @@ async function pollCycle() {
|
|
|
5615
5709
|
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
5616
5710
|
// never silently clear a tripped breaker. Cheap — only tripped
|
|
5617
5711
|
// entries are stored, and the typical state is zero entries.
|
|
5618
|
-
circuitBreakerTrips: restartBreaker.serialize()
|
|
5712
|
+
circuitBreakerTrips: restartBreaker.serialize(),
|
|
5713
|
+
// ENG-6088: spent auto-resume credits survive manager restarts too.
|
|
5714
|
+
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
5619
5715
|
};
|
|
5620
5716
|
if (consecutivePollFailures > 0) {
|
|
5621
5717
|
log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
|
|
@@ -5690,6 +5786,9 @@ async function processAgent(agent, agentStates) {
|
|
|
5690
5786
|
log(`Killed tmux session for paused agent '${agent.code_name}'`);
|
|
5691
5787
|
} catch {
|
|
5692
5788
|
}
|
|
5789
|
+
if (agent.status === "paused") {
|
|
5790
|
+
maybeAutoResume(agent);
|
|
5791
|
+
}
|
|
5693
5792
|
agentStates.push({
|
|
5694
5793
|
agentId: agent.agent_id,
|
|
5695
5794
|
codeName: agent.code_name,
|
|
@@ -5781,6 +5880,18 @@ async function processAgent(agent, agentStates) {
|
|
|
5781
5880
|
reportedTrips.delete(agent.code_name);
|
|
5782
5881
|
log(`[circuit-breaker] Cleared trip for '${agent.code_name}' on operator resume (paused \u2192 active)`);
|
|
5783
5882
|
}
|
|
5883
|
+
if (previousStatus === "paused" && agent.status === "active") {
|
|
5884
|
+
const marker = autoResumeMarkers.get(agent.code_name);
|
|
5885
|
+
const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
|
|
5886
|
+
if (!isSelfResume && autoResumeMarkers.delete(agent.code_name)) {
|
|
5887
|
+
state5 = {
|
|
5888
|
+
...state5,
|
|
5889
|
+
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
5890
|
+
};
|
|
5891
|
+
send({ type: "state-update", state: state5 });
|
|
5892
|
+
log(`[auto-resume] Cleared auto-resume marker for '${agent.code_name}' on operator resume \u2014 credit re-armed (ENG-6088)`);
|
|
5893
|
+
}
|
|
5894
|
+
}
|
|
5784
5895
|
let channelCacheMutated = false;
|
|
5785
5896
|
for (const key of agentState.knownChannelConfigHashes.keys()) {
|
|
5786
5897
|
if (key.startsWith(`${agent.agent_id}:`)) {
|
|
@@ -10305,6 +10416,11 @@ function startManager(opts) {
|
|
|
10305
10416
|
const n = Object.keys(parsed.circuitBreakerTrips).length;
|
|
10306
10417
|
if (n > 0) log(`[startup] rehydrated ${n} circuit-breaker trip(s) \u2014 agents will remain paused until manually resumed`);
|
|
10307
10418
|
}
|
|
10419
|
+
if (parsed.circuitBreakerAutoResumes && typeof parsed.circuitBreakerAutoResumes === "object") {
|
|
10420
|
+
autoResumeMarkers = hydrateAutoResumeMarkers(parsed.circuitBreakerAutoResumes);
|
|
10421
|
+
const n = autoResumeMarkers.size;
|
|
10422
|
+
if (n > 0) log(`[startup] rehydrated ${n} auto-resume marker(s) (ENG-6088)`);
|
|
10423
|
+
}
|
|
10308
10424
|
}
|
|
10309
10425
|
} catch (err) {
|
|
10310
10426
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|