@integrity-labs/agt-cli 0.28.343 → 0.28.345

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,7 +46,7 @@ import {
46
46
  resolveEffectivePinRaw,
47
47
  safeWriteJsonAtomic,
48
48
  setConfigHash
49
- } from "../chunk-GAYSFNTX.js";
49
+ } from "../chunk-GJ5TGARW.js";
50
50
  import {
51
51
  getProjectDir as getProjectDir2,
52
52
  getReadyTasks,
@@ -66,6 +66,7 @@ import {
66
66
  SCHEDULED_TURN_MARKER_FILENAME,
67
67
  SUPPRESS_SENTINEL,
68
68
  StreamEncoder,
69
+ USAGE_LIMIT_MARKER_FILENAME,
69
70
  appendDmFooter,
70
71
  attributeTranscriptUsageByRun,
71
72
  buildEgressAllowlist,
@@ -125,6 +126,7 @@ import {
125
126
  restartEgressSidecar,
126
127
  rotateSessionForWedge,
127
128
  sendToAgent,
129
+ serializeUsageLimitMarker,
128
130
  sessionTranscriptDir,
129
131
  startPersistentSession,
130
132
  stopAllSessionsAndWait,
@@ -135,17 +137,17 @@ import {
135
137
  takeZombieDetection,
136
138
  transcriptActivityAgeSeconds,
137
139
  writeEgressAllowlist
138
- } from "../chunk-7OBFGSQS.js";
140
+ } from "../chunk-O5IINJDB.js";
139
141
  import {
140
142
  reapOrphanChannelMcps
141
143
  } from "../chunk-XWVM4KPK.js";
142
144
 
143
145
  // src/lib/manager-worker.ts
144
- import { createHash as createHash13 } from "crypto";
145
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, existsSync as existsSync12, rmSync as rmSync4, readdirSync as readdirSync5, statSync as statSync4, copyFileSync } from "fs";
146
+ import { createHash as createHash14 } from "crypto";
147
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, rmSync as rmSync5, readdirSync as readdirSync5, statSync as statSync4, copyFileSync } from "fs";
146
148
  import { execFileSync as syncExecFile } from "child_process";
147
- import { join as join21, dirname as dirname6 } from "path";
148
- import { homedir as homedir11 } from "os";
149
+ import { join as join23, dirname as dirname6 } from "path";
150
+ import { homedir as homedir12 } from "os";
149
151
  import { fileURLToPath } from "url";
150
152
 
151
153
  // src/lib/claude-code-upgrade-throttle.ts
@@ -1860,6 +1862,22 @@ var nodeArtifactFs = {
1860
1862
  };
1861
1863
 
1862
1864
  // src/lib/usage-banner-monitor.ts
