@integrity-labs/agt-cli 0.28.611 → 0.28.613
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 +5 -5
- package/dist/{chunk-6L5WLLVR.js → chunk-D7EV77TB.js} +4 -4
- package/dist/{chunk-DD6NRRYC.js → chunk-JRL7754L.js} +2 -2
- package/dist/{chunk-2ASO4HVN.js → chunk-ZQXXK7JC.js} +32 -2
- package/dist/chunk-ZQXXK7JC.js.map +1 -0
- package/dist/{claude-pair-runtime-SZM65MVE.js → claude-pair-runtime-3UQ63455.js} +2 -2
- package/dist/lib/manager-worker.js +13 -13
- package/dist/mcp/direct-chat-channel.js +38 -4
- package/dist/mcp/origami.js +31 -1
- package/dist/mcp/slack-channel.js +228 -16
- package/dist/mcp/telegram-channel.js +38 -4
- package/dist/{persistent-session-JWBUD6RZ.js → persistent-session-U7PE7ZDB.js} +3 -3
- package/dist/{responsiveness-probe-GJEX44DE.js → responsiveness-probe-K6DGNTXA.js} +3 -3
- package/dist/{session-auth-dead-LBRWZQPW.js → session-auth-dead-RPMWBVBH.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-2ASO4HVN.js.map +0 -1
- /package/dist/{chunk-6L5WLLVR.js.map → chunk-D7EV77TB.js.map} +0 -0
- /package/dist/{chunk-DD6NRRYC.js.map → chunk-JRL7754L.js.map} +0 -0
- /package/dist/{claude-pair-runtime-SZM65MVE.js.map → claude-pair-runtime-3UQ63455.js.map} +0 -0
- /package/dist/{persistent-session-JWBUD6RZ.js.map → persistent-session-U7PE7ZDB.js.map} +0 -0
- /package/dist/{responsiveness-probe-GJEX44DE.js.map → responsiveness-probe-K6DGNTXA.js.map} +0 -0
- /package/dist/{session-auth-dead-LBRWZQPW.js.map → session-auth-dead-RPMWBVBH.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-U7PE7ZDB.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-3UQ63455.js.map
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
safeWriteJsonAtomic,
|
|
54
54
|
setConfigHash,
|
|
55
55
|
tripClass
|
|
56
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-D7EV77TB.js";
|
|
57
57
|
import {
|
|
58
58
|
getProjectDir as getProjectDir2,
|
|
59
59
|
getReadyTasks,
|
|
@@ -109,7 +109,7 @@ import {
|
|
|
109
109
|
takeZombieDetection,
|
|
110
110
|
toOpencodeModel,
|
|
111
111
|
writeEgressAllowlist
|
|
112
|
-
} from "../chunk-
|
|
112
|
+
} from "../chunk-JRL7754L.js";
|
|
113
113
|
import {
|
|
114
114
|
ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
|
|
115
115
|
AnchorSessionClient,
|
|
@@ -186,7 +186,7 @@ import {
|
|
|
186
186
|
subagentActivityAgeSeconds,
|
|
187
187
|
sumTranscriptUsageInWindow,
|
|
188
188
|
transcriptActivityAgeSeconds
|
|
189
|
-
} from "../chunk-
|
|
189
|
+
} from "../chunk-ZQXXK7JC.js";
|
|
190
190
|
import {
|
|
191
191
|
reapOrphanChannelMcps
|
|
192
192
|
} from "../chunk-XWVM4KPK.js";
|
|
@@ -11287,7 +11287,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
|
|
|
11287
11287
|
var dayRolloverInboundHold = /* @__PURE__ */ new Map();
|
|
11288
11288
|
var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
|
|
11289
11289
|
async function channelInboundActivityAgeSecondsFor(codeName) {
|
|
11290
|
-
const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-
|
|
11290
|
+
const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-K6DGNTXA.js");
|
|
11291
11291
|
const newest = newestPendingInboundActivityMtimeMs(dirname9(paneLogPath(codeName)));
|
|
11292
11292
|
if (newest === null) return null;
|
|
11293
11293
|
return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
|
|
@@ -11926,7 +11926,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
11926
11926
|
var lastVersionCheckAt = 0;
|
|
11927
11927
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
11928
11928
|
var lastResponsivenessProbeAt = 0;
|
|
11929
|
-
var agtCliVersion = true ? "0.28.
|
|
11929
|
+
var agtCliVersion = true ? "0.28.613" : "dev";
|
|
11930
11930
|
function resolveBrewPath(execFileSync3) {
|
|
11931
11931
|
try {
|
|
11932
11932
|
const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -13233,7 +13233,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
13233
13233
|
if (codeNames.length === 0) return;
|
|
13234
13234
|
void (async () => {
|
|
13235
13235
|
try {
|
|
13236
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13236
|
+
const { collectDiagnostics } = await import("../persistent-session-U7PE7ZDB.js");
|
|
13237
13237
|
await api.post("/host/heartbeat", {
|
|
13238
13238
|
host_id: hostId,
|
|
13239
13239
|
agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor)
|
|
@@ -13341,7 +13341,7 @@ async function pollCycle() {
|
|
|
13341
13341
|
}
|
|
13342
13342
|
try {
|
|
13343
13343
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
13344
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13344
|
+
const { collectDiagnostics } = await import("../persistent-session-U7PE7ZDB.js");
|
|
13345
13345
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
13346
13346
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor) : void 0;
|
|
13347
13347
|
let tailscaleHostname;
|
|
@@ -13464,7 +13464,7 @@ async function pollCycle() {
|
|
|
13464
13464
|
collectPanelessActivityProbes,
|
|
13465
13465
|
getResponsivenessIntervalMs,
|
|
13466
13466
|
occupancyQualificationClassifications
|
|
13467
|
-
} = await import("../responsiveness-probe-
|
|
13467
|
+
} = await import("../responsiveness-probe-K6DGNTXA.js");
|
|
13468
13468
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
13469
13469
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
13470
13470
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -13570,7 +13570,7 @@ async function pollCycle() {
|
|
|
13570
13570
|
collectResponsivenessProbes,
|
|
13571
13571
|
livePendingInboundOldestAgeSeconds,
|
|
13572
13572
|
parkPendingInbound
|
|
13573
|
-
} = await import("../responsiveness-probe-
|
|
13573
|
+
} = await import("../responsiveness-probe-K6DGNTXA.js");
|
|
13574
13574
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
|
|
13575
13575
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
13576
13576
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -13726,7 +13726,7 @@ async function pollCycle() {
|
|
|
13726
13726
|
}
|
|
13727
13727
|
try {
|
|
13728
13728
|
const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
|
|
13729
|
-
const { probeSessionAuth } = await import("../session-auth-dead-
|
|
13729
|
+
const { probeSessionAuth } = await import("../session-auth-dead-RPMWBVBH.js");
|
|
13730
13730
|
const observations = [];
|
|
13731
13731
|
const pendingCacheCommits = [];
|
|
13732
13732
|
const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
|
|
@@ -17215,7 +17215,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
17215
17215
|
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}`));
|
|
17216
17216
|
void (async () => {
|
|
17217
17217
|
try {
|
|
17218
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
17218
|
+
const { collectDiagnostics } = await import("../persistent-session-U7PE7ZDB.js");
|
|
17219
17219
|
await api.post("/host/heartbeat", {
|
|
17220
17220
|
host_id: hostId,
|
|
17221
17221
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor)
|
|
@@ -17266,7 +17266,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
17266
17266
|
}
|
|
17267
17267
|
try {
|
|
17268
17268
|
const hostId = await getHostId();
|
|
17269
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
17269
|
+
const { collectDiagnostics } = await import("../persistent-session-U7PE7ZDB.js");
|
|
17270
17270
|
await api.post("/host/heartbeat", {
|
|
17271
17271
|
host_id: hostId,
|
|
17272
17272
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor)
|
|
@@ -17862,7 +17862,7 @@ async function processClaudePairSessions(agents) {
|
|
|
17862
17862
|
killPairSession,
|
|
17863
17863
|
pairTmuxSession,
|
|
17864
17864
|
finalizeClaudePairOnboarding
|
|
17865
|
-
} = await import("../claude-pair-runtime-
|
|
17865
|
+
} = await import("../claude-pair-runtime-3UQ63455.js");
|
|
17866
17866
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
17867
17867
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
17868
17868
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -30142,8 +30142,12 @@ function busyAckNoticeText() {
|
|
|
30142
30142
|
return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
|
|
30143
30143
|
}
|
|
30144
30144
|
var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
|
|
30145
|
-
function turnFailedNoticeText() {
|
|
30146
|
-
|
|
30145
|
+
function turnFailedNoticeText(failureClass) {
|
|
30146
|
+
const cause = failureClass === "overloaded" ? "the service I run on is overloaded right now \u2014 nothing to do with you or what you asked for" : failureClass === "server_error" ? "something upstream of me is failing right now \u2014 nothing to do with you or what you asked for" : null;
|
|
30147
|
+
if (cause === null) {
|
|
30148
|
+
return "\u26A0\uFE0F Something went wrong on my side and I couldn\u2019t finish that. Sorry \u2014 send it again when you get a chance and I\u2019ll pick it straight up.";
|
|
30149
|
+
}
|
|
30150
|
+
return `\u26A0\uFE0F I couldn\u2019t finish that \u2014 ${cause}. Sorry \u2014 send it again when you get a chance and I\u2019ll pick it straight up.`;
|
|
30147
30151
|
}
|
|
30148
30152
|
function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
|
|
30149
30153
|
if (!dir) return null;
|
|
@@ -33928,7 +33932,14 @@ var AGENT_DIRECTED_NOTICE_KINDS = [
|
|
|
33928
33932
|
// written for the machine. The user learns the same fact from the connect card
|
|
33929
33933
|
// in that conversation flipping to its connected state, so showing this row as
|
|
33930
33934
|
// well would say it twice, the second time in prose aimed at an agent.
|
|
33931
|
-
"integration_connected"
|
|
33935
|
+
"integration_connected",
|
|
33936
|
+
// ENG-8745. The offer RESULT receipt (not the offer card — that one is
|
|
33937
|
+
// written for the human and is how they decided). Its content opens "Your
|
|
33938
|
+
// offer was ACCEPTED", carries an `[offer_result]` machine tag, and closes by
|
|
33939
|
+
// instructing the agent what not to do ("do not re-raise it yourself"). The
|
|
33940
|
+
// human needs none of it: they are the person who tapped the button, and the
|
|
33941
|
+
// card swapped under their finger to say so.
|
|
33942
|
+
"offer_result"
|
|
33932
33943
|
];
|
|
33933
33944
|
var AGENT_DIRECTED_KIND_SET = new Set(AGENT_DIRECTED_NOTICE_KINDS);
|
|
33934
33945
|
|
|
@@ -34872,6 +34883,29 @@ registerTriggerSource(videoRenderTriggerAdapter);
|
|
|
34872
34883
|
|
|
34873
34884
|
// ../core/dist/feature-flags/registry.js
|
|
34874
34885
|
var FLAG_REGISTRY = [
|
|
34886
|
+
{
|
|
34887
|
+
key: "impersonation-enabled",
|
|
34888
|
+
description: "Kill switch for admin impersonation (ENG-9080), filed during the 18 Aug cross-tenant identity incident. ON (default) = impersonation works as today. OFF = both halves of the flow refuse: the mint route stops issuing redeem URLs, and \u2014 the half that actually matters \u2014 the REDEEM route refuses before it inserts the nonce or calls generateLink, so a URL that was already issued cannot still mint a session. Redeem tokens live REDEEM_TTL_SECONDS = 300, so gating only the mint route would leave five minutes in which new sessions keep appearing after an operator believes they have stopped it. Evaluated as a GLOBAL stage value, read UNCACHED on the redeem path (see readImpersonationEnabled): a kill switch that takes a cache TTL to bite is not a kill switch. Containment only \u2014 it does not fix the underlying defect that redeem writes the target's cookies into whatever browser context opens the URL (ENG-8923 / ENG-9020).",
|
|
34889
|
+
flagType: "boolean",
|
|
34890
|
+
// Declared safe value is `true` = impersonation available, i.e. today's
|
|
34891
|
+
// behaviour, so merging this changes nothing until an operator flips it.
|
|
34892
|
+
//
|
|
34893
|
+
// NOTE the asymmetry with the other gates in this file, and it is
|
|
34894
|
+
// deliberate: their safe direction is "off / don't do the new thing"
|
|
34895
|
+
// because they gate NEW behaviour. This one gates EXISTING behaviour, so
|
|
34896
|
+
// the no-op default is `true`. The READ direction is where the safety lives
|
|
34897
|
+
// instead — an unreachable flag store resolves to DISABLED, not to this
|
|
34898
|
+
// default. See readImpersonationEnabled for that argument.
|
|
34899
|
+
defaultValue: true,
|
|
34900
|
+
// Enforcement gate (ADR-0022 section 4): re-enabling impersonation is
|
|
34901
|
+
// "relaxing a security control", exactly the mutation that must not happen
|
|
34902
|
+
// on a single unguarded write. The emergency direction (turning it OFF)
|
|
34903
|
+
// costs one confirmation, which is the right price.
|
|
34904
|
+
sensitive: true
|
|
34905
|
+
// Deliberately NOT public. A browser has no business evaluating whether
|
|
34906
|
+
// impersonation is available; both consumers are server-side route
|
|
34907
|
+
// handlers.
|
|
34908
|
+
},
|
|
34875
34909
|
{
|
|
34876
34910
|
key: "at-rest-v2-writes",
|
|
34877
34911
|
description: "ADR-0061 A2\u2192arm cutover (ENG-8944, parent ENG-8764). Gates whether the at-rest writer (encryptSecret/encryptAtRest) emits the v2 AAD-bound envelope when a DecryptContext is supplied; OFF (default) = the legacy v1 envelope (pre-ADR-0061 behaviour). Resolved FLEET-WIDE and process-global via setV2WritesArmed at the API + webapp + cron bootstrap seams, so ONLY the stage value applies \u2014 per-org overrides do not (the crypto writer carries no request/org context). Ships dark. Flip on only once every reader parses v2 (fleet-wide since release A1) AND the re-seal engine can re-seal v2 blobs before any AUTH_ENCRYPTION_KEY rotation (slice 5b). Reversible without redeploy: clearing it returns the writer to v1 within the flag cache TTL; already-written v2 blobs stay readable.",
|
|
@@ -38105,7 +38139,7 @@ function armTurnFailureWatch(args) {
|
|
|
38105
38139
|
return;
|
|
38106
38140
|
}
|
|
38107
38141
|
lastTurnFailedNoticeAt.set(args.sessionId, now);
|
|
38108
|
-
const ok = await postDirectChatNoticeMessage(args.sessionId, turnFailedNoticeText());
|
|
38142
|
+
const ok = await postDirectChatNoticeMessage(args.sessionId, turnFailedNoticeText(failure.failureClass));
|
|
38109
38143
|
if (!ok) {
|
|
38110
38144
|
lastTurnFailedNoticeAt.delete(args.sessionId);
|
|
38111
38145
|
process.stderr.write(
|
package/dist/mcp/origami.js
CHANGED
|
@@ -40266,7 +40266,14 @@ var AGENT_DIRECTED_NOTICE_KINDS = [
|
|
|
40266
40266
|
// written for the machine. The user learns the same fact from the connect card
|
|
40267
40267
|
// in that conversation flipping to its connected state, so showing this row as
|
|
40268
40268
|
// well would say it twice, the second time in prose aimed at an agent.
|
|
40269
|
-
"integration_connected"
|
|
40269
|
+
"integration_connected",
|
|
40270
|
+
// ENG-8745. The offer RESULT receipt (not the offer card — that one is
|
|
40271
|
+
// written for the human and is how they decided). Its content opens "Your
|
|
40272
|
+
// offer was ACCEPTED", carries an `[offer_result]` machine tag, and closes by
|
|
40273
|
+
// instructing the agent what not to do ("do not re-raise it yourself"). The
|
|
40274
|
+
// human needs none of it: they are the person who tapped the button, and the
|
|
40275
|
+
// card swapped under their finger to say so.
|
|
40276
|
+
"offer_result"
|
|
40270
40277
|
];
|
|
40271
40278
|
var AGENT_DIRECTED_KIND_SET = new Set(AGENT_DIRECTED_NOTICE_KINDS);
|
|
40272
40279
|
|
|
@@ -40763,6 +40770,29 @@ registerTriggerSource(videoRenderTriggerAdapter);
|
|
|
40763
40770
|
|
|
40764
40771
|
// ../core/dist/feature-flags/registry.js
|
|
40765
40772
|
var FLAG_REGISTRY = [
|
|
40773
|
+
{
|
|
40774
|
+
key: "impersonation-enabled",
|
|
40775
|
+
description: "Kill switch for admin impersonation (ENG-9080), filed during the 18 Aug cross-tenant identity incident. ON (default) = impersonation works as today. OFF = both halves of the flow refuse: the mint route stops issuing redeem URLs, and \u2014 the half that actually matters \u2014 the REDEEM route refuses before it inserts the nonce or calls generateLink, so a URL that was already issued cannot still mint a session. Redeem tokens live REDEEM_TTL_SECONDS = 300, so gating only the mint route would leave five minutes in which new sessions keep appearing after an operator believes they have stopped it. Evaluated as a GLOBAL stage value, read UNCACHED on the redeem path (see readImpersonationEnabled): a kill switch that takes a cache TTL to bite is not a kill switch. Containment only \u2014 it does not fix the underlying defect that redeem writes the target's cookies into whatever browser context opens the URL (ENG-8923 / ENG-9020).",
|
|
40776
|
+
flagType: "boolean",
|
|
40777
|
+
// Declared safe value is `true` = impersonation available, i.e. today's
|
|
40778
|
+
// behaviour, so merging this changes nothing until an operator flips it.
|
|
40779
|
+
//
|
|
40780
|
+
// NOTE the asymmetry with the other gates in this file, and it is
|
|
40781
|
+
// deliberate: their safe direction is "off / don't do the new thing"
|
|
40782
|
+
// because they gate NEW behaviour. This one gates EXISTING behaviour, so
|
|
40783
|
+
// the no-op default is `true`. The READ direction is where the safety lives
|
|
40784
|
+
// instead — an unreachable flag store resolves to DISABLED, not to this
|
|
40785
|
+
// default. See readImpersonationEnabled for that argument.
|
|
40786
|
+
defaultValue: true,
|
|
40787
|
+
// Enforcement gate (ADR-0022 section 4): re-enabling impersonation is
|
|
40788
|
+
// "relaxing a security control", exactly the mutation that must not happen
|
|
40789
|
+
// on a single unguarded write. The emergency direction (turning it OFF)
|
|
40790
|
+
// costs one confirmation, which is the right price.
|
|
40791
|
+
sensitive: true
|
|
40792
|
+
// Deliberately NOT public. A browser has no business evaluating whether
|
|
40793
|
+
// impersonation is available; both consumers are server-side route
|
|
40794
|
+
// handlers.
|
|
40795
|
+
},
|
|
40766
40796
|
{
|
|
40767
40797
|
key: "at-rest-v2-writes",
|
|
40768
40798
|
description: "ADR-0061 A2\u2192arm cutover (ENG-8944, parent ENG-8764). Gates whether the at-rest writer (encryptSecret/encryptAtRest) emits the v2 AAD-bound envelope when a DecryptContext is supplied; OFF (default) = the legacy v1 envelope (pre-ADR-0061 behaviour). Resolved FLEET-WIDE and process-global via setV2WritesArmed at the API + webapp + cron bootstrap seams, so ONLY the stage value applies \u2014 per-org overrides do not (the crypto writer carries no request/org context). Ships dark. Flip on only once every reader parses v2 (fleet-wide since release A1) AND the re-seal engine can re-seal v2 blobs before any AUTH_ENCRYPTION_KEY rotation (slice 5b). Reversible without redeploy: clearing it returns the writer to v1 within the flag cache TTL; already-written v2 blobs stay readable.",
|
|
@@ -34317,7 +34317,14 @@ var AGENT_DIRECTED_NOTICE_KINDS = [
|
|
|
34317
34317
|
// written for the machine. The user learns the same fact from the connect card
|
|
34318
34318
|
// in that conversation flipping to its connected state, so showing this row as
|
|
34319
34319
|
// well would say it twice, the second time in prose aimed at an agent.
|
|
34320
|
-
"integration_connected"
|
|
34320
|
+
"integration_connected",
|
|
34321
|
+
// ENG-8745. The offer RESULT receipt (not the offer card — that one is
|
|
34322
|
+
// written for the human and is how they decided). Its content opens "Your
|
|
34323
|
+
// offer was ACCEPTED", carries an `[offer_result]` machine tag, and closes by
|
|
34324
|
+
// instructing the agent what not to do ("do not re-raise it yourself"). The
|
|
34325
|
+
// human needs none of it: they are the person who tapped the button, and the
|
|
34326
|
+
// card swapped under their finger to say so.
|
|
34327
|
+
"offer_result"
|
|
34321
34328
|
];
|
|
34322
34329
|
var AGENT_DIRECTED_KIND_SET = new Set(AGENT_DIRECTED_NOTICE_KINDS);
|
|
34323
34330
|
|
|
@@ -35208,6 +35215,29 @@ registerTriggerSource(videoRenderTriggerAdapter);
|
|
|
35208
35215
|
|
|
35209
35216
|
// ../core/dist/feature-flags/registry.js
|
|
35210
35217
|
var FLAG_REGISTRY = [
|
|
35218
|
+
{
|
|
35219
|
+
key: "impersonation-enabled",
|
|
35220
|
+
description: "Kill switch for admin impersonation (ENG-9080), filed during the 18 Aug cross-tenant identity incident. ON (default) = impersonation works as today. OFF = both halves of the flow refuse: the mint route stops issuing redeem URLs, and \u2014 the half that actually matters \u2014 the REDEEM route refuses before it inserts the nonce or calls generateLink, so a URL that was already issued cannot still mint a session. Redeem tokens live REDEEM_TTL_SECONDS = 300, so gating only the mint route would leave five minutes in which new sessions keep appearing after an operator believes they have stopped it. Evaluated as a GLOBAL stage value, read UNCACHED on the redeem path (see readImpersonationEnabled): a kill switch that takes a cache TTL to bite is not a kill switch. Containment only \u2014 it does not fix the underlying defect that redeem writes the target's cookies into whatever browser context opens the URL (ENG-8923 / ENG-9020).",
|
|
35221
|
+
flagType: "boolean",
|
|
35222
|
+
// Declared safe value is `true` = impersonation available, i.e. today's
|
|
35223
|
+
// behaviour, so merging this changes nothing until an operator flips it.
|
|
35224
|
+
//
|
|
35225
|
+
// NOTE the asymmetry with the other gates in this file, and it is
|
|
35226
|
+
// deliberate: their safe direction is "off / don't do the new thing"
|
|
35227
|
+
// because they gate NEW behaviour. This one gates EXISTING behaviour, so
|
|
35228
|
+
// the no-op default is `true`. The READ direction is where the safety lives
|
|
35229
|
+
// instead — an unreachable flag store resolves to DISABLED, not to this
|
|
35230
|
+
// default. See readImpersonationEnabled for that argument.
|
|
35231
|
+
defaultValue: true,
|
|
35232
|
+
// Enforcement gate (ADR-0022 section 4): re-enabling impersonation is
|
|
35233
|
+
// "relaxing a security control", exactly the mutation that must not happen
|
|
35234
|
+
// on a single unguarded write. The emergency direction (turning it OFF)
|
|
35235
|
+
// costs one confirmation, which is the right price.
|
|
35236
|
+
sensitive: true
|
|
35237
|
+
// Deliberately NOT public. A browser has no business evaluating whether
|
|
35238
|
+
// impersonation is available; both consumers are server-side route
|
|
35239
|
+
// handlers.
|
|
35240
|
+
},
|
|
35211
35241
|
{
|
|
35212
35242
|
key: "at-rest-v2-writes",
|
|
35213
35243
|
description: "ADR-0061 A2\u2192arm cutover (ENG-8944, parent ENG-8764). Gates whether the at-rest writer (encryptSecret/encryptAtRest) emits the v2 AAD-bound envelope when a DecryptContext is supplied; OFF (default) = the legacy v1 envelope (pre-ADR-0061 behaviour). Resolved FLEET-WIDE and process-global via setV2WritesArmed at the API + webapp + cron bootstrap seams, so ONLY the stage value applies \u2014 per-org overrides do not (the crypto writer carries no request/org context). Ships dark. Flip on only once every reader parses v2 (fleet-wide since release A1) AND the re-seal engine can re-seal v2 blobs before any AUTH_ENCRYPTION_KEY rotation (slice 5b). Reversible without redeploy: clearing it returns the writer to v1 within the flag cache TTL; already-written v2 blobs stay readable.",
|
|
@@ -36974,8 +37004,12 @@ function giveUpNoticeText(reason = null) {
|
|
|
36974
37004
|
}
|
|
36975
37005
|
return "\u26A0\uFE0F I couldn't read your last message \u2014 please resend it.";
|
|
36976
37006
|
}
|
|
36977
|
-
function turnFailedNoticeText() {
|
|
36978
|
-
|
|
37007
|
+
function turnFailedNoticeText(failureClass) {
|
|
37008
|
+
const cause = failureClass === "overloaded" ? "the service I run on is overloaded right now \u2014 nothing to do with you or what you asked for" : failureClass === "server_error" ? "something upstream of me is failing right now \u2014 nothing to do with you or what you asked for" : null;
|
|
37009
|
+
if (cause === null) {
|
|
37010
|
+
return "\u26A0\uFE0F Something went wrong on my side and I couldn\u2019t finish that. Sorry \u2014 send it again when you get a chance and I\u2019ll pick it straight up.";
|
|
37011
|
+
}
|
|
37012
|
+
return `\u26A0\uFE0F I couldn\u2019t finish that \u2014 ${cause}. Sorry \u2014 send it again when you get a chance and I\u2019ll pick it straight up.`;
|
|
36979
37013
|
}
|
|
36980
37014
|
function turnRetryingNoticeText() {
|
|
36981
37015
|
return "\u23F3 Still working on this \u2014 things are running slower than usual on my side. I\u2019ll reply as soon as I have it.";
|
|
@@ -39367,6 +39401,91 @@ async function runOrRetry(fn, opts) {
|
|
|
39367
39401
|
}
|
|
39368
39402
|
}
|
|
39369
39403
|
|
|
39404
|
+
// src/slack-socket-reconnect.ts
|
|
39405
|
+
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
39406
|
+
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
39407
|
+
var SOCKET_OPEN_TIMEOUT_MS = 3e4;
|
|
39408
|
+
var CONNECT_FETCH_TIMEOUT_MS = 15e3;
|
|
39409
|
+
var SOCKET_DOWN_ALERT_AFTER_MS = 3 * 6e4;
|
|
39410
|
+
function reconnectDelayMs(attempt, opts = {}) {
|
|
39411
|
+
const base = opts.baseMs ?? RECONNECT_BASE_DELAY_MS;
|
|
39412
|
+
const max = opts.maxMs ?? RECONNECT_MAX_DELAY_MS;
|
|
39413
|
+
const random = opts.random ?? Math.random;
|
|
39414
|
+
const safeAttempt = Math.max(1, Math.min(Math.floor(attempt), 30));
|
|
39415
|
+
const ceiling = Math.min(max, base * 2 ** (safeAttempt - 1));
|
|
39416
|
+
const floor = Math.min(base, ceiling);
|
|
39417
|
+
return Math.round(floor + random() * (ceiling - floor));
|
|
39418
|
+
}
|
|
39419
|
+
function redactSocketUrl(text) {
|
|
39420
|
+
return text.replace(
|
|
39421
|
+
/([?&])(ticket|token|app_id_key)=[^&\s"']*/gi,
|
|
39422
|
+
(_m, sep2, key2) => `${sep2}${key2}=<redacted>`
|
|
39423
|
+
);
|
|
39424
|
+
}
|
|
39425
|
+
function describeWebSocketError(err) {
|
|
39426
|
+
const parts = [];
|
|
39427
|
+
const e = err ?? {};
|
|
39428
|
+
if (typeof e.message === "string" && e.message) parts.push(e.message);
|
|
39429
|
+
const cause = e.error;
|
|
39430
|
+
if (cause && typeof cause === "object") {
|
|
39431
|
+
if (typeof cause.message === "string" && cause.message && !parts.includes(cause.message)) {
|
|
39432
|
+
parts.push(cause.message);
|
|
39433
|
+
}
|
|
39434
|
+
if (typeof cause.code === "string" && cause.code) parts.push(`errno=${cause.code}`);
|
|
39435
|
+
}
|
|
39436
|
+
if (typeof e.code === "number") parts.push(`code=${e.code}`);
|
|
39437
|
+
if (typeof e.reason === "string" && e.reason) parts.push(`reason=${e.reason}`);
|
|
39438
|
+
if (typeof e.wasClean === "boolean") parts.push(`wasClean=${e.wasClean}`);
|
|
39439
|
+
if (parts.length === 0) {
|
|
39440
|
+
const type = typeof e.type === "string" && e.type ? e.type : typeof err;
|
|
39441
|
+
const ctor = err && typeof err === "object" ? err.constructor?.name ?? "Object" : typeof err;
|
|
39442
|
+
return redactSocketUrl(`no detail available (${ctor}, type=${type})`);
|
|
39443
|
+
}
|
|
39444
|
+
return redactSocketUrl(parts.join(" "));
|
|
39445
|
+
}
|
|
39446
|
+
function isWebSocketOpen(ws) {
|
|
39447
|
+
return ws?.readyState === 1;
|
|
39448
|
+
}
|
|
39449
|
+
function createReconnectScheduler(deps) {
|
|
39450
|
+
const now = deps.now ?? Date.now;
|
|
39451
|
+
const setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
39452
|
+
let pending = false;
|
|
39453
|
+
let attempt = 0;
|
|
39454
|
+
let downSince = now();
|
|
39455
|
+
return {
|
|
39456
|
+
schedule(reason) {
|
|
39457
|
+
if (deps.shouldStop() || pending) return;
|
|
39458
|
+
pending = true;
|
|
39459
|
+
if (downSince === null) downSince = now();
|
|
39460
|
+
attempt += 1;
|
|
39461
|
+
const opts = {};
|
|
39462
|
+
if (deps.baseMs !== void 0) opts.baseMs = deps.baseMs;
|
|
39463
|
+
if (deps.maxMs !== void 0) opts.maxMs = deps.maxMs;
|
|
39464
|
+
if (deps.random !== void 0) opts.random = deps.random;
|
|
39465
|
+
const delay = reconnectDelayMs(attempt, opts);
|
|
39466
|
+
deps.log(
|
|
39467
|
+
`slack-channel: Socket Mode reconnecting in ${delay}ms (attempt ${attempt}, ${reason})`
|
|
39468
|
+
);
|
|
39469
|
+
const timer = setTimer(() => {
|
|
39470
|
+
pending = false;
|
|
39471
|
+
if (deps.shouldStop()) return;
|
|
39472
|
+
deps.connect();
|
|
39473
|
+
}, delay);
|
|
39474
|
+
timer.unref?.();
|
|
39475
|
+
},
|
|
39476
|
+
noteOpen() {
|
|
39477
|
+
attempt = 0;
|
|
39478
|
+
downSince = null;
|
|
39479
|
+
},
|
|
39480
|
+
downForMs(connected) {
|
|
39481
|
+
if (connected) return null;
|
|
39482
|
+
return downSince === null ? null : now() - downSince;
|
|
39483
|
+
},
|
|
39484
|
+
attempts: () => attempt,
|
|
39485
|
+
isPending: () => pending
|
|
39486
|
+
};
|
|
39487
|
+
}
|
|
39488
|
+
|
|
39370
39489
|
// src/turn-initiator-marker.ts
|
|
39371
39490
|
import { writeFileSync as writeFileSync7, readFileSync as readFileSync9, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
|
|
39372
39491
|
import { dirname as dirname4, join as join12 } from "path";
|
|
@@ -42678,7 +42797,7 @@ function armSlackTurnFailureWatch(args) {
|
|
|
42678
42797
|
const posted = await postSlackTurnNotice({
|
|
42679
42798
|
channel,
|
|
42680
42799
|
threadTs: args.threadTs,
|
|
42681
|
-
text: turnFailedNoticeText()
|
|
42800
|
+
text: turnFailedNoticeText(failure.failureClass)
|
|
42682
42801
|
});
|
|
42683
42802
|
if (!posted.ok) {
|
|
42684
42803
|
lastSlackTurnFailedNoticeAt.delete(conversationKey);
|
|
@@ -43136,10 +43255,12 @@ function formatLastActivity() {
|
|
|
43136
43255
|
return `${kindLabel} ${ago}`;
|
|
43137
43256
|
}
|
|
43138
43257
|
function buildAgentStatusReply(codeName) {
|
|
43139
|
-
const connected =
|
|
43258
|
+
const connected = isSocketConnected();
|
|
43140
43259
|
const dot = connected ? "\u{1F7E2}" : "\u{1F534}";
|
|
43141
43260
|
const state = connected ? "online" : "offline";
|
|
43142
|
-
const
|
|
43261
|
+
const downMs = socketDownForMs();
|
|
43262
|
+
const downSuffix = !connected && downMs !== null ? ` for ${Math.floor(downMs / 6e4)}m \u2014 inbound is dead` : "";
|
|
43263
|
+
const connectivityLine = `${dot} *${state}* \u2014 Socket Mode ${connected ? "connected" : `disconnected${downSuffix}`}. Last activity: ${formatLastActivity()}.`;
|
|
43143
43264
|
const sessionState = readAgentSessionState(SLACK_AGENT_DIR);
|
|
43144
43265
|
return buildAgentConfigReport({ codeName, connectivityLine, state: sessionState });
|
|
43145
43266
|
}
|
|
@@ -43150,9 +43271,16 @@ function statusRuntimeConfig() {
|
|
|
43150
43271
|
}
|
|
43151
43272
|
async function computeSlackOwnChannelLiveStatus() {
|
|
43152
43273
|
const checked_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
43153
|
-
const connected =
|
|
43274
|
+
const connected = isSocketConnected();
|
|
43154
43275
|
if (!connected) {
|
|
43155
|
-
|
|
43276
|
+
const downMs = socketDownForMs();
|
|
43277
|
+
const forSuffix = downMs === null ? "" : ` for ${Math.floor(downMs / 1e3)}s`;
|
|
43278
|
+
const sustained = downMs !== null && downMs >= SOCKET_DOWN_ALERT_AFTER_MS;
|
|
43279
|
+
return {
|
|
43280
|
+
level: "down",
|
|
43281
|
+
checked_at,
|
|
43282
|
+
reason: `Socket Mode disconnected${forSuffix}${sustained ? " - inbound Slack is DEAD (outbound still works, so this is invisible from outside)" : ""}`
|
|
43283
|
+
};
|
|
43156
43284
|
}
|
|
43157
43285
|
const { id, authFailed } = await getBotUserId();
|
|
43158
43286
|
if (id) {
|
|
@@ -43679,7 +43807,7 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
43679
43807
|
);
|
|
43680
43808
|
return;
|
|
43681
43809
|
}
|
|
43682
|
-
const connected =
|
|
43810
|
+
const connected = isSocketConnected();
|
|
43683
43811
|
const dot = connected ? "\u{1F7E2}" : "\u{1F534}";
|
|
43684
43812
|
const state = connected ? "online" : "offline";
|
|
43685
43813
|
const pong = `${dot} pong - \`${codeName}\` is ${state} (manager \u2022 ${(/* @__PURE__ */ new Date()).toISOString()}). Last activity: ${formatLastActivity()}.`;
|
|
@@ -45751,6 +45879,47 @@ async function resolveUserEmail(userId) {
|
|
|
45751
45879
|
}
|
|
45752
45880
|
var currentWs = null;
|
|
45753
45881
|
var isShuttingDown = false;
|
|
45882
|
+
var clearOpenWatchdog = null;
|
|
45883
|
+
function clearOpenWatchdogFor(ws) {
|
|
45884
|
+
if (currentWs !== ws) return;
|
|
45885
|
+
clearOpenWatchdog?.();
|
|
45886
|
+
clearOpenWatchdog = null;
|
|
45887
|
+
}
|
|
45888
|
+
var reconnectScheduler = createReconnectScheduler({
|
|
45889
|
+
connect: () => connectSocketModeSafely(),
|
|
45890
|
+
log: (line) => process.stderr.write(`${line}
|
|
45891
|
+
`),
|
|
45892
|
+
shouldStop: () => isShuttingDown
|
|
45893
|
+
});
|
|
45894
|
+
function isSocketConnected() {
|
|
45895
|
+
return !isShuttingDown && isWebSocketOpen(currentWs);
|
|
45896
|
+
}
|
|
45897
|
+
function scheduleReconnect(reason) {
|
|
45898
|
+
reconnectScheduler.schedule(reason);
|
|
45899
|
+
}
|
|
45900
|
+
function armOpenWatchdog(ws) {
|
|
45901
|
+
clearOpenWatchdog?.();
|
|
45902
|
+
const timer = setTimeout(() => {
|
|
45903
|
+
if (currentWs !== ws) return;
|
|
45904
|
+
if (isShuttingDown || isWebSocketOpen(ws)) return;
|
|
45905
|
+
process.stderr.write(
|
|
45906
|
+
`slack-channel: Socket Mode did not open within ${SOCKET_OPEN_TIMEOUT_MS}ms - abandoning it
|
|
45907
|
+
`
|
|
45908
|
+
);
|
|
45909
|
+
currentWs = null;
|
|
45910
|
+
clearOpenWatchdog = null;
|
|
45911
|
+
try {
|
|
45912
|
+
ws.close();
|
|
45913
|
+
} catch {
|
|
45914
|
+
}
|
|
45915
|
+
scheduleReconnect("open timeout");
|
|
45916
|
+
}, SOCKET_OPEN_TIMEOUT_MS);
|
|
45917
|
+
timer.unref?.();
|
|
45918
|
+
clearOpenWatchdog = () => clearTimeout(timer);
|
|
45919
|
+
}
|
|
45920
|
+
function socketDownForMs() {
|
|
45921
|
+
return reconnectScheduler.downForMs(isSocketConnected());
|
|
45922
|
+
}
|
|
45754
45923
|
var acquiredLockPath = null;
|
|
45755
45924
|
var stopLockHeartbeat = null;
|
|
45756
45925
|
async function seedMutedChannelsFromApi() {
|
|
@@ -45801,14 +45970,27 @@ async function connectSocketMode() {
|
|
|
45801
45970
|
if (isShuttingDown) return;
|
|
45802
45971
|
const res = await fetch("https://slack.com/api/apps.connections.open", {
|
|
45803
45972
|
method: "POST",
|
|
45804
|
-
headers: { Authorization: `Bearer ${APP_TOKEN}` }
|
|
45973
|
+
headers: { Authorization: `Bearer ${APP_TOKEN}` },
|
|
45974
|
+
// CodeRabbit, #4655. Without a deadline a hanging fetch is the one failure
|
|
45975
|
+
// this whole change does not cover: it never rejects, so the retry chain is
|
|
45976
|
+
// never entered, and no socket exists yet so the open watchdog does not
|
|
45977
|
+
// apply either. The process ends up with nothing pending and no attempt in
|
|
45978
|
+
// flight — the incident's shape, reached a different way. A timeout turns
|
|
45979
|
+
// the hang into a rejection, which the retry chain already handles.
|
|
45980
|
+
signal: AbortSignal.timeout(CONNECT_FETCH_TIMEOUT_MS)
|
|
45981
|
+
// CodeRabbit, #4655. Without a deadline a hanging fetch is the one failure
|
|
45982
|
+
// this whole change does not cover: it never rejects, so the retry chain is
|
|
45983
|
+
// never entered, and no socket exists yet so the open watchdog does not
|
|
45984
|
+
// apply either. The process ends up with nothing pending and no attempt in
|
|
45985
|
+
// flight — the incident's shape, reached a different way. A timeout turns
|
|
45986
|
+
// the hang into a rejection, which the retry chain already handles.
|
|
45805
45987
|
});
|
|
45806
45988
|
const data = await res.json();
|
|
45807
45989
|
if (isShuttingDown) return;
|
|
45808
45990
|
if (!data.ok || !data.url) {
|
|
45809
45991
|
process.stderr.write(`slack-channel: Socket Mode connection failed: ${data.error}
|
|
45810
45992
|
`);
|
|
45811
|
-
|
|
45993
|
+
scheduleReconnect(`apps.connections.open ${data.error ?? "failed"}`);
|
|
45812
45994
|
return;
|
|
45813
45995
|
}
|
|
45814
45996
|
const ws = new WebSocket(data.url);
|
|
@@ -45820,7 +46002,17 @@ async function connectSocketMode() {
|
|
|
45820
46002
|
return;
|
|
45821
46003
|
}
|
|
45822
46004
|
currentWs = ws;
|
|
46005
|
+
armOpenWatchdog(ws);
|
|
45823
46006
|
ws.onopen = () => {
|
|
46007
|
+
if (currentWs !== ws) {
|
|
46008
|
+
try {
|
|
46009
|
+
ws.close();
|
|
46010
|
+
} catch {
|
|
46011
|
+
}
|
|
46012
|
+
return;
|
|
46013
|
+
}
|
|
46014
|
+
clearOpenWatchdogFor(ws);
|
|
46015
|
+
reconnectScheduler.noteOpen();
|
|
45824
46016
|
process.stderr.write("slack-channel: Socket Mode connected\n");
|
|
45825
46017
|
recordActivity("connect");
|
|
45826
46018
|
void setBotStatus(":large_green_circle:", "Active");
|
|
@@ -46466,15 +46658,35 @@ async function connectSocketMode() {
|
|
|
46466
46658
|
`);
|
|
46467
46659
|
}
|
|
46468
46660
|
};
|
|
46469
|
-
ws.onclose = () => {
|
|
46470
|
-
if (currentWs
|
|
46661
|
+
ws.onclose = (evt) => {
|
|
46662
|
+
if (currentWs !== ws) return;
|
|
46663
|
+
clearOpenWatchdogFor(ws);
|
|
46664
|
+
currentWs = null;
|
|
46471
46665
|
if (isShuttingDown) return;
|
|
46472
|
-
process.stderr.write(
|
|
46473
|
-
|
|
46666
|
+
process.stderr.write(
|
|
46667
|
+
`slack-channel: Socket Mode disconnected (${describeWebSocketError(evt)})
|
|
46668
|
+
`
|
|
46669
|
+
);
|
|
46670
|
+
scheduleReconnect("close");
|
|
46474
46671
|
};
|
|
46475
46672
|
ws.onerror = (err) => {
|
|
46476
|
-
process.stderr.write(`slack-channel: WebSocket error: ${err}
|
|
46673
|
+
process.stderr.write(`slack-channel: WebSocket error: ${describeWebSocketError(err)}
|
|
46477
46674
|
`);
|
|
46675
|
+
if (currentWs !== ws) {
|
|
46676
|
+
try {
|
|
46677
|
+
ws.close();
|
|
46678
|
+
} catch {
|
|
46679
|
+
}
|
|
46680
|
+
return;
|
|
46681
|
+
}
|
|
46682
|
+
clearOpenWatchdogFor(ws);
|
|
46683
|
+
currentWs = null;
|
|
46684
|
+
try {
|
|
46685
|
+
ws.close();
|
|
46686
|
+
} catch {
|
|
46687
|
+
}
|
|
46688
|
+
if (isShuttingDown) return;
|
|
46689
|
+
scheduleReconnect("error");
|
|
46478
46690
|
};
|
|
46479
46691
|
}
|
|
46480
46692
|
function shutdown(reason, exitCode = 0) {
|