@integrity-labs/agt-cli 0.27.14 → 0.27.15
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-5ZUNHYKV.js → chunk-F4NG4EXD.js} +36 -27
- package/dist/chunk-F4NG4EXD.js.map +1 -0
- package/dist/{chunk-HKZFMGYE.js → chunk-LJEV2QHN.js} +1 -1
- package/dist/{claude-pair-runtime-ISBA6RDU.js → claude-pair-runtime-OBAJZDXK.js} +2 -2
- package/dist/lib/manager-worker.js +6 -6
- package/dist/mcp/slack-channel.js +146 -40
- package/dist/mcp/telegram-channel.js +174 -27
- package/dist/{persistent-session-N6SYAERB.js → persistent-session-SBSOZG74.js} +2 -2
- package/dist/{responsiveness-probe-GPRQBBZG.js → responsiveness-probe-DU4IJ2RZ.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-5ZUNHYKV.js.map +0 -1
- /package/dist/{chunk-HKZFMGYE.js.map → chunk-LJEV2QHN.js.map} +0 -0
- /package/dist/{claude-pair-runtime-ISBA6RDU.js.map → claude-pair-runtime-OBAJZDXK.js.map} +0 -0
- /package/dist/{persistent-session-N6SYAERB.js.map → persistent-session-SBSOZG74.js.map} +0 -0
- /package/dist/{responsiveness-probe-GPRQBBZG.js.map → responsiveness-probe-DU4IJ2RZ.js.map} +0 -0
|
@@ -14249,6 +14249,102 @@ function decideSenderPolicyForward(evt, policy) {
|
|
|
14249
14249
|
return { forward: true };
|
|
14250
14250
|
}
|
|
14251
14251
|
|
|
14252
|
+
// src/ack-reaction.ts
|
|
14253
|
+
import { readdirSync, readFileSync } from "fs";
|
|
14254
|
+
import { join } from "path";
|
|
14255
|
+
var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
14256
|
+
var ACK_STARTUP_GRACE_MS = 6e4;
|
|
14257
|
+
function decideAckReaction(i) {
|
|
14258
|
+
if (!i.hasTarget) return "none";
|
|
14259
|
+
if (!i.integrationReady) return "undeliverable";
|
|
14260
|
+
if (i.tmux === "dead") return "undeliverable";
|
|
14261
|
+
if (!i.withinStartupGrace && i.claude === "dead") return "undeliverable";
|
|
14262
|
+
const threshold = i.pendingStaleThresholdMs ?? REPLY_WEDGED_THRESHOLD_MS;
|
|
14263
|
+
if (i.oldestPendingAgeMs != null && i.oldestPendingAgeMs > threshold) {
|
|
14264
|
+
return "undeliverable";
|
|
14265
|
+
}
|
|
14266
|
+
return "ack";
|
|
14267
|
+
}
|
|
14268
|
+
function oldestPendingMarkerAgeMs(dir, now = Date.now()) {
|
|
14269
|
+
if (!dir) return null;
|
|
14270
|
+
let names;
|
|
14271
|
+
try {
|
|
14272
|
+
names = readdirSync(dir);
|
|
14273
|
+
} catch {
|
|
14274
|
+
return null;
|
|
14275
|
+
}
|
|
14276
|
+
let oldest = null;
|
|
14277
|
+
for (const name of names) {
|
|
14278
|
+
if (!name.endsWith(".json")) continue;
|
|
14279
|
+
let receivedAt;
|
|
14280
|
+
try {
|
|
14281
|
+
const raw = JSON.parse(readFileSync(join(dir, name), "utf-8"));
|
|
14282
|
+
receivedAt = raw.received_at;
|
|
14283
|
+
} catch {
|
|
14284
|
+
continue;
|
|
14285
|
+
}
|
|
14286
|
+
if (!receivedAt) continue;
|
|
14287
|
+
const t = Date.parse(receivedAt);
|
|
14288
|
+
if (Number.isNaN(t)) continue;
|
|
14289
|
+
const age = now - t;
|
|
14290
|
+
if (age < 0) continue;
|
|
14291
|
+
if (oldest == null || age > oldest) oldest = age;
|
|
14292
|
+
}
|
|
14293
|
+
return oldest;
|
|
14294
|
+
}
|
|
14295
|
+
|
|
14296
|
+
// src/session-probe-runtime.ts
|
|
14297
|
+
import { execFileSync } from "child_process";
|
|
14298
|
+
function agentTmuxSessionName(codeName) {
|
|
14299
|
+
return `agt-${codeName}`;
|
|
14300
|
+
}
|
|
14301
|
+
function escapePgrepRegex(value) {
|
|
14302
|
+
return value.replace(/[.[\]{}()*+?^$|\\]/g, "\\$&");
|
|
14303
|
+
}
|
|
14304
|
+
function probeClaudeProcessInTmux(tmuxSession) {
|
|
14305
|
+
const escapedSession = escapePgrepRegex(tmuxSession);
|
|
14306
|
+
const pattern = `(^|[[:space:]])--name ${escapedSession}([[:space:]]|$)`;
|
|
14307
|
+
try {
|
|
14308
|
+
const out = execFileSync("pgrep", ["-f", "--", pattern], {
|
|
14309
|
+
encoding: "utf-8",
|
|
14310
|
+
timeout: 3e3
|
|
14311
|
+
}).trim();
|
|
14312
|
+
return out.length > 0 ? "alive" : "dead";
|
|
14313
|
+
} catch (err) {
|
|
14314
|
+
const e = err;
|
|
14315
|
+
if (e?.code === "ENOENT") return "unknown";
|
|
14316
|
+
return e?.status === 1 ? "dead" : "unknown";
|
|
14317
|
+
}
|
|
14318
|
+
}
|
|
14319
|
+
function probeTmuxSession(tmuxSession) {
|
|
14320
|
+
try {
|
|
14321
|
+
execFileSync("tmux", ["has-session", "-t", tmuxSession], {
|
|
14322
|
+
stdio: "ignore",
|
|
14323
|
+
timeout: 3e3
|
|
14324
|
+
});
|
|
14325
|
+
return "alive";
|
|
14326
|
+
} catch (err) {
|
|
14327
|
+
const e = err;
|
|
14328
|
+
if (e?.code === "ENOENT") return "unknown";
|
|
14329
|
+
return "dead";
|
|
14330
|
+
}
|
|
14331
|
+
}
|
|
14332
|
+
function probeAgentSession(codeName) {
|
|
14333
|
+
const session = agentTmuxSessionName(codeName);
|
|
14334
|
+
const tmux = probeTmuxSession(session);
|
|
14335
|
+
const claude = tmux === "alive" ? probeClaudeProcessInTmux(session) : tmux;
|
|
14336
|
+
return { tmux, claude };
|
|
14337
|
+
}
|
|
14338
|
+
var probeCache = /* @__PURE__ */ new Map();
|
|
14339
|
+
var SESSION_PROBE_TTL_MS = 15e3;
|
|
14340
|
+
function probeAgentSessionCached(codeName, ttlMs = SESSION_PROBE_TTL_MS, now = Date.now()) {
|
|
14341
|
+
const cached2 = probeCache.get(codeName);
|
|
14342
|
+
if (cached2 && now - cached2.at < ttlMs) return cached2.value;
|
|
14343
|
+
const value = probeAgentSession(codeName);
|
|
14344
|
+
probeCache.set(codeName, { at: now, value });
|
|
14345
|
+
return value;
|
|
14346
|
+
}
|
|
14347
|
+
|
|
14252
14348
|
// src/slack-loop-throttle.ts
|
|
14253
14349
|
var DEFAULT_THROTTLE = {
|
|
14254
14350
|
threshold: 3,
|
|
@@ -15016,20 +15112,20 @@ import {
|
|
|
15016
15112
|
createWriteStream,
|
|
15017
15113
|
existsSync as existsSync2,
|
|
15018
15114
|
mkdirSync as mkdirSync3,
|
|
15019
|
-
readFileSync as
|
|
15020
|
-
readdirSync,
|
|
15115
|
+
readFileSync as readFileSync4,
|
|
15116
|
+
readdirSync as readdirSync2,
|
|
15021
15117
|
renameSync as renameSync2,
|
|
15022
15118
|
statSync,
|
|
15023
15119
|
unlinkSync as unlinkSync2,
|
|
15024
15120
|
watch,
|
|
15025
15121
|
writeFileSync as writeFileSync3
|
|
15026
15122
|
} from "fs";
|
|
15027
|
-
import { basename, join as
|
|
15123
|
+
import { basename, join as join4, resolve as resolve2 } from "path";
|
|
15028
15124
|
import { homedir as homedir2 } from "os";
|
|
15029
15125
|
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
15030
15126
|
|
|
15031
15127
|
// src/slack-thread-store.ts
|
|
15032
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
15128
|
+
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
15033
15129
|
import { dirname } from "path";
|
|
15034
15130
|
var FILE_VERSION = 1;
|
|
15035
15131
|
var DEFAULT_TTL_DAYS = 30;
|
|
@@ -15040,7 +15136,7 @@ function loadThreadStore(filePath, opts = {}) {
|
|
|
15040
15136
|
const ttlMs = ttlDays * 24 * 60 * 60 * 1e3;
|
|
15041
15137
|
let raw;
|
|
15042
15138
|
try {
|
|
15043
|
-
raw =
|
|
15139
|
+
raw = readFileSync2(filePath, "utf-8");
|
|
15044
15140
|
} catch {
|
|
15045
15141
|
return { threads: /* @__PURE__ */ new Map(), pruned: 0 };
|
|
15046
15142
|
}
|
|
@@ -15155,9 +15251,9 @@ async function runOrRetry(fn, opts) {
|
|
|
15155
15251
|
|
|
15156
15252
|
// src/channel-attachments.ts
|
|
15157
15253
|
import { homedir } from "os";
|
|
15158
|
-
import { join, resolve, sep } from "path";
|
|
15254
|
+
import { join as join2, resolve, sep } from "path";
|
|
15159
15255
|
function resolveChannelInboundDir(codeName, channelSlug) {
|
|
15160
|
-
const base =
|
|
15256
|
+
const base = join2(homedir(), ".augmented");
|
|
15161
15257
|
const allowedSegment = /^[A-Za-z0-9_-]+$/;
|
|
15162
15258
|
if (!allowedSegment.test(codeName) || !allowedSegment.test(channelSlug)) {
|
|
15163
15259
|
throw new Error(
|
|
@@ -15803,12 +15899,12 @@ function createSlackBotUserIdClient(args) {
|
|
|
15803
15899
|
import {
|
|
15804
15900
|
existsSync,
|
|
15805
15901
|
mkdirSync as mkdirSync2,
|
|
15806
|
-
readFileSync as
|
|
15902
|
+
readFileSync as readFileSync3,
|
|
15807
15903
|
renameSync,
|
|
15808
15904
|
unlinkSync,
|
|
15809
15905
|
writeFileSync as writeFileSync2
|
|
15810
15906
|
} from "fs";
|
|
15811
|
-
import { join as
|
|
15907
|
+
import { join as join3 } from "path";
|
|
15812
15908
|
function defaultIsPidAlive(pid) {
|
|
15813
15909
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
15814
15910
|
try {
|
|
@@ -15826,7 +15922,7 @@ function acquireMcpSpawnLock(args) {
|
|
|
15826
15922
|
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
15827
15923
|
const selfPid = options.selfPid ?? process.pid;
|
|
15828
15924
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
15829
|
-
const path =
|
|
15925
|
+
const path = join3(agentDir, basename2);
|
|
15830
15926
|
const existing = readLockHolder(path);
|
|
15831
15927
|
if (existing) {
|
|
15832
15928
|
if (existing.pid === selfPid) {
|
|
@@ -15857,7 +15953,7 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
|
15857
15953
|
function readLockHolder(path) {
|
|
15858
15954
|
if (!existsSync(path)) return null;
|
|
15859
15955
|
try {
|
|
15860
|
-
const raw =
|
|
15956
|
+
const raw = readFileSync3(path, "utf8");
|
|
15861
15957
|
const parsed = JSON.parse(raw);
|
|
15862
15958
|
const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
|
|
15863
15959
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
@@ -15939,9 +16035,9 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
|
|
|
15939
16035
|
peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
|
|
15940
16036
|
peer_disabled_mode: SLACK_PEER_DISABLED_MODE
|
|
15941
16037
|
};
|
|
15942
|
-
var SLACK_AGENT_DIR = AGENT_CODE_NAME ?
|
|
15943
|
-
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ?
|
|
15944
|
-
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ?
|
|
16038
|
+
var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join4(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
16039
|
+
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join4(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
|
|
16040
|
+
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join4(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
|
|
15945
16041
|
var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
|
|
15946
16042
|
function redactSlackId(id) {
|
|
15947
16043
|
if (!id) return "<none>";
|
|
@@ -15953,7 +16049,7 @@ function safeSlackMarkerName(channel, threadTs, messageTs) {
|
|
|
15953
16049
|
}
|
|
15954
16050
|
function slackPendingInboundPath(channel, threadTs, messageTs) {
|
|
15955
16051
|
if (!SLACK_PENDING_INBOUND_DIR) return null;
|
|
15956
|
-
return
|
|
16052
|
+
return join4(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
|
|
15957
16053
|
}
|
|
15958
16054
|
function writeSlackPendingInboundMarker(channel, threadTs, messageTs) {
|
|
15959
16055
|
const path = slackPendingInboundPath(channel, threadTs, messageTs);
|
|
@@ -15980,10 +16076,10 @@ function clearAllSlackPendingMarkersForThread(channel, threadTs) {
|
|
|
15980
16076
|
const safeThread = threadTs.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
15981
16077
|
const prefix = `${safeChan}__${safeThread}__`;
|
|
15982
16078
|
try {
|
|
15983
|
-
for (const f of
|
|
16079
|
+
for (const f of readdirSync2(SLACK_PENDING_INBOUND_DIR)) {
|
|
15984
16080
|
if (!f.startsWith(prefix) || !f.endsWith(".json")) continue;
|
|
15985
16081
|
try {
|
|
15986
|
-
unlinkSync2(
|
|
16082
|
+
unlinkSync2(join4(SLACK_PENDING_INBOUND_DIR, f));
|
|
15987
16083
|
} catch {
|
|
15988
16084
|
}
|
|
15989
16085
|
}
|
|
@@ -16004,10 +16100,10 @@ function slackNextRetryName(filename) {
|
|
|
16004
16100
|
async function processSlackRecoveryOutboxFile(filename) {
|
|
16005
16101
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
16006
16102
|
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
|
|
16007
|
-
const fullPath =
|
|
16103
|
+
const fullPath = join4(SLACK_RECOVERY_OUTBOX_DIR, filename);
|
|
16008
16104
|
let payload;
|
|
16009
16105
|
try {
|
|
16010
|
-
payload = JSON.parse(
|
|
16106
|
+
payload = JSON.parse(readFileSync4(fullPath, "utf-8"));
|
|
16011
16107
|
} catch (err) {
|
|
16012
16108
|
process.stderr.write(
|
|
16013
16109
|
`slack-channel(${AGENT_CODE_NAME}): recovery outbox parse failed (${filename}): ${err.message}
|
|
@@ -16081,7 +16177,7 @@ async function processSlackRecoveryOutboxFile(filename) {
|
|
|
16081
16177
|
const next = slackNextRetryName(filename);
|
|
16082
16178
|
if (next) {
|
|
16083
16179
|
try {
|
|
16084
|
-
renameSync2(fullPath,
|
|
16180
|
+
renameSync2(fullPath, join4(SLACK_RECOVERY_OUTBOX_DIR, next.next));
|
|
16085
16181
|
if (next.attempt >= SLACK_MAX_RECOVERY_ATTEMPTS) {
|
|
16086
16182
|
process.stderr.write(
|
|
16087
16183
|
`slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
|
|
@@ -16111,7 +16207,7 @@ function scanSlackRecoveryRetries() {
|
|
|
16111
16207
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
16112
16208
|
let entries;
|
|
16113
16209
|
try {
|
|
16114
|
-
entries =
|
|
16210
|
+
entries = readdirSync2(SLACK_RECOVERY_OUTBOX_DIR);
|
|
16115
16211
|
} catch {
|
|
16116
16212
|
return;
|
|
16117
16213
|
}
|
|
@@ -16120,7 +16216,7 @@ function scanSlackRecoveryRetries() {
|
|
|
16120
16216
|
if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
|
|
16121
16217
|
let mtimeMs;
|
|
16122
16218
|
try {
|
|
16123
|
-
mtimeMs = statSync(
|
|
16219
|
+
mtimeMs = statSync(join4(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
|
|
16124
16220
|
} catch {
|
|
16125
16221
|
continue;
|
|
16126
16222
|
}
|
|
@@ -16141,7 +16237,7 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
16141
16237
|
return;
|
|
16142
16238
|
}
|
|
16143
16239
|
try {
|
|
16144
|
-
for (const f of
|
|
16240
|
+
for (const f of readdirSync2(SLACK_RECOVERY_OUTBOX_DIR)) {
|
|
16145
16241
|
if (isFirstAttemptSlackOutboxFile(f)) void processSlackRecoveryOutboxFile(f);
|
|
16146
16242
|
}
|
|
16147
16243
|
} catch {
|
|
@@ -16150,7 +16246,7 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
16150
16246
|
const watcher = watch(SLACK_RECOVERY_OUTBOX_DIR, (event, filename) => {
|
|
16151
16247
|
if (event !== "rename" || !filename) return;
|
|
16152
16248
|
if (!isFirstAttemptSlackOutboxFile(filename)) return;
|
|
16153
|
-
if (existsSync2(
|
|
16249
|
+
if (existsSync2(join4(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
|
|
16154
16250
|
void processSlackRecoveryOutboxFile(filename);
|
|
16155
16251
|
}
|
|
16156
16252
|
});
|
|
@@ -16174,7 +16270,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16174
16270
|
if (!existsSync2(SLACK_PENDING_INBOUND_DIR)) return;
|
|
16175
16271
|
let filenames;
|
|
16176
16272
|
try {
|
|
16177
|
-
filenames =
|
|
16273
|
+
filenames = readdirSync2(SLACK_PENDING_INBOUND_DIR);
|
|
16178
16274
|
} catch (err) {
|
|
16179
16275
|
process.stderr.write(
|
|
16180
16276
|
`slack-channel(${AGENT_CODE_NAME}): stale-marker readdir failed: ${err.message}
|
|
@@ -16187,10 +16283,10 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16187
16283
|
for (const filename of filenames) {
|
|
16188
16284
|
if (!filename.endsWith(".json")) continue;
|
|
16189
16285
|
if (filename.endsWith(".tmp")) continue;
|
|
16190
|
-
const fullPath =
|
|
16286
|
+
const fullPath = join4(SLACK_PENDING_INBOUND_DIR, filename);
|
|
16191
16287
|
let marker;
|
|
16192
16288
|
try {
|
|
16193
|
-
marker = JSON.parse(
|
|
16289
|
+
marker = JSON.parse(readFileSync4(fullPath, "utf-8"));
|
|
16194
16290
|
} catch (err) {
|
|
16195
16291
|
process.stderr.write(
|
|
16196
16292
|
`slack-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactSlackId(filename)}: ${err.message}
|
|
@@ -16243,7 +16339,7 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
|
16243
16339
|
if (!existsSync2(SLACK_PENDING_INBOUND_DIR)) return;
|
|
16244
16340
|
let filenames;
|
|
16245
16341
|
try {
|
|
16246
|
-
filenames =
|
|
16342
|
+
filenames = readdirSync2(SLACK_PENDING_INBOUND_DIR);
|
|
16247
16343
|
} catch {
|
|
16248
16344
|
return;
|
|
16249
16345
|
}
|
|
@@ -16255,12 +16351,12 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
|
16255
16351
|
if (!filename.startsWith(channelPrefix)) continue;
|
|
16256
16352
|
if (!filename.endsWith(messageSuffix)) continue;
|
|
16257
16353
|
try {
|
|
16258
|
-
unlinkSync2(
|
|
16354
|
+
unlinkSync2(join4(SLACK_PENDING_INBOUND_DIR, filename));
|
|
16259
16355
|
} catch {
|
|
16260
16356
|
}
|
|
16261
16357
|
}
|
|
16262
16358
|
}
|
|
16263
|
-
var RESTART_FLAGS_DIR =
|
|
16359
|
+
var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
|
|
16264
16360
|
function buildAugmentedSlackMetadata() {
|
|
16265
16361
|
if (!AGT_TEAM_ID) return void 0;
|
|
16266
16362
|
return {
|
|
@@ -16442,7 +16538,7 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
16442
16538
|
if (!existsSync2(RESTART_FLAGS_DIR)) {
|
|
16443
16539
|
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
16444
16540
|
}
|
|
16445
|
-
const flagPath =
|
|
16541
|
+
const flagPath = join4(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
16446
16542
|
const flag = {
|
|
16447
16543
|
codeName,
|
|
16448
16544
|
source: "slack",
|
|
@@ -16549,7 +16645,7 @@ async function handleRestartCommand(opts) {
|
|
|
16549
16645
|
if (!existsSync2(RESTART_FLAGS_DIR)) {
|
|
16550
16646
|
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
16551
16647
|
}
|
|
16552
|
-
const flagPath =
|
|
16648
|
+
const flagPath = join4(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
16553
16649
|
const flag = {
|
|
16554
16650
|
codeName,
|
|
16555
16651
|
source: "slack",
|
|
@@ -16608,7 +16704,7 @@ var THREAD_STORE_TTL_DAYS = parseTtlDays(process.env.SLACK_THREAD_FOLLOW_TTL_DAY
|
|
|
16608
16704
|
var threadPersister = null;
|
|
16609
16705
|
function resolveThreadStorePath() {
|
|
16610
16706
|
if (!AGENT_CODE_NAME) return null;
|
|
16611
|
-
return
|
|
16707
|
+
return join4(homedir2(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
|
|
16612
16708
|
}
|
|
16613
16709
|
function parseTtlDays(raw) {
|
|
16614
16710
|
if (!raw) return void 0;
|
|
@@ -16643,9 +16739,9 @@ if (!BOT_TOKEN || !APP_TOKEN) {
|
|
|
16643
16739
|
var slackStderrLogStream = null;
|
|
16644
16740
|
if (AGENT_CODE_NAME) {
|
|
16645
16741
|
try {
|
|
16646
|
-
const logDir =
|
|
16742
|
+
const logDir = join4(homedir2(), ".augmented", AGENT_CODE_NAME);
|
|
16647
16743
|
mkdirSync3(logDir, { recursive: true });
|
|
16648
|
-
slackStderrLogStream = createWriteStream(
|
|
16744
|
+
slackStderrLogStream = createWriteStream(join4(logDir, "slack-channel-stderr.log"), {
|
|
16649
16745
|
flags: "a",
|
|
16650
16746
|
mode: 384
|
|
16651
16747
|
});
|
|
@@ -16734,7 +16830,7 @@ var mcp = new Server(
|
|
|
16734
16830
|
"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.",
|
|
16735
16831
|
"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.",
|
|
16736
16832
|
'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).',
|
|
16737
|
-
"Reaction taxonomy (use slack.react sparingly \u2014 prefer a reply): \u{1F440} = ack (already auto-added on inbound, do not duplicate); \u2705 = success. NEVER react to signal failure \u2014 users can't tell why something failed from an emoji. On failure, slack.reply with one sentence explaining what went wrong (no stack traces, no secrets).",
|
|
16833
|
+
"Reaction taxonomy (use slack.react sparingly \u2014 prefer a reply): \u{1F440} = ack (already auto-added on inbound, do not duplicate); \u2705 = success. NEVER react to signal failure of YOUR work \u2014 users can't tell why something failed from an emoji. On failure, slack.reply with one sentence explaining what went wrong (no stack traces, no secrets). (The \u274C you may see on an inbound is applied by the system, not you \u2014 it marks a message that arrived while the agent couldn't reply; never add \u274C yourself.)",
|
|
16738
16834
|
`When a thread message is NOT addressed to you (different @-mention, side conversation, auto_followed catch-up): SILENTLY SKIP \u2014 no reaction, no reply, no "this wasn't for me" message.`,
|
|
16739
16835
|
"To deliver a file: save under your project dir, call slack.upload_file with path + channel + thread_ts."
|
|
16740
16836
|
].join(" ")
|
|
@@ -16786,7 +16882,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
16786
16882
|
},
|
|
16787
16883
|
{
|
|
16788
16884
|
name: "slack.react",
|
|
16789
|
-
description: "Add an emoji reaction to a Slack message. Use sparingly \u2014 prefer a text reply. Reaction taxonomy: \u2705 = action completed successfully. NEVER react to signal failure \u2014 users can't tell why it failed from a reaction. On failure, call slack.reply with one sentence explaining what went wrong instead. \u{1F440} (eyes) is already auto-applied on inbound; do not duplicate.",
|
|
16885
|
+
description: "Add an emoji reaction to a Slack message. Use sparingly \u2014 prefer a text reply. Reaction taxonomy: \u2705 = action completed successfully. NEVER react to signal failure of your work \u2014 users can't tell why it failed from a reaction. On failure, call slack.reply with one sentence explaining what went wrong instead. \u{1F440} (eyes) is already auto-applied on inbound; do not duplicate. \u274C (:x:) is reserved for the system to mark an inbound that arrived while the agent couldn't reply \u2014 never add \u274C yourself.",
|
|
16790
16886
|
inputSchema: {
|
|
16791
16887
|
type: "object",
|
|
16792
16888
|
properties: {
|
|
@@ -17238,7 +17334,7 @@ ${result.formatted}` : "Thread is empty or not found."
|
|
|
17238
17334
|
};
|
|
17239
17335
|
}
|
|
17240
17336
|
size = stat.size;
|
|
17241
|
-
bytes =
|
|
17337
|
+
bytes = readFileSync4(resolvedPath);
|
|
17242
17338
|
} catch (err) {
|
|
17243
17339
|
return {
|
|
17244
17340
|
content: [{ type: "text", text: `Failed to read file: ${err.message}` }],
|
|
@@ -18258,14 +18354,24 @@ async function connectSocketMode() {
|
|
|
18258
18354
|
isAutoFollowed,
|
|
18259
18355
|
botUserId
|
|
18260
18356
|
});
|
|
18261
|
-
|
|
18357
|
+
const ackProbe = process.env.TMUX && AGENT_CODE_NAME ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
|
|
18358
|
+
const ackDecision = decideAckReaction({
|
|
18359
|
+
hasTarget: decideSlackAckReaction({ channel, ts }),
|
|
18360
|
+
integrationReady: Boolean(BOT_TOKEN),
|
|
18361
|
+
tmux: ackProbe.tmux,
|
|
18362
|
+
claude: ackProbe.claude,
|
|
18363
|
+
withinStartupGrace: process.uptime() * 1e3 < ACK_STARTUP_GRACE_MS,
|
|
18364
|
+
oldestPendingAgeMs: oldestPendingMarkerAgeMs(SLACK_PENDING_INBOUND_DIR)
|
|
18365
|
+
});
|
|
18366
|
+
if (ackDecision !== "none") {
|
|
18367
|
+
const reactionName = ackDecision === "undeliverable" ? "x" : "eyes";
|
|
18262
18368
|
fetch("https://slack.com/api/reactions.add", {
|
|
18263
18369
|
method: "POST",
|
|
18264
18370
|
headers: {
|
|
18265
18371
|
"Content-Type": "application/json",
|
|
18266
18372
|
Authorization: `Bearer ${BOT_TOKEN}`
|
|
18267
18373
|
},
|
|
18268
|
-
body: JSON.stringify({ channel, timestamp: ts, name:
|
|
18374
|
+
body: JSON.stringify({ channel, timestamp: ts, name: reactionName })
|
|
18269
18375
|
}).catch(() => {
|
|
18270
18376
|
});
|
|
18271
18377
|
}
|