@integrity-labs/agt-cli 0.28.1158 → 0.28.1160
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 +3 -3
- package/dist/{chunk-VYBTCFQQ.js → chunk-Y5W6WEJL.js} +2 -2
- package/dist/chunk-Y5W6WEJL.js.map +1 -0
- package/dist/lib/manager-worker.js +113 -29
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/index.js +25 -5
- package/package.json +1 -1
- package/dist/chunk-VYBTCFQQ.js.map +0 -1
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
setConfigHash,
|
|
78
78
|
shellGuard,
|
|
79
79
|
tripClass
|
|
80
|
-
} from "../chunk-
|
|
80
|
+
} from "../chunk-Y5W6WEJL.js";
|
|
81
81
|
import {
|
|
82
82
|
CLAUDE_CODE_SECURITY_FLOOR,
|
|
83
83
|
SELF_UPDATE_TIMEOUTS,
|
|
@@ -4721,6 +4721,26 @@ var SETTLE_MS = 3e4;
|
|
|
4721
4721
|
var TRANSCRIPT_MTIME_WINDOW_MS2 = 2 * 24 * 60 * 60 * 1e3;
|
|
4722
4722
|
var MAX_RUNS_PER_TICK = 20;
|
|
4723
4723
|
var lastCheckedAt = /* @__PURE__ */ new Map();
|
|
4724
|
+
var sharedWorkList = null;
|
|
4725
|
+
var sharedWorkListInFlight = null;
|
|
4726
|
+
async function unreconciledRunsForHost(api2, nowMs) {
|
|
4727
|
+
if (sharedWorkList && nowMs - sharedWorkList.fetchedAt < MIN_CHECK_INTERVAL_MS4) {
|
|
4728
|
+
return sharedWorkList.runs;
|
|
4729
|
+
}
|
|
4730
|
+
if (sharedWorkListInFlight) return sharedWorkListInFlight;
|
|
4731
|
+
const flight = (async () => {
|
|
4732
|
+
const res = await api2.get("/host/workflow-runs/unreconciled");
|
|
4733
|
+
const runs = res?.runs ?? [];
|
|
4734
|
+
sharedWorkList = { fetchedAt: nowMs, runs };
|
|
4735
|
+
return runs;
|
|
4736
|
+
})();
|
|
4737
|
+
sharedWorkListInFlight = flight;
|
|
4738
|
+
try {
|
|
4739
|
+
return await flight;
|
|
4740
|
+
} finally {
|
|
4741
|
+
if (sharedWorkListInFlight === flight) sharedWorkListInFlight = null;
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4724
4744
|
function emptyTotals() {
|
|
4725
4745
|
return { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
4726
4746
|
}
|
|
@@ -4798,8 +4818,7 @@ async function maybeReconcileWorkflowRunTokens(args) {
|
|
|
4798
4818
|
lastCheckedAt.set(codeName, nowMs);
|
|
4799
4819
|
let runs;
|
|
4800
4820
|
try {
|
|
4801
|
-
|
|
4802
|
-
runs = (res?.runs ?? []).filter(
|
|
4821
|
+
runs = (await unreconciledRunsForHost(api2, nowMs)).filter(
|
|
4803
4822
|
(r) => r.agent_id === agentId && !!r.finished_at && nowMs - new Date(r.finished_at).getTime() >= SETTLE_MS
|
|
4804
4823
|
);
|
|
4805
4824
|
} catch (err) {
|
|
@@ -7564,6 +7583,12 @@ function createFetchGate(ttlMs) {
|
|
|
7564
7583
|
}
|
|
7565
7584
|
};
|
|
7566
7585
|
}
|
|
7586
|
+
function forceGates(gates, agentId) {
|
|
7587
|
+
for (const gate of gates) {
|
|
7588
|
+
if (agentId === void 0) gate.forceAll();
|
|
7589
|
+
else gate.forceNext(agentId);
|
|
7590
|
+
}
|
|
7591
|
+
}
|
|
7567
7592
|
|
|
7568
7593
|
// src/lib/conditional-fetch.ts
|
|
7569
7594
|
function stillHeld(quoted, live) {
|
|
@@ -10585,6 +10610,47 @@ var BlockedPromptCostLedger = class {
|
|
|
10585
10610
|
}
|
|
10586
10611
|
};
|
|
10587
10612
|
|
|
10613
|
+
// src/lib/blocked-prompt-report-cadence.ts
|
|
10614
|
+
var BLOCKED_PROMPT_HEARTBEAT_MS = 5 * 6e4;
|
|
10615
|
+
function blockedPromptReportFingerprint(body) {
|
|
10616
|
+
const agents = [...body.agents].map((a) => [
|
|
10617
|
+
a.agent_id,
|
|
10618
|
+
a.state,
|
|
10619
|
+
a.variant ?? "",
|
|
10620
|
+
a.unknown_reason ?? "",
|
|
10621
|
+
a.recovering === true,
|
|
10622
|
+
a.tasks_deferred ?? 0,
|
|
10623
|
+
a.messages_withheld ?? 0
|
|
10624
|
+
]).sort((x, y) => String(x[0]) < String(y[0]) ? -1 : String(x[0]) > String(y[0]) ? 1 : 0);
|
|
10625
|
+
return JSON.stringify({ agents, complete: body.sweep_complete, omitted: body.omitted_no_session });
|
|
10626
|
+
}
|
|
10627
|
+
var BlockedPromptReportCadence = class {
|
|
10628
|
+
constructor(heartbeatMs = BLOCKED_PROMPT_HEARTBEAT_MS) {
|
|
10629
|
+
this.heartbeatMs = heartbeatMs;
|
|
10630
|
+
}
|
|
10631
|
+
lastDelivered = null;
|
|
10632
|
+
/** Should this poll's report go out? `send` false only for `unchanged`. */
|
|
10633
|
+
decide(body, now, opts) {
|
|
10634
|
+
const fingerprint3 = blockedPromptReportFingerprint(body);
|
|
10635
|
+
const last = this.lastDelivered;
|
|
10636
|
+
let reason;
|
|
10637
|
+
if (opts.hasUnreportedRecovery) reason = "recovery-riding";
|
|
10638
|
+
else if (!last) reason = "first";
|
|
10639
|
+
else if (last.fingerprint !== fingerprint3) reason = "changed";
|
|
10640
|
+
else if (now - last.at >= this.heartbeatMs) reason = "heartbeat";
|
|
10641
|
+
else reason = "unchanged";
|
|
10642
|
+
return { send: reason !== "unchanged", reason, fingerprint: fingerprint3 };
|
|
10643
|
+
}
|
|
10644
|
+
/**
|
|
10645
|
+
* Record what happened to a report that went out. Only a confirmed delivery
|
|
10646
|
+
* advances the baseline; anything else clears it, so the next poll sends
|
|
10647
|
+
* whatever it holds — the self-heal for a lost request.
|
|
10648
|
+
*/
|
|
10649
|
+
recordOutcome(fingerprint3, now, delivered) {
|
|
10650
|
+
this.lastDelivered = delivered ? { fingerprint: fingerprint3, at: now } : null;
|
|
10651
|
+
}
|
|
10652
|
+
};
|
|
10653
|
+
|
|
10588
10654
|
// src/lib/occupancy-gate.ts
|
|
10589
10655
|
import { closeSync as closeSync3, openSync as openSync3, readdirSync as readdirSync10, readSync as readSync3, statSync as statSync12 } from "fs";
|
|
10590
10656
|
import { join as join39 } from "path";
|
|
@@ -14215,6 +14281,7 @@ var pendingSessionRestarts = /* @__PURE__ */ new Map();
|
|
|
14215
14281
|
var lastRestartWasExternal = /* @__PURE__ */ new Map();
|
|
14216
14282
|
var blockedPromptCost = new BlockedPromptCostLedger();
|
|
14217
14283
|
var pendingRecoveryCost = /* @__PURE__ */ new Map();
|
|
14284
|
+
var blockedPromptReportCadence = new BlockedPromptReportCadence();
|
|
14218
14285
|
var computerUseTunnels = new TunnelWatcher();
|
|
14219
14286
|
var pendingRestartVerifications = /* @__PURE__ */ new Map();
|
|
14220
14287
|
var agentCreatedAtMs = /* @__PURE__ */ new Map();
|
|
@@ -15807,7 +15874,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
15807
15874
|
var lastVersionCheckAt = 0;
|
|
15808
15875
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
15809
15876
|
var lastResponsivenessProbeAt = 0;
|
|
15810
|
-
var agtCliVersion = true ? "0.28.
|
|
15877
|
+
var agtCliVersion = true ? "0.28.1160" : "dev";
|
|
15811
15878
|
function resolveBrewPath(execFileSync3) {
|
|
15812
15879
|
try {
|
|
15813
15880
|
const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -17568,14 +17635,17 @@ var pollCycleQuarantinedFetches = 0;
|
|
|
17568
17635
|
var pollCycleQuarantinedSkips = 0;
|
|
17569
17636
|
var pollCycleMemoriesUnchanged = 0;
|
|
17570
17637
|
var pollCycleMemoriesFull = 0;
|
|
17571
|
-
|
|
17572
|
-
refreshFetchGate
|
|
17573
|
-
memoriesFetchGate
|
|
17574
|
-
secretsFetchGate
|
|
17575
|
-
defaultSchedulesFetchGate
|
|
17576
|
-
integrationsFetchGate
|
|
17577
|
-
quarantinedFetchGate
|
|
17578
|
-
|
|
17638
|
+
var DESIRED_STATE_FETCH_GATES = [
|
|
17639
|
+
refreshFetchGate,
|
|
17640
|
+
memoriesFetchGate,
|
|
17641
|
+
secretsFetchGate,
|
|
17642
|
+
defaultSchedulesFetchGate,
|
|
17643
|
+
integrationsFetchGate,
|
|
17644
|
+
quarantinedFetchGate
|
|
17645
|
+
];
|
|
17646
|
+
function forceRefreshFetch(agentId) {
|
|
17647
|
+
forceGates(DESIRED_STATE_FETCH_GATES, agentId);
|
|
17648
|
+
if (agentId === void 0) forceNextExchange();
|
|
17579
17649
|
}
|
|
17580
17650
|
var consecutivePollFailures = 0;
|
|
17581
17651
|
var consecutiveEmptyAgentPolls = 0;
|
|
@@ -18912,20 +18982,28 @@ async function pollCycleInner() {
|
|
|
18912
18982
|
`[blocked-prompt-monitor] NOTHING REPORTED: all ${omittedNoSession} active claude-code agent(s) had no live session this poll, so no batch was sent and this host is unassessed. A wedged tmux now reads as 'unknown' rather than 'dead', so reaching here means either a genuine host-wide teardown or no tmux server at all (ENG-10825)`
|
|
18913
18983
|
);
|
|
18914
18984
|
}
|
|
18915
|
-
|
|
18985
|
+
const reportBody = {
|
|
18986
|
+
agents: sweeps,
|
|
18987
|
+
sweep_complete: sweepComplete,
|
|
18988
|
+
// WHY the sweep was partial, not just that it was (obi-wan
|
|
18989
|
+
// `82a0dd7ef253`). `sweep_complete: false` tells the route it may
|
|
18990
|
+
// not close an alert; it does not tell an operator that four agents
|
|
18991
|
+
// on this host have no session and that is why their critical will
|
|
18992
|
+
// not clear. The route writes this onto the alert payload.
|
|
18993
|
+
omitted_no_session: omittedNoSession
|
|
18994
|
+
};
|
|
18995
|
+
const reportDecision = sweeps.length > 0 ? blockedPromptReportCadence.decide(reportBody, Date.now(), {
|
|
18996
|
+
hasUnreportedRecovery: unreportedRecovery.size > 0
|
|
18997
|
+
}) : null;
|
|
18998
|
+
if (reportDecision?.send) {
|
|
18999
|
+
let reportResolved = false;
|
|
18916
19000
|
const disposition = await api.post(
|
|
18917
19001
|
"/host/blocked-prompt-report",
|
|
18918
|
-
|
|
18919
|
-
|
|
18920
|
-
|
|
18921
|
-
// WHY the sweep was partial, not just that it was (obi-wan
|
|
18922
|
-
// `82a0dd7ef253`). `sweep_complete: false` tells the route it may
|
|
18923
|
-
// not close an alert; it does not tell an operator that four agents
|
|
18924
|
-
// on this host have no session and that is why their critical will
|
|
18925
|
-
// not clear. The route writes this onto the alert payload.
|
|
18926
|
-
omitted_no_session: omittedNoSession
|
|
18927
|
-
}
|
|
19002
|
+
// ENG-11720: the SAME object the cadence fingerprinted, so what was
|
|
19003
|
+
// compared and what was sent cannot drift apart.
|
|
19004
|
+
reportBody
|
|
18928
19005
|
).then((res) => {
|
|
19006
|
+
reportResolved = true;
|
|
18929
19007
|
const dropped = res?.dropped_agent_ids ?? [];
|
|
18930
19008
|
if (dropped.length > 0) {
|
|
18931
19009
|
log(
|
|
@@ -18940,6 +19018,11 @@ async function pollCycleInner() {
|
|
|
18940
19018
|
return "withheld";
|
|
18941
19019
|
});
|
|
18942
19020
|
settlePendingRecovery(pendingRecoveryCost, unreportedRecovery, disposition);
|
|
19021
|
+
blockedPromptReportCadence.recordOutcome(
|
|
19022
|
+
reportDecision.fingerprint,
|
|
19023
|
+
Date.now(),
|
|
19024
|
+
reportResolved && disposition !== "withheld"
|
|
19025
|
+
);
|
|
18943
19026
|
if (disposition === "withheld" && unreportedRecovery.size > 0) {
|
|
18944
19027
|
log(
|
|
18945
19028
|
`[blocked-prompt-monitor] holding ${unreportedRecovery.size} unreported recovery total(s) for the next poll \u2014 the route did not persist them (ENG-10825)`
|
|
@@ -23216,7 +23299,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
23216
23299
|
if (restartReasonStampsTiming(restartReason)) {
|
|
23217
23300
|
noteRestartRequested(codeName, requestedAt);
|
|
23218
23301
|
}
|
|
23219
|
-
triggerEarlyPoll(`config-change reload for '${codeName}' (reason=${restartReason})
|
|
23302
|
+
triggerEarlyPoll(`config-change reload for '${codeName}' (reason=${restartReason})`, agentId);
|
|
23220
23303
|
return;
|
|
23221
23304
|
}
|
|
23222
23305
|
if (!hostFlagStore().getBoolean("restart-doorbell")) return;
|
|
@@ -23371,7 +23454,7 @@ function ensureRealtimeAssignStarted(agentStates) {
|
|
|
23371
23454
|
refreshFetchGate.forceNext(payload.agent_id);
|
|
23372
23455
|
memoriesFetchGate.forceNext(payload.agent_id);
|
|
23373
23456
|
log(`[realtime] Agent ${payload.agent_id} assigned`);
|
|
23374
|
-
triggerEarlyPoll(`agent ${payload.agent_id} assigned to this host
|
|
23457
|
+
triggerEarlyPoll(`agent ${payload.agent_id} assigned to this host`, payload.agent_id);
|
|
23375
23458
|
markAgentForFreshMemorySync(payload.agent_id);
|
|
23376
23459
|
},
|
|
23377
23460
|
onUnassign: (payload) => {
|
|
@@ -23503,7 +23586,7 @@ function ensureRealtimeIntegrationContextStarted(agentStates) {
|
|
|
23503
23586
|
}
|
|
23504
23587
|
}
|
|
23505
23588
|
}
|
|
23506
|
-
triggerEarlyPoll(`integration context changed for agent ${payload.agent_id}
|
|
23589
|
+
triggerEarlyPoll(`integration context changed for agent ${payload.agent_id}`, payload.agent_id);
|
|
23507
23590
|
},
|
|
23508
23591
|
log
|
|
23509
23592
|
});
|
|
@@ -23514,7 +23597,8 @@ function ensureRealtimeIntegrationContextStarted(agentStates) {
|
|
|
23514
23597
|
agentIds: [...targetSet],
|
|
23515
23598
|
onTokenRotated: (agentId, event) => {
|
|
23516
23599
|
triggerEarlyPoll(
|
|
23517
|
-
`integration token rotated for agent ${agentId} (integration ${event.integration_id}, ${event.definition_id})
|
|
23600
|
+
`integration token rotated for agent ${agentId} (integration ${event.integration_id}, ${event.definition_id})`,
|
|
23601
|
+
agentId
|
|
23518
23602
|
);
|
|
23519
23603
|
},
|
|
23520
23604
|
onSubscribed: (agentId) => {
|
|
@@ -23556,7 +23640,7 @@ function ensureRealtimeIntegrationContextStarted(agentStates) {
|
|
|
23556
23640
|
log(`[realtime] Integration context subscription failed: ${err.message}`);
|
|
23557
23641
|
});
|
|
23558
23642
|
}
|
|
23559
|
-
function triggerEarlyPoll(reason) {
|
|
23643
|
+
function triggerEarlyPoll(reason, agentId) {
|
|
23560
23644
|
if (!running) return;
|
|
23561
23645
|
if (pollTimer) {
|
|
23562
23646
|
clearTimeout(pollTimer);
|
|
@@ -23564,7 +23648,7 @@ function triggerEarlyPoll(reason) {
|
|
|
23564
23648
|
}
|
|
23565
23649
|
log(`[realtime] Triggering early poll: ${reason}`);
|
|
23566
23650
|
consecutiveEmptyAgentPolls = 0;
|
|
23567
|
-
forceRefreshFetch();
|
|
23651
|
+
forceRefreshFetch(agentId);
|
|
23568
23652
|
pollTimer = setTimeout(() => {
|
|
23569
23653
|
void pollCycleAndReschedule(pollCycle, {
|
|
23570
23654
|
onError: (err) => log(`[poll-loop] early-poll cycle rejected: ${err?.message ?? String(err)}`),
|