@integrity-labs/agt-cli 0.28.56 → 0.28.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +4 -4
- package/dist/{chunk-ATK3HBK6.js → chunk-MKKTPM6J.js} +2 -2
- package/dist/{chunk-TMTSBVPP.js → chunk-SJRG5VUF.js} +19 -1
- package/dist/chunk-SJRG5VUF.js.map +1 -0
- package/dist/{chunk-OLP3LORD.js → chunk-ZKHETP7Y.js} +3 -3
- package/dist/{claude-pair-runtime-LDE4CJO5.js → claude-pair-runtime-RAAIXOKO.js} +2 -2
- package/dist/lib/manager-worker.js +61 -16
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/slack-channel.js +320 -44
- package/dist/mcp/telegram-channel.js +257 -43
- package/dist/{persistent-session-T6VRODM5.js → persistent-session-INTCKEG6.js} +3 -3
- package/dist/{responsiveness-probe-NI26NQJO.js → responsiveness-probe-XAYQAVLV.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-TMTSBVPP.js.map +0 -1
- /package/dist/{chunk-ATK3HBK6.js.map → chunk-MKKTPM6J.js.map} +0 -0
- /package/dist/{chunk-OLP3LORD.js.map → chunk-ZKHETP7Y.js.map} +0 -0
- /package/dist/{claude-pair-runtime-LDE4CJO5.js.map → claude-pair-runtime-RAAIXOKO.js.map} +0 -0
- /package/dist/{persistent-session-T6VRODM5.js.map → persistent-session-INTCKEG6.js.map} +0 -0
- /package/dist/{responsiveness-probe-NI26NQJO.js.map → responsiveness-probe-XAYQAVLV.js.map} +0 -0
|
@@ -15448,15 +15448,15 @@ import {
|
|
|
15448
15448
|
createWriteStream,
|
|
15449
15449
|
existsSync as existsSync7,
|
|
15450
15450
|
ftruncateSync,
|
|
15451
|
-
mkdirSync as
|
|
15451
|
+
mkdirSync as mkdirSync6,
|
|
15452
15452
|
openSync,
|
|
15453
|
-
readFileSync as
|
|
15453
|
+
readFileSync as readFileSync9,
|
|
15454
15454
|
readdirSync as readdirSync3,
|
|
15455
15455
|
renameSync as renameSync3,
|
|
15456
15456
|
statSync as statSync2,
|
|
15457
|
-
unlinkSync as
|
|
15457
|
+
unlinkSync as unlinkSync5,
|
|
15458
15458
|
watch,
|
|
15459
|
-
writeFileSync as
|
|
15459
|
+
writeFileSync as writeFileSync7,
|
|
15460
15460
|
writeSync
|
|
15461
15461
|
} from "fs";
|
|
15462
15462
|
import { basename, join as join7, resolve as resolve2 } from "path";
|
|
@@ -15575,6 +15575,156 @@ function isThreadEntry(value) {
|
|
|
15575
15575
|
return (v.involvement === "started" || v.involvement === "mentioned") && typeof v.last_seen_at === "string";
|
|
15576
15576
|
}
|
|
15577
15577
|
|
|
15578
|
+
// src/dm-restart-notice.ts
|
|
15579
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
15580
|
+
import { dirname as dirname3 } from "path";
|
|
15581
|
+
var RECENT_DM_VERSION = 1;
|
|
15582
|
+
var DEFAULT_RECENT_DM_TTL_MS = 30 * 60 * 1e3;
|
|
15583
|
+
var DEFAULT_DM_NOTICE_WINDOW_MS = 5 * 60 * 1e3;
|
|
15584
|
+
var DEFAULT_MIN_INTERVAL_MS2 = 1e3;
|
|
15585
|
+
function recordDmActivity(map, dmKey, now = /* @__PURE__ */ new Date()) {
|
|
15586
|
+
if (!dmKey) return;
|
|
15587
|
+
map.set(dmKey, { last_activity_at: now.toISOString() });
|
|
15588
|
+
}
|
|
15589
|
+
function recentlyActiveDmKeys(map, now = /* @__PURE__ */ new Date(), windowMs = DEFAULT_DM_NOTICE_WINDOW_MS) {
|
|
15590
|
+
const cutoff = now.getTime() - windowMs;
|
|
15591
|
+
return [...map.entries()].map(([key2, e]) => ({ key: key2, ts: Date.parse(e.last_activity_at) })).filter(({ ts }) => Number.isFinite(ts) && ts >= cutoff).sort((a, b) => b.ts - a.ts).map(({ key: key2 }) => key2);
|
|
15592
|
+
}
|
|
15593
|
+
function serializeRecentDms(map) {
|
|
15594
|
+
const out = { version: RECENT_DM_VERSION, dms: {} };
|
|
15595
|
+
for (const [key2, e] of map) out.dms[key2] = e;
|
|
15596
|
+
return JSON.stringify(out, null, 2);
|
|
15597
|
+
}
|
|
15598
|
+
function loadRecentDms(filePath, opts = {}) {
|
|
15599
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
15600
|
+
const ttlMs = opts.ttlMs ?? DEFAULT_RECENT_DM_TTL_MS;
|
|
15601
|
+
let raw;
|
|
15602
|
+
try {
|
|
15603
|
+
raw = readFileSync6(filePath, "utf-8");
|
|
15604
|
+
} catch (err) {
|
|
15605
|
+
const code = err.code;
|
|
15606
|
+
if (code !== "ENOENT") {
|
|
15607
|
+
opts.onError?.(`dm-restart-notice: failed to read recent-DM file: ${err.message}`);
|
|
15608
|
+
}
|
|
15609
|
+
return /* @__PURE__ */ new Map();
|
|
15610
|
+
}
|
|
15611
|
+
let parsed;
|
|
15612
|
+
try {
|
|
15613
|
+
parsed = JSON.parse(raw);
|
|
15614
|
+
} catch (err) {
|
|
15615
|
+
opts.onError?.(`dm-restart-notice: malformed recent-DM JSON (${err.message}) \u2014 ignoring`);
|
|
15616
|
+
return /* @__PURE__ */ new Map();
|
|
15617
|
+
}
|
|
15618
|
+
if (typeof parsed !== "object" || parsed === null || // CodeRabbit: reject unknown versions so a future/legacy on-disk format can't
|
|
15619
|
+
// be read with this version's (possibly incompatible) semantics.
|
|
15620
|
+
parsed.version !== RECENT_DM_VERSION || typeof parsed.dms !== "object" || parsed.dms === null) {
|
|
15621
|
+
opts.onError?.("dm-restart-notice: unexpected recent-DM shape/version \u2014 ignoring");
|
|
15622
|
+
return /* @__PURE__ */ new Map();
|
|
15623
|
+
}
|
|
15624
|
+
const map = /* @__PURE__ */ new Map();
|
|
15625
|
+
const cutoff = now.getTime() - ttlMs;
|
|
15626
|
+
for (const [key2, val] of Object.entries(parsed.dms)) {
|
|
15627
|
+
const at = val?.last_activity_at;
|
|
15628
|
+
if (typeof at !== "string") continue;
|
|
15629
|
+
const ts = Date.parse(at);
|
|
15630
|
+
if (!Number.isFinite(ts) || ts < cutoff) continue;
|
|
15631
|
+
map.set(key2, { last_activity_at: at });
|
|
15632
|
+
}
|
|
15633
|
+
return map;
|
|
15634
|
+
}
|
|
15635
|
+
function createRecentDmPersister(opts) {
|
|
15636
|
+
const interval = opts.minIntervalMs ?? DEFAULT_MIN_INTERVAL_MS2;
|
|
15637
|
+
let lastWriteAt = 0;
|
|
15638
|
+
let pendingTimer = null;
|
|
15639
|
+
let pendingSnapshot = null;
|
|
15640
|
+
const writeNow = (snap) => {
|
|
15641
|
+
try {
|
|
15642
|
+
mkdirSync3(dirname3(opts.filePath), { recursive: true });
|
|
15643
|
+
writeFileSync4(opts.filePath, serializeRecentDms(snap), "utf-8");
|
|
15644
|
+
lastWriteAt = Date.now();
|
|
15645
|
+
} catch (err) {
|
|
15646
|
+
opts.onError?.(`dm-restart-notice: failed to persist recent-DMs: ${err.message}`);
|
|
15647
|
+
}
|
|
15648
|
+
};
|
|
15649
|
+
return {
|
|
15650
|
+
schedule(map) {
|
|
15651
|
+
pendingSnapshot = new Map(map);
|
|
15652
|
+
const sinceLast = Date.now() - lastWriteAt;
|
|
15653
|
+
if (sinceLast >= interval) {
|
|
15654
|
+
const snap = pendingSnapshot;
|
|
15655
|
+
pendingSnapshot = null;
|
|
15656
|
+
writeNow(snap);
|
|
15657
|
+
return;
|
|
15658
|
+
}
|
|
15659
|
+
if (pendingTimer) return;
|
|
15660
|
+
pendingTimer = setTimeout(() => {
|
|
15661
|
+
pendingTimer = null;
|
|
15662
|
+
const snap = pendingSnapshot;
|
|
15663
|
+
pendingSnapshot = null;
|
|
15664
|
+
if (snap) writeNow(snap);
|
|
15665
|
+
}, interval - sinceLast);
|
|
15666
|
+
pendingTimer.unref?.();
|
|
15667
|
+
},
|
|
15668
|
+
flush(map) {
|
|
15669
|
+
if (pendingTimer) {
|
|
15670
|
+
clearTimeout(pendingTimer);
|
|
15671
|
+
pendingTimer = null;
|
|
15672
|
+
pendingSnapshot = null;
|
|
15673
|
+
}
|
|
15674
|
+
writeNow(new Map(map));
|
|
15675
|
+
},
|
|
15676
|
+
dispose() {
|
|
15677
|
+
if (pendingTimer) clearTimeout(pendingTimer);
|
|
15678
|
+
pendingTimer = null;
|
|
15679
|
+
pendingSnapshot = null;
|
|
15680
|
+
}
|
|
15681
|
+
};
|
|
15682
|
+
}
|
|
15683
|
+
var CHANNEL_ADD_RESTART_VERSION = 1;
|
|
15684
|
+
var CHANNEL_ADD_RESTART_MAX_AGE_MS = 15 * 60 * 1e3;
|
|
15685
|
+
function readChannelAddRestartMarker(filePath) {
|
|
15686
|
+
let raw;
|
|
15687
|
+
try {
|
|
15688
|
+
raw = readFileSync6(filePath, "utf-8");
|
|
15689
|
+
} catch {
|
|
15690
|
+
return null;
|
|
15691
|
+
}
|
|
15692
|
+
try {
|
|
15693
|
+
const parsed = JSON.parse(raw);
|
|
15694
|
+
if (typeof parsed !== "object" || parsed === null || parsed.version !== CHANNEL_ADD_RESTART_VERSION || typeof parsed.at !== "string") {
|
|
15695
|
+
return null;
|
|
15696
|
+
}
|
|
15697
|
+
const rawAdded = parsed.added;
|
|
15698
|
+
const added = Array.isArray(rawAdded) ? rawAdded.filter((x) => typeof x === "string") : [];
|
|
15699
|
+
return { version: CHANNEL_ADD_RESTART_VERSION, at: parsed.at, added };
|
|
15700
|
+
} catch {
|
|
15701
|
+
return null;
|
|
15702
|
+
}
|
|
15703
|
+
}
|
|
15704
|
+
function shouldPostChannelAddNotice(marker, bootMs, maxAgeMs = CHANNEL_ADD_RESTART_MAX_AGE_MS) {
|
|
15705
|
+
if (!marker) return false;
|
|
15706
|
+
const at = Date.parse(marker.at);
|
|
15707
|
+
if (!Number.isFinite(at)) return false;
|
|
15708
|
+
const age = bootMs - at;
|
|
15709
|
+
return age <= maxAgeMs && age >= -6e4;
|
|
15710
|
+
}
|
|
15711
|
+
function clearChannelAddRestartMarker(filePath) {
|
|
15712
|
+
try {
|
|
15713
|
+
unlinkSync3(filePath);
|
|
15714
|
+
} catch {
|
|
15715
|
+
}
|
|
15716
|
+
}
|
|
15717
|
+
function buildChannelAddNoticeText(added) {
|
|
15718
|
+
const list = added.filter(Boolean);
|
|
15719
|
+
if (list.length === 1) {
|
|
15720
|
+
return `Quick heads-up \u2014 I briefly restarted to bring a new channel (${list[0]}) online. I'm back now; please re-send anything I might have missed.`;
|
|
15721
|
+
}
|
|
15722
|
+
if (list.length > 1) {
|
|
15723
|
+
return `Quick heads-up \u2014 I briefly restarted to bring new channels (${list.join(", ")}) online. I'm back now; please re-send anything I might have missed.`;
|
|
15724
|
+
}
|
|
15725
|
+
return `Quick heads-up \u2014 I briefly restarted to update my configuration. I'm back now; please re-send anything I might have missed.`;
|
|
15726
|
+
}
|
|
15727
|
+
|
|
15578
15728
|
// src/safe-async.ts
|
|
15579
15729
|
async function runOrRetry(fn, opts) {
|
|
15580
15730
|
try {
|
|
@@ -15593,8 +15743,8 @@ async function runOrRetry(fn, opts) {
|
|
|
15593
15743
|
}
|
|
15594
15744
|
|
|
15595
15745
|
// src/slack-bot-photo.ts
|
|
15596
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
15597
|
-
import { dirname as
|
|
15746
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
15747
|
+
import { dirname as dirname4 } from "path";
|
|
15598
15748
|
async function applyBotPhoto(opts) {
|
|
15599
15749
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
15600
15750
|
const log = opts.log ?? ((m) => {
|
|
@@ -15603,7 +15753,7 @@ async function applyBotPhoto(opts) {
|
|
|
15603
15753
|
const { token, avatarUrl, markerPath } = opts;
|
|
15604
15754
|
if (markerPath && existsSync5(markerPath)) {
|
|
15605
15755
|
try {
|
|
15606
|
-
if (
|
|
15756
|
+
if (readFileSync7(markerPath, "utf-8").trim() === avatarUrl) {
|
|
15607
15757
|
return { status: "skipped-unchanged" };
|
|
15608
15758
|
}
|
|
15609
15759
|
} catch {
|
|
@@ -15644,8 +15794,8 @@ async function applyBotPhoto(opts) {
|
|
|
15644
15794
|
}
|
|
15645
15795
|
if (markerPath) {
|
|
15646
15796
|
try {
|
|
15647
|
-
|
|
15648
|
-
|
|
15797
|
+
mkdirSync4(dirname4(markerPath), { recursive: true, mode: 448 });
|
|
15798
|
+
writeFileSync5(markerPath, avatarUrl, { mode: 384 });
|
|
15649
15799
|
} catch {
|
|
15650
15800
|
}
|
|
15651
15801
|
}
|
|
@@ -16428,11 +16578,11 @@ function createSlackBotUserIdClient(args) {
|
|
|
16428
16578
|
// src/mcp-spawn-lock.ts
|
|
16429
16579
|
import {
|
|
16430
16580
|
existsSync as existsSync6,
|
|
16431
|
-
mkdirSync as
|
|
16432
|
-
readFileSync as
|
|
16581
|
+
mkdirSync as mkdirSync5,
|
|
16582
|
+
readFileSync as readFileSync8,
|
|
16433
16583
|
renameSync as renameSync2,
|
|
16434
|
-
unlinkSync as
|
|
16435
|
-
writeFileSync as
|
|
16584
|
+
unlinkSync as unlinkSync4,
|
|
16585
|
+
writeFileSync as writeFileSync6
|
|
16436
16586
|
} from "fs";
|
|
16437
16587
|
import { join as join6 } from "path";
|
|
16438
16588
|
function defaultIsPidAlive(pid) {
|
|
@@ -16462,10 +16612,10 @@ function acquireMcpSpawnLock(args) {
|
|
|
16462
16612
|
return { kind: "blocked", path, holder: existing };
|
|
16463
16613
|
}
|
|
16464
16614
|
}
|
|
16465
|
-
|
|
16615
|
+
mkdirSync5(agentDir, { recursive: true, mode: 448 });
|
|
16466
16616
|
const tmpPath = `${path}.${selfPid}.tmp`;
|
|
16467
16617
|
const payload = { pid: selfPid, started_at: now() };
|
|
16468
|
-
|
|
16618
|
+
writeFileSync6(tmpPath, JSON.stringify(payload), { mode: 384 });
|
|
16469
16619
|
renameSync2(tmpPath, path);
|
|
16470
16620
|
return { kind: "acquired", path };
|
|
16471
16621
|
}
|
|
@@ -16476,14 +16626,14 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
|
16476
16626
|
if (!existing) return;
|
|
16477
16627
|
if (existing.pid !== selfPid) return;
|
|
16478
16628
|
try {
|
|
16479
|
-
|
|
16629
|
+
unlinkSync4(lockPath);
|
|
16480
16630
|
} catch {
|
|
16481
16631
|
}
|
|
16482
16632
|
}
|
|
16483
16633
|
function readLockHolder(path) {
|
|
16484
16634
|
if (!existsSync6(path)) return null;
|
|
16485
16635
|
try {
|
|
16486
|
-
const raw =
|
|
16636
|
+
const raw = readFileSync8(path, "utf8");
|
|
16487
16637
|
const parsed = JSON.parse(raw);
|
|
16488
16638
|
const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
|
|
16489
16639
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
@@ -16672,7 +16822,7 @@ function readLiveAllowedUsers() {
|
|
|
16672
16822
|
return liveAllowedUsersCache.value;
|
|
16673
16823
|
}
|
|
16674
16824
|
const value = extractAllowedUsersFromMcpJson(
|
|
16675
|
-
|
|
16825
|
+
readFileSync9(SLACK_MCP_CONFIG_PATH, "utf-8")
|
|
16676
16826
|
);
|
|
16677
16827
|
if (value === null) return null;
|
|
16678
16828
|
liveAllowedUsersCache = { mtimeMs, value };
|
|
@@ -16687,6 +16837,8 @@ function getEffectiveAllowedUsers() {
|
|
|
16687
16837
|
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
|
|
16688
16838
|
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
|
|
16689
16839
|
var SLACK_RESTART_CONFIRM_FILE = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-restart-confirm.json") : null;
|
|
16840
|
+
var SLACK_RECENT_DMS_FILE = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-recent-dms.json") : null;
|
|
16841
|
+
var SLACK_CHANNEL_ADD_RESTART_FILE = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-channel-add-restart.json") : null;
|
|
16690
16842
|
var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
|
|
16691
16843
|
var SLACK_AVATAR_MARKER_PATH = SLACK_AGENT_DIR ? join7(SLACK_AGENT_DIR, "slack-avatar-applied") : null;
|
|
16692
16844
|
function redactSlackId(id) {
|
|
@@ -16717,8 +16869,8 @@ function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undelivera
|
|
|
16717
16869
|
...payload ? { payload } : {}
|
|
16718
16870
|
};
|
|
16719
16871
|
try {
|
|
16720
|
-
|
|
16721
|
-
|
|
16872
|
+
mkdirSync6(SLACK_PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
|
|
16873
|
+
writeFileSync7(path, JSON.stringify(marker), { mode: 384 });
|
|
16722
16874
|
} catch (err) {
|
|
16723
16875
|
process.stderr.write(
|
|
16724
16876
|
`slack-channel(${AGENT_CODE_NAME}): pending-inbound marker write failed: ${err.message}
|
|
@@ -16748,7 +16900,7 @@ function attachSlackReplayPayload(channel, threadTs, messageTs, payload) {
|
|
|
16748
16900
|
if (!path) return;
|
|
16749
16901
|
let marker;
|
|
16750
16902
|
try {
|
|
16751
|
-
marker = JSON.parse(
|
|
16903
|
+
marker = JSON.parse(readFileSync9(path, "utf-8"));
|
|
16752
16904
|
} catch {
|
|
16753
16905
|
return;
|
|
16754
16906
|
}
|
|
@@ -16759,7 +16911,7 @@ function readSlackPendingInboundMarker(channel, threadTs, messageTs) {
|
|
|
16759
16911
|
const path = slackPendingInboundPath(channel, threadTs, messageTs);
|
|
16760
16912
|
if (!path || !existsSync7(path)) return null;
|
|
16761
16913
|
try {
|
|
16762
|
-
return JSON.parse(
|
|
16914
|
+
return JSON.parse(readFileSync9(path, "utf-8"));
|
|
16763
16915
|
} catch {
|
|
16764
16916
|
return null;
|
|
16765
16917
|
}
|
|
@@ -16887,7 +17039,7 @@ function __resetSlackBusyAckNoticeThrottle() {
|
|
|
16887
17039
|
function clearSlackMarkerFileWithHeal(fullPath) {
|
|
16888
17040
|
let marker = null;
|
|
16889
17041
|
try {
|
|
16890
|
-
marker = JSON.parse(
|
|
17042
|
+
marker = JSON.parse(readFileSync9(fullPath, "utf-8"));
|
|
16891
17043
|
} catch {
|
|
16892
17044
|
}
|
|
16893
17045
|
if (marker && decideRecoveryHeal({
|
|
@@ -16897,7 +17049,7 @@ function clearSlackMarkerFileWithHeal(fullPath) {
|
|
|
16897
17049
|
healSlackUndeliverable(marker.channel, marker.message_ts);
|
|
16898
17050
|
}
|
|
16899
17051
|
try {
|
|
16900
|
-
if (existsSync7(fullPath))
|
|
17052
|
+
if (existsSync7(fullPath)) unlinkSync5(fullPath);
|
|
16901
17053
|
} catch {
|
|
16902
17054
|
}
|
|
16903
17055
|
}
|
|
@@ -16941,7 +17093,7 @@ async function processSlackRecoveryOutboxFile(filename) {
|
|
|
16941
17093
|
const fullPath = join7(SLACK_RECOVERY_OUTBOX_DIR, filename);
|
|
16942
17094
|
let payload;
|
|
16943
17095
|
try {
|
|
16944
|
-
payload = JSON.parse(
|
|
17096
|
+
payload = JSON.parse(readFileSync9(fullPath, "utf-8"));
|
|
16945
17097
|
} catch (err) {
|
|
16946
17098
|
process.stderr.write(
|
|
16947
17099
|
`slack-channel(${AGENT_CODE_NAME}): recovery outbox parse failed (${filename}): ${err.message}
|
|
@@ -17009,7 +17161,7 @@ ${payload.text}`;
|
|
|
17009
17161
|
}
|
|
17010
17162
|
if (sendSucceeded) {
|
|
17011
17163
|
try {
|
|
17012
|
-
|
|
17164
|
+
unlinkSync5(fullPath);
|
|
17013
17165
|
} catch {
|
|
17014
17166
|
}
|
|
17015
17167
|
return;
|
|
@@ -17068,7 +17220,7 @@ function scanSlackRecoveryRetries() {
|
|
|
17068
17220
|
function startSlackRecoveryOutboxWatcher() {
|
|
17069
17221
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
17070
17222
|
try {
|
|
17071
|
-
|
|
17223
|
+
mkdirSync6(SLACK_RECOVERY_OUTBOX_DIR, { recursive: true, mode: 448 });
|
|
17072
17224
|
} catch (err) {
|
|
17073
17225
|
process.stderr.write(
|
|
17074
17226
|
`slack-channel(${AGENT_CODE_NAME}): recovery outbox mkdir failed: ${err.message}
|
|
@@ -17127,14 +17279,14 @@ function sweepSlackStaleMarkers(thresholdMs) {
|
|
|
17127
17279
|
const fullPath = join7(SLACK_PENDING_INBOUND_DIR, filename);
|
|
17128
17280
|
let marker;
|
|
17129
17281
|
try {
|
|
17130
|
-
marker = JSON.parse(
|
|
17282
|
+
marker = JSON.parse(readFileSync9(fullPath, "utf-8"));
|
|
17131
17283
|
} catch (err) {
|
|
17132
17284
|
process.stderr.write(
|
|
17133
17285
|
`slack-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactSlackId(filename)}: ${err.message}
|
|
17134
17286
|
`
|
|
17135
17287
|
);
|
|
17136
17288
|
try {
|
|
17137
|
-
|
|
17289
|
+
unlinkSync5(fullPath);
|
|
17138
17290
|
} catch {
|
|
17139
17291
|
}
|
|
17140
17292
|
cleared++;
|
|
@@ -17149,7 +17301,7 @@ function sweepSlackStaleMarkers(thresholdMs) {
|
|
|
17149
17301
|
recordChannelDeflection(SLACK_AGENT_DIR, "slack", "replay_orphaned");
|
|
17150
17302
|
}
|
|
17151
17303
|
try {
|
|
17152
|
-
|
|
17304
|
+
unlinkSync5(fullPath);
|
|
17153
17305
|
} catch {
|
|
17154
17306
|
}
|
|
17155
17307
|
cleared++;
|
|
@@ -17179,7 +17331,7 @@ function listPendingSlackConversations() {
|
|
|
17179
17331
|
if (!name.endsWith(".json")) continue;
|
|
17180
17332
|
try {
|
|
17181
17333
|
const marker = JSON.parse(
|
|
17182
|
-
|
|
17334
|
+
readFileSync9(join7(SLACK_PENDING_INBOUND_DIR, name), "utf8")
|
|
17183
17335
|
);
|
|
17184
17336
|
if (typeof marker.channel !== "string" || !marker.channel) continue;
|
|
17185
17337
|
if (typeof marker.thread_ts !== "string" || !marker.thread_ts) continue;
|
|
@@ -17273,7 +17425,7 @@ async function notifyStrandedInboundsOnFirstConnect() {
|
|
|
17273
17425
|
const fullPath = join7(SLACK_PENDING_INBOUND_DIR, filename);
|
|
17274
17426
|
let marker;
|
|
17275
17427
|
try {
|
|
17276
|
-
marker = JSON.parse(
|
|
17428
|
+
marker = JSON.parse(readFileSync9(fullPath, "utf-8"));
|
|
17277
17429
|
} catch {
|
|
17278
17430
|
continue;
|
|
17279
17431
|
}
|
|
@@ -17286,7 +17438,7 @@ async function notifyStrandedInboundsOnFirstConnect() {
|
|
|
17286
17438
|
const key2 = `${channel}__${thread_ts}`;
|
|
17287
17439
|
if (seen.has(key2)) {
|
|
17288
17440
|
try {
|
|
17289
|
-
|
|
17441
|
+
unlinkSync5(fullPath);
|
|
17290
17442
|
} catch {
|
|
17291
17443
|
}
|
|
17292
17444
|
continue;
|
|
@@ -17300,7 +17452,7 @@ async function notifyStrandedInboundsOnFirstConnect() {
|
|
|
17300
17452
|
if (res.ok) {
|
|
17301
17453
|
notified += 1;
|
|
17302
17454
|
try {
|
|
17303
|
-
|
|
17455
|
+
unlinkSync5(fullPath);
|
|
17304
17456
|
} catch {
|
|
17305
17457
|
}
|
|
17306
17458
|
} else {
|
|
@@ -17372,6 +17524,49 @@ async function notifyRestartCompleteOnFirstConnect() {
|
|
|
17372
17524
|
restartConfirmNoticeInFlight = false;
|
|
17373
17525
|
}
|
|
17374
17526
|
}
|
|
17527
|
+
var dmChannelAddNoticeFired = false;
|
|
17528
|
+
var dmChannelAddNoticeInFlight = false;
|
|
17529
|
+
async function notifyDmUsersOnChannelAddRestart() {
|
|
17530
|
+
if (dmChannelAddNoticeFired || dmChannelAddNoticeInFlight) return;
|
|
17531
|
+
if (!SLACK_CHANNEL_ADD_RESTART_FILE) return;
|
|
17532
|
+
dmChannelAddNoticeInFlight = true;
|
|
17533
|
+
try {
|
|
17534
|
+
const marker = readChannelAddRestartMarker(SLACK_CHANNEL_ADD_RESTART_FILE);
|
|
17535
|
+
if (!shouldPostChannelAddNotice(marker, SLACK_PROCESS_BOOT_MS)) {
|
|
17536
|
+
if (marker) clearChannelAddRestartMarker(SLACK_CHANNEL_ADD_RESTART_FILE);
|
|
17537
|
+
return;
|
|
17538
|
+
}
|
|
17539
|
+
const dmChannels = recentlyActiveDmKeys(recentDms);
|
|
17540
|
+
const text = buildChannelAddNoticeText(marker.added);
|
|
17541
|
+
let notified = 0;
|
|
17542
|
+
for (const channel of dmChannels) {
|
|
17543
|
+
const res = await postSlackMessage({ channel, text });
|
|
17544
|
+
if (res.ok) {
|
|
17545
|
+
notified += 1;
|
|
17546
|
+
} else {
|
|
17547
|
+
process.stderr.write(
|
|
17548
|
+
`slack-channel(${AGENT_CODE_NAME}): channel-add DM notice failed channel=${redactSlackId(channel)} error=${res.error}
|
|
17549
|
+
`
|
|
17550
|
+
);
|
|
17551
|
+
}
|
|
17552
|
+
}
|
|
17553
|
+
clearChannelAddRestartMarker(SLACK_CHANNEL_ADD_RESTART_FILE);
|
|
17554
|
+
if (notified > 0) {
|
|
17555
|
+
process.stderr.write(
|
|
17556
|
+
`slack-channel(${AGENT_CODE_NAME}): notified ${notified} recently-active DM(s) after channel-add restart
|
|
17557
|
+
`
|
|
17558
|
+
);
|
|
17559
|
+
}
|
|
17560
|
+
} catch (err) {
|
|
17561
|
+
process.stderr.write(
|
|
17562
|
+
`slack-channel(${AGENT_CODE_NAME}): channel-add DM notice threw: ${redactAugmentedPaths2(err.message)}
|
|
17563
|
+
`
|
|
17564
|
+
);
|
|
17565
|
+
} finally {
|
|
17566
|
+
dmChannelAddNoticeFired = true;
|
|
17567
|
+
dmChannelAddNoticeInFlight = false;
|
|
17568
|
+
}
|
|
17569
|
+
}
|
|
17375
17570
|
function writeSlackRestartConfirm(reply, requesterName) {
|
|
17376
17571
|
if (!SLACK_RESTART_CONFIRM_FILE) return;
|
|
17377
17572
|
const marker = {
|
|
@@ -17470,6 +17665,7 @@ function buildSlackHelpMessage(codeName) {
|
|
|
17470
17665
|
"All commands are real Slack slash commands (autocomplete via `/`). For backward compatibility, `/help` and the restart command can also be typed as plain messages in any channel where the bot is present:",
|
|
17471
17666
|
`\u2022 \`${agentSlashCommand("/help")}\` (or type \`/help\`) \u2014 show this help`,
|
|
17472
17667
|
`\u2022 \`${agentSlashCommand("/restart")}\` \u2014 restart this agent`,
|
|
17668
|
+
`\u2022 \`${agentSlashCommand("/resume-onboarding")}\` \u2014 re-run this agent's onboarding interview (allowlisted users)`,
|
|
17473
17669
|
`\u2022 \`${agentSlashCommand("/status")}\` \u2014 this agent's model, session origin, uptime + connectivity`,
|
|
17474
17670
|
"\u2022 `/watch <google-doc-url> [duration]` (type it in chat) \u2014 watch a Google Doc for comments that mention me (default 2h, max 7d; auto-pauses when the window ends). In a shared channel, address me as `/watch-<my-name>`.",
|
|
17475
17671
|
"\u2022 `/kill` \u2014 silence all agents in this thread for 6h (use as a thread reply)",
|
|
@@ -17805,7 +18001,7 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
17805
18001
|
}
|
|
17806
18002
|
try {
|
|
17807
18003
|
if (!existsSync7(RESTART_FLAGS_DIR)) {
|
|
17808
|
-
|
|
18004
|
+
mkdirSync6(RESTART_FLAGS_DIR, { recursive: true });
|
|
17809
18005
|
}
|
|
17810
18006
|
const flagPath = join7(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
17811
18007
|
writeSlackRestartConfirm(
|
|
@@ -17825,7 +18021,7 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
17825
18021
|
}
|
|
17826
18022
|
};
|
|
17827
18023
|
const tmpPath = `${flagPath}.${process.pid}.${randomUUID2()}.tmp`;
|
|
17828
|
-
|
|
18024
|
+
writeFileSync7(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
17829
18025
|
renameSync3(tmpPath, flagPath);
|
|
17830
18026
|
process.stderr.write(
|
|
17831
18027
|
`slack-channel(${codeName}): /restart slash-command queued from channel ${hashChannelId(payload.channel_id)}
|
|
@@ -17849,6 +18045,50 @@ async function handleSlashCommandEnvelope(payload) {
|
|
|
17849
18045
|
}
|
|
17850
18046
|
return;
|
|
17851
18047
|
}
|
|
18048
|
+
if (matchesAgentCommand(command, "/resume-onboarding")) {
|
|
18049
|
+
const allowed = getEffectiveAllowedUsers();
|
|
18050
|
+
if (allowed.size > 0 && (!payload.user_id || !allowed.has(payload.user_id))) {
|
|
18051
|
+
await postEphemeralViaResponseUrl(
|
|
18052
|
+
responseUrl,
|
|
18053
|
+
`\u{1F6AB} \`/resume-onboarding\` denied \u2014 your Slack user is not in the allowlist for \`${codeName}\`.`,
|
|
18054
|
+
codeName
|
|
18055
|
+
);
|
|
18056
|
+
return;
|
|
18057
|
+
}
|
|
18058
|
+
if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
|
|
18059
|
+
await postEphemeralViaResponseUrl(
|
|
18060
|
+
responseUrl,
|
|
18061
|
+
":warning: This agent has no host API wiring \u2014 `/resume-onboarding` needs the host runtime to be reachable.",
|
|
18062
|
+
codeName
|
|
18063
|
+
);
|
|
18064
|
+
return;
|
|
18065
|
+
}
|
|
18066
|
+
try {
|
|
18067
|
+
const res = await fetch(`${AGT_HOST}/host/onboarding/reset`, {
|
|
18068
|
+
method: "POST",
|
|
18069
|
+
headers: {
|
|
18070
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
18071
|
+
Authorization: `Bearer ${AGT_API_KEY}`
|
|
18072
|
+
},
|
|
18073
|
+
body: JSON.stringify({ agent_id: AGT_AGENT_ID }),
|
|
18074
|
+
signal: AbortSignal.timeout(SLACK_DOWNLOAD_TIMEOUT_MS)
|
|
18075
|
+
});
|
|
18076
|
+
const data = await res.json();
|
|
18077
|
+
const text = data.ok ? `\u{1F504} ${data.message ?? "Onboarding reset \u2014 I\u2019ll re-interview your manager."}` : `:x: \`/resume-onboarding\` failed${data.error ? `: ${data.error}` : "."}`;
|
|
18078
|
+
await postEphemeralViaResponseUrl(responseUrl, text, codeName);
|
|
18079
|
+
} catch (err) {
|
|
18080
|
+
process.stderr.write(
|
|
18081
|
+
`slack-channel(${codeName}): /resume-onboarding forward failed: ${err.message}
|
|
18082
|
+
`
|
|
18083
|
+
);
|
|
18084
|
+
await postEphemeralViaResponseUrl(
|
|
18085
|
+
responseUrl,
|
|
18086
|
+
":x: `/resume-onboarding` forwarding failed \u2014 host runtime unreachable. Try again in a moment.",
|
|
18087
|
+
codeName
|
|
18088
|
+
);
|
|
18089
|
+
}
|
|
18090
|
+
return;
|
|
18091
|
+
}
|
|
17852
18092
|
if (command === "/debug" || matchesAgentCommand(command, "/investigate")) {
|
|
17853
18093
|
await handleDebugSlashCommand(payload, responseUrl);
|
|
17854
18094
|
return;
|
|
@@ -17923,7 +18163,7 @@ async function handleRestartCommand(opts) {
|
|
|
17923
18163
|
const codeName = AGENT_CODE_NAME ?? "unknown";
|
|
17924
18164
|
try {
|
|
17925
18165
|
if (!existsSync7(RESTART_FLAGS_DIR)) {
|
|
17926
|
-
|
|
18166
|
+
mkdirSync6(RESTART_FLAGS_DIR, { recursive: true });
|
|
17927
18167
|
}
|
|
17928
18168
|
const flagPath = join7(RESTART_FLAGS_DIR, `${codeName}.flag`);
|
|
17929
18169
|
writeSlackRestartConfirm(
|
|
@@ -17944,7 +18184,7 @@ async function handleRestartCommand(opts) {
|
|
|
17944
18184
|
}
|
|
17945
18185
|
};
|
|
17946
18186
|
const tmpPath = `${flagPath}.${process.pid}.${randomUUID2()}.tmp`;
|
|
17947
|
-
|
|
18187
|
+
writeFileSync7(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
17948
18188
|
renameSync3(tmpPath, flagPath);
|
|
17949
18189
|
process.stderr.write(
|
|
17950
18190
|
`slack-channel(${codeName}): /restart queued from channel ${hashChannelId(opts.channel)}
|
|
@@ -18040,6 +18280,8 @@ async function denyUnauthorizedWatch(opts) {
|
|
|
18040
18280
|
...opts.threadTs ? { thread_ts: opts.threadTs } : {}
|
|
18041
18281
|
});
|
|
18042
18282
|
}
|
|
18283
|
+
var recentDms = /* @__PURE__ */ new Map();
|
|
18284
|
+
var recentDmPersister = null;
|
|
18043
18285
|
var trackedThreads = /* @__PURE__ */ new Map();
|
|
18044
18286
|
var THREAD_STORE_PATH = resolveThreadStorePath();
|
|
18045
18287
|
var THREAD_STORE_TTL_DAYS = parseTtlDays(process.env.SLACK_THREAD_FOLLOW_TTL_DAYS);
|
|
@@ -18086,7 +18328,7 @@ var slackStderrLogStream = null;
|
|
|
18086
18328
|
if (AGENT_CODE_NAME) {
|
|
18087
18329
|
try {
|
|
18088
18330
|
const logDir = join7(homedir3(), ".augmented", AGENT_CODE_NAME);
|
|
18089
|
-
|
|
18331
|
+
mkdirSync6(logDir, { recursive: true });
|
|
18090
18332
|
slackStderrLogStream = createWriteStream(join7(logDir, "slack-channel-stderr.log"), {
|
|
18091
18333
|
flags: "a",
|
|
18092
18334
|
mode: 384
|
|
@@ -18667,7 +18909,7 @@ ${result.formatted}` : "Thread is empty or not found."
|
|
|
18667
18909
|
};
|
|
18668
18910
|
}
|
|
18669
18911
|
size = stat2.size;
|
|
18670
|
-
bytes =
|
|
18912
|
+
bytes = readFileSync9(resolvedPath);
|
|
18671
18913
|
} catch (err) {
|
|
18672
18914
|
return {
|
|
18673
18915
|
content: [{ type: "text", text: `Failed to read file: ${err.message}` }],
|
|
@@ -19305,8 +19547,8 @@ async function downloadSlackFile(fileId, codeName) {
|
|
|
19305
19547
|
if (!isPathInside(savedPath, dir)) {
|
|
19306
19548
|
throw new Error(`refusing to write ${savedPath} outside ${dir}`);
|
|
19307
19549
|
}
|
|
19308
|
-
|
|
19309
|
-
|
|
19550
|
+
mkdirSync6(dir, { recursive: true });
|
|
19551
|
+
writeFileSync7(savedPath, bytes, { mode: 384 });
|
|
19310
19552
|
try {
|
|
19311
19553
|
chmodSync(savedPath, 384);
|
|
19312
19554
|
} catch {
|
|
@@ -19385,7 +19627,7 @@ async function replayPendingSlackMarkers() {
|
|
|
19385
19627
|
const fullPath = join7(SLACK_PENDING_INBOUND_DIR, name);
|
|
19386
19628
|
let marker;
|
|
19387
19629
|
try {
|
|
19388
|
-
marker = JSON.parse(
|
|
19630
|
+
marker = JSON.parse(readFileSync9(fullPath, "utf-8"));
|
|
19389
19631
|
} catch {
|
|
19390
19632
|
continue;
|
|
19391
19633
|
}
|
|
@@ -19512,6 +19754,7 @@ async function connectSocketMode() {
|
|
|
19512
19754
|
void notifyStrandedInboundsOnFirstConnect();
|
|
19513
19755
|
}
|
|
19514
19756
|
void notifyRestartCompleteOnFirstConnect();
|
|
19757
|
+
void notifyDmUsersOnChannelAddRestart();
|
|
19515
19758
|
void applyBotPhotoOnFirstConnect();
|
|
19516
19759
|
};
|
|
19517
19760
|
ws.onmessage = async (event) => {
|
|
@@ -19709,6 +19952,14 @@ async function connectSocketMode() {
|
|
|
19709
19952
|
const isThreadReply = !!evt.thread_ts && evt.thread_ts !== evt.ts;
|
|
19710
19953
|
const trackTs = evt.thread_ts ?? evt.ts ?? "";
|
|
19711
19954
|
const threadKey = buildThreadKey(evt.channel, trackTs);
|
|
19955
|
+
if (isDirectMessage && !isBot && evt.channel && evt.user && evt.user !== botUserId) {
|
|
19956
|
+
recordDmActivity(recentDms, evt.channel);
|
|
19957
|
+
if (isShuttingDown) {
|
|
19958
|
+
recentDmPersister?.flush(recentDms);
|
|
19959
|
+
} else {
|
|
19960
|
+
recentDmPersister?.schedule(recentDms);
|
|
19961
|
+
}
|
|
19962
|
+
}
|
|
19712
19963
|
if (isBot && peerClassification?.kind === "peer-ingress") {
|
|
19713
19964
|
const peerGate = peerClassification.peer.gate_path;
|
|
19714
19965
|
if (peerGate === "intra_org_unrestricted") {
|
|
@@ -19944,6 +20195,14 @@ function shutdown(reason, exitCode = 0) {
|
|
|
19944
20195
|
threadPersister?.dispose();
|
|
19945
20196
|
} catch {
|
|
19946
20197
|
}
|
|
20198
|
+
try {
|
|
20199
|
+
recentDmPersister?.flush(recentDms);
|
|
20200
|
+
} catch {
|
|
20201
|
+
}
|
|
20202
|
+
try {
|
|
20203
|
+
recentDmPersister?.dispose();
|
|
20204
|
+
} catch {
|
|
20205
|
+
}
|
|
19947
20206
|
process.stderr.write(`slack-channel: ${reason} \u2014 closing Socket Mode and exiting
|
|
19948
20207
|
`);
|
|
19949
20208
|
try {
|
|
@@ -19987,7 +20246,12 @@ process.on("unhandledRejection", (reason) => {
|
|
|
19987
20246
|
}
|
|
19988
20247
|
if (THREAD_STORE_PATH) {
|
|
19989
20248
|
const threadStoreLabel = "slack-tracked-threads.json";
|
|
19990
|
-
const
|
|
20249
|
+
const recentDmsLabel = "slack-recent-dms.json";
|
|
20250
|
+
const redact = (msg) => {
|
|
20251
|
+
let out = msg.replaceAll(THREAD_STORE_PATH, threadStoreLabel);
|
|
20252
|
+
if (SLACK_RECENT_DMS_FILE) out = out.replaceAll(SLACK_RECENT_DMS_FILE, recentDmsLabel);
|
|
20253
|
+
return out;
|
|
20254
|
+
};
|
|
19991
20255
|
const { threads, pruned } = loadThreadStore(THREAD_STORE_PATH, {
|
|
19992
20256
|
ttlDays: THREAD_STORE_TTL_DAYS,
|
|
19993
20257
|
onError: (msg) => process.stderr.write(`${redact(msg)}
|
|
@@ -20003,6 +20267,18 @@ if (THREAD_STORE_PATH) {
|
|
|
20003
20267
|
`slack-channel: hydrated ${trackedThreads.size} tracked thread(s)${pruned > 0 ? ` (pruned ${pruned} stale)` : ""} from ${threadStoreLabel}
|
|
20004
20268
|
`
|
|
20005
20269
|
);
|
|
20270
|
+
if (SLACK_RECENT_DMS_FILE) {
|
|
20271
|
+
const loadedDms = loadRecentDms(SLACK_RECENT_DMS_FILE, {
|
|
20272
|
+
onError: (msg) => process.stderr.write(`${redact(msg)}
|
|
20273
|
+
`)
|
|
20274
|
+
});
|
|
20275
|
+
for (const [key2, entry] of loadedDms) recentDms.set(key2, entry);
|
|
20276
|
+
recentDmPersister = createRecentDmPersister({
|
|
20277
|
+
filePath: SLACK_RECENT_DMS_FILE,
|
|
20278
|
+
onError: (msg) => process.stderr.write(`${redact(msg)}
|
|
20279
|
+
`)
|
|
20280
|
+
});
|
|
20281
|
+
}
|
|
20006
20282
|
} else {
|
|
20007
20283
|
process.stderr.write(
|
|
20008
20284
|
"slack-channel: AGT_AGENT_CODE_NAME not set \u2014 running without thread-follow persistence\n"
|