@integrity-labs/agt-cli 0.28.582 → 0.28.584
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-XYWEC7PX.js → chunk-XHC5QJCK.js} +3 -3
- package/dist/{chunk-RDBPQZVG.js → chunk-XRG4XYAV.js} +1 -1
- package/dist/chunk-XRG4XYAV.js.map +1 -0
- package/dist/{claude-pair-runtime-MKWK27KO.js → claude-pair-runtime-WLMXUNII.js} +2 -2
- package/dist/lib/manager-worker.js +10 -10
- package/dist/mcp/slack-channel.js +172 -16
- package/dist/mcp/telegram-channel.js +163 -10
- package/dist/{persistent-session-MHBLUAOV.js → persistent-session-R7ACVUYP.js} +2 -2
- package/dist/{responsiveness-probe-XGMKVWSH.js → responsiveness-probe-RDBVTQD7.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-RDBPQZVG.js.map +0 -1
- /package/dist/{chunk-XYWEC7PX.js.map → chunk-XHC5QJCK.js.map} +0 -0
- /package/dist/{claude-pair-runtime-MKWK27KO.js.map → claude-pair-runtime-WLMXUNII.js.map} +0 -0
- /package/dist/{persistent-session-MHBLUAOV.js.map → persistent-session-R7ACVUYP.js.map} +0 -0
- /package/dist/{responsiveness-probe-XGMKVWSH.js.map → responsiveness-probe-RDBVTQD7.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-R7ACVUYP.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-WLMXUNII.js.map
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
safeWriteJsonAtomic,
|
|
54
54
|
setConfigHash,
|
|
55
55
|
tripClass
|
|
56
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-XHC5QJCK.js";
|
|
57
57
|
import {
|
|
58
58
|
getProjectDir as getProjectDir2,
|
|
59
59
|
getReadyTasks,
|
|
@@ -183,7 +183,7 @@ import {
|
|
|
183
183
|
toOpencodeModel,
|
|
184
184
|
transcriptActivityAgeSeconds,
|
|
185
185
|
writeEgressAllowlist
|
|
186
|
-
} from "../chunk-
|
|
186
|
+
} from "../chunk-XRG4XYAV.js";
|
|
187
187
|
import {
|
|
188
188
|
reapOrphanChannelMcps
|
|
189
189
|
} from "../chunk-XWVM4KPK.js";
|
|
@@ -11624,7 +11624,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
11624
11624
|
var lastVersionCheckAt = 0;
|
|
11625
11625
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
11626
11626
|
var lastResponsivenessProbeAt = 0;
|
|
11627
|
-
var agtCliVersion = true ? "0.28.
|
|
11627
|
+
var agtCliVersion = true ? "0.28.584" : "dev";
|
|
11628
11628
|
function resolveBrewPath(execFileSync2) {
|
|
11629
11629
|
try {
|
|
11630
11630
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -12931,7 +12931,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
12931
12931
|
if (codeNames.length === 0) return;
|
|
12932
12932
|
void (async () => {
|
|
12933
12933
|
try {
|
|
12934
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
12934
|
+
const { collectDiagnostics } = await import("../persistent-session-R7ACVUYP.js");
|
|
12935
12935
|
await api.post("/host/heartbeat", {
|
|
12936
12936
|
host_id: hostId,
|
|
12937
12937
|
agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics)
|
|
@@ -13039,7 +13039,7 @@ async function pollCycle() {
|
|
|
13039
13039
|
}
|
|
13040
13040
|
try {
|
|
13041
13041
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
13042
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13042
|
+
const { collectDiagnostics } = await import("../persistent-session-R7ACVUYP.js");
|
|
13043
13043
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
13044
13044
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics) : void 0;
|
|
13045
13045
|
let tailscaleHostname;
|
|
@@ -13162,7 +13162,7 @@ async function pollCycle() {
|
|
|
13162
13162
|
collectPanelessActivityProbes,
|
|
13163
13163
|
getResponsivenessIntervalMs,
|
|
13164
13164
|
occupancyQualificationClassifications
|
|
13165
|
-
} = await import("../responsiveness-probe-
|
|
13165
|
+
} = await import("../responsiveness-probe-RDBVTQD7.js");
|
|
13166
13166
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
13167
13167
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
13168
13168
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -13253,7 +13253,7 @@ async function pollCycle() {
|
|
|
13253
13253
|
collectResponsivenessProbes,
|
|
13254
13254
|
livePendingInboundOldestAgeSeconds,
|
|
13255
13255
|
parkPendingInbound
|
|
13256
|
-
} = await import("../responsiveness-probe-
|
|
13256
|
+
} = await import("../responsiveness-probe-RDBVTQD7.js");
|
|
13257
13257
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
|
|
13258
13258
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
13259
13259
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -16823,7 +16823,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
16823
16823
|
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}`));
|
|
16824
16824
|
void (async () => {
|
|
16825
16825
|
try {
|
|
16826
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
16826
|
+
const { collectDiagnostics } = await import("../persistent-session-R7ACVUYP.js");
|
|
16827
16827
|
await api.post("/host/heartbeat", {
|
|
16828
16828
|
host_id: hostId,
|
|
16829
16829
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics)
|
|
@@ -16874,7 +16874,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
16874
16874
|
}
|
|
16875
16875
|
try {
|
|
16876
16876
|
const hostId = await getHostId();
|
|
16877
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
16877
|
+
const { collectDiagnostics } = await import("../persistent-session-R7ACVUYP.js");
|
|
16878
16878
|
await api.post("/host/heartbeat", {
|
|
16879
16879
|
host_id: hostId,
|
|
16880
16880
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics)
|
|
@@ -17452,7 +17452,7 @@ async function processClaudePairSessions(agents) {
|
|
|
17452
17452
|
killPairSession,
|
|
17453
17453
|
pairTmuxSession,
|
|
17454
17454
|
finalizeClaudePairOnboarding
|
|
17455
|
-
} = await import("../claude-pair-runtime-
|
|
17455
|
+
} = await import("../claude-pair-runtime-WLMXUNII.js");
|
|
17456
17456
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
17457
17457
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
17458
17458
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -36636,6 +36636,145 @@ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDEL
|
|
|
36636
36636
|
function undeliverableNoticeText(replayEnabled = channelReplayEnabled()) {
|
|
36637
36637
|
return replayEnabled ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
|
|
36638
36638
|
}
|
|
36639
|
+
var UNDELIVERABLE_NOTICE_DEBOUNCE_MS = 3e4;
|
|
36640
|
+
function undeliverableNoticeDebounceMs() {
|
|
36641
|
+
const raw = (process.env.AGT_CHANNEL_UNDELIVERABLE_NOTICE_DEBOUNCE_MS ?? "").trim();
|
|
36642
|
+
if (!/^\d+$/.test(raw)) return UNDELIVERABLE_NOTICE_DEBOUNCE_MS;
|
|
36643
|
+
const parsed = Number(raw);
|
|
36644
|
+
return Number.isSafeInteger(parsed) ? parsed : UNDELIVERABLE_NOTICE_DEBOUNCE_MS;
|
|
36645
|
+
}
|
|
36646
|
+
function undeliverableNoticeDelayMs(cause, debounceMs = undeliverableNoticeDebounceMs()) {
|
|
36647
|
+
if (isConfirmedUndeliverableCause(cause)) return 0;
|
|
36648
|
+
return debounceMs;
|
|
36649
|
+
}
|
|
36650
|
+
function isConfirmedUndeliverableCause(cause) {
|
|
36651
|
+
return cause === "integration_down" || cause === "session_dead";
|
|
36652
|
+
}
|
|
36653
|
+
function decideUndeliverableNoticeDispatch(i) {
|
|
36654
|
+
if (i.confirmed && i.hasPendingNotice) return "send-now";
|
|
36655
|
+
if (!shouldPostUndeliverableNotice(i.lastNoticeAtMs, i.nowMs, i.throttleMs)) return "suppress";
|
|
36656
|
+
if (i.confirmed) return "send-now";
|
|
36657
|
+
if (i.hasPendingNotice) return "suppress";
|
|
36658
|
+
return i.delayMs > 0 ? "schedule" : "send-now";
|
|
36659
|
+
}
|
|
36660
|
+
var UndeliverableNoticeScheduler = class {
|
|
36661
|
+
constructor(throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
|
|
36662
|
+
this.throttleMs = throttleMs;
|
|
36663
|
+
}
|
|
36664
|
+
/** decision-time stamps, keyed by conversation. Shared with other one-per-window system notices. */
|
|
36665
|
+
stamps = /* @__PURE__ */ new Map();
|
|
36666
|
+
/** scheduled-but-unsent notices, keyed by conversation. */
|
|
36667
|
+
timers = /* @__PURE__ */ new Map();
|
|
36668
|
+
/** is a notice scheduled but not yet sent for this conversation? */
|
|
36669
|
+
hasPending(key2) {
|
|
36670
|
+
return this.timers.has(key2);
|
|
36671
|
+
}
|
|
36672
|
+
/** when this conversation was last stamped, or undefined if never. */
|
|
36673
|
+
stampedAt(key2) {
|
|
36674
|
+
return this.stamps.get(key2);
|
|
36675
|
+
}
|
|
36676
|
+
/**
|
|
36677
|
+
* ENG-6025: claim this conversation's one-notice-per-window slot for a
|
|
36678
|
+
* DIFFERENT system notice (the watchdog give-up line). Returns false when the
|
|
36679
|
+
* window is still closed. Sharing the stamp is the point: the user gets one
|
|
36680
|
+
* system message per conversation per window, whichever fires first.
|
|
36681
|
+
*/
|
|
36682
|
+
claimThrottleSlot(key2, nowMs = Date.now()) {
|
|
36683
|
+
if (!shouldPostUndeliverableNotice(this.stamps.get(key2), nowMs, this.throttleMs)) return false;
|
|
36684
|
+
this.stamps.set(key2, nowMs);
|
|
36685
|
+
return true;
|
|
36686
|
+
}
|
|
36687
|
+
/**
|
|
36688
|
+
* Run the dispatch decision and carry it out. `send` is invoked synchronously
|
|
36689
|
+
* for 'send-now' and after `delayMs` for 'schedule'; it is never invoked for
|
|
36690
|
+
* 'suppress'. Returns the decision so callers can log it.
|
|
36691
|
+
*/
|
|
36692
|
+
dispatch(key2, i) {
|
|
36693
|
+
const nowMs = i.nowMs ?? Date.now();
|
|
36694
|
+
const decision = decideUndeliverableNoticeDispatch({
|
|
36695
|
+
confirmed: i.confirmed,
|
|
36696
|
+
delayMs: i.delayMs,
|
|
36697
|
+
hasPendingNotice: this.timers.has(key2),
|
|
36698
|
+
lastNoticeAtMs: this.stamps.get(key2),
|
|
36699
|
+
nowMs,
|
|
36700
|
+
throttleMs: this.throttleMs
|
|
36701
|
+
});
|
|
36702
|
+
if (decision === "suppress") return "suppress";
|
|
36703
|
+
if (decision === "send-now") {
|
|
36704
|
+
this.clearTimer(key2);
|
|
36705
|
+
this.stamps.set(key2, nowMs);
|
|
36706
|
+
this.safeSend(i.send);
|
|
36707
|
+
return "send-now";
|
|
36708
|
+
}
|
|
36709
|
+
this.stamps.set(key2, nowMs);
|
|
36710
|
+
const timer = setTimeout(() => {
|
|
36711
|
+
this.timers.delete(key2);
|
|
36712
|
+
this.safeSend(i.send);
|
|
36713
|
+
}, i.delayMs);
|
|
36714
|
+
timer.unref?.();
|
|
36715
|
+
this.timers.set(key2, timer);
|
|
36716
|
+
return "schedule";
|
|
36717
|
+
}
|
|
36718
|
+
/**
|
|
36719
|
+
* The agent engaged: drop a scheduled-but-unsent notice, and its stamp with
|
|
36720
|
+
* it. Returns whether anything was actually cancelled.
|
|
36721
|
+
*
|
|
36722
|
+
* Cancelling something that was never sent cannot be wrong, so this needs no
|
|
36723
|
+
* "was it undeliverable?" gate - which is what Slack's version had, and why
|
|
36724
|
+
* it missed the case where the marker had never been flagged.
|
|
36725
|
+
*/
|
|
36726
|
+
cancel(key2) {
|
|
36727
|
+
if (!this.timers.has(key2)) return false;
|
|
36728
|
+
this.clearTimer(key2);
|
|
36729
|
+
this.stamps.delete(key2);
|
|
36730
|
+
return true;
|
|
36731
|
+
}
|
|
36732
|
+
/**
|
|
36733
|
+
* Forget this conversation entirely - pending timer AND stamp - after a
|
|
36734
|
+
* POSTED notice has been retracted (ENG-7705). Distinct from cancel(): that
|
|
36735
|
+
* one is a no-op when nothing is pending, this one always clears the stamp so
|
|
36736
|
+
* a later busy period earns a fresh notice instead of staying muted.
|
|
36737
|
+
*/
|
|
36738
|
+
forget(key2) {
|
|
36739
|
+
this.clearTimer(key2);
|
|
36740
|
+
this.stamps.delete(key2);
|
|
36741
|
+
}
|
|
36742
|
+
/** Test seam / shutdown: drop all timers and stamps. */
|
|
36743
|
+
reset() {
|
|
36744
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
36745
|
+
this.timers.clear();
|
|
36746
|
+
this.stamps.clear();
|
|
36747
|
+
}
|
|
36748
|
+
clearTimer(key2) {
|
|
36749
|
+
const timer = this.timers.get(key2);
|
|
36750
|
+
if (timer) clearTimeout(timer);
|
|
36751
|
+
this.timers.delete(key2);
|
|
36752
|
+
}
|
|
36753
|
+
/**
|
|
36754
|
+
* ENG-6247 round 3 (CodeRabbit #4446): the notice is fire-and-forget by design
|
|
36755
|
+
* everywhere else - "a failed notice never blocks inbound processing" - and
|
|
36756
|
+
* that has to hold here too.
|
|
36757
|
+
*
|
|
36758
|
+
* The delayed case is the dangerous one: a setTimeout callback runs outside
|
|
36759
|
+
* every request context, so a synchronous throw from `send` is an unhandled
|
|
36760
|
+
* exception and takes the channel server down. Both call sites close over
|
|
36761
|
+
* network-calling code. A notice the user was never promised must not be able
|
|
36762
|
+
* to cost them their agent.
|
|
36763
|
+
*
|
|
36764
|
+
* The immediate case is wrapped for the same reason one step earlier: it runs
|
|
36765
|
+
* inside the inbound handler, so an escaping throw would abort processing of
|
|
36766
|
+
* the very message the notice was about.
|
|
36767
|
+
*
|
|
36768
|
+
* Swallowed rather than logged: this module is pure and has no channel logger,
|
|
36769
|
+
* and both send closures already report their own failures.
|
|
36770
|
+
*/
|
|
36771
|
+
safeSend(send) {
|
|
36772
|
+
try {
|
|
36773
|
+
send();
|
|
36774
|
+
} catch {
|
|
36775
|
+
}
|
|
36776
|
+
}
|
|
36777
|
+
};
|
|
36639
36778
|
function undeliverableNoticeConversationKey(channel, threadTs, isThreadReply) {
|
|
36640
36779
|
return isThreadReply && threadTs ? `${channel}:${threadTs}` : channel;
|
|
36641
36780
|
}
|
|
@@ -41481,16 +41620,19 @@ function healSlackUndeliverable(channel, messageTs) {
|
|
|
41481
41620
|
});
|
|
41482
41621
|
});
|
|
41483
41622
|
}
|
|
41484
|
-
var
|
|
41623
|
+
var slackUndeliverableNotices = new UndeliverableNoticeScheduler();
|
|
41485
41624
|
var lastSlackUndeliverableNoticeInfo = /* @__PURE__ */ new Map();
|
|
41486
|
-
function postUndeliverableNotice(channel, threadTs, isThreadReply) {
|
|
41625
|
+
function postUndeliverableNotice(channel, threadTs, isThreadReply, cause = "unknown") {
|
|
41487
41626
|
if (!BOT_TOKEN || !channel) return;
|
|
41488
|
-
const now = Date.now();
|
|
41489
41627
|
const conversationKey = undeliverableNoticeConversationKey(channel, threadTs, isThreadReply);
|
|
41490
|
-
|
|
41491
|
-
|
|
41492
|
-
|
|
41493
|
-
|
|
41628
|
+
slackUndeliverableNotices.dispatch(conversationKey, {
|
|
41629
|
+
confirmed: isConfirmedUndeliverableCause(cause),
|
|
41630
|
+
delayMs: undeliverableNoticeDelayMs(cause),
|
|
41631
|
+
send: () => sendUndeliverableNotice(channel, threadTs, conversationKey)
|
|
41632
|
+
});
|
|
41633
|
+
}
|
|
41634
|
+
function sendUndeliverableNotice(channel, threadTs, conversationKey) {
|
|
41635
|
+
if (!BOT_TOKEN || !channel) return;
|
|
41494
41636
|
fetch("https://slack.com/api/chat.postMessage", {
|
|
41495
41637
|
method: "POST",
|
|
41496
41638
|
headers: {
|
|
@@ -41510,13 +41652,19 @@ function postUndeliverableNotice(channel, threadTs, isThreadReply) {
|
|
|
41510
41652
|
});
|
|
41511
41653
|
}
|
|
41512
41654
|
function __resetSlackUndeliverableNoticeThrottle() {
|
|
41513
|
-
|
|
41655
|
+
slackUndeliverableNotices.reset();
|
|
41514
41656
|
lastSlackUndeliverableNoticeInfo.clear();
|
|
41515
41657
|
}
|
|
41658
|
+
function __slackUndeliverableNoticeScheduled(conversationKey) {
|
|
41659
|
+
return slackUndeliverableNotices.hasPending(conversationKey);
|
|
41660
|
+
}
|
|
41661
|
+
function cancelScheduledSlackUndeliverableNotice(conversationKey) {
|
|
41662
|
+
slackUndeliverableNotices.cancel(conversationKey);
|
|
41663
|
+
}
|
|
41516
41664
|
function deleteSlackUndeliverableNotice(conversationKey) {
|
|
41665
|
+
slackUndeliverableNotices.forget(conversationKey);
|
|
41517
41666
|
const info = lastSlackUndeliverableNoticeInfo.get(conversationKey);
|
|
41518
41667
|
lastSlackUndeliverableNoticeInfo.delete(conversationKey);
|
|
41519
|
-
lastSlackUndeliverableNoticeAt.delete(conversationKey);
|
|
41520
41668
|
if (!BOT_TOKEN || !info) return;
|
|
41521
41669
|
fetch("https://slack.com/api/chat.delete", {
|
|
41522
41670
|
method: "POST",
|
|
@@ -41604,6 +41752,11 @@ function clearSlackMarkerFileWithHeal(fullPath) {
|
|
|
41604
41752
|
marker = JSON.parse(readFileSync18(fullPath, "utf-8"));
|
|
41605
41753
|
} catch {
|
|
41606
41754
|
}
|
|
41755
|
+
if (marker?.channel) {
|
|
41756
|
+
cancelScheduledSlackUndeliverableNotice(
|
|
41757
|
+
undeliverableNoticeConversationKeyFromMarker(marker)
|
|
41758
|
+
);
|
|
41759
|
+
}
|
|
41607
41760
|
if (marker && decideRecoveryHeal({
|
|
41608
41761
|
wasUndeliverable: marker.undeliverable === true,
|
|
41609
41762
|
hasTarget: Boolean(marker.channel && marker.message_ts)
|
|
@@ -41632,6 +41785,11 @@ function healThenEngageSlackMarker(fullPath, opts) {
|
|
|
41632
41785
|
} catch {
|
|
41633
41786
|
return false;
|
|
41634
41787
|
}
|
|
41788
|
+
if (marker.channel) {
|
|
41789
|
+
cancelScheduledSlackUndeliverableNotice(
|
|
41790
|
+
undeliverableNoticeConversationKeyFromMarker(marker)
|
|
41791
|
+
);
|
|
41792
|
+
}
|
|
41635
41793
|
if (decideRecoveryHeal({
|
|
41636
41794
|
wasUndeliverable: marker.undeliverable === true,
|
|
41637
41795
|
hasTarget: Boolean(marker.channel && marker.message_ts)
|
|
@@ -42415,10 +42573,7 @@ function postSlackWatchdogGiveUpNotice(channel, threadTs, isThreadReply, reason)
|
|
|
42415
42573
|
if (!BOT_TOKEN || !channel) return;
|
|
42416
42574
|
const now = Date.now();
|
|
42417
42575
|
const conversationKey = isThreadReply ? `${channel}:${threadTs}` : channel;
|
|
42418
|
-
if (!
|
|
42419
|
-
return;
|
|
42420
|
-
}
|
|
42421
|
-
lastSlackUndeliverableNoticeAt.set(conversationKey, now);
|
|
42576
|
+
if (!slackUndeliverableNotices.claimThrottleSlot(conversationKey, now)) return;
|
|
42422
42577
|
process.stderr.write(
|
|
42423
42578
|
`slack-channel(${AGENT_CODE_NAME}): give-up notice posted channel=${redactSlackId(channel)}
|
|
42424
42579
|
`
|
|
@@ -45988,8 +46143,8 @@ async function connectSocketMode() {
|
|
|
45988
46143
|
};
|
|
45989
46144
|
const ackDecision = decideAckReaction(ackInputs);
|
|
45990
46145
|
const arrivedWhileBusy = hasInFlightInbound(SLACK_PENDING_INBOUND_DIR);
|
|
46146
|
+
const cause = classifyUndeliverableCause(ackInputs) ?? "unknown";
|
|
45991
46147
|
if (ackDecision === "undeliverable") {
|
|
45992
|
-
const cause = classifyUndeliverableCause(ackInputs) ?? "unknown";
|
|
45993
46148
|
recordChannelDeflection(SLACK_AGENT_DIR, "slack", cause);
|
|
45994
46149
|
process.stderr.write(
|
|
45995
46150
|
`slack-channel(${AGENT_CODE_NAME}): [channel-deflection] cause=${cause} channel=slack ts=${redactSlackId(ts)}
|
|
@@ -46008,7 +46163,7 @@ async function connectSocketMode() {
|
|
|
46008
46163
|
}).catch(() => {
|
|
46009
46164
|
});
|
|
46010
46165
|
if (ackDecision === "undeliverable" && channel && undeliverableNoticeEligible(shouldEngage)) {
|
|
46011
|
-
postUndeliverableNotice(channel, threadTs, isThreadReply);
|
|
46166
|
+
postUndeliverableNotice(channel, threadTs, isThreadReply, cause);
|
|
46012
46167
|
}
|
|
46013
46168
|
}
|
|
46014
46169
|
if (channel && ts && armMarker) {
|
|
@@ -46300,7 +46455,8 @@ connectSocketModeSafely();
|
|
|
46300
46455
|
export {
|
|
46301
46456
|
__resetSlackBusyAckNoticeThrottle,
|
|
46302
46457
|
__resetSlackGiveUpNoticeStateForTests,
|
|
46303
|
-
__resetSlackUndeliverableNoticeThrottle
|
|
46458
|
+
__resetSlackUndeliverableNoticeThrottle,
|
|
46459
|
+
__slackUndeliverableNoticeScheduled
|
|
46304
46460
|
};
|
|
46305
46461
|
/*! Bundled license information:
|
|
46306
46462
|
|
|
@@ -39247,6 +39247,145 @@ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDEL
|
|
|
39247
39247
|
function undeliverableNoticeText(replayEnabled = channelReplayEnabled()) {
|
|
39248
39248
|
return replayEnabled ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
|
|
39249
39249
|
}
|
|
39250
|
+
var UNDELIVERABLE_NOTICE_DEBOUNCE_MS = 3e4;
|
|
39251
|
+
function undeliverableNoticeDebounceMs() {
|
|
39252
|
+
const raw = (process.env.AGT_CHANNEL_UNDELIVERABLE_NOTICE_DEBOUNCE_MS ?? "").trim();
|
|
39253
|
+
if (!/^\d+$/.test(raw)) return UNDELIVERABLE_NOTICE_DEBOUNCE_MS;
|
|
39254
|
+
const parsed = Number(raw);
|
|
39255
|
+
return Number.isSafeInteger(parsed) ? parsed : UNDELIVERABLE_NOTICE_DEBOUNCE_MS;
|
|
39256
|
+
}
|
|
39257
|
+
function undeliverableNoticeDelayMs(cause, debounceMs = undeliverableNoticeDebounceMs()) {
|
|
39258
|
+
if (isConfirmedUndeliverableCause(cause)) return 0;
|
|
39259
|
+
return debounceMs;
|
|
39260
|
+
}
|
|
39261
|
+
function isConfirmedUndeliverableCause(cause) {
|
|
39262
|
+
return cause === "integration_down" || cause === "session_dead";
|
|
39263
|
+
}
|
|
39264
|
+
function decideUndeliverableNoticeDispatch(i) {
|
|
39265
|
+
if (i.confirmed && i.hasPendingNotice) return "send-now";
|
|
39266
|
+
if (!shouldPostUndeliverableNotice(i.lastNoticeAtMs, i.nowMs, i.throttleMs)) return "suppress";
|
|
39267
|
+
if (i.confirmed) return "send-now";
|
|
39268
|
+
if (i.hasPendingNotice) return "suppress";
|
|
39269
|
+
return i.delayMs > 0 ? "schedule" : "send-now";
|
|
39270
|
+
}
|
|
39271
|
+
var UndeliverableNoticeScheduler = class {
|
|
39272
|
+
constructor(throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
|
|
39273
|
+
this.throttleMs = throttleMs;
|
|
39274
|
+
}
|
|
39275
|
+
/** decision-time stamps, keyed by conversation. Shared with other one-per-window system notices. */
|
|
39276
|
+
stamps = /* @__PURE__ */ new Map();
|
|
39277
|
+
/** scheduled-but-unsent notices, keyed by conversation. */
|
|
39278
|
+
timers = /* @__PURE__ */ new Map();
|
|
39279
|
+
/** is a notice scheduled but not yet sent for this conversation? */
|
|
39280
|
+
hasPending(key2) {
|
|
39281
|
+
return this.timers.has(key2);
|
|
39282
|
+
}
|
|
39283
|
+
/** when this conversation was last stamped, or undefined if never. */
|
|
39284
|
+
stampedAt(key2) {
|
|
39285
|
+
return this.stamps.get(key2);
|
|
39286
|
+
}
|
|
39287
|
+
/**
|
|
39288
|
+
* ENG-6025: claim this conversation's one-notice-per-window slot for a
|
|
39289
|
+
* DIFFERENT system notice (the watchdog give-up line). Returns false when the
|
|
39290
|
+
* window is still closed. Sharing the stamp is the point: the user gets one
|
|
39291
|
+
* system message per conversation per window, whichever fires first.
|
|
39292
|
+
*/
|
|
39293
|
+
claimThrottleSlot(key2, nowMs = Date.now()) {
|
|
39294
|
+
if (!shouldPostUndeliverableNotice(this.stamps.get(key2), nowMs, this.throttleMs)) return false;
|
|
39295
|
+
this.stamps.set(key2, nowMs);
|
|
39296
|
+
return true;
|
|
39297
|
+
}
|
|
39298
|
+
/**
|
|
39299
|
+
* Run the dispatch decision and carry it out. `send` is invoked synchronously
|
|
39300
|
+
* for 'send-now' and after `delayMs` for 'schedule'; it is never invoked for
|
|
39301
|
+
* 'suppress'. Returns the decision so callers can log it.
|
|
39302
|
+
*/
|
|
39303
|
+
dispatch(key2, i) {
|
|
39304
|
+
const nowMs = i.nowMs ?? Date.now();
|
|
39305
|
+
const decision = decideUndeliverableNoticeDispatch({
|
|
39306
|
+
confirmed: i.confirmed,
|
|
39307
|
+
delayMs: i.delayMs,
|
|
39308
|
+
hasPendingNotice: this.timers.has(key2),
|
|
39309
|
+
lastNoticeAtMs: this.stamps.get(key2),
|
|
39310
|
+
nowMs,
|
|
39311
|
+
throttleMs: this.throttleMs
|
|
39312
|
+
});
|
|
39313
|
+
if (decision === "suppress") return "suppress";
|
|
39314
|
+
if (decision === "send-now") {
|
|
39315
|
+
this.clearTimer(key2);
|
|
39316
|
+
this.stamps.set(key2, nowMs);
|
|
39317
|
+
this.safeSend(i.send);
|
|
39318
|
+
return "send-now";
|
|
39319
|
+
}
|
|
39320
|
+
this.stamps.set(key2, nowMs);
|
|
39321
|
+
const timer = setTimeout(() => {
|
|
39322
|
+
this.timers.delete(key2);
|
|
39323
|
+
this.safeSend(i.send);
|
|
39324
|
+
}, i.delayMs);
|
|
39325
|
+
timer.unref?.();
|
|
39326
|
+
this.timers.set(key2, timer);
|
|
39327
|
+
return "schedule";
|
|
39328
|
+
}
|
|
39329
|
+
/**
|
|
39330
|
+
* The agent engaged: drop a scheduled-but-unsent notice, and its stamp with
|
|
39331
|
+
* it. Returns whether anything was actually cancelled.
|
|
39332
|
+
*
|
|
39333
|
+
* Cancelling something that was never sent cannot be wrong, so this needs no
|
|
39334
|
+
* "was it undeliverable?" gate - which is what Slack's version had, and why
|
|
39335
|
+
* it missed the case where the marker had never been flagged.
|
|
39336
|
+
*/
|
|
39337
|
+
cancel(key2) {
|
|
39338
|
+
if (!this.timers.has(key2)) return false;
|
|
39339
|
+
this.clearTimer(key2);
|
|
39340
|
+
this.stamps.delete(key2);
|
|
39341
|
+
return true;
|
|
39342
|
+
}
|
|
39343
|
+
/**
|
|
39344
|
+
* Forget this conversation entirely - pending timer AND stamp - after a
|
|
39345
|
+
* POSTED notice has been retracted (ENG-7705). Distinct from cancel(): that
|
|
39346
|
+
* one is a no-op when nothing is pending, this one always clears the stamp so
|
|
39347
|
+
* a later busy period earns a fresh notice instead of staying muted.
|
|
39348
|
+
*/
|
|
39349
|
+
forget(key2) {
|
|
39350
|
+
this.clearTimer(key2);
|
|
39351
|
+
this.stamps.delete(key2);
|
|
39352
|
+
}
|
|
39353
|
+
/** Test seam / shutdown: drop all timers and stamps. */
|
|
39354
|
+
reset() {
|
|
39355
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
39356
|
+
this.timers.clear();
|
|
39357
|
+
this.stamps.clear();
|
|
39358
|
+
}
|
|
39359
|
+
clearTimer(key2) {
|
|
39360
|
+
const timer = this.timers.get(key2);
|
|
39361
|
+
if (timer) clearTimeout(timer);
|
|
39362
|
+
this.timers.delete(key2);
|
|
39363
|
+
}
|
|
39364
|
+
/**
|
|
39365
|
+
* ENG-6247 round 3 (CodeRabbit #4446): the notice is fire-and-forget by design
|
|
39366
|
+
* everywhere else - "a failed notice never blocks inbound processing" - and
|
|
39367
|
+
* that has to hold here too.
|
|
39368
|
+
*
|
|
39369
|
+
* The delayed case is the dangerous one: a setTimeout callback runs outside
|
|
39370
|
+
* every request context, so a synchronous throw from `send` is an unhandled
|
|
39371
|
+
* exception and takes the channel server down. Both call sites close over
|
|
39372
|
+
* network-calling code. A notice the user was never promised must not be able
|
|
39373
|
+
* to cost them their agent.
|
|
39374
|
+
*
|
|
39375
|
+
* The immediate case is wrapped for the same reason one step earlier: it runs
|
|
39376
|
+
* inside the inbound handler, so an escaping throw would abort processing of
|
|
39377
|
+
* the very message the notice was about.
|
|
39378
|
+
*
|
|
39379
|
+
* Swallowed rather than logged: this module is pure and has no channel logger,
|
|
39380
|
+
* and both send closures already report their own failures.
|
|
39381
|
+
*/
|
|
39382
|
+
safeSend(send) {
|
|
39383
|
+
try {
|
|
39384
|
+
send();
|
|
39385
|
+
} catch {
|
|
39386
|
+
}
|
|
39387
|
+
}
|
|
39388
|
+
};
|
|
39250
39389
|
var BUSY_ACK_THRESHOLD_MS = 12e4;
|
|
39251
39390
|
var BUSY_ACK_NOTICE_THROTTLE_MS = 20 * 60 * 1e3;
|
|
39252
39391
|
function channelBusyAckEnabled() {
|
|
@@ -39959,12 +40098,18 @@ async function setMessageReaction(chatId, messageId, emoji2) {
|
|
|
39959
40098
|
);
|
|
39960
40099
|
}
|
|
39961
40100
|
}
|
|
39962
|
-
var
|
|
39963
|
-
|
|
39964
|
-
|
|
39965
|
-
|
|
39966
|
-
|
|
39967
|
-
|
|
40101
|
+
var telegramUndeliverableNotices = new UndeliverableNoticeScheduler();
|
|
40102
|
+
function cancelScheduledUndeliverableNotice(chatId) {
|
|
40103
|
+
telegramUndeliverableNotices.cancel(String(chatId));
|
|
40104
|
+
}
|
|
40105
|
+
function notifyUndeliverable(chatId, messageId, cause = "unknown") {
|
|
40106
|
+
telegramUndeliverableNotices.dispatch(String(chatId), {
|
|
40107
|
+
confirmed: isConfirmedUndeliverableCause(cause),
|
|
40108
|
+
delayMs: undeliverableNoticeDelayMs(cause),
|
|
40109
|
+
send: () => void sendUndeliverableNotice(chatId, messageId)
|
|
40110
|
+
});
|
|
40111
|
+
}
|
|
40112
|
+
async function sendUndeliverableNotice(chatId, messageId) {
|
|
39968
40113
|
try {
|
|
39969
40114
|
const resp = await telegramApiCall(
|
|
39970
40115
|
"sendMessage",
|
|
@@ -39991,7 +40136,10 @@ async function notifyUndeliverable(chatId, messageId) {
|
|
|
39991
40136
|
}
|
|
39992
40137
|
}
|
|
39993
40138
|
function __resetUndeliverableNoticeThrottle() {
|
|
39994
|
-
|
|
40139
|
+
telegramUndeliverableNotices.reset();
|
|
40140
|
+
}
|
|
40141
|
+
function __telegramUndeliverableNoticeScheduled(chatId) {
|
|
40142
|
+
return telegramUndeliverableNotices.hasPending(String(chatId));
|
|
39995
40143
|
}
|
|
39996
40144
|
var lastBackOnlineNoticeAt = /* @__PURE__ */ new Map();
|
|
39997
40145
|
function notifyBackOnline(chatId) {
|
|
@@ -41005,6 +41153,9 @@ function clearTelegramMarkerFileWithHeal(fullPath) {
|
|
|
41005
41153
|
marker = JSON.parse(readFileSync14(fullPath, "utf-8"));
|
|
41006
41154
|
} catch {
|
|
41007
41155
|
}
|
|
41156
|
+
if (marker?.chat_id) {
|
|
41157
|
+
cancelScheduledUndeliverableNotice(marker.chat_id);
|
|
41158
|
+
}
|
|
41008
41159
|
if (marker && decideRecoveryHeal({
|
|
41009
41160
|
wasUndeliverable: marker.undeliverable === true,
|
|
41010
41161
|
hasTarget: Boolean(marker.chat_id)
|
|
@@ -41035,6 +41186,7 @@ function markTelegramMarkerEngagedInPlace(fullPath, opts) {
|
|
|
41035
41186
|
(/* @__PURE__ */ new Date()).toISOString()
|
|
41036
41187
|
);
|
|
41037
41188
|
if (!decision.stamp) return opts.promise ? decision.promised : false;
|
|
41189
|
+
if (marker.chat_id) cancelScheduledUndeliverableNotice(marker.chat_id);
|
|
41038
41190
|
Object.assign(marker, decision.stamp);
|
|
41039
41191
|
if (marker.undeliverable) delete marker.undeliverable;
|
|
41040
41192
|
rewriteTelegramMarkerInPlace(fullPath, marker);
|
|
@@ -41053,6 +41205,7 @@ function markTelegramMarkerSeenWithHeal(fullPath) {
|
|
|
41053
41205
|
} catch {
|
|
41054
41206
|
return;
|
|
41055
41207
|
}
|
|
41208
|
+
if (marker.chat_id) cancelScheduledUndeliverableNotice(marker.chat_id);
|
|
41056
41209
|
if (decideRecoveryHeal({
|
|
41057
41210
|
wasUndeliverable: marker.undeliverable === true,
|
|
41058
41211
|
hasTarget: Boolean(marker.chat_id)
|
|
@@ -41733,8 +41886,7 @@ function armTelegramTurnFailureWatch(args) {
|
|
|
41733
41886
|
}
|
|
41734
41887
|
async function notifyWatchdogGiveUp(chatId, reason) {
|
|
41735
41888
|
const now = Date.now();
|
|
41736
|
-
if (!
|
|
41737
|
-
lastUndeliverableNoticeAt.set(chatId, now);
|
|
41889
|
+
if (!telegramUndeliverableNotices.claimThrottleSlot(chatId, now)) return;
|
|
41738
41890
|
try {
|
|
41739
41891
|
const resp = await telegramApiCall(
|
|
41740
41892
|
"sendMessage",
|
|
@@ -43277,7 +43429,7 @@ async function pollLoop() {
|
|
|
43277
43429
|
if (ackDecision === "ack") {
|
|
43278
43430
|
void setMessageReaction(chatId, messageId, ACK_EMOJI);
|
|
43279
43431
|
} else if (ackDecision === "undeliverable") {
|
|
43280
|
-
|
|
43432
|
+
notifyUndeliverable(chatId, messageId, classifyUndeliverableCause(ackInputs) ?? "unknown");
|
|
43281
43433
|
}
|
|
43282
43434
|
const fileMeta = [];
|
|
43283
43435
|
for (const attachment of classifiedAttachments) {
|
|
@@ -43508,6 +43660,7 @@ export {
|
|
|
43508
43660
|
__resetBusyAckNoticeThrottle,
|
|
43509
43661
|
__resetGiveUpNoticeStateForTests,
|
|
43510
43662
|
__resetUndeliverableNoticeThrottle,
|
|
43663
|
+
__telegramUndeliverableNoticeScheduled,
|
|
43511
43664
|
_resetQueuedReplyCountsForTests
|
|
43512
43665
|
};
|
|
43513
43666
|
/*! Bundled license information:
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
writeDirectChatSessionState,
|
|
44
44
|
writeEgressAllowlist,
|
|
45
45
|
writePersistentClaudeWrapper
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-XRG4XYAV.js";
|
|
47
47
|
import "./chunk-XWVM4KPK.js";
|
|
48
48
|
export {
|
|
49
49
|
EGRESS_BASELINE_DOMAINS,
|
|
@@ -91,4 +91,4 @@ export {
|
|
|
91
91
|
writeEgressAllowlist,
|
|
92
92
|
writePersistentClaudeWrapper
|
|
93
93
|
};
|
|
94
|
-
//# sourceMappingURL=persistent-session-
|
|
94
|
+
//# sourceMappingURL=persistent-session-R7ACVUYP.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
paneLogPath
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XRG4XYAV.js";
|
|
4
4
|
import "./chunk-XWVM4KPK.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/responsiveness-probe.ts
|
|
@@ -689,4 +689,4 @@ export {
|
|
|
689
689
|
readAndResetSlackReplyBindingClassifications,
|
|
690
690
|
readAndResetSlackReplyTargetClassifications
|
|
691
691
|
};
|
|
692
|
-
//# sourceMappingURL=responsiveness-probe-
|
|
692
|
+
//# sourceMappingURL=responsiveness-probe-RDBVTQD7.js.map
|