@integrity-labs/agt-cli 0.28.26 → 0.28.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +3 -3
- package/dist/{chunk-NDGLXWV7.js → chunk-GW2FVIMZ.js} +1 -1
- package/dist/lib/manager-worker.js +191 -174
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +48 -11
- package/package.json +1 -1
- /package/dist/{chunk-NDGLXWV7.js.map → chunk-GW2FVIMZ.js.map} +0 -0
package/dist/bin/agt.js
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
success,
|
|
34
34
|
table,
|
|
35
35
|
warn
|
|
36
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-GW2FVIMZ.js";
|
|
37
37
|
import {
|
|
38
38
|
CHANNEL_REGISTRY,
|
|
39
39
|
DEPLOYMENT_TEMPLATES,
|
|
@@ -4773,7 +4773,7 @@ import { execFileSync, execSync } from "child_process";
|
|
|
4773
4773
|
import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
|
|
4774
4774
|
import chalk18 from "chalk";
|
|
4775
4775
|
import ora16 from "ora";
|
|
4776
|
-
var cliVersion = true ? "0.28.
|
|
4776
|
+
var cliVersion = true ? "0.28.28" : "dev";
|
|
4777
4777
|
async function fetchLatestVersion() {
|
|
4778
4778
|
const host2 = getHost();
|
|
4779
4779
|
if (!host2) return null;
|
|
@@ -5696,7 +5696,7 @@ function handleError(err) {
|
|
|
5696
5696
|
}
|
|
5697
5697
|
|
|
5698
5698
|
// src/bin/agt.ts
|
|
5699
|
-
var cliVersion2 = true ? "0.28.
|
|
5699
|
+
var cliVersion2 = true ? "0.28.28" : "dev";
|
|
5700
5700
|
var program = new Command();
|
|
5701
5701
|
program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
|
|
5702
5702
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
provisionStopHook,
|
|
23
23
|
requireHost,
|
|
24
24
|
safeWriteJsonAtomic
|
|
25
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-GW2FVIMZ.js";
|
|
26
26
|
import {
|
|
27
27
|
getProjectDir as getProjectDir2,
|
|
28
28
|
getReadyTasks,
|
|
@@ -106,7 +106,6 @@ import {
|
|
|
106
106
|
// src/lib/manager-worker.ts
|
|
107
107
|
import { createHash as createHash4 } from "crypto";
|
|
108
108
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync8, rmSync as rmSync3, readdirSync as readdirSync5, statSync as statSync4, unlinkSync, copyFileSync } from "fs";
|
|
109
|
-
import https from "https";
|
|
110
109
|
import { execFileSync as syncExecFile } from "child_process";
|
|
111
110
|
import { join as join14, dirname as dirname3 } from "path";
|
|
112
111
|
import { homedir as homedir7 } from "os";
|
|
@@ -3417,57 +3416,13 @@ function killAgentChannelProcesses(codeName, opts) {
|
|
|
3417
3416
|
return pids;
|
|
3418
3417
|
}
|
|
3419
3418
|
|
|
3420
|
-
// src/lib/delivery-hint.ts
|
|
3421
|
-
var DEFAULT_PROBABILITY = 0.1;
|
|
3422
|
-
function envSuffixFor(codeName) {
|
|
3423
|
-
return codeName.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
3424
|
-
}
|
|
3425
|
-
function hintProbability(codeName, env = process.env) {
|
|
3426
|
-
const suffix = codeName ? `__${envSuffixFor(codeName)}` : "";
|
|
3427
|
-
if (suffix && env[`AGT_DELIVERY_HINT_DISABLED${suffix}`] === "1") return 0;
|
|
3428
|
-
const perAgent = suffix ? env[`AGT_DELIVERY_HINT_PROBABILITY${suffix}`] : void 0;
|
|
3429
|
-
if (perAgent != null) return clampProbability(perAgent);
|
|
3430
|
-
if (env["AGT_DELIVERY_HINT_DISABLED"] === "1") return 0;
|
|
3431
|
-
const host = env["AGT_DELIVERY_HINT_PROBABILITY"];
|
|
3432
|
-
if (host != null) return clampProbability(host);
|
|
3433
|
-
return DEFAULT_PROBABILITY;
|
|
3434
|
-
}
|
|
3435
|
-
function clampProbability(raw) {
|
|
3436
|
-
const parsed = parseFloat(raw);
|
|
3437
|
-
if (!Number.isFinite(parsed)) return DEFAULT_PROBABILITY;
|
|
3438
|
-
if (parsed < 0) return 0;
|
|
3439
|
-
if (parsed > 1) return 1;
|
|
3440
|
-
return parsed;
|
|
3441
|
-
}
|
|
3442
|
-
function shouldIncludeHint(probability, rng = Math.random) {
|
|
3443
|
-
if (probability <= 0) return false;
|
|
3444
|
-
if (probability >= 1) return true;
|
|
3445
|
-
return rng() < probability;
|
|
3446
|
-
}
|
|
3447
|
-
var HINT_VARIANTS = Object.freeze([
|
|
3448
|
-
'Quick note: you can change this scheduled task just by asking me \u2014 e.g. "Change the schedule for this task" or "Make this report less verbose in future".',
|
|
3449
|
-
`By the way, this is on a schedule \u2014 if you'd like to tweak it, just say something like "run this weekly instead of daily" or "make this report less verbose in future" and I'll handle it.`,
|
|
3450
|
-
'Heads up: I deliver this on a schedule you can edit conversationally. Try "Change the schedule for this task" or "Make this report shorter" whenever you want to adjust it.',
|
|
3451
|
-
`PS \u2014 no UI needed to tune this. Just tell me "Send this at 9am instead" or "Skip weekends for this report" and I'll update the schedule.`,
|
|
3452
|
-
'FYI this is a scheduled delivery. You can reshape it in plain English \u2014 e.g. "Make this fortnightly" or "Only include items from the last 24 hours".',
|
|
3453
|
-
`You're the boss of this schedule \u2014 ask me things like "Pause this for two weeks" or "Include a summary at the top next time" and I'll apply it.`,
|
|
3454
|
-
`Side note: you can say "Change this to Mondays only" or "Drop the preamble in future reports" and I'll update the task. No config screen required.`,
|
|
3455
|
-
'Reminder: I run this on a schedule you can edit by talking. Good openers: "Change when this fires" or "Make future reports more concise".',
|
|
3456
|
-
`Small tip \u2014 this delivery is editable on the fly. Try "Move this to afternoons" or "Cut the detail down in future runs" and I'll retune it.`,
|
|
3457
|
-
`If this cadence or format isn't quite right, just ask \u2014 "Only run this on weekdays" or "Shorter summaries from now on" both work, no form to fill out.`
|
|
3458
|
-
]);
|
|
3459
|
-
function pickHintVariant(rng = Math.random) {
|
|
3460
|
-
const idx = Math.floor(rng() * HINT_VARIANTS.length) % HINT_VARIANTS.length;
|
|
3461
|
-
return HINT_VARIANTS[idx];
|
|
3462
|
-
}
|
|
3463
|
-
|
|
3464
3419
|
// src/lib/delivery-schedule-link.ts
|
|
3465
|
-
function
|
|
3420
|
+
function envSuffixFor(codeName) {
|
|
3466
3421
|
return codeName.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
3467
3422
|
}
|
|
3468
3423
|
function scheduleLinkFooterEnabled(codeName, env = process.env) {
|
|
3469
3424
|
if (codeName) {
|
|
3470
|
-
const perAgent = env[`AGT_SCHEDULE_LINK_FOOTER_DISABLED__${
|
|
3425
|
+
const perAgent = env[`AGT_SCHEDULE_LINK_FOOTER_DISABLED__${envSuffixFor(codeName)}`];
|
|
3471
3426
|
if (perAgent === "1") return false;
|
|
3472
3427
|
}
|
|
3473
3428
|
if (env["AGT_SCHEDULE_LINK_FOOTER_DISABLED"] === "1") return false;
|
|
@@ -4098,6 +4053,189 @@ function formatBoardForPrompt(items, template) {
|
|
|
4098
4053
|
return lines.join("\n");
|
|
4099
4054
|
}
|
|
4100
4055
|
|
|
4056
|
+
// src/lib/manager/channels/state.ts
|
|
4057
|
+
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
4058
|
+
var alertSlackWebhook = null;
|
|
4059
|
+
function getAlertSlackWebhook() {
|
|
4060
|
+
return alertSlackWebhook;
|
|
4061
|
+
}
|
|
4062
|
+
function setAlertSlackWebhook(value) {
|
|
4063
|
+
alertSlackWebhook = value;
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
// src/lib/delivery-hint.ts
|
|
4067
|
+
var DEFAULT_PROBABILITY = 0.1;
|
|
4068
|
+
function envSuffixFor2(codeName) {
|
|
4069
|
+
return codeName.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
4070
|
+
}
|
|
4071
|
+
function hintProbability(codeName, env = process.env) {
|
|
4072
|
+
const suffix = codeName ? `__${envSuffixFor2(codeName)}` : "";
|
|
4073
|
+
if (suffix && env[`AGT_DELIVERY_HINT_DISABLED${suffix}`] === "1") return 0;
|
|
4074
|
+
const perAgent = suffix ? env[`AGT_DELIVERY_HINT_PROBABILITY${suffix}`] : void 0;
|
|
4075
|
+
if (perAgent != null) return clampProbability(perAgent);
|
|
4076
|
+
if (env["AGT_DELIVERY_HINT_DISABLED"] === "1") return 0;
|
|
4077
|
+
const host = env["AGT_DELIVERY_HINT_PROBABILITY"];
|
|
4078
|
+
if (host != null) return clampProbability(host);
|
|
4079
|
+
return DEFAULT_PROBABILITY;
|
|
4080
|
+
}
|
|
4081
|
+
function clampProbability(raw) {
|
|
4082
|
+
const parsed = parseFloat(raw);
|
|
4083
|
+
if (!Number.isFinite(parsed)) return DEFAULT_PROBABILITY;
|
|
4084
|
+
if (parsed < 0) return 0;
|
|
4085
|
+
if (parsed > 1) return 1;
|
|
4086
|
+
return parsed;
|
|
4087
|
+
}
|
|
4088
|
+
function shouldIncludeHint(probability, rng = Math.random) {
|
|
4089
|
+
if (probability <= 0) return false;
|
|
4090
|
+
if (probability >= 1) return true;
|
|
4091
|
+
return rng() < probability;
|
|
4092
|
+
}
|
|
4093
|
+
var HINT_VARIANTS = Object.freeze([
|
|
4094
|
+
'Quick note: you can change this scheduled task just by asking me \u2014 e.g. "Change the schedule for this task" or "Make this report less verbose in future".',
|
|
4095
|
+
`By the way, this is on a schedule \u2014 if you'd like to tweak it, just say something like "run this weekly instead of daily" or "make this report less verbose in future" and I'll handle it.`,
|
|
4096
|
+
'Heads up: I deliver this on a schedule you can edit conversationally. Try "Change the schedule for this task" or "Make this report shorter" whenever you want to adjust it.',
|
|
4097
|
+
`PS \u2014 no UI needed to tune this. Just tell me "Send this at 9am instead" or "Skip weekends for this report" and I'll update the schedule.`,
|
|
4098
|
+
'FYI this is a scheduled delivery. You can reshape it in plain English \u2014 e.g. "Make this fortnightly" or "Only include items from the last 24 hours".',
|
|
4099
|
+
`You're the boss of this schedule \u2014 ask me things like "Pause this for two weeks" or "Include a summary at the top next time" and I'll apply it.`,
|
|
4100
|
+
`Side note: you can say "Change this to Mondays only" or "Drop the preamble in future reports" and I'll update the task. No config screen required.`,
|
|
4101
|
+
'Reminder: I run this on a schedule you can edit by talking. Good openers: "Change when this fires" or "Make future reports more concise".',
|
|
4102
|
+
`Small tip \u2014 this delivery is editable on the fly. Try "Move this to afternoons" or "Cut the detail down in future runs" and I'll retune it.`,
|
|
4103
|
+
`If this cadence or format isn't quite right, just ask \u2014 "Only run this on weekdays" or "Shorter summaries from now on" both work, no form to fill out.`
|
|
4104
|
+
]);
|
|
4105
|
+
function pickHintVariant(rng = Math.random) {
|
|
4106
|
+
const idx = Math.floor(rng() * HINT_VARIANTS.length) % HINT_VARIANTS.length;
|
|
4107
|
+
return HINT_VARIANTS[idx];
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
// src/lib/manager/channels/slack.ts
|
|
4111
|
+
async function sendSlackWebhookMessage(text) {
|
|
4112
|
+
const alertSlackWebhook2 = getAlertSlackWebhook();
|
|
4113
|
+
if (!alertSlackWebhook2) {
|
|
4114
|
+
log("sendSlackWebhookMessage: no alertSlackWebhook configured \u2014 message dropped");
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
try {
|
|
4118
|
+
const controller = new AbortController();
|
|
4119
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
4120
|
+
try {
|
|
4121
|
+
const response = await fetch(alertSlackWebhook2, {
|
|
4122
|
+
method: "POST",
|
|
4123
|
+
headers: { "Content-Type": "application/json" },
|
|
4124
|
+
body: JSON.stringify({ text }),
|
|
4125
|
+
signal: controller.signal
|
|
4126
|
+
});
|
|
4127
|
+
if (!response.ok) {
|
|
4128
|
+
log(`Slack webhook failed: ${response.status} ${response.statusText}`);
|
|
4129
|
+
}
|
|
4130
|
+
} finally {
|
|
4131
|
+
clearTimeout(timeout);
|
|
4132
|
+
}
|
|
4133
|
+
} catch (err) {
|
|
4134
|
+
log(`Slack webhook error: ${err.message}`);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
async function sendSlackChannelMessage(agentCodeName, channelId, text) {
|
|
4138
|
+
const result = await postSlackChannelMessage(agentCodeName, channelId, text);
|
|
4139
|
+
return result.ok;
|
|
4140
|
+
}
|
|
4141
|
+
async function postSlackChannelMessage(agentCodeName, channelId, text, threadTs) {
|
|
4142
|
+
const botToken = agentChannelTokens.get(agentCodeName)?.slack;
|
|
4143
|
+
if (!botToken) {
|
|
4144
|
+
log(`No Slack bot token cached for '${agentCodeName}' \u2014 cannot post to ${channelId}`);
|
|
4145
|
+
return { ok: false };
|
|
4146
|
+
}
|
|
4147
|
+
try {
|
|
4148
|
+
const controller = new AbortController();
|
|
4149
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
4150
|
+
try {
|
|
4151
|
+
const body = { channel: channelId, text };
|
|
4152
|
+
if (threadTs) body.thread_ts = threadTs;
|
|
4153
|
+
const response = await fetch("https://slack.com/api/chat.postMessage", {
|
|
4154
|
+
method: "POST",
|
|
4155
|
+
headers: {
|
|
4156
|
+
"Authorization": `Bearer ${botToken}`,
|
|
4157
|
+
"Content-Type": "application/json"
|
|
4158
|
+
},
|
|
4159
|
+
body: JSON.stringify(body),
|
|
4160
|
+
signal: controller.signal
|
|
4161
|
+
});
|
|
4162
|
+
clearTimeout(timeout);
|
|
4163
|
+
const data = await response.json();
|
|
4164
|
+
if (!data.ok) {
|
|
4165
|
+
log(`Slack chat.postMessage failed for '${agentCodeName}' to ${channelId}: ${data.error}`);
|
|
4166
|
+
return { ok: false, error: data.error };
|
|
4167
|
+
}
|
|
4168
|
+
return { ok: true, ts: data.ts };
|
|
4169
|
+
} finally {
|
|
4170
|
+
clearTimeout(timeout);
|
|
4171
|
+
}
|
|
4172
|
+
} catch (err) {
|
|
4173
|
+
log(`Slack channel message error for '${agentCodeName}': ${err.message}`);
|
|
4174
|
+
return { ok: false };
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
async function maybePostSlackThreadHint(agentCodeName, channelId, primaryTs) {
|
|
4178
|
+
if (!shouldIncludeHint(hintProbability(agentCodeName))) return;
|
|
4179
|
+
if (!primaryTs) {
|
|
4180
|
+
return;
|
|
4181
|
+
}
|
|
4182
|
+
const hint = pickHintVariant();
|
|
4183
|
+
const result = await postSlackChannelMessage(agentCodeName, channelId, hint, primaryTs);
|
|
4184
|
+
if (result.ok) {
|
|
4185
|
+
log(`[delivery-hint] Slack thread hint posted for '${agentCodeName}'`);
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
// src/lib/manager/channels/telegram.ts
|
|
4190
|
+
import https from "https";
|
|
4191
|
+
function telegramApiCall(botToken, method, body) {
|
|
4192
|
+
return new Promise((resolve, reject) => {
|
|
4193
|
+
const postData = JSON.stringify(body);
|
|
4194
|
+
const req = https.request({
|
|
4195
|
+
hostname: "api.telegram.org",
|
|
4196
|
+
port: 443,
|
|
4197
|
+
path: `/bot${botToken}/${method}`,
|
|
4198
|
+
method: "POST",
|
|
4199
|
+
family: 4,
|
|
4200
|
+
timeout: 1e4,
|
|
4201
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(postData) }
|
|
4202
|
+
}, (res) => {
|
|
4203
|
+
let data = "";
|
|
4204
|
+
res.on("data", (d) => {
|
|
4205
|
+
data += d;
|
|
4206
|
+
});
|
|
4207
|
+
res.on("end", () => {
|
|
4208
|
+
try {
|
|
4209
|
+
resolve(JSON.parse(data));
|
|
4210
|
+
} catch {
|
|
4211
|
+
reject(new Error("Invalid JSON from Telegram API"));
|
|
4212
|
+
}
|
|
4213
|
+
});
|
|
4214
|
+
});
|
|
4215
|
+
req.on("error", reject);
|
|
4216
|
+
req.on("timeout", () => {
|
|
4217
|
+
req.destroy();
|
|
4218
|
+
reject(new Error("Telegram API timeout"));
|
|
4219
|
+
});
|
|
4220
|
+
req.write(postData);
|
|
4221
|
+
req.end();
|
|
4222
|
+
});
|
|
4223
|
+
}
|
|
4224
|
+
async function maybeSendTelegramFollowUpHint(agentCodeName, botToken, chatId) {
|
|
4225
|
+
if (!shouldIncludeHint(hintProbability(agentCodeName))) return;
|
|
4226
|
+
const hint = pickHintVariant();
|
|
4227
|
+
try {
|
|
4228
|
+
const result = await telegramApiCall(botToken, "sendMessage", { chat_id: chatId, text: hint });
|
|
4229
|
+
if (!result.ok) {
|
|
4230
|
+
log(`[delivery-hint] Telegram follow-up failed for '${agentCodeName}': ${result.description ?? "unknown"}`);
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
log(`[delivery-hint] Telegram follow-up hint posted for '${agentCodeName}'`);
|
|
4234
|
+
} catch (err) {
|
|
4235
|
+
log(`[delivery-hint] Telegram follow-up failed for '${agentCodeName}': ${err.message}`);
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4101
4239
|
// src/lib/wedge-detection.ts
|
|
4102
4240
|
var DEFAULTS = {
|
|
4103
4241
|
inboundWaitSeconds: 120,
|
|
@@ -5578,13 +5716,8 @@ var STALE_TASK_THRESHOLD_MS = (() => {
|
|
|
5578
5716
|
if (Number.isFinite(minutes) && minutes > 0) return minutes * 60 * 1e3;
|
|
5579
5717
|
return 30 * 60 * 1e3;
|
|
5580
5718
|
})();
|
|
5581
|
-
var alertSlackWebhook = null;
|
|
5582
5719
|
var alertedJobs = /* @__PURE__ */ new Set();
|
|
5583
5720
|
var taskDisplayInfo = /* @__PURE__ */ new Map();
|
|
5584
|
-
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
5585
|
-
function __setAgentChannelTokensForTest(codeName, tokens) {
|
|
5586
|
-
agentChannelTokens.set(codeName, tokens);
|
|
5587
|
-
}
|
|
5588
5721
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
5589
5722
|
var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
|
|
5590
5723
|
var state6 = {
|
|
@@ -5608,6 +5741,7 @@ function resolveAgentFramework(codeName) {
|
|
|
5608
5741
|
var gatewayPool = null;
|
|
5609
5742
|
function clearAgentCaches(agentId, codeName) {
|
|
5610
5743
|
const channelCacheMutated = clearAgentState(agentId, codeName);
|
|
5744
|
+
agentChannelTokens.delete(codeName);
|
|
5611
5745
|
agentFrameworkCache.delete(codeName);
|
|
5612
5746
|
kanbanBoardCache.delete(codeName);
|
|
5613
5747
|
lastHarvestAt.delete(codeName);
|
|
@@ -5629,7 +5763,7 @@ var cachedMaintenanceWindow = null;
|
|
|
5629
5763
|
var lastVersionCheckAt = 0;
|
|
5630
5764
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
5631
5765
|
var lastResponsivenessProbeAt = 0;
|
|
5632
|
-
var agtCliVersion = true ? "0.28.
|
|
5766
|
+
var agtCliVersion = true ? "0.28.28" : "dev";
|
|
5633
5767
|
function resolveBrewPath(execFileSync4) {
|
|
5634
5768
|
try {
|
|
5635
5769
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -7531,10 +7665,10 @@ async function processAgent(agent, agentStates) {
|
|
|
7531
7665
|
});
|
|
7532
7666
|
return;
|
|
7533
7667
|
}
|
|
7534
|
-
if (!
|
|
7668
|
+
if (!getAlertSlackWebhook() && refreshData.team?.settings) {
|
|
7535
7669
|
const webhook = refreshData.team.settings["alert_slack_webhook"];
|
|
7536
7670
|
if (typeof webhook === "string" && webhook.startsWith("https://")) {
|
|
7537
|
-
|
|
7671
|
+
setAlertSlackWebhook(webhook);
|
|
7538
7672
|
}
|
|
7539
7673
|
}
|
|
7540
7674
|
if (!refreshData.charter || !refreshData.tools) {
|
|
@@ -10820,7 +10954,7 @@ async function monitorCronHealth(agentStates) {
|
|
|
10820
10954
|
for (const alert of alerts) {
|
|
10821
10955
|
log(`ALERT [${alert.type}] ${alert.agentCodeName}/${alert.jobName}: ${alert.detail}`);
|
|
10822
10956
|
}
|
|
10823
|
-
if (
|
|
10957
|
+
if (getAlertSlackWebhook()) {
|
|
10824
10958
|
await sendSlackAlert(alerts);
|
|
10825
10959
|
}
|
|
10826
10960
|
try {
|
|
@@ -10828,122 +10962,6 @@ async function monitorCronHealth(agentStates) {
|
|
|
10828
10962
|
} catch {
|
|
10829
10963
|
}
|
|
10830
10964
|
}
|
|
10831
|
-
function telegramApiCall(botToken, method, body) {
|
|
10832
|
-
return new Promise((resolve, reject) => {
|
|
10833
|
-
const postData = JSON.stringify(body);
|
|
10834
|
-
const req = https.request({
|
|
10835
|
-
hostname: "api.telegram.org",
|
|
10836
|
-
port: 443,
|
|
10837
|
-
path: `/bot${botToken}/${method}`,
|
|
10838
|
-
method: "POST",
|
|
10839
|
-
family: 4,
|
|
10840
|
-
timeout: 1e4,
|
|
10841
|
-
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(postData) }
|
|
10842
|
-
}, (res) => {
|
|
10843
|
-
let data = "";
|
|
10844
|
-
res.on("data", (d) => {
|
|
10845
|
-
data += d;
|
|
10846
|
-
});
|
|
10847
|
-
res.on("end", () => {
|
|
10848
|
-
try {
|
|
10849
|
-
resolve(JSON.parse(data));
|
|
10850
|
-
} catch {
|
|
10851
|
-
reject(new Error("Invalid JSON from Telegram API"));
|
|
10852
|
-
}
|
|
10853
|
-
});
|
|
10854
|
-
});
|
|
10855
|
-
req.on("error", reject);
|
|
10856
|
-
req.on("timeout", () => {
|
|
10857
|
-
req.destroy();
|
|
10858
|
-
reject(new Error("Telegram API timeout"));
|
|
10859
|
-
});
|
|
10860
|
-
req.write(postData);
|
|
10861
|
-
req.end();
|
|
10862
|
-
});
|
|
10863
|
-
}
|
|
10864
|
-
async function sendSlackWebhookMessage(text) {
|
|
10865
|
-
if (!alertSlackWebhook) {
|
|
10866
|
-
log("sendSlackWebhookMessage: no alertSlackWebhook configured \u2014 message dropped");
|
|
10867
|
-
return;
|
|
10868
|
-
}
|
|
10869
|
-
try {
|
|
10870
|
-
const response = await fetch(alertSlackWebhook, {
|
|
10871
|
-
method: "POST",
|
|
10872
|
-
headers: { "Content-Type": "application/json" },
|
|
10873
|
-
body: JSON.stringify({ text })
|
|
10874
|
-
});
|
|
10875
|
-
if (!response.ok) {
|
|
10876
|
-
log(`Slack webhook failed: ${response.status} ${response.statusText}`);
|
|
10877
|
-
}
|
|
10878
|
-
} catch (err) {
|
|
10879
|
-
log(`Slack webhook error: ${err.message}`);
|
|
10880
|
-
}
|
|
10881
|
-
}
|
|
10882
|
-
async function sendSlackChannelMessage(agentCodeName, channelId, text) {
|
|
10883
|
-
const result = await postSlackChannelMessage(agentCodeName, channelId, text);
|
|
10884
|
-
return result.ok;
|
|
10885
|
-
}
|
|
10886
|
-
async function postSlackChannelMessage(agentCodeName, channelId, text, threadTs) {
|
|
10887
|
-
const botToken = agentChannelTokens.get(agentCodeName)?.slack;
|
|
10888
|
-
if (!botToken) {
|
|
10889
|
-
log(`No Slack bot token cached for '${agentCodeName}' \u2014 cannot post to ${channelId}`);
|
|
10890
|
-
return { ok: false };
|
|
10891
|
-
}
|
|
10892
|
-
try {
|
|
10893
|
-
const controller = new AbortController();
|
|
10894
|
-
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
10895
|
-
try {
|
|
10896
|
-
const body = { channel: channelId, text };
|
|
10897
|
-
if (threadTs) body.thread_ts = threadTs;
|
|
10898
|
-
const response = await fetch("https://slack.com/api/chat.postMessage", {
|
|
10899
|
-
method: "POST",
|
|
10900
|
-
headers: {
|
|
10901
|
-
"Authorization": `Bearer ${botToken}`,
|
|
10902
|
-
"Content-Type": "application/json"
|
|
10903
|
-
},
|
|
10904
|
-
body: JSON.stringify(body),
|
|
10905
|
-
signal: controller.signal
|
|
10906
|
-
});
|
|
10907
|
-
clearTimeout(timeout);
|
|
10908
|
-
const data = await response.json();
|
|
10909
|
-
if (!data.ok) {
|
|
10910
|
-
log(`Slack chat.postMessage failed for '${agentCodeName}' to ${channelId}: ${data.error}`);
|
|
10911
|
-
return { ok: false, error: data.error };
|
|
10912
|
-
}
|
|
10913
|
-
return { ok: true, ts: data.ts };
|
|
10914
|
-
} finally {
|
|
10915
|
-
clearTimeout(timeout);
|
|
10916
|
-
}
|
|
10917
|
-
} catch (err) {
|
|
10918
|
-
log(`Slack channel message error for '${agentCodeName}': ${err.message}`);
|
|
10919
|
-
return { ok: false };
|
|
10920
|
-
}
|
|
10921
|
-
}
|
|
10922
|
-
async function maybePostSlackThreadHint(agentCodeName, channelId, primaryTs) {
|
|
10923
|
-
if (!shouldIncludeHint(hintProbability(agentCodeName))) return;
|
|
10924
|
-
if (!primaryTs) {
|
|
10925
|
-
return;
|
|
10926
|
-
}
|
|
10927
|
-
const hint = pickHintVariant();
|
|
10928
|
-
const result = await postSlackChannelMessage(agentCodeName, channelId, hint, primaryTs);
|
|
10929
|
-
if (result.ok) {
|
|
10930
|
-
log(`[delivery-hint] Slack thread hint posted for '${agentCodeName}'`);
|
|
10931
|
-
}
|
|
10932
|
-
}
|
|
10933
|
-
async function maybeSendTelegramFollowUpHint(agentCodeName, botToken, chatId) {
|
|
10934
|
-
if (!shouldIncludeHint(hintProbability(agentCodeName))) return;
|
|
10935
|
-
const hint = pickHintVariant();
|
|
10936
|
-
try {
|
|
10937
|
-
const result = await telegramApiCall(botToken, "sendMessage", { chat_id: chatId, text: hint });
|
|
10938
|
-
if (!result.ok) {
|
|
10939
|
-
log(`[delivery-hint] Telegram follow-up failed for '${agentCodeName}': ${result.description ?? "unknown"}`);
|
|
10940
|
-
return;
|
|
10941
|
-
}
|
|
10942
|
-
log(`[delivery-hint] Telegram follow-up hint posted for '${agentCodeName}'`);
|
|
10943
|
-
} catch (err) {
|
|
10944
|
-
log(`[delivery-hint] Telegram follow-up failed for '${agentCodeName}': ${err.message}`);
|
|
10945
|
-
}
|
|
10946
|
-
}
|
|
10947
10965
|
async function sendSlackAlert(alerts) {
|
|
10948
10966
|
const blocks = alerts.map((a) => {
|
|
10949
10967
|
const emoji = a.type === "late_standup" ? ":warning:" : ":x:";
|
|
@@ -12159,7 +12177,6 @@ export {
|
|
|
12159
12177
|
BACK_ONLINE_GREETING_GUIDANCE,
|
|
12160
12178
|
SCHEDULED_CARD_DELIVERY_CONTRACT,
|
|
12161
12179
|
__resetScheduledDeliveryDedupeForTest,
|
|
12162
|
-
__setAgentChannelTokensForTest,
|
|
12163
12180
|
applyRestartAcks,
|
|
12164
12181
|
buildDoneCardNotification,
|
|
12165
12182
|
buildSchedulerTaskInput,
|