@integrity-labs/agt-cli 0.28.499 → 0.28.501
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 +4 -4
- package/dist/{chunk-Q5O2VAQA.js → chunk-N4Y5IBZR.js} +443 -34
- package/dist/chunk-N4Y5IBZR.js.map +1 -0
- package/dist/{chunk-5XHO4HKA.js → chunk-XKBUTWON.js} +25 -2
- package/dist/chunk-XKBUTWON.js.map +1 -0
- package/dist/{claude-pair-runtime-TA2D5FPK.js → claude-pair-runtime-U5ES3GOY.js} +2 -2
- package/dist/lib/manager-worker.js +42 -336
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-NZMYK2WQ.js → persistent-session-HL3LNPAX.js} +4 -2
- package/dist/{responsiveness-probe-RFANLJWP.js → responsiveness-probe-AZ44ONES.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-5XHO4HKA.js.map +0 -1
- package/dist/chunk-Q5O2VAQA.js.map +0 -1
- /package/dist/{claude-pair-runtime-TA2D5FPK.js.map → claude-pair-runtime-U5ES3GOY.js.map} +0 -0
- /package/dist/{persistent-session-NZMYK2WQ.js.map → persistent-session-HL3LNPAX.js.map} +0 -0
- /package/dist/{responsiveness-probe-RFANLJWP.js.map → responsiveness-probe-AZ44ONES.js.map} +0 -0
|
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
|
|
|
100
100
|
return { ok: true };
|
|
101
101
|
} catch {
|
|
102
102
|
}
|
|
103
|
-
const { resolveClaudeBinary } = await import("./persistent-session-
|
|
103
|
+
const { resolveClaudeBinary } = await import("./persistent-session-HL3LNPAX.js");
|
|
104
104
|
const claudeBin = resolveClaudeBinary();
|
|
105
105
|
const pairEnv = {
|
|
106
106
|
...process.env,
|
|
@@ -373,4 +373,4 @@ export {
|
|
|
373
373
|
startClaudePair,
|
|
374
374
|
submitClaudePairCode
|
|
375
375
|
};
|
|
376
|
-
//# sourceMappingURL=claude-pair-runtime-
|
|
376
|
+
//# sourceMappingURL=claude-pair-runtime-U5ES3GOY.js.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ApiError,
|
|
3
3
|
CHANNEL_SECRET_ENV_KEYS,
|
|
4
|
+
FIRST_CONNECT_WINDOW_MS,
|
|
4
5
|
HostFlagStore,
|
|
6
|
+
RestartBreaker,
|
|
5
7
|
SUPERVISOR_RESTART_EXIT_CODE,
|
|
6
8
|
api,
|
|
7
9
|
atomicWriteFileSync,
|
|
@@ -25,9 +27,11 @@ import {
|
|
|
25
27
|
getCachedClaudeAuthMode,
|
|
26
28
|
getHostId,
|
|
27
29
|
givenUpMcpServerKeys,
|
|
30
|
+
isProvisioningReloadReason,
|
|
28
31
|
isTransientAuthFailure,
|
|
29
32
|
liveProxyExtraHeaderVars,
|
|
30
33
|
liveProxyTokenVars,
|
|
34
|
+
markQuarantinedForProbe,
|
|
31
35
|
migrateAgentDirToIdKeyed,
|
|
32
36
|
pinAllowsUrgent,
|
|
33
37
|
provision,
|
|
@@ -38,14 +42,17 @@ import {
|
|
|
38
42
|
provisionPreCompactHook,
|
|
39
43
|
provisionSessionStateHook,
|
|
40
44
|
provisionStopHook,
|
|
45
|
+
readEnvNumber,
|
|
41
46
|
reapMissingMcpSessions,
|
|
42
47
|
reapStaleMcpChildren,
|
|
48
|
+
reaperRestartBreakerReason,
|
|
43
49
|
reconstructQuarantinedMcpServerEntry,
|
|
44
50
|
requireHost,
|
|
45
51
|
resolveEffectivePinRaw,
|
|
46
52
|
safeWriteJsonAtomic,
|
|
47
|
-
setConfigHash
|
|
48
|
-
|
|
53
|
+
setConfigHash,
|
|
54
|
+
tripClass
|
|
55
|
+
} from "../chunk-N4Y5IBZR.js";
|
|
49
56
|
import {
|
|
50
57
|
getProjectDir as getProjectDir2,
|
|
51
58
|
getReadyTasks,
|
|
@@ -56,7 +63,6 @@ import {
|
|
|
56
63
|
import {
|
|
57
64
|
ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
|
|
58
65
|
AnchorSessionClient,
|
|
59
|
-
BIND_FAILURE_QUARANTINE_THRESHOLD,
|
|
60
66
|
CLAUDE_MD_MAX_CHARS,
|
|
61
67
|
CONVERSATION_FAILURE_CATEGORIES,
|
|
62
68
|
DEFAULT_FRAMEWORK,
|
|
@@ -67,9 +73,6 @@ import {
|
|
|
67
73
|
KANBAN_CHECK_COMMAND,
|
|
68
74
|
KANBAN_NUDGE_ACTIONABLE_STATUSES,
|
|
69
75
|
MAX_AVATAR_ENV_URL_BYTES,
|
|
70
|
-
MIN_PROVISIONING_MAX_FOR_QUARANTINE_ORDERING,
|
|
71
|
-
RESTART_BREAKER_PROVISIONING_MAX,
|
|
72
|
-
RESTART_BREAKER_PROVISIONING_WINDOW_MS,
|
|
73
76
|
SCHEDULED_TURN_MARKER_FILENAME,
|
|
74
77
|
SUPPRESS_SENTINEL,
|
|
75
78
|
StreamEncoder,
|
|
@@ -150,6 +153,7 @@ import {
|
|
|
150
153
|
resolveClaudeBinary,
|
|
151
154
|
resolveDmTarget,
|
|
152
155
|
restartEgressSidecar,
|
|
156
|
+
rotatePaneLogForDayRollover,
|
|
153
157
|
rotateSessionForWedge,
|
|
154
158
|
serializeAccountEnforcementMarker,
|
|
155
159
|
sessionTranscriptDir,
|
|
@@ -167,7 +171,7 @@ import {
|
|
|
167
171
|
toOpencodeModel,
|
|
168
172
|
transcriptActivityAgeSeconds,
|
|
169
173
|
writeEgressAllowlist
|
|
170
|
-
} from "../chunk-
|
|
174
|
+
} from "../chunk-XKBUTWON.js";
|
|
171
175
|
import {
|
|
172
176
|
reapOrphanChannelMcps
|
|
173
177
|
} from "../chunk-XWVM4KPK.js";
|
|
@@ -844,315 +848,8 @@ function logClaudeMdCeilingTransition(codeName, report, log2) {
|
|
|
844
848
|
return line;
|
|
845
849
|
}
|
|
846
850
|
|
|
847
|
-
// src/lib/restart-breaker.ts
|
|
848
|
-
function reaperRestartBreakerReason(activeKeys) {
|
|
849
|
-
return activeKeys.length >= 2 ? "mcp-presence-reaper" : void 0;
|
|
850
|
-
}
|
|
851
|
-
var KNOWN_REASON_CLASS_MAP = {
|
|
852
|
-
crash: true,
|
|
853
|
-
provisioning: true,
|
|
854
|
-
"credential-rotation": true,
|
|
855
|
-
// ENG-8215: the self-healing class (bind-remediation - the manager's OWN
|
|
856
|
-
// repair attempts, which must never count toward a trip). Adding it here is
|
|
857
|
-
// forced by the Record type, which is exactly why that shape was chosen over
|
|
858
|
-
// a Set literal (ENG-7577, CodeRabbit on #3861) - a new union member cannot
|
|
859
|
-
// be silently omitted from the rehydration allowlist.
|
|
860
|
-
"self-healing": true
|
|
861
|
-
};
|
|
862
|
-
var KNOWN_REASON_CLASSES = new Set(
|
|
863
|
-
Object.keys(KNOWN_REASON_CLASS_MAP)
|
|
864
|
-
);
|
|
865
|
-
var PROVISIONING_RELOAD_REASONS = /* @__PURE__ */ new Set([
|
|
866
|
-
"hot-reload-mcp",
|
|
867
|
-
"managed-mcp-churn",
|
|
868
|
-
// ENG-8215: 'bind-remediation' MOVED OUT of this set into SELF_HEALING_REASONS.
|
|
869
|
-
// It is the manager repairing itself, not a config reload, and counting it
|
|
870
|
-
// meant the harder the platform tried to fix an agent the more certain it
|
|
871
|
-
// became that it should give up. Deliberately NOT re-added here.
|
|
872
|
-
// ENG-7576: the dashboard integration-add stop itself - the first restart of
|
|
873
|
-
// the very burst this class exists for, previously uncounted entirely.
|
|
874
|
-
"integration-change",
|
|
875
|
-
// ENG-7771: a sender_policy delivery restart is a deliberate config-delivery
|
|
876
|
-
// reload (operator changed the policy, or the fail-closed first-poll verify
|
|
877
|
-
// after a manager restart with no persisted baseline), not a crash. On the
|
|
878
|
-
// tight crash tally, a host whose sender-policy-baseline.json write keeps
|
|
879
|
-
// failing (disk full, permissions) would convert the fail-closed restart
|
|
880
|
-
// into repeated crash tallies across a multi-deploy day and trip the
|
|
881
|
-
// breaker - taking the agent DOWN, which is strictly worse than the stale
|
|
882
|
-
// policy the restart exists to fix. The looser provisioning tally still
|
|
883
|
-
// trips + pages on sustained thrash.
|
|
884
|
-
"sender-policy-change"
|
|
885
|
-
]);
|
|
886
|
-
function isProvisioningReloadReason(reason) {
|
|
887
|
-
return PROVISIONING_RELOAD_REASONS.has(reason);
|
|
888
|
-
}
|
|
889
|
-
var CREDENTIAL_ROTATION_REASONS = /* @__PURE__ */ new Set([
|
|
890
|
-
"credential-rotation"
|
|
891
|
-
]);
|
|
892
|
-
function isCredentialRotationReason(reason) {
|
|
893
|
-
return CREDENTIAL_ROTATION_REASONS.has(reason);
|
|
894
|
-
}
|
|
895
|
-
var SELF_HEALING_REASONS = /* @__PURE__ */ new Set([
|
|
896
|
-
"bind-remediation"
|
|
897
|
-
]);
|
|
898
|
-
function isSelfHealingReason(reason) {
|
|
899
|
-
return SELF_HEALING_REASONS.has(reason);
|
|
900
|
-
}
|
|
901
|
-
function restartReasonClass(reason) {
|
|
902
|
-
if (isCredentialRotationReason(reason)) return "credential-rotation";
|
|
903
|
-
if (isSelfHealingReason(reason)) return "self-healing";
|
|
904
|
-
return isProvisioningReloadReason(reason) ? "provisioning" : "crash";
|
|
905
|
-
}
|
|
906
|
-
function countByClass(events) {
|
|
907
|
-
let crash = 0;
|
|
908
|
-
let provisioning = 0;
|
|
909
|
-
let credentialRotation = 0;
|
|
910
|
-
let selfHealing = 0;
|
|
911
|
-
for (const e of events) {
|
|
912
|
-
const klass = restartReasonClass(e.reason);
|
|
913
|
-
if (klass === "provisioning") provisioning += 1;
|
|
914
|
-
else if (klass === "credential-rotation") credentialRotation += 1;
|
|
915
|
-
else if (klass === "self-healing") selfHealing += 1;
|
|
916
|
-
else crash += 1;
|
|
917
|
-
}
|
|
918
|
-
return {
|
|
919
|
-
crash,
|
|
920
|
-
provisioning,
|
|
921
|
-
"credential-rotation": credentialRotation,
|
|
922
|
-
"self-healing": selfHealing
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
function maxProvisioningBucket(events) {
|
|
926
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
927
|
-
for (const e of events) {
|
|
928
|
-
if (!isProvisioningReloadReason(e.reason)) continue;
|
|
929
|
-
const key = e.integrationKey ?? "";
|
|
930
|
-
buckets.set(key, (buckets.get(key) ?? 0) + 1);
|
|
931
|
-
}
|
|
932
|
-
let best = { count: 0, key: "" };
|
|
933
|
-
for (const [key, count] of buckets) {
|
|
934
|
-
if (count > best.count) best = { count, key };
|
|
935
|
-
}
|
|
936
|
-
return best;
|
|
937
|
-
}
|
|
938
|
-
function tripClass(trip) {
|
|
939
|
-
if (trip.trippedClass) return trip.trippedClass;
|
|
940
|
-
if (!Array.isArray(trip.eventsAtTrip) || trip.eventsAtTrip.length === 0) return "crash";
|
|
941
|
-
return trip.eventsAtTrip.every((e) => isProvisioningReloadReason(e.reason)) ? "provisioning" : "crash";
|
|
942
|
-
}
|
|
943
|
-
var DEFAULT_MAX = 2;
|
|
944
|
-
var DEFAULT_WINDOW_MS = 6e5;
|
|
945
|
-
var DEFAULT_PROVISIONING_MAX = RESTART_BREAKER_PROVISIONING_MAX;
|
|
946
|
-
var DEFAULT_PROVISIONING_WINDOW_MS = RESTART_BREAKER_PROVISIONING_WINDOW_MS;
|
|
947
|
-
function readEnvNumber(name, fallback) {
|
|
948
|
-
const raw = process.env[name];
|
|
949
|
-
if (!raw) return fallback;
|
|
950
|
-
const parsed = Number(raw);
|
|
951
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
952
|
-
}
|
|
953
|
-
var RestartBreaker = class {
|
|
954
|
-
max;
|
|
955
|
-
windowMs;
|
|
956
|
-
/**
|
|
957
|
-
* ENG-8215: NOT readonly - the constructor may clamp it upward to preserve the
|
|
958
|
-
* quarantine-fires-first ordering against a runtime override. Never mutated
|
|
959
|
-
* after construction.
|
|
960
|
-
*/
|
|
961
|
-
provisioningMax;
|
|
962
|
-
provisioningWindowMs;
|
|
963
|
-
/** Longest window either tally needs — how long the events log must retain. */
|
|
964
|
-
retentionMs;
|
|
965
|
-
now;
|
|
966
|
-
events = /* @__PURE__ */ new Map();
|
|
967
|
-
trips = /* @__PURE__ */ new Map();
|
|
968
|
-
/**
|
|
969
|
-
* ENG-8215: set when a supplied `provisioningMax` was raised to keep MCP
|
|
970
|
-
* quarantine ahead of the breaker. Read once by the manager at construction so
|
|
971
|
-
* an overridden-and-clamped value is visible in manager.log rather than
|
|
972
|
-
* silently discarded.
|
|
973
|
-
*/
|
|
974
|
-
quarantineOrderingClamped;
|
|
975
|
-
constructor(opts = {}) {
|
|
976
|
-
this.max = opts.max ?? readEnvNumber("AGT_RESTART_BREAKER_MAX", DEFAULT_MAX);
|
|
977
|
-
this.windowMs = opts.windowMs ?? readEnvNumber("AGT_RESTART_BREAKER_WINDOW_MS", DEFAULT_WINDOW_MS);
|
|
978
|
-
this.provisioningMax = opts.provisioningMax ?? readEnvNumber("AGT_RESTART_BREAKER_PROVISIONING_MAX", DEFAULT_PROVISIONING_MAX);
|
|
979
|
-
this.provisioningWindowMs = opts.provisioningWindowMs ?? readEnvNumber("AGT_RESTART_BREAKER_PROVISIONING_WINDOW_MS", DEFAULT_PROVISIONING_WINDOW_MS);
|
|
980
|
-
this.now = opts.now ?? Date.now;
|
|
981
|
-
if (!Number.isFinite(this.max) || this.max < 1) {
|
|
982
|
-
throw new Error("restart-breaker max must be a finite number >= 1");
|
|
983
|
-
}
|
|
984
|
-
if (!Number.isFinite(this.windowMs) || this.windowMs < 1e3) {
|
|
985
|
-
throw new Error("restart-breaker windowMs must be a finite number >= 1000");
|
|
986
|
-
}
|
|
987
|
-
if (!Number.isFinite(this.provisioningMax) || this.provisioningMax < 1) {
|
|
988
|
-
throw new Error("restart-breaker provisioningMax must be a finite number >= 1");
|
|
989
|
-
}
|
|
990
|
-
if (this.provisioningMax < MIN_PROVISIONING_MAX_FOR_QUARANTINE_ORDERING) {
|
|
991
|
-
this.quarantineOrderingClamped = {
|
|
992
|
-
requested: this.provisioningMax,
|
|
993
|
-
applied: MIN_PROVISIONING_MAX_FOR_QUARANTINE_ORDERING,
|
|
994
|
-
quarantineThreshold: BIND_FAILURE_QUARANTINE_THRESHOLD
|
|
995
|
-
};
|
|
996
|
-
this.provisioningMax = MIN_PROVISIONING_MAX_FOR_QUARANTINE_ORDERING;
|
|
997
|
-
}
|
|
998
|
-
if (!Number.isFinite(this.provisioningWindowMs) || this.provisioningWindowMs < 1e3) {
|
|
999
|
-
throw new Error("restart-breaker provisioningWindowMs must be a finite number >= 1000");
|
|
1000
|
-
}
|
|
1001
|
-
this.retentionMs = Math.max(this.windowMs, this.provisioningWindowMs);
|
|
1002
|
-
}
|
|
1003
|
-
/**
|
|
1004
|
-
* ENG-8215: the clamp record, if the configured provisioning max had to be
|
|
1005
|
-
* raised to keep MCP quarantine ahead of the breaker. `undefined` when the
|
|
1006
|
-
* configured value already satisfied the ordering.
|
|
1007
|
-
*/
|
|
1008
|
-
getQuarantineOrderingClamp() {
|
|
1009
|
-
return this.quarantineOrderingClamped;
|
|
1010
|
-
}
|
|
1011
|
-
/** True if this agent's breaker is currently tripped (manager must skip spawn). */
|
|
1012
|
-
isTripped(codeName) {
|
|
1013
|
-
return this.trips.has(codeName);
|
|
1014
|
-
}
|
|
1015
|
-
getTrip(codeName) {
|
|
1016
|
-
return this.trips.get(codeName);
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Record a restart event. If recording this event puts the count
|
|
1020
|
-
* inside the window above `max`, the breaker trips and the call site
|
|
1021
|
-
* should NOT respawn.
|
|
1022
|
-
*
|
|
1023
|
-
* Idempotent on already-tripped breakers: returns the existing trip
|
|
1024
|
-
* without double-counting events. Callers may still record the reason
|
|
1025
|
-
* via the decision-log for forensics.
|
|
1026
|
-
*/
|
|
1027
|
-
record(codeName, reason, integrationKey) {
|
|
1028
|
-
const existing = this.trips.get(codeName);
|
|
1029
|
-
if (existing) {
|
|
1030
|
-
const bucket = maxProvisioningBucket(existing.eventsAtTrip);
|
|
1031
|
-
return {
|
|
1032
|
-
tripped: false,
|
|
1033
|
-
trip: existing,
|
|
1034
|
-
windowCount: existing.eventsAtTrip.length,
|
|
1035
|
-
classCounts: countByClass(existing.eventsAtTrip),
|
|
1036
|
-
maxProvisioningBucket: bucket.count,
|
|
1037
|
-
maxProvisioningBucketKey: bucket.key
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
const at = this.now();
|
|
1041
|
-
const retentionCutoff = at - this.retentionMs;
|
|
1042
|
-
const prior = (this.events.get(codeName) ?? []).filter((e) => e.at >= retentionCutoff);
|
|
1043
|
-
prior.push(
|
|
1044
|
-
isProvisioningReloadReason(reason) && integrationKey ? { reason, at, integrationKey } : { reason, at }
|
|
1045
|
-
);
|
|
1046
|
-
this.events.set(codeName, prior);
|
|
1047
|
-
const crashCount = prior.filter(
|
|
1048
|
-
(e) => e.at >= at - this.windowMs && restartReasonClass(e.reason) === "crash"
|
|
1049
|
-
).length;
|
|
1050
|
-
const provEvents = prior.filter(
|
|
1051
|
-
(e) => e.at >= at - this.provisioningWindowMs && isProvisioningReloadReason(e.reason)
|
|
1052
|
-
);
|
|
1053
|
-
const credRotCount = prior.filter(
|
|
1054
|
-
(e) => e.at >= at - this.provisioningWindowMs && isCredentialRotationReason(e.reason)
|
|
1055
|
-
).length;
|
|
1056
|
-
const selfHealCount = prior.filter(
|
|
1057
|
-
(e) => e.at >= at - this.provisioningWindowMs && isSelfHealingReason(e.reason)
|
|
1058
|
-
).length;
|
|
1059
|
-
const windowCount = prior.filter((e) => e.at >= at - this.windowMs).length;
|
|
1060
|
-
const classCounts = {
|
|
1061
|
-
crash: crashCount,
|
|
1062
|
-
provisioning: provEvents.length,
|
|
1063
|
-
"credential-rotation": credRotCount,
|
|
1064
|
-
"self-healing": selfHealCount
|
|
1065
|
-
};
|
|
1066
|
-
const provBucket = maxProvisioningBucket(provEvents);
|
|
1067
|
-
let trippedClass;
|
|
1068
|
-
let tripEvents;
|
|
1069
|
-
let tripWindowMs = this.windowMs;
|
|
1070
|
-
if (crashCount > this.max) {
|
|
1071
|
-
trippedClass = "crash";
|
|
1072
|
-
tripEvents = prior.filter(
|
|
1073
|
-
(e) => e.at >= at - this.windowMs && restartReasonClass(e.reason) === "crash"
|
|
1074
|
-
);
|
|
1075
|
-
tripWindowMs = this.windowMs;
|
|
1076
|
-
} else if (provBucket.count > this.provisioningMax) {
|
|
1077
|
-
trippedClass = "provisioning";
|
|
1078
|
-
tripEvents = provEvents.filter((e) => (e.integrationKey ?? "") === provBucket.key);
|
|
1079
|
-
tripWindowMs = this.provisioningWindowMs;
|
|
1080
|
-
}
|
|
1081
|
-
if (trippedClass && tripEvents) {
|
|
1082
|
-
const trip = {
|
|
1083
|
-
trippedAt: at,
|
|
1084
|
-
eventsAtTrip: [...tripEvents],
|
|
1085
|
-
statusMessage: formatStatusMessage(tripEvents, tripWindowMs, trippedClass, provBucket.key),
|
|
1086
|
-
// ENG-7577: persist the tripping class so the recovery path can gate on
|
|
1087
|
-
// it after a manager restart, without re-deriving it from prose.
|
|
1088
|
-
trippedClass
|
|
1089
|
-
};
|
|
1090
|
-
this.trips.set(codeName, trip);
|
|
1091
|
-
this.events.delete(codeName);
|
|
1092
|
-
return {
|
|
1093
|
-
tripped: true,
|
|
1094
|
-
trip,
|
|
1095
|
-
windowCount,
|
|
1096
|
-
classCounts,
|
|
1097
|
-
trippedClass,
|
|
1098
|
-
maxProvisioningBucket: provBucket.count,
|
|
1099
|
-
maxProvisioningBucketKey: provBucket.key
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
return {
|
|
1103
|
-
tripped: false,
|
|
1104
|
-
windowCount,
|
|
1105
|
-
classCounts,
|
|
1106
|
-
maxProvisioningBucket: provBucket.count,
|
|
1107
|
-
maxProvisioningBucketKey: provBucket.key
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
/** Operator-initiated reset: drops the trip + the events log for this agent. */
|
|
1111
|
-
clear(codeName) {
|
|
1112
|
-
this.trips.delete(codeName);
|
|
1113
|
-
this.events.delete(codeName);
|
|
1114
|
-
}
|
|
1115
|
-
/** Snapshot tripped agents for `manager-state.json`. */
|
|
1116
|
-
serialize() {
|
|
1117
|
-
return Object.fromEntries(this.trips.entries());
|
|
1118
|
-
}
|
|
1119
|
-
/**
|
|
1120
|
-
* Rehydrate trip state from `manager-state.json`. Called once at
|
|
1121
|
-
* worker startup. Window history is intentionally NOT persisted — only
|
|
1122
|
-
* tripped state. A tripped breaker survives manager restart; an
|
|
1123
|
-
* un-tripped one starts a fresh window in the new worker.
|
|
1124
|
-
*/
|
|
1125
|
-
hydrate(saved) {
|
|
1126
|
-
if (!saved) return;
|
|
1127
|
-
for (const [codeName, trip] of Object.entries(saved)) {
|
|
1128
|
-
if (trip && typeof trip.trippedAt === "number" && Array.isArray(trip.eventsAtTrip)) {
|
|
1129
|
-
const klass = trip.trippedClass;
|
|
1130
|
-
this.trips.set(
|
|
1131
|
-
codeName,
|
|
1132
|
-
klass === void 0 || KNOWN_REASON_CLASSES.has(klass) ? trip : { ...trip, trippedClass: void 0 }
|
|
1133
|
-
);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
/** Test helper — current in-window event count for `codeName`. */
|
|
1138
|
-
windowCount(codeName) {
|
|
1139
|
-
const cutoff = this.now() - this.windowMs;
|
|
1140
|
-
return (this.events.get(codeName) ?? []).filter((e) => e.at >= cutoff).length;
|
|
1141
|
-
}
|
|
1142
|
-
};
|
|
1143
|
-
function formatStatusMessage(events, windowMs, klass = "crash", integrationKey = "") {
|
|
1144
|
-
const last = events[events.length - 1];
|
|
1145
|
-
const windowLabel = windowMs < 6e4 ? `${Math.round(windowMs / 1e3)}s` : `${(windowMs / 6e4).toFixed(1).replace(/\.0$/, "")}min`;
|
|
1146
|
-
const reasonCounts = /* @__PURE__ */ new Map();
|
|
1147
|
-
for (const e of events) reasonCounts.set(e.reason, (reasonCounts.get(e.reason) ?? 0) + 1);
|
|
1148
|
-
const breakdown = Array.from(reasonCounts.entries()).map(([r, n]) => `${r}=${n}`).join(", ");
|
|
1149
|
-
const classLabel = klass === "provisioning" ? "provisioning-reload " : "";
|
|
1150
|
-
const integrationLabel = klass === "provisioning" && integrationKey ? ` for integration '${integrationKey}'` : "";
|
|
1151
|
-
return `Circuit breaker tripped: ${events.length} ${classLabel}restarts in ${windowLabel}${integrationLabel} (${breakdown}); most recent=${last.reason} at ${new Date(last.at).toISOString()}`;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
851
|
// src/lib/mcp-flap-dampener.ts
|
|
1155
|
-
var
|
|
852
|
+
var DEFAULT_WINDOW_MS = 6e5;
|
|
1156
853
|
var DEFAULT_MIN_TRANSITIONS = 4;
|
|
1157
854
|
var DEFAULT_MIN_DISTINCT = 2;
|
|
1158
855
|
var DEFAULT_LONG_WINDOW_MS = 36e5;
|
|
@@ -1181,7 +878,7 @@ var McpFlapDampener = class {
|
|
|
1181
878
|
// see that method for the Kylie/dig (2026-06-29) rationale.
|
|
1182
879
|
lastFlapAt = /* @__PURE__ */ new Map();
|
|
1183
880
|
constructor(opts = {}) {
|
|
1184
|
-
this.windowMs = opts.windowMs ?? readEnvNumber2("AGT_MCP_FLAP_WINDOW_MS",
|
|
881
|
+
this.windowMs = opts.windowMs ?? readEnvNumber2("AGT_MCP_FLAP_WINDOW_MS", DEFAULT_WINDOW_MS);
|
|
1185
882
|
this.minTransitions = opts.minTransitions ?? readEnvNumber2("AGT_MCP_FLAP_MIN_TRANSITIONS", DEFAULT_MIN_TRANSITIONS);
|
|
1186
883
|
this.minDistinct = opts.minDistinct ?? readEnvNumber2("AGT_MCP_FLAP_MIN_DISTINCT", DEFAULT_MIN_DISTINCT);
|
|
1187
884
|
this.longWindowMs = opts.longWindowMs ?? readEnvNumber2("AGT_MCP_FLAP_LONG_WINDOW_MS", DEFAULT_LONG_WINDOW_MS);
|
|
@@ -1585,7 +1282,11 @@ async function runConnectivityProbes(integrations, options = {}) {
|
|
|
1585
1282
|
cliBinary: integ.cli_binary,
|
|
1586
1283
|
mcpServerKey: integ.mcp_server_key,
|
|
1587
1284
|
connectivityTest: integ.connectivity_test ?? null,
|
|
1588
|
-
inlineServerEntry: integ.inline_server_entry
|
|
1285
|
+
inlineServerEntry: integ.inline_server_entry,
|
|
1286
|
+
// ENG-8363: row age + quarantine status decide whether a missing
|
|
1287
|
+
// `.mcp.json` key is "not written yet" (transient) or "not wired" (down).
|
|
1288
|
+
createdAt: integ.created_at ?? null,
|
|
1289
|
+
expectedAbsent: integ.quarantined ?? false
|
|
1589
1290
|
};
|
|
1590
1291
|
let outcome;
|
|
1591
1292
|
try {
|
|
@@ -9208,8 +8909,8 @@ var restartBreaker = new RestartBreaker();
|
|
|
9208
8909
|
}
|
|
9209
8910
|
}
|
|
9210
8911
|
var reportedTrips = /* @__PURE__ */ new Map();
|
|
9211
|
-
var
|
|
9212
|
-
var
|
|
8912
|
+
var RESTART_BREAKER_PROVISIONING_MAX = readEnvNumber("AGT_RESTART_BREAKER_PROVISIONING_MAX", 5);
|
|
8913
|
+
var RESTART_BREAKER_PROVISIONING_WINDOW_MS = readEnvNumber(
|
|
9213
8914
|
"AGT_RESTART_BREAKER_PROVISIONING_WINDOW_MS",
|
|
9214
8915
|
18e5
|
|
9215
8916
|
);
|
|
@@ -9254,13 +8955,13 @@ function recordRestartForBreaker(codeName, reason) {
|
|
|
9254
8955
|
let integrationKey;
|
|
9255
8956
|
if (isProvisioningReloadReason(reason)) {
|
|
9256
8957
|
const attribution = agentState.provisioningAttribution.get(codeName);
|
|
9257
|
-
if (attribution && Date.now() - attribution.at <=
|
|
8958
|
+
if (attribution && Date.now() - attribution.at <= RESTART_BREAKER_PROVISIONING_WINDOW_MS) {
|
|
9258
8959
|
integrationKey = attribution.key;
|
|
9259
8960
|
}
|
|
9260
8961
|
}
|
|
9261
8962
|
const result = restartBreaker.record(codeName, reason, integrationKey);
|
|
9262
8963
|
if (!result.tripped || !result.trip) {
|
|
9263
|
-
if (isProvisioningReloadReason(reason) && result.maxProvisioningBucket >=
|
|
8964
|
+
if (isProvisioningReloadReason(reason) && result.maxProvisioningBucket >= RESTART_BREAKER_PROVISIONING_MAX) {
|
|
9264
8965
|
const culprit = result.maxProvisioningBucketKey ? ` [${result.maxProvisioningBucketKey}]` : "";
|
|
9265
8966
|
log(
|
|
9266
8967
|
`[restart-breaker] agent=${codeName} provisioning-reload churn at the trip bar: ${result.maxProvisioningBucket} restarts for one integration${culprit} in window (reason=${reason}); one more will auto-pause the agent; that integration is likely churning (ENG-7560, ENG-7812)`
|
|
@@ -9649,11 +9350,7 @@ function bindRemediationEnabled() {
|
|
|
9649
9350
|
return hostFlagStore().getBoolean("bind-remediation");
|
|
9650
9351
|
}
|
|
9651
9352
|
var BIND_REMEDIATION_JITTER_MS = 3e4;
|
|
9652
|
-
var BIND_REMEDIATION_FIRST_CONNECT_WINDOW_MS =
|
|
9653
|
-
"AGT_BIND_REMEDIATION_FIRST_CONNECT_WINDOW_MS",
|
|
9654
|
-
9e5
|
|
9655
|
-
// 15 min
|
|
9656
|
-
);
|
|
9353
|
+
var BIND_REMEDIATION_FIRST_CONNECT_WINDOW_MS = FIRST_CONNECT_WINDOW_MS;
|
|
9657
9354
|
var BIND_REMEDIATION_MAX_CONNECTIVITY_DEFERRALS = readEnvNumber(
|
|
9658
9355
|
"AGT_BIND_REMEDIATION_MAX_CONNECTIVITY_DEFERRALS",
|
|
9659
9356
|
3
|
|
@@ -9953,7 +9650,13 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
|
|
|
9953
9650
|
// ENG-8055: forward the reconstructed inline server entry (managed
|
|
9954
9651
|
// quarantined rows) so the MCP probe legs hit the real server instead of
|
|
9955
9652
|
// the missing `.mcp.json` key. Absent on normal (active) rows.
|
|
9956
|
-
inline_server_entry: i.inline_server_entry
|
|
9653
|
+
inline_server_entry: i.inline_server_entry,
|
|
9654
|
+
// ENG-8363: age + quarantine status, so an absent `.mcp.json` key is
|
|
9655
|
+
// classified rather than guessed. Forwarding both is what makes the
|
|
9656
|
+
// escalation to `down` reachable at all — an omitted `created_at` reads
|
|
9657
|
+
// as "still settling" and never escalates (fail-open by design).
|
|
9658
|
+
created_at: i.created_at ?? null,
|
|
9659
|
+
quarantined: i.quarantined ?? false
|
|
9957
9660
|
};
|
|
9958
9661
|
}),
|
|
9959
9662
|
{ probeDeps, intervalMs: intervalSec * 1e3 }
|
|
@@ -10250,7 +9953,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
10250
9953
|
var lastVersionCheckAt = 0;
|
|
10251
9954
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
10252
9955
|
var lastResponsivenessProbeAt = 0;
|
|
10253
|
-
var agtCliVersion = true ? "0.28.
|
|
9956
|
+
var agtCliVersion = true ? "0.28.501" : "dev";
|
|
10254
9957
|
function resolveBrewPath(execFileSync2) {
|
|
10255
9958
|
try {
|
|
10256
9959
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -11413,7 +11116,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
11413
11116
|
if (codeNames.length === 0) return;
|
|
11414
11117
|
void (async () => {
|
|
11415
11118
|
try {
|
|
11416
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
11119
|
+
const { collectDiagnostics } = await import("../persistent-session-HL3LNPAX.js");
|
|
11417
11120
|
await api.post("/host/heartbeat", {
|
|
11418
11121
|
host_id: hostId,
|
|
11419
11122
|
agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor)
|
|
@@ -11520,7 +11223,7 @@ async function pollCycle() {
|
|
|
11520
11223
|
}
|
|
11521
11224
|
try {
|
|
11522
11225
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
11523
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
11226
|
+
const { collectDiagnostics } = await import("../persistent-session-HL3LNPAX.js");
|
|
11524
11227
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
11525
11228
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor) : void 0;
|
|
11526
11229
|
let tailscaleHostname;
|
|
@@ -11635,7 +11338,7 @@ async function pollCycle() {
|
|
|
11635
11338
|
collectResponsivenessProbes,
|
|
11636
11339
|
collectPanelessActivityProbes,
|
|
11637
11340
|
getResponsivenessIntervalMs
|
|
11638
|
-
} = await import("../responsiveness-probe-
|
|
11341
|
+
} = await import("../responsiveness-probe-AZ44ONES.js");
|
|
11639
11342
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
11640
11343
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
11641
11344
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -11699,7 +11402,7 @@ async function pollCycle() {
|
|
|
11699
11402
|
collectResponsivenessProbes,
|
|
11700
11403
|
livePendingInboundOldestAgeSeconds,
|
|
11701
11404
|
parkPendingInbound
|
|
11702
|
-
} = await import("../responsiveness-probe-
|
|
11405
|
+
} = await import("../responsiveness-probe-AZ44ONES.js");
|
|
11703
11406
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
|
|
11704
11407
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
11705
11408
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -13314,7 +13017,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13314
13017
|
);
|
|
13315
13018
|
}
|
|
13316
13019
|
}
|
|
13317
|
-
probeSet = [...probeSet, ...qRows];
|
|
13020
|
+
probeSet = [...probeSet, ...markQuarantinedForProbe(qRows)];
|
|
13318
13021
|
log(
|
|
13319
13022
|
`Auto-resume: connectivity-probing ${qRows.length} quarantined integration(s) for '${agent.code_name}'`
|
|
13320
13023
|
);
|
|
@@ -14762,6 +14465,9 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
14762
14465
|
}
|
|
14763
14466
|
}
|
|
14764
14467
|
}
|
|
14468
|
+
if (restartTrigger === "day-rollover") {
|
|
14469
|
+
rotatePaneLogForDayRollover(codeName, log, agentTimezone);
|
|
14470
|
+
}
|
|
14765
14471
|
if (restartBreaker.isTripped(codeName)) {
|
|
14766
14472
|
const trip = restartBreaker.getTrip(codeName);
|
|
14767
14473
|
return {
|
|
@@ -15188,7 +14894,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
15188
14894
|
void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
|
|
15189
14895
|
void (async () => {
|
|
15190
14896
|
try {
|
|
15191
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
14897
|
+
const { collectDiagnostics } = await import("../persistent-session-HL3LNPAX.js");
|
|
15192
14898
|
await api.post("/host/heartbeat", {
|
|
15193
14899
|
host_id: hostId,
|
|
15194
14900
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
|
|
@@ -15238,7 +14944,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
15238
14944
|
}
|
|
15239
14945
|
try {
|
|
15240
14946
|
const hostId = await getHostId();
|
|
15241
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
14947
|
+
const { collectDiagnostics } = await import("../persistent-session-HL3LNPAX.js");
|
|
15242
14948
|
await api.post("/host/heartbeat", {
|
|
15243
14949
|
host_id: hostId,
|
|
15244
14950
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
|
|
@@ -15726,7 +15432,7 @@ async function processClaudePairSessions(agents) {
|
|
|
15726
15432
|
killPairSession,
|
|
15727
15433
|
pairTmuxSession,
|
|
15728
15434
|
finalizeClaudePairOnboarding
|
|
15729
|
-
} = await import("../claude-pair-runtime-
|
|
15435
|
+
} = await import("../claude-pair-runtime-U5ES3GOY.js");
|
|
15730
15436
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
15731
15437
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
15732
15438
|
const killed = await killPairSession(pairTmuxSession(pairId));
|