@integrity-labs/agt-cli 0.28.342 → 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,14 +46,14 @@ import {
46
46
  resolveEffectivePinRaw,
47
47
  safeWriteJsonAtomic,
48
48
  setConfigHash
49
- } from "../chunk-V2FVNWF2.js";
49
+ } from "../chunk-AJ23F3MU.js";
50
50
  import {
51
51
  getProjectDir as getProjectDir2,
52
52
  getReadyTasks,
53
53
  loadSchedulerState,
54
54
  markTaskFired,
55
55
  syncTasksToScheduler
56
- } from "../chunk-I3YS5WFV.js";
56
+ } from "../chunk-EP6E6CIY.js";
57
57
  import {
58
58
  AnchorSessionClient,
59
59
  CONVERSATION_FAILURE_CATEGORIES,
@@ -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 createHash12 } 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";
@@ -6712,6 +6736,769 @@ function stopAllOpencodeSlackIngests(log2) {
6712
6736
  for (const codeName of [...ingests.keys()]) stopOpencodeSlackIngest(codeName, log2);
6713
6737
  }
6714
6738
 
6739
+ // src/lib/manager/opencode-scheduler.ts
6740
+ import { createHash as createHash12 } from "crypto";
6741
+ var MAX_OPENCODE_SCHED_CONCURRENCY = 2;
6742
+ var opencodeSchedulerStates = /* @__PURE__ */ new Map();
6743
+ var inFlightOpencodeTasks = /* @__PURE__ */ new Set();
6744
+ var opencodeTaskConcurrency = /* @__PURE__ */ new Map();
6745
+ var knownOpencodeTaskHashes = /* @__PURE__ */ new Map();
6746
+ function shouldDeliver(task, reply) {
6747
+ if ((task.deliveryPolicy ?? "always") === "never") return false;
6748
+ if (task.deliveryMode === "none") return false;
6749
+ if (task.deliveryTo == null) return false;
6750
+ return typeof reply === "string" && reply.trim().length > 0;
6751
+ }
6752
+ async function syncAndCheckOpencodeScheduler(agent, tasks, refreshData) {
6753
+ const codeName = agent.code_name;
6754
+ const tasksHash = createHash12("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
6755
+ if (knownOpencodeTaskHashes.get(agent.agent_id) !== tasksHash) {
6756
+ const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
6757
+ const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
6758
+ opencodeSchedulerStates.set(codeName, state8);
6759
+ knownOpencodeTaskHashes.set(agent.agent_id, tasksHash);
6760
+ log(`[opencode-scheduler] tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
6761
+ }
6762
+ if (!opencodeSchedulerStates.has(codeName)) {
6763
+ opencodeSchedulerStates.set(codeName, loadSchedulerState(codeName));
6764
+ }
6765
+ const state7 = opencodeSchedulerStates.get(codeName);
6766
+ const ready = getReadyTasks(state7, inFlightOpencodeTasks);
6767
+ if (ready.length === 0) return;
6768
+ if (!isOpencodeSessionHealthy(codeName)) {
6769
+ log(`[opencode-scheduler] '${codeName}' has ${ready.length} due task(s) but no healthy serve yet - deferring`);
6770
+ return;
6771
+ }
6772
+ for (const task of ready) {
6773
+ if ((opencodeTaskConcurrency.get(codeName) ?? 0) >= MAX_OPENCODE_SCHED_CONCURRENCY) break;
6774
+ if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
6775
+ opencodeSchedulerStates.set(codeName, markTaskFired(codeName, task.taskId, "ok"));
6776
+ continue;
6777
+ }
6778
+ inFlightOpencodeTasks.add(task.taskId);
6779
+ opencodeTaskConcurrency.set(codeName, (opencodeTaskConcurrency.get(codeName) ?? 0) + 1);
6780
+ void fireOpencodeScheduledTask(agent, task).finally(() => {
6781
+ inFlightOpencodeTasks.delete(task.taskId);
6782
+ opencodeTaskConcurrency.set(
6783
+ codeName,
6784
+ Math.max(0, (opencodeTaskConcurrency.get(codeName) ?? 1) - 1)
6785
+ );
6786
+ });
6787
+ }
6788
+ }
6789
+ async function fireOpencodeScheduledTask(agent, task) {
6790
+ const codeName = agent.code_name;
6791
+ const conversationKey = `scheduled:${task.taskId}:${Date.now()}`;
6792
+ try {
6793
+ const result = await injectOpencodeMessage(
6794
+ codeName,
6795
+ {
6796
+ channelId: "scheduler",
6797
+ conversationKey,
6798
+ senderId: "scheduler",
6799
+ text: task.prompt,
6800
+ meta: {
6801
+ source: "scheduled_task",
6802
+ task: task.name,
6803
+ // The manager delivers the output; the agent isn't replying into a
6804
+ // channel thread itself, so it must not try to.
6805
+ requires_reply: "false"
6806
+ }
6807
+ },
6808
+ { awaitReply: true }
6809
+ );
6810
+ if (result.status === "declined") {
6811
+ log(`[opencode-scheduler] task '${task.name}' for '${codeName}' declined: ${result.reason}`);
6812
+ return;
6813
+ }
6814
+ const reply = result.status === "replied" ? result.reply : null;
6815
+ if (shouldDeliver(task, reply)) {
6816
+ try {
6817
+ await deliverScheduledTaskOutput(codeName, agent.agent_id, task.deliveryTo, reply, task.taskId);
6818
+ } catch (err) {
6819
+ log(`[opencode-scheduler] delivery failed for task '${task.name}' on '${codeName}': ${err.message}`);
6820
+ }
6821
+ } else if ((task.deliveryPolicy ?? "always") !== "never" && task.deliveryTo != null) {
6822
+ log(`[opencode-scheduler] task '${task.name}' for '${codeName}' produced no output to deliver`);
6823
+ }
6824
+ opencodeSchedulerStates.set(codeName, markTaskFired(codeName, task.taskId, "ok"));
6825
+ } catch (err) {
6826
+ log(`[opencode-scheduler] task '${task.name}' for '${codeName}' failed: ${err.message}`);
6827
+ opencodeSchedulerStates.set(codeName, markTaskFired(codeName, task.taskId, "error"));
6828
+ }
6829
+ }
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
+
6715
7502
  // src/lib/wedge-detection.ts
6716
7503
  var DEFAULTS = {
6717
7504
  inboundWaitSeconds: 120,
@@ -6852,24 +7639,24 @@ function partitionActionableByPoison(actionable, states, config2) {
6852
7639
  }
6853
7640
 
6854
7641
  // src/lib/restart-flags.ts
6855
- 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";
6856
7643
  import { homedir as homedir10 } from "os";
6857
- import { join as join20 } from "path";
6858
- import { randomUUID } from "crypto";
7644
+ import { join as join21 } from "path";
7645
+ import { randomUUID as randomUUID2 } from "crypto";
6859
7646
  function restartFlagsDir() {
6860
- return join20(homedir10(), ".augmented", "restart-flags");
7647
+ return join21(homedir10(), ".augmented", "restart-flags");
6861
7648
  }
6862
7649
  function flagPath(codeName) {
6863
- return join20(restartFlagsDir(), `${codeName}.flag`);
7650
+ return join21(restartFlagsDir(), `${codeName}.flag`);
6864
7651
  }
6865
7652
  function readRestartFlags() {
6866
7653
  const dir = restartFlagsDir();
6867
- if (!existsSync11(dir)) return [];
7654
+ if (!existsSync12(dir)) return [];
6868
7655
  const out = [];
6869
7656
  for (const entry of readdirSync4(dir)) {
6870
7657
  if (!entry.endsWith(".flag")) continue;
6871
7658
  try {
6872
- const raw = readFileSync17(join20(dir, entry), "utf8");
7659
+ const raw = readFileSync18(join21(dir, entry), "utf8");
6873
7660
  const parsed = JSON.parse(raw);
6874
7661
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
6875
7662
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -6887,7 +7674,7 @@ function readRestartFlags() {
6887
7674
  }
6888
7675
  function deleteRestartFlag(codeName) {
6889
7676
  const path = flagPath(codeName);
6890
- if (existsSync11(path)) {
7677
+ if (existsSync12(path)) {
6891
7678
  rmSync3(path, { force: true });
6892
7679
  }
6893
7680
  }
@@ -7233,10 +8020,10 @@ function startRealtimeConfig(config2) {
7233
8020
  filter: filterStr
7234
8021
  }, (payload) => {
7235
8022
  const agent = payload.new;
7236
- 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}`;
7237
8024
  const prev = lastKnown.get(agent.agent_id);
7238
- if (prev === fingerprint2) return;
7239
- lastKnown.set(agent.agent_id, fingerprint2);
8025
+ if (prev === fingerprint3) return;
8026
+ lastKnown.set(agent.agent_id, fingerprint3);
7240
8027
  log2(`[realtime] Agent config changed: ${agent.code_name} (status=${agent.status})`);
7241
8028
  onConfigChange(agent);
7242
8029
  }).subscribe((status) => {
@@ -8121,15 +8908,15 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
8121
8908
  var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
8122
8909
  function projectMcpHash(_codeName, projectDir) {
8123
8910
  try {
8124
- const raw = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8125
- return createHash12("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");
8126
8913
  } catch {
8127
8914
  return null;
8128
8915
  }
8129
8916
  }
8130
8917
  function projectMcpKeys(_codeName, projectDir) {
8131
8918
  try {
8132
- const raw = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8919
+ const raw = readFileSync19(join22(projectDir, ".mcp.json"), "utf-8");
8133
8920
  const parsed = JSON.parse(raw);
8134
8921
  const servers = parsed.mcpServers;
8135
8922
  if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
@@ -8147,7 +8934,7 @@ function seedSessionLaunchBaselines(codeName, projectDir) {
8147
8934
  else runningMcpServerKeys.delete(codeName);
8148
8935
  let launchStructure = null;
8149
8936
  try {
8150
- const raw = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8937
+ const raw = readFileSync19(join22(projectDir, ".mcp.json"), "utf-8");
8151
8938
  launchStructure = managedMcpStructureHashFromFile(
8152
8939
  JSON.parse(raw),
8153
8940
  isManagedMcpServerKey
@@ -8247,7 +9034,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
8247
9034
  if (result && result.rebindCandidates.length > 0 && hostFlagStore().getBoolean("session-tool-rebind")) {
8248
9035
  let mcpJsonForRebind = null;
8249
9036
  try {
8250
- mcpJsonForRebind = JSON.parse(readFileSync18(join21(projectDir, ".mcp.json"), "utf-8"));
9037
+ mcpJsonForRebind = JSON.parse(readFileSync19(join22(projectDir, ".mcp.json"), "utf-8"));
8251
9038
  } catch {
8252
9039
  mcpJsonForRebind = null;
8253
9040
  }
@@ -8376,7 +9163,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
8376
9163
  function projectChannelSecretHash(projectDir) {
8377
9164
  try {
8378
9165
  const entries = parseEnvIntegrations(
8379
- readFileSync18(join21(projectDir, ".env.integrations"), "utf-8")
9166
+ readFileSync19(join22(projectDir, ".env.integrations"), "utf-8")
8380
9167
  );
8381
9168
  return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
8382
9169
  } catch {
@@ -8439,6 +9226,7 @@ function clearAgentCaches(agentId, codeName) {
8439
9226
  const channelCacheMutated = clearAgentState(agentId, codeName);
8440
9227
  agentChannelTokens.delete(codeName);
8441
9228
  stopOpencodeSlackIngest(codeName, log);
9229
+ stopOpencodeTelegramIngest(codeName, log);
8442
9230
  agentFrameworkCache.delete(codeName);
8443
9231
  kanbanBoardCache.delete(codeName);
8444
9232
  clearKanbanNudgeState(codeName);
@@ -8468,7 +9256,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
8468
9256
  var lastVersionCheckAt = 0;
8469
9257
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
8470
9258
  var lastResponsivenessProbeAt = 0;
8471
- var agtCliVersion = true ? "0.28.342" : "dev";
9259
+ var agtCliVersion = true ? "0.28.344" : "dev";
8472
9260
  function resolveBrewPath(execFileSync2) {
8473
9261
  try {
8474
9262
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -8481,7 +9269,7 @@ function resolveBrewPath(execFileSync2) {
8481
9269
  "/usr/local/bin/brew"
8482
9270
  ];
8483
9271
  for (const path of fallbacks) {
8484
- if (existsSync12(path)) return path;
9272
+ if (existsSync13(path)) return path;
8485
9273
  }
8486
9274
  return null;
8487
9275
  }
@@ -8491,7 +9279,7 @@ function claudeBinaryInstalled(execFileSync2) {
8491
9279
  "/opt/homebrew/bin/claude",
8492
9280
  "/usr/local/bin/claude"
8493
9281
  ];
8494
- if (canonical.some((path) => existsSync12(path))) return true;
9282
+ if (canonical.some((path) => existsSync13(path))) return true;
8495
9283
  try {
8496
9284
  execFileSync2("which", ["claude"], { timeout: 5e3 });
8497
9285
  return true;
@@ -8646,8 +9434,8 @@ function claudeManagedSettingsPath() {
8646
9434
  function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
8647
9435
  try {
8648
9436
  let settings = {};
8649
- if (existsSync12(path)) {
8650
- const raw = readFileSync18(path, "utf-8").trim();
9437
+ if (existsSync13(path)) {
9438
+ const raw = readFileSync19(path, "utf-8").trim();
8651
9439
  if (raw) {
8652
9440
  let parsed;
8653
9441
  try {
@@ -8663,8 +9451,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
8663
9451
  }
8664
9452
  if (settings.channelsEnabled === true) return "ok";
8665
9453
  settings.channelsEnabled = true;
8666
- mkdirSync8(dirname6(path), { recursive: true });
8667
- writeFileSync9(path, `${JSON.stringify(settings, null, 2)}
9454
+ mkdirSync9(dirname6(path), { recursive: true });
9455
+ writeFileSync10(path, `${JSON.stringify(settings, null, 2)}
8668
9456
  `);
8669
9457
  log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
8670
9458
  return "ok";
@@ -8709,7 +9497,7 @@ async function ensureFrameworkBinary(frameworkId) {
8709
9497
  if (!process.env.PATH?.split(":").includes(brewBinDir)) {
8710
9498
  process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
8711
9499
  }
8712
- if (existsSync12("/home/linuxbrew/.linuxbrew/bin/claude")) {
9500
+ if (existsSync13("/home/linuxbrew/.linuxbrew/bin/claude")) {
8713
9501
  log("Claude Code installed successfully");
8714
9502
  } else {
8715
9503
  log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
@@ -8765,7 +9553,7 @@ ${r.stderr}`;
8765
9553
  }
8766
9554
  var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
8767
9555
  function selfUpdateAppliedMarkerPath() {
8768
- return join21(homedir11(), ".augmented", ".last-self-update-applied");
9556
+ return join22(homedir11(), ".augmented", ".last-self-update-applied");
8769
9557
  }
8770
9558
  var selfUpdateUpToDateLogged = false;
8771
9559
  var selfUpdatePinnedLogged = false;
@@ -8791,7 +9579,7 @@ async function checkAndUpdateCli(opts) {
8791
9579
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
8792
9580
  if (!isBrewFormula && !isNpmGlobal) return "noop";
8793
9581
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
8794
- const markerPath = join21(homedir11(), ".augmented", ".last-update-check");
9582
+ const markerPath = join22(homedir11(), ".augmented", ".last-update-check");
8795
9583
  if (!force) {
8796
9584
  try {
8797
9585
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
@@ -9136,14 +9924,14 @@ async function checkClaudeAuth() {
9136
9924
  }
9137
9925
  var evalEmptyMcpConfigPath = null;
9138
9926
  function ensureEvalEmptyMcpConfig() {
9139
- if (evalEmptyMcpConfigPath && existsSync12(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
9140
- const dir = join21(homedir11(), ".augmented");
9927
+ if (evalEmptyMcpConfigPath && existsSync13(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
9928
+ const dir = join22(homedir11(), ".augmented");
9141
9929
  try {
9142
- mkdirSync8(dir, { recursive: true });
9930
+ mkdirSync9(dir, { recursive: true });
9143
9931
  } catch {
9144
9932
  }
9145
- const p = join21(dir, ".eval-empty-mcp.json");
9146
- writeFileSync9(p, JSON.stringify({ mcpServers: {} }));
9933
+ const p = join22(dir, ".eval-empty-mcp.json");
9934
+ writeFileSync10(p, JSON.stringify({ mcpServers: {} }));
9147
9935
  evalEmptyMcpConfigPath = p;
9148
9936
  return p;
9149
9937
  }
@@ -9234,10 +10022,10 @@ function resolveConversationEvalBackend() {
9234
10022
  return conversationEvalBackend;
9235
10023
  }
9236
10024
  function getStateFile() {
9237
- 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");
9238
10026
  }
9239
10027
  function channelHashCacheDir() {
9240
- return config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
10028
+ return config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented");
9241
10029
  }
9242
10030
  function loadChannelHashCache2() {
9243
10031
  loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
@@ -9289,7 +10077,7 @@ function removeDeliveryBaselineEntries(agentId) {
9289
10077
  var _channelQuarantineStore = null;
9290
10078
  function channelQuarantineStore() {
9291
10079
  if (!_channelQuarantineStore) {
9292
- const dir = config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
10080
+ const dir = config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented");
9293
10081
  _channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
9294
10082
  }
9295
10083
  return _channelQuarantineStore;
@@ -9297,7 +10085,7 @@ function channelQuarantineStore() {
9297
10085
  var _hostFlagStore = null;
9298
10086
  function hostFlagStore() {
9299
10087
  if (!_hostFlagStore) {
9300
- const dir = config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
10088
+ const dir = config?.configDir ?? join22(process.env["HOME"] ?? "/tmp", ".augmented");
9301
10089
  _hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
9302
10090
  }
9303
10091
  return _hostFlagStore;
@@ -9364,13 +10152,13 @@ function parseSkillFrontmatter(content) {
9364
10152
  return out;
9365
10153
  }
9366
10154
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
9367
- const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync10 } = await import("fs");
9368
- const skillsDir = join21(configDir, codeName, "project", ".claude", "skills");
9369
- 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");
9370
10158
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
9371
10159
  const entries = [];
9372
10160
  for (const dir of readdirSync6(skillsDir).sort()) {
9373
- const skillFile = join21(skillsDir, dir, "SKILL.md");
10161
+ const skillFile = join22(skillsDir, dir, "SKILL.md");
9374
10162
  if (!ex(skillFile)) continue;
9375
10163
  try {
9376
10164
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -9412,7 +10200,7 @@ ${SKILLS_INDEX_END}`;
9412
10200
  next = current.trimEnd() + "\n\n" + section + "\n";
9413
10201
  }
9414
10202
  if (next !== current) {
9415
- writeFileSync10(claudeMdPath, next, "utf-8");
10203
+ writeFileSync11(claudeMdPath, next, "utf-8");
9416
10204
  log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
9417
10205
  }
9418
10206
  }
@@ -9433,7 +10221,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
9433
10221
  if (codeNames.length === 0) return;
9434
10222
  void (async () => {
9435
10223
  try {
9436
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
10224
+ const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
9437
10225
  await api.post("/host/heartbeat", {
9438
10226
  host_id: hostId,
9439
10227
  agent_diagnostics: collectDiagnostics(codeNames)
@@ -9532,7 +10320,7 @@ async function pollCycle() {
9532
10320
  }
9533
10321
  try {
9534
10322
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
9535
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
10323
+ const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
9536
10324
  const diagCodeNames = [...agentState.persistentSessionAgents];
9537
10325
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
9538
10326
  let tailscaleHostname;
@@ -9562,7 +10350,7 @@ async function pollCycle() {
9562
10350
  claudeAuth = await detectClaudeAuth();
9563
10351
  } catch (err) {
9564
10352
  const errText = err instanceof Error ? err.message : String(err);
9565
- const errId = createHash12("sha256").update(errText).digest("hex").slice(0, 12);
10353
+ const errId = createHash14("sha256").update(errText).digest("hex").slice(0, 12);
9566
10354
  log(`Claude auth detection failed (error_id=${errId})`);
9567
10355
  }
9568
10356
  const hostHasClaudeCode = state6.agents.some(
@@ -9687,7 +10475,7 @@ async function pollCycle() {
9687
10475
  const {
9688
10476
  collectResponsivenessProbes,
9689
10477
  getResponsivenessIntervalMs
9690
- } = await import("../responsiveness-probe-XLS2FXIX.js");
10478
+ } = await import("../responsiveness-probe-WF3SMLS6.js");
9691
10479
  const probeIntervalMs = getResponsivenessIntervalMs();
9692
10480
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
9693
10481
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -9719,8 +10507,8 @@ async function pollCycle() {
9719
10507
  collectResponsivenessProbes,
9720
10508
  livePendingInboundOldestAgeSeconds,
9721
10509
  parkPendingInbound
9722
- } = await import("../responsiveness-probe-XLS2FXIX.js");
9723
- const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
10510
+ } = await import("../responsiveness-probe-WF3SMLS6.js");
10511
+ const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
9724
10512
  const wedgeNow = /* @__PURE__ */ new Date();
9725
10513
  const liveAgents = agentState.persistentSessionAgents;
9726
10514
  for (const tracked of consecutiveWedgeCycles.keys()) {
@@ -9808,13 +10596,13 @@ async function pollCycle() {
9808
10596
  );
9809
10597
  if (hostFlagStore().getBoolean("wedge-transient-notice")) {
9810
10598
  try {
9811
- const paneTail = readFileSync18(paneLogPath(codeName), "utf8").slice(-65536);
10599
+ const paneTail = readFileSync19(paneLogPath(codeName), "utf8").slice(-65536);
9812
10600
  const transient = detectTransientApiErrorInLog(paneTail);
9813
10601
  if (transient) {
9814
- const wedgeHome = join21(homedir11(), ".augmented", codeName);
9815
- if (existsSync12(wedgeHome)) {
10602
+ const wedgeHome = join22(homedir11(), ".augmented", codeName);
10603
+ if (existsSync13(wedgeHome)) {
9816
10604
  atomicWriteFileSync(
9817
- join21(wedgeHome, "watchdog-give-up.json"),
10605
+ join22(wedgeHome, "watchdog-give-up.json"),
9818
10606
  JSON.stringify({
9819
10607
  gave_up_at: wedgeNow.toISOString(),
9820
10608
  reason: "transient_overload"
@@ -10063,7 +10851,7 @@ async function pollCycle() {
10063
10851
  } catch {
10064
10852
  }
10065
10853
  killAgentChannelProcesses(prev.codeName, { log });
10066
- const agentDir = join21(adapter.getAgentDir(prev.codeName), "provision");
10854
+ const agentDir = join22(adapter.getAgentDir(prev.codeName), "provision");
10067
10855
  await cleanupAgentFiles(prev.codeName, agentDir);
10068
10856
  clearAgentCaches(prev.agentId, prev.codeName);
10069
10857
  }
@@ -10150,10 +10938,10 @@ async function pollCycle() {
10150
10938
  // pending-inbound marker. Best-effort: a write failure is logged by
10151
10939
  // the watchdog, never fails the poll cycle.
10152
10940
  signalGiveUp: (codeName) => {
10153
- const dir = join21(homedir11(), ".augmented", codeName);
10154
- if (!existsSync12(dir)) return;
10941
+ const dir = join22(homedir11(), ".augmented", codeName);
10942
+ if (!existsSync13(dir)) return;
10155
10943
  atomicWriteFileSync(
10156
- join21(dir, "watchdog-give-up.json"),
10944
+ join22(dir, "watchdog-give-up.json"),
10157
10945
  JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
10158
10946
  );
10159
10947
  }
@@ -10292,7 +11080,7 @@ async function processAgent(agent, agentStates) {
10292
11080
  }
10293
11081
  const now = (/* @__PURE__ */ new Date()).toISOString();
10294
11082
  const adapter = resolveAgentFramework(agent.code_name);
10295
- let agentDir = join21(adapter.getAgentDir(agent.code_name), "provision");
11083
+ let agentDir = join22(adapter.getAgentDir(agent.code_name), "provision");
10296
11084
  if (agent.status === "draft" || agent.status === "paused") {
10297
11085
  if (previousKnownStatus !== agent.status) {
10298
11086
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
@@ -10338,7 +11126,7 @@ async function processAgent(agent, agentStates) {
10338
11126
  const residuals = {
10339
11127
  gatewayRunning: false,
10340
11128
  portAllocated: false,
10341
- provisionDirExists: existsSync12(agentDir)
11129
+ provisionDirExists: existsSync13(agentDir)
10342
11130
  };
10343
11131
  if (!hasRevokedResiduals(residuals)) {
10344
11132
  agentStates.push({
@@ -10476,7 +11264,7 @@ async function processAgent(agent, agentStates) {
10476
11264
  const frameworkId = refreshData.agent.framework ?? DEFAULT_FRAMEWORK;
10477
11265
  agentFrameworkCache.set(agent.code_name, frameworkId);
10478
11266
  const frameworkAdapter = getFramework(frameworkId);
10479
- agentDir = join21(frameworkAdapter.getAgentDir(agent.code_name), "provision");
11267
+ agentDir = join22(frameworkAdapter.getAgentDir(agent.code_name), "provision");
10480
11268
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
10481
11269
  agentRestartTimezoneInputs.set(agent.code_name, {
10482
11270
  agentTimezone: typeof refreshData.agent.timezone === "string" ? refreshData.agent.timezone : null,
@@ -10521,9 +11309,9 @@ async function processAgent(agent, agentStates) {
10521
11309
  try {
10522
11310
  const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
10523
11311
  const changedFiles = [];
10524
- mkdirSync8(agentDir, { recursive: true });
11312
+ mkdirSync9(agentDir, { recursive: true });
10525
11313
  for (const artifact of artifacts) {
10526
- const filePath = join21(agentDir, artifact.relativePath);
11314
+ const filePath = join22(agentDir, artifact.relativePath);
10527
11315
  let existingHash;
10528
11316
  let newHash;
10529
11317
  let writeContent = artifact.content;
@@ -10542,8 +11330,8 @@ async function processAgent(agent, agentStates) {
10542
11330
  };
10543
11331
  newHash = sha256(stripDynamicSections(artifact.content));
10544
11332
  try {
10545
- const projectClaudeMd = join21(config.configDir, agent.code_name, "project", "CLAUDE.md");
10546
- 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");
10547
11335
  existingHash = sha256(stripDynamicSections(existing));
10548
11336
  } catch {
10549
11337
  existingHash = null;
@@ -10561,7 +11349,7 @@ async function processAgent(agent, agentStates) {
10561
11349
  const generatorKeys = Object.keys(generatorServers);
10562
11350
  let existingRaw = "";
10563
11351
  try {
10564
- existingRaw = readFileSync18(filePath, "utf-8");
11352
+ existingRaw = readFileSync19(filePath, "utf-8");
10565
11353
  } catch {
10566
11354
  }
10567
11355
  const existingServers = parseMcp(existingRaw);
@@ -10583,26 +11371,26 @@ async function processAgent(agent, agentStates) {
10583
11371
  }
10584
11372
  }
10585
11373
  if (changedFiles.length > 0) {
10586
- const isFirst = !existsSync12(join21(agentDir, "CHARTER.md"));
11374
+ const isFirst = !existsSync13(join22(agentDir, "CHARTER.md"));
10587
11375
  const verb = isFirst ? "Provisioning" : "Updating";
10588
11376
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
10589
11377
  log(`${verb} '${agent.code_name}': ${fileNames}`);
10590
11378
  for (const file of changedFiles) {
10591
- const filePath = join21(agentDir, file.relativePath);
10592
- mkdirSync8(dirname6(filePath), { recursive: true });
11379
+ const filePath = join22(agentDir, file.relativePath);
11380
+ mkdirSync9(dirname6(filePath), { recursive: true });
10593
11381
  if (file.relativePath === ".mcp.json") {
10594
11382
  safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
10595
11383
  } else {
10596
- writeFileSync9(filePath, file.content);
11384
+ writeFileSync10(filePath, file.content);
10597
11385
  }
10598
11386
  }
10599
11387
  try {
10600
- const provSkillsDir = join21(agentDir, ".claude", "skills");
10601
- if (existsSync12(provSkillsDir)) {
11388
+ const provSkillsDir = join22(agentDir, ".claude", "skills");
11389
+ if (existsSync13(provSkillsDir)) {
10602
11390
  for (const folder of readdirSync5(provSkillsDir)) {
10603
11391
  if (folder.startsWith("knowledge-")) {
10604
11392
  try {
10605
- rmSync4(join21(provSkillsDir, folder), { recursive: true });
11393
+ rmSync4(join22(provSkillsDir, folder), { recursive: true });
10606
11394
  } catch {
10607
11395
  }
10608
11396
  }
@@ -10615,7 +11403,7 @@ async function processAgent(agent, agentStates) {
10615
11403
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
10616
11404
  const hashes = /* @__PURE__ */ new Map();
10617
11405
  for (const file of trackedFiles2) {
10618
- const h = hashFile(join21(agentDir, file));
11406
+ const h = hashFile(join22(agentDir, file));
10619
11407
  if (h) hashes.set(file, h);
10620
11408
  }
10621
11409
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -10633,14 +11421,14 @@ async function processAgent(agent, agentStates) {
10633
11421
  }
10634
11422
  if (Array.isArray(refreshData.workflows)) {
10635
11423
  try {
10636
- const provWorkflowsDir = join21(agentDir, ".claude", "workflows");
10637
- if (existsSync12(provWorkflowsDir)) {
11424
+ const provWorkflowsDir = join22(agentDir, ".claude", "workflows");
11425
+ if (existsSync13(provWorkflowsDir)) {
10638
11426
  const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
10639
11427
  for (const file of readdirSync5(provWorkflowsDir)) {
10640
11428
  if (!file.endsWith(".js")) continue;
10641
11429
  if (expected.has(file)) continue;
10642
11430
  try {
10643
- rmSync4(join21(provWorkflowsDir, file));
11431
+ rmSync4(join22(provWorkflowsDir, file));
10644
11432
  } catch {
10645
11433
  }
10646
11434
  }
@@ -10697,10 +11485,10 @@ async function processAgent(agent, agentStates) {
10697
11485
  }
10698
11486
  let lastDriftCheckAt = now;
10699
11487
  const written = agentState.writtenHashes.get(agent.agent_id);
10700
- if (written && existsSync12(agentDir)) {
11488
+ if (written && existsSync13(agentDir)) {
10701
11489
  const driftedFiles = [];
10702
11490
  for (const [file, expectedHash] of written) {
10703
- const localHash = hashFile(join21(agentDir, file));
11491
+ const localHash = hashFile(join22(agentDir, file));
10704
11492
  if (localHash && localHash !== expectedHash) {
10705
11493
  driftedFiles.push(file);
10706
11494
  }
@@ -10711,7 +11499,7 @@ async function processAgent(agent, agentStates) {
10711
11499
  try {
10712
11500
  const localHashes = {};
10713
11501
  for (const file of driftedFiles) {
10714
- localHashes[file] = hashFile(join21(agentDir, file));
11502
+ localHashes[file] = hashFile(join22(agentDir, file));
10715
11503
  }
10716
11504
  await api.post("/host/drift", {
10717
11505
  agent_id: agent.agent_id,
@@ -10898,15 +11686,15 @@ async function processAgent(agent, agentStates) {
10898
11686
  const addedChannels = [...restartDecision.added];
10899
11687
  const writeDmNoticeMarkers = isChannelAddRestart ? () => {
10900
11688
  try {
10901
- const agentAugmentedDir = join21(homedir11(), ".augmented", agent.code_name);
10902
- mkdirSync8(agentAugmentedDir, { recursive: true });
11689
+ const agentAugmentedDir = join22(homedir11(), ".augmented", agent.code_name);
11690
+ mkdirSync9(agentAugmentedDir, { recursive: true });
10903
11691
  const markerJson = JSON.stringify({
10904
11692
  version: 1,
10905
11693
  at: (/* @__PURE__ */ new Date()).toISOString(),
10906
11694
  added: addedChannels
10907
11695
  });
10908
11696
  for (const file of ["slack-channel-add-restart.json", "telegram-channel-add-restart.json"]) {
10909
- atomicWriteFileSync(join21(agentAugmentedDir, file), markerJson);
11697
+ atomicWriteFileSync(join22(agentAugmentedDir, file), markerJson);
10910
11698
  }
10911
11699
  } catch (err) {
10912
11700
  log(`[hot-reload] channel-add DM-notice marker write failed for '${agent.code_name}' (non-fatal): ${err.message}`);
@@ -10982,7 +11770,7 @@ async function processAgent(agent, agentStates) {
10982
11770
  const behaviourSubset = extractMsTeamsBehaviourSubset(
10983
11771
  msteamsEntry?.config
10984
11772
  );
10985
- const behaviourHash = createHash12("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
11773
+ const behaviourHash = createHash14("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
10986
11774
  const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
10987
11775
  const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
10988
11776
  const behaviourDecision = decideSenderPolicyRestart({
@@ -11038,7 +11826,7 @@ async function processAgent(agent, agentStates) {
11038
11826
  const slackBehaviourSubset = extractSlackBehaviourSubset(
11039
11827
  slackEntry?.config
11040
11828
  );
11041
- const slackBehaviourHash = createHash12("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
11829
+ const slackBehaviourHash = createHash14("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
11042
11830
  const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
11043
11831
  const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
11044
11832
  const slackBehaviourDecision = decideSenderPolicyRestart({
@@ -11095,24 +11883,24 @@ async function processAgent(agent, agentStates) {
11095
11883
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK) === "claude-code") {
11096
11884
  try {
11097
11885
  const agentProvisionDir = agentDir;
11098
- const projectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
11099
- mkdirSync8(agentProvisionDir, { recursive: true });
11100
- mkdirSync8(projectDir, { recursive: true });
11101
- const provisionMcpPath = join21(agentProvisionDir, ".mcp.json");
11102
- 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");
11103
11891
  let mcpConfig = { mcpServers: {} };
11104
11892
  try {
11105
- mcpConfig = JSON.parse(readFileSync18(provisionMcpPath, "utf-8"));
11893
+ mcpConfig = JSON.parse(readFileSync19(provisionMcpPath, "utf-8"));
11106
11894
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
11107
11895
  } catch {
11108
11896
  }
11109
- const localDirectChatChannel = join21(homedir11(), ".augmented", "_mcp", "direct-chat-channel.js");
11897
+ const localDirectChatChannel = join22(homedir11(), ".augmented", "_mcp", "direct-chat-channel.js");
11110
11898
  const directChatTeamSettings = refreshData.team?.settings;
11111
11899
  const directChatTz = (() => {
11112
11900
  const tz = directChatTeamSettings?.["timezone"];
11113
11901
  return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
11114
11902
  })();
11115
- if (existsSync12(localDirectChatChannel)) {
11903
+ if (existsSync13(localDirectChatChannel)) {
11116
11904
  const directChatEnv = {
11117
11905
  AGT_HOST: requireHost(),
11118
11906
  // ENG-5901 Track D: templated — the manager exports the real
@@ -11132,7 +11920,7 @@ async function processAgent(agent, agentStates) {
11132
11920
  // ~/.augmented/<codeName>/.current-turn-initiator.json. Note getAgentDir
11133
11921
  // returns the agent root (NOT the /provision subdir `agentDir` points at),
11134
11922
  // so it byte-matches the broker readers' path.
11135
- AGT_TURN_INITIATOR_FILE: join21(
11923
+ AGT_TURN_INITIATOR_FILE: join22(
11136
11924
  frameworkAdapter.getAgentDir(agent.code_name),
11137
11925
  ".current-turn-initiator.json"
11138
11926
  )
@@ -11152,8 +11940,8 @@ async function processAgent(agent, agentStates) {
11152
11940
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
11153
11941
  }
11154
11942
  }
11155
- const staleChannelsPath = join21(projectDir, ".mcp-channels.json");
11156
- if (existsSync12(staleChannelsPath)) {
11943
+ const staleChannelsPath = join22(projectDir, ".mcp-channels.json");
11944
+ if (existsSync13(staleChannelsPath)) {
11157
11945
  try {
11158
11946
  rmSync4(staleChannelsPath, { force: true });
11159
11947
  } catch {
@@ -11242,7 +12030,7 @@ async function processAgent(agent, agentStates) {
11242
12030
  }
11243
12031
  if (hostFlagStore().getBoolean("connectivity-probe")) {
11244
12032
  try {
11245
- const probeProjectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
12033
+ const probeProjectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
11246
12034
  await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
11247
12035
  } catch (err) {
11248
12036
  log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
@@ -11253,7 +12041,7 @@ async function processAgent(agent, agentStates) {
11253
12041
  const forceDue = attemptsLeft > 0;
11254
12042
  let probeRan = false;
11255
12043
  try {
11256
- const probeProjectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
12044
+ const probeProjectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
11257
12045
  probeRan = await runAgentSessionToolBindProbes(agent, integrations, probeProjectDir, { forceDue });
11258
12046
  } catch (err) {
11259
12047
  log(`Session-tool-bind probe failed for '${agent.code_name}': ${err.message}`);
@@ -11266,9 +12054,9 @@ async function processAgent(agent, agentStates) {
11266
12054
  }
11267
12055
  if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
11268
12056
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
11269
- if (mcpPath && existsSync12(mcpPath)) {
12057
+ if (mcpPath && existsSync13(mcpPath)) {
11270
12058
  try {
11271
- const cfg = JSON.parse(readFileSync18(mcpPath, "utf-8"));
12059
+ const cfg = JSON.parse(readFileSync19(mcpPath, "utf-8"));
11272
12060
  const expectedRemoteKeys = new Set(
11273
12061
  integrations.map((i) => i.definition_id)
11274
12062
  );
@@ -11293,11 +12081,11 @@ async function processAgent(agent, agentStates) {
11293
12081
  recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
11294
12082
  }
11295
12083
  if (intHash !== prevIntHash) {
11296
- const projectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
11297
- const envIntPath = join21(projectDir, ".env.integrations");
12084
+ const projectDir = join22(homedir11(), ".augmented", agent.code_name, "project");
12085
+ const envIntPath = join22(projectDir, ".env.integrations");
11298
12086
  let preWriteEnv;
11299
12087
  try {
11300
- preWriteEnv = readFileSync18(envIntPath, "utf-8");
12088
+ preWriteEnv = readFileSync19(envIntPath, "utf-8");
11301
12089
  } catch {
11302
12090
  preWriteEnv = void 0;
11303
12091
  }
@@ -11309,9 +12097,9 @@ async function processAgent(agent, agentStates) {
11309
12097
  let rotationHandled = true;
11310
12098
  if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
11311
12099
  try {
11312
- const projectMcpPath = join21(projectDir, ".mcp.json");
11313
- const postWriteEnv = readFileSync18(envIntPath, "utf-8");
11314
- 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");
11315
12103
  const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
11316
12104
  const mcpJsonForReap = JSON.parse(mcpContent);
11317
12105
  const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
@@ -11388,10 +12176,10 @@ async function processAgent(agent, agentStates) {
11388
12176
  desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
11389
12177
  }
11390
12178
  const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
11391
- const headersHash = createHash12("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
12179
+ const headersHash = createHash14("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
11392
12180
  return `${e.serverId}|${e.url}|${headersHash}`;
11393
12181
  }).join("\n");
11394
- const mcpHash = createHash12("sha256").update(hashBasis).digest("hex").slice(0, 16);
12182
+ const mcpHash = createHash14("sha256").update(hashBasis).digest("hex").slice(0, 16);
11395
12183
  const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
11396
12184
  const structureHash = managedMcpStructureHash(desiredEntries);
11397
12185
  const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
@@ -11406,15 +12194,15 @@ async function processAgent(agent, agentStates) {
11406
12194
  if (mcpHash !== prevMcpHash) {
11407
12195
  for (const e of desiredEntries) {
11408
12196
  frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
11409
- const urlHash = createHash12("sha256").update(e.url).digest("hex").slice(0, 12);
12197
+ const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
11410
12198
  log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
11411
12199
  }
11412
12200
  if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
11413
12201
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
11414
12202
  if (mcpPath) {
11415
12203
  try {
11416
- const { readFileSync: readFileSync19 } = await import("fs");
11417
- const mcpConfig = JSON.parse(readFileSync19(mcpPath, "utf-8"));
12204
+ const { readFileSync: readFileSync20 } = await import("fs");
12205
+ const mcpConfig = JSON.parse(readFileSync20(mcpPath, "utf-8"));
11418
12206
  if (mcpConfig.mcpServers) {
11419
12207
  for (const key of Object.keys(mcpConfig.mcpServers)) {
11420
12208
  if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
@@ -11515,7 +12303,7 @@ async function processAgent(agent, agentStates) {
11515
12303
  if (frameworkAdapter.installSkillFiles) {
11516
12304
  const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
11517
12305
  const installedIntegrationSkills = [];
11518
- const { createHash: createHash13 } = await import("crypto");
12306
+ const { createHash: createHash15 } = await import("crypto");
11519
12307
  const refreshAny = refreshData;
11520
12308
  const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
11521
12309
  const contextBySlug = /* @__PURE__ */ new Map();
@@ -11544,7 +12332,7 @@ async function processAgent(agent, agentStates) {
11544
12332
  )
11545
12333
  }));
11546
12334
  const bundle = buildIntegrationBundle(renderedScopes);
11547
- const contentHash = createHash13("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
12335
+ const contentHash = createHash15("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
11548
12336
  const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
11549
12337
  if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
11550
12338
  frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
@@ -11561,14 +12349,14 @@ async function processAgent(agent, agentStates) {
11561
12349
  const frameworkId2 = frameworkAdapter.id;
11562
12350
  const candidateSkillDirs = [
11563
12351
  // Claude Code — framework runtime tree
11564
- join21(homedir12(), ".augmented", agent.code_name, "skills"),
12352
+ join22(homedir12(), ".augmented", agent.code_name, "skills"),
11565
12353
  // Claude Code — project tree
11566
- join21(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
12354
+ join22(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
11567
12355
  // Defensive: legacy provision-side path, not currently an
11568
12356
  // install target but cheap to sweep.
11569
- join21(agentDir, ".claude", "skills")
12357
+ join22(agentDir, ".claude", "skills")
11570
12358
  ];
11571
- const existingDirs = candidateSkillDirs.filter((d) => existsSync12(d));
12359
+ const existingDirs = candidateSkillDirs.filter((d) => existsSync13(d));
11572
12360
  const discoveredEntries = /* @__PURE__ */ new Set();
11573
12361
  for (const dir of existingDirs) {
11574
12362
  try {
@@ -11582,8 +12370,8 @@ async function processAgent(agent, agentStates) {
11582
12370
  }
11583
12371
  const removeSkillFolder = (entry, reason) => {
11584
12372
  for (const dir of existingDirs) {
11585
- const p = join21(dir, entry);
11586
- if (existsSync12(p)) {
12373
+ const p = join22(dir, entry);
12374
+ if (existsSync13(p)) {
11587
12375
  rmSync5(p, { recursive: true, force: true });
11588
12376
  }
11589
12377
  }
@@ -11602,7 +12390,7 @@ async function processAgent(agent, agentStates) {
11602
12390
  const sharedSkillsPayload = refreshAny.shared_skills;
11603
12391
  const desiredResolved = globalSkillsPayload !== void 0 || sharedSkillsPayload !== void 0;
11604
12392
  const manifestPath = managedSkillManifestPath(
11605
- join21(homedir11(), ".augmented", agent.code_name)
12393
+ join22(homedir11(), ".augmented", agent.code_name)
11606
12394
  );
11607
12395
  const prevIds = /* @__PURE__ */ new Set([
11608
12396
  ...agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
@@ -11611,7 +12399,7 @@ async function processAgent(agent, agentStates) {
11611
12399
  const plan = planGlobalSkillSync(
11612
12400
  [...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
11613
12401
  prevIds,
11614
- (content) => createHash13("sha256").update(content).digest("hex").slice(0, 12),
12402
+ (content) => createHash15("sha256").update(content).digest("hex").slice(0, 12),
11615
12403
  (skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
11616
12404
  { desiredResolved }
11617
12405
  );
@@ -11622,15 +12410,15 @@ async function processAgent(agent, agentStates) {
11622
12410
  }
11623
12411
  if (plan.removes.length) {
11624
12412
  const globalSkillDirs = [
11625
- join21(homedir11(), ".augmented", agent.code_name, "skills"),
11626
- join21(homedir11(), ".augmented", agent.code_name, "project", ".claude", "skills"),
11627
- 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")
11628
12416
  ];
11629
12417
  for (const id of plan.removes) {
11630
12418
  let prunedAny = false;
11631
12419
  for (const dir of globalSkillDirs) {
11632
- const p = join21(dir, id);
11633
- if (existsSync12(p) && existsSync12(join21(p, "SKILL.md"))) {
12420
+ const p = join22(dir, id);
12421
+ if (existsSync13(p) && existsSync13(join22(p, "SKILL.md"))) {
11634
12422
  rmSync4(p, { recursive: true, force: true });
11635
12423
  prunedAny = true;
11636
12424
  }
@@ -11662,7 +12450,7 @@ async function processAgent(agent, agentStates) {
11662
12450
  const slug = hook.integration_slug ?? hook.plugin_slug;
11663
12451
  if (!slug) continue;
11664
12452
  try {
11665
- const scriptHash = createHash13("sha256").update(hook.script).digest("hex").slice(0, 12);
12453
+ const scriptHash = createHash15("sha256").update(hook.script).digest("hex").slice(0, 12);
11666
12454
  const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
11667
12455
  if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
11668
12456
  const result = await frameworkAdapter.executePluginHook({
@@ -11677,9 +12465,9 @@ async function processAgent(agent, agentStates) {
11677
12465
  } else if (result.timedOut) {
11678
12466
  log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
11679
12467
  } else {
11680
- const stderrHash = createHash13("sha256").update(result.stderr).digest("hex").slice(0, 12);
12468
+ const stderrHash = createHash15("sha256").update(result.stderr).digest("hex").slice(0, 12);
11681
12469
  const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
11682
- const missingCmdHash = missingCmd ? createHash13("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
12470
+ const missingCmdHash = missingCmd ? createHash15("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
11683
12471
  log(
11684
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}]`
11685
12473
  );
@@ -11836,8 +12624,8 @@ async function processAgent(agent, agentStates) {
11836
12624
  const sess = getSessionState(agent.code_name);
11837
12625
  let mcpJsonParsed = null;
11838
12626
  try {
11839
- const mcpPath = join21(getProjectDir(agent.code_name), ".mcp.json");
11840
- 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"));
11841
12629
  } catch {
11842
12630
  }
11843
12631
  reapMissingMcpSessions({
@@ -11983,6 +12771,9 @@ async function processAgent(agent, agentStates) {
11983
12771
  log(`[mcp-presence-reaper] presence check failed for '${agent.code_name}': ${err.message}`);
11984
12772
  }
11985
12773
  }
12774
+ if (agentFw === "opencode" && tasks.length > 0) {
12775
+ await syncAndCheckOpencodeScheduler(agent, tasks, refreshData);
12776
+ }
11986
12777
  } else if (agentFw === "claude-code" && tasks.length > 0) {
11987
12778
  await syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData);
11988
12779
  }
@@ -12163,10 +12954,10 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
12163
12954
  }
12164
12955
  }
12165
12956
  const trackedFiles = frameworkAdapter.driftTrackedFiles();
12166
- if (trackedFiles.length > 0 && existsSync12(agentDir)) {
12957
+ if (trackedFiles.length > 0 && existsSync13(agentDir)) {
12167
12958
  const hashes = /* @__PURE__ */ new Map();
12168
12959
  for (const file of trackedFiles) {
12169
- const h = hashFile(join21(agentDir, file));
12960
+ const h = hashFile(join22(agentDir, file));
12170
12961
  if (h) hashes.set(file, h);
12171
12962
  }
12172
12963
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -12181,7 +12972,7 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
12181
12972
  refreshData.agent.onboarding_state
12182
12973
  );
12183
12974
  const obStep = obState.step;
12184
- const markerPath = join21(homedir11(), ".augmented", agent.code_name, "onboarding-drive.json");
12975
+ const markerPath = join22(homedir11(), ".augmented", agent.code_name, "onboarding-drive.json");
12185
12976
  const marker = readOnboardingDriveMarker(markerPath);
12186
12977
  const decision = decideOnboardingDrive(obStep, marker, Date.now(), obState.generation ?? 0);
12187
12978
  if (decision.clearMarker) {
@@ -12246,10 +13037,12 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
12246
13037
  const codeName = agent.code_name;
12247
13038
  if (isOpencodeSessionHealthy(codeName)) {
12248
13039
  await ensureOpencodeSlackIngest({ codeName, agentId: agent.agent_id, log });
13040
+ await ensureOpencodeTelegramIngest({ codeName, agentId: agent.agent_id, log });
12249
13041
  return { decision: "healthy", spawnAttempted: false, sessionHealthyAfter: true };
12250
13042
  }
12251
13043
  stopOpencodeSlackIngest(codeName, log);
12252
- const opencodeProjectDir = join21(getFramework("opencode").getAgentDir(codeName), "provision");
13044
+ stopOpencodeTelegramIngest(codeName, log);
13045
+ const opencodeProjectDir = join22(getFramework("opencode").getAgentDir(codeName), "provision");
12253
13046
  const openRouterRaw = refreshData.openrouter ?? null;
12254
13047
  const serveEnv = {
12255
13048
  AGT_HOST: requireHost(),
@@ -12289,8 +13082,8 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
12289
13082
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
12290
13083
  const codeName = agent.code_name;
12291
13084
  const projectDir = getProjectDir(codeName);
12292
- const mcpConfigPath = join21(projectDir, ".mcp.json");
12293
- const claudeMdPath = join21(projectDir, "CLAUDE.md");
13085
+ const mcpConfigPath = join22(projectDir, ".mcp.json");
13086
+ const claudeMdPath = join22(projectDir, "CLAUDE.md");
12294
13087
  if (restartBreaker.isTripped(codeName)) {
12295
13088
  const trip = restartBreaker.getTrip(codeName);
12296
13089
  return {
@@ -12448,7 +13241,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
12448
13241
  const ctx = getLastFailureContext(codeName);
12449
13242
  const recovery = prepareForRespawn(codeName);
12450
13243
  const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
12451
- ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash12("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)`;
12452
13245
  const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
12453
13246
  const recoverySummary = recovery ? `; recovery=${recovery}` : "";
12454
13247
  log(
@@ -12462,7 +13255,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash12("sha256")
12462
13255
  );
12463
13256
  getHostId().then((hostId) => {
12464
13257
  if (!hostId) return;
12465
- const paneTailHash = zombie.paneTail ? `sha256:${createHash12("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;
12466
13259
  return api.post("/host/events", {
12467
13260
  host_id: hostId,
12468
13261
  agent_code_name: codeName,
@@ -12611,7 +13404,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash12("sha256")
12611
13404
  if (!claudeAuthTupleBySession.has(codeName)) {
12612
13405
  claudeAuthTupleBySession.set(codeName, currentAuthTuple);
12613
13406
  }
12614
- const stableTasksHash = createHash12("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
13407
+ const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
12615
13408
  const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
12616
13409
  if (stableTasksHash !== prevHash) {
12617
13410
  const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
@@ -12828,7 +13621,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
12828
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}`));
12829
13622
  void (async () => {
12830
13623
  try {
12831
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
13624
+ const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
12832
13625
  await api.post("/host/heartbeat", {
12833
13626
  host_id: hostId,
12834
13627
  agent_diagnostics: collectDiagnostics([codeName])
@@ -12878,7 +13671,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
12878
13671
  }
12879
13672
  try {
12880
13673
  const hostId = await getHostId();
12881
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
13674
+ const { collectDiagnostics } = await import("../persistent-session-IDWQO7QL.js");
12882
13675
  await api.post("/host/heartbeat", {
12883
13676
  host_id: hostId,
12884
13677
  agent_diagnostics: collectDiagnostics([codeName])
@@ -13164,8 +13957,8 @@ async function processDirectChatMessage(agent, msg) {
13164
13957
  if (useDoorbell) {
13165
13958
  try {
13166
13959
  const doorbell = directChatDoorbellPath(agent.agentId, homedir11());
13167
- mkdirSync8(dirname6(doorbell), { recursive: true });
13168
- writeFileSync9(doorbell, String(Date.now()));
13960
+ mkdirSync9(dirname6(doorbell), { recursive: true });
13961
+ writeFileSync10(doorbell, String(Date.now()));
13169
13962
  log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
13170
13963
  return;
13171
13964
  } catch (err) {
@@ -13274,8 +14067,8 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
13274
14067
  if (run_id) openInjectedRunByCode.set(codeName, run_id);
13275
14068
  try {
13276
14069
  const doorbell = directChatDoorbellPath(agentId, homedir11());
13277
- mkdirSync8(dirname6(doorbell), { recursive: true });
13278
- writeFileSync9(doorbell, String(Date.now()));
14070
+ mkdirSync9(dirname6(doorbell), { recursive: true });
14071
+ writeFileSync10(doorbell, String(Date.now()));
13279
14072
  } catch (err) {
13280
14073
  log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
13281
14074
  }
@@ -13376,7 +14169,7 @@ async function processClaudePairSessions(agents) {
13376
14169
  killPairSession,
13377
14170
  pairTmuxSession,
13378
14171
  finalizeClaudePairOnboarding
13379
- } = await import("../claude-pair-runtime-NUZWSILK.js");
14172
+ } = await import("../claude-pair-runtime-JU3CVFUU.js");
13380
14173
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
13381
14174
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
13382
14175
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -13550,8 +14343,15 @@ function generateArtifacts(agent, refreshData, adapter) {
13550
14343
  log(
13551
14344
  `[provision/active-tasks] agent=${agent.code_name} count=${activeTasksFromRefresh.length} estimated_tokens=${activeTasksTokens}`
13552
14345
  );
14346
+ const provisionAgent = (() => {
14347
+ const rawAgent = refreshData.agent;
14348
+ if (rawAgent?.["framework"] !== "opencode") return rawAgent;
14349
+ const hostOverride = process.env["AGT_OPENCODE_MODEL"]?.trim();
14350
+ const resolved = hostOverride || resolveModelChain(refreshData).primary || rawAgent?.["primary_model"];
14351
+ return resolved ? { ...rawAgent, primary_model: resolved } : rawAgent;
14352
+ })();
13553
14353
  const provisionInput = {
13554
- agent: refreshData.agent,
14354
+ agent: provisionAgent,
13555
14355
  charterFrontmatter,
13556
14356
  charterContent,
13557
14357
  toolsFrontmatter,
@@ -13619,8 +14419,8 @@ function parseMemoryFile(raw, fallbackName) {
13619
14419
  };
13620
14420
  }
13621
14421
  async function syncMemories(agent, configDir, log2) {
13622
- const projectDir = join21(configDir, agent.code_name, "project");
13623
- const memoryDir = join21(projectDir, "memory");
14422
+ const projectDir = join22(configDir, agent.code_name, "project");
14423
+ const memoryDir = join22(projectDir, "memory");
13624
14424
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
13625
14425
  if (isFreshSync) {
13626
14426
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -13631,15 +14431,15 @@ async function syncMemories(agent, configDir, log2) {
13631
14431
  }
13632
14432
  pendingFreshMemorySync.delete(agent.agent_id);
13633
14433
  }
13634
- if (existsSync12(memoryDir)) {
14434
+ if (existsSync13(memoryDir)) {
13635
14435
  const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
13636
14436
  const currentHashes = /* @__PURE__ */ new Map();
13637
14437
  const changedMemories = [];
13638
14438
  for (const file of readdirSync5(memoryDir)) {
13639
14439
  if (!file.endsWith(".md")) continue;
13640
14440
  try {
13641
- const raw = readFileSync18(join21(memoryDir, file), "utf-8");
13642
- const fileHash = createHash12("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);
13643
14443
  currentHashes.set(file, fileHash);
13644
14444
  if (prevHashes.get(file) === fileHash) continue;
13645
14445
  const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
@@ -13663,7 +14463,7 @@ async function syncMemories(agent, configDir, log2) {
13663
14463
  } catch (err) {
13664
14464
  for (const mem of changedMemories) {
13665
14465
  for (const [file] of currentHashes) {
13666
- 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$/, ""));
13667
14467
  if (parsed?.name === mem.name) currentHashes.delete(file);
13668
14468
  }
13669
14469
  }
@@ -13676,29 +14476,29 @@ async function syncMemories(agent, configDir, log2) {
13676
14476
  }
13677
14477
  }
13678
14478
  async function downloadMemories(agent, memoryDir, log2, { force }) {
13679
- const localFiles = existsSync12(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
13680
- const localListHash = createHash12("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);
13681
14481
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
13682
14482
  const prevDownload = lastDownloadHash.get(agent.agent_id);
13683
14483
  try {
13684
14484
  const dbMemories = await api.post("/host/memories", {
13685
14485
  agent_id: agent.agent_id
13686
14486
  });
13687
- const responseHash = createHash12("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);
13688
14488
  if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
13689
14489
  return true;
13690
14490
  }
13691
14491
  lastDownloadHash.set(agent.agent_id, responseHash);
13692
14492
  lastLocalFileHash.set(agent.agent_id, localListHash);
13693
14493
  if (dbMemories.memories?.length) {
13694
- mkdirSync8(memoryDir, { recursive: true });
14494
+ mkdirSync9(memoryDir, { recursive: true });
13695
14495
  let written = 0;
13696
14496
  let overwritten = 0;
13697
14497
  for (let i = 0; i < dbMemories.memories.length; i++) {
13698
14498
  const mem = dbMemories.memories[i];
13699
14499
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
13700
14500
  const slug = rawSlug || `memory-${i}`;
13701
- const filePath = join21(memoryDir, `${slug}.md`);
14501
+ const filePath = join22(memoryDir, `${slug}.md`);
13702
14502
  const desired = `---
13703
14503
  name: ${JSON.stringify(mem.name)}
13704
14504
  type: ${mem.type}
@@ -13707,23 +14507,23 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
13707
14507
 
13708
14508
  ${mem.content}
13709
14509
  `;
13710
- if (existsSync12(filePath)) {
14510
+ if (existsSync13(filePath)) {
13711
14511
  let existing = "";
13712
14512
  try {
13713
- existing = readFileSync18(filePath, "utf-8");
14513
+ existing = readFileSync19(filePath, "utf-8");
13714
14514
  } catch {
13715
14515
  }
13716
14516
  if (existing === desired) continue;
13717
- writeFileSync9(filePath, desired);
14517
+ writeFileSync10(filePath, desired);
13718
14518
  overwritten++;
13719
14519
  } else {
13720
- writeFileSync9(filePath, desired);
14520
+ writeFileSync10(filePath, desired);
13721
14521
  written++;
13722
14522
  }
13723
14523
  }
13724
14524
  if (written > 0 || overwritten > 0) {
13725
14525
  const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
13726
- lastLocalFileHash.set(agent.agent_id, createHash12("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));
13727
14527
  log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
13728
14528
  }
13729
14529
  }
@@ -13734,7 +14534,7 @@ ${mem.content}
13734
14534
  }
13735
14535
  }
13736
14536
  async function cleanupAgentFiles(codeName, agentDir) {
13737
- if (existsSync12(agentDir)) {
14537
+ if (existsSync13(agentDir)) {
13738
14538
  try {
13739
14539
  rmSync4(agentDir, { recursive: true, force: true });
13740
14540
  log(`Removed provision directory for '${codeName}'`);
@@ -13956,6 +14756,7 @@ async function stopPolling(opts = {}) {
13956
14756
  stopCaffeinate();
13957
14757
  stopRealtimeChat();
13958
14758
  stopAllOpencodeSlackIngests(log);
14759
+ stopAllOpencodeTelegramIngests(log);
13959
14760
  log(formatDrainShutdownLine({ step: "stop-subsystems", elapsedMs: Date.now() - subsysStartedAt }));
13960
14761
  for (const codeName of [...scheduledRunsByCode.keys()]) {
13961
14762
  closeScheduledRunsForCode(codeName, "cancelled", "manager shutdown");
@@ -13970,8 +14771,8 @@ function startManager(opts) {
13970
14771
  config = opts;
13971
14772
  try {
13972
14773
  const stateFile = getStateFile();
13973
- if (existsSync12(stateFile)) {
13974
- const raw = readFileSync18(stateFile, "utf-8");
14774
+ if (existsSync13(stateFile)) {
14775
+ const raw = readFileSync19(stateFile, "utf-8");
13975
14776
  const parsed = JSON.parse(raw);
13976
14777
  if (Array.isArray(parsed.agents)) {
13977
14778
  state6.agents = parsed.agents;
@@ -13998,7 +14799,7 @@ function startManager(opts) {
13998
14799
  log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
13999
14800
  }
14000
14801
  log(
14001
- `[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")}`
14002
14803
  );
14003
14804
  deployMcpAssets();
14004
14805
  reapOrphanChannelMcps({ log });
@@ -14019,7 +14820,7 @@ async function reapOrphanedClaudePids() {
14019
14820
  const looksLikeClaude = (pid) => {
14020
14821
  if (process.platform !== "linux") return true;
14021
14822
  try {
14022
- const comm = readFileSync18(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
14823
+ const comm = readFileSync19(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
14023
14824
  return comm.includes("claude");
14024
14825
  } catch {
14025
14826
  return false;
@@ -14116,14 +14917,14 @@ function restartRunningChannelMcps(basenames) {
14116
14917
  }
14117
14918
  }
14118
14919
  function deployMcpAssets() {
14119
- const targetDir = join21(homedir11(), ".augmented", "_mcp");
14120
- mkdirSync8(targetDir, { recursive: true });
14920
+ const targetDir = join22(homedir11(), ".augmented", "_mcp");
14921
+ mkdirSync9(targetDir, { recursive: true });
14121
14922
  const moduleDir = dirname6(fileURLToPath(import.meta.url));
14122
14923
  let mcpSourceDir = "";
14123
14924
  let dir = moduleDir;
14124
14925
  for (let i = 0; i < 6; i++) {
14125
- const candidate = join21(dir, "dist", "mcp");
14126
- if (existsSync12(join21(candidate, "index.js"))) {
14926
+ const candidate = join22(dir, "dist", "mcp");
14927
+ if (existsSync13(join22(candidate, "index.js"))) {
14127
14928
  mcpSourceDir = candidate;
14128
14929
  break;
14129
14930
  }
@@ -14138,8 +14939,8 @@ function deployMcpAssets() {
14138
14939
  const changedBasenames = [];
14139
14940
  const fileHash = (p) => {
14140
14941
  try {
14141
- if (!existsSync12(p)) return null;
14142
- return createHash12("sha256").update(readFileSync18(p)).digest("hex");
14942
+ if (!existsSync13(p)) return null;
14943
+ return createHash14("sha256").update(readFileSync19(p)).digest("hex");
14143
14944
  } catch {
14144
14945
  return null;
14145
14946
  }
@@ -14203,9 +15004,9 @@ function deployMcpAssets() {
14203
15004
  // needs restarting to pick up a token rotation.
14204
15005
  "xero.js"
14205
15006
  ]) {
14206
- const src = join21(mcpSourceDir, file);
14207
- const dst = join21(targetDir, file);
14208
- if (!existsSync12(src)) continue;
15007
+ const src = join22(mcpSourceDir, file);
15008
+ const dst = join22(targetDir, file);
15009
+ if (!existsSync13(src)) continue;
14209
15010
  const before = fileHash(dst);
14210
15011
  try {
14211
15012
  copyFileSync(src, dst);
@@ -14222,23 +15023,23 @@ function deployMcpAssets() {
14222
15023
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
14223
15024
  restartRunningChannelMcps(changedBasenames);
14224
15025
  }
14225
- const localMcpPath = join21(targetDir, "index.js");
15026
+ const localMcpPath = join22(targetDir, "index.js");
14226
15027
  try {
14227
- const agentsDir = join21(homedir11(), ".augmented", "agents");
14228
- if (existsSync12(agentsDir)) {
15028
+ const agentsDir = join22(homedir11(), ".augmented", "agents");
15029
+ if (existsSync13(agentsDir)) {
14229
15030
  for (const entry of readdirSync5(agentsDir, { withFileTypes: true })) {
14230
15031
  if (!entry.isDirectory()) continue;
14231
15032
  for (const subdir of ["provision", "project"]) {
14232
- const mcpJsonPath = join21(agentsDir, entry.name, subdir, ".mcp.json");
15033
+ const mcpJsonPath = join22(agentsDir, entry.name, subdir, ".mcp.json");
14233
15034
  try {
14234
- const raw = readFileSync18(mcpJsonPath, "utf-8");
15035
+ const raw = readFileSync19(mcpJsonPath, "utf-8");
14235
15036
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
14236
15037
  const mcpConfig = JSON.parse(raw);
14237
15038
  const augServer = mcpConfig.mcpServers?.["augmented"];
14238
15039
  if (!augServer) continue;
14239
15040
  augServer.command = "node";
14240
15041
  augServer.args = [localMcpPath];
14241
- writeFileSync9(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
15042
+ writeFileSync10(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
14242
15043
  log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
14243
15044
  } catch {
14244
15045
  }