@integrity-labs/agt-cli 0.28.215 → 0.28.217
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
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
success,
|
|
39
39
|
table,
|
|
40
40
|
warn
|
|
41
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-U6RV5FVC.js";
|
|
42
42
|
import {
|
|
43
43
|
CHANNEL_REGISTRY,
|
|
44
44
|
DEFAULT_FRAMEWORK,
|
|
@@ -4826,7 +4826,7 @@ import { execFileSync, execSync } from "child_process";
|
|
|
4826
4826
|
import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
|
|
4827
4827
|
import chalk18 from "chalk";
|
|
4828
4828
|
import ora16 from "ora";
|
|
4829
|
-
var cliVersion = true ? "0.28.
|
|
4829
|
+
var cliVersion = true ? "0.28.217" : "dev";
|
|
4830
4830
|
async function fetchLatestVersion() {
|
|
4831
4831
|
const host2 = getHost();
|
|
4832
4832
|
if (!host2) return null;
|
|
@@ -5840,7 +5840,7 @@ function handleError(err) {
|
|
|
5840
5840
|
}
|
|
5841
5841
|
|
|
5842
5842
|
// src/bin/agt.ts
|
|
5843
|
-
var cliVersion2 = true ? "0.28.
|
|
5843
|
+
var cliVersion2 = true ? "0.28.217" : "dev";
|
|
5844
5844
|
var program = new Command();
|
|
5845
5845
|
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");
|
|
5846
5846
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
@@ -5529,7 +5529,7 @@ function requireHost() {
|
|
|
5529
5529
|
}
|
|
5530
5530
|
|
|
5531
5531
|
// src/lib/api-client.ts
|
|
5532
|
-
var agtCliVersion = true ? "0.28.
|
|
5532
|
+
var agtCliVersion = true ? "0.28.217" : "dev";
|
|
5533
5533
|
var lastConfigHash = null;
|
|
5534
5534
|
function setConfigHash(hash) {
|
|
5535
5535
|
lastConfigHash = hash && hash.length > 0 ? hash : null;
|
|
@@ -7581,4 +7581,4 @@ export {
|
|
|
7581
7581
|
managerInstallSystemUnitCommand,
|
|
7582
7582
|
managerUninstallSystemUnitCommand
|
|
7583
7583
|
};
|
|
7584
|
-
//# sourceMappingURL=chunk-
|
|
7584
|
+
//# sourceMappingURL=chunk-U6RV5FVC.js.map
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
requireHost,
|
|
37
37
|
safeWriteJsonAtomic,
|
|
38
38
|
setConfigHash
|
|
39
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-U6RV5FVC.js";
|
|
40
40
|
import {
|
|
41
41
|
getProjectDir as getProjectDir2,
|
|
42
42
|
getReadyTasks,
|
|
@@ -685,6 +685,11 @@ var McpFlapDampener = class {
|
|
|
685
685
|
now;
|
|
686
686
|
history = /* @__PURE__ */ new Map();
|
|
687
687
|
flapping = /* @__PURE__ */ new Set();
|
|
688
|
+
// ENG-7239: timestamp of the most recent poll on which each (agent, channel)
|
|
689
|
+
// was flapping. Drives `flappedWithin` so the caller can exempt a managed-MCP
|
|
690
|
+
// churn restart from the ENG-5441 breaker for a short cool-off after a flap;
|
|
691
|
+
// see that method for the Kylie/dig (2026-06-29) rationale.
|
|
692
|
+
lastFlapAt = /* @__PURE__ */ new Map();
|
|
688
693
|
constructor(opts = {}) {
|
|
689
694
|
this.windowMs = opts.windowMs ?? readEnvNumber2("AGT_MCP_FLAP_WINDOW_MS", DEFAULT_WINDOW_MS2);
|
|
690
695
|
this.minTransitions = opts.minTransitions ?? readEnvNumber2("AGT_MCP_FLAP_MIN_TRANSITIONS", DEFAULT_MIN_TRANSITIONS);
|
|
@@ -742,9 +747,11 @@ var McpFlapDampener = class {
|
|
|
742
747
|
this.flapping.add(k);
|
|
743
748
|
onset = true;
|
|
744
749
|
}
|
|
750
|
+
const nowFlapping = this.flapping.has(k);
|
|
751
|
+
if (nowFlapping) this.lastFlapAt.set(k, at);
|
|
745
752
|
const reported = tier === "slow" ? slow : fast;
|
|
746
753
|
return {
|
|
747
|
-
flapping:
|
|
754
|
+
flapping: nowFlapping,
|
|
748
755
|
distinctStates: reported.distinctStates,
|
|
749
756
|
transitions: reported.transitions,
|
|
750
757
|
windowCount: prior.length,
|
|
@@ -776,6 +783,27 @@ var McpFlapDampener = class {
|
|
|
776
783
|
isFlapping(codeName, channel) {
|
|
777
784
|
return this.flapping.has(this.key(codeName, channel));
|
|
778
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* ENG-7239: true if (codeName, channel) was flapping within the last
|
|
788
|
+
* `withinMs`. Lets the caller exempt a managed-MCP-churn session restart from
|
|
789
|
+
* the ENG-5441 circuit breaker during (and for a short cool-off after) a
|
|
790
|
+
* flap episode.
|
|
791
|
+
*
|
|
792
|
+
* Why a cool-off and not just `isFlapping`: the dampener recovers eagerly, on
|
|
793
|
+
* the first stable poll (ENG-6512), so during an ACTIVE oscillation a single
|
|
794
|
+
* transient poll where the desired set momentarily repeats clears `flapping`
|
|
795
|
+
* for that instant. If the very next poll flips again, that flip's restart
|
|
796
|
+
* would (pre-fix) be treated as a genuine change and counted toward the
|
|
797
|
+
* breaker; three such flips in 10 min auto-paused Kylie (org dig) on
|
|
798
|
+
* 2026-06-29 and left her unreachable on every channel. Keying the breaker
|
|
799
|
+
* exemption off "flapped within the cool-off" instead of the exact-instant
|
|
800
|
+
* `isFlapping` closes that race. Mirrors ENG-5547's single-dead-MCP reaper
|
|
801
|
+
* exemption ("restart, but don't count toward the agent-wide breaker").
|
|
802
|
+
*/
|
|
803
|
+
flappedWithin(codeName, channel, withinMs) {
|
|
804
|
+
const at = this.lastFlapAt.get(this.key(codeName, channel));
|
|
805
|
+
return at !== void 0 && this.now() - at <= withinMs;
|
|
806
|
+
}
|
|
779
807
|
/** Drop all state for an agent (called on deprovision / agent removal). */
|
|
780
808
|
forget(codeName) {
|
|
781
809
|
const prefix = `${codeName}\0`;
|
|
@@ -785,10 +813,29 @@ var McpFlapDampener = class {
|
|
|
785
813
|
for (const k of [...this.flapping]) {
|
|
786
814
|
if (k.startsWith(prefix)) this.flapping.delete(k);
|
|
787
815
|
}
|
|
816
|
+
for (const k of [...this.lastFlapAt.keys()]) {
|
|
817
|
+
if (k.startsWith(prefix)) this.lastFlapAt.delete(k);
|
|
818
|
+
}
|
|
788
819
|
}
|
|
789
820
|
};
|
|
790
821
|
var FLAP_CHANNEL_INTEGRATIONS = "integrations";
|
|
791
822
|
var FLAP_CHANNEL_MANAGED_MCP = "managed-mcp";
|
|
823
|
+
var DEFAULT_FLAP_BREAKER_COOLDOWN_MS = 6e5;
|
|
824
|
+
var MANAGED_MCP_SERVER_PREFIXES = [
|
|
825
|
+
"composio_",
|
|
826
|
+
"one_",
|
|
827
|
+
"pipedream_",
|
|
828
|
+
"nango_",
|
|
829
|
+
"paragon_",
|
|
830
|
+
"composio-",
|
|
831
|
+
"one-",
|
|
832
|
+
"pipedream-",
|
|
833
|
+
"nango-",
|
|
834
|
+
"paragon-"
|
|
835
|
+
];
|
|
836
|
+
function isManagedMcpServerKey(key) {
|
|
837
|
+
return MANAGED_MCP_SERVER_PREFIXES.some((p) => key.startsWith(p));
|
|
838
|
+
}
|
|
792
839
|
|
|
793
840
|
// src/lib/auto-resume.ts
|
|
794
841
|
var DEFAULT_QUIET_MS = 18e5;
|
|
@@ -5365,6 +5412,9 @@ var lastChannelSweepAt = 0;
|
|
|
5365
5412
|
var config = null;
|
|
5366
5413
|
var running = false;
|
|
5367
5414
|
var pollTimer = null;
|
|
5415
|
+
var livenessTimer = null;
|
|
5416
|
+
var livenessInFlight = false;
|
|
5417
|
+
var LIVENESS_HEARTBEAT_INTERVAL_MS = 3e4;
|
|
5368
5418
|
var PANE_TAIL_PREVIEW_LINES = 5;
|
|
5369
5419
|
function extractCharterTelegramPeers(rawContent, gateContext) {
|
|
5370
5420
|
if (!rawContent || rawContent.length === 0) return [];
|
|
@@ -5469,6 +5519,10 @@ var pendingRestartVerifications = /* @__PURE__ */ new Map();
|
|
|
5469
5519
|
var restartBreaker = new RestartBreaker();
|
|
5470
5520
|
var reportedTrips = /* @__PURE__ */ new Map();
|
|
5471
5521
|
var mcpFlapDampener = new McpFlapDampener();
|
|
5522
|
+
var FLAP_BREAKER_COOLDOWN_MS = readEnvNumber2(
|
|
5523
|
+
"AGT_MCP_FLAP_BREAKER_COOLDOWN_MS",
|
|
5524
|
+
DEFAULT_FLAP_BREAKER_COOLDOWN_MS
|
|
5525
|
+
);
|
|
5472
5526
|
function recordConfigChurnEvent(agentId, codeName, channel, signature) {
|
|
5473
5527
|
api.post("/host/mcp-config-churn", {
|
|
5474
5528
|
agent_id: agentId,
|
|
@@ -5496,6 +5550,12 @@ async function reportReaperEvent(agentId, reaper, action, reason) {
|
|
|
5496
5550
|
}
|
|
5497
5551
|
}
|
|
5498
5552
|
function recordRestartForBreaker(codeName, reason) {
|
|
5553
|
+
if (reason === "managed-mcp-churn" && mcpFlapDampener.flappedWithin(codeName, FLAP_CHANNEL_MANAGED_MCP, FLAP_BREAKER_COOLDOWN_MS)) {
|
|
5554
|
+
log(
|
|
5555
|
+
`[mcp-flap-dampener] '${codeName}' managed-MCP churn restart NOT counted toward circuit breaker, managed-MCP set flapped within ${Math.round(FLAP_BREAKER_COOLDOWN_MS / 1e3)}s (ENG-7239)`
|
|
5556
|
+
);
|
|
5557
|
+
return;
|
|
5558
|
+
}
|
|
5499
5559
|
const result = restartBreaker.record(codeName, reason);
|
|
5500
5560
|
if (!result.tripped || !result.trip) return;
|
|
5501
5561
|
const trip = result.trip;
|
|
@@ -5752,7 +5812,7 @@ function scheduleSessionRestart(codeName, delayMs, reason, breakerReason = "hot-
|
|
|
5752
5812
|
runningMcpHashes.delete(codeName);
|
|
5753
5813
|
recordRestartForBreaker(codeName, breakerReason);
|
|
5754
5814
|
log(`[hot-reload] Session stopped for '${codeName}' \u2014 will respawn with ${reason}`);
|
|
5755
|
-
if (breakerReason === "hot-reload-mcp" || breakerReason === "bind-remediation" || breakerReason === "channel-set-change") {
|
|
5815
|
+
if (breakerReason === "hot-reload-mcp" || breakerReason === "managed-mcp-churn" || breakerReason === "bind-remediation" || breakerReason === "channel-set-change") {
|
|
5756
5816
|
const prior = pendingRestartVerifications.get(codeName);
|
|
5757
5817
|
pendingRestartVerifications.set(codeName, {
|
|
5758
5818
|
firedAt: Date.now(),
|
|
@@ -6064,7 +6124,8 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
6064
6124
|
);
|
|
6065
6125
|
}
|
|
6066
6126
|
if (decision.restart && !quarantineOnlyDrift && !flapSuppressed) {
|
|
6067
|
-
|
|
6127
|
+
const driftBreakerReason = decision.addedOrRemoved.length > 0 && decision.addedOrRemoved.every(isManagedMcpServerKey) ? "managed-mcp-churn" : "hot-reload-mcp";
|
|
6128
|
+
scheduleSessionRestart(codeName, 0, ".mcp.json content change (ENG-4897)", driftBreakerReason);
|
|
6068
6129
|
runningMcpHashes.delete(codeName);
|
|
6069
6130
|
runningMcpServerKeys.delete(codeName);
|
|
6070
6131
|
} else if (flapSuppressed) {
|
|
@@ -6163,7 +6224,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
6163
6224
|
var lastVersionCheckAt = 0;
|
|
6164
6225
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
6165
6226
|
var lastResponsivenessProbeAt = 0;
|
|
6166
|
-
var agtCliVersion = true ? "0.28.
|
|
6227
|
+
var agtCliVersion = true ? "0.28.217" : "dev";
|
|
6167
6228
|
function resolveBrewPath(execFileSync2) {
|
|
6168
6229
|
try {
|
|
6169
6230
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -8750,20 +8811,8 @@ async function processAgent(agent, agentStates) {
|
|
|
8750
8811
|
const { readFileSync: readFileSync15 } = await import("fs");
|
|
8751
8812
|
const mcpConfig = JSON.parse(readFileSync15(mcpPath, "utf-8"));
|
|
8752
8813
|
if (mcpConfig.mcpServers) {
|
|
8753
|
-
const managedPrefixes = [
|
|
8754
|
-
"composio_",
|
|
8755
|
-
"one_",
|
|
8756
|
-
"pipedream_",
|
|
8757
|
-
"nango_",
|
|
8758
|
-
"paragon_",
|
|
8759
|
-
"composio-",
|
|
8760
|
-
"one-",
|
|
8761
|
-
"pipedream-",
|
|
8762
|
-
"nango-",
|
|
8763
|
-
"paragon-"
|
|
8764
|
-
];
|
|
8765
8814
|
for (const key of Object.keys(mcpConfig.mcpServers)) {
|
|
8766
|
-
if (
|
|
8815
|
+
if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
|
|
8767
8816
|
frameworkAdapter.removeMcpServer(agent.code_name, key);
|
|
8768
8817
|
log(`[managed-toolkit] Removed stale MCP server '${key}' for '${agent.code_name}'`);
|
|
8769
8818
|
}
|
|
@@ -10729,9 +10778,27 @@ async function driveArtifactStreamingInner() {
|
|
|
10729
10778
|
if (!liveIds.has(id)) artifactScanners.delete(id);
|
|
10730
10779
|
}
|
|
10731
10780
|
}
|
|
10781
|
+
async function postLivenessHeartbeat() {
|
|
10782
|
+
if (!running || !config || restartAfterUpgrade) return;
|
|
10783
|
+
if (livenessInFlight) return;
|
|
10784
|
+
livenessInFlight = true;
|
|
10785
|
+
try {
|
|
10786
|
+
const hostId = await getHostId();
|
|
10787
|
+
if (!hostId) return;
|
|
10788
|
+
await api.post("/host/heartbeat", { host_id: hostId, agt_version: agtCliVersion });
|
|
10789
|
+
} catch (err) {
|
|
10790
|
+
log(`[liveness] fast heartbeat failed: ${err.message}`);
|
|
10791
|
+
} finally {
|
|
10792
|
+
livenessInFlight = false;
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10732
10795
|
function startPolling() {
|
|
10733
10796
|
if (!config || running) return;
|
|
10734
10797
|
running = true;
|
|
10798
|
+
livenessTimer = setInterval(() => {
|
|
10799
|
+
void postLivenessHeartbeat();
|
|
10800
|
+
}, LIVENESS_HEARTBEAT_INTERVAL_MS);
|
|
10801
|
+
livenessTimer.unref();
|
|
10735
10802
|
void startCaffeinate();
|
|
10736
10803
|
loadChannelHashCache2();
|
|
10737
10804
|
hostFlagStore().init();
|
|
@@ -10819,6 +10886,10 @@ async function stopPolling(opts = {}) {
|
|
|
10819
10886
|
clearTimeout(pollTimer);
|
|
10820
10887
|
pollTimer = null;
|
|
10821
10888
|
}
|
|
10889
|
+
if (livenessTimer) {
|
|
10890
|
+
clearInterval(livenessTimer);
|
|
10891
|
+
livenessTimer = null;
|
|
10892
|
+
}
|
|
10822
10893
|
const shutdownTimer = setTimeout(() => {
|
|
10823
10894
|
log("Shutdown timeout exceeded (15s), forcing exit");
|
|
10824
10895
|
process.exit(opts.forcedExitCode ?? 1);
|