@integrity-labs/agt-cli 0.28.343 → 0.28.344
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-7OBFGSQS.js → chunk-7MP37HYR.js} +13 -1
- package/dist/{chunk-7OBFGSQS.js.map → chunk-7MP37HYR.js.map} +1 -1
- package/dist/{chunk-GAYSFNTX.js → chunk-AJ23F3MU.js} +15 -3
- package/dist/{chunk-GAYSFNTX.js.map → chunk-AJ23F3MU.js.map} +1 -1
- package/dist/{claude-pair-runtime-NUZWSILK.js → claude-pair-runtime-JU3CVFUU.js} +2 -2
- package/dist/lib/manager-worker.js +883 -184
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/origami.js +12 -0
- package/dist/{persistent-session-XWJSAZB4.js → persistent-session-IDWQO7QL.js} +2 -2
- package/dist/{responsiveness-probe-XLS2FXIX.js → responsiveness-probe-WF3SMLS6.js} +2 -2
- package/package.json +1 -1
- /package/dist/{claude-pair-runtime-NUZWSILK.js.map → claude-pair-runtime-JU3CVFUU.js.map} +0 -0
- /package/dist/{persistent-session-XWJSAZB4.js.map → persistent-session-IDWQO7QL.js.map} +0 -0
- /package/dist/{responsiveness-probe-XLS2FXIX.js.map → responsiveness-probe-WF3SMLS6.js.map} +0 -0
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
resolveEffectivePinRaw,
|
|
47
47
|
safeWriteJsonAtomic,
|
|
48
48
|
setConfigHash
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-AJ23F3MU.js";
|
|
50
50
|
import {
|
|
51
51
|
getProjectDir as getProjectDir2,
|
|
52
52
|
getReadyTasks,
|
|
@@ -135,16 +135,16 @@ import {
|
|
|
135
135
|
takeZombieDetection,
|
|
136
136
|
transcriptActivityAgeSeconds,
|
|
137
137
|
writeEgressAllowlist
|
|
138
|
-
} from "../chunk-
|
|
138
|
+
} from "../chunk-7MP37HYR.js";
|
|
139
139
|
import {
|
|
140
140
|
reapOrphanChannelMcps
|
|
141
141
|
} from "../chunk-XWVM4KPK.js";
|
|
142
142
|
|
|
143
143
|
// src/lib/manager-worker.ts
|
|
144
|
-
import { createHash as
|
|
145
|
-
import { readFileSync as
|
|
144
|
+
import { createHash as createHash14 } from "crypto";
|
|
145
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, existsSync as existsSync13, rmSync as rmSync4, readdirSync as readdirSync5, statSync as statSync4, copyFileSync } from "fs";
|
|
146
146
|
import { execFileSync as syncExecFile } from "child_process";
|
|
147
|
-
import { join as
|
|
147
|
+
import { join as join22, dirname as dirname6 } from "path";
|
|
148
148
|
import { homedir as homedir11 } from "os";
|
|
149
149
|
import { fileURLToPath } from "url";
|
|
150
150
|
|
|
@@ -5608,6 +5608,7 @@ async function spawnServe(config2, session) {
|
|
|
5608
5608
|
mkdirSync6(join19(homedir9(), ".augmented", codeName), { recursive: true });
|
|
5609
5609
|
const serveEnv = {
|
|
5610
5610
|
...process.env,
|
|
5611
|
+
...readAgentProviderEnv(codeName),
|
|
5611
5612
|
...stripUndefined(config2.serveEnv ?? {}),
|
|
5612
5613
|
OPENCODE_SERVER_PASSWORD: password,
|
|
5613
5614
|
HOME: process.env.HOME?.trim() || homedir9(),
|
|
@@ -5694,6 +5695,29 @@ function stripUndefined(env) {
|
|
|
5694
5695
|
for (const [k, v] of Object.entries(env)) if (v !== void 0) out[k] = v;
|
|
5695
5696
|
return out;
|
|
5696
5697
|
}
|
|
5698
|
+
var PROVIDER_KEY_RE = /^[A-Z][A-Z0-9_]*_API_KEY$/;
|
|
5699
|
+
function readAgentProviderEnv(codeName, dir) {
|
|
5700
|
+
const file = join19(dir ?? join19(homedir9(), ".augmented", codeName), ".env");
|
|
5701
|
+
const out = {};
|
|
5702
|
+
try {
|
|
5703
|
+
if (!existsSync10(file)) return out;
|
|
5704
|
+
for (const raw of readFileSync16(file, "utf-8").split("\n")) {
|
|
5705
|
+
const line = raw.trim();
|
|
5706
|
+
if (!line || line.startsWith("#")) continue;
|
|
5707
|
+
const eq = line.indexOf("=");
|
|
5708
|
+
if (eq <= 0) continue;
|
|
5709
|
+
const key = line.slice(0, eq).trim();
|
|
5710
|
+
if (!PROVIDER_KEY_RE.test(key)) continue;
|
|
5711
|
+
let val = line.slice(eq + 1).trim();
|
|
5712
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
5713
|
+
val = val.slice(1, -1);
|
|
5714
|
+
}
|
|
5715
|
+
out[key] = val;
|
|
5716
|
+
}
|
|
5717
|
+
} catch {
|
|
5718
|
+
}
|
|
5719
|
+
return out;
|
|
5720
|
+
}
|
|
5697
5721
|
|
|
5698
5722
|
// src/lib/opencode-slack-ingest.ts
|
|
5699
5723
|
import { createHash as createHash11 } from "crypto";
|
|
@@ -6804,6 +6828,677 @@ async function fireOpencodeScheduledTask(agent, task) {
|
|
|
6804
6828
|
}
|
|
6805
6829
|
}
|
|
6806
6830
|
|
|
6831
|
+
// src/lib/opencode-telegram-ingest.ts
|
|
6832
|
+
import { createHash as createHash13 } from "crypto";
|
|
6833
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync17, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync8 } from "fs";
|
|
6834
|
+
import { randomUUID } from "crypto";
|
|
6835
|
+
import { join as join20 } from "path";
|
|
6836
|
+
|
|
6837
|
+
// src/lib/telegram-ingest.ts
|
|
6838
|
+
import https2 from "https";
|
|
6839
|
+
|
|
6840
|
+
// ../../packages/core/dist/channels/governance/telegram-peer-classifier.js
|
|
6841
|
+
function classifyPeerMessage2(msg, cfg, self) {
|
|
6842
|
+
if (!msg.from?.is_bot)
|
|
6843
|
+
return { kind: "human" };
|
|
6844
|
+
if (self.bot_id !== null && msg.from.id === self.bot_id) {
|
|
6845
|
+
return { kind: "self" };
|
|
6846
|
+
}
|
|
6847
|
+
if (cfg.peer_disabled_mode === "all") {
|
|
6848
|
+
return { kind: "drop", reason: "peer_disabled_all" };
|
|
6849
|
+
}
|
|
6850
|
+
if (cfg.peer_agent_mode === "off") {
|
|
6851
|
+
return { kind: "drop", reason: "mode_off" };
|
|
6852
|
+
}
|
|
6853
|
+
const chatId = String(msg.chat.id);
|
|
6854
|
+
if (cfg.peer_group_ids.length === 0) {
|
|
6855
|
+
if (msg.chat.type !== "private") {
|
|
6856
|
+
return { kind: "drop", reason: "chat_not_allowed" };
|
|
6857
|
+
}
|
|
6858
|
+
} else if (!cfg.peer_group_ids.includes(chatId)) {
|
|
6859
|
+
return { kind: "drop", reason: "chat_not_allowed" };
|
|
6860
|
+
}
|
|
6861
|
+
const peer = cfg.peers.find((p) => p.bot_id === msg.from.id);
|
|
6862
|
+
if (!peer) {
|
|
6863
|
+
return { kind: "drop", reason: "unknown_peer" };
|
|
6864
|
+
}
|
|
6865
|
+
if (peer.gate_path === null) {
|
|
6866
|
+
return { kind: "drop", reason: "cross_team_grant_missing" };
|
|
6867
|
+
}
|
|
6868
|
+
if (cfg.peer_disabled_mode === "cross_team_only" && peer.gate_path !== void 0 && peer.gate_path !== "same_team") {
|
|
6869
|
+
return { kind: "drop", reason: "peer_disabled_cross_team" };
|
|
6870
|
+
}
|
|
6871
|
+
if (self.bot_id === null || self.bot_username === null) {
|
|
6872
|
+
return { kind: "drop", reason: "self_resolution_pending" };
|
|
6873
|
+
}
|
|
6874
|
+
const addressing = classifyAddressing2(msg, self.bot_id, self.bot_username);
|
|
6875
|
+
if (!addressing.addressed) {
|
|
6876
|
+
return { kind: "drop", reason: "not_addressed" };
|
|
6877
|
+
}
|
|
6878
|
+
if (addressing.viaReplyOnly) {
|
|
6879
|
+
const inAllowedGroup = cfg.peer_group_ids.length > 0 && cfg.peer_group_ids.includes(chatId);
|
|
6880
|
+
const respondMode = cfg.peer_agent_mode === "respond";
|
|
6881
|
+
if (!(inAllowedGroup && respondMode)) {
|
|
6882
|
+
return { kind: "drop", reason: "not_addressed" };
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
return { kind: "peer-ingress", peer };
|
|
6886
|
+
}
|
|
6887
|
+
function classifyAddressing2(msg, ourBotId, ourBotUsername) {
|
|
6888
|
+
let viaMentionOrCommand = false;
|
|
6889
|
+
const text = msg.text ?? msg.caption ?? "";
|
|
6890
|
+
const entities = msg.entities ?? msg.caption_entities ?? [];
|
|
6891
|
+
const usAt = "@" + ourBotUsername;
|
|
6892
|
+
for (const entity of entities) {
|
|
6893
|
+
const slice = text.slice(entity.offset, entity.offset + entity.length).toLowerCase();
|
|
6894
|
+
if (entity.type === "bot_command") {
|
|
6895
|
+
const at = slice.indexOf("@");
|
|
6896
|
+
if (at < 0 || slice.slice(at + 1) === ourBotUsername) {
|
|
6897
|
+
viaMentionOrCommand = true;
|
|
6898
|
+
break;
|
|
6899
|
+
}
|
|
6900
|
+
} else if (entity.type === "mention" && slice === usAt) {
|
|
6901
|
+
viaMentionOrCommand = true;
|
|
6902
|
+
break;
|
|
6903
|
+
}
|
|
6904
|
+
}
|
|
6905
|
+
const viaReply = msg.reply_to_message?.from?.id === ourBotId;
|
|
6906
|
+
if (viaMentionOrCommand)
|
|
6907
|
+
return { addressed: true, viaReplyOnly: false };
|
|
6908
|
+
if (viaReply)
|
|
6909
|
+
return { addressed: true, viaReplyOnly: true };
|
|
6910
|
+
return { addressed: false, viaReplyOnly: false };
|
|
6911
|
+
}
|
|
6912
|
+
var CODE_NAME_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
6913
|
+
function parsePeersEnv2(raw, gateRaw) {
|
|
6914
|
+
if (!raw || raw.trim().length === 0)
|
|
6915
|
+
return [];
|
|
6916
|
+
let parsed;
|
|
6917
|
+
try {
|
|
6918
|
+
parsed = JSON.parse(raw);
|
|
6919
|
+
} catch {
|
|
6920
|
+
return [];
|
|
6921
|
+
}
|
|
6922
|
+
if (!Array.isArray(parsed))
|
|
6923
|
+
return [];
|
|
6924
|
+
const gateMap = /* @__PURE__ */ new Map();
|
|
6925
|
+
let gateConfigInvalid = false;
|
|
6926
|
+
const gatePresent = !!(gateRaw && gateRaw.trim().length > 0);
|
|
6927
|
+
if (gatePresent) {
|
|
6928
|
+
try {
|
|
6929
|
+
const gateParsed = JSON.parse(gateRaw);
|
|
6930
|
+
if (!gateParsed || typeof gateParsed !== "object" || Array.isArray(gateParsed)) {
|
|
6931
|
+
gateConfigInvalid = true;
|
|
6932
|
+
} else {
|
|
6933
|
+
for (const [k, v] of Object.entries(gateParsed)) {
|
|
6934
|
+
if (v === null) {
|
|
6935
|
+
gateMap.set(k, null);
|
|
6936
|
+
} else if (typeof v === "string") {
|
|
6937
|
+
const isGrantPath = v.startsWith("grant:") && v.slice("grant:".length).trim().length > 0;
|
|
6938
|
+
if (v === "same_team" || v === "intra_org_unrestricted" || isGrantPath) {
|
|
6939
|
+
gateMap.set(k, v);
|
|
6940
|
+
} else {
|
|
6941
|
+
gateConfigInvalid = true;
|
|
6942
|
+
break;
|
|
6943
|
+
}
|
|
6944
|
+
} else {
|
|
6945
|
+
gateConfigInvalid = true;
|
|
6946
|
+
break;
|
|
6947
|
+
}
|
|
6948
|
+
}
|
|
6949
|
+
}
|
|
6950
|
+
} catch {
|
|
6951
|
+
gateConfigInvalid = true;
|
|
6952
|
+
}
|
|
6953
|
+
if (gateConfigInvalid) {
|
|
6954
|
+
console.error("[telegram-peer-classifier] TELEGRAM_PEERS_GATE is present but malformed; failing closed (every peer drops as cross_team_grant_missing)");
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
const out = [];
|
|
6958
|
+
for (const entry of parsed) {
|
|
6959
|
+
if (entry && typeof entry === "object" && typeof entry.code_name === "string" && CODE_NAME_RE2.test(entry.code_name) && typeof entry.agent_id === "string" && typeof entry.bot_id === "number" && Number.isInteger(entry.bot_id) && entry.bot_id > 0) {
|
|
6960
|
+
const e = entry;
|
|
6961
|
+
const peer = { code_name: e.code_name, bot_id: e.bot_id, agent_id: e.agent_id };
|
|
6962
|
+
if (gateConfigInvalid) {
|
|
6963
|
+
peer.gate_path = null;
|
|
6964
|
+
} else {
|
|
6965
|
+
const key = String(e.bot_id);
|
|
6966
|
+
if (gateMap.has(key)) {
|
|
6967
|
+
peer.gate_path = gateMap.get(key) ?? null;
|
|
6968
|
+
} else if (gatePresent) {
|
|
6969
|
+
peer.gate_path = null;
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
out.push(peer);
|
|
6973
|
+
}
|
|
6974
|
+
}
|
|
6975
|
+
return out;
|
|
6976
|
+
}
|
|
6977
|
+
function parsePeerGroupIdsEnv2(raw) {
|
|
6978
|
+
if (!raw)
|
|
6979
|
+
return [];
|
|
6980
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
6981
|
+
}
|
|
6982
|
+
function parsePeerAgentModeEnv2(raw) {
|
|
6983
|
+
if (raw === "listen" || raw === "respond")
|
|
6984
|
+
return raw;
|
|
6985
|
+
return "off";
|
|
6986
|
+
}
|
|
6987
|
+
|
|
6988
|
+
// ../../packages/core/dist/channels/governance/telegram-sender-gate.js
|
|
6989
|
+
function evaluateTelegramInboundGate(evt, deps) {
|
|
6990
|
+
const isBot = Boolean(evt.from?.is_bot);
|
|
6991
|
+
const text = (evt.text ?? evt.caption ?? "").trim();
|
|
6992
|
+
const chatId = String(evt.chat.id);
|
|
6993
|
+
const content = text.length > 0 ? { forward: true } : { forward: false, reason: "empty" };
|
|
6994
|
+
let peerLeaf;
|
|
6995
|
+
let isSelf = false;
|
|
6996
|
+
if (isBot) {
|
|
6997
|
+
const classifierMsg = {
|
|
6998
|
+
message_id: evt.message_id,
|
|
6999
|
+
text: evt.text,
|
|
7000
|
+
caption: evt.caption,
|
|
7001
|
+
from: evt.from ? { id: evt.from.id, username: evt.from.username, is_bot: evt.from.is_bot } : void 0,
|
|
7002
|
+
chat: { id: evt.chat.id, type: evt.chat.type },
|
|
7003
|
+
reply_to_message: evt.reply_to_message ? {
|
|
7004
|
+
message_id: evt.reply_to_message.message_id,
|
|
7005
|
+
from: evt.reply_to_message.from ? { id: evt.reply_to_message.from.id, is_bot: evt.reply_to_message.from.is_bot } : void 0
|
|
7006
|
+
} : void 0,
|
|
7007
|
+
entities: evt.entities,
|
|
7008
|
+
caption_entities: evt.caption_entities
|
|
7009
|
+
};
|
|
7010
|
+
const c = classifyPeerMessage2(classifierMsg, deps.peerConfig, deps.self);
|
|
7011
|
+
isSelf = c.kind === "self";
|
|
7012
|
+
peerLeaf = { kind: c.kind, reason: c.kind === "drop" ? c.reason : void 0 };
|
|
7013
|
+
}
|
|
7014
|
+
const decision = decideInboundAccess({
|
|
7015
|
+
content,
|
|
7016
|
+
// Telegram's only self signal is the peer classifier (Slack catches self at
|
|
7017
|
+
// evt.user===botUserId; Teams inside its activity filter).
|
|
7018
|
+
isSelf,
|
|
7019
|
+
isBot,
|
|
7020
|
+
peer: peerLeaf,
|
|
7021
|
+
// TELEGRAM_ALLOWED_CHATS is Telegram's org-boundary axis. An unlisted chat is
|
|
7022
|
+
// external ⇒ silent drop.
|
|
7023
|
+
orgBoundary: deps.allowedChats.size > 0 && !deps.allowedChats.has(chatId) ? { forward: false } : { forward: true },
|
|
7024
|
+
// No sender-policy mode system and no command dispatch on the injection gate.
|
|
7025
|
+
sameOrg: false
|
|
7026
|
+
});
|
|
7027
|
+
if (decision.kind === "drop")
|
|
7028
|
+
return { admit: false, reason: decision.reason };
|
|
7029
|
+
return { admit: true };
|
|
7030
|
+
}
|
|
7031
|
+
|
|
7032
|
+
// ../../packages/core/dist/channels/governance/telegram-inbound-config.js
|
|
7033
|
+
function parseTelegramAllowedChatsEnv(env) {
|
|
7034
|
+
return new Set((env["TELEGRAM_ALLOWED_CHATS"] ?? "").split(",").map((s) => s.trim()).filter(Boolean));
|
|
7035
|
+
}
|
|
7036
|
+
function parseTelegramPeerDisabledModeEnv(env, onWarn) {
|
|
7037
|
+
const next = (env["PEER_DISABLED"] ?? "").trim().toLowerCase();
|
|
7038
|
+
if (next === "off" || next === "cross_team_only" || next === "all")
|
|
7039
|
+
return next;
|
|
7040
|
+
if (next.length > 0) {
|
|
7041
|
+
onWarn?.(`invalid PEER_DISABLED=${JSON.stringify(env["PEER_DISABLED"])} - defaulting to 'all' (fail-closed)`);
|
|
7042
|
+
return "all";
|
|
7043
|
+
}
|
|
7044
|
+
const legacy = (env["TELEGRAM_PEER_DISABLED"] ?? "").trim().toLowerCase();
|
|
7045
|
+
return legacy === "1" || legacy === "true" || legacy === "yes" ? "all" : "off";
|
|
7046
|
+
}
|
|
7047
|
+
function buildTelegramPeerClassifierConfigFromEnv(env, opts) {
|
|
7048
|
+
return {
|
|
7049
|
+
peer_agent_mode: parsePeerAgentModeEnv2(env["TELEGRAM_PEER_AGENT_MODE"]),
|
|
7050
|
+
peer_group_ids: parsePeerGroupIdsEnv2(env["TELEGRAM_PEER_GROUP_IDS"]),
|
|
7051
|
+
peers: parsePeersEnv2(env["TELEGRAM_PEERS"], env["TELEGRAM_PEERS_GATE"]),
|
|
7052
|
+
peer_disabled_mode: parseTelegramPeerDisabledModeEnv(env, opts?.onWarn)
|
|
7053
|
+
};
|
|
7054
|
+
}
|
|
7055
|
+
|
|
7056
|
+
// src/lib/telegram-ingest.ts
|
|
7057
|
+
var nodeHttpsTelegramFetch = (url, init) => new Promise((resolve, reject) => {
|
|
7058
|
+
const u = new URL(url);
|
|
7059
|
+
const body = init?.body;
|
|
7060
|
+
const headers = { ...init?.headers ?? {} };
|
|
7061
|
+
if (body !== void 0 && headers["Content-Length"] === void 0) {
|
|
7062
|
+
headers["Content-Length"] = String(Buffer.byteLength(body));
|
|
7063
|
+
}
|
|
7064
|
+
const req = https2.request(
|
|
7065
|
+
{
|
|
7066
|
+
hostname: u.hostname,
|
|
7067
|
+
port: 443,
|
|
7068
|
+
path: u.pathname + u.search,
|
|
7069
|
+
method: init?.method ?? "GET",
|
|
7070
|
+
family: 4,
|
|
7071
|
+
// Backstop above the long-poll hold (getUpdates parks up to
|
|
7072
|
+
// LONG_POLL_SECONDS); short calls carry their own AbortSignal.timeout.
|
|
7073
|
+
timeout: POLL_HTTP_TIMEOUT_MS + 5e3,
|
|
7074
|
+
headers
|
|
7075
|
+
},
|
|
7076
|
+
(res) => {
|
|
7077
|
+
let data = "";
|
|
7078
|
+
res.on("data", (d) => {
|
|
7079
|
+
data += d;
|
|
7080
|
+
});
|
|
7081
|
+
res.on("end", () => {
|
|
7082
|
+
const status = res.statusCode ?? 0;
|
|
7083
|
+
resolve({
|
|
7084
|
+
ok: status >= 200 && status < 300,
|
|
7085
|
+
status,
|
|
7086
|
+
json: async () => JSON.parse(data.length > 0 ? data : "{}")
|
|
7087
|
+
});
|
|
7088
|
+
});
|
|
7089
|
+
}
|
|
7090
|
+
);
|
|
7091
|
+
req.on("error", reject);
|
|
7092
|
+
req.on("timeout", () => {
|
|
7093
|
+
req.destroy(new Error("Telegram API socket timeout"));
|
|
7094
|
+
});
|
|
7095
|
+
const signal = init?.signal;
|
|
7096
|
+
if (signal) {
|
|
7097
|
+
if (signal.aborted) req.destroy(new Error("aborted"));
|
|
7098
|
+
else signal.addEventListener("abort", () => req.destroy(new Error("aborted")), { once: true });
|
|
7099
|
+
}
|
|
7100
|
+
if (body !== void 0) req.write(body);
|
|
7101
|
+
req.end();
|
|
7102
|
+
});
|
|
7103
|
+
var TELEGRAM_API = "https://api.telegram.org";
|
|
7104
|
+
var LONG_POLL_SECONDS = 50;
|
|
7105
|
+
var POLL_HTTP_TIMEOUT_MS = (LONG_POLL_SECONDS + 10) * 1e3;
|
|
7106
|
+
var POLL_ERROR_BACKOFF_MS = 5e3;
|
|
7107
|
+
function memoryOffsetStore(initial = 0) {
|
|
7108
|
+
let value = initial;
|
|
7109
|
+
return { load: () => value, save: (o) => {
|
|
7110
|
+
value = o;
|
|
7111
|
+
} };
|
|
7112
|
+
}
|
|
7113
|
+
function resolveTelegramIngestParamsFromEnv(env) {
|
|
7114
|
+
const botToken = env["TELEGRAM_BOT_TOKEN"];
|
|
7115
|
+
if (!botToken) return null;
|
|
7116
|
+
const ackReaction = (env["TELEGRAM_ACK_REACTION"] ?? "").trim() || "\u{1F440}";
|
|
7117
|
+
const skipReaction = (env["TELEGRAM_SKIP_REACTION"] ?? "").trim() || void 0;
|
|
7118
|
+
return {
|
|
7119
|
+
botToken,
|
|
7120
|
+
allowedChats: parseTelegramAllowedChatsEnv(env),
|
|
7121
|
+
peerConfig: buildTelegramPeerClassifierConfigFromEnv(env),
|
|
7122
|
+
ackReaction,
|
|
7123
|
+
...skipReaction ? { skipReaction } : {}
|
|
7124
|
+
};
|
|
7125
|
+
}
|
|
7126
|
+
function isTelegramAuthFailure(status, description) {
|
|
7127
|
+
return status === 401 || (description ?? "").toLowerCase().includes("unauthorized");
|
|
7128
|
+
}
|
|
7129
|
+
async function resolveTelegramBotIdentity(botToken, fetchImpl) {
|
|
7130
|
+
const doFetch = fetchImpl ?? nodeHttpsTelegramFetch;
|
|
7131
|
+
const unresolved = { bot_id: null, bot_username: null };
|
|
7132
|
+
try {
|
|
7133
|
+
const res = await doFetch(`${TELEGRAM_API}/bot${botToken}/getMe`, {
|
|
7134
|
+
method: "POST",
|
|
7135
|
+
signal: AbortSignal.timeout(1e4)
|
|
7136
|
+
});
|
|
7137
|
+
const data = await res.json();
|
|
7138
|
+
if (data.ok && data.result) {
|
|
7139
|
+
return {
|
|
7140
|
+
self: {
|
|
7141
|
+
bot_id: data.result.id,
|
|
7142
|
+
// Lowercased to match resolveBotUsername()'s cache convention.
|
|
7143
|
+
bot_username: data.result.username ? data.result.username.toLowerCase() : null
|
|
7144
|
+
},
|
|
7145
|
+
authFailed: false
|
|
7146
|
+
};
|
|
7147
|
+
}
|
|
7148
|
+
return { self: unresolved, authFailed: isTelegramAuthFailure(res.status, data.description) };
|
|
7149
|
+
} catch {
|
|
7150
|
+
return { self: unresolved, authFailed: false };
|
|
7151
|
+
}
|
|
7152
|
+
}
|
|
7153
|
+
var BoundedSet2 = class {
|
|
7154
|
+
max;
|
|
7155
|
+
set = /* @__PURE__ */ new Set();
|
|
7156
|
+
constructor(max = 500) {
|
|
7157
|
+
this.max = max;
|
|
7158
|
+
}
|
|
7159
|
+
has(k) {
|
|
7160
|
+
return this.set.has(k);
|
|
7161
|
+
}
|
|
7162
|
+
add(k) {
|
|
7163
|
+
this.set.add(k);
|
|
7164
|
+
if (this.set.size > this.max) {
|
|
7165
|
+
const oldest = this.set.values().next().value;
|
|
7166
|
+
if (oldest !== void 0) this.set.delete(oldest);
|
|
7167
|
+
}
|
|
7168
|
+
}
|
|
7169
|
+
};
|
|
7170
|
+
async function dispatchTelegramInbound(update, ctx) {
|
|
7171
|
+
const msg = update.message;
|
|
7172
|
+
const chatId = String(msg.chat.id);
|
|
7173
|
+
const dedupKey = String(update.update_id);
|
|
7174
|
+
if (ctx.seen.has(dedupKey)) return "dropped_duplicate";
|
|
7175
|
+
ctx.seen.add(dedupKey);
|
|
7176
|
+
const decision = evaluateTelegramInboundGate(msg, {
|
|
7177
|
+
allowedChats: ctx.allowedChats,
|
|
7178
|
+
peerConfig: ctx.peerConfig,
|
|
7179
|
+
self: ctx.self
|
|
7180
|
+
});
|
|
7181
|
+
if (!decision.admit) {
|
|
7182
|
+
ctx.log(`[telegram-ingest:${ctx.codeName}] dropped chat=${redactId(chatId)} reason=${decision.reason}`);
|
|
7183
|
+
return "dropped_gate";
|
|
7184
|
+
}
|
|
7185
|
+
if (ctx.ackReaction) {
|
|
7186
|
+
void ctx.addReaction({ chatId, messageId: msg.message_id, emoji: ctx.ackReaction });
|
|
7187
|
+
}
|
|
7188
|
+
const conversationKey = msg.message_thread_id ? `${chatId}:${msg.message_thread_id}` : chatId;
|
|
7189
|
+
const meta = {
|
|
7190
|
+
source: "telegram",
|
|
7191
|
+
lane: "conversational",
|
|
7192
|
+
requires_reply: "true",
|
|
7193
|
+
chat_id: chatId
|
|
7194
|
+
};
|
|
7195
|
+
if (msg.message_thread_id) meta["message_thread_id"] = String(msg.message_thread_id);
|
|
7196
|
+
const result = await ctx.inject({
|
|
7197
|
+
channelId: "telegram",
|
|
7198
|
+
conversationKey,
|
|
7199
|
+
senderId: msg.from ? String(msg.from.id) : "unknown",
|
|
7200
|
+
text: msg.text ?? msg.caption ?? "",
|
|
7201
|
+
meta
|
|
7202
|
+
});
|
|
7203
|
+
if (result.status === "declined") {
|
|
7204
|
+
ctx.log(`[telegram-ingest:${ctx.codeName}] inject declined reason=${result.reason}`);
|
|
7205
|
+
return "declined";
|
|
7206
|
+
}
|
|
7207
|
+
if (result.status === "replied" && result.reply) {
|
|
7208
|
+
await ctx.sendReply({
|
|
7209
|
+
chatId,
|
|
7210
|
+
text: result.reply,
|
|
7211
|
+
...msg.message_thread_id ? { messageThreadId: msg.message_thread_id } : {}
|
|
7212
|
+
});
|
|
7213
|
+
ctx.log(`[telegram-ingest:${ctx.codeName}] outcome=replied chat=${redactId(chatId)} len=${result.reply.length}`);
|
|
7214
|
+
return "admitted_replied";
|
|
7215
|
+
}
|
|
7216
|
+
ctx.log(
|
|
7217
|
+
`[telegram-ingest:${ctx.codeName}] outcome=no_reply chat=${redactId(chatId)} inject_status=${result.status}`
|
|
7218
|
+
);
|
|
7219
|
+
if (ctx.skipReaction) {
|
|
7220
|
+
void ctx.addReaction({ chatId, messageId: msg.message_id, emoji: ctx.skipReaction });
|
|
7221
|
+
}
|
|
7222
|
+
return "admitted_no_reply";
|
|
7223
|
+
}
|
|
7224
|
+
function redactId(id) {
|
|
7225
|
+
if (!id) return "?";
|
|
7226
|
+
return `${id.slice(0, 2)}***`;
|
|
7227
|
+
}
|
|
7228
|
+
function defaultSendReply2(botToken, fetchImpl, log2) {
|
|
7229
|
+
return async ({ chatId, text, messageThreadId }) => {
|
|
7230
|
+
try {
|
|
7231
|
+
const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/sendMessage`, {
|
|
7232
|
+
method: "POST",
|
|
7233
|
+
headers: { "Content-Type": "application/json" },
|
|
7234
|
+
body: JSON.stringify({
|
|
7235
|
+
chat_id: chatId,
|
|
7236
|
+
text,
|
|
7237
|
+
...messageThreadId ? { message_thread_id: messageThreadId } : {}
|
|
7238
|
+
}),
|
|
7239
|
+
// Dedicated short timeout so an outbound reply can't inherit the ~50s
|
|
7240
|
+
// long-poll getUpdates timeout and hang a turn on a slow Telegram edge.
|
|
7241
|
+
signal: AbortSignal.timeout(1e4)
|
|
7242
|
+
});
|
|
7243
|
+
const data = await res.json();
|
|
7244
|
+
if (!data.ok) log2(`[telegram-ingest] sendMessage failed: ${data.description}`);
|
|
7245
|
+
} catch (err) {
|
|
7246
|
+
log2(`[telegram-ingest] sendMessage threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7247
|
+
}
|
|
7248
|
+
};
|
|
7249
|
+
}
|
|
7250
|
+
function defaultAddReaction2(botToken, fetchImpl, log2) {
|
|
7251
|
+
return async ({ chatId, messageId, emoji }) => {
|
|
7252
|
+
try {
|
|
7253
|
+
const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/setMessageReaction`, {
|
|
7254
|
+
method: "POST",
|
|
7255
|
+
headers: { "Content-Type": "application/json" },
|
|
7256
|
+
body: JSON.stringify({
|
|
7257
|
+
chat_id: chatId,
|
|
7258
|
+
message_id: messageId,
|
|
7259
|
+
reaction: [{ type: "emoji", emoji }]
|
|
7260
|
+
}),
|
|
7261
|
+
// Dedicated short timeout — the ack must not inherit the long-poll window.
|
|
7262
|
+
signal: AbortSignal.timeout(1e4)
|
|
7263
|
+
});
|
|
7264
|
+
const data = await res.json();
|
|
7265
|
+
if (!data.ok) log2(`[telegram-ingest] setMessageReaction failed: ${data.description}`);
|
|
7266
|
+
} catch (err) {
|
|
7267
|
+
log2(`[telegram-ingest] setMessageReaction threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7268
|
+
}
|
|
7269
|
+
};
|
|
7270
|
+
}
|
|
7271
|
+
function sleep(ms) {
|
|
7272
|
+
return new Promise((resolve) => {
|
|
7273
|
+
setTimeout(resolve, ms).unref?.();
|
|
7274
|
+
});
|
|
7275
|
+
}
|
|
7276
|
+
function startTelegramIngest(config2) {
|
|
7277
|
+
const fetchImpl = config2.fetchImpl ?? nodeHttpsTelegramFetch;
|
|
7278
|
+
const sendReply = config2.sendReply ?? defaultSendReply2(config2.botToken, fetchImpl, config2.log);
|
|
7279
|
+
const addReaction = config2.addReaction ?? defaultAddReaction2(config2.botToken, fetchImpl, config2.log);
|
|
7280
|
+
const offsetStore = config2.offsetStore ?? memoryOffsetStore();
|
|
7281
|
+
const seen = new BoundedSet2();
|
|
7282
|
+
const ctx = {
|
|
7283
|
+
codeName: config2.codeName,
|
|
7284
|
+
allowedChats: config2.allowedChats,
|
|
7285
|
+
peerConfig: config2.peerConfig,
|
|
7286
|
+
self: config2.self,
|
|
7287
|
+
inject: config2.inject,
|
|
7288
|
+
sendReply,
|
|
7289
|
+
addReaction,
|
|
7290
|
+
...config2.ackReaction ? { ackReaction: config2.ackReaction } : {},
|
|
7291
|
+
...config2.skipReaction ? { skipReaction: config2.skipReaction } : {},
|
|
7292
|
+
log: config2.log,
|
|
7293
|
+
seen
|
|
7294
|
+
};
|
|
7295
|
+
let stopped = false;
|
|
7296
|
+
let controller = null;
|
|
7297
|
+
let nextOffset = offsetStore.load();
|
|
7298
|
+
const pollLoop = async () => {
|
|
7299
|
+
while (!stopped) {
|
|
7300
|
+
try {
|
|
7301
|
+
controller = new AbortController();
|
|
7302
|
+
const timeout = AbortSignal.timeout(POLL_HTTP_TIMEOUT_MS);
|
|
7303
|
+
const signal = anySignal([controller.signal, timeout]);
|
|
7304
|
+
const res = await fetchImpl(`${TELEGRAM_API}/bot${config2.botToken}/getUpdates`, {
|
|
7305
|
+
method: "POST",
|
|
7306
|
+
headers: { "Content-Type": "application/json" },
|
|
7307
|
+
body: JSON.stringify({
|
|
7308
|
+
offset: nextOffset,
|
|
7309
|
+
timeout: LONG_POLL_SECONDS,
|
|
7310
|
+
allowed_updates: ["message"]
|
|
7311
|
+
}),
|
|
7312
|
+
signal
|
|
7313
|
+
});
|
|
7314
|
+
if (stopped) return;
|
|
7315
|
+
const data = await res.json();
|
|
7316
|
+
if (!data.ok) {
|
|
7317
|
+
config2.log(`[telegram-ingest:${config2.codeName}] getUpdates failed: ${data.description ?? "unknown"}`);
|
|
7318
|
+
await sleep(POLL_ERROR_BACKOFF_MS);
|
|
7319
|
+
continue;
|
|
7320
|
+
}
|
|
7321
|
+
const updates = data.result ?? [];
|
|
7322
|
+
for (const update of updates) {
|
|
7323
|
+
nextOffset = Math.max(nextOffset, update.update_id + 1);
|
|
7324
|
+
const msg = update.message;
|
|
7325
|
+
if (!msg) continue;
|
|
7326
|
+
try {
|
|
7327
|
+
await dispatchTelegramInbound({ update_id: update.update_id, message: msg }, ctx);
|
|
7328
|
+
} catch (err) {
|
|
7329
|
+
config2.log(
|
|
7330
|
+
`[telegram-ingest:${config2.codeName}] dispatch threw: ${err instanceof Error ? err.message : String(err)}`
|
|
7331
|
+
);
|
|
7332
|
+
}
|
|
7333
|
+
}
|
|
7334
|
+
if (updates.length > 0) offsetStore.save(nextOffset);
|
|
7335
|
+
} catch (err) {
|
|
7336
|
+
if (stopped) return;
|
|
7337
|
+
config2.log(`[telegram-ingest:${config2.codeName}] poll threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7338
|
+
await sleep(POLL_ERROR_BACKOFF_MS);
|
|
7339
|
+
}
|
|
7340
|
+
}
|
|
7341
|
+
};
|
|
7342
|
+
void pollLoop();
|
|
7343
|
+
return {
|
|
7344
|
+
stop() {
|
|
7345
|
+
stopped = true;
|
|
7346
|
+
try {
|
|
7347
|
+
controller?.abort();
|
|
7348
|
+
} catch {
|
|
7349
|
+
}
|
|
7350
|
+
}
|
|
7351
|
+
};
|
|
7352
|
+
}
|
|
7353
|
+
function anySignal(signals) {
|
|
7354
|
+
const controller = new AbortController();
|
|
7355
|
+
for (const s of signals) {
|
|
7356
|
+
if (s.aborted) {
|
|
7357
|
+
controller.abort();
|
|
7358
|
+
break;
|
|
7359
|
+
}
|
|
7360
|
+
s.addEventListener("abort", () => controller.abort(), { once: true });
|
|
7361
|
+
}
|
|
7362
|
+
return controller.signal;
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7365
|
+
// src/lib/opencode-telegram-ingest.ts
|
|
7366
|
+
var ingests2 = /* @__PURE__ */ new Map();
|
|
7367
|
+
function fingerprint2(params) {
|
|
7368
|
+
return createHash13("sha256").update(
|
|
7369
|
+
JSON.stringify({
|
|
7370
|
+
botToken: params.botToken,
|
|
7371
|
+
allowedChats: [...params.allowedChats].sort(),
|
|
7372
|
+
peerConfig: params.peerConfig,
|
|
7373
|
+
ackReaction: params.ackReaction ?? null,
|
|
7374
|
+
skipReaction: params.skipReaction ?? null
|
|
7375
|
+
})
|
|
7376
|
+
).digest("hex");
|
|
7377
|
+
}
|
|
7378
|
+
function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
7379
|
+
let dir;
|
|
7380
|
+
let filePath;
|
|
7381
|
+
try {
|
|
7382
|
+
dir = getFramework("opencode").getAgentDir(codeName);
|
|
7383
|
+
filePath = join20(dir, "telegram-getupdates-offset-opencode.json");
|
|
7384
|
+
} catch {
|
|
7385
|
+
dir = null;
|
|
7386
|
+
filePath = null;
|
|
7387
|
+
}
|
|
7388
|
+
return {
|
|
7389
|
+
load() {
|
|
7390
|
+
if (!filePath) return 0;
|
|
7391
|
+
try {
|
|
7392
|
+
const parsed = JSON.parse(readFileSync17(filePath, "utf-8"));
|
|
7393
|
+
if (currentBotId != null && typeof parsed?.bot_id === "number" && parsed.bot_id !== currentBotId) {
|
|
7394
|
+
log2(`[telegram-ingest:${codeName}] offset cursor belongs to a different bot; ignoring (bot swap)`);
|
|
7395
|
+
return 0;
|
|
7396
|
+
}
|
|
7397
|
+
const offset = parsed?.offset;
|
|
7398
|
+
if (typeof offset === "number" && Number.isFinite(offset) && offset > 0) {
|
|
7399
|
+
const normalized = Math.floor(offset);
|
|
7400
|
+
if (Number.isSafeInteger(normalized) && normalized > 0) return normalized;
|
|
7401
|
+
}
|
|
7402
|
+
} catch {
|
|
7403
|
+
}
|
|
7404
|
+
return 0;
|
|
7405
|
+
},
|
|
7406
|
+
save(offset) {
|
|
7407
|
+
if (!filePath || !dir) return;
|
|
7408
|
+
const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
7409
|
+
try {
|
|
7410
|
+
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
7411
|
+
writeFileSync8(
|
|
7412
|
+
tmpPath,
|
|
7413
|
+
JSON.stringify({
|
|
7414
|
+
offset,
|
|
7415
|
+
// Stamp the owning bot so a later token swap invalidates this cursor.
|
|
7416
|
+
...currentBotId != null ? { bot_id: currentBotId } : {},
|
|
7417
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
7418
|
+
}),
|
|
7419
|
+
{ mode: 384 }
|
|
7420
|
+
);
|
|
7421
|
+
renameSync2(tmpPath, filePath);
|
|
7422
|
+
} catch (err) {
|
|
7423
|
+
log2(`[telegram-ingest:${codeName}] offset persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7424
|
+
try {
|
|
7425
|
+
if (existsSync11(tmpPath)) unlinkSync(tmpPath);
|
|
7426
|
+
} catch {
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7430
|
+
};
|
|
7431
|
+
}
|
|
7432
|
+
async function ensureOpencodeTelegramIngest(opts) {
|
|
7433
|
+
const { codeName, agentId, log: log2 } = opts;
|
|
7434
|
+
const readEnv = opts.readEnv ?? readChannelServerEnv;
|
|
7435
|
+
let params;
|
|
7436
|
+
try {
|
|
7437
|
+
const env = readEnv(codeName, "telegram");
|
|
7438
|
+
if (!env) {
|
|
7439
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7440
|
+
return;
|
|
7441
|
+
}
|
|
7442
|
+
params = resolveTelegramIngestParamsFromEnv(env);
|
|
7443
|
+
} catch (err) {
|
|
7444
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7445
|
+
log2(`[telegram-ingest:${codeName}] not started: ${err instanceof Error ? err.message : String(err)}`);
|
|
7446
|
+
return;
|
|
7447
|
+
}
|
|
7448
|
+
if (!params) {
|
|
7449
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7450
|
+
return;
|
|
7451
|
+
}
|
|
7452
|
+
const key = fingerprint2(params);
|
|
7453
|
+
const existing = ingests2.get(codeName);
|
|
7454
|
+
if (existing?.key === key && !existing.selfUnresolved) return;
|
|
7455
|
+
const { self, authFailed } = await resolveTelegramBotIdentity(params.botToken, opts.fetchImpl);
|
|
7456
|
+
if (authFailed) {
|
|
7457
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7458
|
+
log2(`[telegram-ingest:${codeName}] not started: Telegram bot token rejected (needs reconnect)`);
|
|
7459
|
+
return;
|
|
7460
|
+
}
|
|
7461
|
+
if (existing?.key === key && self.bot_id === null) return;
|
|
7462
|
+
if (self.bot_id === null) {
|
|
7463
|
+
log2(`[telegram-ingest:${codeName}] bot identity unresolved; peer classification degraded`);
|
|
7464
|
+
}
|
|
7465
|
+
if (existing) {
|
|
7466
|
+
existing.handle.stop();
|
|
7467
|
+
ingests2.delete(codeName);
|
|
7468
|
+
log2(
|
|
7469
|
+
existing.key === key ? `[telegram-ingest:${codeName}] bot identity resolved; restarting ingest` : `[telegram-ingest:${codeName}] config changed; restarting ingest`
|
|
7470
|
+
);
|
|
7471
|
+
}
|
|
7472
|
+
const handle = startTelegramIngest({
|
|
7473
|
+
codeName,
|
|
7474
|
+
agentId,
|
|
7475
|
+
botToken: params.botToken,
|
|
7476
|
+
allowedChats: params.allowedChats,
|
|
7477
|
+
peerConfig: params.peerConfig,
|
|
7478
|
+
self,
|
|
7479
|
+
inject: (msg) => injectOpencodeMessage(codeName, msg, { awaitReply: true }),
|
|
7480
|
+
log: log2,
|
|
7481
|
+
offsetStore: opts.offsetStore ?? createFileOffsetStore(codeName, log2, self.bot_id),
|
|
7482
|
+
...params.ackReaction ? { ackReaction: params.ackReaction } : {},
|
|
7483
|
+
...params.skipReaction ? { skipReaction: params.skipReaction } : {},
|
|
7484
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
7485
|
+
});
|
|
7486
|
+
ingests2.set(codeName, { key, handle, selfUnresolved: self.bot_id === null });
|
|
7487
|
+
log2(
|
|
7488
|
+
`[telegram-ingest:${codeName}] ingest started (peer_mode=${params.peerConfig.peer_agent_mode}, allowlist=${params.allowedChats.size})`
|
|
7489
|
+
);
|
|
7490
|
+
}
|
|
7491
|
+
function stopOpencodeTelegramIngest(codeName, log2) {
|
|
7492
|
+
const entry = ingests2.get(codeName);
|
|
7493
|
+
if (!entry) return;
|
|
7494
|
+
entry.handle.stop();
|
|
7495
|
+
ingests2.delete(codeName);
|
|
7496
|
+
log2?.(`[telegram-ingest:${codeName}] ingest stopped`);
|
|
7497
|
+
}
|
|
7498
|
+
function stopAllOpencodeTelegramIngests(log2) {
|
|
7499
|
+
for (const codeName of [...ingests2.keys()]) stopOpencodeTelegramIngest(codeName, log2);
|
|
7500
|
+
}
|
|
7501
|
+
|
|
6807
7502
|
// src/lib/wedge-detection.ts
|
|
6808
7503
|
var DEFAULTS = {
|
|
6809
7504
|
inboundWaitSeconds: 120,
|
|
@@ -6944,24 +7639,24 @@ function partitionActionableByPoison(actionable, states, config2) {
|
|
|
6944
7639
|
}
|
|
6945
7640
|
|
|
6946
7641
|
// src/lib/restart-flags.ts
|
|
6947
|
-
import { existsSync as
|
|
7642
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync18, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
6948
7643
|
import { homedir as homedir10 } from "os";
|
|
6949
|
-
import { join as
|
|
6950
|
-
import { randomUUID } from "crypto";
|
|
7644
|
+
import { join as join21 } from "path";
|
|
7645
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
6951
7646
|
function restartFlagsDir() {
|
|
6952
|
-
return
|
|
7647
|
+
return join21(homedir10(), ".augmented", "restart-flags");
|
|
6953
7648
|
}
|
|
6954
7649
|
function flagPath(codeName) {
|
|
6955
|
-
return
|
|
7650
|
+
return join21(restartFlagsDir(), `${codeName}.flag`);
|
|
6956
7651
|
}
|
|
6957
7652
|
function readRestartFlags() {
|
|
6958
7653
|
const dir = restartFlagsDir();
|
|
6959
|
-
if (!
|
|
7654
|
+
if (!existsSync12(dir)) return [];
|
|
6960
7655
|
const out = [];
|
|
6961
7656
|
for (const entry of readdirSync4(dir)) {
|
|
6962
7657
|
if (!entry.endsWith(".flag")) continue;
|
|
6963
7658
|
try {
|
|
6964
|
-
const raw =
|
|
7659
|
+
const raw = readFileSync18(join21(dir, entry), "utf8");
|
|
6965
7660
|
const parsed = JSON.parse(raw);
|
|
6966
7661
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
6967
7662
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -6979,7 +7674,7 @@ function readRestartFlags() {
|
|
|
6979
7674
|
}
|
|
6980
7675
|
function deleteRestartFlag(codeName) {
|
|
6981
7676
|
const path = flagPath(codeName);
|
|
6982
|
-
if (
|
|
7677
|
+
if (existsSync12(path)) {
|
|
6983
7678
|
rmSync3(path, { force: true });
|
|
6984
7679
|
}
|
|
6985
7680
|
}
|
|
@@ -7325,10 +8020,10 @@ function startRealtimeConfig(config2) {
|
|
|
7325
8020
|
filter: filterStr
|
|
7326
8021
|
}, (payload) => {
|
|
7327
8022
|
const agent = payload.new;
|
|
7328
|
-
const
|
|
8023
|
+
const fingerprint3 = `${agent.status}|${agent.framework}|${agent.session_mode}|${agent.primary_model}`;
|
|
7329
8024
|
const prev = lastKnown.get(agent.agent_id);
|
|
7330
|
-
if (prev ===
|
|
7331
|
-
lastKnown.set(agent.agent_id,
|
|
8025
|
+
if (prev === fingerprint3) return;
|
|
8026
|
+
lastKnown.set(agent.agent_id, fingerprint3);
|
|
7332
8027
|
log2(`[realtime] Agent config changed: ${agent.code_name} (status=${agent.status})`);
|
|
7333
8028
|
onConfigChange(agent);
|
|
7334
8029
|
}).subscribe((status) => {
|
|
@@ -8213,15 +8908,15 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
|
|
|
8213
8908
|
var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
|
|
8214
8909
|
function projectMcpHash(_codeName, projectDir) {
|
|
8215
8910
|
try {
|
|
8216
|
-
const raw =
|
|
8217
|
-
return
|
|
8911
|
+
const raw = readFileSync19(join22(projectDir, ".mcp.json"), "utf-8");
|
|
8912
|
+
return createHash14("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
8218
8913
|
} catch {
|
|
8219
8914
|
return null;
|
|
8220
8915
|
}
|
|
8221
8916
|
}
|
|
8222
8917
|
function projectMcpKeys(_codeName, projectDir) {
|
|
8223
8918
|
try {
|
|
8224
|
-
const raw =
|
|
8919
|
+
const raw = readFileSync19(join22(projectDir, ".mcp.json"), "utf-8");
|
|
8225
8920
|
const parsed = JSON.parse(raw);
|
|
8226
8921
|
const servers = parsed.mcpServers;
|
|
8227
8922
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -8239,7 +8934,7 @@ function seedSessionLaunchBaselines(codeName, projectDir) {
|
|
|
8239
8934
|
else runningMcpServerKeys.delete(codeName);
|
|
8240
8935
|
let launchStructure = null;
|
|
8241
8936
|
try {
|
|
8242
|
-
const raw =
|
|
8937
|
+
const raw = readFileSync19(join22(projectDir, ".mcp.json"), "utf-8");
|
|
8243
8938
|
launchStructure = managedMcpStructureHashFromFile(
|
|
8244
8939
|
JSON.parse(raw),
|
|
8245
8940
|
isManagedMcpServerKey
|
|
@@ -8339,7 +9034,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
|
|
|
8339
9034
|
if (result && result.rebindCandidates.length > 0 && hostFlagStore().getBoolean("session-tool-rebind")) {
|
|
8340
9035
|
let mcpJsonForRebind = null;
|
|
8341
9036
|
try {
|
|
8342
|
-
mcpJsonForRebind = JSON.parse(
|
|
9037
|
+
mcpJsonForRebind = JSON.parse(readFileSync19(join22(projectDir, ".mcp.json"), "utf-8"));
|
|
8343
9038
|
} catch {
|
|
8344
9039
|
mcpJsonForRebind = null;
|
|
8345
9040
|
}
|
|
@@ -8468,7 +9163,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
8468
9163
|
function projectChannelSecretHash(projectDir) {
|
|
8469
9164
|
try {
|
|
8470
9165
|
const entries = parseEnvIntegrations(
|
|
8471
|
-
|
|
9166
|
+
readFileSync19(join22(projectDir, ".env.integrations"), "utf-8")
|
|
8472
9167
|
);
|
|
8473
9168
|
return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
|
|
8474
9169
|
} catch {
|
|
@@ -8531,6 +9226,7 @@ function clearAgentCaches(agentId, codeName) {
|
|
|
8531
9226
|
const channelCacheMutated = clearAgentState(agentId, codeName);
|
|
8532
9227
|
agentChannelTokens.delete(codeName);
|
|
8533
9228
|
stopOpencodeSlackIngest(codeName, log);
|
|
9229
|
+
stopOpencodeTelegramIngest(codeName, log);
|
|
8534
9230
|
agentFrameworkCache.delete(codeName);
|
|
8535
9231
|
kanbanBoardCache.delete(codeName);
|
|
8536
9232
|
clearKanbanNudgeState(codeName);
|
|
@@ -8560,7 +9256,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
8560
9256
|
var lastVersionCheckAt = 0;
|
|
8561
9257
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
8562
9258
|
var lastResponsivenessProbeAt = 0;
|
|
8563
|
-
var agtCliVersion = true ? "0.28.
|
|
9259
|
+
var agtCliVersion = true ? "0.28.344" : "dev";
|
|
8564
9260
|
function resolveBrewPath(execFileSync2) {
|
|
8565
9261
|
try {
|
|
8566
9262
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -8573,7 +9269,7 @@ function resolveBrewPath(execFileSync2) {
|
|
|
8573
9269
|
"/usr/local/bin/brew"
|
|
8574
9270
|
];
|
|
8575
9271
|
for (const path of fallbacks) {
|
|
8576
|
-
if (
|
|
9272
|
+
if (existsSync13(path)) return path;
|
|
8577
9273
|
}
|
|
8578
9274
|
return null;
|
|
8579
9275
|
}
|
|
@@ -8583,7 +9279,7 @@ function claudeBinaryInstalled(execFileSync2) {
|
|
|
8583
9279
|
"/opt/homebrew/bin/claude",
|
|
8584
9280
|
"/usr/local/bin/claude"
|
|
8585
9281
|
];
|
|
8586
|
-
if (canonical.some((path) =>
|
|
9282
|
+
if (canonical.some((path) => existsSync13(path))) return true;
|
|
8587
9283
|
try {
|
|
8588
9284
|
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
8589
9285
|
return true;
|
|
@@ -8738,8 +9434,8 @@ function claudeManagedSettingsPath() {
|
|
|
8738
9434
|
function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
8739
9435
|
try {
|
|
8740
9436
|
let settings = {};
|
|
8741
|
-
if (
|
|
8742
|
-
const raw =
|
|
9437
|
+
if (existsSync13(path)) {
|
|
9438
|
+
const raw = readFileSync19(path, "utf-8").trim();
|
|
8743
9439
|
if (raw) {
|
|
8744
9440
|
let parsed;
|
|
8745
9441
|
try {
|
|
@@ -8755,8 +9451,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
8755
9451
|
}
|
|
8756
9452
|
if (settings.channelsEnabled === true) return "ok";
|
|
8757
9453
|
settings.channelsEnabled = true;
|
|
8758
|
-
|
|
8759
|
-
|
|
9454
|
+
mkdirSync9(dirname6(path), { recursive: true });
|
|
9455
|
+
writeFileSync10(path, `${JSON.stringify(settings, null, 2)}
|
|
8760
9456
|
`);
|
|
8761
9457
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
8762
9458
|
return "ok";
|
|
@@ -8801,7 +9497,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
8801
9497
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
8802
9498
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
8803
9499
|
}
|
|
8804
|
-
if (
|
|
9500
|
+
if (existsSync13("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
8805
9501
|
log("Claude Code installed successfully");
|
|
8806
9502
|
} else {
|
|
8807
9503
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -8857,7 +9553,7 @@ ${r.stderr}`;
|
|
|
8857
9553
|
}
|
|
8858
9554
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
8859
9555
|
function selfUpdateAppliedMarkerPath() {
|
|
8860
|
-
return
|
|
9556
|
+
return join22(homedir11(), ".augmented", ".last-self-update-applied");
|
|
8861
9557
|
}
|
|
8862
9558
|
var selfUpdateUpToDateLogged = false;
|
|
8863
9559
|
var selfUpdatePinnedLogged = false;
|
|
@@ -8883,7 +9579,7 @@ async function checkAndUpdateCli(opts) {
|
|
|
8883
9579
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
8884
9580
|
if (!isBrewFormula && !isNpmGlobal) return "noop";
|
|
8885
9581
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
8886
|
-
const markerPath =
|
|
9582
|
+
const markerPath = join22(homedir11(), ".augmented", ".last-update-check");
|
|
8887
9583
|
if (!force) {
|
|
8888
9584
|
try {
|
|
8889
9585
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
@@ -9228,14 +9924,14 @@ async function checkClaudeAuth() {
|
|
|
9228
9924
|
}
|
|
9229
9925
|
var evalEmptyMcpConfigPath = null;
|
|
9230
9926
|
function ensureEvalEmptyMcpConfig() {
|
|
9231
|
-
if (evalEmptyMcpConfigPath &&
|
|
9232
|
-
const dir =
|
|
9927
|
+
if (evalEmptyMcpConfigPath && existsSync13(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
9928
|
+
const dir = join22(homedir11(), ".augmented");
|
|
9233
9929
|
try {
|
|
9234
|
-
|
|
9930
|
+
mkdirSync9(dir, { recursive: true });
|
|
9235
9931
|
} catch {
|
|
9236
9932
|
}
|
|
9237
|
-
const p =
|
|
9238
|
-
|
|
9933
|
+
const p = join22(dir, ".eval-empty-mcp.json");
|
|
9934
|
+
writeFileSync10(p, JSON.stringify({ mcpServers: {} }));
|
|
9239
9935
|
evalEmptyMcpConfigPath = p;
|
|
9240
9936
|
return p;
|
|
9241
9937
|
}
|
|
@@ -9326,10 +10022,10 @@ function resolveConversationEvalBackend() {
|
|
|
9326
10022
|
return conversationEvalBackend;
|
|
9327
10023
|
}
|
|
9328
10024
|
function getStateFile() {
|
|
9329
|
-
return
|
|
10025
|
+
return join22(config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
9330
10026
|
}
|
|
9331
10027
|
function channelHashCacheDir() {
|
|
9332
|
-
return config?.configDir ??
|
|
10028
|
+
return config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
9333
10029
|
}
|
|
9334
10030
|
function loadChannelHashCache2() {
|
|
9335
10031
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -9381,7 +10077,7 @@ function removeDeliveryBaselineEntries(agentId) {
|
|
|
9381
10077
|
var _channelQuarantineStore = null;
|
|
9382
10078
|
function channelQuarantineStore() {
|
|
9383
10079
|
if (!_channelQuarantineStore) {
|
|
9384
|
-
const dir = config?.configDir ??
|
|
10080
|
+
const dir = config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
9385
10081
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
9386
10082
|
}
|
|
9387
10083
|
return _channelQuarantineStore;
|
|
@@ -9389,7 +10085,7 @@ function channelQuarantineStore() {
|
|
|
9389
10085
|
var _hostFlagStore = null;
|
|
9390
10086
|
function hostFlagStore() {
|
|
9391
10087
|
if (!_hostFlagStore) {
|
|
9392
|
-
const dir = config?.configDir ??
|
|
10088
|
+
const dir = config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
9393
10089
|
_hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
|
|
9394
10090
|
}
|
|
9395
10091
|
return _hostFlagStore;
|
|
@@ -9456,13 +10152,13 @@ function parseSkillFrontmatter(content) {
|
|
|
9456
10152
|
return out;
|
|
9457
10153
|
}
|
|
9458
10154
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
9459
|
-
const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync:
|
|
9460
|
-
const skillsDir =
|
|
9461
|
-
const claudeMdPath =
|
|
10155
|
+
const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync11 } = await import("fs");
|
|
10156
|
+
const skillsDir = join22(configDir, codeName, "project", ".claude", "skills");
|
|
10157
|
+
const claudeMdPath = join22(configDir, codeName, "project", "CLAUDE.md");
|
|
9462
10158
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
9463
10159
|
const entries = [];
|
|
9464
10160
|
for (const dir of readdirSync6(skillsDir).sort()) {
|
|
9465
|
-
const skillFile =
|
|
10161
|
+
const skillFile = join22(skillsDir, dir, "SKILL.md");
|
|
9466
10162
|
if (!ex(skillFile)) continue;
|
|
9467
10163
|
try {
|
|
9468
10164
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -9504,7 +10200,7 @@ ${SKILLS_INDEX_END}`;
|
|
|
9504
10200
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
9505
10201
|
}
|
|
9506
10202
|
if (next !== current) {
|
|
9507
|
-
|
|
10203
|
+
writeFileSync11(claudeMdPath, next, "utf-8");
|
|
9508
10204
|
log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
|
|
9509
10205
|
}
|
|
9510
10206
|
}
|
|
@@ -9525,7 +10221,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
9525
10221
|
if (codeNames.length === 0) return;
|
|
9526
10222
|
void (async () => {
|
|
9527
10223
|
try {
|
|
9528
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
10224
|
+
const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
|
|
9529
10225
|
await api.post("/host/heartbeat", {
|
|
9530
10226
|
host_id: hostId,
|
|
9531
10227
|
agent_diagnostics: collectDiagnostics(codeNames)
|
|
@@ -9624,7 +10320,7 @@ async function pollCycle() {
|
|
|
9624
10320
|
}
|
|
9625
10321
|
try {
|
|
9626
10322
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
9627
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
10323
|
+
const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
|
|
9628
10324
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
9629
10325
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
9630
10326
|
let tailscaleHostname;
|
|
@@ -9654,7 +10350,7 @@ async function pollCycle() {
|
|
|
9654
10350
|
claudeAuth = await detectClaudeAuth();
|
|
9655
10351
|
} catch (err) {
|
|
9656
10352
|
const errText = err instanceof Error ? err.message : String(err);
|
|
9657
|
-
const errId =
|
|
10353
|
+
const errId = createHash14("sha256").update(errText).digest("hex").slice(0, 12);
|
|
9658
10354
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
9659
10355
|
}
|
|
9660
10356
|
const hostHasClaudeCode = state6.agents.some(
|
|
@@ -9779,7 +10475,7 @@ async function pollCycle() {
|
|
|
9779
10475
|
const {
|
|
9780
10476
|
collectResponsivenessProbes,
|
|
9781
10477
|
getResponsivenessIntervalMs
|
|
9782
|
-
} = await import("../responsiveness-probe-
|
|
10478
|
+
} = await import("../responsiveness-probe-WF3SMLS6.js");
|
|
9783
10479
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
9784
10480
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
9785
10481
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -9811,7 +10507,7 @@ async function pollCycle() {
|
|
|
9811
10507
|
collectResponsivenessProbes,
|
|
9812
10508
|
livePendingInboundOldestAgeSeconds,
|
|
9813
10509
|
parkPendingInbound
|
|
9814
|
-
} = await import("../responsiveness-probe-
|
|
10510
|
+
} = await import("../responsiveness-probe-WF3SMLS6.js");
|
|
9815
10511
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
|
|
9816
10512
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
9817
10513
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -9900,13 +10596,13 @@ async function pollCycle() {
|
|
|
9900
10596
|
);
|
|
9901
10597
|
if (hostFlagStore().getBoolean("wedge-transient-notice")) {
|
|
9902
10598
|
try {
|
|
9903
|
-
const paneTail =
|
|
10599
|
+
const paneTail = readFileSync19(paneLogPath(codeName), "utf8").slice(-65536);
|
|
9904
10600
|
const transient = detectTransientApiErrorInLog(paneTail);
|
|
9905
10601
|
if (transient) {
|
|
9906
|
-
const wedgeHome =
|
|
9907
|
-
if (
|
|
10602
|
+
const wedgeHome = join22(homedir11(), ".augmented", codeName);
|
|
10603
|
+
if (existsSync13(wedgeHome)) {
|
|
9908
10604
|
atomicWriteFileSync(
|
|
9909
|
-
|
|
10605
|
+
join22(wedgeHome, "watchdog-give-up.json"),
|
|
9910
10606
|
JSON.stringify({
|
|
9911
10607
|
gave_up_at: wedgeNow.toISOString(),
|
|
9912
10608
|
reason: "transient_overload"
|
|
@@ -10155,7 +10851,7 @@ async function pollCycle() {
|
|
|
10155
10851
|
} catch {
|
|
10156
10852
|
}
|
|
10157
10853
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
10158
|
-
const agentDir =
|
|
10854
|
+
const agentDir = join22(adapter.getAgentDir(prev.codeName), "provision");
|
|
10159
10855
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
10160
10856
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
10161
10857
|
}
|
|
@@ -10242,10 +10938,10 @@ async function pollCycle() {
|
|
|
10242
10938
|
// pending-inbound marker. Best-effort: a write failure is logged by
|
|
10243
10939
|
// the watchdog, never fails the poll cycle.
|
|
10244
10940
|
signalGiveUp: (codeName) => {
|
|
10245
|
-
const dir =
|
|
10246
|
-
if (!
|
|
10941
|
+
const dir = join22(homedir11(), ".augmented", codeName);
|
|
10942
|
+
if (!existsSync13(dir)) return;
|
|
10247
10943
|
atomicWriteFileSync(
|
|
10248
|
-
|
|
10944
|
+
join22(dir, "watchdog-give-up.json"),
|
|
10249
10945
|
JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
10250
10946
|
);
|
|
10251
10947
|
}
|
|
@@ -10384,7 +11080,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10384
11080
|
}
|
|
10385
11081
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10386
11082
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
10387
|
-
let agentDir =
|
|
11083
|
+
let agentDir = join22(adapter.getAgentDir(agent.code_name), "provision");
|
|
10388
11084
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
10389
11085
|
if (previousKnownStatus !== agent.status) {
|
|
10390
11086
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -10430,7 +11126,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10430
11126
|
const residuals = {
|
|
10431
11127
|
gatewayRunning: false,
|
|
10432
11128
|
portAllocated: false,
|
|
10433
|
-
provisionDirExists:
|
|
11129
|
+
provisionDirExists: existsSync13(agentDir)
|
|
10434
11130
|
};
|
|
10435
11131
|
if (!hasRevokedResiduals(residuals)) {
|
|
10436
11132
|
agentStates.push({
|
|
@@ -10568,7 +11264,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10568
11264
|
const frameworkId = refreshData.agent.framework ?? DEFAULT_FRAMEWORK;
|
|
10569
11265
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
10570
11266
|
const frameworkAdapter = getFramework(frameworkId);
|
|
10571
|
-
agentDir =
|
|
11267
|
+
agentDir = join22(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
10572
11268
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
10573
11269
|
agentRestartTimezoneInputs.set(agent.code_name, {
|
|
10574
11270
|
agentTimezone: typeof refreshData.agent.timezone === "string" ? refreshData.agent.timezone : null,
|
|
@@ -10613,9 +11309,9 @@ async function processAgent(agent, agentStates) {
|
|
|
10613
11309
|
try {
|
|
10614
11310
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
10615
11311
|
const changedFiles = [];
|
|
10616
|
-
|
|
11312
|
+
mkdirSync9(agentDir, { recursive: true });
|
|
10617
11313
|
for (const artifact of artifacts) {
|
|
10618
|
-
const filePath =
|
|
11314
|
+
const filePath = join22(agentDir, artifact.relativePath);
|
|
10619
11315
|
let existingHash;
|
|
10620
11316
|
let newHash;
|
|
10621
11317
|
let writeContent = artifact.content;
|
|
@@ -10634,8 +11330,8 @@ async function processAgent(agent, agentStates) {
|
|
|
10634
11330
|
};
|
|
10635
11331
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
10636
11332
|
try {
|
|
10637
|
-
const projectClaudeMd =
|
|
10638
|
-
const existing =
|
|
11333
|
+
const projectClaudeMd = join22(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
11334
|
+
const existing = readFileSync19(projectClaudeMd, "utf-8");
|
|
10639
11335
|
existingHash = sha256(stripDynamicSections(existing));
|
|
10640
11336
|
} catch {
|
|
10641
11337
|
existingHash = null;
|
|
@@ -10653,7 +11349,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10653
11349
|
const generatorKeys = Object.keys(generatorServers);
|
|
10654
11350
|
let existingRaw = "";
|
|
10655
11351
|
try {
|
|
10656
|
-
existingRaw =
|
|
11352
|
+
existingRaw = readFileSync19(filePath, "utf-8");
|
|
10657
11353
|
} catch {
|
|
10658
11354
|
}
|
|
10659
11355
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -10675,26 +11371,26 @@ async function processAgent(agent, agentStates) {
|
|
|
10675
11371
|
}
|
|
10676
11372
|
}
|
|
10677
11373
|
if (changedFiles.length > 0) {
|
|
10678
|
-
const isFirst = !
|
|
11374
|
+
const isFirst = !existsSync13(join22(agentDir, "CHARTER.md"));
|
|
10679
11375
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
10680
11376
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
10681
11377
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
10682
11378
|
for (const file of changedFiles) {
|
|
10683
|
-
const filePath =
|
|
10684
|
-
|
|
11379
|
+
const filePath = join22(agentDir, file.relativePath);
|
|
11380
|
+
mkdirSync9(dirname6(filePath), { recursive: true });
|
|
10685
11381
|
if (file.relativePath === ".mcp.json") {
|
|
10686
11382
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
10687
11383
|
} else {
|
|
10688
|
-
|
|
11384
|
+
writeFileSync10(filePath, file.content);
|
|
10689
11385
|
}
|
|
10690
11386
|
}
|
|
10691
11387
|
try {
|
|
10692
|
-
const provSkillsDir =
|
|
10693
|
-
if (
|
|
11388
|
+
const provSkillsDir = join22(agentDir, ".claude", "skills");
|
|
11389
|
+
if (existsSync13(provSkillsDir)) {
|
|
10694
11390
|
for (const folder of readdirSync5(provSkillsDir)) {
|
|
10695
11391
|
if (folder.startsWith("knowledge-")) {
|
|
10696
11392
|
try {
|
|
10697
|
-
rmSync4(
|
|
11393
|
+
rmSync4(join22(provSkillsDir, folder), { recursive: true });
|
|
10698
11394
|
} catch {
|
|
10699
11395
|
}
|
|
10700
11396
|
}
|
|
@@ -10707,7 +11403,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10707
11403
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
10708
11404
|
const hashes = /* @__PURE__ */ new Map();
|
|
10709
11405
|
for (const file of trackedFiles2) {
|
|
10710
|
-
const h = hashFile(
|
|
11406
|
+
const h = hashFile(join22(agentDir, file));
|
|
10711
11407
|
if (h) hashes.set(file, h);
|
|
10712
11408
|
}
|
|
10713
11409
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -10725,14 +11421,14 @@ async function processAgent(agent, agentStates) {
|
|
|
10725
11421
|
}
|
|
10726
11422
|
if (Array.isArray(refreshData.workflows)) {
|
|
10727
11423
|
try {
|
|
10728
|
-
const provWorkflowsDir =
|
|
10729
|
-
if (
|
|
11424
|
+
const provWorkflowsDir = join22(agentDir, ".claude", "workflows");
|
|
11425
|
+
if (existsSync13(provWorkflowsDir)) {
|
|
10730
11426
|
const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
|
|
10731
11427
|
for (const file of readdirSync5(provWorkflowsDir)) {
|
|
10732
11428
|
if (!file.endsWith(".js")) continue;
|
|
10733
11429
|
if (expected.has(file)) continue;
|
|
10734
11430
|
try {
|
|
10735
|
-
rmSync4(
|
|
11431
|
+
rmSync4(join22(provWorkflowsDir, file));
|
|
10736
11432
|
} catch {
|
|
10737
11433
|
}
|
|
10738
11434
|
}
|
|
@@ -10789,10 +11485,10 @@ async function processAgent(agent, agentStates) {
|
|
|
10789
11485
|
}
|
|
10790
11486
|
let lastDriftCheckAt = now;
|
|
10791
11487
|
const written = agentState.writtenHashes.get(agent.agent_id);
|
|
10792
|
-
if (written &&
|
|
11488
|
+
if (written && existsSync13(agentDir)) {
|
|
10793
11489
|
const driftedFiles = [];
|
|
10794
11490
|
for (const [file, expectedHash] of written) {
|
|
10795
|
-
const localHash = hashFile(
|
|
11491
|
+
const localHash = hashFile(join22(agentDir, file));
|
|
10796
11492
|
if (localHash && localHash !== expectedHash) {
|
|
10797
11493
|
driftedFiles.push(file);
|
|
10798
11494
|
}
|
|
@@ -10803,7 +11499,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10803
11499
|
try {
|
|
10804
11500
|
const localHashes = {};
|
|
10805
11501
|
for (const file of driftedFiles) {
|
|
10806
|
-
localHashes[file] = hashFile(
|
|
11502
|
+
localHashes[file] = hashFile(join22(agentDir, file));
|
|
10807
11503
|
}
|
|
10808
11504
|
await api.post("/host/drift", {
|
|
10809
11505
|
agent_id: agent.agent_id,
|
|
@@ -10990,15 +11686,15 @@ async function processAgent(agent, agentStates) {
|
|
|
10990
11686
|
const addedChannels = [...restartDecision.added];
|
|
10991
11687
|
const writeDmNoticeMarkers = isChannelAddRestart ? () => {
|
|
10992
11688
|
try {
|
|
10993
|
-
const agentAugmentedDir =
|
|
10994
|
-
|
|
11689
|
+
const agentAugmentedDir = join22(homedir11(), ".augmented", agent.code_name);
|
|
11690
|
+
mkdirSync9(agentAugmentedDir, { recursive: true });
|
|
10995
11691
|
const markerJson = JSON.stringify({
|
|
10996
11692
|
version: 1,
|
|
10997
11693
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10998
11694
|
added: addedChannels
|
|
10999
11695
|
});
|
|
11000
11696
|
for (const file of ["slack-channel-add-restart.json", "telegram-channel-add-restart.json"]) {
|
|
11001
|
-
atomicWriteFileSync(
|
|
11697
|
+
atomicWriteFileSync(join22(agentAugmentedDir, file), markerJson);
|
|
11002
11698
|
}
|
|
11003
11699
|
} catch (err) {
|
|
11004
11700
|
log(`[hot-reload] channel-add DM-notice marker write failed for '${agent.code_name}' (non-fatal): ${err.message}`);
|
|
@@ -11074,7 +11770,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11074
11770
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
11075
11771
|
msteamsEntry?.config
|
|
11076
11772
|
);
|
|
11077
|
-
const behaviourHash =
|
|
11773
|
+
const behaviourHash = createHash14("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
11078
11774
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
11079
11775
|
const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
|
|
11080
11776
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
@@ -11130,7 +11826,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11130
11826
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
11131
11827
|
slackEntry?.config
|
|
11132
11828
|
);
|
|
11133
|
-
const slackBehaviourHash =
|
|
11829
|
+
const slackBehaviourHash = createHash14("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
11134
11830
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
11135
11831
|
const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
|
|
11136
11832
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
@@ -11187,24 +11883,24 @@ async function processAgent(agent, agentStates) {
|
|
|
11187
11883
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK) === "claude-code") {
|
|
11188
11884
|
try {
|
|
11189
11885
|
const agentProvisionDir = agentDir;
|
|
11190
|
-
const projectDir =
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
const provisionMcpPath =
|
|
11194
|
-
const projectMcpPath =
|
|
11886
|
+
const projectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
|
|
11887
|
+
mkdirSync9(agentProvisionDir, { recursive: true });
|
|
11888
|
+
mkdirSync9(projectDir, { recursive: true });
|
|
11889
|
+
const provisionMcpPath = join22(agentProvisionDir, ".mcp.json");
|
|
11890
|
+
const projectMcpPath = join22(projectDir, ".mcp.json");
|
|
11195
11891
|
let mcpConfig = { mcpServers: {} };
|
|
11196
11892
|
try {
|
|
11197
|
-
mcpConfig = JSON.parse(
|
|
11893
|
+
mcpConfig = JSON.parse(readFileSync19(provisionMcpPath, "utf-8"));
|
|
11198
11894
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
11199
11895
|
} catch {
|
|
11200
11896
|
}
|
|
11201
|
-
const localDirectChatChannel =
|
|
11897
|
+
const localDirectChatChannel = join22(homedir11(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
11202
11898
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
11203
11899
|
const directChatTz = (() => {
|
|
11204
11900
|
const tz = directChatTeamSettings?.["timezone"];
|
|
11205
11901
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
11206
11902
|
})();
|
|
11207
|
-
if (
|
|
11903
|
+
if (existsSync13(localDirectChatChannel)) {
|
|
11208
11904
|
const directChatEnv = {
|
|
11209
11905
|
AGT_HOST: requireHost(),
|
|
11210
11906
|
// ENG-5901 Track D: templated — the manager exports the real
|
|
@@ -11224,7 +11920,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11224
11920
|
// ~/.augmented/<codeName>/.current-turn-initiator.json. Note getAgentDir
|
|
11225
11921
|
// returns the agent root (NOT the /provision subdir `agentDir` points at),
|
|
11226
11922
|
// so it byte-matches the broker readers' path.
|
|
11227
|
-
AGT_TURN_INITIATOR_FILE:
|
|
11923
|
+
AGT_TURN_INITIATOR_FILE: join22(
|
|
11228
11924
|
frameworkAdapter.getAgentDir(agent.code_name),
|
|
11229
11925
|
".current-turn-initiator.json"
|
|
11230
11926
|
)
|
|
@@ -11244,8 +11940,8 @@ async function processAgent(agent, agentStates) {
|
|
|
11244
11940
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
11245
11941
|
}
|
|
11246
11942
|
}
|
|
11247
|
-
const staleChannelsPath =
|
|
11248
|
-
if (
|
|
11943
|
+
const staleChannelsPath = join22(projectDir, ".mcp-channels.json");
|
|
11944
|
+
if (existsSync13(staleChannelsPath)) {
|
|
11249
11945
|
try {
|
|
11250
11946
|
rmSync4(staleChannelsPath, { force: true });
|
|
11251
11947
|
} catch {
|
|
@@ -11334,7 +12030,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11334
12030
|
}
|
|
11335
12031
|
if (hostFlagStore().getBoolean("connectivity-probe")) {
|
|
11336
12032
|
try {
|
|
11337
|
-
const probeProjectDir =
|
|
12033
|
+
const probeProjectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
|
|
11338
12034
|
await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
|
|
11339
12035
|
} catch (err) {
|
|
11340
12036
|
log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -11345,7 +12041,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11345
12041
|
const forceDue = attemptsLeft > 0;
|
|
11346
12042
|
let probeRan = false;
|
|
11347
12043
|
try {
|
|
11348
|
-
const probeProjectDir =
|
|
12044
|
+
const probeProjectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
|
|
11349
12045
|
probeRan = await runAgentSessionToolBindProbes(agent, integrations, probeProjectDir, { forceDue });
|
|
11350
12046
|
} catch (err) {
|
|
11351
12047
|
log(`Session-tool-bind probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -11358,9 +12054,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11358
12054
|
}
|
|
11359
12055
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
11360
12056
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
11361
|
-
if (mcpPath &&
|
|
12057
|
+
if (mcpPath && existsSync13(mcpPath)) {
|
|
11362
12058
|
try {
|
|
11363
|
-
const cfg = JSON.parse(
|
|
12059
|
+
const cfg = JSON.parse(readFileSync19(mcpPath, "utf-8"));
|
|
11364
12060
|
const expectedRemoteKeys = new Set(
|
|
11365
12061
|
integrations.map((i) => i.definition_id)
|
|
11366
12062
|
);
|
|
@@ -11385,11 +12081,11 @@ async function processAgent(agent, agentStates) {
|
|
|
11385
12081
|
recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
|
|
11386
12082
|
}
|
|
11387
12083
|
if (intHash !== prevIntHash) {
|
|
11388
|
-
const projectDir =
|
|
11389
|
-
const envIntPath =
|
|
12084
|
+
const projectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
|
|
12085
|
+
const envIntPath = join22(projectDir, ".env.integrations");
|
|
11390
12086
|
let preWriteEnv;
|
|
11391
12087
|
try {
|
|
11392
|
-
preWriteEnv =
|
|
12088
|
+
preWriteEnv = readFileSync19(envIntPath, "utf-8");
|
|
11393
12089
|
} catch {
|
|
11394
12090
|
preWriteEnv = void 0;
|
|
11395
12091
|
}
|
|
@@ -11401,9 +12097,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11401
12097
|
let rotationHandled = true;
|
|
11402
12098
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
11403
12099
|
try {
|
|
11404
|
-
const projectMcpPath =
|
|
11405
|
-
const postWriteEnv =
|
|
11406
|
-
const mcpContent =
|
|
12100
|
+
const projectMcpPath = join22(projectDir, ".mcp.json");
|
|
12101
|
+
const postWriteEnv = readFileSync19(envIntPath, "utf-8");
|
|
12102
|
+
const mcpContent = readFileSync19(projectMcpPath, "utf-8");
|
|
11407
12103
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
11408
12104
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
11409
12105
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -11480,10 +12176,10 @@ async function processAgent(agent, agentStates) {
|
|
|
11480
12176
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
11481
12177
|
}
|
|
11482
12178
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
11483
|
-
const headersHash =
|
|
12179
|
+
const headersHash = createHash14("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
11484
12180
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
11485
12181
|
}).join("\n");
|
|
11486
|
-
const mcpHash =
|
|
12182
|
+
const mcpHash = createHash14("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
11487
12183
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
11488
12184
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
11489
12185
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -11498,15 +12194,15 @@ async function processAgent(agent, agentStates) {
|
|
|
11498
12194
|
if (mcpHash !== prevMcpHash) {
|
|
11499
12195
|
for (const e of desiredEntries) {
|
|
11500
12196
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
11501
|
-
const urlHash =
|
|
12197
|
+
const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
11502
12198
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
11503
12199
|
}
|
|
11504
12200
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
11505
12201
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
11506
12202
|
if (mcpPath) {
|
|
11507
12203
|
try {
|
|
11508
|
-
const { readFileSync:
|
|
11509
|
-
const mcpConfig = JSON.parse(
|
|
12204
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
12205
|
+
const mcpConfig = JSON.parse(readFileSync20(mcpPath, "utf-8"));
|
|
11510
12206
|
if (mcpConfig.mcpServers) {
|
|
11511
12207
|
for (const key of Object.keys(mcpConfig.mcpServers)) {
|
|
11512
12208
|
if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
|
|
@@ -11607,7 +12303,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11607
12303
|
if (frameworkAdapter.installSkillFiles) {
|
|
11608
12304
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
11609
12305
|
const installedIntegrationSkills = [];
|
|
11610
|
-
const { createHash:
|
|
12306
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
11611
12307
|
const refreshAny = refreshData;
|
|
11612
12308
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
11613
12309
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -11636,7 +12332,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11636
12332
|
)
|
|
11637
12333
|
}));
|
|
11638
12334
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
11639
|
-
const contentHash =
|
|
12335
|
+
const contentHash = createHash15("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
11640
12336
|
const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
|
|
11641
12337
|
if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
|
|
11642
12338
|
frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
|
|
@@ -11653,14 +12349,14 @@ async function processAgent(agent, agentStates) {
|
|
|
11653
12349
|
const frameworkId2 = frameworkAdapter.id;
|
|
11654
12350
|
const candidateSkillDirs = [
|
|
11655
12351
|
// Claude Code — framework runtime tree
|
|
11656
|
-
|
|
12352
|
+
join22(homedir12(), ".augmented", agent.code_name, "skills"),
|
|
11657
12353
|
// Claude Code — project tree
|
|
11658
|
-
|
|
12354
|
+
join22(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
11659
12355
|
// Defensive: legacy provision-side path, not currently an
|
|
11660
12356
|
// install target but cheap to sweep.
|
|
11661
|
-
|
|
12357
|
+
join22(agentDir, ".claude", "skills")
|
|
11662
12358
|
];
|
|
11663
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
12359
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync13(d));
|
|
11664
12360
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
11665
12361
|
for (const dir of existingDirs) {
|
|
11666
12362
|
try {
|
|
@@ -11674,8 +12370,8 @@ async function processAgent(agent, agentStates) {
|
|
|
11674
12370
|
}
|
|
11675
12371
|
const removeSkillFolder = (entry, reason) => {
|
|
11676
12372
|
for (const dir of existingDirs) {
|
|
11677
|
-
const p =
|
|
11678
|
-
if (
|
|
12373
|
+
const p = join22(dir, entry);
|
|
12374
|
+
if (existsSync13(p)) {
|
|
11679
12375
|
rmSync5(p, { recursive: true, force: true });
|
|
11680
12376
|
}
|
|
11681
12377
|
}
|
|
@@ -11694,7 +12390,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11694
12390
|
const sharedSkillsPayload = refreshAny.shared_skills;
|
|
11695
12391
|
const desiredResolved = globalSkillsPayload !== void 0 || sharedSkillsPayload !== void 0;
|
|
11696
12392
|
const manifestPath = managedSkillManifestPath(
|
|
11697
|
-
|
|
12393
|
+
join22(homedir11(), ".augmented", agent.code_name)
|
|
11698
12394
|
);
|
|
11699
12395
|
const prevIds = /* @__PURE__ */ new Set([
|
|
11700
12396
|
...agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
|
|
@@ -11703,7 +12399,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11703
12399
|
const plan = planGlobalSkillSync(
|
|
11704
12400
|
[...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
|
|
11705
12401
|
prevIds,
|
|
11706
|
-
(content) =>
|
|
12402
|
+
(content) => createHash15("sha256").update(content).digest("hex").slice(0, 12),
|
|
11707
12403
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
|
|
11708
12404
|
{ desiredResolved }
|
|
11709
12405
|
);
|
|
@@ -11714,15 +12410,15 @@ async function processAgent(agent, agentStates) {
|
|
|
11714
12410
|
}
|
|
11715
12411
|
if (plan.removes.length) {
|
|
11716
12412
|
const globalSkillDirs = [
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
12413
|
+
join22(homedir11(), ".augmented", agent.code_name, "skills"),
|
|
12414
|
+
join22(homedir11(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
12415
|
+
join22(agentDir, ".claude", "skills")
|
|
11720
12416
|
];
|
|
11721
12417
|
for (const id of plan.removes) {
|
|
11722
12418
|
let prunedAny = false;
|
|
11723
12419
|
for (const dir of globalSkillDirs) {
|
|
11724
|
-
const p =
|
|
11725
|
-
if (
|
|
12420
|
+
const p = join22(dir, id);
|
|
12421
|
+
if (existsSync13(p) && existsSync13(join22(p, "SKILL.md"))) {
|
|
11726
12422
|
rmSync4(p, { recursive: true, force: true });
|
|
11727
12423
|
prunedAny = true;
|
|
11728
12424
|
}
|
|
@@ -11754,7 +12450,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11754
12450
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
11755
12451
|
if (!slug) continue;
|
|
11756
12452
|
try {
|
|
11757
|
-
const scriptHash =
|
|
12453
|
+
const scriptHash = createHash15("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
11758
12454
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
11759
12455
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
11760
12456
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -11769,9 +12465,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11769
12465
|
} else if (result.timedOut) {
|
|
11770
12466
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
11771
12467
|
} else {
|
|
11772
|
-
const stderrHash =
|
|
12468
|
+
const stderrHash = createHash15("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
11773
12469
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
11774
|
-
const missingCmdHash = missingCmd ?
|
|
12470
|
+
const missingCmdHash = missingCmd ? createHash15("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
11775
12471
|
log(
|
|
11776
12472
|
`Integration hook on_install '${slug}' exited ${result.exitCode} for '${agent.code_name}' ` + (missingCmdHash ? `[missing_command_hash=${missingCmdHash}] ` : "") + `[stderr_hash=${stderrHash} stderr_len=${result.stderr.length}]`
|
|
11777
12473
|
);
|
|
@@ -11928,8 +12624,8 @@ async function processAgent(agent, agentStates) {
|
|
|
11928
12624
|
const sess = getSessionState(agent.code_name);
|
|
11929
12625
|
let mcpJsonParsed = null;
|
|
11930
12626
|
try {
|
|
11931
|
-
const mcpPath =
|
|
11932
|
-
mcpJsonParsed = JSON.parse(
|
|
12627
|
+
const mcpPath = join22(getProjectDir(agent.code_name), ".mcp.json");
|
|
12628
|
+
mcpJsonParsed = JSON.parse(readFileSync19(mcpPath, "utf-8"));
|
|
11933
12629
|
} catch {
|
|
11934
12630
|
}
|
|
11935
12631
|
reapMissingMcpSessions({
|
|
@@ -12258,10 +12954,10 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
12258
12954
|
}
|
|
12259
12955
|
}
|
|
12260
12956
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
12261
|
-
if (trackedFiles.length > 0 &&
|
|
12957
|
+
if (trackedFiles.length > 0 && existsSync13(agentDir)) {
|
|
12262
12958
|
const hashes = /* @__PURE__ */ new Map();
|
|
12263
12959
|
for (const file of trackedFiles) {
|
|
12264
|
-
const h = hashFile(
|
|
12960
|
+
const h = hashFile(join22(agentDir, file));
|
|
12265
12961
|
if (h) hashes.set(file, h);
|
|
12266
12962
|
}
|
|
12267
12963
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -12276,7 +12972,7 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
12276
12972
|
refreshData.agent.onboarding_state
|
|
12277
12973
|
);
|
|
12278
12974
|
const obStep = obState.step;
|
|
12279
|
-
const markerPath =
|
|
12975
|
+
const markerPath = join22(homedir11(), ".augmented", agent.code_name, "onboarding-drive.json");
|
|
12280
12976
|
const marker = readOnboardingDriveMarker(markerPath);
|
|
12281
12977
|
const decision = decideOnboardingDrive(obStep, marker, Date.now(), obState.generation ?? 0);
|
|
12282
12978
|
if (decision.clearMarker) {
|
|
@@ -12341,10 +13037,12 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
|
|
|
12341
13037
|
const codeName = agent.code_name;
|
|
12342
13038
|
if (isOpencodeSessionHealthy(codeName)) {
|
|
12343
13039
|
await ensureOpencodeSlackIngest({ codeName, agentId: agent.agent_id, log });
|
|
13040
|
+
await ensureOpencodeTelegramIngest({ codeName, agentId: agent.agent_id, log });
|
|
12344
13041
|
return { decision: "healthy", spawnAttempted: false, sessionHealthyAfter: true };
|
|
12345
13042
|
}
|
|
12346
13043
|
stopOpencodeSlackIngest(codeName, log);
|
|
12347
|
-
|
|
13044
|
+
stopOpencodeTelegramIngest(codeName, log);
|
|
13045
|
+
const opencodeProjectDir = join22(getFramework("opencode").getAgentDir(codeName), "provision");
|
|
12348
13046
|
const openRouterRaw = refreshData.openrouter ?? null;
|
|
12349
13047
|
const serveEnv = {
|
|
12350
13048
|
AGT_HOST: requireHost(),
|
|
@@ -12384,8 +13082,8 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
|
|
|
12384
13082
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
12385
13083
|
const codeName = agent.code_name;
|
|
12386
13084
|
const projectDir = getProjectDir(codeName);
|
|
12387
|
-
const mcpConfigPath =
|
|
12388
|
-
const claudeMdPath =
|
|
13085
|
+
const mcpConfigPath = join22(projectDir, ".mcp.json");
|
|
13086
|
+
const claudeMdPath = join22(projectDir, "CLAUDE.md");
|
|
12389
13087
|
if (restartBreaker.isTripped(codeName)) {
|
|
12390
13088
|
const trip = restartBreaker.getTrip(codeName);
|
|
12391
13089
|
return {
|
|
@@ -12543,7 +13241,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
12543
13241
|
const ctx = getLastFailureContext(codeName);
|
|
12544
13242
|
const recovery = prepareForRespawn(codeName);
|
|
12545
13243
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
12546
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
13244
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
12547
13245
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
12548
13246
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
12549
13247
|
log(
|
|
@@ -12557,7 +13255,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
|
|
|
12557
13255
|
);
|
|
12558
13256
|
getHostId().then((hostId) => {
|
|
12559
13257
|
if (!hostId) return;
|
|
12560
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
13258
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash14("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
12561
13259
|
return api.post("/host/events", {
|
|
12562
13260
|
host_id: hostId,
|
|
12563
13261
|
agent_code_name: codeName,
|
|
@@ -12706,7 +13404,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
|
|
|
12706
13404
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
12707
13405
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
12708
13406
|
}
|
|
12709
|
-
const stableTasksHash =
|
|
13407
|
+
const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
12710
13408
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
12711
13409
|
if (stableTasksHash !== prevHash) {
|
|
12712
13410
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -12923,7 +13621,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
12923
13621
|
void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
|
|
12924
13622
|
void (async () => {
|
|
12925
13623
|
try {
|
|
12926
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13624
|
+
const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
|
|
12927
13625
|
await api.post("/host/heartbeat", {
|
|
12928
13626
|
host_id: hostId,
|
|
12929
13627
|
agent_diagnostics: collectDiagnostics([codeName])
|
|
@@ -12973,7 +13671,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
12973
13671
|
}
|
|
12974
13672
|
try {
|
|
12975
13673
|
const hostId = await getHostId();
|
|
12976
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13674
|
+
const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
|
|
12977
13675
|
await api.post("/host/heartbeat", {
|
|
12978
13676
|
host_id: hostId,
|
|
12979
13677
|
agent_diagnostics: collectDiagnostics([codeName])
|
|
@@ -13259,8 +13957,8 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
13259
13957
|
if (useDoorbell) {
|
|
13260
13958
|
try {
|
|
13261
13959
|
const doorbell = directChatDoorbellPath(agent.agentId, homedir11());
|
|
13262
|
-
|
|
13263
|
-
|
|
13960
|
+
mkdirSync9(dirname6(doorbell), { recursive: true });
|
|
13961
|
+
writeFileSync10(doorbell, String(Date.now()));
|
|
13264
13962
|
log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
|
|
13265
13963
|
return;
|
|
13266
13964
|
} catch (err) {
|
|
@@ -13369,8 +14067,8 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
|
|
|
13369
14067
|
if (run_id) openInjectedRunByCode.set(codeName, run_id);
|
|
13370
14068
|
try {
|
|
13371
14069
|
const doorbell = directChatDoorbellPath(agentId, homedir11());
|
|
13372
|
-
|
|
13373
|
-
|
|
14070
|
+
mkdirSync9(dirname6(doorbell), { recursive: true });
|
|
14071
|
+
writeFileSync10(doorbell, String(Date.now()));
|
|
13374
14072
|
} catch (err) {
|
|
13375
14073
|
log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
13376
14074
|
}
|
|
@@ -13471,7 +14169,7 @@ async function processClaudePairSessions(agents) {
|
|
|
13471
14169
|
killPairSession,
|
|
13472
14170
|
pairTmuxSession,
|
|
13473
14171
|
finalizeClaudePairOnboarding
|
|
13474
|
-
} = await import("../claude-pair-runtime-
|
|
14172
|
+
} = await import("../claude-pair-runtime-JU3CVFUU.js");
|
|
13475
14173
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
13476
14174
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
13477
14175
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -13721,8 +14419,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
13721
14419
|
};
|
|
13722
14420
|
}
|
|
13723
14421
|
async function syncMemories(agent, configDir, log2) {
|
|
13724
|
-
const projectDir =
|
|
13725
|
-
const memoryDir =
|
|
14422
|
+
const projectDir = join22(configDir, agent.code_name, "project");
|
|
14423
|
+
const memoryDir = join22(projectDir, "memory");
|
|
13726
14424
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
13727
14425
|
if (isFreshSync) {
|
|
13728
14426
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -13733,15 +14431,15 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
13733
14431
|
}
|
|
13734
14432
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
13735
14433
|
}
|
|
13736
|
-
if (
|
|
14434
|
+
if (existsSync13(memoryDir)) {
|
|
13737
14435
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
13738
14436
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
13739
14437
|
const changedMemories = [];
|
|
13740
14438
|
for (const file of readdirSync5(memoryDir)) {
|
|
13741
14439
|
if (!file.endsWith(".md")) continue;
|
|
13742
14440
|
try {
|
|
13743
|
-
const raw =
|
|
13744
|
-
const fileHash =
|
|
14441
|
+
const raw = readFileSync19(join22(memoryDir, file), "utf-8");
|
|
14442
|
+
const fileHash = createHash14("sha256").update(raw).digest("hex").slice(0, 16);
|
|
13745
14443
|
currentHashes.set(file, fileHash);
|
|
13746
14444
|
if (prevHashes.get(file) === fileHash) continue;
|
|
13747
14445
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -13765,7 +14463,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
13765
14463
|
} catch (err) {
|
|
13766
14464
|
for (const mem of changedMemories) {
|
|
13767
14465
|
for (const [file] of currentHashes) {
|
|
13768
|
-
const parsed = parseMemoryFile(
|
|
14466
|
+
const parsed = parseMemoryFile(readFileSync19(join22(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
13769
14467
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
13770
14468
|
}
|
|
13771
14469
|
}
|
|
@@ -13778,29 +14476,29 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
13778
14476
|
}
|
|
13779
14477
|
}
|
|
13780
14478
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
13781
|
-
const localFiles =
|
|
13782
|
-
const localListHash =
|
|
14479
|
+
const localFiles = existsSync13(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
14480
|
+
const localListHash = createHash14("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
13783
14481
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
13784
14482
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
13785
14483
|
try {
|
|
13786
14484
|
const dbMemories = await api.post("/host/memories", {
|
|
13787
14485
|
agent_id: agent.agent_id
|
|
13788
14486
|
});
|
|
13789
|
-
const responseHash =
|
|
14487
|
+
const responseHash = createHash14("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
|
|
13790
14488
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
13791
14489
|
return true;
|
|
13792
14490
|
}
|
|
13793
14491
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
13794
14492
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
13795
14493
|
if (dbMemories.memories?.length) {
|
|
13796
|
-
|
|
14494
|
+
mkdirSync9(memoryDir, { recursive: true });
|
|
13797
14495
|
let written = 0;
|
|
13798
14496
|
let overwritten = 0;
|
|
13799
14497
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
13800
14498
|
const mem = dbMemories.memories[i];
|
|
13801
14499
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
13802
14500
|
const slug = rawSlug || `memory-${i}`;
|
|
13803
|
-
const filePath =
|
|
14501
|
+
const filePath = join22(memoryDir, `${slug}.md`);
|
|
13804
14502
|
const desired = `---
|
|
13805
14503
|
name: ${JSON.stringify(mem.name)}
|
|
13806
14504
|
type: ${mem.type}
|
|
@@ -13809,23 +14507,23 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
13809
14507
|
|
|
13810
14508
|
${mem.content}
|
|
13811
14509
|
`;
|
|
13812
|
-
if (
|
|
14510
|
+
if (existsSync13(filePath)) {
|
|
13813
14511
|
let existing = "";
|
|
13814
14512
|
try {
|
|
13815
|
-
existing =
|
|
14513
|
+
existing = readFileSync19(filePath, "utf-8");
|
|
13816
14514
|
} catch {
|
|
13817
14515
|
}
|
|
13818
14516
|
if (existing === desired) continue;
|
|
13819
|
-
|
|
14517
|
+
writeFileSync10(filePath, desired);
|
|
13820
14518
|
overwritten++;
|
|
13821
14519
|
} else {
|
|
13822
|
-
|
|
14520
|
+
writeFileSync10(filePath, desired);
|
|
13823
14521
|
written++;
|
|
13824
14522
|
}
|
|
13825
14523
|
}
|
|
13826
14524
|
if (written > 0 || overwritten > 0) {
|
|
13827
14525
|
const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
13828
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
14526
|
+
lastLocalFileHash.set(agent.agent_id, createHash14("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
13829
14527
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
13830
14528
|
}
|
|
13831
14529
|
}
|
|
@@ -13836,7 +14534,7 @@ ${mem.content}
|
|
|
13836
14534
|
}
|
|
13837
14535
|
}
|
|
13838
14536
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
13839
|
-
if (
|
|
14537
|
+
if (existsSync13(agentDir)) {
|
|
13840
14538
|
try {
|
|
13841
14539
|
rmSync4(agentDir, { recursive: true, force: true });
|
|
13842
14540
|
log(`Removed provision directory for '${codeName}'`);
|
|
@@ -14058,6 +14756,7 @@ async function stopPolling(opts = {}) {
|
|
|
14058
14756
|
stopCaffeinate();
|
|
14059
14757
|
stopRealtimeChat();
|
|
14060
14758
|
stopAllOpencodeSlackIngests(log);
|
|
14759
|
+
stopAllOpencodeTelegramIngests(log);
|
|
14061
14760
|
log(formatDrainShutdownLine({ step: "stop-subsystems", elapsedMs: Date.now() - subsysStartedAt }));
|
|
14062
14761
|
for (const codeName of [...scheduledRunsByCode.keys()]) {
|
|
14063
14762
|
closeScheduledRunsForCode(codeName, "cancelled", "manager shutdown");
|
|
@@ -14072,8 +14771,8 @@ function startManager(opts) {
|
|
|
14072
14771
|
config = opts;
|
|
14073
14772
|
try {
|
|
14074
14773
|
const stateFile = getStateFile();
|
|
14075
|
-
if (
|
|
14076
|
-
const raw =
|
|
14774
|
+
if (existsSync13(stateFile)) {
|
|
14775
|
+
const raw = readFileSync19(stateFile, "utf-8");
|
|
14077
14776
|
const parsed = JSON.parse(raw);
|
|
14078
14777
|
if (Array.isArray(parsed.agents)) {
|
|
14079
14778
|
state6.agents = parsed.agents;
|
|
@@ -14100,7 +14799,7 @@ function startManager(opts) {
|
|
|
14100
14799
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
14101
14800
|
}
|
|
14102
14801
|
log(
|
|
14103
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
14802
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join22(homedir11(), ".augmented", "manager.log")}`
|
|
14104
14803
|
);
|
|
14105
14804
|
deployMcpAssets();
|
|
14106
14805
|
reapOrphanChannelMcps({ log });
|
|
@@ -14121,7 +14820,7 @@ async function reapOrphanedClaudePids() {
|
|
|
14121
14820
|
const looksLikeClaude = (pid) => {
|
|
14122
14821
|
if (process.platform !== "linux") return true;
|
|
14123
14822
|
try {
|
|
14124
|
-
const comm =
|
|
14823
|
+
const comm = readFileSync19(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
14125
14824
|
return comm.includes("claude");
|
|
14126
14825
|
} catch {
|
|
14127
14826
|
return false;
|
|
@@ -14218,14 +14917,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
14218
14917
|
}
|
|
14219
14918
|
}
|
|
14220
14919
|
function deployMcpAssets() {
|
|
14221
|
-
const targetDir =
|
|
14222
|
-
|
|
14920
|
+
const targetDir = join22(homedir11(), ".augmented", "_mcp");
|
|
14921
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
14223
14922
|
const moduleDir = dirname6(fileURLToPath(import.meta.url));
|
|
14224
14923
|
let mcpSourceDir = "";
|
|
14225
14924
|
let dir = moduleDir;
|
|
14226
14925
|
for (let i = 0; i < 6; i++) {
|
|
14227
|
-
const candidate =
|
|
14228
|
-
if (
|
|
14926
|
+
const candidate = join22(dir, "dist", "mcp");
|
|
14927
|
+
if (existsSync13(join22(candidate, "index.js"))) {
|
|
14229
14928
|
mcpSourceDir = candidate;
|
|
14230
14929
|
break;
|
|
14231
14930
|
}
|
|
@@ -14240,8 +14939,8 @@ function deployMcpAssets() {
|
|
|
14240
14939
|
const changedBasenames = [];
|
|
14241
14940
|
const fileHash = (p) => {
|
|
14242
14941
|
try {
|
|
14243
|
-
if (!
|
|
14244
|
-
return
|
|
14942
|
+
if (!existsSync13(p)) return null;
|
|
14943
|
+
return createHash14("sha256").update(readFileSync19(p)).digest("hex");
|
|
14245
14944
|
} catch {
|
|
14246
14945
|
return null;
|
|
14247
14946
|
}
|
|
@@ -14305,9 +15004,9 @@ function deployMcpAssets() {
|
|
|
14305
15004
|
// needs restarting to pick up a token rotation.
|
|
14306
15005
|
"xero.js"
|
|
14307
15006
|
]) {
|
|
14308
|
-
const src =
|
|
14309
|
-
const dst =
|
|
14310
|
-
if (!
|
|
15007
|
+
const src = join22(mcpSourceDir, file);
|
|
15008
|
+
const dst = join22(targetDir, file);
|
|
15009
|
+
if (!existsSync13(src)) continue;
|
|
14311
15010
|
const before = fileHash(dst);
|
|
14312
15011
|
try {
|
|
14313
15012
|
copyFileSync(src, dst);
|
|
@@ -14324,23 +15023,23 @@ function deployMcpAssets() {
|
|
|
14324
15023
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
14325
15024
|
restartRunningChannelMcps(changedBasenames);
|
|
14326
15025
|
}
|
|
14327
|
-
const localMcpPath =
|
|
15026
|
+
const localMcpPath = join22(targetDir, "index.js");
|
|
14328
15027
|
try {
|
|
14329
|
-
const agentsDir =
|
|
14330
|
-
if (
|
|
15028
|
+
const agentsDir = join22(homedir11(), ".augmented", "agents");
|
|
15029
|
+
if (existsSync13(agentsDir)) {
|
|
14331
15030
|
for (const entry of readdirSync5(agentsDir, { withFileTypes: true })) {
|
|
14332
15031
|
if (!entry.isDirectory()) continue;
|
|
14333
15032
|
for (const subdir of ["provision", "project"]) {
|
|
14334
|
-
const mcpJsonPath =
|
|
15033
|
+
const mcpJsonPath = join22(agentsDir, entry.name, subdir, ".mcp.json");
|
|
14335
15034
|
try {
|
|
14336
|
-
const raw =
|
|
15035
|
+
const raw = readFileSync19(mcpJsonPath, "utf-8");
|
|
14337
15036
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
14338
15037
|
const mcpConfig = JSON.parse(raw);
|
|
14339
15038
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
14340
15039
|
if (!augServer) continue;
|
|
14341
15040
|
augServer.command = "node";
|
|
14342
15041
|
augServer.args = [localMcpPath];
|
|
14343
|
-
|
|
15042
|
+
writeFileSync10(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
14344
15043
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
14345
15044
|
} catch {
|
|
14346
15045
|
}
|