@integrity-labs/agt-cli 0.27.14 → 0.27.16
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-HKZFMGYE.js → chunk-6D55CCVZ.js} +2 -38
- package/dist/chunk-6D55CCVZ.js.map +1 -0
- package/dist/{chunk-5ZUNHYKV.js → chunk-F4NG4EXD.js} +36 -27
- package/dist/chunk-F4NG4EXD.js.map +1 -0
- 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/direct-chat-channel.js +1 -462
- package/dist/mcp/slack-channel.js +176 -558
- package/dist/mcp/telegram-channel.js +188 -501
- 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 +0 -1
- /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,
|
|
@@ -14351,468 +14447,6 @@ async function isThreadKilled(opts) {
|
|
|
14351
14447
|
}
|
|
14352
14448
|
}
|
|
14353
14449
|
|
|
14354
|
-
// src/slack-progress.ts
|
|
14355
|
-
var MODE_EMOJI = {
|
|
14356
|
-
thinking: "\u{1F4AD}",
|
|
14357
|
-
working: "\u{1F6E0}\uFE0F",
|
|
14358
|
-
waiting: "\u23F3"
|
|
14359
|
-
};
|
|
14360
|
-
var TERMINAL_EMOJI = {
|
|
14361
|
-
completed: "\u2705",
|
|
14362
|
-
failed: "\u274C"
|
|
14363
|
-
};
|
|
14364
|
-
function formatWallClock(ms, timeZone) {
|
|
14365
|
-
const fmt = new Intl.DateTimeFormat("en-GB", {
|
|
14366
|
-
hour: "2-digit",
|
|
14367
|
-
minute: "2-digit",
|
|
14368
|
-
second: "2-digit",
|
|
14369
|
-
hour12: false,
|
|
14370
|
-
timeZone,
|
|
14371
|
-
timeZoneName: "short"
|
|
14372
|
-
});
|
|
14373
|
-
const parts = fmt.formatToParts(new Date(ms));
|
|
14374
|
-
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
14375
|
-
return `${get("hour")}:${get("minute")}:${get("second")} ${get("timeZoneName")}`;
|
|
14376
|
-
}
|
|
14377
|
-
function renderHeaderLine(state) {
|
|
14378
|
-
if (state.terminal) {
|
|
14379
|
-
return `${TERMINAL_EMOJI[state.terminal.kind]} *${state.terminal.kind === "completed" ? "Done" : "Failed"}* \u2014 ${state.initialLabel}`;
|
|
14380
|
-
}
|
|
14381
|
-
return `${MODE_EMOJI[state.mode]} *Working on:* ${state.initialLabel}`;
|
|
14382
|
-
}
|
|
14383
|
-
function renderStepLine(state) {
|
|
14384
|
-
if (state.terminal) {
|
|
14385
|
-
return state.terminal.message ? state.terminal.message : void 0;
|
|
14386
|
-
}
|
|
14387
|
-
const parts = [];
|
|
14388
|
-
if (state.stepNumber) {
|
|
14389
|
-
if (state.stepNumber.total != null) {
|
|
14390
|
-
parts.push(`*Step ${state.stepNumber.current} of ${state.stepNumber.total}*`);
|
|
14391
|
-
} else {
|
|
14392
|
-
parts.push(`*Step ${state.stepNumber.current}*`);
|
|
14393
|
-
}
|
|
14394
|
-
}
|
|
14395
|
-
if (state.stepLabel) parts.push(state.stepLabel);
|
|
14396
|
-
return parts.length > 0 ? parts.join(": ") : void 0;
|
|
14397
|
-
}
|
|
14398
|
-
function renderProgressBlocks(state) {
|
|
14399
|
-
const header = renderHeaderLine(state);
|
|
14400
|
-
const step = renderStepLine(state);
|
|
14401
|
-
const footer = state.terminal ? `_${state.terminal.kind === "completed" ? "completed" : "failed"}: ${formatWallClock(state.lastChangeAt)}_` : `_last update: ${formatWallClock(state.lastChangeAt)}_`;
|
|
14402
|
-
const lines = [header];
|
|
14403
|
-
if (step) lines.push(step);
|
|
14404
|
-
if (state.detail) lines.push(`_${state.detail}_`);
|
|
14405
|
-
lines.push(footer);
|
|
14406
|
-
const fallbackText = state.terminal ? `${state.terminal.kind === "completed" ? "Done" : "Failed"} \u2014 ${state.initialLabel}` : `Working on: ${state.initialLabel}`;
|
|
14407
|
-
return {
|
|
14408
|
-
text: fallbackText,
|
|
14409
|
-
blocks: [
|
|
14410
|
-
{
|
|
14411
|
-
type: "section",
|
|
14412
|
-
text: { type: "mrkdwn", text: lines.join("\n") }
|
|
14413
|
-
}
|
|
14414
|
-
]
|
|
14415
|
-
};
|
|
14416
|
-
}
|
|
14417
|
-
var SLACK_POST_URL = "https://slack.com/api/chat.postMessage";
|
|
14418
|
-
var SLACK_UPDATE_URL = "https://slack.com/api/chat.update";
|
|
14419
|
-
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
14420
|
-
var SlackApiError = class extends Error {
|
|
14421
|
-
constructor(endpoint, slackError, status) {
|
|
14422
|
-
super(`Slack ${endpoint} failed: ${slackError} (status=${status})`);
|
|
14423
|
-
this.endpoint = endpoint;
|
|
14424
|
-
this.slackError = slackError;
|
|
14425
|
-
this.status = status;
|
|
14426
|
-
this.name = "SlackApiError";
|
|
14427
|
-
}
|
|
14428
|
-
};
|
|
14429
|
-
function createSlackProgressFlush(opts) {
|
|
14430
|
-
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
14431
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
14432
|
-
let anchorTs = null;
|
|
14433
|
-
async function slackCall(endpoint, body) {
|
|
14434
|
-
const url = endpoint === "chat.postMessage" ? SLACK_POST_URL : SLACK_UPDATE_URL;
|
|
14435
|
-
const controller = new AbortController();
|
|
14436
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
14437
|
-
let response;
|
|
14438
|
-
try {
|
|
14439
|
-
response = await fetchImpl(url, {
|
|
14440
|
-
method: "POST",
|
|
14441
|
-
headers: {
|
|
14442
|
-
"Content-Type": "application/json",
|
|
14443
|
-
Authorization: `Bearer ${opts.botToken}`
|
|
14444
|
-
},
|
|
14445
|
-
body: JSON.stringify(body),
|
|
14446
|
-
signal: controller.signal
|
|
14447
|
-
});
|
|
14448
|
-
} finally {
|
|
14449
|
-
clearTimeout(timer);
|
|
14450
|
-
}
|
|
14451
|
-
const parsed = await response.json().catch(() => ({ ok: false, error: "invalid_json" }));
|
|
14452
|
-
if (!parsed.ok) {
|
|
14453
|
-
throw new SlackApiError(endpoint, parsed.error ?? "unknown", response.status);
|
|
14454
|
-
}
|
|
14455
|
-
return parsed;
|
|
14456
|
-
}
|
|
14457
|
-
return async function flush(state) {
|
|
14458
|
-
const payload = renderProgressBlocks(state);
|
|
14459
|
-
if (anchorTs == null) {
|
|
14460
|
-
const body = {
|
|
14461
|
-
channel: opts.channel,
|
|
14462
|
-
text: payload.text,
|
|
14463
|
-
blocks: payload.blocks
|
|
14464
|
-
};
|
|
14465
|
-
if (opts.threadTs) body.thread_ts = opts.threadTs;
|
|
14466
|
-
try {
|
|
14467
|
-
const res = await slackCall("chat.postMessage", body);
|
|
14468
|
-
if (!res.ts) {
|
|
14469
|
-
throw new SlackApiError(
|
|
14470
|
-
"chat.postMessage",
|
|
14471
|
-
"missing ts in successful response",
|
|
14472
|
-
200
|
|
14473
|
-
);
|
|
14474
|
-
}
|
|
14475
|
-
anchorTs = res.ts;
|
|
14476
|
-
opts.onAnchorPosted?.(res.ts);
|
|
14477
|
-
} catch (err) {
|
|
14478
|
-
throw err;
|
|
14479
|
-
}
|
|
14480
|
-
return;
|
|
14481
|
-
}
|
|
14482
|
-
try {
|
|
14483
|
-
await slackCall("chat.update", {
|
|
14484
|
-
channel: opts.channel,
|
|
14485
|
-
ts: anchorTs,
|
|
14486
|
-
text: payload.text,
|
|
14487
|
-
blocks: payload.blocks
|
|
14488
|
-
});
|
|
14489
|
-
} catch (err) {
|
|
14490
|
-
if (opts.onApiError && err instanceof SlackApiError) {
|
|
14491
|
-
await opts.onApiError(err);
|
|
14492
|
-
return;
|
|
14493
|
-
}
|
|
14494
|
-
throw err;
|
|
14495
|
-
}
|
|
14496
|
-
};
|
|
14497
|
-
}
|
|
14498
|
-
|
|
14499
|
-
// src/progress-tools.ts
|
|
14500
|
-
import { randomUUID } from "crypto";
|
|
14501
|
-
|
|
14502
|
-
// src/progress-handle.ts
|
|
14503
|
-
async function startProgressHandle(opts) {
|
|
14504
|
-
const now = opts.now ?? (() => Date.now());
|
|
14505
|
-
const setTimer = opts.setTimer ?? ((cb, ms) => setTimeout(cb, ms));
|
|
14506
|
-
const clearTimer = opts.clearTimer ?? ((id) => clearTimeout(id));
|
|
14507
|
-
const debounceMs = opts.debounceMs ?? 1e3;
|
|
14508
|
-
const onPostTerminalUpdate = opts.onPostTerminalUpdate ?? defaultPostTerminalWarn;
|
|
14509
|
-
const state = {
|
|
14510
|
-
initialLabel: opts.initialLabel,
|
|
14511
|
-
mode: opts.initialMode ?? "working",
|
|
14512
|
-
lastChangeAt: now()
|
|
14513
|
-
};
|
|
14514
|
-
let lastFlushAt = 0;
|
|
14515
|
-
let pendingTimer = null;
|
|
14516
|
-
let inFlight = null;
|
|
14517
|
-
let terminal = false;
|
|
14518
|
-
async function doFlush() {
|
|
14519
|
-
while (inFlight) await inFlight;
|
|
14520
|
-
if (pendingTimer != null) {
|
|
14521
|
-
clearTimer(pendingTimer);
|
|
14522
|
-
pendingTimer = null;
|
|
14523
|
-
}
|
|
14524
|
-
lastFlushAt = now();
|
|
14525
|
-
const promise = opts.flush(snapshotState());
|
|
14526
|
-
inFlight = promise.then(
|
|
14527
|
-
() => {
|
|
14528
|
-
inFlight = null;
|
|
14529
|
-
},
|
|
14530
|
-
(err) => {
|
|
14531
|
-
inFlight = null;
|
|
14532
|
-
throw err;
|
|
14533
|
-
}
|
|
14534
|
-
);
|
|
14535
|
-
await inFlight;
|
|
14536
|
-
}
|
|
14537
|
-
function snapshotState() {
|
|
14538
|
-
return {
|
|
14539
|
-
initialLabel: state.initialLabel,
|
|
14540
|
-
mode: state.mode,
|
|
14541
|
-
stepLabel: state.stepLabel,
|
|
14542
|
-
stepNumber: state.stepNumber ? { ...state.stepNumber } : void 0,
|
|
14543
|
-
detail: state.detail,
|
|
14544
|
-
lastChangeAt: state.lastChangeAt,
|
|
14545
|
-
terminal: state.terminal ? { ...state.terminal } : void 0
|
|
14546
|
-
};
|
|
14547
|
-
}
|
|
14548
|
-
function applyUpdate(next) {
|
|
14549
|
-
if (next.mode !== void 0) state.mode = next.mode;
|
|
14550
|
-
if (next.stepLabel !== void 0) state.stepLabel = next.stepLabel;
|
|
14551
|
-
if (next.stepNumber !== void 0) state.stepNumber = { ...next.stepNumber };
|
|
14552
|
-
if (next.detail !== void 0) state.detail = next.detail;
|
|
14553
|
-
state.lastChangeAt = now();
|
|
14554
|
-
}
|
|
14555
|
-
async function scheduleOrFlush() {
|
|
14556
|
-
const elapsed = now() - lastFlushAt;
|
|
14557
|
-
if (elapsed >= debounceMs) {
|
|
14558
|
-
await doFlush();
|
|
14559
|
-
return;
|
|
14560
|
-
}
|
|
14561
|
-
if (pendingTimer != null) return;
|
|
14562
|
-
const remaining = debounceMs - elapsed;
|
|
14563
|
-
pendingTimer = setTimer(() => {
|
|
14564
|
-
pendingTimer = null;
|
|
14565
|
-
void doFlush().catch(() => {
|
|
14566
|
-
});
|
|
14567
|
-
}, remaining);
|
|
14568
|
-
}
|
|
14569
|
-
lastFlushAt = now();
|
|
14570
|
-
await opts.flush(snapshotState());
|
|
14571
|
-
return {
|
|
14572
|
-
snapshot: snapshotState,
|
|
14573
|
-
async update(next) {
|
|
14574
|
-
if (terminal) {
|
|
14575
|
-
const peeked = snapshotState();
|
|
14576
|
-
Object.assign(peeked, {
|
|
14577
|
-
mode: next.mode ?? peeked.mode,
|
|
14578
|
-
stepLabel: next.stepLabel ?? peeked.stepLabel,
|
|
14579
|
-
stepNumber: next.stepNumber ?? peeked.stepNumber,
|
|
14580
|
-
detail: next.detail ?? peeked.detail
|
|
14581
|
-
});
|
|
14582
|
-
try {
|
|
14583
|
-
onPostTerminalUpdate(peeked);
|
|
14584
|
-
} catch (err) {
|
|
14585
|
-
console.warn(
|
|
14586
|
-
"[progress-handle] onPostTerminalUpdate threw \u2014 swallowed to keep update() non-throwing:",
|
|
14587
|
-
err
|
|
14588
|
-
);
|
|
14589
|
-
}
|
|
14590
|
-
return;
|
|
14591
|
-
}
|
|
14592
|
-
applyUpdate(next);
|
|
14593
|
-
await scheduleOrFlush();
|
|
14594
|
-
},
|
|
14595
|
-
async complete(summary) {
|
|
14596
|
-
if (terminal) return;
|
|
14597
|
-
terminal = true;
|
|
14598
|
-
state.terminal = { kind: "completed", message: summary };
|
|
14599
|
-
state.lastChangeAt = now();
|
|
14600
|
-
await doFlush();
|
|
14601
|
-
},
|
|
14602
|
-
async fail(reason) {
|
|
14603
|
-
if (terminal) return;
|
|
14604
|
-
terminal = true;
|
|
14605
|
-
state.terminal = { kind: "failed", message: reason };
|
|
14606
|
-
state.lastChangeAt = now();
|
|
14607
|
-
await doFlush();
|
|
14608
|
-
},
|
|
14609
|
-
isTerminal: () => terminal
|
|
14610
|
-
};
|
|
14611
|
-
}
|
|
14612
|
-
function defaultPostTerminalWarn(state) {
|
|
14613
|
-
console.warn(
|
|
14614
|
-
`[progress-handle] update() called after terminal transition (${state.terminal?.kind}) \u2014 ignored.`
|
|
14615
|
-
);
|
|
14616
|
-
}
|
|
14617
|
-
|
|
14618
|
-
// src/progress-tools.ts
|
|
14619
|
-
var ProgressToolRegistry = class {
|
|
14620
|
-
constructor(opts) {
|
|
14621
|
-
this.opts = opts;
|
|
14622
|
-
this.toolNames = {
|
|
14623
|
-
start: `${opts.prefix}_progress_start`,
|
|
14624
|
-
update: `${opts.prefix}_progress_update`,
|
|
14625
|
-
complete: `${opts.prefix}_progress_complete`,
|
|
14626
|
-
fail: `${opts.prefix}_progress_fail`
|
|
14627
|
-
};
|
|
14628
|
-
}
|
|
14629
|
-
handles = /* @__PURE__ */ new Map();
|
|
14630
|
-
toolNames;
|
|
14631
|
-
/** Tool definitions to splice into the MCP ListToolsRequest response. */
|
|
14632
|
-
getDefinitions() {
|
|
14633
|
-
const { surfaceDescription, startArgsSchema } = this.opts;
|
|
14634
|
-
const debounceMs = this.opts.debounceMs ?? 1e3;
|
|
14635
|
-
const debounceText = debounceMs === 1e3 ? "per second" : `every ${debounceMs}ms`;
|
|
14636
|
-
return [
|
|
14637
|
-
{
|
|
14638
|
-
name: this.toolNames.start,
|
|
14639
|
-
description: `Post a "still working" anchor to ${surfaceDescription} and return a progress_id. The anchor message updates in place as you call ${this.toolNames.update} \u2014 operators see one message that ticks forward instead of a flood of new ones. Always end with ${this.toolNames.complete} or ${this.toolNames.fail}; otherwise the anchor lingers as "working" until the stale-state sweep flips it. Opt-in \u2014 only call this for tasks that will take more than a few seconds.`,
|
|
14640
|
-
inputSchema: {
|
|
14641
|
-
type: "object",
|
|
14642
|
-
properties: {
|
|
14643
|
-
label: {
|
|
14644
|
-
type: "string",
|
|
14645
|
-
description: 'Short top-line label for the task (e.g. "diagnose vigil"). Stays constant for the lifetime of this progress anchor.'
|
|
14646
|
-
},
|
|
14647
|
-
initial_mode: {
|
|
14648
|
-
type: "string",
|
|
14649
|
-
enum: ["thinking", "working", "waiting"],
|
|
14650
|
-
description: 'Initial mode emoji \u2014 defaults to "working".'
|
|
14651
|
-
},
|
|
14652
|
-
...startArgsSchema.properties
|
|
14653
|
-
},
|
|
14654
|
-
required: ["label", ...startArgsSchema.required]
|
|
14655
|
-
}
|
|
14656
|
-
},
|
|
14657
|
-
{
|
|
14658
|
-
name: this.toolNames.update,
|
|
14659
|
-
description: `Update the progress anchor in place. The state machine debounces calls to one ${surfaceDescription} API call ${debounceText}, so spamming this every step is safe \u2014 only the latest pending state will paint. Any subset of fields can be passed; omitted ones retain their previous value.`,
|
|
14660
|
-
inputSchema: {
|
|
14661
|
-
type: "object",
|
|
14662
|
-
properties: {
|
|
14663
|
-
progress_id: { type: "string", description: "The progress_id returned from start." },
|
|
14664
|
-
step_label: { type: "string", description: 'Short label for the current step (e.g. "Querying CloudWatch logs").' },
|
|
14665
|
-
step_current: { type: "number", description: "1-based step counter \u2014 current." },
|
|
14666
|
-
step_total: { type: "number", description: "Total steps if known. Omit for open-ended progress." },
|
|
14667
|
-
mode: { type: "string", enum: ["thinking", "working", "waiting"] },
|
|
14668
|
-
detail: { type: "string", description: "Free-text detail line under the step label." }
|
|
14669
|
-
},
|
|
14670
|
-
required: ["progress_id"]
|
|
14671
|
-
}
|
|
14672
|
-
},
|
|
14673
|
-
{
|
|
14674
|
-
name: this.toolNames.complete,
|
|
14675
|
-
description: `Mark the progress anchor as \u2705 completed. Flushes any pending debounced state before painting the terminal banner \u2014 the operator never sees a stale "Step N" beside the \u2705. After this, the handle is dead; further updates are no-ops.`,
|
|
14676
|
-
inputSchema: {
|
|
14677
|
-
type: "object",
|
|
14678
|
-
properties: {
|
|
14679
|
-
progress_id: { type: "string" },
|
|
14680
|
-
summary: { type: "string", description: "Optional one-line summary of what was achieved." }
|
|
14681
|
-
},
|
|
14682
|
-
required: ["progress_id"]
|
|
14683
|
-
}
|
|
14684
|
-
},
|
|
14685
|
-
{
|
|
14686
|
-
name: this.toolNames.fail,
|
|
14687
|
-
description: `Mark the progress anchor as \u274C failed. Always include a one-sentence reason \u2014 the operator can't tell *why* from emoji alone. Same flush-then-paint behaviour as complete.`,
|
|
14688
|
-
inputSchema: {
|
|
14689
|
-
type: "object",
|
|
14690
|
-
properties: {
|
|
14691
|
-
progress_id: { type: "string" },
|
|
14692
|
-
reason: { type: "string", description: "One-sentence failure reason." }
|
|
14693
|
-
},
|
|
14694
|
-
required: ["progress_id", "reason"]
|
|
14695
|
-
}
|
|
14696
|
-
}
|
|
14697
|
-
];
|
|
14698
|
-
}
|
|
14699
|
-
/**
|
|
14700
|
-
* Dispatch a CallToolRequest. Returns `undefined` if the tool name
|
|
14701
|
-
* isn't one of ours (so the caller can keep walking its dispatch
|
|
14702
|
-
* chain). Returns a tool result otherwise.
|
|
14703
|
-
*/
|
|
14704
|
-
async handle(name, args) {
|
|
14705
|
-
if (name === this.toolNames.start) return this.handleStart(args);
|
|
14706
|
-
if (name === this.toolNames.update) return this.handleUpdate(args);
|
|
14707
|
-
if (name === this.toolNames.complete) return this.handleComplete(args);
|
|
14708
|
-
if (name === this.toolNames.fail) return this.handleFail(args);
|
|
14709
|
-
return void 0;
|
|
14710
|
-
}
|
|
14711
|
-
/** Live count of in-flight handles. Exposed for tests + diagnostics. */
|
|
14712
|
-
size() {
|
|
14713
|
-
return this.handles.size;
|
|
14714
|
-
}
|
|
14715
|
-
async handleStart(args) {
|
|
14716
|
-
const label = typeof args.label === "string" ? args.label : null;
|
|
14717
|
-
if (!label) return errResult(`${this.toolNames.start}: 'label' must be a non-empty string`);
|
|
14718
|
-
if ("initial_mode" in args && !isMode(args.initial_mode)) {
|
|
14719
|
-
return errResult(
|
|
14720
|
-
`${this.toolNames.start}: 'initial_mode' must be one of 'thinking', 'working', or 'waiting'`
|
|
14721
|
-
);
|
|
14722
|
-
}
|
|
14723
|
-
for (const k of this.opts.startArgsSchema.required) {
|
|
14724
|
-
if (typeof args[k] !== "string" || args[k].length === 0) {
|
|
14725
|
-
return errResult(`${this.toolNames.start}: '${k}' must be a non-empty string`);
|
|
14726
|
-
}
|
|
14727
|
-
}
|
|
14728
|
-
const initialMode = isMode(args.initial_mode) ? args.initial_mode : void 0;
|
|
14729
|
-
const progressId = randomUUID();
|
|
14730
|
-
try {
|
|
14731
|
-
const flush = this.opts.createFlush(args);
|
|
14732
|
-
const handle = await startProgressHandle({
|
|
14733
|
-
initialLabel: label,
|
|
14734
|
-
initialMode,
|
|
14735
|
-
flush,
|
|
14736
|
-
debounceMs: this.opts.debounceMs ?? 1e3
|
|
14737
|
-
});
|
|
14738
|
-
this.handles.set(progressId, handle);
|
|
14739
|
-
return okResult(JSON.stringify({ progress_id: progressId }));
|
|
14740
|
-
} catch (err) {
|
|
14741
|
-
return errResult(`${this.toolNames.start} failed: ${err.message ?? String(err)}`);
|
|
14742
|
-
}
|
|
14743
|
-
}
|
|
14744
|
-
async handleUpdate(args) {
|
|
14745
|
-
const id = typeof args.progress_id === "string" ? args.progress_id : null;
|
|
14746
|
-
if (!id) return errResult(`${this.toolNames.update}: 'progress_id' must be a string`);
|
|
14747
|
-
const handle = this.handles.get(id);
|
|
14748
|
-
if (!handle) return errResult(`${this.toolNames.update}: unknown progress_id (already terminated, or never started)`);
|
|
14749
|
-
if ("mode" in args && !isMode(args.mode)) {
|
|
14750
|
-
return errResult(
|
|
14751
|
-
`${this.toolNames.update}: 'mode' must be one of 'thinking', 'working', or 'waiting'`
|
|
14752
|
-
);
|
|
14753
|
-
}
|
|
14754
|
-
if (typeof args.step_total === "number" && typeof args.step_current !== "number") {
|
|
14755
|
-
return errResult(
|
|
14756
|
-
`${this.toolNames.update}: 'step_total' requires 'step_current'`
|
|
14757
|
-
);
|
|
14758
|
-
}
|
|
14759
|
-
const next = {};
|
|
14760
|
-
if (typeof args.step_label === "string") next.stepLabel = args.step_label;
|
|
14761
|
-
if (typeof args.step_current === "number") {
|
|
14762
|
-
next.stepNumber = {
|
|
14763
|
-
current: args.step_current,
|
|
14764
|
-
...typeof args.step_total === "number" ? { total: args.step_total } : {}
|
|
14765
|
-
};
|
|
14766
|
-
}
|
|
14767
|
-
if (isMode(args.mode)) next.mode = args.mode;
|
|
14768
|
-
if (typeof args.detail === "string") next.detail = args.detail;
|
|
14769
|
-
try {
|
|
14770
|
-
await handle.update(next);
|
|
14771
|
-
return okResult("ok");
|
|
14772
|
-
} catch (err) {
|
|
14773
|
-
return errResult(`${this.toolNames.update} failed: ${err.message ?? String(err)}`);
|
|
14774
|
-
}
|
|
14775
|
-
}
|
|
14776
|
-
async handleComplete(args) {
|
|
14777
|
-
const id = typeof args.progress_id === "string" ? args.progress_id : null;
|
|
14778
|
-
if (!id) return errResult(`${this.toolNames.complete}: 'progress_id' must be a string`);
|
|
14779
|
-
const handle = this.handles.get(id);
|
|
14780
|
-
if (!handle) return errResult(`${this.toolNames.complete}: unknown progress_id`);
|
|
14781
|
-
const summary = typeof args.summary === "string" ? args.summary : void 0;
|
|
14782
|
-
try {
|
|
14783
|
-
await handle.complete(summary);
|
|
14784
|
-
this.handles.delete(id);
|
|
14785
|
-
return okResult("ok");
|
|
14786
|
-
} catch (err) {
|
|
14787
|
-
return errResult(`${this.toolNames.complete} failed: ${err.message ?? String(err)}`);
|
|
14788
|
-
}
|
|
14789
|
-
}
|
|
14790
|
-
async handleFail(args) {
|
|
14791
|
-
const id = typeof args.progress_id === "string" ? args.progress_id : null;
|
|
14792
|
-
if (!id) return errResult(`${this.toolNames.fail}: 'progress_id' must be a string`);
|
|
14793
|
-
const reason = typeof args.reason === "string" ? args.reason : null;
|
|
14794
|
-
if (!reason) return errResult(`${this.toolNames.fail}: 'reason' must be a non-empty string`);
|
|
14795
|
-
const handle = this.handles.get(id);
|
|
14796
|
-
if (!handle) return errResult(`${this.toolNames.fail}: unknown progress_id`);
|
|
14797
|
-
try {
|
|
14798
|
-
await handle.fail(reason);
|
|
14799
|
-
this.handles.delete(id);
|
|
14800
|
-
return okResult("ok");
|
|
14801
|
-
} catch (err) {
|
|
14802
|
-
return errResult(`${this.toolNames.fail} failed: ${err.message ?? String(err)}`);
|
|
14803
|
-
}
|
|
14804
|
-
}
|
|
14805
|
-
};
|
|
14806
|
-
function okResult(text) {
|
|
14807
|
-
return { content: [{ type: "text", text }] };
|
|
14808
|
-
}
|
|
14809
|
-
function errResult(text) {
|
|
14810
|
-
return { content: [{ type: "text", text }], isError: true };
|
|
14811
|
-
}
|
|
14812
|
-
function isMode(value) {
|
|
14813
|
-
return value === "thinking" || value === "working" || value === "waiting";
|
|
14814
|
-
}
|
|
14815
|
-
|
|
14816
14450
|
// src/slack-thread-context.ts
|
|
14817
14451
|
var SLACK_AUTOLOAD_THREAD_LIMIT = 30;
|
|
14818
14452
|
var SLACK_AUTOLOAD_THREAD_MAX_CHARS = 4e3;
|
|
@@ -15016,20 +14650,20 @@ import {
|
|
|
15016
14650
|
createWriteStream,
|
|
15017
14651
|
existsSync as existsSync2,
|
|
15018
14652
|
mkdirSync as mkdirSync3,
|
|
15019
|
-
readFileSync as
|
|
15020
|
-
readdirSync,
|
|
14653
|
+
readFileSync as readFileSync4,
|
|
14654
|
+
readdirSync as readdirSync2,
|
|
15021
14655
|
renameSync as renameSync2,
|
|
15022
14656
|
statSync,
|
|
15023
14657
|
unlinkSync as unlinkSync2,
|
|
15024
14658
|
watch,
|
|
15025
14659
|
writeFileSync as writeFileSync3
|
|
15026
14660
|
} from "fs";
|
|
15027
|
-
import { basename, join as
|
|
14661
|
+
import { basename, join as join4, resolve as resolve2 } from "path";
|
|
15028
14662
|
import { homedir as homedir2 } from "os";
|
|
15029
|
-
import { createHash, randomUUID
|
|
14663
|
+
import { createHash, randomUUID } from "crypto";
|
|
15030
14664
|
|
|
15031
14665
|
// src/slack-thread-store.ts
|
|
15032
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
14666
|
+
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
15033
14667
|
import { dirname } from "path";
|
|
15034
14668
|
var FILE_VERSION = 1;
|
|
15035
14669
|
var DEFAULT_TTL_DAYS = 30;
|
|
@@ -15040,7 +14674,7 @@ function loadThreadStore(filePath, opts = {}) {
|
|
|
15040
14674
|
const ttlMs = ttlDays * 24 * 60 * 60 * 1e3;
|
|
15041
14675
|
let raw;
|
|
15042
14676
|
try {
|
|
15043
|
-
raw =
|
|
14677
|
+
raw = readFileSync2(filePath, "utf-8");
|
|
15044
14678
|
} catch {
|
|
15045
14679
|
return { threads: /* @__PURE__ */ new Map(), pruned: 0 };
|
|
15046
14680
|
}
|
|
@@ -15155,9 +14789,9 @@ async function runOrRetry(fn, opts) {
|
|
|
15155
14789
|
|
|
15156
14790
|
// src/channel-attachments.ts
|
|
15157
14791
|
import { homedir } from "os";
|
|
15158
|
-
import { join, resolve, sep } from "path";
|
|
14792
|
+
import { join as join2, resolve, sep } from "path";
|
|
15159
14793
|
function resolveChannelInboundDir(codeName, channelSlug) {
|
|
15160
|
-
const base =
|
|
14794
|
+
const base = join2(homedir(), ".augmented");
|
|
15161
14795
|
const allowedSegment = /^[A-Za-z0-9_-]+$/;
|
|
15162
14796
|
if (!allowedSegment.test(codeName) || !allowedSegment.test(channelSlug)) {
|
|
15163
14797
|
throw new Error(
|
|
@@ -15803,12 +15437,12 @@ function createSlackBotUserIdClient(args) {
|
|
|
15803
15437
|
import {
|
|
15804
15438
|
existsSync,
|
|
15805
15439
|
mkdirSync as mkdirSync2,
|
|
15806
|
-
readFileSync as
|
|
15440
|
+
readFileSync as readFileSync3,
|
|
15807
15441
|
renameSync,
|
|
15808
15442
|
unlinkSync,
|
|
15809
15443
|
writeFileSync as writeFileSync2
|
|
15810
15444
|
} from "fs";
|
|
15811
|
-
import { join as
|
|
15445
|
+
import { join as join3 } from "path";
|
|
15812
15446
|
function defaultIsPidAlive(pid) {
|
|
15813
15447
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
15814
15448
|
try {
|
|
@@ -15826,7 +15460,7 @@ function acquireMcpSpawnLock(args) {
|
|
|
15826
15460
|
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
15827
15461
|
const selfPid = options.selfPid ?? process.pid;
|
|
15828
15462
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
15829
|
-
const path =
|
|
15463
|
+
const path = join3(agentDir, basename2);
|
|
15830
15464
|
const existing = readLockHolder(path);
|
|
15831
15465
|
if (existing) {
|
|
15832
15466
|
if (existing.pid === selfPid) {
|
|
@@ -15857,7 +15491,7 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
|
15857
15491
|
function readLockHolder(path) {
|
|
15858
15492
|
if (!existsSync(path)) return null;
|
|
15859
15493
|
try {
|
|
15860
|
-
const raw =
|
|
15494
|
+
const raw = readFileSync3(path, "utf8");
|
|
15861
15495
|
const parsed = JSON.parse(raw);
|
|
15862
15496
|
const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
|
|
15863
15497
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
@@ -15939,9 +15573,9 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
|
|
|
15939
15573
|
peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
|
|
15940
15574
|
peer_disabled_mode: SLACK_PEER_DISABLED_MODE
|
|
15941
15575
|
};
|
|
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 ?
|
|
15576
|
+
var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join4(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
15577
|
+
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join4(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
|
|
15578
|
+
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join4(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
|
|
15945
15579
|
var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
|
|
15946
15580
|
function redactSlackId(id) {
|
|
15947
15581
|
if (!id) return "<none>";
|
|
@@ -15953,7 +15587,7 @@ function safeSlackMarkerName(channel, threadTs, messageTs) {
|
|
|
15953
15587
|
}
|
|
15954
15588
|
function slackPendingInboundPath(channel, threadTs, messageTs) {
|
|
15955
15589
|
if (!SLACK_PENDING_INBOUND_DIR) return null;
|
|
15956
|
-
return
|
|
15590
|
+
return join4(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
|
|
15957
15591
|
}
|
|
15958
15592
|
function writeSlackPendingInboundMarker(channel, threadTs, messageTs) {
|
|
15959
15593
|
const path = slackPendingInboundPath(channel, threadTs, messageTs);
|
|
@@ -15980,10 +15614,10 @@ function clearAllSlackPendingMarkersForThread(channel, threadTs) {
|
|
|
15980
15614
|
const safeThread = threadTs.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
15981
15615
|
const prefix = `${safeChan}__${safeThread}__`;
|
|
15982
15616
|
try {
|
|
15983
|
-
for (const f of
|
|
15617
|
+
for (const f of readdirSync2(SLACK_PENDING_INBOUND_DIR)) {
|
|
15984
15618
|
if (!f.startsWith(prefix) || !f.endsWith(".json")) continue;
|
|
15985
15619
|
try {
|
|
15986
|
-
unlinkSync2(
|
|
15620
|
+
unlinkSync2(join4(SLACK_PENDING_INBOUND_DIR, f));
|
|
15987
15621
|
} catch {
|
|
15988
15622
|
}
|
|
15989
15623
|
}
|
|
@@ -16004,10 +15638,10 @@ function slackNextRetryName(filename) {
|
|
|
16004
15638
|
async function processSlackRecoveryOutboxFile(filename) {
|
|
16005
15639
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
16006
15640
|
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
|
|
16007
|
-
const fullPath =
|
|
15641
|
+
const fullPath = join4(SLACK_RECOVERY_OUTBOX_DIR, filename);
|
|
16008
15642
|
let payload;
|
|
16009
15643
|
try {
|
|
16010
|
-
payload = JSON.parse(
|
|
15644
|
+
payload = JSON.parse(readFileSync4(fullPath, "utf-8"));
|
|
16011
15645
|
} catch (err) {
|
|
16012
15646
|
process.stderr.write(
|
|
16013
15647
|
`slack-channel(${AGENT_CODE_NAME}): recovery outbox parse failed (${filename}): ${err.message}
|
|
@@ -16081,7 +15715,7 @@ async function processSlackRecoveryOutboxFile(filename) {
|
|
|
16081
15715
|
const next = slackNextRetryName(filename);
|
|
16082
15716
|
if (next) {
|
|
16083
15717
|
try {
|
|
16084
|
-
renameSync2(fullPath,
|
|
15718
|
+
renameSync2(fullPath, join4(SLACK_RECOVERY_OUTBOX_DIR, next.next));
|
|
16085
15719
|
if (next.attempt >= SLACK_MAX_RECOVERY_ATTEMPTS) {
|
|
16086
15720
|
process.stderr.write(
|
|
16087
15721
|
`slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
|
|
@@ -16111,7 +15745,7 @@ function scanSlackRecoveryRetries() {
|
|
|
16111
15745
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
16112
15746
|
let entries;
|
|
16113
15747
|
try {
|
|
16114
|
-
entries =
|
|
15748
|
+
entries = readdirSync2(SLACK_RECOVERY_OUTBOX_DIR);
|
|
16115
15749
|
} catch {
|
|
16116
15750
|
return;
|
|
16117
15751
|
}
|
|
@@ -16120,7 +15754,7 @@ function scanSlackRecoveryRetries() {
|
|
|
16120
15754
|
if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
|
|
16121
15755
|
let mtimeMs;
|
|
16122
15756
|
try {
|
|
16123
|
-
mtimeMs = statSync(
|
|
15757
|
+
mtimeMs = statSync(join4(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
|
|
16124
15758
|
} catch {
|
|
16125
15759
|
continue;
|
|
16126
15760
|
}
|
|
@@ -16141,7 +15775,7 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
16141
15775
|
return;
|
|
16142
15776
|
}
|
|
16143
15777
|
try {
|
|
16144
|
-
for (const f of
|
|
15778
|
+
for (const f of readdirSync2(SLACK_RECOVERY_OUTBOX_DIR)) {
|
|
16145
15779
|
if (isFirstAttemptSlackOutboxFile(f)) void processSlackRecoveryOutboxFile(f);
|
|
16146
15780
|
}
|
|
16147
15781
|
} catch {
|
|
@@ -16150,7 +15784,7 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
16150
15784
|
const watcher = watch(SLACK_RECOVERY_OUTBOX_DIR, (event, filename) => {
|
|
16151
15785
|
if (event !== "rename" || !filename) return;
|
|
16152
15786
|
if (!isFirstAttemptSlackOutboxFile(filename)) return;
|
|
16153
|
-
if (existsSync2(
|
|
15787
|
+
if (existsSync2(join4(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
|
|
16154
15788
|
void processSlackRecoveryOutboxFile(filename);
|
|
16155
15789
|
}
|
|
16156
15790
|
});
|
|
@@ -16174,7 +15808,7 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16174
15808
|
if (!existsSync2(SLACK_PENDING_INBOUND_DIR)) return;
|
|
16175
15809
|
let filenames;
|
|
16176
15810
|
try {
|
|
16177
|
-
filenames =
|
|
15811
|
+
filenames = readdirSync2(SLACK_PENDING_INBOUND_DIR);
|
|
16178
15812
|
} catch (err) {
|
|
16179
15813
|
process.stderr.write(
|
|
16180
15814
|
`slack-channel(${AGENT_CODE_NAME}): stale-marker readdir failed: ${err.message}
|
|
@@ -16187,10 +15821,10 @@ function sweepSlackStaleMarkersOnBoot() {
|
|
|
16187
15821
|
for (const filename of filenames) {
|
|
16188
15822
|
if (!filename.endsWith(".json")) continue;
|
|
16189
15823
|
if (filename.endsWith(".tmp")) continue;
|
|
16190
|
-
const fullPath =
|
|
15824
|
+
const fullPath = join4(SLACK_PENDING_INBOUND_DIR, filename);
|
|
16191
15825
|
let marker;
|
|
16192
15826
|
try {
|
|
16193
|
-
marker = JSON.parse(
|
|
15827
|
+
marker = JSON.parse(readFileSync4(fullPath, "utf-8"));
|
|
16194
15828
|
} catch (err) {
|
|
16195
15829
|
process.stderr.write(
|
|
16196
15830
|
`slack-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactSlackId(filename)}: ${err.message}
|
|
@@ -16243,7 +15877,7 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
|
16243
15877
|
if (!existsSync2(SLACK_PENDING_INBOUND_DIR)) return;
|
|
16244
15878
|
let filenames;
|
|
16245
15879
|
try {
|
|
16246
|
-
filenames =
|
|
15880
|
+
filenames = readdirSync2(SLACK_PENDING_INBOUND_DIR);
|
|
16247
15881
|
} catch {
|
|
16248
15882
|
return;
|
|
16249
15883
|
}
|
|
@@ -16255,12 +15889,12 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
|
16255
15889
|
if (!filename.startsWith(channelPrefix)) continue;
|
|
16256
15890
|
if (!filename.endsWith(messageSuffix)) continue;
|
|
16257
15891
|
try {
|
|
16258
|
-
unlinkSync2(
|
|
15892
|
+
unlinkSync2(join4(SLACK_PENDING_INBOUND_DIR, filename));
|
|
16259
15893
|
} catch {
|
|
16260
15894
|
}
|
|
16261
15895
|
}
|
|
16262
15896
|
}
|
|
16263
|
-
var RESTART_FLAGS_DIR =
|
|
15897
|
+
var RESTART_FLAGS_DIR = join4(homedir2(), ".augmented", "restart-flags");
|
|
16264
15898
|
function buildAugmentedSlackMetadata() {
|
|
16265
15899
|
if (!AGT_TEAM_ID) return void 0;
|
|
16266
15900
|
return {
|
|
@@ -16442,7 +16076,7 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
16442
16076
|
if (!existsSync2(RESTART_FLAGS_DIR)) {
|
|
16443
16077
|
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
16444
16078
|
}
|
|
16445
|
-
const flagPath =
|
|
16079
|
+
const flagPath = join4(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
16446
16080
|
const flag = {
|
|
16447
16081
|
codeName,
|
|
16448
16082
|
source: "slack",
|
|
@@ -16452,7 +16086,7 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
16452
16086
|
...payload.thread_ts ? { thread_ts: payload.thread_ts } : {}
|
|
16453
16087
|
}
|
|
16454
16088
|
};
|
|
16455
|
-
const tmpPath = `${flagPath}.${process.pid}.${
|
|
16089
|
+
const tmpPath = `${flagPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
16456
16090
|
writeFileSync3(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
16457
16091
|
renameSync2(tmpPath, flagPath);
|
|
16458
16092
|
process.stderr.write(
|
|
@@ -16549,7 +16183,7 @@ async function handleRestartCommand(opts) {
|
|
|
16549
16183
|
if (!existsSync2(RESTART_FLAGS_DIR)) {
|
|
16550
16184
|
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
16551
16185
|
}
|
|
16552
|
-
const flagPath =
|
|
16186
|
+
const flagPath = join4(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
16553
16187
|
const flag = {
|
|
16554
16188
|
codeName,
|
|
16555
16189
|
source: "slack",
|
|
@@ -16560,7 +16194,7 @@ async function handleRestartCommand(opts) {
|
|
|
16560
16194
|
message_ts: opts.ts
|
|
16561
16195
|
}
|
|
16562
16196
|
};
|
|
16563
|
-
const tmpPath = `${flagPath}.${process.pid}.${
|
|
16197
|
+
const tmpPath = `${flagPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
16564
16198
|
writeFileSync3(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
16565
16199
|
renameSync2(tmpPath, flagPath);
|
|
16566
16200
|
process.stderr.write(
|
|
@@ -16608,7 +16242,7 @@ var THREAD_STORE_TTL_DAYS = parseTtlDays(process.env.SLACK_THREAD_FOLLOW_TTL_DAY
|
|
|
16608
16242
|
var threadPersister = null;
|
|
16609
16243
|
function resolveThreadStorePath() {
|
|
16610
16244
|
if (!AGENT_CODE_NAME) return null;
|
|
16611
|
-
return
|
|
16245
|
+
return join4(homedir2(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
|
|
16612
16246
|
}
|
|
16613
16247
|
function parseTtlDays(raw) {
|
|
16614
16248
|
if (!raw) return void 0;
|
|
@@ -16643,9 +16277,9 @@ if (!BOT_TOKEN || !APP_TOKEN) {
|
|
|
16643
16277
|
var slackStderrLogStream = null;
|
|
16644
16278
|
if (AGENT_CODE_NAME) {
|
|
16645
16279
|
try {
|
|
16646
|
-
const logDir =
|
|
16280
|
+
const logDir = join4(homedir2(), ".augmented", AGENT_CODE_NAME);
|
|
16647
16281
|
mkdirSync3(logDir, { recursive: true });
|
|
16648
|
-
slackStderrLogStream = createWriteStream(
|
|
16282
|
+
slackStderrLogStream = createWriteStream(join4(logDir, "slack-channel-stderr.log"), {
|
|
16649
16283
|
flags: "a",
|
|
16650
16284
|
mode: 384
|
|
16651
16285
|
});
|
|
@@ -16730,41 +16364,17 @@ var mcp = new Server(
|
|
|
16730
16364
|
// 2048 chars, so anything appended late silently disappears.
|
|
16731
16365
|
"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.",
|
|
16732
16366
|
`Inbound: <channel ... thread_ts="..." [thread_context="..."]>. Pass channel + thread_ts to slack.reply on threads. thread_context = thread pre-loaded; ground replies ONLY in it, never another channel's.`,
|
|
16733
|
-
"Long task (3+ tool calls or >5s)? slack_progress_start (channel + thread_ts), slack_progress_update between steps, slack_progress_complete/_fail to close. One anchor edits in place; avoids thread spam. Not for one-shots.",
|
|
16734
16367
|
"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
16368
|
"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
16369
|
'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).",
|
|
16370
|
+
"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
16371
|
`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
16372
|
"To deliver a file: save under your project dir, call slack.upload_file with path + channel + thread_ts."
|
|
16740
16373
|
].join(" ")
|
|
16741
16374
|
}
|
|
16742
16375
|
);
|
|
16743
|
-
var progressRegistry = new ProgressToolRegistry({
|
|
16744
|
-
prefix: "slack",
|
|
16745
|
-
surfaceDescription: "a Slack thread",
|
|
16746
|
-
startArgsSchema: {
|
|
16747
|
-
properties: {
|
|
16748
|
-
channel: {
|
|
16749
|
-
type: "string",
|
|
16750
|
-
description: "Slack channel id (from the `channel` attribute on the inbound <channel> tag)."
|
|
16751
|
-
},
|
|
16752
|
-
thread_ts: {
|
|
16753
|
-
type: "string",
|
|
16754
|
-
description: "Thread `ts` so the anchor lands in the same thread as the request (from the `thread_ts` attribute on the inbound <channel> tag). Omit to post at channel-root."
|
|
16755
|
-
}
|
|
16756
|
-
},
|
|
16757
|
-
required: ["channel"]
|
|
16758
|
-
},
|
|
16759
|
-
createFlush: (args) => createSlackProgressFlush({
|
|
16760
|
-
botToken: BOT_TOKEN,
|
|
16761
|
-
channel: args.channel,
|
|
16762
|
-
threadTs: typeof args.thread_ts === "string" ? args.thread_ts : void 0
|
|
16763
|
-
})
|
|
16764
|
-
});
|
|
16765
16376
|
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
16766
16377
|
tools: [
|
|
16767
|
-
...progressRegistry.getDefinitions(),
|
|
16768
16378
|
{
|
|
16769
16379
|
name: "slack.reply",
|
|
16770
16380
|
description: "Send a message back to a Slack channel or thread",
|
|
@@ -16786,7 +16396,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
16786
16396
|
},
|
|
16787
16397
|
{
|
|
16788
16398
|
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.",
|
|
16399
|
+
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
16400
|
inputSchema: {
|
|
16791
16401
|
type: "object",
|
|
16792
16402
|
properties: {
|
|
@@ -17015,8 +16625,6 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
17015
16625
|
if (isImpersonating() && !channelsEnabledOverride() && SLACK_EGRESS_TOOLS.has(name)) {
|
|
17016
16626
|
return buildImpersonationRefusal(name);
|
|
17017
16627
|
}
|
|
17018
|
-
const progressResult = await progressRegistry.handle(name, args ?? {});
|
|
17019
|
-
if (progressResult !== void 0) return progressResult;
|
|
17020
16628
|
if (name === "slack.reply") {
|
|
17021
16629
|
const { channel, text, thread_ts } = args;
|
|
17022
16630
|
if (channel && thread_ts) {
|
|
@@ -17238,7 +16846,7 @@ ${result.formatted}` : "Thread is empty or not found."
|
|
|
17238
16846
|
};
|
|
17239
16847
|
}
|
|
17240
16848
|
size = stat.size;
|
|
17241
|
-
bytes =
|
|
16849
|
+
bytes = readFileSync4(resolvedPath);
|
|
17242
16850
|
} catch (err) {
|
|
17243
16851
|
return {
|
|
17244
16852
|
content: [{ type: "text", text: `Failed to read file: ${err.message}` }],
|
|
@@ -17537,15 +17145,15 @@ async function handleSendStructured(args) {
|
|
|
17537
17145
|
const { validateSlackBlocks: validateSlackBlocks2 } = runtime;
|
|
17538
17146
|
const { channel, blocks, text, thread_ts, interactive } = args;
|
|
17539
17147
|
if (typeof channel !== "string" || !channel) {
|
|
17540
|
-
return
|
|
17148
|
+
return errResult("channel is required");
|
|
17541
17149
|
}
|
|
17542
17150
|
if (typeof text !== "string" || !text) {
|
|
17543
|
-
return
|
|
17151
|
+
return errResult("text is required (used as fallback for push notifications and unfurls)");
|
|
17544
17152
|
}
|
|
17545
17153
|
noteThreadActivity(channel, thread_ts);
|
|
17546
17154
|
const validation = validateSlackBlocks2(blocks);
|
|
17547
17155
|
if (!validation.ok) {
|
|
17548
|
-
return
|
|
17156
|
+
return errResult(`Invalid blocks: ${validation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
|
|
17549
17157
|
}
|
|
17550
17158
|
let effectiveBlocks = blocks;
|
|
17551
17159
|
let ratingCallbackId = null;
|
|
@@ -17553,13 +17161,13 @@ async function handleSendStructured(args) {
|
|
|
17553
17161
|
let ratingTokenised = [];
|
|
17554
17162
|
if (interactive) {
|
|
17555
17163
|
if (interactive.type !== "rate_run") {
|
|
17556
|
-
return
|
|
17164
|
+
return errResult(`Unsupported interactive.type '${interactive.type}'. Supported: rate_run.`);
|
|
17557
17165
|
}
|
|
17558
17166
|
if (typeof interactive.run_id !== "string" || !interactive.run_id) {
|
|
17559
|
-
return
|
|
17167
|
+
return errResult("interactive.run_id is required when interactive is set");
|
|
17560
17168
|
}
|
|
17561
17169
|
if (!interactiveHostAvailable()) {
|
|
17562
|
-
return
|
|
17170
|
+
return errResult(
|
|
17563
17171
|
"interactive=rate_run requires Block Kit plus host MCP wiring (AGT_HOST, AGT_API_KEY, AGT_AGENT_ID)."
|
|
17564
17172
|
);
|
|
17565
17173
|
}
|
|
@@ -17590,7 +17198,7 @@ async function handleSendStructured(args) {
|
|
|
17590
17198
|
effectiveBlocks = [...blocks, ratingBlock];
|
|
17591
17199
|
const finalValidation = runtime.validateSlackBlocks(effectiveBlocks);
|
|
17592
17200
|
if (!finalValidation.ok) {
|
|
17593
|
-
return
|
|
17201
|
+
return errResult(
|
|
17594
17202
|
`Invalid blocks (after appending rating row): ${finalValidation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`
|
|
17595
17203
|
);
|
|
17596
17204
|
}
|
|
@@ -17611,7 +17219,7 @@ async function handleSendStructured(args) {
|
|
|
17611
17219
|
kindData: { run_id: interactive.run_id, scale }
|
|
17612
17220
|
});
|
|
17613
17221
|
} catch (err) {
|
|
17614
|
-
return
|
|
17222
|
+
return errResult(`Failed to register rating row: ${err.message}`);
|
|
17615
17223
|
}
|
|
17616
17224
|
}
|
|
17617
17225
|
const result = await postSlackMessageWithTs({
|
|
@@ -17621,7 +17229,7 @@ async function handleSendStructured(args) {
|
|
|
17621
17229
|
...thread_ts ? { thread_ts } : {}
|
|
17622
17230
|
});
|
|
17623
17231
|
if (!result.ok || !result.ts) {
|
|
17624
|
-
return
|
|
17232
|
+
return errResult(`Slack chat.postMessage failed: ${result.error ?? "unknown"}`);
|
|
17625
17233
|
}
|
|
17626
17234
|
if (ratingCallbackId) {
|
|
17627
17235
|
try {
|
|
@@ -17676,17 +17284,17 @@ async function handleSendStructured(args) {
|
|
|
17676
17284
|
}
|
|
17677
17285
|
async function handleAskUser(args) {
|
|
17678
17286
|
if (!askUserToolAvailable()) {
|
|
17679
|
-
return
|
|
17287
|
+
return errResult("slack.ask_user is disabled or the host MCP is missing AGT_HOST/AGT_API_KEY/AGT_AGENT_ID env wiring.");
|
|
17680
17288
|
}
|
|
17681
17289
|
const { randomUUID: rndUUID } = await import("crypto");
|
|
17682
17290
|
const runtime = await Promise.resolve().then(() => (init_slack_block_kit_runtime(), slack_block_kit_runtime_exports));
|
|
17683
17291
|
const { validateAskUserOptions: validateAskUserOptions2, buildAskUserBlocks: buildAskUserBlocks2 } = runtime;
|
|
17684
17292
|
const { channel, question, options, timeout_seconds, thread_ts } = args;
|
|
17685
|
-
if (typeof channel !== "string" || !channel) return
|
|
17686
|
-
if (typeof question !== "string" || !question) return
|
|
17293
|
+
if (typeof channel !== "string" || !channel) return errResult("channel is required");
|
|
17294
|
+
if (typeof question !== "string" || !question) return errResult("question is required");
|
|
17687
17295
|
const optsValidation = validateAskUserOptions2(options);
|
|
17688
17296
|
if (!optsValidation.ok) {
|
|
17689
|
-
return
|
|
17297
|
+
return errResult(`Invalid options: ${optsValidation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
|
|
17690
17298
|
}
|
|
17691
17299
|
const requestedTimeout = typeof timeout_seconds === "number" ? timeout_seconds : 300;
|
|
17692
17300
|
const timeoutSec = Math.max(10, Math.min(3600, requestedTimeout));
|
|
@@ -17719,7 +17327,7 @@ async function handleAskUser(args) {
|
|
|
17719
17327
|
expiresAt
|
|
17720
17328
|
});
|
|
17721
17329
|
} catch (err) {
|
|
17722
|
-
return
|
|
17330
|
+
return errResult(`Failed to register pending interaction: ${err.message}`);
|
|
17723
17331
|
}
|
|
17724
17332
|
const slackResult = await postSlackMessageWithTs({
|
|
17725
17333
|
channel,
|
|
@@ -17729,7 +17337,7 @@ async function handleAskUser(args) {
|
|
|
17729
17337
|
...thread_ts ? { thread_ts } : {}
|
|
17730
17338
|
});
|
|
17731
17339
|
if (!slackResult.ok || !slackResult.ts) {
|
|
17732
|
-
return
|
|
17340
|
+
return errResult(`Slack chat.postMessage failed: ${slackResult.error ?? "unknown"}`);
|
|
17733
17341
|
}
|
|
17734
17342
|
try {
|
|
17735
17343
|
await runtime.updatePendingInteractionMessageTs(cfg, callbackId, slackResult.ts);
|
|
@@ -17776,7 +17384,7 @@ async function handleAskUser(args) {
|
|
|
17776
17384
|
}
|
|
17777
17385
|
async function handleChannelRequestInput(args) {
|
|
17778
17386
|
if (!askUserToolAvailable()) {
|
|
17779
|
-
return
|
|
17387
|
+
return errResult(
|
|
17780
17388
|
"channel_request_input is disabled or the host MCP is missing AGT_HOST/AGT_API_KEY/AGT_AGENT_ID env wiring."
|
|
17781
17389
|
);
|
|
17782
17390
|
}
|
|
@@ -17784,10 +17392,10 @@ async function handleChannelRequestInput(args) {
|
|
|
17784
17392
|
const { apiCall: apiCall2, waitForResolution: waitForResolution2, validateAskUserOptions: validateAskUserOptions2 } = runtime;
|
|
17785
17393
|
const { thread_id, prompt_text, options, schema, request_id, timeout_seconds } = args;
|
|
17786
17394
|
if (typeof thread_id !== "string" || !thread_id) {
|
|
17787
|
-
return
|
|
17395
|
+
return errResult("thread_id is required (shape: `<channel_id>:<thread_ts>`)");
|
|
17788
17396
|
}
|
|
17789
17397
|
if (typeof prompt_text !== "string" || !prompt_text) {
|
|
17790
|
-
return
|
|
17398
|
+
return errResult("prompt_text is required");
|
|
17791
17399
|
}
|
|
17792
17400
|
const optsValidation = validateAskUserOptions2(options, {
|
|
17793
17401
|
maxOptions: 4,
|
|
@@ -17795,25 +17403,25 @@ async function handleChannelRequestInput(args) {
|
|
|
17795
17403
|
maxValueChars: 2e3
|
|
17796
17404
|
});
|
|
17797
17405
|
if (!optsValidation.ok) {
|
|
17798
|
-
return
|
|
17406
|
+
return errResult(
|
|
17799
17407
|
`Invalid options: ${optsValidation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`
|
|
17800
17408
|
);
|
|
17801
17409
|
}
|
|
17802
17410
|
const optionsArr = options;
|
|
17803
17411
|
if (optionsArr.length < 2) {
|
|
17804
|
-
return
|
|
17412
|
+
return errResult("options must contain at least 2 entries (this is a picker, not a notification)");
|
|
17805
17413
|
}
|
|
17806
17414
|
if (!schema || typeof schema.type !== "string") {
|
|
17807
|
-
return
|
|
17415
|
+
return errResult("schema is required with shape { type: 'yes_no' | 'choice' }");
|
|
17808
17416
|
}
|
|
17809
17417
|
if (schema.type !== "yes_no" && schema.type !== "choice") {
|
|
17810
|
-
return
|
|
17418
|
+
return errResult("schema.type must be 'yes_no' or 'choice'");
|
|
17811
17419
|
}
|
|
17812
17420
|
if (schema.type === "yes_no" && optionsArr.length !== 2) {
|
|
17813
|
-
return
|
|
17421
|
+
return errResult("schema.type='yes_no' requires exactly 2 options");
|
|
17814
17422
|
}
|
|
17815
17423
|
if (request_id !== void 0 && (typeof request_id !== "string" || !request_id)) {
|
|
17816
|
-
return
|
|
17424
|
+
return errResult("request_id must be a non-empty string when provided");
|
|
17817
17425
|
}
|
|
17818
17426
|
const requestedTimeout = typeof timeout_seconds === "number" ? timeout_seconds : 300;
|
|
17819
17427
|
const timeoutSec = Math.max(10, Math.min(3600, requestedTimeout));
|
|
@@ -17835,10 +17443,10 @@ async function handleChannelRequestInput(args) {
|
|
|
17835
17443
|
dispatchPayload = await res.json().catch(() => ({}));
|
|
17836
17444
|
if (!res.ok || !dispatchPayload.ok || !dispatchPayload.callback_id) {
|
|
17837
17445
|
const reason = dispatchPayload.reason ?? `http_${res.status}`;
|
|
17838
|
-
return
|
|
17446
|
+
return errResult(`channel_request_input dispatch failed: ${reason}`);
|
|
17839
17447
|
}
|
|
17840
17448
|
} catch (err) {
|
|
17841
|
-
return
|
|
17449
|
+
return errResult(
|
|
17842
17450
|
`channel_request_input dispatch failed: ${err.message}`
|
|
17843
17451
|
);
|
|
17844
17452
|
}
|
|
@@ -17876,7 +17484,7 @@ async function handleChannelRequestInput(args) {
|
|
|
17876
17484
|
]
|
|
17877
17485
|
};
|
|
17878
17486
|
}
|
|
17879
|
-
function
|
|
17487
|
+
function errResult(text) {
|
|
17880
17488
|
return { content: [{ type: "text", text }], isError: true };
|
|
17881
17489
|
}
|
|
17882
17490
|
var MAX_INBOUND_FILE_BYTES = 25 * 1024 * 1024;
|
|
@@ -18258,14 +17866,24 @@ async function connectSocketMode() {
|
|
|
18258
17866
|
isAutoFollowed,
|
|
18259
17867
|
botUserId
|
|
18260
17868
|
});
|
|
18261
|
-
|
|
17869
|
+
const ackProbe = process.env.TMUX && AGENT_CODE_NAME ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
|
|
17870
|
+
const ackDecision = decideAckReaction({
|
|
17871
|
+
hasTarget: decideSlackAckReaction({ channel, ts }),
|
|
17872
|
+
integrationReady: Boolean(BOT_TOKEN),
|
|
17873
|
+
tmux: ackProbe.tmux,
|
|
17874
|
+
claude: ackProbe.claude,
|
|
17875
|
+
withinStartupGrace: process.uptime() * 1e3 < ACK_STARTUP_GRACE_MS,
|
|
17876
|
+
oldestPendingAgeMs: oldestPendingMarkerAgeMs(SLACK_PENDING_INBOUND_DIR)
|
|
17877
|
+
});
|
|
17878
|
+
if (ackDecision !== "none") {
|
|
17879
|
+
const reactionName = ackDecision === "undeliverable" ? "x" : "eyes";
|
|
18262
17880
|
fetch("https://slack.com/api/reactions.add", {
|
|
18263
17881
|
method: "POST",
|
|
18264
17882
|
headers: {
|
|
18265
17883
|
"Content-Type": "application/json",
|
|
18266
17884
|
Authorization: `Bearer ${BOT_TOKEN}`
|
|
18267
17885
|
},
|
|
18268
|
-
body: JSON.stringify({ channel, timestamp: ts, name:
|
|
17886
|
+
body: JSON.stringify({ channel, timestamp: ts, name: reactionName })
|
|
18269
17887
|
}).catch(() => {
|
|
18270
17888
|
});
|
|
18271
17889
|
}
|