@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.
@@ -46,7 +46,7 @@ import {
46
46
  resolveEffectivePinRaw,
47
47
  safeWriteJsonAtomic,
48
48
  setConfigHash
49
- } from "../chunk-GAYSFNTX.js";
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-7OBFGSQS.js";
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 createHash13 } from "crypto";
145
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, existsSync as existsSync12, rmSync as rmSync4, readdirSync as readdirSync5, statSync as statSync4, copyFileSync } from "fs";
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 join21, dirname as dirname6 } from "path";
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 existsSync11, mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync17, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
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 join20 } from "path";
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 join20(homedir10(), ".augmented", "restart-flags");
7647
+ return join21(homedir10(), ".augmented", "restart-flags");
6953
7648
  }
6954
7649
  function flagPath(codeName) {
6955
- return join20(restartFlagsDir(), `${codeName}.flag`);
7650
+ return join21(restartFlagsDir(), `${codeName}.flag`);
6956
7651
  }
6957
7652
  function readRestartFlags() {
6958
7653
  const dir = restartFlagsDir();
6959
- if (!existsSync11(dir)) return [];
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 = readFileSync17(join20(dir, entry), "utf8");
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 (existsSync11(path)) {
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 fingerprint2 = `${agent.status}|${agent.framework}|${agent.session_mode}|${agent.primary_model}`;
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 === fingerprint2) return;
7331
- lastKnown.set(agent.agent_id, fingerprint2);
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 = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8217
- return createHash13("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
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 = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
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 = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
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(readFileSync18(join21(projectDir, ".mcp.json"), "utf-8"));
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
- readFileSync18(join21(projectDir, ".env.integrations"), "utf-8")
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.343" : "dev";
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 (existsSync12(path)) return path;
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) => existsSync12(path))) return true;
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 (existsSync12(path)) {
8742
- const raw = readFileSync18(path, "utf-8").trim();
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
- mkdirSync8(dirname6(path), { recursive: true });
8759
- writeFileSync9(path, `${JSON.stringify(settings, null, 2)}
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 (existsSync12("/home/linuxbrew/.linuxbrew/bin/claude")) {
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 join21(homedir11(), ".augmented", ".last-self-update-applied");
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 = join21(homedir11(), ".augmented", ".last-update-check");
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 && existsSync12(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
9232
- const dir = join21(homedir11(), ".augmented");
9927
+ if (evalEmptyMcpConfigPath && existsSync13(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
9928
+ const dir = join22(homedir11(), ".augmented");
9233
9929
  try {
9234
- mkdirSync8(dir, { recursive: true });
9930
+ mkdirSync9(dir, { recursive: true });
9235
9931
  } catch {
9236
9932
  }
9237
- const p = join21(dir, ".eval-empty-mcp.json");
9238
- writeFileSync9(p, JSON.stringify({ mcpServers: {} }));
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 join21(config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
10025
+ return join22(config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
9330
10026
  }
9331
10027
  function channelHashCacheDir() {
9332
- return config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
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 ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
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 ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
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: writeFileSync10 } = await import("fs");
9460
- const skillsDir = join21(configDir, codeName, "project", ".claude", "skills");
9461
- const claudeMdPath = join21(configDir, codeName, "project", "CLAUDE.md");
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 = join21(skillsDir, dir, "SKILL.md");
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
- writeFileSync10(claudeMdPath, next, "utf-8");
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-XWJSAZB4.js");
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-XWJSAZB4.js");
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 = createHash13("sha256").update(errText).digest("hex").slice(0, 12);
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-XLS2FXIX.js");
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-XLS2FXIX.js");
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 = readFileSync18(paneLogPath(codeName), "utf8").slice(-65536);
10599
+ const paneTail = readFileSync19(paneLogPath(codeName), "utf8").slice(-65536);
9904
10600
  const transient = detectTransientApiErrorInLog(paneTail);
9905
10601
  if (transient) {
9906
- const wedgeHome = join21(homedir11(), ".augmented", codeName);
9907
- if (existsSync12(wedgeHome)) {
10602
+ const wedgeHome = join22(homedir11(), ".augmented", codeName);
10603
+ if (existsSync13(wedgeHome)) {
9908
10604
  atomicWriteFileSync(
9909
- join21(wedgeHome, "watchdog-give-up.json"),
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 = join21(adapter.getAgentDir(prev.codeName), "provision");
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 = join21(homedir11(), ".augmented", codeName);
10246
- if (!existsSync12(dir)) return;
10941
+ const dir = join22(homedir11(), ".augmented", codeName);
10942
+ if (!existsSync13(dir)) return;
10247
10943
  atomicWriteFileSync(
10248
- join21(dir, "watchdog-give-up.json"),
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 = join21(adapter.getAgentDir(agent.code_name), "provision");
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: existsSync12(agentDir)
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 = join21(frameworkAdapter.getAgentDir(agent.code_name), "provision");
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
- mkdirSync8(agentDir, { recursive: true });
11312
+ mkdirSync9(agentDir, { recursive: true });
10617
11313
  for (const artifact of artifacts) {
10618
- const filePath = join21(agentDir, artifact.relativePath);
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 = join21(config.configDir, agent.code_name, "project", "CLAUDE.md");
10638
- const existing = readFileSync18(projectClaudeMd, "utf-8");
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 = readFileSync18(filePath, "utf-8");
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 = !existsSync12(join21(agentDir, "CHARTER.md"));
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 = join21(agentDir, file.relativePath);
10684
- mkdirSync8(dirname6(filePath), { recursive: true });
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
- writeFileSync9(filePath, file.content);
11384
+ writeFileSync10(filePath, file.content);
10689
11385
  }
10690
11386
  }
10691
11387
  try {
10692
- const provSkillsDir = join21(agentDir, ".claude", "skills");
10693
- if (existsSync12(provSkillsDir)) {
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(join21(provSkillsDir, folder), { recursive: true });
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(join21(agentDir, file));
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 = join21(agentDir, ".claude", "workflows");
10729
- if (existsSync12(provWorkflowsDir)) {
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(join21(provWorkflowsDir, file));
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 && existsSync12(agentDir)) {
11488
+ if (written && existsSync13(agentDir)) {
10793
11489
  const driftedFiles = [];
10794
11490
  for (const [file, expectedHash] of written) {
10795
- const localHash = hashFile(join21(agentDir, file));
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(join21(agentDir, file));
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 = join21(homedir11(), ".augmented", agent.code_name);
10994
- mkdirSync8(agentAugmentedDir, { recursive: true });
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(join21(agentAugmentedDir, file), markerJson);
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 = createHash13("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
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 = createHash13("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
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 = join21(homedir11(), ".augmented", agent.code_name, "project");
11191
- mkdirSync8(agentProvisionDir, { recursive: true });
11192
- mkdirSync8(projectDir, { recursive: true });
11193
- const provisionMcpPath = join21(agentProvisionDir, ".mcp.json");
11194
- const projectMcpPath = join21(projectDir, ".mcp.json");
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(readFileSync18(provisionMcpPath, "utf-8"));
11893
+ mcpConfig = JSON.parse(readFileSync19(provisionMcpPath, "utf-8"));
11198
11894
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
11199
11895
  } catch {
11200
11896
  }
11201
- const localDirectChatChannel = join21(homedir11(), ".augmented", "_mcp", "direct-chat-channel.js");
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 (existsSync12(localDirectChatChannel)) {
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: join21(
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 = join21(projectDir, ".mcp-channels.json");
11248
- if (existsSync12(staleChannelsPath)) {
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 = join21(homedir11(), ".augmented", agent.code_name, "project");
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 = join21(homedir11(), ".augmented", agent.code_name, "project");
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 && existsSync12(mcpPath)) {
12057
+ if (mcpPath && existsSync13(mcpPath)) {
11362
12058
  try {
11363
- const cfg = JSON.parse(readFileSync18(mcpPath, "utf-8"));
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 = join21(homedir11(), ".augmented", agent.code_name, "project");
11389
- const envIntPath = join21(projectDir, ".env.integrations");
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 = readFileSync18(envIntPath, "utf-8");
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 = join21(projectDir, ".mcp.json");
11405
- const postWriteEnv = readFileSync18(envIntPath, "utf-8");
11406
- const mcpContent = readFileSync18(projectMcpPath, "utf-8");
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 = createHash13("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
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 = createHash13("sha256").update(hashBasis).digest("hex").slice(0, 16);
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 = createHash13("sha256").update(e.url).digest("hex").slice(0, 12);
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: readFileSync19 } = await import("fs");
11509
- const mcpConfig = JSON.parse(readFileSync19(mcpPath, "utf-8"));
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: createHash14 } = await import("crypto");
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 = createHash14("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
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
- join21(homedir12(), ".augmented", agent.code_name, "skills"),
12352
+ join22(homedir12(), ".augmented", agent.code_name, "skills"),
11657
12353
  // Claude Code — project tree
11658
- join21(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
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
- join21(agentDir, ".claude", "skills")
12357
+ join22(agentDir, ".claude", "skills")
11662
12358
  ];
11663
- const existingDirs = candidateSkillDirs.filter((d) => existsSync12(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 = join21(dir, entry);
11678
- if (existsSync12(p)) {
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
- join21(homedir11(), ".augmented", agent.code_name)
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) => createHash14("sha256").update(content).digest("hex").slice(0, 12),
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
- join21(homedir11(), ".augmented", agent.code_name, "skills"),
11718
- join21(homedir11(), ".augmented", agent.code_name, "project", ".claude", "skills"),
11719
- join21(agentDir, ".claude", "skills")
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 = join21(dir, id);
11725
- if (existsSync12(p) && existsSync12(join21(p, "SKILL.md"))) {
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 = createHash14("sha256").update(hook.script).digest("hex").slice(0, 12);
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 = createHash14("sha256").update(result.stderr).digest("hex").slice(0, 12);
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 ? createHash14("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
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 = join21(getProjectDir(agent.code_name), ".mcp.json");
11932
- mcpJsonParsed = JSON.parse(readFileSync18(mcpPath, "utf-8"));
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 && existsSync12(agentDir)) {
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(join21(agentDir, file));
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 = join21(homedir11(), ".augmented", agent.code_name, "onboarding-drive.json");
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
- const opencodeProjectDir = join21(getFramework("opencode").getAgentDir(codeName), "provision");
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 = join21(projectDir, ".mcp.json");
12388
- const claudeMdPath = join21(projectDir, "CLAUDE.md");
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:${createHash13("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
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:${createHash13("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
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 = createHash13("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
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-XWJSAZB4.js");
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-XWJSAZB4.js");
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
- mkdirSync8(dirname6(doorbell), { recursive: true });
13263
- writeFileSync9(doorbell, String(Date.now()));
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
- mkdirSync8(dirname6(doorbell), { recursive: true });
13373
- writeFileSync9(doorbell, String(Date.now()));
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-NUZWSILK.js");
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 = join21(configDir, agent.code_name, "project");
13725
- const memoryDir = join21(projectDir, "memory");
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 (existsSync12(memoryDir)) {
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 = readFileSync18(join21(memoryDir, file), "utf-8");
13744
- const fileHash = createHash13("sha256").update(raw).digest("hex").slice(0, 16);
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(readFileSync18(join21(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
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 = existsSync12(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
13782
- const localListHash = createHash13("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
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 = createHash13("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
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
- mkdirSync8(memoryDir, { recursive: true });
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 = join21(memoryDir, `${slug}.md`);
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 (existsSync12(filePath)) {
14510
+ if (existsSync13(filePath)) {
13813
14511
  let existing = "";
13814
14512
  try {
13815
- existing = readFileSync18(filePath, "utf-8");
14513
+ existing = readFileSync19(filePath, "utf-8");
13816
14514
  } catch {
13817
14515
  }
13818
14516
  if (existing === desired) continue;
13819
- writeFileSync9(filePath, desired);
14517
+ writeFileSync10(filePath, desired);
13820
14518
  overwritten++;
13821
14519
  } else {
13822
- writeFileSync9(filePath, desired);
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, createHash13("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
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 (existsSync12(agentDir)) {
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 (existsSync12(stateFile)) {
14076
- const raw = readFileSync18(stateFile, "utf-8");
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=${join21(homedir11(), ".augmented", "manager.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 = readFileSync18(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
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 = join21(homedir11(), ".augmented", "_mcp");
14222
- mkdirSync8(targetDir, { recursive: true });
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 = join21(dir, "dist", "mcp");
14228
- if (existsSync12(join21(candidate, "index.js"))) {
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 (!existsSync12(p)) return null;
14244
- return createHash13("sha256").update(readFileSync18(p)).digest("hex");
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 = join21(mcpSourceDir, file);
14309
- const dst = join21(targetDir, file);
14310
- if (!existsSync12(src)) continue;
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 = join21(targetDir, "index.js");
15026
+ const localMcpPath = join22(targetDir, "index.js");
14328
15027
  try {
14329
- const agentsDir = join21(homedir11(), ".augmented", "agents");
14330
- if (existsSync12(agentsDir)) {
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 = join21(agentsDir, entry.name, subdir, ".mcp.json");
15033
+ const mcpJsonPath = join22(agentsDir, entry.name, subdir, ".mcp.json");
14335
15034
  try {
14336
- const raw = readFileSync18(mcpJsonPath, "utf-8");
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
- writeFileSync9(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
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
  }