1865
+ import { mkdirSync as mkdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
1866
+ import { homedir as homedir3 } from "os";
1867
+ import { join as join5 } from "path";
1868
+ function syncUsageLimitMarker(args) {
1869
+ const path = join5(homedir3(), ".augmented", args.codeName, USAGE_LIMIT_MARKER_FILENAME);
1870
+ try {
1871
+ if (args.pct >= 100) {
1872
+ mkdirSync4(join5(homedir3(), ".augmented", args.codeName), { recursive: true });
1873
+ writeFileSync4(path, serializeUsageLimitMarker(args.weekResetsAt), "utf-8");
1874
+ } else {
1875
+ rmSync2(path, { force: true });
1876
+ }
1877
+ } catch (err) {
1878
+ args.log(`[usage-banner] usage-limit marker sync failed for '${args.codeName}': ${err.message}`);
1879
+ }
1880
+ }
1863
1881
  var MIN_CHECK_INTERVAL_MS = 6e4;
1864
1882
  var PANE_TAIL_LINES_FOR_BANNER = 5e3;
1865
1883
  var state = /* @__PURE__ */ new Map();
@@ -1892,6 +1910,12 @@ async function maybeReportUsageBanner(args) {
1892
1910
  state.set(codeName, next);
1893
1911
  return;
1894
1912
  }
1913
+ syncUsageLimitMarker({
1914
+ codeName,
1915
+ pct: observation.pct,
1916
+ weekResetsAt: observation.weekResetsAt,
1917
+ log: log2
1918
+ });
1895
1919
  const observedAtIso = next.lastWeekResetsAt;
1896
1920
  const weekResetsAtIso = observation.weekResetsAt.toISOString();
1897
1921
  if (existing && existing.lastPct === observation.pct && observedAtIso === weekResetsAtIso) {
@@ -1916,7 +1940,7 @@ async function maybeReportUsageBanner(args) {
1916
1940
 
1917
1941
  // src/lib/token-usage-monitor.ts
1918
1942
  import { readdirSync, readFileSync as readFileSync6, statSync } from "fs";
1919
- import { join as join5 } from "path";
1943
+ import { join as join6 } from "path";
1920
1944
  var MIN_CHECK_INTERVAL_MS2 = 6e4;
1921
1945
  var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
1922
1946
  var MAX_ENTRIES_PER_POST = 200;
@@ -1945,7 +1969,7 @@ async function maybeReportTokenUsage(args) {
1945
1969
  if (!name.endsWith(".jsonl")) continue;
1946
1970
  const sessionId = name.slice(0, -".jsonl".length);
1947
1971
  if (!sessionId) continue;
1948
- const path = join5(dir, name);
1972
+ const path = join6(dir, name);
1949
1973
  let st;
1950
1974
  try {
1951
1975
  st = statSync(path);
@@ -2043,7 +2067,7 @@ async function maybeReportTokenUsage(args) {
2043
2067
 
2044
2068
  // src/lib/workflow-run-reconciler.ts
2045
2069
  import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
2046
- import { join as join6 } from "path";
2070
+ import { join as join7 } from "path";
2047
2071
  var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
2048
2072
  var SETTLE_MS = 3e4;
2049
2073
  var TRANSCRIPT_MTIME_WINDOW_MS2 = 2 * 24 * 60 * 60 * 1e3;
@@ -2062,7 +2086,7 @@ function collectJsonlRecursive(dir, minMtimeMs, out, depth) {
2062
2086
  return;
2063
2087
  }
2064
2088
  for (const name of entries) {
2065
- const p = join6(dir, name);
2089
+ const p = join7(dir, name);
2066
2090
  let st;
2067
2091
  try {
2068
2092
  st = statSync2(p);
@@ -2085,7 +2109,7 @@ function enumerateTranscriptFiles(transcriptDir, nowMs, minMtimeMs = nowMs - TRA
2085
2109
  return out;
2086
2110
  }
2087
2111
  for (const name of entries) {
2088
- const path = join6(transcriptDir, name);
2112
+ const path = join7(transcriptDir, name);
2089
2113
  let st;
2090
2114
  try {
2091
2115
  st = statSync2(path);
@@ -2097,7 +2121,7 @@ function enumerateTranscriptFiles(transcriptDir, nowMs, minMtimeMs = nowMs - TRA
2097
2121
  continue;
2098
2122
  }
2099
2123
  if (st.isDirectory()) {
2100
- collectJsonlRecursive(join6(path, "subagents"), minMtimeMs, out, 0);
2124
+ collectJsonlRecursive(join7(path, "subagents"), minMtimeMs, out, 0);
2101
2125
  }
2102
2126
  }
2103
2127
  return out;
@@ -2188,7 +2212,7 @@ async function maybeReconcileWorkflowRunTokens(args) {
2188
2212
 
2189
2213
  // src/lib/conversation-evaluator.ts
2190
2214
  import { readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
2191
- import { join as join7 } from "path";
2215
+ import { join as join8 } from "path";
2192
2216
  var MIN_CHECK_INTERVAL_MS4 = 5 * 6e4;
2193
2217
  var TRANSCRIPT_MTIME_WINDOW_MS3 = 7 * 24 * 60 * 60 * 1e3;
2194
2218
  var WINDOW_PAD_MS = 5 * 6e4;
@@ -2624,7 +2648,7 @@ function readRecentTurns(dir, nowMs) {
2624
2648
  return;
2625
2649
  }
2626
2650
  for (const ent of entries) {
2627
- const full = join7(d, ent.name);
2651
+ const full = join8(d, ent.name);
2628
2652
  if (ent.isDirectory()) {
2629
2653
  visit(full);
2630
2654
  continue;
@@ -2930,10 +2954,10 @@ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
2930
2954
 
2931
2955
  // src/lib/activity-cache-monitor.ts
2932
2956
  import { existsSync as existsSync2, readFileSync as readFileSync9 } from "fs";
2933
- import { homedir as homedir3 } from "os";
2934
- import { join as join8 } from "path";
2957
+ import { homedir as homedir4 } from "os";
2958
+ import { join as join9 } from "path";
2935
2959
  var MIN_CHECK_INTERVAL_MS6 = 6e4;
2936
- var STATS_CACHE_PATH = join8(homedir3(), ".claude", "stats-cache.json");
2960
+ var STATS_CACHE_PATH = join9(homedir4(), ".claude", "stats-cache.json");
2937
2961
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
2938
2962
  var state5 = { lastObservedDate: null, lastCheckedAt: 0 };
2939
2963
  function selectNewDailyRows(raw, lastObservedDate) {
@@ -3167,8 +3191,8 @@ function bundleFingerprint(files) {
3167
3191
 
3168
3192
  // src/lib/claude-auth-detect.ts
3169
3193
  import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
3170
- import { homedir as homedir4, platform } from "os";
3171
- import { join as join9 } from "path";
3194
+ import { homedir as homedir5, platform } from "os";
3195
+ import { join as join10 } from "path";
3172
3196
  import { execFile } from "child_process";
3173
3197
  import { promisify } from "util";
3174
3198
  var execFileAsync = promisify(execFile);
@@ -3183,8 +3207,8 @@ async function detectClaudeAuth() {
3183
3207
  }
3184
3208
  async function findClaudeCredentialsPaths() {
3185
3209
  const candidates = [
3186
- join9(homedir4(), ".claude", ".credentials.json"),
3187
- join9(homedir4(), ".claude", "credentials.json")
3210
+ join10(homedir5(), ".claude", ".credentials.json"),
3211
+ join10(homedir5(), ".claude", "credentials.json")
3188
3212
  ];
3189
3213
  const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
3190
3214
  if (isLinuxRoot) {
@@ -3192,8 +3216,8 @@ async function findClaudeCredentialsPaths() {
3192
3216
  const entries = await readdir2("/home", { withFileTypes: true });
3193
3217
  for (const entry of entries) {
3194
3218
  if (!entry.isDirectory()) continue;
3195
- candidates.push(join9("/home", entry.name, ".claude", ".credentials.json"));
3196
- candidates.push(join9("/home", entry.name, ".claude", "credentials.json"));
3219
+ candidates.push(join10("/home", entry.name, ".claude", ".credentials.json"));
3220
+ candidates.push(join10("/home", entry.name, ".claude", "credentials.json"));
3197
3221
  }
3198
3222
  } catch {
3199
3223
  }
@@ -3289,11 +3313,11 @@ function computeChannelConfigHash(input) {
3289
3313
  }
3290
3314
 
3291
3315
  // src/lib/channel-hash-cache.ts
3292
- import { existsSync as existsSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
3293
- import { join as join10 } from "path";
3316
+ import { existsSync as existsSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
3317
+ import { join as join11 } from "path";
3294
3318
  var CACHE_FILENAME = "channel-hash-cache.json";
3295
3319
  function getChannelHashCacheFile(configDir) {
3296
- return join10(configDir, CACHE_FILENAME);
3320
+ return join11(configDir, CACHE_FILENAME);
3297
3321
  }
3298
3322
  function loadChannelHashCache(target, configDir) {
3299
3323
  const path = getChannelHashCacheFile(configDir);
@@ -3314,14 +3338,14 @@ function saveChannelHashCache(source, configDir) {
3314
3338
  const obj = {};
3315
3339
  for (const [key, value] of source) obj[key] = value;
3316
3340
  try {
3317
- writeFileSync4(path, JSON.stringify(obj, null, 2));
3341
+ writeFileSync5(path, JSON.stringify(obj, null, 2));
3318
3342
  } catch {
3319
3343
  }
3320
3344
  }
3321
3345
 
3322
3346
  // src/lib/sender-policy-baseline.ts
3323
3347
  import { existsSync as existsSync4, readFileSync as readFileSync11 } from "fs";
3324
- import { join as join11 } from "path";
3348
+ import { join as join12 } from "path";
3325
3349
  var BASELINE_FILENAME = "sender-policy-baseline.json";
3326
3350
  var SENDER_POLICY_BASELINE_VERSION = 1;
3327
3351
  var BASELINE_CONCERNS = ["senderPolicy", "slackBehaviour", "msteamsBehaviour"];
@@ -3333,7 +3357,7 @@ function createDeliveryBaselineMaps() {
3333
3357
  };
3334
3358
  }
3335
3359
  function getSenderPolicyBaselineFile(configDir) {
3336
- return join11(configDir, BASELINE_FILENAME);
3360
+ return join12(configDir, BASELINE_FILENAME);
3337
3361
  }
3338
3362
  function loadSenderPolicyBaseline(target, configDir, log2) {
3339
3363
  const path = getSenderPolicyBaselineFile(configDir);
@@ -3827,11 +3851,11 @@ function planGlobalSkillSync(globalSkills, prevIds, hashOf, knownHash, options)
3827
3851
  }
3828
3852
 
3829
3853
  // src/lib/manager/managed-skill-manifest.ts
3830
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync12, writeFileSync as writeFileSync5 } from "fs";
3831
- import { dirname as dirname4, join as join12 } from "path";
3854
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
3855
+ import { dirname as dirname4, join as join13 } from "path";
3832
3856
  var MANIFEST_VERSION = 1;
3833
3857
  function managedSkillManifestPath(agentRootDir) {
3834
- return join12(agentRootDir, "managed-skills.json");
3858
+ return join13(agentRootDir, "managed-skills.json");
3835
3859
  }
3836
3860
  function readManagedSkillManifest(path) {
3837
3861
  try {
@@ -3845,21 +3869,21 @@ function readManagedSkillManifest(path) {
3845
3869
  }
3846
3870
  function writeManagedSkillManifest(path, ids) {
3847
3871
  try {
3848
- mkdirSync4(dirname4(path), { recursive: true });
3872
+ mkdirSync5(dirname4(path), { recursive: true });
3849
3873
  const body = {
3850
3874
  version: MANIFEST_VERSION,
3851
3875
  globalSkillIds: [...ids].sort()
3852
3876
  };
3853
- writeFileSync5(path, JSON.stringify(body, null, 2));
3877
+ writeFileSync6(path, JSON.stringify(body, null, 2));
3854
3878
  } catch {
3855
3879
  }
3856
3880
  }
3857
3881
 
3858
3882
  // src/lib/manager/runtime.ts
3859
3883
  import { createHash as createHash6 } from "crypto";
3860
- import { readFileSync as readFileSync13, appendFileSync, mkdirSync as mkdirSync5, chmodSync, existsSync as existsSync6 } from "fs";
3861
- import { join as join13, dirname as dirname5 } from "path";
3862
- import { homedir as homedir5 } from "os";
3884
+ import { readFileSync as readFileSync13, appendFileSync, mkdirSync as mkdirSync6, chmodSync, existsSync as existsSync6 } from "fs";
3885
+ import { join as join14, dirname as dirname5 } from "path";
3886
+ import { homedir as homedir6 } from "os";
3863
3887
  function redactForDiskLog(value) {
3864
3888
  try {
3865
3889
  return value.replace(/\b(Bearer\s+)[A-Za-z0-9._-]+\b/gi, "$1[REDACTED]").replace(/\bxox[baprs]-[A-Za-z0-9-]+\b/g, "[REDACTED-SLACK]").replace(/\btlk_[A-Za-z0-9._-]+\b/g, "[REDACTED-HOST]").replace(/\bsk-ant-[A-Za-z0-9_-]+\b/g, "[REDACTED-ANTHROPIC]").replace(/\b\d{8,12}:[A-Za-z0-9_-]{30,}\b/g, "[REDACTED-TELEGRAM]").replace(
@@ -3879,8 +3903,8 @@ function log(msg) {
3879
3903
  `;
3880
3904
  if (!managerLogPath) {
3881
3905
  try {
3882
- managerLogPath = join13(homedir5(), ".augmented", "manager.log");
3883
- mkdirSync5(dirname5(managerLogPath), { recursive: true });
3906
+ managerLogPath = join14(homedir6(), ".augmented", "manager.log");
3907
+ mkdirSync6(dirname5(managerLogPath), { recursive: true });
3884
3908
  if (existsSync6(managerLogPath)) {
3885
3909
  chmodSync(managerLogPath, 384);
3886
3910
  }
@@ -4067,9 +4091,9 @@ function resolveModelChain(refreshData) {
4067
4091
  }
4068
4092
 
4069
4093
  // src/lib/manager/claude-auth.ts
4070
- import { existsSync as existsSync7, rmSync as rmSync2 } from "fs";
4071
- import { join as join14 } from "path";
4072
- import { homedir as homedir6 } from "os";
4094
+ import { existsSync as existsSync7, rmSync as rmSync3 } from "fs";
4095
+ import { join as join15 } from "path";
4096
+ import { homedir as homedir7 } from "os";
4073
4097
  async function applyClaudeAuthToEnv(childEnv, label) {
4074
4098
  const apiKey = getApiKey();
4075
4099
  if (!apiKey) {
@@ -4081,12 +4105,12 @@ async function applyClaudeAuthToEnv(childEnv, label) {
4081
4105
  throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
4082
4106
  }
4083
4107
  childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
4084
- const claudeDir = join14(homedir6(), ".claude");
4108
+ const claudeDir = join15(homedir7(), ".claude");
4085
4109
  for (const filename of [".credentials.json", "credentials.json"]) {
4086
- const p = join14(claudeDir, filename);
4110
+ const p = join15(claudeDir, filename);
4087
4111
  if (existsSync7(p)) {
4088
4112
  try {
4089
- rmSync2(p, { force: true });
4113
+ rmSync3(p, { force: true });
4090
4114
  log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
4091
4115
  } catch {
4092
4116
  }
@@ -4099,7 +4123,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
4099
4123
 
4100
4124
  // src/lib/manager/kanban/parsers.ts
4101
4125
  import { existsSync as existsSync8, readFileSync as readFileSync14 } from "fs";
4102
- import { join as join15 } from "path";
4126
+ import { join as join16 } from "path";
4103
4127
  var STANDUP_TEMPLATES = /* @__PURE__ */ new Set(["daily-standup", "end-of-day-summary"]);
4104
4128
  var TASK_UPDATE_TEMPLATES = /* @__PURE__ */ new Set(["hourly-status", "task-update"]);
4105
4129
  var PLAN_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan"]);
@@ -4235,8 +4259,8 @@ function getBuiltInSkillContent(skillId) {
4235
4259
  if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
4236
4260
  try {
4237
4261
  const candidates = [
4238
- join15(process.cwd(), "skills", skillId, "SKILL.md"),
4239
- join15(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "..", "..", "skills", skillId, "SKILL.md")
4262
+ join16(process.cwd(), "skills", skillId, "SKILL.md"),
4263
+ join16(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "..", "..", "skills", skillId, "SKILL.md")
4240
4264
  ];
4241
4265
  for (const candidate of candidates) {
4242
4266
  if (existsSync8(candidate)) {
@@ -4381,12 +4405,12 @@ function formatBoardForPrompt(items, template) {
4381
4405
  }
4382
4406
 
4383
4407
  // src/lib/manager/kanban/nudge-state-cache.ts
4384
- import { existsSync as existsSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
4385
- import { join as join16 } from "path";
4408
+ import { existsSync as existsSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
4409
+ import { join as join17 } from "path";
4386
4410
  var CACHE_FILENAME2 = "kanban-nudge-state.json";
4387
4411
  var KANBAN_NUDGE_STATE_VERSION = 1;
4388
4412
  function getKanbanNudgeStateFile(configDir) {
4389
- return join16(configDir, CACHE_FILENAME2);
4413
+ return join17(configDir, CACHE_FILENAME2);
4390
4414
  }
4391
4415
  function loadKanbanNudgeState(target, configDir) {
4392
4416
  const path = getKanbanNudgeStateFile(configDir);
@@ -4418,7 +4442,7 @@ function saveKanbanNudgeState(source, configDir) {
4418
4442
  const agents = {};
4419
4443
  for (const [codeName, state7] of source) agents[codeName] = state7;
4420
4444
  try {
4421
- writeFileSync6(path, JSON.stringify({ version: KANBAN_NUDGE_STATE_VERSION, agents }, null, 2));
4445
+ writeFileSync7(path, JSON.stringify({ version: KANBAN_NUDGE_STATE_VERSION, agents }, null, 2));
4422
4446
  } catch {
4423
4447
  }
4424
4448
  }
@@ -4959,9 +4983,9 @@ function closeScheduledRunsForCode(codeName, outcome, reason) {
4959
4983
 
4960
4984
  // src/lib/manager/scheduler/kanban-route.ts
4961
4985
  import { createHash as createHash9 } from "crypto";
4962
- import { writeFileSync as writeFileSync7, renameSync } from "fs";
4963
- import { homedir as homedir7 } from "os";
4964
- import { join as join17 } from "path";
4986
+ import { writeFileSync as writeFileSync8, renameSync } from "fs";
4987
+ import { homedir as homedir8 } from "os";
4988
+ import { join as join18 } from "path";
4965
4989
 
4966
4990
  // src/lib/manager/delivery/scheduled-output.ts
4967
4991
  async function deliverScheduledTaskOutput(agentCodeName, agentId, rawTarget, body, taskId) {
@@ -5265,10 +5289,10 @@ function resolveScheduledSlackTarget(task) {
5265
5289
  }
5266
5290
  function stampScheduledTurnMarker(codeName, taskId, target) {
5267
5291
  try {
5268
- const file = join17(homedir7(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
5292
+ const file = join18(homedir8(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
5269
5293
  const marker = { ts: Date.now(), task_id: taskId, ...target ? { target } : {} };
5270
5294
  const tmp = `${file}.tmp`;
5271
- writeFileSync7(tmp, JSON.stringify(marker), "utf8");
5295
+ writeFileSync8(tmp, JSON.stringify(marker), "utf8");
5272
5296
  renameSync(tmp, file);
5273
5297
  } catch (err) {
5274
5298
  log(`[scheduled-kanban] scheduled-turn marker write failed for '${codeName}': ${err.message}`);
@@ -5451,10 +5475,10 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
5451
5475
 
5452
5476
  // src/lib/manager/scheduler/execution.ts
5453
5477
  import { createHash as createHash10 } from "crypto";
5454
- import { homedir as homedir8 } from "os";
5455
- import { join as join18 } from "path";
5478
+ import { homedir as homedir9 } from "os";
5479
+ import { join as join19 } from "path";
5456
5480
  function claudePidFilePath() {
5457
- return join18(homedir8(), ".augmented", "manager-claude-pids.json");
5481
+ return join19(homedir9(), ".augmented", "manager-claude-pids.json");
5458
5482
  }
5459
5483
  var inFlightClaudePids = /* @__PURE__ */ new Map();
5460
5484
  function registerClaudeSpawn(record) {
@@ -5518,9 +5542,9 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
5518
5542
  import { spawn, execSync } from "child_process";
5519
5543
  import { createServer } from "net";
5520
5544
  import { randomBytes } from "crypto";
5521
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync16 } from "fs";
5522
- import { homedir as homedir9, userInfo } from "os";
5523
- import { join as join19 } from "path";
5545
+ import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "fs";
5546
+ import { homedir as homedir10, userInfo } from "os";
5547
+ import { join as join20 } from "path";
5524
5548
  var OPENCODE_BIN = process.env["AGT_OPENCODE_BIN"]?.trim() || "opencode";
5525
5549
  var sessions = /* @__PURE__ */ new Map();
5526
5550
  var bridges = /* @__PURE__ */ new Map();
@@ -5528,7 +5552,7 @@ function opencodeTmuxSession(codeName) {
5528
5552
  return `agt-oc-${codeName}`;
5529
5553
  }
5530
5554
  function opencodePaneLogPath(codeName) {
5531
- return join19(homedir9(), ".augmented", codeName, "opencode-serve.log");
5555
+ return join20(homedir10(), ".augmented", codeName, "opencode-serve.log");
5532
5556
  }
5533
5557
  function baseUrlFor(port) {
5534
5558
  return `http://127.0.0.1:${port}`;
@@ -5595,7 +5619,7 @@ async function startOpencodeSession(config2) {
5595
5619
  }
5596
5620
  async function spawnServe(config2, session) {
5597
5621
  const { codeName, projectDir, log: log2 } = config2;
5598
- if (!existsSync10(join19(projectDir, "opencode.json"))) {
5622
+ if (!existsSync10(join20(projectDir, "opencode.json"))) {
5599
5623
  log2(`[opencode-session] warning: no opencode.json in ${projectDir} for '${codeName}' (provisioning may not have run)`);
5600
5624
  }
5601
5625
  const tmuxSession = opencodeTmuxSession(codeName);
@@ -5605,12 +5629,13 @@ async function spawnServe(config2, session) {
5605
5629
  execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
5606
5630
  } catch {
5607
5631
  }
5608
- mkdirSync6(join19(homedir9(), ".augmented", codeName), { recursive: true });
5632
+ mkdirSync7(join20(homedir10(), ".augmented", codeName), { recursive: true });
5609
5633
  const serveEnv = {
5610
5634
  ...process.env,
5635
+ ...readAgentProviderEnv(codeName),
5611
5636
  ...stripUndefined(config2.serveEnv ?? {}),
5612
5637
  OPENCODE_SERVER_PASSWORD: password,
5613
- HOME: process.env.HOME?.trim() || homedir9(),
5638
+ HOME: process.env.HOME?.trim() || homedir10(),
5614
5639
  USER: process.env.USER?.trim() || userInfo().username
5615
5640
  };
5616
5641
  if (config2.runId) serveEnv["AGT_RUN_ID"] = config2.runId;
@@ -5694,6 +5719,29 @@ function stripUndefined(env) {
5694
5719
  for (const [k, v] of Object.entries(env)) if (v !== void 0) out[k] = v;
5695
5720
  return out;
5696
5721
  }
5722
+ var PROVIDER_KEY_RE = /^[A-Z][A-Z0-9_]*_API_KEY$/;
5723
+ function readAgentProviderEnv(codeName, dir) {
5724
+ const file = join20(dir ?? join20(homedir10(), ".augmented", codeName), ".env");
5725
+ const out = {};
5726
+ try {
5727
+ if (!existsSync10(file)) return out;
5728
+ for (const raw of readFileSync16(file, "utf-8").split("\n")) {
5729
+ const line = raw.trim();
5730
+ if (!line || line.startsWith("#")) continue;
5731
+ const eq = line.indexOf("=");
5732
+ if (eq <= 0) continue;
5733
+ const key = line.slice(0, eq).trim();
5734
+ if (!PROVIDER_KEY_RE.test(key)) continue;
5735
+ let val = line.slice(eq + 1).trim();
5736
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
5737
+ val = val.slice(1, -1);
5738
+ }
5739
+ out[key] = val;
5740
+ }
5741
+ } catch {
5742
+ }
5743
+ return out;
5744
+ }
5697
5745
 
5698
5746
  // src/lib/opencode-slack-ingest.ts
5699
5747
  import { createHash as createHash11 } from "crypto";
@@ -6804,6 +6852,677 @@ async function fireOpencodeScheduledTask(agent, task) {
6804
6852
  }
6805
6853
  }
6806
6854
 
6855
+ // src/lib/opencode-telegram-ingest.ts
6856
+ import { createHash as createHash13 } from "crypto";
6857
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync17, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
6858
+ import { randomUUID } from "crypto";
6859
+ import { join as join21 } from "path";
6860
+
6861
+ // src/lib/telegram-ingest.ts
6862
+ import https2 from "https";
6863
+
6864
+ // ../../packages/core/dist/channels/governance/telegram-peer-classifier.js
6865
+ function classifyPeerMessage2(msg, cfg, self) {
6866
+ if (!msg.from?.is_bot)
6867
+ return { kind: "human" };
6868
+ if (self.bot_id !== null && msg.from.id === self.bot_id) {
6869
+ return { kind: "self" };
6870
+ }
6871
+ if (cfg.peer_disabled_mode === "all") {
6872
+ return { kind: "drop", reason: "peer_disabled_all" };
6873
+ }
6874
+ if (cfg.peer_agent_mode === "off") {
6875
+ return { kind: "drop", reason: "mode_off" };
6876
+ }
6877
+ const chatId = String(msg.chat.id);
6878
+ if (cfg.peer_group_ids.length === 0) {
6879
+ if (msg.chat.type !== "private") {
6880
+ return { kind: "drop", reason: "chat_not_allowed" };
6881
+ }
6882
+ } else if (!cfg.peer_group_ids.includes(chatId)) {
6883
+ return { kind: "drop", reason: "chat_not_allowed" };
6884
+ }
6885
+ const peer = cfg.peers.find((p) => p.bot_id === msg.from.id);
6886
+ if (!peer) {
6887
+ return { kind: "drop", reason: "unknown_peer" };
6888
+ }
6889
+ if (peer.gate_path === null) {
6890
+ return { kind: "drop", reason: "cross_team_grant_missing" };
6891
+ }
6892
+ if (cfg.peer_disabled_mode === "cross_team_only" && peer.gate_path !== void 0 && peer.gate_path !== "same_team") {
6893
+ return { kind: "drop", reason: "peer_disabled_cross_team" };
6894
+ }
6895
+ if (self.bot_id === null || self.bot_username === null) {
6896
+ return { kind: "drop", reason: "self_resolution_pending" };
6897
+ }
6898
+ const addressing = classifyAddressing2(msg, self.bot_id, self.bot_username);
6899
+ if (!addressing.addressed) {
6900
+ return { kind: "drop", reason: "not_addressed" };
6901
+ }
6902
+ if (addressing.viaReplyOnly) {
6903
+ const inAllowedGroup = cfg.peer_group_ids.length > 0 && cfg.peer_group_ids.includes(chatId);
6904
+ const respondMode = cfg.peer_agent_mode === "respond";
6905
+ if (!(inAllowedGroup && respondMode)) {
6906
+ return { kind: "drop", reason: "not_addressed" };
6907
+ }
6908
+ }
6909
+ return { kind: "peer-ingress", peer };
6910
+ }
6911
+ function classifyAddressing2(msg, ourBotId, ourBotUsername) {
6912
+ let viaMentionOrCommand = false;
6913
+ const text = msg.text ?? msg.caption ?? "";
6914
+ const entities = msg.entities ?? msg.caption_entities ?? [];
6915
+ const usAt = "@" + ourBotUsername;
6916
+ for (const entity of entities) {
6917
+ const slice = text.slice(entity.offset, entity.offset + entity.length).toLowerCase();
6918
+ if (entity.type === "bot_command") {
6919
+ const at = slice.indexOf("@");
6920
+ if (at < 0 || slice.slice(at + 1) === ourBotUsername) {
6921
+ viaMentionOrCommand = true;
6922
+ break;
6923
+ }
6924
+ } else if (entity.type === "mention" && slice === usAt) {
6925
+ viaMentionOrCommand = true;
6926
+ break;
6927
+ }
6928
+ }
6929
+ const viaReply = msg.reply_to_message?.from?.id === ourBotId;
6930
+ if (viaMentionOrCommand)
6931
+ return { addressed: true, viaReplyOnly: false };
6932
+ if (viaReply)
6933
+ return { addressed: true, viaReplyOnly: true };
6934
+ return { addressed: false, viaReplyOnly: false };
6935
+ }
6936
+ var CODE_NAME_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
6937
+ function parsePeersEnv2(raw, gateRaw) {
6938
+ if (!raw || raw.trim().length === 0)
6939
+ return [];
6940
+ let parsed;
6941
+ try {
6942
+ parsed = JSON.parse(raw);
6943
+ } catch {
6944
+ return [];
6945
+ }
6946
+ if (!Array.isArray(parsed))
6947
+ return [];
6948
+ const gateMap = /* @__PURE__ */ new Map();
6949
+ let gateConfigInvalid = false;
6950
+ const gatePresent = !!(gateRaw && gateRaw.trim().length > 0);
6951
+ if (gatePresent) {
6952
+ try {
6953
+ const gateParsed = JSON.parse(gateRaw);
6954
+ if (!gateParsed || typeof gateParsed !== "object" || Array.isArray(gateParsed)) {
6955
+ gateConfigInvalid = true;
6956
+ } else {
6957
+ for (const [k, v] of Object.entries(gateParsed)) {
6958
+ if (v === null) {
6959
+ gateMap.set(k, null);
6960
+ } else if (typeof v === "string") {
6961
+ const isGrantPath = v.startsWith("grant:") && v.slice("grant:".length).trim().length > 0;
6962
+ if (v === "same_team" || v === "intra_org_unrestricted" || isGrantPath) {
6963
+ gateMap.set(k, v);
6964
+ } else {
6965
+ gateConfigInvalid = true;
6966
+ break;
6967
+ }
6968
+ } else {
6969
+ gateConfigInvalid = true;
6970
+ break;
6971
+ }
6972
+ }
6973
+ }
6974
+ } catch {
6975
+ gateConfigInvalid = true;
6976
+ }
6977
+ if (gateConfigInvalid) {
6978
+ console.error("[telegram-peer-classifier] TELEGRAM_PEERS_GATE is present but malformed; failing closed (every peer drops as cross_team_grant_missing)");
6979
+ }
6980
+ }
6981
+ const out = [];
6982
+ for (const entry of parsed) {
6983
+ 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) {
6984
+ const e = entry;
6985
+ const peer = { code_name: e.code_name, bot_id: e.bot_id, agent_id: e.agent_id };
6986
+ if (gateConfigInvalid) {
6987
+ peer.gate_path = null;
6988
+ } else {
6989
+ const key = String(e.bot_id);
6990
+ if (gateMap.has(key)) {
6991
+ peer.gate_path = gateMap.get(key) ?? null;
6992
+ } else if (gatePresent) {
6993
+ peer.gate_path = null;
6994
+ }
6995
+ }
6996
+ out.push(peer);
6997
+ }
6998
+ }
6999
+ return out;
7000
+ }
7001
+ function parsePeerGroupIdsEnv2(raw) {
7002
+ if (!raw)
7003
+ return [];
7004
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
7005
+ }
7006
+ function parsePeerAgentModeEnv2(raw) {
7007
+ if (raw === "listen" || raw === "respond")
7008
+ return raw;
7009
+ return "off";
7010
+ }
7011
+
7012
+ // ../../packages/core/dist/channels/governance/telegram-sender-gate.js
7013
+ function evaluateTelegramInboundGate(evt, deps) {
7014
+ const isBot = Boolean(evt.from?.is_bot);
7015
+ const text = (evt.text ?? evt.caption ?? "").trim();
7016
+ const chatId = String(evt.chat.id);
7017
+ const content = text.length > 0 ? { forward: true } : { forward: false, reason: "empty" };
7018
+ let peerLeaf;
7019
+ let isSelf = false;
7020
+ if (isBot) {
7021
+ const classifierMsg = {
7022
+ message_id: evt.message_id,
7023
+ text: evt.text,
7024
+ caption: evt.caption,
7025
+ from: evt.from ? { id: evt.from.id, username: evt.from.username, is_bot: evt.from.is_bot } : void 0,
7026
+ chat: { id: evt.chat.id, type: evt.chat.type },
7027
+ reply_to_message: evt.reply_to_message ? {
7028
+ message_id: evt.reply_to_message.message_id,
7029
+ from: evt.reply_to_message.from ? { id: evt.reply_to_message.from.id, is_bot: evt.reply_to_message.from.is_bot } : void 0
7030
+ } : void 0,
7031
+ entities: evt.entities,
7032
+ caption_entities: evt.caption_entities
7033
+ };
7034
+ const c = classifyPeerMessage2(classifierMsg, deps.peerConfig, deps.self);
7035
+ isSelf = c.kind === "self";
7036
+ peerLeaf = { kind: c.kind, reason: c.kind === "drop" ? c.reason : void 0 };
7037
+ }
7038
+ const decision = decideInboundAccess({
7039
+ content,
7040
+ // Telegram's only self signal is the peer classifier (Slack catches self at
7041
+ // evt.user===botUserId; Teams inside its activity filter).
7042
+ isSelf,
7043
+ isBot,
7044
+ peer: peerLeaf,
7045
+ // TELEGRAM_ALLOWED_CHATS is Telegram's org-boundary axis. An unlisted chat is
7046
+ // external ⇒ silent drop.
7047
+ orgBoundary: deps.allowedChats.size > 0 && !deps.allowedChats.has(chatId) ? { forward: false } : { forward: true },
7048
+ // No sender-policy mode system and no command dispatch on the injection gate.
7049
+ sameOrg: false
7050
+ });
7051
+ if (decision.kind === "drop")
7052
+ return { admit: false, reason: decision.reason };
7053
+ return { admit: true };
7054
+ }
7055
+
7056
+ // ../../packages/core/dist/channels/governance/telegram-inbound-config.js
7057
+ function parseTelegramAllowedChatsEnv(env) {
7058
+ return new Set((env["TELEGRAM_ALLOWED_CHATS"] ?? "").split(",").map((s) => s.trim()).filter(Boolean));
7059
+ }
7060
+ function parseTelegramPeerDisabledModeEnv(env, onWarn) {
7061
+ const next = (env["PEER_DISABLED"] ?? "").trim().toLowerCase();
7062
+ if (next === "off" || next === "cross_team_only" || next === "all")
7063
+ return next;
7064
+ if (next.length > 0) {
7065
+ onWarn?.(`invalid PEER_DISABLED=${JSON.stringify(env["PEER_DISABLED"])} - defaulting to 'all' (fail-closed)`);
7066
+ return "all";
7067
+ }
7068
+ const legacy = (env["TELEGRAM_PEER_DISABLED"] ?? "").trim().toLowerCase();
7069
+ return legacy === "1" || legacy === "true" || legacy === "yes" ? "all" : "off";
7070
+ }
7071
+ function buildTelegramPeerClassifierConfigFromEnv(env, opts) {
7072
+ return {
7073
+ peer_agent_mode: parsePeerAgentModeEnv2(env["TELEGRAM_PEER_AGENT_MODE"]),
7074
+ peer_group_ids: parsePeerGroupIdsEnv2(env["TELEGRAM_PEER_GROUP_IDS"]),
7075
+ peers: parsePeersEnv2(env["TELEGRAM_PEERS"], env["TELEGRAM_PEERS_GATE"]),
7076
+ peer_disabled_mode: parseTelegramPeerDisabledModeEnv(env, opts?.onWarn)
7077
+ };
7078
+ }
7079
+
7080
+ // src/lib/telegram-ingest.ts
7081
+ var nodeHttpsTelegramFetch = (url, init) => new Promise((resolve, reject) => {
7082
+ const u = new URL(url);
7083
+ const body = init?.body;
7084
+ const headers = { ...init?.headers ?? {} };
7085
+ if (body !== void 0 && headers["Content-Length"] === void 0) {
7086
+ headers["Content-Length"] = String(Buffer.byteLength(body));
7087
+ }
7088
+ const req = https2.request(
7089
+ {
7090
+ hostname: u.hostname,
7091
+ port: 443,
7092
+ path: u.pathname + u.search,
7093
+ method: init?.method ?? "GET",
7094
+ family: 4,
7095
+ // Backstop above the long-poll hold (getUpdates parks up to
7096
+ // LONG_POLL_SECONDS); short calls carry their own AbortSignal.timeout.
7097
+ timeout: POLL_HTTP_TIMEOUT_MS + 5e3,
7098
+ headers
7099
+ },
7100
+ (res) => {
7101
+ let data = "";
7102
+ res.on("data", (d) => {
7103
+ data += d;
7104
+ });
7105
+ res.on("end", () => {
7106
+ const status = res.statusCode ?? 0;
7107
+ resolve({
7108
+ ok: status >= 200 && status < 300,
7109
+ status,
7110
+ json: async () => JSON.parse(data.length > 0 ? data : "{}")
7111
+ });
7112
+ });
7113
+ }
7114
+ );
7115
+ req.on("error", reject);
7116
+ req.on("timeout", () => {
7117
+ req.destroy(new Error("Telegram API socket timeout"));
7118
+ });
7119
+ const signal = init?.signal;
7120
+ if (signal) {
7121
+ if (signal.aborted) req.destroy(new Error("aborted"));
7122
+ else signal.addEventListener("abort", () => req.destroy(new Error("aborted")), { once: true });
7123
+ }
7124
+ if (body !== void 0) req.write(body);
7125
+ req.end();
7126
+ });
7127
+ var TELEGRAM_API = "https://api.telegram.org";
7128
+ var LONG_POLL_SECONDS = 50;
7129
+ var POLL_HTTP_TIMEOUT_MS = (LONG_POLL_SECONDS + 10) * 1e3;
7130
+ var POLL_ERROR_BACKOFF_MS = 5e3;
7131
+ function memoryOffsetStore(initial = 0) {
7132
+ let value = initial;
7133
+ return { load: () => value, save: (o) => {
7134
+ value = o;
7135
+ } };
7136
+ }
7137
+ function resolveTelegramIngestParamsFromEnv(env) {
7138
+ const botToken = env["TELEGRAM_BOT_TOKEN"];
7139
+ if (!botToken) return null;
7140
+ const ackReaction = (env["TELEGRAM_ACK_REACTION"] ?? "").trim() || "\u{1F440}";
7141
+ const skipReaction = (env["TELEGRAM_SKIP_REACTION"] ?? "").trim() || void 0;
7142
+ return {
7143
+ botToken,
7144
+ allowedChats: parseTelegramAllowedChatsEnv(env),
7145
+ peerConfig: buildTelegramPeerClassifierConfigFromEnv(env),
7146
+ ackReaction,
7147
+ ...skipReaction ? { skipReaction } : {}
7148
+ };
7149
+ }
7150
+ function isTelegramAuthFailure(status, description) {
7151
+ return status === 401 || (description ?? "").toLowerCase().includes("unauthorized");
7152
+ }
7153
+ async function resolveTelegramBotIdentity(botToken, fetchImpl) {
7154
+ const doFetch = fetchImpl ?? nodeHttpsTelegramFetch;
7155
+ const unresolved = { bot_id: null, bot_username: null };
7156
+ try {
7157
+ const res = await doFetch(`${TELEGRAM_API}/bot${botToken}/getMe`, {
7158
+ method: "POST",
7159
+ signal: AbortSignal.timeout(1e4)
7160
+ });
7161
+ const data = await res.json();
7162
+ if (data.ok && data.result) {
7163
+ return {
7164
+ self: {
7165
+ bot_id: data.result.id,
7166
+ // Lowercased to match resolveBotUsername()'s cache convention.
7167
+ bot_username: data.result.username ? data.result.username.toLowerCase() : null
7168
+ },
7169
+ authFailed: false
7170
+ };
7171
+ }
7172
+ return { self: unresolved, authFailed: isTelegramAuthFailure(res.status, data.description) };
7173
+ } catch {
7174
+ return { self: unresolved, authFailed: false };
7175
+ }
7176
+ }
7177
+ var BoundedSet2 = class {
7178
+ max;
7179
+ set = /* @__PURE__ */ new Set();
7180
+ constructor(max = 500) {
7181
+ this.max = max;
7182
+ }
7183
+ has(k) {
7184
+ return this.set.has(k);
7185
+ }
7186
+ add(k) {
7187
+ this.set.add(k);
7188
+ if (this.set.size > this.max) {
7189
+ const oldest = this.set.values().next().value;
7190
+ if (oldest !== void 0) this.set.delete(oldest);
7191
+ }
7192
+ }
7193
+ };
7194
+ async function dispatchTelegramInbound(update, ctx) {
7195
+ const msg = update.message;
7196
+ const chatId = String(msg.chat.id);
7197
+ const dedupKey = String(update.update_id);
7198
+ if (ctx.seen.has(dedupKey)) return "dropped_duplicate";
7199
+ ctx.seen.add(dedupKey);
7200
+ const decision = evaluateTelegramInboundGate(msg, {
7201
+ allowedChats: ctx.allowedChats,
7202
+ peerConfig: ctx.peerConfig,
7203
+ self: ctx.self
7204
+ });
7205
+ if (!decision.admit) {
7206
+ ctx.log(`[telegram-ingest:${ctx.codeName}] dropped chat=${redactId(chatId)} reason=${decision.reason}`);
7207
+ return "dropped_gate";
7208
+ }
7209
+ if (ctx.ackReaction) {
7210
+ void ctx.addReaction({ chatId, messageId: msg.message_id, emoji: ctx.ackReaction });
7211
+ }
7212
+ const conversationKey = msg.message_thread_id ? `${chatId}:${msg.message_thread_id}` : chatId;
7213
+ const meta = {
7214
+ source: "telegram",
7215
+ lane: "conversational",
7216
+ requires_reply: "true",
7217
+ chat_id: chatId
7218
+ };
7219
+ if (msg.message_thread_id) meta["message_thread_id"] = String(msg.message_thread_id);
7220
+ const result = await ctx.inject({
7221
+ channelId: "telegram",
7222
+ conversationKey,
7223
+ senderId: msg.from ? String(msg.from.id) : "unknown",
7224
+ text: msg.text ?? msg.caption ?? "",
7225
+ meta
7226
+ });
7227
+ if (result.status === "declined") {
7228
+ ctx.log(`[telegram-ingest:${ctx.codeName}] inject declined reason=${result.reason}`);
7229
+ return "declined";
7230
+ }
7231
+ if (result.status === "replied" && result.reply) {
7232
+ await ctx.sendReply({
7233
+ chatId,
7234
+ text: result.reply,
7235
+ ...msg.message_thread_id ? { messageThreadId: msg.message_thread_id } : {}
7236
+ });
7237
+ ctx.log(`[telegram-ingest:${ctx.codeName}] outcome=replied chat=${redactId(chatId)} len=${result.reply.length}`);
7238
+ return "admitted_replied";
7239
+ }
7240
+ ctx.log(
7241
+ `[telegram-ingest:${ctx.codeName}] outcome=no_reply chat=${redactId(chatId)} inject_status=${result.status}`
7242
+ );
7243
+ if (ctx.skipReaction) {
7244
+ void ctx.addReaction({ chatId, messageId: msg.message_id, emoji: ctx.skipReaction });
7245
+ }
7246
+ return "admitted_no_reply";
7247
+ }
7248
+ function redactId(id) {
7249
+ if (!id) return "?";
7250
+ return `${id.slice(0, 2)}***`;
7251
+ }
7252
+ function defaultSendReply2(botToken, fetchImpl, log2) {
7253
+ return async ({ chatId, text, messageThreadId }) => {
7254
+ try {
7255
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/sendMessage`, {
7256
+ method: "POST",
7257
+ headers: { "Content-Type": "application/json" },
7258
+ body: JSON.stringify({
7259
+ chat_id: chatId,
7260
+ text,
7261
+ ...messageThreadId ? { message_thread_id: messageThreadId } : {}
7262
+ }),
7263
+ // Dedicated short timeout so an outbound reply can't inherit the ~50s
7264
+ // long-poll getUpdates timeout and hang a turn on a slow Telegram edge.
7265
+ signal: AbortSignal.timeout(1e4)
7266
+ });
7267
+ const data = await res.json();
7268
+ if (!data.ok) log2(`[telegram-ingest] sendMessage failed: ${data.description}`);
7269
+ } catch (err) {
7270
+ log2(`[telegram-ingest] sendMessage threw: ${err instanceof Error ? err.message : String(err)}`);
7271
+ }
7272
+ };
7273
+ }
7274
+ function defaultAddReaction2(botToken, fetchImpl, log2) {
7275
+ return async ({ chatId, messageId, emoji }) => {
7276
+ try {
7277
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/setMessageReaction`, {
7278
+ method: "POST",
7279
+ headers: { "Content-Type": "application/json" },
7280
+ body: JSON.stringify({
7281
+ chat_id: chatId,
7282
+ message_id: messageId,
7283
+ reaction: [{ type: "emoji", emoji }]
7284
+ }),
7285
+ // Dedicated short timeout — the ack must not inherit the long-poll window.
7286
+ signal: AbortSignal.timeout(1e4)
7287
+ });
7288
+ const data = await res.json();
7289
+ if (!data.ok) log2(`[telegram-ingest] setMessageReaction failed: ${data.description}`);
7290
+ } catch (err) {
7291
+ log2(`[telegram-ingest] setMessageReaction threw: ${err instanceof Error ? err.message : String(err)}`);
7292
+ }
7293
+ };
7294
+ }
7295
+ function sleep(ms) {
7296
+ return new Promise((resolve) => {
7297
+ setTimeout(resolve, ms).unref?.();
7298
+ });
7299
+ }
7300
+ function startTelegramIngest(config2) {
7301
+ const fetchImpl = config2.fetchImpl ?? nodeHttpsTelegramFetch;
7302
+ const sendReply = config2.sendReply ?? defaultSendReply2(config2.botToken, fetchImpl, config2.log);
7303
+ const addReaction = config2.addReaction ?? defaultAddReaction2(config2.botToken, fetchImpl, config2.log);
7304
+ const offsetStore = config2.offsetStore ?? memoryOffsetStore();
7305
+ const seen = new BoundedSet2();
7306
+ const ctx = {
7307
+ codeName: config2.codeName,
7308
+ allowedChats: config2.allowedChats,
7309
+ peerConfig: config2.peerConfig,
7310
+ self: config2.self,
7311
+ inject: config2.inject,
7312
+ sendReply,
7313
+ addReaction,
7314
+ ...config2.ackReaction ? { ackReaction: config2.ackReaction } : {},
7315
+ ...config2.skipReaction ? { skipReaction: config2.skipReaction } : {},
7316
+ log: config2.log,
7317
+ seen
7318
+ };
7319
+ let stopped = false;
7320
+ let controller = null;
7321
+ let nextOffset = offsetStore.load();
7322
+ const pollLoop = async () => {
7323
+ while (!stopped) {
7324
+ try {
7325
+ controller = new AbortController();
7326
+ const timeout = AbortSignal.timeout(POLL_HTTP_TIMEOUT_MS);
7327
+ const signal = anySignal([controller.signal, timeout]);
7328
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${config2.botToken}/getUpdates`, {
7329
+ method: "POST",
7330
+ headers: { "Content-Type": "application/json" },
7331
+ body: JSON.stringify({
7332
+ offset: nextOffset,
7333
+ timeout: LONG_POLL_SECONDS,
7334
+ allowed_updates: ["message"]
7335
+ }),
7336
+ signal
7337
+ });
7338
+ if (stopped) return;
7339
+ const data = await res.json();
7340
+ if (!data.ok) {
7341
+ config2.log(`[telegram-ingest:${config2.codeName}] getUpdates failed: ${data.description ?? "unknown"}`);
7342
+ await sleep(POLL_ERROR_BACKOFF_MS);
7343
+ continue;
7344
+ }
7345
+ const updates = data.result ?? [];
7346
+ for (const update of updates) {
7347
+ nextOffset = Math.max(nextOffset, update.update_id + 1);
7348
+ const msg = update.message;
7349
+ if (!msg) continue;
7350
+ try {
7351
+ await dispatchTelegramInbound({ update_id: update.update_id, message: msg }, ctx);
7352
+ } catch (err) {
7353
+ config2.log(
7354
+ `[telegram-ingest:${config2.codeName}] dispatch threw: ${err instanceof Error ? err.message : String(err)}`
7355
+ );
7356
+ }
7357
+ }
7358
+ if (updates.length > 0) offsetStore.save(nextOffset);
7359
+ } catch (err) {
7360
+ if (stopped) return;
7361
+ config2.log(`[telegram-ingest:${config2.codeName}] poll threw: ${err instanceof Error ? err.message : String(err)}`);
7362
+ await sleep(POLL_ERROR_BACKOFF_MS);
7363
+ }
7364
+ }
7365
+ };
7366
+ void pollLoop();
7367
+ return {
7368
+ stop() {
7369
+ stopped = true;
7370
+ try {
7371
+ controller?.abort();
7372
+ } catch {
7373
+ }
7374
+ }
7375
+ };
7376
+ }
7377
+ function anySignal(signals) {
7378
+ const controller = new AbortController();
7379
+ for (const s of signals) {
7380
+ if (s.aborted) {
7381
+ controller.abort();
7382
+ break;
7383
+ }
7384
+ s.addEventListener("abort", () => controller.abort(), { once: true });
7385
+ }
7386
+ return controller.signal;
7387
+ }
7388
+
7389
+ // src/lib/opencode-telegram-ingest.ts
7390
+ var ingests2 = /* @__PURE__ */ new Map();
7391
+ function fingerprint2(params) {
7392
+ return createHash13("sha256").update(
7393
+ JSON.stringify({
7394
+ botToken: params.botToken,
7395
+ allowedChats: [...params.allowedChats].sort(),
7396
+ peerConfig: params.peerConfig,
7397
+ ackReaction: params.ackReaction ?? null,
7398
+ skipReaction: params.skipReaction ?? null
7399
+ })
7400
+ ).digest("hex");
7401
+ }
7402
+ function createFileOffsetStore(codeName, log2, currentBotId) {
7403
+ let dir;
7404
+ let filePath;
7405
+ try {
7406
+ dir = getFramework("opencode").getAgentDir(codeName);
7407
+ filePath = join21(dir, "telegram-getupdates-offset-opencode.json");
7408
+ } catch {
7409
+ dir = null;
7410
+ filePath = null;
7411
+ }
7412
+ return {
7413
+ load() {
7414
+ if (!filePath) return 0;
7415
+ try {
7416
+ const parsed = JSON.parse(readFileSync17(filePath, "utf-8"));
7417
+ if (currentBotId != null && typeof parsed?.bot_id === "number" && parsed.bot_id !== currentBotId) {
7418
+ log2(`[telegram-ingest:${codeName}] offset cursor belongs to a different bot; ignoring (bot swap)`);
7419
+ return 0;
7420
+ }
7421
+ const offset = parsed?.offset;
7422
+ if (typeof offset === "number" && Number.isFinite(offset) && offset > 0) {
7423
+ const normalized = Math.floor(offset);
7424
+ if (Number.isSafeInteger(normalized) && normalized > 0) return normalized;
7425
+ }
7426
+ } catch {
7427
+ }
7428
+ return 0;
7429
+ },
7430
+ save(offset) {
7431
+ if (!filePath || !dir) return;
7432
+ const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
7433
+ try {
7434
+ mkdirSync8(dir, { recursive: true, mode: 448 });
7435
+ writeFileSync9(
7436
+ tmpPath,
7437
+ JSON.stringify({
7438
+ offset,
7439
+ // Stamp the owning bot so a later token swap invalidates this cursor.
7440
+ ...currentBotId != null ? { bot_id: currentBotId } : {},
7441
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
7442
+ }),
7443
+ { mode: 384 }
7444
+ );
7445
+ renameSync2(tmpPath, filePath);
7446
+ } catch (err) {
7447
+ log2(`[telegram-ingest:${codeName}] offset persist failed: ${err instanceof Error ? err.message : String(err)}`);
7448
+ try {
7449
+ if (existsSync11(tmpPath)) unlinkSync(tmpPath);
7450
+ } catch {
7451
+ }
7452
+ }
7453
+ }
7454
+ };
7455
+ }
7456
+ async function ensureOpencodeTelegramIngest(opts) {
7457
+ const { codeName, agentId, log: log2 } = opts;
7458
+ const readEnv = opts.readEnv ?? readChannelServerEnv;
7459
+ let params;
7460
+ try {
7461
+ const env = readEnv(codeName, "telegram");
7462
+ if (!env) {
7463
+ stopOpencodeTelegramIngest(codeName, log2);
7464
+ return;
7465
+ }
7466
+ params = resolveTelegramIngestParamsFromEnv(env);
7467
+ } catch (err) {
7468
+ stopOpencodeTelegramIngest(codeName, log2);
7469
+ log2(`[telegram-ingest:${codeName}] not started: ${err instanceof Error ? err.message : String(err)}`);
7470
+ return;
7471
+ }
7472
+ if (!params) {
7473
+ stopOpencodeTelegramIngest(codeName, log2);
7474
+ return;
7475
+ }
7476
+ const key = fingerprint2(params);
7477
+ const existing = ingests2.get(codeName);
7478
+ if (existing?.key === key && !existing.selfUnresolved) return;
7479
+ const { self, authFailed } = await resolveTelegramBotIdentity(params.botToken, opts.fetchImpl);
7480
+ if (authFailed) {
7481
+ stopOpencodeTelegramIngest(codeName, log2);
7482
+ log2(`[telegram-ingest:${codeName}] not started: Telegram bot token rejected (needs reconnect)`);
7483
+ return;
7484
+ }
7485
+ if (existing?.key === key && self.bot_id === null) return;
7486
+ if (self.bot_id === null) {
7487
+ log2(`[telegram-ingest:${codeName}] bot identity unresolved; peer classification degraded`);
7488
+ }
7489
+ if (existing) {
7490
+ existing.handle.stop();
7491
+ ingests2.delete(codeName);
7492
+ log2(
7493
+ existing.key === key ? `[telegram-ingest:${codeName}] bot identity resolved; restarting ingest` : `[telegram-ingest:${codeName}] config changed; restarting ingest`
7494
+ );
7495
+ }
7496
+ const handle = startTelegramIngest({
7497
+ codeName,
7498
+ agentId,
7499
+ botToken: params.botToken,
7500
+ allowedChats: params.allowedChats,
7501
+ peerConfig: params.peerConfig,
7502
+ self,
7503
+ inject: (msg) => injectOpencodeMessage(codeName, msg, { awaitReply: true }),
7504
+ log: log2,
7505
+ offsetStore: opts.offsetStore ?? createFileOffsetStore(codeName, log2, self.bot_id),
7506
+ ...params.ackReaction ? { ackReaction: params.ackReaction } : {},
7507
+ ...params.skipReaction ? { skipReaction: params.skipReaction } : {},
7508
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
7509
+ });
7510
+ ingests2.set(codeName, { key, handle, selfUnresolved: self.bot_id === null });
7511
+ log2(
7512
+ `[telegram-ingest:${codeName}] ingest started (peer_mode=${params.peerConfig.peer_agent_mode}, allowlist=${params.allowedChats.size})`
7513
+ );
7514
+ }
7515
+ function stopOpencodeTelegramIngest(codeName, log2) {
7516
+ const entry = ingests2.get(codeName);
7517
+ if (!entry) return;
7518
+ entry.handle.stop();
7519
+ ingests2.delete(codeName);
7520
+ log2?.(`[telegram-ingest:${codeName}] ingest stopped`);
7521
+ }
7522
+ function stopAllOpencodeTelegramIngests(log2) {
7523
+ for (const codeName of [...ingests2.keys()]) stopOpencodeTelegramIngest(codeName, log2);
7524
+ }
7525
+
6807
7526
  // src/lib/wedge-detection.ts
6808
7527
  var DEFAULTS = {
6809
7528
  inboundWaitSeconds: 120,
@@ -6944,24 +7663,24 @@ function partitionActionableByPoison(actionable, states, config2) {
6944
7663
  }
6945
7664
 
6946
7665
  // src/lib/restart-flags.ts
6947
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync17, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
6948
- import { homedir as homedir10 } from "os";
6949
- import { join as join20 } from "path";
6950
- import { randomUUID } from "crypto";
7666
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync4, readFileSync as readFileSync18, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
7667
+ import { homedir as homedir11 } from "os";
7668
+ import { join as join22 } from "path";
7669
+ import { randomUUID as randomUUID2 } from "crypto";
6951
7670
  function restartFlagsDir() {
6952
- return join20(homedir10(), ".augmented", "restart-flags");
7671
+ return join22(homedir11(), ".augmented", "restart-flags");
6953
7672
  }
6954
7673
  function flagPath(codeName) {
6955
- return join20(restartFlagsDir(), `${codeName}.flag`);
7674
+ return join22(restartFlagsDir(), `${codeName}.flag`);
6956
7675
  }
6957
7676
  function readRestartFlags() {
6958
7677
  const dir = restartFlagsDir();
6959
- if (!existsSync11(dir)) return [];
7678
+ if (!existsSync12(dir)) return [];
6960
7679
  const out = [];
6961
7680
  for (const entry of readdirSync4(dir)) {
6962
7681
  if (!entry.endsWith(".flag")) continue;
6963
7682
  try {
6964
- const raw = readFileSync17(join20(dir, entry), "utf8");
7683
+ const raw = readFileSync18(join22(dir, entry), "utf8");
6965
7684
  const parsed = JSON.parse(raw);
6966
7685
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
6967
7686
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -6979,8 +7698,8 @@ function readRestartFlags() {
6979
7698
  }
6980
7699
  function deleteRestartFlag(codeName) {
6981
7700
  const path = flagPath(codeName);
6982
- if (existsSync11(path)) {
6983
- rmSync3(path, { force: true });
7701
+ if (existsSync12(path)) {
7702
+ rmSync4(path, { force: true });
6984
7703
  }
6985
7704
  }
6986
7705
 
@@ -7325,10 +8044,10 @@ function startRealtimeConfig(config2) {
7325
8044
  filter: filterStr
7326
8045
  }, (payload) => {
7327
8046
  const agent = payload.new;
7328
- const fingerprint2 = `${agent.status}|${agent.framework}|${agent.session_mode}|${agent.primary_model}`;
8047
+ const fingerprint3 = `${agent.status}|${agent.framework}|${agent.session_mode}|${agent.primary_model}`;
7329
8048
  const prev = lastKnown.get(agent.agent_id);
7330
- if (prev === fingerprint2) return;
7331
- lastKnown.set(agent.agent_id, fingerprint2);
8049
+ if (prev === fingerprint3) return;
8050
+ lastKnown.set(agent.agent_id, fingerprint3);
7332
8051
  log2(`[realtime] Agent config changed: ${agent.code_name} (status=${agent.status})`);
7333
8052
  onConfigChange(agent);
7334
8053
  }).subscribe((status) => {
@@ -8213,15 +8932,15 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
8213
8932
  var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
8214
8933
  function projectMcpHash(_codeName, projectDir) {
8215
8934
  try {
8216
- const raw = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8217
- return createHash13("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
8935
+ const raw = readFileSync19(join23(projectDir, ".mcp.json"), "utf-8");
8936
+ return createHash14("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
8218
8937
  } catch {
8219
8938
  return null;
8220
8939
  }
8221
8940
  }
8222
8941
  function projectMcpKeys(_codeName, projectDir) {
8223
8942
  try {
8224
- const raw = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8943
+ const raw = readFileSync19(join23(projectDir, ".mcp.json"), "utf-8");
8225
8944
  const parsed = JSON.parse(raw);
8226
8945
  const servers = parsed.mcpServers;
8227
8946
  if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
@@ -8239,7 +8958,7 @@ function seedSessionLaunchBaselines(codeName, projectDir) {
8239
8958
  else runningMcpServerKeys.delete(codeName);
8240
8959
  let launchStructure = null;
8241
8960
  try {
8242
- const raw = readFileSync18(join21(projectDir, ".mcp.json"), "utf-8");
8961
+ const raw = readFileSync19(join23(projectDir, ".mcp.json"), "utf-8");
8243
8962
  launchStructure = managedMcpStructureHashFromFile(
8244
8963
  JSON.parse(raw),
8245
8964
  isManagedMcpServerKey
@@ -8339,7 +9058,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
8339
9058
  if (result && result.rebindCandidates.length > 0 && hostFlagStore().getBoolean("session-tool-rebind")) {
8340
9059
  let mcpJsonForRebind = null;
8341
9060
  try {
8342
- mcpJsonForRebind = JSON.parse(readFileSync18(join21(projectDir, ".mcp.json"), "utf-8"));
9061
+ mcpJsonForRebind = JSON.parse(readFileSync19(join23(projectDir, ".mcp.json"), "utf-8"));
8343
9062
  } catch {
8344
9063
  mcpJsonForRebind = null;
8345
9064
  }
@@ -8468,7 +9187,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
8468
9187
  function projectChannelSecretHash(projectDir) {
8469
9188
  try {
8470
9189
  const entries = parseEnvIntegrations(
8471
- readFileSync18(join21(projectDir, ".env.integrations"), "utf-8")
9190
+ readFileSync19(join23(projectDir, ".env.integrations"), "utf-8")
8472
9191
  );
8473
9192
  return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
8474
9193
  } catch {
@@ -8531,6 +9250,7 @@ function clearAgentCaches(agentId, codeName) {
8531
9250
  const channelCacheMutated = clearAgentState(agentId, codeName);
8532
9251
  agentChannelTokens.delete(codeName);
8533
9252
  stopOpencodeSlackIngest(codeName, log);
9253
+ stopOpencodeTelegramIngest(codeName, log);
8534
9254
  agentFrameworkCache.delete(codeName);
8535
9255
  kanbanBoardCache.delete(codeName);
8536
9256
  clearKanbanNudgeState(codeName);
@@ -8560,7 +9280,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
8560
9280
  var lastVersionCheckAt = 0;
8561
9281
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
8562
9282
  var lastResponsivenessProbeAt = 0;
8563
- var agtCliVersion = true ? "0.28.343" : "dev";
9283
+ var agtCliVersion = true ? "0.28.345" : "dev";
8564
9284
  function resolveBrewPath(execFileSync2) {
8565
9285
  try {
8566
9286
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -8573,7 +9293,7 @@ function resolveBrewPath(execFileSync2) {
8573
9293
  "/usr/local/bin/brew"
8574
9294
  ];
8575
9295
  for (const path of fallbacks) {
8576
- if (existsSync12(path)) return path;
9296
+ if (existsSync13(path)) return path;
8577
9297
  }
8578
9298
  return null;
8579
9299
  }
@@ -8583,7 +9303,7 @@ function claudeBinaryInstalled(execFileSync2) {
8583
9303
  "/opt/homebrew/bin/claude",
8584
9304
  "/usr/local/bin/claude"
8585
9305
  ];
8586
- if (canonical.some((path) => existsSync12(path))) return true;
9306
+ if (canonical.some((path) => existsSync13(path))) return true;
8587
9307
  try {
8588
9308
  execFileSync2("which", ["claude"], { timeout: 5e3 });
8589
9309
  return true;
@@ -8738,8 +9458,8 @@ function claudeManagedSettingsPath() {
8738
9458
  function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
8739
9459
  try {
8740
9460
  let settings = {};
8741
- if (existsSync12(path)) {
8742
- const raw = readFileSync18(path, "utf-8").trim();
9461
+ if (existsSync13(path)) {
9462
+ const raw = readFileSync19(path, "utf-8").trim();
8743
9463
  if (raw) {
8744
9464
  let parsed;
8745
9465
  try {
@@ -8755,8 +9475,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
8755
9475
  }
8756
9476
  if (settings.channelsEnabled === true) return "ok";
8757
9477
  settings.channelsEnabled = true;
8758
- mkdirSync8(dirname6(path), { recursive: true });
8759
- writeFileSync9(path, `${JSON.stringify(settings, null, 2)}
9478
+ mkdirSync10(dirname6(path), { recursive: true });
9479
+ writeFileSync11(path, `${JSON.stringify(settings, null, 2)}
8760
9480
  `);
8761
9481
  log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
8762
9482
  return "ok";
@@ -8801,7 +9521,7 @@ async function ensureFrameworkBinary(frameworkId) {
8801
9521
  if (!process.env.PATH?.split(":").includes(brewBinDir)) {
8802
9522
  process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
8803
9523
  }
8804
- if (existsSync12("/home/linuxbrew/.linuxbrew/bin/claude")) {
9524
+ if (existsSync13("/home/linuxbrew/.linuxbrew/bin/claude")) {
8805
9525
  log("Claude Code installed successfully");
8806
9526
  } else {
8807
9527
  log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
@@ -8857,7 +9577,7 @@ ${r.stderr}`;
8857
9577
  }
8858
9578
  var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
8859
9579
  function selfUpdateAppliedMarkerPath() {
8860
- return join21(homedir11(), ".augmented", ".last-self-update-applied");
9580
+ return join23(homedir12(), ".augmented", ".last-self-update-applied");
8861
9581
  }
8862
9582
  var selfUpdateUpToDateLogged = false;
8863
9583
  var selfUpdatePinnedLogged = false;
@@ -8883,7 +9603,7 @@ async function checkAndUpdateCli(opts) {
8883
9603
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
8884
9604
  if (!isBrewFormula && !isNpmGlobal) return "noop";
8885
9605
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
8886
- const markerPath = join21(homedir11(), ".augmented", ".last-update-check");
9606
+ const markerPath = join23(homedir12(), ".augmented", ".last-update-check");
8887
9607
  if (!force) {
8888
9608
  try {
8889
9609
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
@@ -9228,14 +9948,14 @@ async function checkClaudeAuth() {
9228
9948
  }
9229
9949
  var evalEmptyMcpConfigPath = null;
9230
9950
  function ensureEvalEmptyMcpConfig() {
9231
- if (evalEmptyMcpConfigPath && existsSync12(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
9232
- const dir = join21(homedir11(), ".augmented");
9951
+ if (evalEmptyMcpConfigPath && existsSync13(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
9952
+ const dir = join23(homedir12(), ".augmented");
9233
9953
  try {
9234
- mkdirSync8(dir, { recursive: true });
9954
+ mkdirSync10(dir, { recursive: true });
9235
9955
  } catch {
9236
9956
  }
9237
- const p = join21(dir, ".eval-empty-mcp.json");
9238
- writeFileSync9(p, JSON.stringify({ mcpServers: {} }));
9957
+ const p = join23(dir, ".eval-empty-mcp.json");
9958
+ writeFileSync11(p, JSON.stringify({ mcpServers: {} }));
9239
9959
  evalEmptyMcpConfigPath = p;
9240
9960
  return p;
9241
9961
  }
@@ -9260,7 +9980,7 @@ async function runEvalClaude(prompt, model) {
9260
9980
  ""
9261
9981
  ];
9262
9982
  const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
9263
- cwd: homedir11(),
9983
+ cwd: homedir12(),
9264
9984
  timeout: 12e4,
9265
9985
  stdin: "ignore",
9266
9986
  env: childEnv,
@@ -9326,10 +10046,10 @@ function resolveConversationEvalBackend() {
9326
10046
  return conversationEvalBackend;
9327
10047
  }
9328
10048
  function getStateFile() {
9329
- return join21(config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
10049
+ return join23(config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
9330
10050
  }
9331
10051
  function channelHashCacheDir() {
9332
- return config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
10052
+ return config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented");
9333
10053
  }
9334
10054
  function loadChannelHashCache2() {
9335
10055
  loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
@@ -9381,7 +10101,7 @@ function removeDeliveryBaselineEntries(agentId) {
9381
10101
  var _channelQuarantineStore = null;
9382
10102
  function channelQuarantineStore() {
9383
10103
  if (!_channelQuarantineStore) {
9384
- const dir = config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
10104
+ const dir = config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented");
9385
10105
  _channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
9386
10106
  }
9387
10107
  return _channelQuarantineStore;
@@ -9389,7 +10109,7 @@ function channelQuarantineStore() {
9389
10109
  var _hostFlagStore = null;
9390
10110
  function hostFlagStore() {
9391
10111
  if (!_hostFlagStore) {
9392
- const dir = config?.configDir ?? join21(process.env["HOME"] ?? "/tmp", ".augmented");
10112
+ const dir = config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented");
9393
10113
  _hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
9394
10114
  }
9395
10115
  return _hostFlagStore;
@@ -9456,13 +10176,13 @@ function parseSkillFrontmatter(content) {
9456
10176
  return out;
9457
10177
  }
9458
10178
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
9459
- const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync10 } = await import("fs");
9460
- const skillsDir = join21(configDir, codeName, "project", ".claude", "skills");
9461
- const claudeMdPath = join21(configDir, codeName, "project", "CLAUDE.md");
10179
+ const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync12 } = await import("fs");
10180
+ const skillsDir = join23(configDir, codeName, "project", ".claude", "skills");
10181
+ const claudeMdPath = join23(configDir, codeName, "project", "CLAUDE.md");
9462
10182
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
9463
10183
  const entries = [];
9464
10184
  for (const dir of readdirSync6(skillsDir).sort()) {
9465
- const skillFile = join21(skillsDir, dir, "SKILL.md");
10185
+ const skillFile = join23(skillsDir, dir, "SKILL.md");
9466
10186
  if (!ex(skillFile)) continue;
9467
10187
  try {
9468
10188
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -9504,7 +10224,7 @@ ${SKILLS_INDEX_END}`;
9504
10224
  next = current.trimEnd() + "\n\n" + section + "\n";
9505
10225
  }
9506
10226
  if (next !== current) {
9507
- writeFileSync10(claudeMdPath, next, "utf-8");
10227
+ writeFileSync12(claudeMdPath, next, "utf-8");
9508
10228
  log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
9509
10229
  }
9510
10230
  }
@@ -9525,7 +10245,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
9525
10245
  if (codeNames.length === 0) return;
9526
10246
  void (async () => {
9527
10247
  try {
9528
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
10248
+ const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
9529
10249
  await api.post("/host/heartbeat", {
9530
10250
  host_id: hostId,
9531
10251
  agent_diagnostics: collectDiagnostics(codeNames)
@@ -9624,7 +10344,7 @@ async function pollCycle() {
9624
10344
  }
9625
10345
  try {
9626
10346
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
9627
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
10347
+ const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
9628
10348
  const diagCodeNames = [...agentState.persistentSessionAgents];
9629
10349
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
9630
10350
  let tailscaleHostname;
@@ -9654,7 +10374,7 @@ async function pollCycle() {
9654
10374
  claudeAuth = await detectClaudeAuth();
9655
10375
  } catch (err) {
9656
10376
  const errText = err instanceof Error ? err.message : String(err);
9657
- const errId = createHash13("sha256").update(errText).digest("hex").slice(0, 12);
10377
+ const errId = createHash14("sha256").update(errText).digest("hex").slice(0, 12);
9658
10378
  log(`Claude auth detection failed (error_id=${errId})`);
9659
10379
  }
9660
10380
  const hostHasClaudeCode = state6.agents.some(
@@ -9779,7 +10499,7 @@ async function pollCycle() {
9779
10499
  const {
9780
10500
  collectResponsivenessProbes,
9781
10501
  getResponsivenessIntervalMs
9782
- } = await import("../responsiveness-probe-XLS2FXIX.js");
10502
+ } = await import("../responsiveness-probe-UYVXWW5D.js");
9783
10503
  const probeIntervalMs = getResponsivenessIntervalMs();
9784
10504
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
9785
10505
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -9811,7 +10531,7 @@ async function pollCycle() {
9811
10531
  collectResponsivenessProbes,
9812
10532
  livePendingInboundOldestAgeSeconds,
9813
10533
  parkPendingInbound
9814
- } = await import("../responsiveness-probe-XLS2FXIX.js");
10534
+ } = await import("../responsiveness-probe-UYVXWW5D.js");
9815
10535
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
9816
10536
  const wedgeNow = /* @__PURE__ */ new Date();
9817
10537
  const liveAgents = agentState.persistentSessionAgents;
@@ -9900,13 +10620,13 @@ async function pollCycle() {
9900
10620
  );
9901
10621
  if (hostFlagStore().getBoolean("wedge-transient-notice")) {
9902
10622
  try {
9903
- const paneTail = readFileSync18(paneLogPath(codeName), "utf8").slice(-65536);
10623
+ const paneTail = readFileSync19(paneLogPath(codeName), "utf8").slice(-65536);
9904
10624
  const transient = detectTransientApiErrorInLog(paneTail);
9905
10625
  if (transient) {
9906
- const wedgeHome = join21(homedir11(), ".augmented", codeName);
9907
- if (existsSync12(wedgeHome)) {
10626
+ const wedgeHome = join23(homedir12(), ".augmented", codeName);
10627
+ if (existsSync13(wedgeHome)) {
9908
10628
  atomicWriteFileSync(
9909
- join21(wedgeHome, "watchdog-give-up.json"),
10629
+ join23(wedgeHome, "watchdog-give-up.json"),
9910
10630
  JSON.stringify({
9911
10631
  gave_up_at: wedgeNow.toISOString(),
9912
10632
  reason: "transient_overload"
@@ -10155,7 +10875,7 @@ async function pollCycle() {
10155
10875
  } catch {
10156
10876
  }
10157
10877
  killAgentChannelProcesses(prev.codeName, { log });
10158
- const agentDir = join21(adapter.getAgentDir(prev.codeName), "provision");
10878
+ const agentDir = join23(adapter.getAgentDir(prev.codeName), "provision");
10159
10879
  await cleanupAgentFiles(prev.codeName, agentDir);
10160
10880
  clearAgentCaches(prev.agentId, prev.codeName);
10161
10881
  }
@@ -10242,10 +10962,10 @@ async function pollCycle() {
10242
10962
  // pending-inbound marker. Best-effort: a write failure is logged by
10243
10963
  // the watchdog, never fails the poll cycle.
10244
10964
  signalGiveUp: (codeName) => {
10245
- const dir = join21(homedir11(), ".augmented", codeName);
10246
- if (!existsSync12(dir)) return;
10965
+ const dir = join23(homedir12(), ".augmented", codeName);
10966
+ if (!existsSync13(dir)) return;
10247
10967
  atomicWriteFileSync(
10248
- join21(dir, "watchdog-give-up.json"),
10968
+ join23(dir, "watchdog-give-up.json"),
10249
10969
  JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
10250
10970
  );
10251
10971
  }
@@ -10384,7 +11104,7 @@ async function processAgent(agent, agentStates) {
10384
11104
  }
10385
11105
  const now = (/* @__PURE__ */ new Date()).toISOString();
10386
11106
  const adapter = resolveAgentFramework(agent.code_name);
10387
- let agentDir = join21(adapter.getAgentDir(agent.code_name), "provision");
11107
+ let agentDir = join23(adapter.getAgentDir(agent.code_name), "provision");
10388
11108
  if (agent.status === "draft" || agent.status === "paused") {
10389
11109
  if (previousKnownStatus !== agent.status) {
10390
11110
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
@@ -10430,7 +11150,7 @@ async function processAgent(agent, agentStates) {
10430
11150
  const residuals = {
10431
11151
  gatewayRunning: false,
10432
11152
  portAllocated: false,
10433
- provisionDirExists: existsSync12(agentDir)
11153
+ provisionDirExists: existsSync13(agentDir)
10434
11154
  };
10435
11155
  if (!hasRevokedResiduals(residuals)) {
10436
11156
  agentStates.push({
@@ -10568,7 +11288,7 @@ async function processAgent(agent, agentStates) {
10568
11288
  const frameworkId = refreshData.agent.framework ?? DEFAULT_FRAMEWORK;
10569
11289
  agentFrameworkCache.set(agent.code_name, frameworkId);
10570
11290
  const frameworkAdapter = getFramework(frameworkId);
10571
- agentDir = join21(frameworkAdapter.getAgentDir(agent.code_name), "provision");
11291
+ agentDir = join23(frameworkAdapter.getAgentDir(agent.code_name), "provision");
10572
11292
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
10573
11293
  agentRestartTimezoneInputs.set(agent.code_name, {
10574
11294
  agentTimezone: typeof refreshData.agent.timezone === "string" ? refreshData.agent.timezone : null,
@@ -10613,9 +11333,9 @@ async function processAgent(agent, agentStates) {
10613
11333
  try {
10614
11334
  const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
10615
11335
  const changedFiles = [];
10616
- mkdirSync8(agentDir, { recursive: true });
11336
+ mkdirSync10(agentDir, { recursive: true });
10617
11337
  for (const artifact of artifacts) {
10618
- const filePath = join21(agentDir, artifact.relativePath);
11338
+ const filePath = join23(agentDir, artifact.relativePath);
10619
11339
  let existingHash;
10620
11340
  let newHash;
10621
11341
  let writeContent = artifact.content;
@@ -10634,8 +11354,8 @@ async function processAgent(agent, agentStates) {
10634
11354
  };
10635
11355
  newHash = sha256(stripDynamicSections(artifact.content));
10636
11356
  try {
10637
- const projectClaudeMd = join21(config.configDir, agent.code_name, "project", "CLAUDE.md");
10638
- const existing = readFileSync18(projectClaudeMd, "utf-8");
11357
+ const projectClaudeMd = join23(config.configDir, agent.code_name, "project", "CLAUDE.md");
11358
+ const existing = readFileSync19(projectClaudeMd, "utf-8");
10639
11359
  existingHash = sha256(stripDynamicSections(existing));
10640
11360
  } catch {
10641
11361
  existingHash = null;
@@ -10653,7 +11373,7 @@ async function processAgent(agent, agentStates) {
10653
11373
  const generatorKeys = Object.keys(generatorServers);
10654
11374
  let existingRaw = "";
10655
11375
  try {
10656
- existingRaw = readFileSync18(filePath, "utf-8");
11376
+ existingRaw = readFileSync19(filePath, "utf-8");
10657
11377
  } catch {
10658
11378
  }
10659
11379
  const existingServers = parseMcp(existingRaw);
@@ -10675,26 +11395,26 @@ async function processAgent(agent, agentStates) {
10675
11395
  }
10676
11396
  }
10677
11397
  if (changedFiles.length > 0) {
10678
- const isFirst = !existsSync12(join21(agentDir, "CHARTER.md"));
11398
+ const isFirst = !existsSync13(join23(agentDir, "CHARTER.md"));
10679
11399
  const verb = isFirst ? "Provisioning" : "Updating";
10680
11400
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
10681
11401
  log(`${verb} '${agent.code_name}': ${fileNames}`);
10682
11402
  for (const file of changedFiles) {
10683
- const filePath = join21(agentDir, file.relativePath);
10684
- mkdirSync8(dirname6(filePath), { recursive: true });
11403
+ const filePath = join23(agentDir, file.relativePath);
11404
+ mkdirSync10(dirname6(filePath), { recursive: true });
10685
11405
  if (file.relativePath === ".mcp.json") {
10686
11406
  safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
10687
11407
  } else {
10688
- writeFileSync9(filePath, file.content);
11408
+ writeFileSync11(filePath, file.content);
10689
11409
  }
10690
11410
  }
10691
11411
  try {
10692
- const provSkillsDir = join21(agentDir, ".claude", "skills");
10693
- if (existsSync12(provSkillsDir)) {
11412
+ const provSkillsDir = join23(agentDir, ".claude", "skills");
11413
+ if (existsSync13(provSkillsDir)) {
10694
11414
  for (const folder of readdirSync5(provSkillsDir)) {
10695
11415
  if (folder.startsWith("knowledge-")) {
10696
11416
  try {
10697
- rmSync4(join21(provSkillsDir, folder), { recursive: true });
11417
+ rmSync5(join23(provSkillsDir, folder), { recursive: true });
10698
11418
  } catch {
10699
11419
  }
10700
11420
  }
@@ -10707,7 +11427,7 @@ async function processAgent(agent, agentStates) {
10707
11427
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
10708
11428
  const hashes = /* @__PURE__ */ new Map();
10709
11429
  for (const file of trackedFiles2) {
10710
- const h = hashFile(join21(agentDir, file));
11430
+ const h = hashFile(join23(agentDir, file));
10711
11431
  if (h) hashes.set(file, h);
10712
11432
  }
10713
11433
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -10725,14 +11445,14 @@ async function processAgent(agent, agentStates) {
10725
11445
  }
10726
11446
  if (Array.isArray(refreshData.workflows)) {
10727
11447
  try {
10728
- const provWorkflowsDir = join21(agentDir, ".claude", "workflows");
10729
- if (existsSync12(provWorkflowsDir)) {
11448
+ const provWorkflowsDir = join23(agentDir, ".claude", "workflows");
11449
+ if (existsSync13(provWorkflowsDir)) {
10730
11450
  const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
10731
11451
  for (const file of readdirSync5(provWorkflowsDir)) {
10732
11452
  if (!file.endsWith(".js")) continue;
10733
11453
  if (expected.has(file)) continue;
10734
11454
  try {
10735
- rmSync4(join21(provWorkflowsDir, file));
11455
+ rmSync5(join23(provWorkflowsDir, file));
10736
11456
  } catch {
10737
11457
  }
10738
11458
  }
@@ -10789,10 +11509,10 @@ async function processAgent(agent, agentStates) {
10789
11509
  }
10790
11510
  let lastDriftCheckAt = now;
10791
11511
  const written = agentState.writtenHashes.get(agent.agent_id);
10792
- if (written && existsSync12(agentDir)) {
11512
+ if (written && existsSync13(agentDir)) {
10793
11513
  const driftedFiles = [];
10794
11514
  for (const [file, expectedHash] of written) {
10795
- const localHash = hashFile(join21(agentDir, file));
11515
+ const localHash = hashFile(join23(agentDir, file));
10796
11516
  if (localHash && localHash !== expectedHash) {
10797
11517
  driftedFiles.push(file);
10798
11518
  }
@@ -10803,7 +11523,7 @@ async function processAgent(agent, agentStates) {
10803
11523
  try {
10804
11524
  const localHashes = {};
10805
11525
  for (const file of driftedFiles) {
10806
- localHashes[file] = hashFile(join21(agentDir, file));
11526
+ localHashes[file] = hashFile(join23(agentDir, file));
10807
11527
  }
10808
11528
  await api.post("/host/drift", {
10809
11529
  agent_id: agent.agent_id,
@@ -10990,15 +11710,15 @@ async function processAgent(agent, agentStates) {
10990
11710
  const addedChannels = [...restartDecision.added];
10991
11711
  const writeDmNoticeMarkers = isChannelAddRestart ? () => {
10992
11712
  try {
10993
- const agentAugmentedDir = join21(homedir11(), ".augmented", agent.code_name);
10994
- mkdirSync8(agentAugmentedDir, { recursive: true });
11713
+ const agentAugmentedDir = join23(homedir12(), ".augmented", agent.code_name);
11714
+ mkdirSync10(agentAugmentedDir, { recursive: true });
10995
11715
  const markerJson = JSON.stringify({
10996
11716
  version: 1,
10997
11717
  at: (/* @__PURE__ */ new Date()).toISOString(),
10998
11718
  added: addedChannels
10999
11719
  });
11000
11720
  for (const file of ["slack-channel-add-restart.json", "telegram-channel-add-restart.json"]) {
11001
- atomicWriteFileSync(join21(agentAugmentedDir, file), markerJson);
11721
+ atomicWriteFileSync(join23(agentAugmentedDir, file), markerJson);
11002
11722
  }
11003
11723
  } catch (err) {
11004
11724
  log(`[hot-reload] channel-add DM-notice marker write failed for '${agent.code_name}' (non-fatal): ${err.message}`);
@@ -11074,7 +11794,7 @@ async function processAgent(agent, agentStates) {
11074
11794
  const behaviourSubset = extractMsTeamsBehaviourSubset(
11075
11795
  msteamsEntry?.config
11076
11796
  );
11077
- const behaviourHash = createHash13("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
11797
+ const behaviourHash = createHash14("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
11078
11798
  const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
11079
11799
  const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
11080
11800
  const behaviourDecision = decideSenderPolicyRestart({
@@ -11130,7 +11850,7 @@ async function processAgent(agent, agentStates) {
11130
11850
  const slackBehaviourSubset = extractSlackBehaviourSubset(
11131
11851
  slackEntry?.config
11132
11852
  );
11133
- const slackBehaviourHash = createHash13("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
11853
+ const slackBehaviourHash = createHash14("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
11134
11854
  const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
11135
11855
  const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
11136
11856
  const slackBehaviourDecision = decideSenderPolicyRestart({
@@ -11187,24 +11907,24 @@ async function processAgent(agent, agentStates) {
11187
11907
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK) === "claude-code") {
11188
11908
  try {
11189
11909
  const agentProvisionDir = agentDir;
11190
- const projectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
11191
- mkdirSync8(agentProvisionDir, { recursive: true });
11192
- mkdirSync8(projectDir, { recursive: true });
11193
- const provisionMcpPath = join21(agentProvisionDir, ".mcp.json");
11194
- const projectMcpPath = join21(projectDir, ".mcp.json");
11910
+ const projectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
11911
+ mkdirSync10(agentProvisionDir, { recursive: true });
11912
+ mkdirSync10(projectDir, { recursive: true });
11913
+ const provisionMcpPath = join23(agentProvisionDir, ".mcp.json");
11914
+ const projectMcpPath = join23(projectDir, ".mcp.json");
11195
11915
  let mcpConfig = { mcpServers: {} };
11196
11916
  try {
11197
- mcpConfig = JSON.parse(readFileSync18(provisionMcpPath, "utf-8"));
11917
+ mcpConfig = JSON.parse(readFileSync19(provisionMcpPath, "utf-8"));
11198
11918
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
11199
11919
  } catch {
11200
11920
  }
11201
- const localDirectChatChannel = join21(homedir11(), ".augmented", "_mcp", "direct-chat-channel.js");
11921
+ const localDirectChatChannel = join23(homedir12(), ".augmented", "_mcp", "direct-chat-channel.js");
11202
11922
  const directChatTeamSettings = refreshData.team?.settings;
11203
11923
  const directChatTz = (() => {
11204
11924
  const tz = directChatTeamSettings?.["timezone"];
11205
11925
  return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
11206
11926
  })();
11207
- if (existsSync12(localDirectChatChannel)) {
11927
+ if (existsSync13(localDirectChatChannel)) {
11208
11928
  const directChatEnv = {
11209
11929
  AGT_HOST: requireHost(),
11210
11930
  // ENG-5901 Track D: templated — the manager exports the real
@@ -11224,7 +11944,7 @@ async function processAgent(agent, agentStates) {
11224
11944
  // ~/.augmented/<codeName>/.current-turn-initiator.json. Note getAgentDir
11225
11945
  // returns the agent root (NOT the /provision subdir `agentDir` points at),
11226
11946
  // so it byte-matches the broker readers' path.
11227
- AGT_TURN_INITIATOR_FILE: join21(
11947
+ AGT_TURN_INITIATOR_FILE: join23(
11228
11948
  frameworkAdapter.getAgentDir(agent.code_name),
11229
11949
  ".current-turn-initiator.json"
11230
11950
  )
@@ -11244,10 +11964,10 @@ async function processAgent(agent, agentStates) {
11244
11964
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
11245
11965
  }
11246
11966
  }
11247
- const staleChannelsPath = join21(projectDir, ".mcp-channels.json");
11248
- if (existsSync12(staleChannelsPath)) {
11967
+ const staleChannelsPath = join23(projectDir, ".mcp-channels.json");
11968
+ if (existsSync13(staleChannelsPath)) {
11249
11969
  try {
11250
- rmSync4(staleChannelsPath, { force: true });
11970
+ rmSync5(staleChannelsPath, { force: true });
11251
11971
  } catch {
11252
11972
  }
11253
11973
  }
@@ -11334,7 +12054,7 @@ async function processAgent(agent, agentStates) {
11334
12054
  }
11335
12055
  if (hostFlagStore().getBoolean("connectivity-probe")) {
11336
12056
  try {
11337
- const probeProjectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
12057
+ const probeProjectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
11338
12058
  await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
11339
12059
  } catch (err) {
11340
12060
  log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
@@ -11345,7 +12065,7 @@ async function processAgent(agent, agentStates) {
11345
12065
  const forceDue = attemptsLeft > 0;
11346
12066
  let probeRan = false;
11347
12067
  try {
11348
- const probeProjectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
12068
+ const probeProjectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
11349
12069
  probeRan = await runAgentSessionToolBindProbes(agent, integrations, probeProjectDir, { forceDue });
11350
12070
  } catch (err) {
11351
12071
  log(`Session-tool-bind probe failed for '${agent.code_name}': ${err.message}`);
@@ -11358,9 +12078,9 @@ async function processAgent(agent, agentStates) {
11358
12078
  }
11359
12079
  if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
11360
12080
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
11361
- if (mcpPath && existsSync12(mcpPath)) {
12081
+ if (mcpPath && existsSync13(mcpPath)) {
11362
12082
  try {
11363
- const cfg = JSON.parse(readFileSync18(mcpPath, "utf-8"));
12083
+ const cfg = JSON.parse(readFileSync19(mcpPath, "utf-8"));
11364
12084
  const expectedRemoteKeys = new Set(
11365
12085
  integrations.map((i) => i.definition_id)
11366
12086
  );
@@ -11385,11 +12105,11 @@ async function processAgent(agent, agentStates) {
11385
12105
  recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
11386
12106
  }
11387
12107
  if (intHash !== prevIntHash) {
11388
- const projectDir = join21(homedir11(), ".augmented", agent.code_name, "project");
11389
- const envIntPath = join21(projectDir, ".env.integrations");
12108
+ const projectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
12109
+ const envIntPath = join23(projectDir, ".env.integrations");
11390
12110
  let preWriteEnv;
11391
12111
  try {
11392
- preWriteEnv = readFileSync18(envIntPath, "utf-8");
12112
+ preWriteEnv = readFileSync19(envIntPath, "utf-8");
11393
12113
  } catch {
11394
12114
  preWriteEnv = void 0;
11395
12115
  }
@@ -11401,9 +12121,9 @@ async function processAgent(agent, agentStates) {
11401
12121
  let rotationHandled = true;
11402
12122
  if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
11403
12123
  try {
11404
- const projectMcpPath = join21(projectDir, ".mcp.json");
11405
- const postWriteEnv = readFileSync18(envIntPath, "utf-8");
11406
- const mcpContent = readFileSync18(projectMcpPath, "utf-8");
12124
+ const projectMcpPath = join23(projectDir, ".mcp.json");
12125
+ const postWriteEnv = readFileSync19(envIntPath, "utf-8");
12126
+ const mcpContent = readFileSync19(projectMcpPath, "utf-8");
11407
12127
  const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
11408
12128
  const mcpJsonForReap = JSON.parse(mcpContent);
11409
12129
  const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
@@ -11480,10 +12200,10 @@ async function processAgent(agent, agentStates) {
11480
12200
  desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
11481
12201
  }
11482
12202
  const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
11483
- const headersHash = createHash13("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
12203
+ const headersHash = createHash14("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
11484
12204
  return `${e.serverId}|${e.url}|${headersHash}`;
11485
12205
  }).join("\n");
11486
- const mcpHash = createHash13("sha256").update(hashBasis).digest("hex").slice(0, 16);
12206
+ const mcpHash = createHash14("sha256").update(hashBasis).digest("hex").slice(0, 16);
11487
12207
  const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
11488
12208
  const structureHash = managedMcpStructureHash(desiredEntries);
11489
12209
  const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
@@ -11498,15 +12218,15 @@ async function processAgent(agent, agentStates) {
11498
12218
  if (mcpHash !== prevMcpHash) {
11499
12219
  for (const e of desiredEntries) {
11500
12220
  frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
11501
- const urlHash = createHash13("sha256").update(e.url).digest("hex").slice(0, 12);
12221
+ const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
11502
12222
  log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
11503
12223
  }
11504
12224
  if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
11505
12225
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
11506
12226
  if (mcpPath) {
11507
12227
  try {
11508
- const { readFileSync: readFileSync19 } = await import("fs");
11509
- const mcpConfig = JSON.parse(readFileSync19(mcpPath, "utf-8"));
12228
+ const { readFileSync: readFileSync20 } = await import("fs");
12229
+ const mcpConfig = JSON.parse(readFileSync20(mcpPath, "utf-8"));
11510
12230
  if (mcpConfig.mcpServers) {
11511
12231
  for (const key of Object.keys(mcpConfig.mcpServers)) {
11512
12232
  if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
@@ -11607,7 +12327,7 @@ async function processAgent(agent, agentStates) {
11607
12327
  if (frameworkAdapter.installSkillFiles) {
11608
12328
  const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
11609
12329
  const installedIntegrationSkills = [];
11610
- const { createHash: createHash14 } = await import("crypto");
12330
+ const { createHash: createHash15 } = await import("crypto");
11611
12331
  const refreshAny = refreshData;
11612
12332
  const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
11613
12333
  const contextBySlug = /* @__PURE__ */ new Map();
@@ -11636,7 +12356,7 @@ async function processAgent(agent, agentStates) {
11636
12356
  )
11637
12357
  }));
11638
12358
  const bundle = buildIntegrationBundle(renderedScopes);
11639
- const contentHash = createHash14("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
12359
+ const contentHash = createHash15("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
11640
12360
  const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
11641
12361
  if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
11642
12362
  frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
@@ -11648,19 +12368,19 @@ async function processAgent(agent, agentStates) {
11648
12368
  }
11649
12369
  }
11650
12370
  try {
11651
- const { readdirSync: readdirSync6, rmSync: rmSync5 } = await import("fs");
11652
- const { homedir: homedir12 } = await import("os");
12371
+ const { readdirSync: readdirSync6, rmSync: rmSync6 } = await import("fs");
12372
+ const { homedir: homedir13 } = await import("os");
11653
12373
  const frameworkId2 = frameworkAdapter.id;
11654
12374
  const candidateSkillDirs = [
11655
12375
  // Claude Code — framework runtime tree
11656
- join21(homedir12(), ".augmented", agent.code_name, "skills"),
12376
+ join23(homedir13(), ".augmented", agent.code_name, "skills"),
11657
12377
  // Claude Code — project tree
11658
- join21(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
12378
+ join23(homedir13(), ".augmented", agent.code_name, "project", ".claude", "skills"),
11659
12379
  // Defensive: legacy provision-side path, not currently an
11660
12380
  // install target but cheap to sweep.
11661
- join21(agentDir, ".claude", "skills")
12381
+ join23(agentDir, ".claude", "skills")
11662
12382
  ];
11663
- const existingDirs = candidateSkillDirs.filter((d) => existsSync12(d));
12383
+ const existingDirs = candidateSkillDirs.filter((d) => existsSync13(d));
11664
12384
  const discoveredEntries = /* @__PURE__ */ new Set();
11665
12385
  for (const dir of existingDirs) {
11666
12386
  try {
@@ -11674,9 +12394,9 @@ async function processAgent(agent, agentStates) {
11674
12394
  }
11675
12395
  const removeSkillFolder = (entry, reason) => {
11676
12396
  for (const dir of existingDirs) {
11677
- const p = join21(dir, entry);
11678
- if (existsSync12(p)) {
11679
- rmSync5(p, { recursive: true, force: true });
12397
+ const p = join23(dir, entry);
12398
+ if (existsSync13(p)) {
12399
+ rmSync6(p, { recursive: true, force: true });
11680
12400
  }
11681
12401
  }
11682
12402
  log(`Removed ${reason} '${entry}' for '${agent.code_name}' (framework=${frameworkId2})`);
@@ -11694,7 +12414,7 @@ async function processAgent(agent, agentStates) {
11694
12414
  const sharedSkillsPayload = refreshAny.shared_skills;
11695
12415
  const desiredResolved = globalSkillsPayload !== void 0 || sharedSkillsPayload !== void 0;
11696
12416
  const manifestPath = managedSkillManifestPath(
11697
- join21(homedir11(), ".augmented", agent.code_name)
12417
+ join23(homedir12(), ".augmented", agent.code_name)
11698
12418
  );
11699
12419
  const prevIds = /* @__PURE__ */ new Set([
11700
12420
  ...agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
@@ -11703,7 +12423,7 @@ async function processAgent(agent, agentStates) {
11703
12423
  const plan = planGlobalSkillSync(
11704
12424
  [...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
11705
12425
  prevIds,
11706
- (content) => createHash14("sha256").update(content).digest("hex").slice(0, 12),
12426
+ (content) => createHash15("sha256").update(content).digest("hex").slice(0, 12),
11707
12427
  (skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
11708
12428
  { desiredResolved }
11709
12429
  );
@@ -11714,16 +12434,16 @@ async function processAgent(agent, agentStates) {
11714
12434
  }
11715
12435
  if (plan.removes.length) {
11716
12436
  const globalSkillDirs = [
11717
- join21(homedir11(), ".augmented", agent.code_name, "skills"),
11718
- join21(homedir11(), ".augmented", agent.code_name, "project", ".claude", "skills"),
11719
- join21(agentDir, ".claude", "skills")
12437
+ join23(homedir12(), ".augmented", agent.code_name, "skills"),
12438
+ join23(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
12439
+ join23(agentDir, ".claude", "skills")
11720
12440
  ];
11721
12441
  for (const id of plan.removes) {
11722
12442
  let prunedAny = false;
11723
12443
  for (const dir of globalSkillDirs) {
11724
- const p = join21(dir, id);
11725
- if (existsSync12(p) && existsSync12(join21(p, "SKILL.md"))) {
11726
- rmSync4(p, { recursive: true, force: true });
12444
+ const p = join23(dir, id);
12445
+ if (existsSync13(p) && existsSync13(join23(p, "SKILL.md"))) {
12446
+ rmSync5(p, { recursive: true, force: true });
11727
12447
  prunedAny = true;
11728
12448
  }
11729
12449
  }
@@ -11754,7 +12474,7 @@ async function processAgent(agent, agentStates) {
11754
12474
  const slug = hook.integration_slug ?? hook.plugin_slug;
11755
12475
  if (!slug) continue;
11756
12476
  try {
11757
- const scriptHash = createHash14("sha256").update(hook.script).digest("hex").slice(0, 12);
12477
+ const scriptHash = createHash15("sha256").update(hook.script).digest("hex").slice(0, 12);
11758
12478
  const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
11759
12479
  if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
11760
12480
  const result = await frameworkAdapter.executePluginHook({
@@ -11769,9 +12489,9 @@ async function processAgent(agent, agentStates) {
11769
12489
  } else if (result.timedOut) {
11770
12490
  log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
11771
12491
  } else {
11772
- const stderrHash = createHash14("sha256").update(result.stderr).digest("hex").slice(0, 12);
12492
+ const stderrHash = createHash15("sha256").update(result.stderr).digest("hex").slice(0, 12);
11773
12493
  const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
11774
- const missingCmdHash = missingCmd ? createHash14("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
12494
+ const missingCmdHash = missingCmd ? createHash15("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
11775
12495
  log(
11776
12496
  `Integration hook on_install '${slug}' exited ${result.exitCode} for '${agent.code_name}' ` + (missingCmdHash ? `[missing_command_hash=${missingCmdHash}] ` : "") + `[stderr_hash=${stderrHash} stderr_len=${result.stderr.length}]`
11777
12497
  );
@@ -11928,8 +12648,8 @@ async function processAgent(agent, agentStates) {
11928
12648
  const sess = getSessionState(agent.code_name);
11929
12649
  let mcpJsonParsed = null;
11930
12650
  try {
11931
- const mcpPath = join21(getProjectDir(agent.code_name), ".mcp.json");
11932
- mcpJsonParsed = JSON.parse(readFileSync18(mcpPath, "utf-8"));
12651
+ const mcpPath = join23(getProjectDir(agent.code_name), ".mcp.json");
12652
+ mcpJsonParsed = JSON.parse(readFileSync19(mcpPath, "utf-8"));
11933
12653
  } catch {
11934
12654
  }
11935
12655
  reapMissingMcpSessions({
@@ -12258,10 +12978,10 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
12258
12978
  }
12259
12979
  }
12260
12980
  const trackedFiles = frameworkAdapter.driftTrackedFiles();
12261
- if (trackedFiles.length > 0 && existsSync12(agentDir)) {
12981
+ if (trackedFiles.length > 0 && existsSync13(agentDir)) {
12262
12982
  const hashes = /* @__PURE__ */ new Map();
12263
12983
  for (const file of trackedFiles) {
12264
- const h = hashFile(join21(agentDir, file));
12984
+ const h = hashFile(join23(agentDir, file));
12265
12985
  if (h) hashes.set(file, h);
12266
12986
  }
12267
12987
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -12276,7 +12996,7 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
12276
12996
  refreshData.agent.onboarding_state
12277
12997
  );
12278
12998
  const obStep = obState.step;
12279
- const markerPath = join21(homedir11(), ".augmented", agent.code_name, "onboarding-drive.json");
12999
+ const markerPath = join23(homedir12(), ".augmented", agent.code_name, "onboarding-drive.json");
12280
13000
  const marker = readOnboardingDriveMarker(markerPath);
12281
13001
  const decision = decideOnboardingDrive(obStep, marker, Date.now(), obState.generation ?? 0);
12282
13002
  if (decision.clearMarker) {
@@ -12341,10 +13061,12 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
12341
13061
  const codeName = agent.code_name;
12342
13062
  if (isOpencodeSessionHealthy(codeName)) {
12343
13063
  await ensureOpencodeSlackIngest({ codeName, agentId: agent.agent_id, log });
13064
+ await ensureOpencodeTelegramIngest({ codeName, agentId: agent.agent_id, log });
12344
13065
  return { decision: "healthy", spawnAttempted: false, sessionHealthyAfter: true };
12345
13066
  }
12346
13067
  stopOpencodeSlackIngest(codeName, log);
12347
- const opencodeProjectDir = join21(getFramework("opencode").getAgentDir(codeName), "provision");
13068
+ stopOpencodeTelegramIngest(codeName, log);
13069
+ const opencodeProjectDir = join23(getFramework("opencode").getAgentDir(codeName), "provision");
12348
13070
  const openRouterRaw = refreshData.openrouter ?? null;
12349
13071
  const serveEnv = {
12350
13072
  AGT_HOST: requireHost(),
@@ -12384,8 +13106,8 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
12384
13106
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
12385
13107
  const codeName = agent.code_name;
12386
13108
  const projectDir = getProjectDir(codeName);
12387
- const mcpConfigPath = join21(projectDir, ".mcp.json");
12388
- const claudeMdPath = join21(projectDir, "CLAUDE.md");
13109
+ const mcpConfigPath = join23(projectDir, ".mcp.json");
13110
+ const claudeMdPath = join23(projectDir, "CLAUDE.md");
12389
13111
  if (restartBreaker.isTripped(codeName)) {
12390
13112
  const trip = restartBreaker.getTrip(codeName);
12391
13113
  return {
@@ -12543,7 +13265,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
12543
13265
  const ctx = getLastFailureContext(codeName);
12544
13266
  const recovery = prepareForRespawn(codeName);
12545
13267
  const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
12546
- ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
13268
+ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
12547
13269
  const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
12548
13270
  const recoverySummary = recovery ? `; recovery=${recovery}` : "";
12549
13271
  log(
@@ -12557,7 +13279,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
12557
13279
  );
12558
13280
  getHostId().then((hostId) => {
12559
13281
  if (!hostId) return;
12560
- const paneTailHash = zombie.paneTail ? `sha256:${createHash13("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
13282
+ const paneTailHash = zombie.paneTail ? `sha256:${createHash14("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
12561
13283
  return api.post("/host/events", {
12562
13284
  host_id: hostId,
12563
13285
  agent_code_name: codeName,
@@ -12706,7 +13428,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
12706
13428
  if (!claudeAuthTupleBySession.has(codeName)) {
12707
13429
  claudeAuthTupleBySession.set(codeName, currentAuthTuple);
12708
13430
  }
12709
- const stableTasksHash = createHash13("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
13431
+ const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
12710
13432
  const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
12711
13433
  if (stableTasksHash !== prevHash) {
12712
13434
  const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
@@ -12923,7 +13645,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
12923
13645
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
12924
13646
  void (async () => {
12925
13647
  try {
12926
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
13648
+ const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
12927
13649
  await api.post("/host/heartbeat", {
12928
13650
  host_id: hostId,
12929
13651
  agent_diagnostics: collectDiagnostics([codeName])
@@ -12973,7 +13695,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
12973
13695
  }
12974
13696
  try {
12975
13697
  const hostId = await getHostId();
12976
- const { collectDiagnostics } = await import("../persistent-session-XWJSAZB4.js");
13698
+ const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
12977
13699
  await api.post("/host/heartbeat", {
12978
13700
  host_id: hostId,
12979
13701
  agent_diagnostics: collectDiagnostics([codeName])
@@ -13258,9 +13980,9 @@ async function processDirectChatMessage(agent, msg) {
13258
13980
  const useDoorbell = hostFlagStore().getBoolean("direct-chat-doorbell") || isolationMode(agent.codeName) === "docker";
13259
13981
  if (useDoorbell) {
13260
13982
  try {
13261
- const doorbell = directChatDoorbellPath(agent.agentId, homedir11());
13262
- mkdirSync8(dirname6(doorbell), { recursive: true });
13263
- writeFileSync9(doorbell, String(Date.now()));
13983
+ const doorbell = directChatDoorbellPath(agent.agentId, homedir12());
13984
+ mkdirSync10(dirname6(doorbell), { recursive: true });
13985
+ writeFileSync11(doorbell, String(Date.now()));
13264
13986
  log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
13265
13987
  return;
13266
13988
  } catch (err) {
@@ -13368,9 +14090,9 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
13368
14090
  }
13369
14091
  if (run_id) openInjectedRunByCode.set(codeName, run_id);
13370
14092
  try {
13371
- const doorbell = directChatDoorbellPath(agentId, homedir11());
13372
- mkdirSync8(dirname6(doorbell), { recursive: true });
13373
- writeFileSync9(doorbell, String(Date.now()));
14093
+ const doorbell = directChatDoorbellPath(agentId, homedir12());
14094
+ mkdirSync10(dirname6(doorbell), { recursive: true });
14095
+ writeFileSync11(doorbell, String(Date.now()));
13374
14096
  } catch (err) {
13375
14097
  log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
13376
14098
  }
@@ -13471,7 +14193,7 @@ async function processClaudePairSessions(agents) {
13471
14193
  killPairSession,
13472
14194
  pairTmuxSession,
13473
14195
  finalizeClaudePairOnboarding
13474
- } = await import("../claude-pair-runtime-NUZWSILK.js");
14196
+ } = await import("../claude-pair-runtime-2ZQZVFKF.js");
13475
14197
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
13476
14198
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
13477
14199
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -13721,8 +14443,8 @@ function parseMemoryFile(raw, fallbackName) {
13721
14443
  };
13722
14444
  }
13723
14445
  async function syncMemories(agent, configDir, log2) {
13724
- const projectDir = join21(configDir, agent.code_name, "project");
13725
- const memoryDir = join21(projectDir, "memory");
14446
+ const projectDir = join23(configDir, agent.code_name, "project");
14447
+ const memoryDir = join23(projectDir, "memory");
13726
14448
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
13727
14449
  if (isFreshSync) {
13728
14450
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -13733,15 +14455,15 @@ async function syncMemories(agent, configDir, log2) {
13733
14455
  }
13734
14456
  pendingFreshMemorySync.delete(agent.agent_id);
13735
14457
  }
13736
- if (existsSync12(memoryDir)) {
14458
+ if (existsSync13(memoryDir)) {
13737
14459
  const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
13738
14460
  const currentHashes = /* @__PURE__ */ new Map();
13739
14461
  const changedMemories = [];
13740
14462
  for (const file of readdirSync5(memoryDir)) {
13741
14463
  if (!file.endsWith(".md")) continue;
13742
14464
  try {
13743
- const raw = readFileSync18(join21(memoryDir, file), "utf-8");
13744
- const fileHash = createHash13("sha256").update(raw).digest("hex").slice(0, 16);
14465
+ const raw = readFileSync19(join23(memoryDir, file), "utf-8");
14466
+ const fileHash = createHash14("sha256").update(raw).digest("hex").slice(0, 16);
13745
14467
  currentHashes.set(file, fileHash);
13746
14468
  if (prevHashes.get(file) === fileHash) continue;
13747
14469
  const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
@@ -13765,7 +14487,7 @@ async function syncMemories(agent, configDir, log2) {
13765
14487
  } catch (err) {
13766
14488
  for (const mem of changedMemories) {
13767
14489
  for (const [file] of currentHashes) {
13768
- const parsed = parseMemoryFile(readFileSync18(join21(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
14490
+ const parsed = parseMemoryFile(readFileSync19(join23(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
13769
14491
  if (parsed?.name === mem.name) currentHashes.delete(file);
13770
14492
  }
13771
14493
  }
@@ -13778,29 +14500,29 @@ async function syncMemories(agent, configDir, log2) {
13778
14500
  }
13779
14501
  }
13780
14502
  async function downloadMemories(agent, memoryDir, log2, { force }) {
13781
- const localFiles = existsSync12(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
13782
- const localListHash = createHash13("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
14503
+ const localFiles = existsSync13(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
14504
+ const localListHash = createHash14("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
13783
14505
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
13784
14506
  const prevDownload = lastDownloadHash.get(agent.agent_id);
13785
14507
  try {
13786
14508
  const dbMemories = await api.post("/host/memories", {
13787
14509
  agent_id: agent.agent_id
13788
14510
  });
13789
- const responseHash = createHash13("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
14511
+ const responseHash = createHash14("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
13790
14512
  if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
13791
14513
  return true;
13792
14514
  }
13793
14515
  lastDownloadHash.set(agent.agent_id, responseHash);
13794
14516
  lastLocalFileHash.set(agent.agent_id, localListHash);
13795
14517
  if (dbMemories.memories?.length) {
13796
- mkdirSync8(memoryDir, { recursive: true });
14518
+ mkdirSync10(memoryDir, { recursive: true });
13797
14519
  let written = 0;
13798
14520
  let overwritten = 0;
13799
14521
  for (let i = 0; i < dbMemories.memories.length; i++) {
13800
14522
  const mem = dbMemories.memories[i];
13801
14523
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
13802
14524
  const slug = rawSlug || `memory-${i}`;
13803
- const filePath = join21(memoryDir, `${slug}.md`);
14525
+ const filePath = join23(memoryDir, `${slug}.md`);
13804
14526
  const desired = `---
13805
14527
  name: ${JSON.stringify(mem.name)}
13806
14528
  type: ${mem.type}
@@ -13809,23 +14531,23 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
13809
14531
 
13810
14532
  ${mem.content}
13811
14533
  `;
13812
- if (existsSync12(filePath)) {
14534
+ if (existsSync13(filePath)) {
13813
14535
  let existing = "";
13814
14536
  try {
13815
- existing = readFileSync18(filePath, "utf-8");
14537
+ existing = readFileSync19(filePath, "utf-8");
13816
14538
  } catch {
13817
14539
  }
13818
14540
  if (existing === desired) continue;
13819
- writeFileSync9(filePath, desired);
14541
+ writeFileSync11(filePath, desired);
13820
14542
  overwritten++;
13821
14543
  } else {
13822
- writeFileSync9(filePath, desired);
14544
+ writeFileSync11(filePath, desired);
13823
14545
  written++;
13824
14546
  }
13825
14547
  }
13826
14548
  if (written > 0 || overwritten > 0) {
13827
14549
  const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
13828
- lastLocalFileHash.set(agent.agent_id, createHash13("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
14550
+ lastLocalFileHash.set(agent.agent_id, createHash14("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
13829
14551
  log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
13830
14552
  }
13831
14553
  }
@@ -13836,9 +14558,9 @@ ${mem.content}
13836
14558
  }
13837
14559
  }
13838
14560
  async function cleanupAgentFiles(codeName, agentDir) {
13839
- if (existsSync12(agentDir)) {
14561
+ if (existsSync13(agentDir)) {
13840
14562
  try {
13841
- rmSync4(agentDir, { recursive: true, force: true });
14563
+ rmSync5(agentDir, { recursive: true, force: true });
13842
14564
  log(`Removed provision directory for '${codeName}'`);
13843
14565
  } catch (err) {
13844
14566
  log(`Failed to remove provision dir for '${codeName}': ${err.message}`);
@@ -14058,6 +14780,7 @@ async function stopPolling(opts = {}) {
14058
14780
  stopCaffeinate();
14059
14781
  stopRealtimeChat();
14060
14782
  stopAllOpencodeSlackIngests(log);
14783
+ stopAllOpencodeTelegramIngests(log);
14061
14784
  log(formatDrainShutdownLine({ step: "stop-subsystems", elapsedMs: Date.now() - subsysStartedAt }));
14062
14785
  for (const codeName of [...scheduledRunsByCode.keys()]) {
14063
14786
  closeScheduledRunsForCode(codeName, "cancelled", "manager shutdown");
@@ -14072,8 +14795,8 @@ function startManager(opts) {
14072
14795
  config = opts;
14073
14796
  try {
14074
14797
  const stateFile = getStateFile();
14075
- if (existsSync12(stateFile)) {
14076
- const raw = readFileSync18(stateFile, "utf-8");
14798
+ if (existsSync13(stateFile)) {
14799
+ const raw = readFileSync19(stateFile, "utf-8");
14077
14800
  const parsed = JSON.parse(raw);
14078
14801
  if (Array.isArray(parsed.agents)) {
14079
14802
  state6.agents = parsed.agents;
@@ -14100,7 +14823,7 @@ function startManager(opts) {
14100
14823
  log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
14101
14824
  }
14102
14825
  log(
14103
- `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join21(homedir11(), ".augmented", "manager.log")}`
14826
+ `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join23(homedir12(), ".augmented", "manager.log")}`
14104
14827
  );
14105
14828
  deployMcpAssets();
14106
14829
  reapOrphanChannelMcps({ log });
@@ -14121,7 +14844,7 @@ async function reapOrphanedClaudePids() {
14121
14844
  const looksLikeClaude = (pid) => {
14122
14845
  if (process.platform !== "linux") return true;
14123
14846
  try {
14124
- const comm = readFileSync18(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
14847
+ const comm = readFileSync19(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
14125
14848
  return comm.includes("claude");
14126
14849
  } catch {
14127
14850
  return false;
@@ -14218,14 +14941,14 @@ function restartRunningChannelMcps(basenames) {
14218
14941
  }
14219
14942
  }
14220
14943
  function deployMcpAssets() {
14221
- const targetDir = join21(homedir11(), ".augmented", "_mcp");
14222
- mkdirSync8(targetDir, { recursive: true });
14944
+ const targetDir = join23(homedir12(), ".augmented", "_mcp");
14945
+ mkdirSync10(targetDir, { recursive: true });
14223
14946
  const moduleDir = dirname6(fileURLToPath(import.meta.url));
14224
14947
  let mcpSourceDir = "";
14225
14948
  let dir = moduleDir;
14226
14949
  for (let i = 0; i < 6; i++) {
14227
- const candidate = join21(dir, "dist", "mcp");
14228
- if (existsSync12(join21(candidate, "index.js"))) {
14950
+ const candidate = join23(dir, "dist", "mcp");
14951
+ if (existsSync13(join23(candidate, "index.js"))) {
14229
14952
  mcpSourceDir = candidate;
14230
14953
  break;
14231
14954
  }
@@ -14240,8 +14963,8 @@ function deployMcpAssets() {
14240
14963
  const changedBasenames = [];
14241
14964
  const fileHash = (p) => {
14242
14965
  try {
14243
- if (!existsSync12(p)) return null;
14244
- return createHash13("sha256").update(readFileSync18(p)).digest("hex");
14966
+ if (!existsSync13(p)) return null;
14967
+ return createHash14("sha256").update(readFileSync19(p)).digest("hex");
14245
14968
  } catch {
14246
14969
  return null;
14247
14970
  }
@@ -14305,9 +15028,9 @@ function deployMcpAssets() {
14305
15028
  // needs restarting to pick up a token rotation.
14306
15029
  "xero.js"
14307
15030
  ]) {
14308
- const src = join21(mcpSourceDir, file);
14309
- const dst = join21(targetDir, file);
14310
- if (!existsSync12(src)) continue;
15031
+ const src = join23(mcpSourceDir, file);
15032
+ const dst = join23(targetDir, file);
15033
+ if (!existsSync13(src)) continue;
14311
15034
  const before = fileHash(dst);
14312
15035
  try {
14313
15036
  copyFileSync(src, dst);
@@ -14324,23 +15047,23 @@ function deployMcpAssets() {
14324
15047
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
14325
15048
  restartRunningChannelMcps(changedBasenames);
14326
15049
  }
14327
- const localMcpPath = join21(targetDir, "index.js");
15050
+ const localMcpPath = join23(targetDir, "index.js");
14328
15051
  try {
14329
- const agentsDir = join21(homedir11(), ".augmented", "agents");
14330
- if (existsSync12(agentsDir)) {
15052
+ const agentsDir = join23(homedir12(), ".augmented", "agents");
15053
+ if (existsSync13(agentsDir)) {
14331
15054
  for (const entry of readdirSync5(agentsDir, { withFileTypes: true })) {
14332
15055
  if (!entry.isDirectory()) continue;
14333
15056
  for (const subdir of ["provision", "project"]) {
14334
- const mcpJsonPath = join21(agentsDir, entry.name, subdir, ".mcp.json");
15057
+ const mcpJsonPath = join23(agentsDir, entry.name, subdir, ".mcp.json");
14335
15058
  try {
14336
- const raw = readFileSync18(mcpJsonPath, "utf-8");
15059
+ const raw = readFileSync19(mcpJsonPath, "utf-8");
14337
15060
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
14338
15061
  const mcpConfig = JSON.parse(raw);
14339
15062
  const augServer = mcpConfig.mcpServers?.["augmented"];
14340
15063
  if (!augServer) continue;
14341
15064
  augServer.command = "node";
14342
15065
  augServer.args = [localMcpPath];
14343
- writeFileSync9(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
15066
+ writeFileSync11(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
14344
15067
  log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
14345
15068
  } catch {
14346
15069
  }