@integrity-labs/agt-cli 0.28.1149 → 0.28.1150
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-
|
|
43
|
+
} from "../chunk-HZFXWUA2.js";
|
|
44
44
|
import {
|
|
45
45
|
getProjectDir,
|
|
46
46
|
isSessionResumeDisabled,
|
|
@@ -5512,7 +5512,7 @@ import { execFileSync, execSync } from "child_process";
|
|
|
5512
5512
|
import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
|
|
5513
5513
|
import chalk18 from "chalk";
|
|
5514
5514
|
import ora16 from "ora";
|
|
5515
|
-
var cliVersion = true ? "0.28.
|
|
5515
|
+
var cliVersion = true ? "0.28.1150" : "dev";
|
|
5516
5516
|
async function fetchLatestVersion() {
|
|
5517
5517
|
const host2 = getHost();
|
|
5518
5518
|
if (!host2) return null;
|
|
@@ -6772,7 +6772,7 @@ function handleError(err) {
|
|
|
6772
6772
|
}
|
|
6773
6773
|
|
|
6774
6774
|
// src/bin/agt.ts
|
|
6775
|
-
var cliVersion2 = true ? "0.28.
|
|
6775
|
+
var cliVersion2 = true ? "0.28.1150" : "dev";
|
|
6776
6776
|
var program = new Command();
|
|
6777
6777
|
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");
|
|
6778
6778
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
@@ -8078,7 +8078,7 @@ function exchangeFailureKind(err) {
|
|
|
8078
8078
|
}
|
|
8079
8079
|
|
|
8080
8080
|
// src/lib/api-client.ts
|
|
8081
|
-
var agtCliVersion = true ? "0.28.
|
|
8081
|
+
var agtCliVersion = true ? "0.28.1150" : "dev";
|
|
8082
8082
|
var lastConfigHash = null;
|
|
8083
8083
|
function setConfigHash(hash) {
|
|
8084
8084
|
lastConfigHash = hash && hash.length > 0 ? hash : null;
|
|
@@ -13235,4 +13235,4 @@ export {
|
|
|
13235
13235
|
managerInstallSystemUnitCommand,
|
|
13236
13236
|
managerUninstallSystemUnitCommand
|
|
13237
13237
|
};
|
|
13238
|
-
//# sourceMappingURL=chunk-
|
|
13238
|
+
//# sourceMappingURL=chunk-HZFXWUA2.js.map
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
setConfigHash,
|
|
78
78
|
shellGuard,
|
|
79
79
|
tripClass
|
|
80
|
-
} from "../chunk-
|
|
80
|
+
} from "../chunk-HZFXWUA2.js";
|
|
81
81
|
import {
|
|
82
82
|
CLAUDE_CODE_SECURITY_FLOOR,
|
|
83
83
|
compareVersions
|
|
@@ -957,6 +957,156 @@ function claudeCodeUpgradeThrottled() {
|
|
|
957
957
|
}
|
|
958
958
|
}
|
|
959
959
|
|
|
960
|
+
// src/lib/claude-pair-loop.ts
|
|
961
|
+
var PAIR_LOOP_ACTIVE_INTERVAL_MS = 2e3;
|
|
962
|
+
var PAIR_LOOP_IDLE_INTERVAL_MS = 1e4;
|
|
963
|
+
var PAIR_LOOP_MAX_BACKOFF_MS = 12e4;
|
|
964
|
+
var PAIR_LOOP_ARMED_MAX_MS = 15 * 6e4;
|
|
965
|
+
function pairLoopArmed(opts) {
|
|
966
|
+
const max = opts.maxArmedMs ?? PAIR_LOOP_ARMED_MAX_MS;
|
|
967
|
+
const live = /* @__PURE__ */ new Set([...opts.spawnedPairIds, ...opts.pendingPairIds]);
|
|
968
|
+
for (const id of [...opts.firstSeenMs.keys()]) {
|
|
969
|
+
if (!live.has(id)) opts.firstSeenMs.delete(id);
|
|
970
|
+
}
|
|
971
|
+
let fresh = false;
|
|
972
|
+
for (const id of live) {
|
|
973
|
+
let seen = opts.firstSeenMs.get(id);
|
|
974
|
+
if (seen === void 0) {
|
|
975
|
+
seen = opts.nowMs;
|
|
976
|
+
opts.firstSeenMs.set(id, seen);
|
|
977
|
+
}
|
|
978
|
+
if (opts.nowMs - seen < max) fresh = true;
|
|
979
|
+
}
|
|
980
|
+
return fresh;
|
|
981
|
+
}
|
|
982
|
+
function nextPairLoopDelayMs(armed, consecutiveFailures) {
|
|
983
|
+
if (consecutiveFailures > 0) {
|
|
984
|
+
const exp = Math.min(consecutiveFailures - 1, 16);
|
|
985
|
+
return Math.min(PAIR_LOOP_IDLE_INTERVAL_MS * 2 ** exp, PAIR_LOOP_MAX_BACKOFF_MS);
|
|
986
|
+
}
|
|
987
|
+
return armed ? PAIR_LOOP_ACTIVE_INTERVAL_MS : PAIR_LOOP_IDLE_INTERVAL_MS;
|
|
988
|
+
}
|
|
989
|
+
var ClaudePairLoop = class {
|
|
990
|
+
constructor(deps) {
|
|
991
|
+
this.deps = deps;
|
|
992
|
+
this.setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
993
|
+
this.clearTimer = deps.clearTimer ?? ((h) => clearTimeout(h));
|
|
994
|
+
}
|
|
995
|
+
timer = null;
|
|
996
|
+
inFlight = false;
|
|
997
|
+
started = false;
|
|
998
|
+
consecutiveFailures = 0;
|
|
999
|
+
// Bumped by stop(): a timer callback from an earlier start() whose tick is
|
|
1000
|
+
// still in flight must not schedule a second timer chain after a restart.
|
|
1001
|
+
generation = 0;
|
|
1002
|
+
inFlightTick = null;
|
|
1003
|
+
setTimer;
|
|
1004
|
+
clearTimer;
|
|
1005
|
+
/** Arm the loop. The first tick runs after one idle interval. Idempotent. */
|
|
1006
|
+
start() {
|
|
1007
|
+
if (this.started) return;
|
|
1008
|
+
this.started = true;
|
|
1009
|
+
this.schedule(PAIR_LOOP_IDLE_INTERVAL_MS);
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Disarm the loop. No new tick starts, and a callback from this generation
|
|
1013
|
+
* will not reschedule. Returns a promise that settles when a tick already in
|
|
1014
|
+
* flight has finished (immediately if none), so shutdown can wait for it
|
|
1015
|
+
* before sweeping tmux sessions.
|
|
1016
|
+
*/
|
|
1017
|
+
stop() {
|
|
1018
|
+
this.started = false;
|
|
1019
|
+
this.generation += 1;
|
|
1020
|
+
if (this.timer != null) this.clearTimer(this.timer);
|
|
1021
|
+
this.timer = null;
|
|
1022
|
+
const pending2 = this.inFlightTick;
|
|
1023
|
+
return pending2 ? pending2.then(() => void 0, () => void 0) : Promise.resolve();
|
|
1024
|
+
}
|
|
1025
|
+
/** True while started. A tick in flight checks this before spawning anything new. */
|
|
1026
|
+
get accepting() {
|
|
1027
|
+
return this.started;
|
|
1028
|
+
}
|
|
1029
|
+
get failures() {
|
|
1030
|
+
return this.consecutiveFailures;
|
|
1031
|
+
}
|
|
1032
|
+
/** True while a tick is running. Set synchronously before the tick starts. */
|
|
1033
|
+
get busy() {
|
|
1034
|
+
return this.inFlight;
|
|
1035
|
+
}
|
|
1036
|
+
/** One guarded pass. Safe to call from anywhere; never overlaps itself. */
|
|
1037
|
+
async runOnce() {
|
|
1038
|
+
if (!this.deps.canRun()) return "skipped-not-running";
|
|
1039
|
+
if (this.inFlight) return "skipped-in-flight";
|
|
1040
|
+
this.inFlight = true;
|
|
1041
|
+
try {
|
|
1042
|
+
const tick = this.deps.tick();
|
|
1043
|
+
this.inFlightTick = tick;
|
|
1044
|
+
await tick;
|
|
1045
|
+
if (this.consecutiveFailures > 0) {
|
|
1046
|
+
this.deps.log(`[claude-pair] loop recovered after ${this.consecutiveFailures} failed tick(s)`);
|
|
1047
|
+
}
|
|
1048
|
+
this.consecutiveFailures = 0;
|
|
1049
|
+
return "ran";
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
this.consecutiveFailures += 1;
|
|
1052
|
+
if (this.consecutiveFailures === 1 || this.consecutiveFailures % 10 === 0) {
|
|
1053
|
+
this.deps.log(
|
|
1054
|
+
`[claude-pair] poll failed (${this.consecutiveFailures} in a row): ${err?.message ?? String(err)}`
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
return "failed";
|
|
1058
|
+
} finally {
|
|
1059
|
+
this.inFlight = false;
|
|
1060
|
+
this.inFlightTick = null;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
schedule(ms) {
|
|
1064
|
+
if (!this.started) return;
|
|
1065
|
+
const gen = this.generation;
|
|
1066
|
+
const h = this.setTimer(() => {
|
|
1067
|
+
this.timer = null;
|
|
1068
|
+
if (gen !== this.generation) return;
|
|
1069
|
+
void this.runOnce().finally(() => {
|
|
1070
|
+
if (gen !== this.generation) return;
|
|
1071
|
+
let armed = false;
|
|
1072
|
+
try {
|
|
1073
|
+
armed = this.deps.isArmed();
|
|
1074
|
+
} catch {
|
|
1075
|
+
armed = false;
|
|
1076
|
+
}
|
|
1077
|
+
this.schedule(nextPairLoopDelayMs(armed, this.consecutiveFailures));
|
|
1078
|
+
});
|
|
1079
|
+
}, ms);
|
|
1080
|
+
if (h && typeof h === "object" && typeof h.unref === "function") h.unref();
|
|
1081
|
+
this.timer = h;
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
var PairRetryBackoff = class {
|
|
1085
|
+
state = /* @__PURE__ */ new Map();
|
|
1086
|
+
/** True while this pair is inside its backoff window: skip it this tick. */
|
|
1087
|
+
shouldSkip(pairId, nowMs) {
|
|
1088
|
+
const s = this.state.get(pairId);
|
|
1089
|
+
return !!s && nowMs < s.notBeforeMs;
|
|
1090
|
+
}
|
|
1091
|
+
recordFailure(pairId, nowMs) {
|
|
1092
|
+
const failures = (this.state.get(pairId)?.failures ?? 0) + 1;
|
|
1093
|
+
this.state.set(pairId, { failures, notBeforeMs: nowMs + nextPairLoopDelayMs(true, failures) });
|
|
1094
|
+
return failures;
|
|
1095
|
+
}
|
|
1096
|
+
/** A dispatch that completed clears the pair's backoff. */
|
|
1097
|
+
recordSuccess(pairId) {
|
|
1098
|
+
this.state.delete(pairId);
|
|
1099
|
+
}
|
|
1100
|
+
/** Forget pairs that are neither spawned nor pending any more. */
|
|
1101
|
+
prune(liveIds) {
|
|
1102
|
+
const live = new Set(liveIds);
|
|
1103
|
+
for (const id of [...this.state.keys()]) if (!live.has(id)) this.state.delete(id);
|
|
1104
|
+
}
|
|
1105
|
+
failuresFor(pairId) {
|
|
1106
|
+
return this.state.get(pairId)?.failures ?? 0;
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
960
1110
|
// src/lib/mcp-config-drift.ts
|
|
961
1111
|
import { createHash } from "crypto";
|
|
962
1112
|
function decideMcpDriftAction(currentHash, knownHash) {
|
|
@@ -15649,7 +15799,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
15649
15799
|
var lastVersionCheckAt = 0;
|
|
15650
15800
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
15651
15801
|
var lastResponsivenessProbeAt = 0;
|
|
15652
|
-
var agtCliVersion = true ? "0.28.
|
|
15802
|
+
var agtCliVersion = true ? "0.28.1150" : "dev";
|
|
15653
15803
|
function resolveBrewPath(execFileSync3) {
|
|
15654
15804
|
try {
|
|
15655
15805
|
const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -17767,7 +17917,12 @@ async function pollCycleInner() {
|
|
|
17767
17917
|
try {
|
|
17768
17918
|
const desiredClaudeCodeVersion = hbResp?.desired_claude_code_version?.trim();
|
|
17769
17919
|
const { maybeUpdateClaudeCode, shouldRunClaudeCodeUpdater } = await import("../claude-code-updater-JXL4CM2V.js");
|
|
17770
|
-
|
|
17920
|
+
claudeUpdaterInFlight = true;
|
|
17921
|
+
if (desiredClaudeCodeVersion && (claudePairLoop.busy || claudePairingInProgress())) {
|
|
17922
|
+
log(
|
|
17923
|
+
claudePairingInProgress() ? "[claude-code-updater] Claude re-login in progress on this host, deferring to a later cycle" : "[claude-code-updater] a claude-pair poll is mid-flight (no re-login), deferring to the next cycle"
|
|
17924
|
+
);
|
|
17925
|
+
} else if (desiredClaudeCodeVersion && shouldRunClaudeCodeUpdater({
|
|
17771
17926
|
desiredVersion: desiredClaudeCodeVersion,
|
|
17772
17927
|
installedVersion: cachedFrameworkVersion,
|
|
17773
17928
|
windowGate: decideMaintenanceWindowGate({ window: cachedMaintenanceWindow, now: /* @__PURE__ */ new Date() })
|
|
@@ -17780,6 +17935,8 @@ async function pollCycleInner() {
|
|
|
17780
17935
|
}
|
|
17781
17936
|
} catch (err) {
|
|
17782
17937
|
log(`Claude Code updater error: ${err.message}`);
|
|
17938
|
+
} finally {
|
|
17939
|
+
claudeUpdaterInFlight = false;
|
|
17783
17940
|
}
|
|
17784
17941
|
} catch (err) {
|
|
17785
17942
|
setConfigHash(null);
|
|
@@ -18460,6 +18617,10 @@ async function pollCycleInner() {
|
|
|
18460
18617
|
}
|
|
18461
18618
|
const data = await api.post("/host/agents", { host_id: hostId });
|
|
18462
18619
|
const agents = data.agents ?? [];
|
|
18620
|
+
pairableAgentsSnapshot = agents.map((a) => ({
|
|
18621
|
+
agentId: a.agent_id,
|
|
18622
|
+
codeName: a.code_name
|
|
18623
|
+
}));
|
|
18463
18624
|
hostFlagStore().setMirrorAgentIds(agents.map((a) => a.agent_id));
|
|
18464
18625
|
if (agents.length > 0) {
|
|
18465
18626
|
if (consecutiveEmptyAgentPolls > 0) {
|
|
@@ -18881,14 +19042,6 @@ async function pollCycleInner() {
|
|
|
18881
19042
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
18882
19043
|
}
|
|
18883
19044
|
}
|
|
18884
|
-
try {
|
|
18885
|
-
await processClaudePairSessions(agents.map((a) => ({
|
|
18886
|
-
agentId: a.agent_id,
|
|
18887
|
-
codeName: a.code_name
|
|
18888
|
-
})));
|
|
18889
|
-
} catch (err) {
|
|
18890
|
-
log(`[claude-pair] poll failed: ${err.message}`);
|
|
18891
|
-
}
|
|
18892
19045
|
if (Date.now() - lastChannelSweepAt >= CHANNEL_SWEEP_INTERVAL_MS) {
|
|
18893
19046
|
lastChannelSweepAt = Date.now();
|
|
18894
19047
|
const agentCodeNames = new Set(
|
|
@@ -22269,7 +22422,11 @@ async function ensurePersistentSessionUnchecked(agent, tasks, boardItems, refres
|
|
|
22269
22422
|
const currentPolicyId = currentPolicyModel ? modelPolicy?.policyId ?? null : null;
|
|
22270
22423
|
const recordedAuthTuple = claudeAuthTupleBySession.get(codeName);
|
|
22271
22424
|
const recordedPolicyModel = modelPolicyModelBySession.get(codeName);
|
|
22272
|
-
|
|
22425
|
+
const authTupleChanged = Boolean(recordedAuthTuple && recordedAuthTuple !== currentAuthTuple);
|
|
22426
|
+
if (authTupleChanged && claudePairingInProgress()) {
|
|
22427
|
+
log(`[${codeName}] auth tuple changed while a Claude re-login is in progress, deferring the restart to the next cycle`);
|
|
22428
|
+
}
|
|
22429
|
+
if (recordedAuthTuple && recordedAuthTuple !== currentAuthTuple && !claudePairingInProgress() && isSessionHealthy(codeName)) {
|
|
22273
22430
|
const authTupleDiff = diffAuthTuples(recordedAuthTuple, currentAuthTuple);
|
|
22274
22431
|
log(
|
|
22275
22432
|
`[persistent-session] Auth config changed for '${codeName}' (${recordedAuthTuple} \u2192 ${currentAuthTuple}) \u2014 ${authTupleDiff.summary} \u2014 restarting session`
|
|
@@ -22280,7 +22437,9 @@ async function ensurePersistentSessionUnchecked(agent, tasks, boardItems, refres
|
|
|
22280
22437
|
previousModel: recordedPolicyModel,
|
|
22281
22438
|
currentModel: currentPolicyModel
|
|
22282
22439
|
});
|
|
22283
|
-
|
|
22440
|
+
let restartNoticeLanded = false;
|
|
22441
|
+
const noticeAlreadyDelivered = restartNoticeDeliveredFor.get(codeName) === currentAuthTuple;
|
|
22442
|
+
if (policyNotice && isSessionHealthy(codeName) && !noticeAlreadyDelivered) {
|
|
22284
22443
|
const status = await injectMessageWithStatus(
|
|
22285
22444
|
codeName,
|
|
22286
22445
|
"system",
|
|
@@ -22293,6 +22452,7 @@ async function ensurePersistentSessionUnchecked(agent, tasks, boardItems, refres
|
|
|
22293
22452
|
log
|
|
22294
22453
|
).catch(() => ({ delivered: false, fallbackUsed: false }));
|
|
22295
22454
|
if (status.fallbackUsed) {
|
|
22455
|
+
restartNoticeLanded = true;
|
|
22296
22456
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
22297
22457
|
} else {
|
|
22298
22458
|
log(
|
|
@@ -22300,18 +22460,35 @@ async function ensurePersistentSessionUnchecked(agent, tasks, boardItems, refres
|
|
|
22300
22460
|
);
|
|
22301
22461
|
}
|
|
22302
22462
|
}
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
|
|
22306
|
-
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22463
|
+
if (claudePairingInProgress()) {
|
|
22464
|
+
if (restartNoticeLanded) {
|
|
22465
|
+
restartNoticeDeliveredFor.set(codeName, currentAuthTuple);
|
|
22466
|
+
await injectMessageWithStatus(
|
|
22467
|
+
codeName,
|
|
22468
|
+
"system",
|
|
22469
|
+
"Update: the restart announced above is postponed for a moment while a Claude sign-in finishes on this host. Carry on; it will happen shortly, without a further notice.",
|
|
22470
|
+
{ task_name: "model-policy-restart-postponed" },
|
|
22471
|
+
log
|
|
22472
|
+
).catch(() => void 0);
|
|
22311
22473
|
}
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22474
|
+
log(
|
|
22475
|
+
`[${codeName}] Claude re-login in progress, deferring the auth-tuple restart to the next cycle` + (restartNoticeLanded || noticeAlreadyDelivered ? " (restart notice already delivered, not re-sent)" : "")
|
|
22476
|
+
);
|
|
22477
|
+
} else {
|
|
22478
|
+
restartNoticeDeliveredFor.delete(codeName);
|
|
22479
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName, "auth-tuple-change", void 0, void 0, {
|
|
22480
|
+
reason: authTupleDiff.summary,
|
|
22481
|
+
detail: {
|
|
22482
|
+
auth_tuple: {
|
|
22483
|
+
changed: authTupleDiff.changed,
|
|
22484
|
+
unchanged: authTupleDiff.unchanged,
|
|
22485
|
+
components: authTupleDiff.components
|
|
22486
|
+
}
|
|
22487
|
+
}
|
|
22488
|
+
});
|
|
22489
|
+
agentState.persistentSessionAgents.delete(codeName);
|
|
22490
|
+
restartTrigger = "auth-tuple";
|
|
22491
|
+
}
|
|
22315
22492
|
}
|
|
22316
22493
|
const staleForToday = isStaleForToday(codeName, /* @__PURE__ */ new Date(), agentTimezone);
|
|
22317
22494
|
if (!staleForToday) pendingDayRolloverReset.delete(codeName);
|
|
@@ -23771,6 +23948,47 @@ var boardTokenCache = new BoardTokenCache();
|
|
|
23771
23948
|
var notifyBoardCache = /* @__PURE__ */ new Map();
|
|
23772
23949
|
var notifiedWaitingHumanIds = /* @__PURE__ */ new Map();
|
|
23773
23950
|
var spawnedPairIds = /* @__PURE__ */ new Set();
|
|
23951
|
+
var pairableAgentsSnapshot = [];
|
|
23952
|
+
var lastPairPendingIds = [];
|
|
23953
|
+
var pairFirstSeenMs = /* @__PURE__ */ new Map();
|
|
23954
|
+
function claudePairLoopArmed() {
|
|
23955
|
+
return pairLoopArmed({
|
|
23956
|
+
spawnedPairIds,
|
|
23957
|
+
pendingPairIds: lastPairPendingIds,
|
|
23958
|
+
firstSeenMs: pairFirstSeenMs,
|
|
23959
|
+
nowMs: Date.now()
|
|
23960
|
+
});
|
|
23961
|
+
}
|
|
23962
|
+
var pairRetryBackoff = new PairRetryBackoff();
|
|
23963
|
+
var restartNoticeDeliveredFor = /* @__PURE__ */ new Map();
|
|
23964
|
+
var claudeUpdaterInFlight = false;
|
|
23965
|
+
async function runClaudePairTick() {
|
|
23966
|
+
if (pairableAgentsSnapshot.length === 0 && spawnedPairIds.size === 0) lastPairPendingIds = [];
|
|
23967
|
+
if (claudeUpdaterInFlight) return;
|
|
23968
|
+
await processClaudePairSessions(pairableAgentsSnapshot);
|
|
23969
|
+
pairRetryBackoff.prune([...spawnedPairIds, ...lastPairPendingIds]);
|
|
23970
|
+
}
|
|
23971
|
+
async function _claudePairTickForTests(agents) {
|
|
23972
|
+
const prevRunning = running;
|
|
23973
|
+
running = true;
|
|
23974
|
+
claudePairLoop.start();
|
|
23975
|
+
pairableAgentsSnapshot = [...agents];
|
|
23976
|
+
try {
|
|
23977
|
+
await runClaudePairTick();
|
|
23978
|
+
} finally {
|
|
23979
|
+
void claudePairLoop.stop();
|
|
23980
|
+
running = prevRunning;
|
|
23981
|
+
}
|
|
23982
|
+
}
|
|
23983
|
+
function claudePairingInProgress() {
|
|
23984
|
+
return claudePairLoopArmed();
|
|
23985
|
+
}
|
|
23986
|
+
var claudePairLoop = new ClaudePairLoop({
|
|
23987
|
+
tick: runClaudePairTick,
|
|
23988
|
+
isArmed: claudePairLoopArmed,
|
|
23989
|
+
canRun: () => running && !!config && !restartAfterUpgrade,
|
|
23990
|
+
log
|
|
23991
|
+
});
|
|
23774
23992
|
async function processClaudePairSessions(agents) {
|
|
23775
23993
|
if (agents.length === 0 && spawnedPairIds.size === 0) return;
|
|
23776
23994
|
const agentIds = agents.map((a) => a.agentId);
|
|
@@ -23779,6 +23997,7 @@ async function processClaudePairSessions(agents) {
|
|
|
23779
23997
|
agent_ids: agentIds,
|
|
23780
23998
|
spawned_pair_ids: Array.from(spawnedPairIds)
|
|
23781
23999
|
});
|
|
24000
|
+
lastPairPendingIds = (pendingResp.pending ?? []).map((p) => p.pair_id);
|
|
23782
24001
|
const {
|
|
23783
24002
|
startClaudePair,
|
|
23784
24003
|
submitClaudePairCode,
|
|
@@ -23834,6 +24053,9 @@ async function processClaudePairSessions(agents) {
|
|
|
23834
24053
|
return resp?.no_op !== true;
|
|
23835
24054
|
}
|
|
23836
24055
|
for (const session of pendingResp.pending) {
|
|
24056
|
+
if (!running || !claudePairLoop.accepting) break;
|
|
24057
|
+
if (pairRetryBackoff.shouldSkip(session.pair_id, Date.now())) continue;
|
|
24058
|
+
let dispatchFailed = false;
|
|
23837
24059
|
const codeName = codeNameByAgentId.get(session.agent_id) ?? "<unassigned>";
|
|
23838
24060
|
const pairSession = pairTmuxSession(session.pair_id);
|
|
23839
24061
|
try {
|
|
@@ -23930,7 +24152,13 @@ async function processClaudePairSessions(agents) {
|
|
|
23930
24152
|
}
|
|
23931
24153
|
}
|
|
23932
24154
|
} catch (err) {
|
|
23933
|
-
|
|
24155
|
+
dispatchFailed = true;
|
|
24156
|
+
const failures = pairRetryBackoff.recordFailure(session.pair_id, Date.now());
|
|
24157
|
+
log(
|
|
24158
|
+
`[claude-pair] dispatch failed for pair ${session.pair_id.slice(0, 8)} (${failures} in a row, retrying in ${Math.round(nextPairLoopDelayMs(true, failures) / 1e3)}s): ${err.message}`
|
|
24159
|
+
);
|
|
24160
|
+
} finally {
|
|
24161
|
+
if (!dispatchFailed) pairRetryBackoff.recordSuccess(session.pair_id);
|
|
23934
24162
|
}
|
|
23935
24163
|
}
|
|
23936
24164
|
}
|
|
@@ -24617,6 +24845,7 @@ function startPolling() {
|
|
|
24617
24845
|
void postLivenessHeartbeat();
|
|
24618
24846
|
}, LIVENESS_HEARTBEAT_INTERVAL_MS);
|
|
24619
24847
|
livenessTimer.unref();
|
|
24848
|
+
claudePairLoop.start();
|
|
24620
24849
|
startPaneOccupancySampler(() => agentState.persistentSessionAgents);
|
|
24621
24850
|
startPidPressureSampler(() => agentState.persistentSessionAgents);
|
|
24622
24851
|
startRuntimeVersionSampler(() => agentState.persistentSessionAgents);
|
|
@@ -24783,6 +25012,7 @@ async function stopPolling(opts = {}) {
|
|
|
24783
25012
|
clearInterval(livenessTimer);
|
|
24784
25013
|
livenessTimer = null;
|
|
24785
25014
|
}
|
|
25015
|
+
const pairLoopDrained = claudePairLoop.stop();
|
|
24786
25016
|
stopPaneOccupancySampler();
|
|
24787
25017
|
stopPidPressureSampler();
|
|
24788
25018
|
stopRuntimeVersionSampler();
|
|
@@ -24829,6 +25059,7 @@ async function stopPolling(opts = {}) {
|
|
|
24829
25059
|
closeSessionRunForCode(codeName, "completed", "manager shutdown");
|
|
24830
25060
|
}
|
|
24831
25061
|
const drainReleaseSnapshot = snapshotDrainReleaseTargets();
|
|
25062
|
+
await Promise.race([pairLoopDrained, new Promise((r) => setTimeout(r, 3e3).unref())]);
|
|
24832
25063
|
log("Killing tmux sessions...");
|
|
24833
25064
|
try {
|
|
24834
25065
|
await killAllAgtTmuxSessions();
|
|
@@ -25198,6 +25429,7 @@ export {
|
|
|
25198
25429
|
REALTIME_REBIND_STUCK_AFTER_MS,
|
|
25199
25430
|
SELF_UPDATE_LATCH_MAX_HOLD_MS,
|
|
25200
25431
|
UntrustworthyToolkitResponseError,
|
|
25432
|
+
_claudePairTickForTests,
|
|
25201
25433
|
applyMemoryTombstones,
|
|
25202
25434
|
applyRestartAcks,
|
|
25203
25435
|
cancelKanbanNoticeOnDrain,
|
|
@@ -25206,6 +25438,8 @@ export {
|
|
|
25206
25438
|
claudeCodeUpgradeMarkerPath,
|
|
25207
25439
|
claudeCodeUpgradeThrottled,
|
|
25208
25440
|
claudeManagedSettingsPath,
|
|
25441
|
+
claudePairLoopArmed,
|
|
25442
|
+
claudePairingInProgress,
|
|
25209
25443
|
coverageOf,
|
|
25210
25444
|
createManagedPruneLedger,
|
|
25211
25445
|
createManagedToolkitReader,
|