@integrity-labs/agt-cli 0.23.1 → 0.24.0
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-2TOCO5D2.js → chunk-HSIESZMZ.js} +23 -1
- package/dist/{chunk-2TOCO5D2.js.map → chunk-HSIESZMZ.js.map} +1 -1
- package/dist/{chunk-Q4QD3TAC.js → chunk-WZTRMJM4.js} +205 -159
- package/dist/chunk-WZTRMJM4.js.map +1 -0
- package/dist/{chunk-TCCBS3PD.js → chunk-ZKQGDH3T.js} +2 -2
- package/dist/{claude-pair-runtime-KUWBZW33.js → claude-pair-runtime-XCFWTH6T.js} +2 -2
- package/dist/lib/manager-worker.js +551 -251
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/index.js +71 -0
- package/dist/mcp/slack-channel.js +32 -246
- package/dist/mcp/telegram-channel.js +30 -232
- package/dist/pane-mcp-banner-scraper-JA437JIB.js +22 -0
- package/dist/pane-mcp-banner-scraper-JA437JIB.js.map +1 -0
- package/dist/{persistent-session-XEA4SPAN.js → persistent-session-L4YIJJML.js} +3 -3
- package/dist/{responsiveness-probe-VWPOCABI.js → responsiveness-probe-YRLESO54.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-Q4QD3TAC.js.map +0 -1
- /package/dist/{chunk-TCCBS3PD.js.map → chunk-ZKQGDH3T.js.map} +0 -0
- /package/dist/{claude-pair-runtime-KUWBZW33.js.map → claude-pair-runtime-XCFWTH6T.js.map} +0 -0
- /package/dist/{persistent-session-XEA4SPAN.js.map → persistent-session-L4YIJJML.js.map} +0 -0
- /package/dist/{responsiveness-probe-VWPOCABI.js.map → responsiveness-probe-YRLESO54.js.map} +0 -0
package/dist/mcp/index.js
CHANGED
|
@@ -22293,6 +22293,77 @@ async function registerForwardedApiTools() {
|
|
|
22293
22293
|
}
|
|
22294
22294
|
return { registered, skipped };
|
|
22295
22295
|
}
|
|
22296
|
+
server.tool(
|
|
22297
|
+
"projects.list",
|
|
22298
|
+
"List artefacts (websites, slide decks, mockups) this agent has previously published. Call this BEFORE re-publishing to find and modify existing work instead of creating duplicates.",
|
|
22299
|
+
{
|
|
22300
|
+
publisher: external_exports.string().optional().describe('Filter by publisher (e.g. "here-now")'),
|
|
22301
|
+
kind: external_exports.enum(["mockup", "deck", "site", "app", "other"]).optional().describe("Filter by artefact kind"),
|
|
22302
|
+
limit: external_exports.number().int().min(1).max(100).optional().describe("Maximum projects to return (default 50, max 100)")
|
|
22303
|
+
},
|
|
22304
|
+
async (params) => {
|
|
22305
|
+
const data = await apiPost("/host/my-published-projects", {
|
|
22306
|
+
agent_id: AGT_AGENT_ID,
|
|
22307
|
+
publisher: params.publisher,
|
|
22308
|
+
kind: params.kind,
|
|
22309
|
+
limit: params.limit
|
|
22310
|
+
});
|
|
22311
|
+
if (!data.projects.length) {
|
|
22312
|
+
return { content: [{ type: "text", text: "No published projects yet." }] };
|
|
22313
|
+
}
|
|
22314
|
+
const lines = data.projects.map((p) => {
|
|
22315
|
+
const title = p.title ?? "(untitled)";
|
|
22316
|
+
const expires = p.expires_at ? ` expires ${p.expires_at}` : "";
|
|
22317
|
+
return `- [${p.kind}] ${title} \u2014 ${p.live_url} (id: ${p.id}, ${p.mode}, published ${p.published_at}${expires})`;
|
|
22318
|
+
});
|
|
22319
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
22320
|
+
}
|
|
22321
|
+
);
|
|
22322
|
+
server.tool(
|
|
22323
|
+
"projects.get",
|
|
22324
|
+
"Fetch metadata for a single published project. Returns the project record without the source HTML \u2014 call projects.get_source_chunk to retrieve the HTML in chunks when you need to modify it.",
|
|
22325
|
+
{
|
|
22326
|
+
project_id: external_exports.string().uuid().describe("The project id returned by projects.list")
|
|
22327
|
+
},
|
|
22328
|
+
async (params) => {
|
|
22329
|
+
const data = await apiPost(`/host/published-project/${encodeURIComponent(params.project_id)}`, {
|
|
22330
|
+
agent_id: AGT_AGENT_ID
|
|
22331
|
+
});
|
|
22332
|
+
const p = data.project;
|
|
22333
|
+
const header = [
|
|
22334
|
+
`Project: ${p.title ?? "(untitled)"} [${p.kind}]`,
|
|
22335
|
+
`Publisher: ${p.publisher} (${p.mode})`,
|
|
22336
|
+
`Live URL: ${p.live_url}`,
|
|
22337
|
+
p.claim_url ? `Claim URL: ${p.claim_url}` : null,
|
|
22338
|
+
`Published: ${p.published_at}`,
|
|
22339
|
+
p.expires_at ? `Expires: ${p.expires_at}` : null,
|
|
22340
|
+
`Last modified: ${p.last_modified_at}`,
|
|
22341
|
+
p.has_source_html ? `Source HTML: ${p.source_html_length} bytes (call projects.get_source_chunk to fetch)` : "(source HTML not preserved for this project)"
|
|
22342
|
+
].filter((s) => s !== null).join("\n");
|
|
22343
|
+
return { content: [{ type: "text", text: header }] };
|
|
22344
|
+
}
|
|
22345
|
+
);
|
|
22346
|
+
server.tool(
|
|
22347
|
+
"projects.get_source_chunk",
|
|
22348
|
+
"Fetch a chunk of source HTML for a published project. Use this to assemble the HTML for modification when projects.get reports a non-zero source_html_length.",
|
|
22349
|
+
{
|
|
22350
|
+
project_id: external_exports.string().uuid().describe("The project id returned by projects.list"),
|
|
22351
|
+
offset: external_exports.number().int().min(0).describe("Byte offset into the source HTML to start from"),
|
|
22352
|
+
limit: external_exports.number().int().min(1).max(65536).optional().describe("Max bytes to return (default 32KB, max 64KB)")
|
|
22353
|
+
},
|
|
22354
|
+
async (params) => {
|
|
22355
|
+
const data = await apiPost(`/host/published-project/${encodeURIComponent(params.project_id)}/source-chunk`, {
|
|
22356
|
+
agent_id: AGT_AGENT_ID,
|
|
22357
|
+
offset: params.offset,
|
|
22358
|
+
limit: params.limit
|
|
22359
|
+
});
|
|
22360
|
+
const trailer = data.end_of_content ? "\n\n---\n(end of source)" : `
|
|
22361
|
+
|
|
22362
|
+
---
|
|
22363
|
+
(more \u2014 next offset: ${data.offset + data.length}, total: ${data.total_length} bytes)`;
|
|
22364
|
+
return { content: [{ type: "text", text: data.chunk + trailer }] };
|
|
22365
|
+
}
|
|
22366
|
+
);
|
|
22296
22367
|
async function main() {
|
|
22297
22368
|
try {
|
|
22298
22369
|
const result = await registerForwardedApiTools();
|
|
@@ -14890,21 +14890,16 @@ var StdioServerTransport = class {
|
|
|
14890
14890
|
// src/slack-channel.ts
|
|
14891
14891
|
import {
|
|
14892
14892
|
chmodSync,
|
|
14893
|
-
closeSync,
|
|
14894
14893
|
createWriteStream,
|
|
14895
14894
|
existsSync,
|
|
14896
|
-
fsyncSync,
|
|
14897
|
-
ftruncateSync,
|
|
14898
14895
|
mkdirSync as mkdirSync2,
|
|
14899
|
-
openSync,
|
|
14900
14896
|
readFileSync as readFileSync2,
|
|
14901
14897
|
readdirSync,
|
|
14902
14898
|
renameSync,
|
|
14903
|
-
statSync
|
|
14899
|
+
statSync,
|
|
14904
14900
|
unlinkSync,
|
|
14905
14901
|
watch,
|
|
14906
|
-
writeFileSync as writeFileSync2
|
|
14907
|
-
writeSync
|
|
14902
|
+
writeFileSync as writeFileSync2
|
|
14908
14903
|
} from "fs";
|
|
14909
14904
|
import { basename, join as join2, resolve as resolve2 } from "path";
|
|
14910
14905
|
import { homedir as homedir2 } from "os";
|
|
@@ -15503,40 +15498,6 @@ function createSlackBotUserIdClient(args) {
|
|
|
15503
15498
|
};
|
|
15504
15499
|
}
|
|
15505
15500
|
|
|
15506
|
-
// src/pending-reminder-schedule.ts
|
|
15507
|
-
import { statSync } from "fs";
|
|
15508
|
-
var REMINDER_SCHEDULE_MS = [5, 10, 20].map((m) => m * 6e4);
|
|
15509
|
-
var MAX_REMINDERS = REMINDER_SCHEDULE_MS.length;
|
|
15510
|
-
var RESPONSE_TIMEOUT_MS = REMINDER_SCHEDULE_MS[0];
|
|
15511
|
-
function nextReminderFireAtMs(receivedAtMs, currentReminderCount) {
|
|
15512
|
-
const safeCount = Number.isFinite(currentReminderCount) ? Math.max(0, Math.trunc(currentReminderCount)) : 0;
|
|
15513
|
-
let elapsed = 0;
|
|
15514
|
-
for (let i = 0; i <= safeCount && i < MAX_REMINDERS; i++) {
|
|
15515
|
-
elapsed += REMINDER_SCHEDULE_MS[i];
|
|
15516
|
-
}
|
|
15517
|
-
return receivedAtMs + elapsed;
|
|
15518
|
-
}
|
|
15519
|
-
function reminderText(reminderIndex, cancelCommandAvailable) {
|
|
15520
|
-
switch (reminderIndex) {
|
|
15521
|
-
case 0:
|
|
15522
|
-
return "Heads-up \u2014 I'm still on it. This one's taking more than the usual minute.";
|
|
15523
|
-
case 1:
|
|
15524
|
-
return "Still working. Going deeper than expected; back when I have something useful.";
|
|
15525
|
-
case 2:
|
|
15526
|
-
default:
|
|
15527
|
-
return cancelCommandAvailable ? "I'm 30+ min in on this. Type `/cancel` to abort, or ping me if it's urgent and I'll re-scope." : "I'm 30+ min in on this. If the answer's urgent, ping me and I'll pause to triage. Otherwise I'll keep going.";
|
|
15528
|
-
}
|
|
15529
|
-
}
|
|
15530
|
-
function agentTurnEndedSinceInbound(lastStopAtPath, receivedAtMs, deps = { statSync }) {
|
|
15531
|
-
if (!lastStopAtPath) return false;
|
|
15532
|
-
try {
|
|
15533
|
-
const stat = deps.statSync(lastStopAtPath);
|
|
15534
|
-
return stat.mtimeMs > receivedAtMs;
|
|
15535
|
-
} catch {
|
|
15536
|
-
return false;
|
|
15537
|
-
}
|
|
15538
|
-
}
|
|
15539
|
-
|
|
15540
15501
|
// src/slack-channel.ts
|
|
15541
15502
|
var BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
|
|
15542
15503
|
var APP_TOKEN = process.env.SLACK_APP_TOKEN;
|
|
@@ -15581,12 +15542,9 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
|
|
|
15581
15542
|
peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
|
|
15582
15543
|
peer_disabled_mode: SLACK_PEER_DISABLED_MODE
|
|
15583
15544
|
};
|
|
15584
|
-
var pendingMessages = /* @__PURE__ */ new Map();
|
|
15585
|
-
var CANCEL_COMMAND_AVAILABLE = false;
|
|
15586
15545
|
var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join2(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
15587
15546
|
var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join2(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
|
|
15588
15547
|
var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join2(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
|
|
15589
|
-
var LAST_STOP_AT_PATH = SLACK_AGENT_DIR ? join2(SLACK_AGENT_DIR, "last-stop-at") : null;
|
|
15590
15548
|
var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
|
|
15591
15549
|
function redactSlackId(id) {
|
|
15592
15550
|
if (!id) return "<none>";
|
|
@@ -15619,57 +15577,6 @@ function writeSlackPendingInboundMarker(channel, threadTs, messageTs) {
|
|
|
15619
15577
|
);
|
|
15620
15578
|
}
|
|
15621
15579
|
}
|
|
15622
|
-
function updateSlackPendingInboundMarker(marker) {
|
|
15623
|
-
const path = slackPendingInboundPath(marker.channel, marker.thread_ts, marker.message_ts);
|
|
15624
|
-
if (!path) return false;
|
|
15625
|
-
let fd;
|
|
15626
|
-
try {
|
|
15627
|
-
fd = openSync(path, "r+");
|
|
15628
|
-
} catch (err) {
|
|
15629
|
-
const code = err.code;
|
|
15630
|
-
if (code === "ENOENT") return false;
|
|
15631
|
-
process.stderr.write(
|
|
15632
|
-
`slack-channel(${AGENT_CODE_NAME}): pending-inbound marker open-for-update failed: ${err.message}
|
|
15633
|
-
`
|
|
15634
|
-
);
|
|
15635
|
-
return false;
|
|
15636
|
-
}
|
|
15637
|
-
try {
|
|
15638
|
-
const payload = Buffer.from(JSON.stringify(marker));
|
|
15639
|
-
ftruncateSync(fd, 0);
|
|
15640
|
-
writeSync(fd, payload, 0, payload.length, 0);
|
|
15641
|
-
fsyncSync(fd);
|
|
15642
|
-
return true;
|
|
15643
|
-
} catch (err) {
|
|
15644
|
-
process.stderr.write(
|
|
15645
|
-
`slack-channel(${AGENT_CODE_NAME}): pending-inbound marker write failed: ${err.message}
|
|
15646
|
-
`
|
|
15647
|
-
);
|
|
15648
|
-
return false;
|
|
15649
|
-
} finally {
|
|
15650
|
-
try {
|
|
15651
|
-
closeSync(fd);
|
|
15652
|
-
} catch {
|
|
15653
|
-
}
|
|
15654
|
-
}
|
|
15655
|
-
}
|
|
15656
|
-
function readSlackPendingInboundMarker(channel, threadTs, messageTs) {
|
|
15657
|
-
const path = slackPendingInboundPath(channel, threadTs, messageTs);
|
|
15658
|
-
if (!path || !existsSync(path)) return null;
|
|
15659
|
-
try {
|
|
15660
|
-
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
15661
|
-
} catch {
|
|
15662
|
-
return null;
|
|
15663
|
-
}
|
|
15664
|
-
}
|
|
15665
|
-
function clearSlackPendingInboundMarker(channel, threadTs, messageTs) {
|
|
15666
|
-
const path = slackPendingInboundPath(channel, threadTs, messageTs);
|
|
15667
|
-
if (!path) return;
|
|
15668
|
-
try {
|
|
15669
|
-
if (existsSync(path)) unlinkSync(path);
|
|
15670
|
-
} catch {
|
|
15671
|
-
}
|
|
15672
|
-
}
|
|
15673
15580
|
function clearAllSlackPendingMarkersForThread(channel, threadTs) {
|
|
15674
15581
|
if (!SLACK_PENDING_INBOUND_DIR) return;
|
|
15675
15582
|
const safeChan = channel.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
@@ -15816,7 +15723,7 @@ function scanSlackRecoveryRetries() {
|
|
|
15816
15723
|
if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
|
|
15817
15724
|
let mtimeMs;
|
|
15818
15725
|
try {
|
|
15819
|
-
mtimeMs =
|
|
15726
|
+
mtimeMs = statSync(join2(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
|
|
15820
15727
|
} catch {
|
|
15821
15728
|
continue;
|
|
15822
15729
|
}
|
|
@@ -15861,105 +15768,11 @@ function startSlackRecoveryOutboxWatcher() {
|
|
|
15861
15768
|
retryTimer.unref?.();
|
|
15862
15769
|
}
|
|
15863
15770
|
startSlackRecoveryOutboxWatcher();
|
|
15864
|
-
var
|
|
15865
|
-
async function slackFetchWithTimeout(url, body) {
|
|
15866
|
-
const controller = new AbortController();
|
|
15867
|
-
const timeoutId = setTimeout(() => controller.abort(), SLACK_REMINDER_FETCH_TIMEOUT_MS);
|
|
15868
|
-
try {
|
|
15869
|
-
await fetch(url, {
|
|
15870
|
-
method: "POST",
|
|
15871
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${BOT_TOKEN}` },
|
|
15872
|
-
body: JSON.stringify(body),
|
|
15873
|
-
signal: controller.signal
|
|
15874
|
-
});
|
|
15875
|
-
} finally {
|
|
15876
|
-
clearTimeout(timeoutId);
|
|
15877
|
-
}
|
|
15878
|
-
}
|
|
15879
|
-
async function fireSlackResponseTimeout(channel, threadTs, messageTs) {
|
|
15880
|
-
const marker = readSlackPendingInboundMarker(channel, threadTs, messageTs);
|
|
15881
|
-
if (!marker) {
|
|
15882
|
-
return;
|
|
15883
|
-
}
|
|
15884
|
-
const receivedAtMs = Date.parse(marker.received_at);
|
|
15885
|
-
if (Number.isFinite(receivedAtMs) && agentTurnEndedSinceInbound(LAST_STOP_AT_PATH, receivedAtMs)) {
|
|
15886
|
-
process.stderr.write(
|
|
15887
|
-
`slack-channel(${AGENT_CODE_NAME}): suppressing reminder for ${redactSlackId(messageTs)} in ${redactSlackId(channel)} \u2014 turn ended after inbound at ${marker.received_at}; clearing marker
|
|
15888
|
-
`
|
|
15889
|
-
);
|
|
15890
|
-
const key2 = `${channel}:${threadTs}:${messageTs}`;
|
|
15891
|
-
const existing = pendingMessages.get(key2);
|
|
15892
|
-
if (existing) {
|
|
15893
|
-
clearTimeout(existing);
|
|
15894
|
-
pendingMessages.delete(key2);
|
|
15895
|
-
}
|
|
15896
|
-
clearSlackPendingInboundMarker(channel, threadTs, messageTs);
|
|
15897
|
-
return;
|
|
15898
|
-
}
|
|
15899
|
-
const reminderIndex = marker.reminder_count ?? 0;
|
|
15900
|
-
if (reminderIndex >= MAX_REMINDERS) {
|
|
15901
|
-
return;
|
|
15902
|
-
}
|
|
15903
|
-
try {
|
|
15904
|
-
await slackFetchWithTimeout("https://slack.com/api/chat.postMessage", {
|
|
15905
|
-
channel,
|
|
15906
|
-
thread_ts: threadTs,
|
|
15907
|
-
text: reminderText(reminderIndex, CANCEL_COMMAND_AVAILABLE)
|
|
15908
|
-
});
|
|
15909
|
-
} catch {
|
|
15910
|
-
}
|
|
15911
|
-
if (reminderIndex === 1) {
|
|
15912
|
-
try {
|
|
15913
|
-
await slackFetchWithTimeout("https://slack.com/api/reactions.remove", {
|
|
15914
|
-
channel,
|
|
15915
|
-
timestamp: messageTs,
|
|
15916
|
-
name: "eyes"
|
|
15917
|
-
});
|
|
15918
|
-
} catch {
|
|
15919
|
-
}
|
|
15920
|
-
try {
|
|
15921
|
-
await slackFetchWithTimeout("https://slack.com/api/reactions.add", {
|
|
15922
|
-
channel,
|
|
15923
|
-
timestamp: messageTs,
|
|
15924
|
-
name: "hourglass_flowing_sand"
|
|
15925
|
-
});
|
|
15926
|
-
} catch {
|
|
15927
|
-
}
|
|
15928
|
-
}
|
|
15929
|
-
const nextCount = reminderIndex + 1;
|
|
15930
|
-
const persisted = updateSlackPendingInboundMarker({ ...marker, reminder_count: nextCount });
|
|
15931
|
-
if (!persisted) {
|
|
15932
|
-
process.stderr.write(
|
|
15933
|
-
`slack-channel(${AGENT_CODE_NAME}): reminder ${nextCount}/${MAX_REMINDERS} posted but marker cleared mid-fire (agent replied) \u2014 not arming next timer
|
|
15934
|
-
`
|
|
15935
|
-
);
|
|
15936
|
-
return;
|
|
15937
|
-
}
|
|
15938
|
-
process.stderr.write(
|
|
15939
|
-
`slack-channel(${AGENT_CODE_NAME}): reminder ${nextCount}/${MAX_REMINDERS} posted for message ${redactSlackId(messageTs)} in ${redactSlackId(channel)} (marker left pending)
|
|
15940
|
-
`
|
|
15941
|
-
);
|
|
15942
|
-
if (nextCount < MAX_REMINDERS) {
|
|
15943
|
-
armSlackPendingTimer(channel, threadTs, messageTs, REMINDER_SCHEDULE_MS[nextCount]);
|
|
15944
|
-
}
|
|
15945
|
-
}
|
|
15946
|
-
function armSlackPendingTimer(channel, threadTs, messageTs, durationMs) {
|
|
15947
|
-
const key2 = `${channel}:${threadTs}:${messageTs}`;
|
|
15948
|
-
const existing = pendingMessages.get(key2);
|
|
15949
|
-
if (existing) clearTimeout(existing);
|
|
15950
|
-
const timer = setTimeout(() => {
|
|
15951
|
-
pendingMessages.delete(key2);
|
|
15952
|
-
void fireSlackResponseTimeout(channel, threadTs, messageTs);
|
|
15953
|
-
}, Math.max(0, durationMs));
|
|
15954
|
-
timer.unref?.();
|
|
15955
|
-
pendingMessages.set(key2, timer);
|
|
15956
|
-
}
|
|
15771
|
+
var STALE_MARKER_MS = 24 * 60 * 60 * 1e3;
|
|
15957
15772
|
function trackPendingMessage(channel, threadTs, messageTs) {
|
|
15958
15773
|
writeSlackPendingInboundMarker(channel, threadTs, messageTs);
|
|
15959
|
-
armSlackPendingTimer(channel, threadTs, messageTs, RESPONSE_TIMEOUT_MS);
|
|
15960
15774
|
}
|
|
15961
|
-
|
|
15962
|
-
function rearmSlackPendingTimersFromDisk() {
|
|
15775
|
+
function sweepSlackStaleMarkersOnBoot() {
|
|
15963
15776
|
if (!SLACK_PENDING_INBOUND_DIR) return;
|
|
15964
15777
|
if (!existsSync(SLACK_PENDING_INBOUND_DIR)) return;
|
|
15965
15778
|
let filenames;
|
|
@@ -15967,16 +15780,13 @@ function rearmSlackPendingTimersFromDisk() {
|
|
|
15967
15780
|
filenames = readdirSync(SLACK_PENDING_INBOUND_DIR);
|
|
15968
15781
|
} catch (err) {
|
|
15969
15782
|
process.stderr.write(
|
|
15970
|
-
`slack-channel(${AGENT_CODE_NAME}):
|
|
15783
|
+
`slack-channel(${AGENT_CODE_NAME}): stale-marker readdir failed: ${err.message}
|
|
15971
15784
|
`
|
|
15972
15785
|
);
|
|
15973
15786
|
return;
|
|
15974
15787
|
}
|
|
15975
15788
|
const now = Date.now();
|
|
15976
|
-
let armed = 0;
|
|
15977
|
-
let firedNow = 0;
|
|
15978
15789
|
let cleared = 0;
|
|
15979
|
-
let exhausted = 0;
|
|
15980
15790
|
for (const filename of filenames) {
|
|
15981
15791
|
if (!filename.endsWith(".json")) continue;
|
|
15982
15792
|
if (filename.endsWith(".tmp")) continue;
|
|
@@ -15986,25 +15796,18 @@ function rearmSlackPendingTimersFromDisk() {
|
|
|
15986
15796
|
marker = JSON.parse(readFileSync2(fullPath, "utf-8"));
|
|
15987
15797
|
} catch (err) {
|
|
15988
15798
|
process.stderr.write(
|
|
15989
|
-
`slack-channel(${AGENT_CODE_NAME}):
|
|
15799
|
+
`slack-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactSlackId(filename)}: ${err.message}
|
|
15990
15800
|
`
|
|
15991
15801
|
);
|
|
15992
15802
|
try {
|
|
15993
15803
|
unlinkSync(fullPath);
|
|
15994
15804
|
} catch {
|
|
15995
15805
|
}
|
|
15806
|
+
cleared++;
|
|
15996
15807
|
continue;
|
|
15997
15808
|
}
|
|
15998
15809
|
const { channel, thread_ts, message_ts, received_at } = marker;
|
|
15999
15810
|
if (!channel || !thread_ts || !message_ts || !received_at) {
|
|
16000
|
-
try {
|
|
16001
|
-
unlinkSync(fullPath);
|
|
16002
|
-
} catch {
|
|
16003
|
-
}
|
|
16004
|
-
continue;
|
|
16005
|
-
}
|
|
16006
|
-
const receivedAtMs = Date.parse(received_at);
|
|
16007
|
-
if (!Number.isFinite(receivedAtMs)) {
|
|
16008
15811
|
try {
|
|
16009
15812
|
unlinkSync(fullPath);
|
|
16010
15813
|
} catch {
|
|
@@ -16012,49 +15815,24 @@ function rearmSlackPendingTimersFromDisk() {
|
|
|
16012
15815
|
cleared++;
|
|
16013
15816
|
continue;
|
|
16014
15817
|
}
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
`slack-channel(${AGENT_CODE_NAME}): rearm skipping stale marker channel=${redactSlackId(channel)} message=${redactSlackId(message_ts)}
|
|
16018
|
-
`
|
|
16019
|
-
);
|
|
15818
|
+
const receivedAtMs = Date.parse(received_at);
|
|
15819
|
+
if (!Number.isFinite(receivedAtMs) || receivedAtMs > now || now - receivedAtMs > STALE_MARKER_MS) {
|
|
16020
15820
|
try {
|
|
16021
15821
|
unlinkSync(fullPath);
|
|
16022
15822
|
} catch {
|
|
16023
15823
|
}
|
|
16024
15824
|
cleared++;
|
|
16025
|
-
continue;
|
|
16026
|
-
}
|
|
16027
|
-
const reminderCount = marker.reminder_count ?? 0;
|
|
16028
|
-
if (reminderCount >= MAX_REMINDERS) {
|
|
16029
|
-
exhausted++;
|
|
16030
|
-
continue;
|
|
16031
|
-
}
|
|
16032
|
-
const nextFireAt = nextReminderFireAtMs(receivedAtMs, reminderCount);
|
|
16033
|
-
const remainingMs = nextFireAt - now;
|
|
16034
|
-
if (remainingMs <= 0) {
|
|
16035
|
-
pendingMessages.delete(`${channel}:${thread_ts}:${message_ts}`);
|
|
16036
|
-
void fireSlackResponseTimeout(channel, thread_ts, message_ts);
|
|
16037
|
-
firedNow++;
|
|
16038
|
-
} else {
|
|
16039
|
-
armSlackPendingTimer(channel, thread_ts, message_ts, remainingMs);
|
|
16040
|
-
armed++;
|
|
16041
15825
|
}
|
|
16042
15826
|
}
|
|
16043
|
-
if (
|
|
15827
|
+
if (cleared > 0) {
|
|
16044
15828
|
process.stderr.write(
|
|
16045
|
-
`slack-channel(${AGENT_CODE_NAME}):
|
|
15829
|
+
`slack-channel(${AGENT_CODE_NAME}): stale-marker sweep cleared=${cleared}
|
|
16046
15830
|
`
|
|
16047
15831
|
);
|
|
16048
15832
|
}
|
|
16049
15833
|
}
|
|
16050
|
-
|
|
15834
|
+
sweepSlackStaleMarkersOnBoot();
|
|
16051
15835
|
function clearPendingMessage(channel, threadTs) {
|
|
16052
|
-
for (const [key2, timer] of pendingMessages) {
|
|
16053
|
-
if (key2.startsWith(`${channel}:${threadTs}:`)) {
|
|
16054
|
-
clearTimeout(timer);
|
|
16055
|
-
pendingMessages.delete(key2);
|
|
16056
|
-
}
|
|
16057
|
-
}
|
|
16058
15836
|
clearAllSlackPendingMarkersForThread(channel, threadTs);
|
|
16059
15837
|
}
|
|
16060
15838
|
function noteThreadActivity(channel, threadTs) {
|
|
@@ -16064,16 +15842,24 @@ function noteThreadActivity(channel, threadTs) {
|
|
|
16064
15842
|
function noteThreadActivityByMessageTs(channel, messageTs) {
|
|
16065
15843
|
if (!channel || !messageTs) return;
|
|
16066
15844
|
clearPendingMessage(channel, messageTs);
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
15845
|
+
if (!SLACK_PENDING_INBOUND_DIR) return;
|
|
15846
|
+
if (!existsSync(SLACK_PENDING_INBOUND_DIR)) return;
|
|
15847
|
+
let filenames;
|
|
15848
|
+
try {
|
|
15849
|
+
filenames = readdirSync(SLACK_PENDING_INBOUND_DIR);
|
|
15850
|
+
} catch {
|
|
15851
|
+
return;
|
|
15852
|
+
}
|
|
15853
|
+
const safeChannel = channel.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
15854
|
+
const safeMessageTs = messageTs.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
15855
|
+
const channelPrefix = `${safeChannel}__`;
|
|
15856
|
+
const messageSuffix = `__${safeMessageTs}.json`;
|
|
15857
|
+
for (const filename of filenames) {
|
|
15858
|
+
if (!filename.startsWith(channelPrefix)) continue;
|
|
15859
|
+
if (!filename.endsWith(messageSuffix)) continue;
|
|
15860
|
+
try {
|
|
15861
|
+
unlinkSync(join2(SLACK_PENDING_INBOUND_DIR, filename));
|
|
15862
|
+
} catch {
|
|
16077
15863
|
}
|
|
16078
15864
|
}
|
|
16079
15865
|
}
|
|
@@ -17028,7 +16814,7 @@ ${formatted}` : "Thread is empty or not found."
|
|
|
17028
16814
|
let bytes;
|
|
17029
16815
|
let size;
|
|
17030
16816
|
try {
|
|
17031
|
-
const stat =
|
|
16817
|
+
const stat = statSync(resolvedPath);
|
|
17032
16818
|
if (!stat.isFile()) {
|
|
17033
16819
|
return {
|
|
17034
16820
|
content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }],
|