@integrity-labs/agt-cli 0.27.26 → 0.27.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +3 -3
- package/dist/{chunk-S53Y2M2Q.js → chunk-2ZTHGYH7.js} +23 -3
- package/dist/chunk-2ZTHGYH7.js.map +1 -0
- package/dist/lib/manager-worker.js +2 -2
- package/dist/mcp/slack-channel.js +175 -62
- package/dist/mcp/telegram-channel.js +6 -0
- package/package.json +1 -1
- package/dist/chunk-S53Y2M2Q.js.map +0 -1
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
provisionOrientHook,
|
|
16
16
|
provisionStopHook,
|
|
17
17
|
requireHost
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-2ZTHGYH7.js";
|
|
19
19
|
import {
|
|
20
20
|
getProjectDir as getProjectDir2,
|
|
21
21
|
getReadyTasks,
|
|
@@ -3208,7 +3208,7 @@ var cachedFrameworkVersion = null;
|
|
|
3208
3208
|
var lastVersionCheckAt = 0;
|
|
3209
3209
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3210
3210
|
var lastResponsivenessProbeAt = 0;
|
|
3211
|
-
var agtCliVersion = true ? "0.27.
|
|
3211
|
+
var agtCliVersion = true ? "0.27.28" : "dev";
|
|
3212
3212
|
function resolveBrewPath(execFileSync4) {
|
|
3213
3213
|
try {
|
|
3214
3214
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -14316,6 +14316,7 @@ import { readdirSync, readFileSync } from "fs";
|
|
|
14316
14316
|
import { join } from "path";
|
|
14317
14317
|
var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
14318
14318
|
var ACK_STARTUP_GRACE_MS = 6e4;
|
|
14319
|
+
var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
|
|
14319
14320
|
function decideAckReaction(i) {
|
|
14320
14321
|
if (!i.hasTarget) return "none";
|
|
14321
14322
|
if (!i.integrationReady) return "undeliverable";
|
|
@@ -14323,6 +14324,11 @@ function decideAckReaction(i) {
|
|
|
14323
14324
|
if (!i.withinStartupGrace && i.claude === "dead") return "undeliverable";
|
|
14324
14325
|
const threshold = i.pendingStaleThresholdMs ?? REPLY_WEDGED_THRESHOLD_MS;
|
|
14325
14326
|
if (i.oldestPendingAgeMs != null && i.oldestPendingAgeMs > threshold) {
|
|
14327
|
+
const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
|
|
14328
|
+
const paneIsFresh = i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
|
|
14329
|
+
if (paneIsFresh && i.tmux === "alive" && i.claude === "alive") {
|
|
14330
|
+
return "ack";
|
|
14331
|
+
}
|
|
14326
14332
|
return "undeliverable";
|
|
14327
14333
|
}
|
|
14328
14334
|
return "ack";
|
|
@@ -14617,6 +14623,69 @@ var SLACK_EGRESS_TOOLS = /* @__PURE__ */ new Set([
|
|
|
14617
14623
|
"slack.upload_file"
|
|
14618
14624
|
]);
|
|
14619
14625
|
|
|
14626
|
+
// src/slack-pending-inbound-cleanup.ts
|
|
14627
|
+
import { existsSync, readdirSync as readdirSync2, statSync, unlinkSync } from "fs";
|
|
14628
|
+
import { join as join2 } from "path";
|
|
14629
|
+
function sanitizeMarkerSegment(value) {
|
|
14630
|
+
return value.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
14631
|
+
}
|
|
14632
|
+
var defaultClearMarkerFile = (fullPath) => {
|
|
14633
|
+
try {
|
|
14634
|
+
if (existsSync(fullPath)) unlinkSync(fullPath);
|
|
14635
|
+
} catch {
|
|
14636
|
+
}
|
|
14637
|
+
};
|
|
14638
|
+
function clearAllSlackPendingMarkersForThread(dir, channel, threadTs, clear = defaultClearMarkerFile) {
|
|
14639
|
+
if (!dir) return 0;
|
|
14640
|
+
const prefix = `${sanitizeMarkerSegment(channel)}__${sanitizeMarkerSegment(threadTs)}__`;
|
|
14641
|
+
let cleared = 0;
|
|
14642
|
+
try {
|
|
14643
|
+
for (const f of readdirSync2(dir)) {
|
|
14644
|
+
if (!f.startsWith(prefix) || !f.endsWith(".json")) continue;
|
|
14645
|
+
clear(join2(dir, f));
|
|
14646
|
+
cleared += 1;
|
|
14647
|
+
}
|
|
14648
|
+
} catch {
|
|
14649
|
+
}
|
|
14650
|
+
return cleared;
|
|
14651
|
+
}
|
|
14652
|
+
function clearSlackPendingMarkerByMessageTs(dir, channel, messageTs, clear = defaultClearMarkerFile) {
|
|
14653
|
+
if (!dir) return 0;
|
|
14654
|
+
const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
|
|
14655
|
+
const messageSuffix = `__${sanitizeMarkerSegment(messageTs)}.json`;
|
|
14656
|
+
let cleared = 0;
|
|
14657
|
+
try {
|
|
14658
|
+
for (const f of readdirSync2(dir)) {
|
|
14659
|
+
if (!f.startsWith(channelPrefix) || !f.endsWith(messageSuffix)) continue;
|
|
14660
|
+
clear(join2(dir, f));
|
|
14661
|
+
cleared += 1;
|
|
14662
|
+
}
|
|
14663
|
+
} catch {
|
|
14664
|
+
}
|
|
14665
|
+
return cleared;
|
|
14666
|
+
}
|
|
14667
|
+
function clearOldestSlackPendingMarkerInChannel(dir, channel, clear = defaultClearMarkerFile) {
|
|
14668
|
+
if (!dir) return null;
|
|
14669
|
+
const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
|
|
14670
|
+
try {
|
|
14671
|
+
const entries = readdirSync2(dir).filter((f) => f.startsWith(channelPrefix) && f.endsWith(".json")).map((f) => {
|
|
14672
|
+
const full = join2(dir, f);
|
|
14673
|
+
let mtime = 0;
|
|
14674
|
+
try {
|
|
14675
|
+
mtime = statSync(full).mtimeMs;
|
|
14676
|
+
} catch {
|
|
14677
|
+
}
|
|
14678
|
+
return { name: f, full, mtime };
|
|
14679
|
+
}).filter((e) => e.mtime > 0).sort((a, b) => a.mtime - b.mtime);
|
|
14680
|
+
const oldest = entries[0];
|
|
14681
|
+
if (!oldest) return null;
|
|
14682
|
+
clear(oldest.full);
|
|
14683
|
+
return oldest.name;
|
|
14684
|
+
} catch {
|
|
14685
|
+
return null;
|
|
14686
|
+
}
|
|
14687
|
+
}
|
|
14688
|
+
|
|
14620
14689
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
14621
14690
|
import process2 from "process";
|
|
14622
14691
|
|
|
@@ -14713,17 +14782,17 @@ var StdioServerTransport = class {
|
|
|
14713
14782
|
import {
|
|
14714
14783
|
chmodSync,
|
|
14715
14784
|
createWriteStream,
|
|
14716
|
-
existsSync as
|
|
14785
|
+
existsSync as existsSync3,
|
|
14717
14786
|
mkdirSync as mkdirSync3,
|
|
14718
14787
|
readFileSync as readFileSync4,
|
|
14719
|
-
readdirSync as
|
|
14788
|
+
readdirSync as readdirSync3,
|
|
14720
14789
|
renameSync as renameSync2,
|
|
14721
|
-
statSync,
|
|
14722
|
-
unlinkSync as
|
|
14790
|
+
statSync as statSync2,
|
|
14791
|
+
unlinkSync as unlinkSync3,
|
|
14723
14792
|
watch,
|
|
14724
14793
|
writeFileSync as writeFileSync3
|
|
14725
14794
|
} from "fs";
|
|
14726
|
-
import { basename, join as
|
|
14795
|
+
import { basename, join as join5, resolve as resolve2 } from "path";
|
|
14727
14796
|
import { homedir as homedir2 } from "os";
|
|
14728
14797
|
import { createHash, randomUUID } from "crypto";
|
|
14729
14798
|
|
|
@@ -14854,9 +14923,9 @@ async function runOrRetry(fn, opts) {
|
|
|
14854
14923
|
|
|
14855
14924
|
// src/channel-attachments.ts
|
|
14856
14925
|
import { homedir } from "os";
|
|
14857
|
-
import { join as
|
|
14926
|
+
import { join as join3, resolve, sep } from "path";
|
|
14858
14927
|
function resolveChannelInboundDir(codeName, channelSlug) {
|
|
14859
|
-
const base =
|
|
14928
|
+
const base = join3(homedir(), ".augmented");
|
|
14860
14929
|
const allowedSegment = /^[A-Za-z0-9_-]+$/;
|
|
14861
14930
|
if (!allowedSegment.test(codeName) || !allowedSegment.test(channelSlug)) {
|
|
14862
14931
|
throw new Error(
|
|
@@ -15500,14 +15569,14 @@ function createSlackBotUserIdClient(args) {
|
|
|
15500
15569
|
|
|
15501
15570
|
// src/mcp-spawn-lock.ts
|
|
15502
15571
|
import {
|
|
15503
|
-
existsSync,
|
|
15572
|
+
existsSync as existsSync2,
|
|
15504
15573
|
mkdirSync as mkdirSync2,
|
|
15505
15574
|
readFileSync as readFileSync3,
|
|
15506
15575
|
renameSync,
|
|
15507
|
-
unlinkSync,
|
|
15576
|
+
unlinkSync as unlinkSync2,
|
|
15508
15577
|
writeFileSync as writeFileSync2
|
|
15509
15578
|
} from "fs";
|
|
15510
|
-
import { join as
|
|
15579
|
+
import { join as join4 } from "path";
|
|
15511
15580
|
function defaultIsPidAlive(pid) {
|
|
15512
15581
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
15513
15582
|
try {
|
|
@@ -15525,7 +15594,7 @@ function acquireMcpSpawnLock(args) {
|
|
|
15525
15594
|
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
15526
15595
|
const selfPid = options.selfPid ?? process.pid;
|
|
15527
15596
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
15528
|
-
const path =
|
|
15597
|
+
const path = join4(agentDir, basename2);
|
|
15529
15598
|
const existing = readLockHolder(path);
|
|
15530
15599
|
if (existing) {
|
|
15531
15600
|
if (existing.pid === selfPid) {
|
|
@@ -15549,12 +15618,12 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
|
15549
15618
|
if (!existing) return;
|
|
15550
15619
|
if (existing.pid !== selfPid) return;
|
|
15551
15620
|
try {
|
|
15552
|
-
|
|
15621
|
+
unlinkSync2(lockPath);
|
|
15553
15622
|
} catch {
|
|
15554
15623
|
}
|
|
15555
15624
|
}
|
|
15556
15625
|
function readLockHolder(path) {
|
|
15557
|
-
if (!
|
|
15626
|
+
if (!existsSync2(path)) return null;
|
|
15558
15627
|
try {
|
|
15559
15628
|
const raw = readFileSync3(path, "utf8");
|
|
15560
15629
|
const parsed = JSON.parse(raw);
|
|
@@ -15710,9 +15779,9 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
|
|
|
15710
15779
|
peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
|
|
15711
15780
|
peer_disabled_mode: SLACK_PEER_DISABLED_MODE
|
|
15712
15781
|
};
|
|
15713
|
-
var SLACK_AGENT_DIR = AGENT_CODE_NAME ?
|
|
15714
|
-
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ?
|
|
15715
|
-
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ?
|
|
15782
|
+
var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join5(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
15783
|
+
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join5(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
|
|
15784
|
+
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join5(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
|
|
15716
15785
|
var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
|
|
15717
15786
|
function redactSlackId(id) {
|
|
15718
15787
|
if (!id) return "<none>";
|
|
@@ -15724,7 +15793,7 @@ function safeSlackMarkerName(channel, threadTs, messageTs) {
|
|
|
15724
15793
|
}
|
|
15725
15794
|
function slackPendingInboundPath(channel, threadTs, messageTs) {
|
|
15726
15795
|
if (!SLACK_PENDING_INBOUND_DIR) return null;
|
|
15727
|
-
return
|
|
15796
|
+
return join5(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
|
|
15728
15797
|
}
|
|
15729
15798
|
function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undeliverable = false) {
|
|
15730
15799
|
const path = slackPendingInboundPath(channel, threadTs, messageTs);
|
|
@@ -15780,22 +15849,32 @@ function clearSlackMarkerFileWithHeal(fullPath) {
|
|
|
15780
15849
|
healSlackUndeliverable(marker.channel, marker.message_ts);
|
|
15781
15850
|
}
|
|
15782
15851
|
try {
|
|
15783
|
-
if (
|
|
15852
|
+
if (existsSync3(fullPath)) unlinkSync3(fullPath);
|
|
15784
15853
|
} catch {
|
|
15785
15854
|
}
|
|
15786
15855
|
}
|
|
15787
|
-
function
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
|
|
15792
|
-
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
|
|
15797
|
-
|
|
15798
|
-
|
|
15856
|
+
function clearAllSlackPendingMarkersForThread2(channel, threadTs) {
|
|
15857
|
+
clearAllSlackPendingMarkersForThread(
|
|
15858
|
+
SLACK_PENDING_INBOUND_DIR,
|
|
15859
|
+
channel,
|
|
15860
|
+
threadTs,
|
|
15861
|
+
clearSlackMarkerFileWithHeal
|
|
15862
|
+
);
|
|
15863
|
+
}
|
|
15864
|
+
function clearSlackPendingMarkerByMessageTs2(channel, messageTs) {
|
|
15865
|
+
clearSlackPendingMarkerByMessageTs(
|
|
15866
|
+
SLACK_PENDING_INBOUND_DIR,
|
|
15867
|
+
channel,
|
|
15868
|
+
messageTs,
|
|
15869
|
+
clearSlackMarkerFileWithHeal
|
|
15870
|
+
);
|
|
15871
|
+
}
|
|
15872
|
+
function clearOldestSlackPendingMarkerInChannel2(channel) {
|
|
15873
|
+
clearOldestSlackPendingMarkerInChannel(
|
|
15874
|
+
SLACK_PENDING_INBOUND_DIR,
|
|
15875
|
+
channel,
|
|
15876
|
+
clearSlackMarkerFileWithHeal
|
|
15877
|
+
);
|
|
15799
15878
|
}
|
|
15800
15879
|
function slackNextRetryName(filename) {
|
|
15801
15880
|
const match = filename.match(/^(.*?)(?:\.retry-(\d+))?\.json$/);
|
|
@@ -15811,7 +15890,7 @@ function slackNextRetryName(filename) {
|
|
|
15811
15890
|
async function processSlackRecoveryOutboxFile(filename) {
|
|
15812
15891
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
15813
15892
|
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
|
|
15814
|
-
const fullPath =
|
|
15893
|
+
const fullPath = join5(SLACK_RECOVERY_OUTBOX_DIR, filename);
|
|
15815
15894
|
let payload;
|
|
15816
15895
|
try {
|
|
15817
15896
|
payload = JSON.parse(readFileSync4(fullPath, "utf-8"));
|
|
@@ -15880,7 +15959,7 @@ async function processSlackRecoveryOutboxFile(filename) {
|
|
|
15880
15959
|
}
|
|
15881
15960
|
if (sendSucceeded) {
|
|
15882
15961
|
try {
|
|
15883
|
-
|
|
15962
|
+
unlinkSync3(fullPath);
|
|
15884
15963
|
} catch {
|
|
15885
15964
|
}
|
|
15886
15965
|
return;
|
|
@@ -15888,7 +15967,7 @@ async function processSlackRecoveryOutboxFile(filename) {
|
|
|
15888
15967
|
const next = slackNextRetryName(filename);
|
|
15889
15968
|
if (next) {
|
|
15890
15969
|
try {
|
|
15891
|
-
renameSync2(fullPath,
|
|
15970
|
+
renameSync2(fullPath, join5(SLACK_RECOVERY_OUTBOX_DIR, next.next));
|
|
15892
15971
|
if (next.attempt >= SLACK_MAX_RECOVERY_ATTEMPTS) {
|
|
15893
15972
|
process.stderr.write(
|
|
15894
15973
|
`slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
|
|
@@ -15918,7 +15997,7 @@ function scanSlackRecoveryRetries() {
|
|
|
15918
15997
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
15919
15998
|
let entries;
|
|
15920
15999
|
try {
|
|
15921
|
-
entries =
|
|
16000
|
+
entries = readdirSync3(SLACK_RECOVERY_OUTBOX_DIR);
|
|
15922
16001
|
} catch {
|
|
15923
16002
|
return;
|
|
15924
16003
|
}
|
|
@@ -15927,7 +16006,7 @@ function scanSlackRecoveryRetries() {
|
|
|
15927
16006
|
if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
|
|
15928
16007
|
let mtimeMs;
|
|
15929
16008
|
try {
|
|
15930
|
-
mtimeMs =
|
|
16009
|
+
mtimeMs = statSync2(join5(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
|
|
15931
16010
|
} catch {
|
|
15932
16011
|
continue;
|
|
15933
16012
|
}
|
|
@@ -15948,7 +16027,7 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
15948
16027
|
return;
|
|
15949
16028
|
}
|
|
15950
16029
|
try {
|
|
15951
|
-
for (const f of
|
|
16030
|
+
for (const f of readdirSync3(SLACK_RECOVERY_OUTBOX_DIR)) {
|
|
15952
16031
|
if (isFirstAttemptSlackOutboxFile(f)) void processSlackRecoveryOutboxFile(f);
|
|
15953
16032
|
}
|
|
15954
16033
|
} catch {
|
|
@@ -15957,7 +16036,7 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
15957
16036
|
const watcher = watch(SLACK_RECOVERY_OUTBOX_DIR, (event, filename) => {
|
|
15958
16037
|
if (event !== "rename" || !filename) return;
|
|
15959
16038
|
if (!isFirstAttemptSlackOutboxFile(filename)) return;
|
|
15960
|
-
if (
|
|
16039
|
+
if (existsSync3(join5(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
|
|
15961
16040
|
void processSlackRecoveryOutboxFile(filename);
|
|
15962
16041
|
}
|
|
15963
16042
|
});
|
|
@@ -15978,10 +16057,10 @@ function trackPendingMessage(channel, threadTs, messageTs, undeliverable = false
|
|
|
15978
16057
|
}
|
|
15979
16058
|
function sweepSlackStaleMarkersOnBoot() {
|
|
15980
16059
|
if (!SLACK_PENDING_INBOUND_DIR) return;
|
|
15981
|
-
if (!
|
|
16060
|
+
if (!existsSync3(SLACK_PENDING_INBOUND_DIR)) return;
|
|
15982
16061
|
let filenames;
|
|
15983
16062
|
try {
|
|
15984
|
-
filenames =
|
|
16063
|
+
filenames = readdirSync3(SLACK_PENDING_INBOUND_DIR);
|
|
15985
16064
|
} catch (err) {
|
|
15986
16065
|
process.stderr.write(
|
|
15987
16066
|
`slack-channel(${AGENT_CODE_NAME}): stale-marker readdir failed: ${err.message}
|
|
@@ -15994,7 +16073,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
15994
16073
|
for (const filename of filenames) {
|
|
15995
16074
|
if (!filename.endsWith(".json")) continue;
|
|
15996
16075
|
if (filename.endsWith(".tmp")) continue;
|
|
15997
|
-
const fullPath =
|
|
16076
|
+
const fullPath = join5(SLACK_PENDING_INBOUND_DIR, filename);
|
|
15998
16077
|
let marker;
|
|
15999
16078
|
try {
|
|
16000
16079
|
marker = JSON.parse(readFileSync4(fullPath, "utf-8"));
|
|
@@ -16004,7 +16083,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16004
16083
|
`
|
|
16005
16084
|
);
|
|
16006
16085
|
try {
|
|
16007
|
-
|
|
16086
|
+
unlinkSync3(fullPath);
|
|
16008
16087
|
} catch {
|
|
16009
16088
|
}
|
|
16010
16089
|
cleared++;
|
|
@@ -16013,7 +16092,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16013
16092
|
const { channel, thread_ts, message_ts, received_at } = marker;
|
|
16014
16093
|
if (!channel || !thread_ts || !message_ts || !received_at) {
|
|
16015
16094
|
try {
|
|
16016
|
-
|
|
16095
|
+
unlinkSync3(fullPath);
|
|
16017
16096
|
} catch {
|
|
16018
16097
|
}
|
|
16019
16098
|
cleared++;
|
|
@@ -16022,7 +16101,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16022
16101
|
const receivedAtMs = Date.parse(received_at);
|
|
16023
16102
|
if (!Number.isFinite(receivedAtMs) || receivedAtMs > now || now - receivedAtMs > STALE_MARKER_MS) {
|
|
16024
16103
|
try {
|
|
16025
|
-
|
|
16104
|
+
unlinkSync3(fullPath);
|
|
16026
16105
|
} catch {
|
|
16027
16106
|
}
|
|
16028
16107
|
cleared++;
|
|
@@ -16037,7 +16116,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16037
16116
|
}
|
|
16038
16117
|
sweepSlackStaleMarkersOnBoot();
|
|
16039
16118
|
function clearPendingMessage(channel, threadTs) {
|
|
16040
|
-
|
|
16119
|
+
clearAllSlackPendingMarkersForThread2(channel, threadTs);
|
|
16041
16120
|
}
|
|
16042
16121
|
function noteThreadActivity(channel, threadTs) {
|
|
16043
16122
|
if (!channel || !threadTs) return;
|
|
@@ -16047,10 +16126,10 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
|
16047
16126
|
if (!channel || !messageTs) return;
|
|
16048
16127
|
clearPendingMessage(channel, messageTs);
|
|
16049
16128
|
if (!SLACK_PENDING_INBOUND_DIR) return;
|
|
16050
|
-
if (!
|
|
16129
|
+
if (!existsSync3(SLACK_PENDING_INBOUND_DIR)) return;
|
|
16051
16130
|
let filenames;
|
|
16052
16131
|
try {
|
|
16053
|
-
filenames =
|
|
16132
|
+
filenames = readdirSync3(SLACK_PENDING_INBOUND_DIR);
|
|
16054
16133
|
} catch {
|
|
16055
16134
|
return;
|
|
16056
16135
|
}
|
|
@@ -16061,10 +16140,10 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
|
16061
16140
|
for (const filename of filenames) {
|
|
16062
16141
|
if (!filename.startsWith(channelPrefix)) continue;
|
|
16063
16142
|
if (!filename.endsWith(messageSuffix)) continue;
|
|
16064
|
-
clearSlackMarkerFileWithHeal(
|
|
16143
|
+
clearSlackMarkerFileWithHeal(join5(SLACK_PENDING_INBOUND_DIR, filename));
|
|
16065
16144
|
}
|
|
16066
16145
|
}
|
|
16067
|
-
var RESTART_FLAGS_DIR =
|
|
16146
|
+
var RESTART_FLAGS_DIR = join5(homedir2(), ".augmented", "restart-flags");
|
|
16068
16147
|
function buildAugmentedSlackMetadata() {
|
|
16069
16148
|
if (!AGT_TEAM_ID) return void 0;
|
|
16070
16149
|
return {
|
|
@@ -16243,10 +16322,10 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
16243
16322
|
return;
|
|
16244
16323
|
}
|
|
16245
16324
|
try {
|
|
16246
|
-
if (!
|
|
16325
|
+
if (!existsSync3(RESTART_FLAGS_DIR)) {
|
|
16247
16326
|
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
16248
16327
|
}
|
|
16249
|
-
const flagPath =
|
|
16328
|
+
const flagPath = join5(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
16250
16329
|
const flag = {
|
|
16251
16330
|
codeName,
|
|
16252
16331
|
source: "slack",
|
|
@@ -16350,10 +16429,10 @@ async function handleHelpCommand(opts) {
|
|
|
16350
16429
|
async function handleRestartCommand(opts) {
|
|
16351
16430
|
const codeName = AGENT_CODE_NAME ?? "unknown";
|
|
16352
16431
|
try {
|
|
16353
|
-
if (!
|
|
16432
|
+
if (!existsSync3(RESTART_FLAGS_DIR)) {
|
|
16354
16433
|
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
16355
16434
|
}
|
|
16356
|
-
const flagPath =
|
|
16435
|
+
const flagPath = join5(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
16357
16436
|
const flag = {
|
|
16358
16437
|
codeName,
|
|
16359
16438
|
source: "slack",
|
|
@@ -16412,7 +16491,7 @@ var THREAD_STORE_TTL_DAYS = parseTtlDays(process.env.SLACK_THREAD_FOLLOW_TTL_DAY
|
|
|
16412
16491
|
var threadPersister = null;
|
|
16413
16492
|
function resolveThreadStorePath() {
|
|
16414
16493
|
if (!AGENT_CODE_NAME) return null;
|
|
16415
|
-
return
|
|
16494
|
+
return join5(homedir2(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
|
|
16416
16495
|
}
|
|
16417
16496
|
function parseTtlDays(raw) {
|
|
16418
16497
|
if (!raw) return void 0;
|
|
@@ -16447,9 +16526,9 @@ if (!BOT_TOKEN || !APP_TOKEN) {
|
|
|
16447
16526
|
var slackStderrLogStream = null;
|
|
16448
16527
|
if (AGENT_CODE_NAME) {
|
|
16449
16528
|
try {
|
|
16450
|
-
const logDir =
|
|
16529
|
+
const logDir = join5(homedir2(), ".augmented", AGENT_CODE_NAME);
|
|
16451
16530
|
mkdirSync3(logDir, { recursive: true });
|
|
16452
|
-
slackStderrLogStream = createWriteStream(
|
|
16531
|
+
slackStderrLogStream = createWriteStream(join5(logDir, "slack-channel-stderr.log"), {
|
|
16453
16532
|
flags: "a",
|
|
16454
16533
|
mode: 384
|
|
16455
16534
|
});
|
|
@@ -16533,7 +16612,7 @@ var mcp = new Server(
|
|
|
16533
16612
|
// Highest-priority lines first — Claude Code truncates this string at
|
|
16534
16613
|
// 2048 chars, so anything appended late silently disappears.
|
|
16535
16614
|
"CRITICAL: every response to a Slack <channel> tag MUST go through slack.reply. Text in your session WITHOUT a slack.reply call never reaches the user \u2014 the message dies inside the agent process.",
|
|
16536
|
-
`Inbound: <channel ... thread_ts="..." [thread_context="..."]>. Pass channel +
|
|
16615
|
+
`Inbound: <channel ... thread_ts="..." message_ts="..." [thread_context="..."]>. Pass channel + message_ts to slack.reply (and thread_ts on threads). thread_context = thread pre-loaded; ground replies ONLY in it, never another channel's.`,
|
|
16537
16616
|
"Inbound attachments: <channel> `files` is a JSON-serialised array \u2014 JSON.parse it. If an entry has `path`, the image is already downloaded \u2014 Read it directly, do NOT call slack.download_attachment. Use that tool only for entries with `file_id` but NO `path` (PDF, docx, csv): pass file_id + channel verbatim, then Read the returned path. Single-image messages also get a top-level `image_path`. Don't surface internal file-handling errors that don't affect the answer.",
|
|
16538
16617
|
"Address users by user_name, never by raw user ID. In multi-participant threads the CURRENT speaker is the one on the latest <channel> tag.",
|
|
16539
16618
|
'Mentioned in a channel \u2192 respond in that thread. DM \u2192 respond directly. auto_followed="true" \u2192 only reply if useful, OR if your own bot user is @-mentioned (counts even in auto_followed).',
|
|
@@ -16558,7 +16637,18 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
16558
16637
|
text: { type: "string", description: "The message to send" },
|
|
16559
16638
|
thread_ts: {
|
|
16560
16639
|
type: "string",
|
|
16561
|
-
description: "Thread timestamp for threaded replies (from the thread_ts attribute)"
|
|
16640
|
+
description: "Thread timestamp for threaded replies (from the thread_ts attribute). Omit for a top-level reply (e.g. a fresh DM)."
|
|
16641
|
+
},
|
|
16642
|
+
// ENG-5861: explicit message_ts so the cleanup gate can match the
|
|
16643
|
+
// exact pending-inbound marker — necessary because top-level DM
|
|
16644
|
+
// replies legitimately have no thread_ts and the marker filename
|
|
16645
|
+
// is `<channel>__<thread_ts>__<message_ts>.json`. Without this
|
|
16646
|
+
// the marker sat undrained, eventually tripping ENG-5832\'s
|
|
16647
|
+
// "wedged" threshold and firing a false-positive red ✗ on the
|
|
16648
|
+
// next inbound.
|
|
16649
|
+
message_ts: {
|
|
16650
|
+
type: "string",
|
|
16651
|
+
description: "The message_ts of the specific inbound this reply addresses (from the message_ts attribute on the <channel> tag). Used by the pending-inbound cleanup gate so the marker is reliably cleared even for top-level DMs that have no thread_ts."
|
|
16562
16652
|
}
|
|
16563
16653
|
},
|
|
16564
16654
|
required: ["channel", "text"]
|
|
@@ -16796,7 +16886,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
16796
16886
|
return buildImpersonationRefusal(name);
|
|
16797
16887
|
}
|
|
16798
16888
|
if (name === "slack.reply") {
|
|
16799
|
-
const { channel, text, thread_ts } = args;
|
|
16889
|
+
const { channel, text, thread_ts, message_ts } = args;
|
|
16800
16890
|
if (channel && thread_ts) {
|
|
16801
16891
|
const killed = await isThreadKilled({
|
|
16802
16892
|
channelType: "slack",
|
|
@@ -16855,8 +16945,15 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
16855
16945
|
isError: true
|
|
16856
16946
|
};
|
|
16857
16947
|
}
|
|
16858
|
-
if (channel
|
|
16859
|
-
|
|
16948
|
+
if (channel) {
|
|
16949
|
+
if (message_ts) {
|
|
16950
|
+
clearSlackPendingMarkerByMessageTs2(channel, message_ts);
|
|
16951
|
+
if (thread_ts) clearPendingMessage(channel, thread_ts);
|
|
16952
|
+
} else if (thread_ts) {
|
|
16953
|
+
clearPendingMessage(channel, thread_ts);
|
|
16954
|
+
} else {
|
|
16955
|
+
clearOldestSlackPendingMarkerInChannel2(channel);
|
|
16956
|
+
}
|
|
16860
16957
|
}
|
|
16861
16958
|
try {
|
|
16862
16959
|
const res = await fetch("https://slack.com/api/chat.postMessage", {
|
|
@@ -17008,7 +17105,7 @@ ${result.formatted}` : "Thread is empty or not found."
|
|
|
17008
17105
|
let bytes;
|
|
17009
17106
|
let size;
|
|
17010
17107
|
try {
|
|
17011
|
-
const stat =
|
|
17108
|
+
const stat = statSync2(resolvedPath);
|
|
17012
17109
|
if (!stat.isFile()) {
|
|
17013
17110
|
return {
|
|
17014
17111
|
content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }],
|
|
@@ -18056,13 +18153,22 @@ async function connectSocketMode() {
|
|
|
18056
18153
|
botUserId
|
|
18057
18154
|
});
|
|
18058
18155
|
const ackProbe = process.env.TMUX && AGENT_CODE_NAME ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
|
|
18156
|
+
let paneLogFreshAgeMs = null;
|
|
18157
|
+
if (SLACK_AGENT_DIR) {
|
|
18158
|
+
try {
|
|
18159
|
+
const paneMtimeMs = statSync2(join5(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
|
|
18160
|
+
paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
|
|
18161
|
+
} catch {
|
|
18162
|
+
}
|
|
18163
|
+
}
|
|
18059
18164
|
const ackDecision = decideAckReaction({
|
|
18060
18165
|
hasTarget: decideSlackAckReaction({ channel, ts }),
|
|
18061
18166
|
integrationReady: Boolean(BOT_TOKEN),
|
|
18062
18167
|
tmux: ackProbe.tmux,
|
|
18063
18168
|
claude: ackProbe.claude,
|
|
18064
18169
|
withinStartupGrace: process.uptime() * 1e3 < ACK_STARTUP_GRACE_MS,
|
|
18065
|
-
oldestPendingAgeMs: oldestPendingMarkerAgeMs(SLACK_PENDING_INBOUND_DIR)
|
|
18170
|
+
oldestPendingAgeMs: oldestPendingMarkerAgeMs(SLACK_PENDING_INBOUND_DIR),
|
|
18171
|
+
paneLogFreshAgeMs
|
|
18066
18172
|
});
|
|
18067
18173
|
if (ackDecision !== "none") {
|
|
18068
18174
|
const reactionName = ackDecision === "undeliverable" ? "x" : "eyes";
|
|
@@ -18117,6 +18223,13 @@ async function connectSocketMode() {
|
|
|
18117
18223
|
user_name: userName,
|
|
18118
18224
|
channel,
|
|
18119
18225
|
thread_ts: threadTs,
|
|
18226
|
+
// ENG-5861: explicit message_ts so the agent can pass it back
|
|
18227
|
+
// to slack.reply for reliable pending-inbound cleanup. For
|
|
18228
|
+
// top-level messages threadTs === ts; for threaded replies
|
|
18229
|
+
// threadTs is the thread root and message_ts is the specific
|
|
18230
|
+
// reply being addressed — different keys, both needed by the
|
|
18231
|
+
// marker-cleanup gate.
|
|
18232
|
+
message_ts: ts,
|
|
18120
18233
|
event_type: evt.type,
|
|
18121
18234
|
...isAutoFollowed ? { auto_followed: "true" } : {},
|
|
18122
18235
|
// Only set these when we actually have attachments to avoid
|
|
@@ -15506,6 +15506,7 @@ import { readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
|
15506
15506
|
import { join as join3 } from "path";
|
|
15507
15507
|
var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
15508
15508
|
var ACK_STARTUP_GRACE_MS = 6e4;
|
|
15509
|
+
var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
|
|
15509
15510
|
function decideAckReaction(i) {
|
|
15510
15511
|
if (!i.hasTarget) return "none";
|
|
15511
15512
|
if (!i.integrationReady) return "undeliverable";
|
|
@@ -15513,6 +15514,11 @@ function decideAckReaction(i) {
|
|
|
15513
15514
|
if (!i.withinStartupGrace && i.claude === "dead") return "undeliverable";
|
|
15514
15515
|
const threshold = i.pendingStaleThresholdMs ?? REPLY_WEDGED_THRESHOLD_MS;
|
|
15515
15516
|
if (i.oldestPendingAgeMs != null && i.oldestPendingAgeMs > threshold) {
|
|
15517
|
+
const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
|
|
15518
|
+
const paneIsFresh = i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
|
|
15519
|
+
if (paneIsFresh && i.tmux === "alive" && i.claude === "alive") {
|
|
15520
|
+
return "ack";
|
|
15521
|
+
}
|
|
15516
15522
|
return "undeliverable";
|
|
15517
15523
|
}
|
|
15518
15524
|
return "ack";
|