omnius 1.0.67 → 1.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -588820,10 +588820,10 @@ async function handleSlashCommand(input, ctx3) {
588820
588820
  let key = process.env["OMNIUS_API_KEY"] || "";
588821
588821
  if (!key) {
588822
588822
  try {
588823
- const { homedir: homedir52 } = await import("node:os");
588823
+ const { homedir: homedir53 } = await import("node:os");
588824
588824
  const { readFileSync: readFileSync107, existsSync: existsSync131 } = await import("node:fs");
588825
588825
  const { join: join148 } = await import("node:path");
588826
- const p2 = join148(homedir52(), ".omnius", "api.key");
588826
+ const p2 = join148(homedir53(), ".omnius", "api.key");
588827
588827
  if (existsSync131(p2)) key = readFileSync107(p2, "utf8").trim();
588828
588828
  } catch {
588829
588829
  }
@@ -588866,12 +588866,12 @@ async function handleSlashCommand(input, ctx3) {
588866
588866
  if (action === "new") {
588867
588867
  try {
588868
588868
  const { randomBytes: randomBytes27 } = await import("node:crypto");
588869
- const { homedir: homedir52 } = await import("node:os");
588869
+ const { homedir: homedir53 } = await import("node:os");
588870
588870
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
588871
588871
  const { join: join148 } = await import("node:path");
588872
588872
  const newKey = randomBytes27(16).toString("hex");
588873
588873
  process.env["OMNIUS_API_KEY"] = newKey;
588874
- const dir = join148(homedir52(), ".omnius");
588874
+ const dir = join148(homedir53(), ".omnius");
588875
588875
  mkdirSync81(dir, { recursive: true });
588876
588876
  writeFileSync73(join148(dir, "api.key"), newKey + "\n", "utf8");
588877
588877
  renderInfo(`New API key: ${c3.bold(c3.yellow(newKey))}`);
@@ -589137,10 +589137,10 @@ async function handleSlashCommand(input, ctx3) {
589137
589137
  "Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client."
589138
589138
  );
589139
589139
  try {
589140
- const { homedir: homedir53 } = await import("node:os");
589140
+ const { homedir: homedir54 } = await import("node:os");
589141
589141
  const { mkdirSync: mkdirSync82, writeFileSync: writeFileSync74 } = await import("node:fs");
589142
589142
  const { join: join149 } = await import("node:path");
589143
- const dir = join149(homedir53(), ".omnius");
589143
+ const dir = join149(homedir54(), ".omnius");
589144
589144
  mkdirSync82(dir, { recursive: true });
589145
589145
  writeFileSync74(join149(dir, "api.key"), apiKey + "\n", "utf8");
589146
589146
  } catch {
@@ -589153,10 +589153,10 @@ async function handleSlashCommand(input, ctx3) {
589153
589153
  }
589154
589154
  const port2 = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
589155
589155
  try {
589156
- const { homedir: homedir53 } = await import("node:os");
589156
+ const { homedir: homedir54 } = await import("node:os");
589157
589157
  const { mkdirSync: mkdirSync82, writeFileSync: writeFileSync74 } = await import("node:fs");
589158
589158
  const { join: join149 } = await import("node:path");
589159
- const dir = join149(homedir53(), ".omnius");
589159
+ const dir = join149(homedir54(), ".omnius");
589160
589160
  mkdirSync82(dir, { recursive: true });
589161
589161
  writeFileSync74(join149(dir, "access"), `${val2}
589162
589162
  `, "utf8");
@@ -589257,10 +589257,10 @@ async function handleSlashCommand(input, ctx3) {
589257
589257
  "Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client."
589258
589258
  );
589259
589259
  try {
589260
- const { homedir: homedir53 } = await import("node:os");
589260
+ const { homedir: homedir54 } = await import("node:os");
589261
589261
  const { mkdirSync: mkdirSync82, writeFileSync: writeFileSync74 } = await import("node:fs");
589262
589262
  const { join: join149 } = await import("node:path");
589263
- const dir = join149(homedir53(), ".omnius");
589263
+ const dir = join149(homedir54(), ".omnius");
589264
589264
  mkdirSync82(dir, { recursive: true });
589265
589265
  writeFileSync74(join149(dir, "api.key"), apiKey + "\n", "utf8");
589266
589266
  } catch {
@@ -589272,11 +589272,11 @@ async function handleSlashCommand(input, ctx3) {
589272
589272
  ctx3.saveSettings({ omniusAccess: val });
589273
589273
  }
589274
589274
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
589275
- const { homedir: homedir52 } = await import("node:os");
589275
+ const { homedir: homedir53 } = await import("node:os");
589276
589276
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
589277
589277
  const { join: join148 } = await import("node:path");
589278
589278
  try {
589279
- const dir = join148(homedir52(), ".omnius");
589279
+ const dir = join148(homedir53(), ".omnius");
589280
589280
  mkdirSync81(dir, { recursive: true });
589281
589281
  writeFileSync73(join148(dir, "access"), `${val}
589282
589282
  `, "utf8");
@@ -589699,7 +589699,7 @@ async function handleSlashCommand(input, ctx3) {
589699
589699
  );
589700
589700
  }
589701
589701
  } else if (sub === "name") {
589702
- const { homedir: homedir52 } = __require("node:os");
589702
+ const { homedir: homedir53 } = __require("node:os");
589703
589703
  const {
589704
589704
  existsSync: ex,
589705
589705
  readFileSync: rf,
@@ -589707,7 +589707,7 @@ async function handleSlashCommand(input, ctx3) {
589707
589707
  mkdirSync: mkd
589708
589708
  } = __require("node:fs");
589709
589709
  const namePath = __require("node:path").join(
589710
- homedir52(),
589710
+ homedir53(),
589711
589711
  ".omnius",
589712
589712
  "agent-name"
589713
589713
  );
@@ -592595,9 +592595,9 @@ sleep 1
592595
592595
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
592596
592596
  if (!sponsorName || sponsorName.length < 2) {
592597
592597
  try {
592598
- const { homedir: homedir52 } = __require("os");
592598
+ const { homedir: homedir53 } = __require("os");
592599
592599
  const namePath = __require("path").join(
592600
- homedir52(),
592600
+ homedir53(),
592601
592601
  ".omnius",
592602
592602
  "agent-name"
592603
592603
  );
@@ -593367,6 +593367,21 @@ sleep 1
593367
593367
  ctx3.telegramStatus?.();
593368
593368
  return "handled";
593369
593369
  }
593370
+ if (parts[0] === "revoke" || parts[0] === "deauth" || parts[0] === "deauthorize") {
593371
+ const hasGlobalFlagRevoke = parts.includes("--global");
593372
+ const extra = parts.slice(1).filter((part) => part !== "--global");
593373
+ if (extra.length > 0) {
593374
+ renderWarning("Usage: /telegram revoke [--global]");
593375
+ return "handled";
593376
+ }
593377
+ const scope = hasGlobalFlagRevoke ? "global" : "project";
593378
+ const result = ctx3.telegramRevokeAdmin?.(scope);
593379
+ renderInfo(`Telegram admin revoked from ${c3.bold(result?.scope ?? scope)} settings.`);
593380
+ if (!hasGlobalFlagRevoke) {
593381
+ renderInfo("Use /telegram revoke --global to clear the old shared ~/.omnius admin too.");
593382
+ }
593383
+ return "handled";
593384
+ }
593370
593385
  if (parts[0] === "mode" || parts[0] === "profile") {
593371
593386
  const requested = parts.slice(1).find((part) => !part.startsWith("--"));
593372
593387
  if (!requested) {
@@ -593681,6 +593696,24 @@ sleep 1
593681
593696
  renderInfo("Use /telegram to start.");
593682
593697
  return "handled";
593683
593698
  }
593699
+ if (parts.length === 1 && parts[0] === "--global") {
593700
+ if (ctx3.isTelegramActive?.()) {
593701
+ ctx3.telegramStop?.();
593702
+ return "handled";
593703
+ }
593704
+ const settings = ctx3.getTelegramSettings?.("global") ?? {};
593705
+ if (!settings.key) {
593706
+ renderWarning("No global Telegram bot token configured.");
593707
+ renderInfo("Set one with /telegram --key <bot-token> --global, or use /telegram --key <bot-token> for this project.");
593708
+ return "handled";
593709
+ }
593710
+ try {
593711
+ await ctx3.telegramStart?.(settings.key, settings.admin, "global");
593712
+ } catch (err) {
593713
+ renderError(`Telegram error: ${err instanceof Error ? err.message : String(err)}`);
593714
+ }
593715
+ return "handled";
593716
+ }
593684
593717
  if (!arg) {
593685
593718
  if (ctx3.isTelegramActive?.()) {
593686
593719
  ctx3.telegramStop?.();
@@ -593690,7 +593723,13 @@ sleep 1
593690
593723
  if (!settings.key) {
593691
593724
  renderWarning("No Telegram bot token configured.");
593692
593725
  renderInfo("Set one first: /telegram --key <bot-token>");
593693
- renderInfo("Get a token from @BotFather on Telegram. The token saves project-locally by default when .omnius/ exists.");
593726
+ const globalSettings = ctx3.getTelegramSettings?.("global") ?? {};
593727
+ if (globalSettings.key) {
593728
+ renderInfo("A global Telegram token exists, but it is no longer used implicitly inside project folders.");
593729
+ renderInfo("Use /telegram --global to start the shared bot, or /telegram --key <bot-token> to isolate this folder.");
593730
+ } else {
593731
+ renderInfo("Get a token from @BotFather on Telegram. The token saves project-locally by default when .omnius/ exists.");
593732
+ }
593694
593733
  return "handled";
593695
593734
  }
593696
593735
  try {
@@ -593715,7 +593754,7 @@ sleep 1
593715
593754
  } catch {
593716
593755
  }
593717
593756
  try {
593718
- await ctx3.telegramStart?.(settings.key, settings.admin);
593757
+ await ctx3.telegramStart?.(settings.key, settings.admin, settings.keyScope ?? "project");
593719
593758
  } catch (err) {
593720
593759
  renderError(
593721
593760
  `Telegram error: ${err instanceof Error ? err.message : String(err)}`
@@ -593727,7 +593766,9 @@ sleep 1
593727
593766
  renderInfo("Usage:");
593728
593767
  renderInfo(" /telegram --key <token> Save bot token (project-local when .omnius/ exists)");
593729
593768
  renderInfo(" /telegram --admin <id> Set admin filter (project-local by default)");
593769
+ renderInfo(" /telegram revoke [--global] Revoke saved admin access");
593730
593770
  renderInfo(" /telegram Toggle on/off");
593771
+ renderInfo(" /telegram --global Start/stop legacy shared global token explicitly");
593731
593772
  renderInfo(" /telegram stop Stop bridge");
593732
593773
  renderInfo(" /telegram status Show status");
593733
593774
  renderInfo(" /telegram mode auto|chat|action Set interaction routing profile");
@@ -594610,7 +594651,7 @@ async function showPlatformOnboardingMenu(ctx3, id) {
594610
594651
  if (!settings.key) renderWarning("No Telegram bot token configured.");
594611
594652
  else if (ctx3.isTelegramActive?.()) {
594612
594653
  renderWarning("Telegram bridge already active. Use /telegram stop before restarting.");
594613
- } else await ctx3.telegramStart?.(settings.key, settings.admin);
594654
+ } else await ctx3.telegramStart?.(settings.key, settings.admin, settings.keyScope ?? "project");
594614
594655
  } else if (result.key === "telegram-stop") {
594615
594656
  ctx3.telegramStop?.();
594616
594657
  }
@@ -596760,13 +596801,13 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
596760
596801
  mkdirSync: mkdirSync81,
596761
596802
  existsSync: exists2
596762
596803
  } = await import("node:fs");
596763
- const { homedir: homedir52 } = await import("node:os");
596804
+ const { homedir: homedir53 } = await import("node:os");
596764
596805
  const modelName = basename30(onnxDrop.path, ".onnx").replace(
596765
596806
  /[^a-zA-Z0-9_-]/g,
596766
596807
  "-"
596767
596808
  );
596768
596809
  const destDir = pathJoin(
596769
- homedir52(),
596810
+ homedir53(),
596770
596811
  ".omnius",
596771
596812
  "voice",
596772
596813
  "models",
@@ -600293,12 +600334,12 @@ var init_commands = __esm({
600293
600334
  if (val === "any" && !process.env["OMNIUS_API_KEY"]) {
600294
600335
  try {
600295
600336
  const { randomBytes: randomBytes27 } = await import("node:crypto");
600296
- const { homedir: homedir52 } = await import("node:os");
600337
+ const { homedir: homedir53 } = await import("node:os");
600297
600338
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
600298
600339
  const { join: join148 } = await import("node:path");
600299
600340
  const apiKey = randomBytes27(16).toString("hex");
600300
600341
  process.env["OMNIUS_API_KEY"] = apiKey;
600301
- const dir = join148(homedir52(), ".omnius");
600342
+ const dir = join148(homedir53(), ".omnius");
600302
600343
  mkdirSync81(dir, { recursive: true });
600303
600344
  writeFileSync73(join148(dir, "api.key"), apiKey + "\n", "utf8");
600304
600345
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
@@ -600318,10 +600359,10 @@ var init_commands = __esm({
600318
600359
  }
600319
600360
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
600320
600361
  try {
600321
- const { homedir: homedir52 } = await import("node:os");
600362
+ const { homedir: homedir53 } = await import("node:os");
600322
600363
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
600323
600364
  const { join: join148 } = await import("node:path");
600324
- const dir = join148(homedir52(), ".omnius");
600365
+ const dir = join148(homedir53(), ".omnius");
600325
600366
  mkdirSync81(dir, { recursive: true });
600326
600367
  writeFileSync73(join148(dir, "access"), `${val}
600327
600368
  `, "utf8");
@@ -609530,6 +609571,7 @@ var init_vision_ingress = __esm({
609530
609571
  // packages/cli/src/tui/telegram-bridge.ts
609531
609572
  import { mkdirSync as mkdirSync65, existsSync as existsSync113, unlinkSync as unlinkSync22, readdirSync as readdirSync40, statSync as statSync39, statfsSync as statfsSync5, readFileSync as readFileSync92, writeFileSync as writeFileSync59, appendFileSync as appendFileSync9 } from "node:fs";
609532
609573
  import { join as join127, resolve as resolve43, basename as basename27, relative as relative13, isAbsolute as isAbsolute8, extname as extname16 } from "node:path";
609574
+ import { homedir as homedir40 } from "node:os";
609533
609575
  import { writeFile as writeFileAsync } from "node:fs/promises";
609534
609576
  import { createHash as createHash23, randomBytes as randomBytes22, randomInt } from "node:crypto";
609535
609577
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
@@ -609915,11 +609957,14 @@ function redactTelegramLocalPaths(text) {
609915
609957
  function telegramContextJsonString(text, maxLength = TELEGRAM_CONTEXT_LINE_LIMIT) {
609916
609958
  return JSON.stringify(truncateTelegramContextLine(redactTelegramLocalPaths(text), maxLength));
609917
609959
  }
609960
+ function telegramActorKindLabel(actor) {
609961
+ return actor?.isBot ? "bot" : "human";
609962
+ }
609918
609963
  function telegramSpeakerLabel(msg) {
609919
609964
  if (msg.username && msg.username !== "unknown") return `@${msg.username}`;
609920
609965
  if (msg.firstName) return msg.firstName;
609921
- if (msg.fromUserId) return `user:${msg.fromUserId}`;
609922
- return "Telegram user";
609966
+ if (msg.fromUserId) return msg.isBot ? `bot:${msg.fromUserId}` : `user:${msg.fromUserId}`;
609967
+ return msg.isBot ? "Telegram bot" : "Telegram user";
609923
609968
  }
609924
609969
  function telegramReplySenderLabel(sender) {
609925
609970
  if (!sender) return "unknown sender";
@@ -609941,8 +609986,8 @@ function telegramHistorySpeaker(entry) {
609941
609986
  if (entry.speaker) return entry.speaker;
609942
609987
  if (entry.username) return `@${entry.username}`;
609943
609988
  if (entry.firstName) return entry.firstName;
609944
- if (entry.fromUserId) return `user:${entry.fromUserId}`;
609945
- return "Telegram user";
609989
+ if (entry.fromUserId) return entry.isBot ? `bot:${entry.fromUserId}` : `user:${entry.fromUserId}`;
609990
+ return entry.isBot ? "Telegram bot" : "Telegram user";
609946
609991
  }
609947
609992
  function telegramHistoryTime(entry) {
609948
609993
  if (!entry.ts) return "";
@@ -610049,6 +610094,13 @@ function telegramMemoryTokens(text) {
610049
610094
  }
610050
610095
  return tokens;
610051
610096
  }
610097
+ function telegramMeaningfulMemoryTokens(text) {
610098
+ const tokens = /* @__PURE__ */ new Set();
610099
+ for (const token of telegramMemoryTokens(text)) {
610100
+ if (!TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS.has(token)) tokens.add(token);
610101
+ }
610102
+ return tokens;
610103
+ }
610052
610104
  function telegramMemorySimilarity(a2, b) {
610053
610105
  if (a2.size === 0 || b.size === 0) return 0;
610054
610106
  let intersection = 0;
@@ -610689,6 +610741,7 @@ function normalizeTelegramUpdate(update2) {
610689
610741
  if (!message2 || typeof message2 !== "object") return null;
610690
610742
  const fromId = message2.from?.id ?? message2.sender_chat?.id ?? 0;
610691
610743
  const username = message2.from?.username ?? message2.sender_chat?.username ?? "";
610744
+ const isBot = message2.from?.is_bot === true;
610692
610745
  const chatType = message2.chat?.type ?? "private";
610693
610746
  const media = normalizeTelegramMedia(message2);
610694
610747
  const replyContext = normalizeTelegramReplyContext(message2);
@@ -610703,6 +610756,7 @@ function normalizeTelegramUpdate(update2) {
610703
610756
  firstName: message2.from?.first_name,
610704
610757
  messageId: message2.message_id ?? 0,
610705
610758
  fromUserId: fromId,
610759
+ isBot,
610706
610760
  chatType,
610707
610761
  chatTitle: message2.chat?.title,
610708
610762
  media,
@@ -610794,7 +610848,7 @@ function normalizeTelegramSendKind(rawKind, path12) {
610794
610848
  }
610795
610849
  return classifyMedia(path12) ?? "document";
610796
610850
  }
610797
- function renderTelegramStart(botUsername, adminId, mode = "auto") {
610851
+ function renderTelegramStart(botUsername, adminId, mode = "auto", canReadAllGroupMessages) {
610798
610852
  process.stdout.write(`
610799
610853
  ${c3.cyan("✈")} ${c3.bold("Telegram Bridge")} connected as @${botUsername}
610800
610854
  `);
@@ -610802,6 +610856,15 @@ function renderTelegramStart(botUsername, adminId, mode = "auto") {
610802
610856
  `);
610803
610857
  process.stdout.write(` ${c3.dim("Auto mode uses quick chat for conversational turns and sub-agents for action requests")}
610804
610858
  `);
610859
+ if (canReadAllGroupMessages) {
610860
+ process.stdout.write(` ${c3.dim("Group ingest: all delivered group messages are visible to the bridge")}
610861
+ `);
610862
+ } else {
610863
+ process.stdout.write(` ${c3.yellow("Group ingest: Telegram may only deliver commands/mentions/replies while BotFather privacy mode is enabled")}
610864
+ `);
610865
+ process.stdout.write(` ${c3.dim("Disable privacy with @BotFather /setprivacy for full ambient group memory.")}
610866
+ `);
610867
+ }
610805
610868
  if (adminId) {
610806
610869
  process.stdout.write(` ${c3.dim(`Admin: ${adminId} (full memory + tools)`)}
610807
610870
  `);
@@ -610814,12 +610877,14 @@ function renderTelegramStart(botUsername, adminId, mode = "auto") {
610814
610877
 
610815
610878
  `);
610816
610879
  }
610817
- function renderTelegramStatus(active, botUsername, adminId, activeSubAgents, mode = "auto") {
610880
+ function renderTelegramStatus(active, botUsername, adminId, activeSubAgents, mode = "auto", canReadAllGroupMessages) {
610818
610881
  if (active) {
610819
610882
  process.stdout.write(`
610820
610883
  ${c3.green("●")} Telegram bridge: ${c3.bold("ACTIVE")} (@${botUsername ?? "?"})
610821
610884
  `);
610822
610885
  process.stdout.write(` Mode: ${mode}
610886
+ `);
610887
+ process.stdout.write(` Group ingest: ${canReadAllGroupMessages ? "all delivered group messages" : "privacy-limited by Telegram/BotFather"}
610823
610888
  `);
610824
610889
  if (adminId) {
610825
610890
  process.stdout.write(` Admin: ${adminId}
@@ -610884,7 +610949,7 @@ function renderTelegramSubAgentError(username, error) {
610884
610949
  process.stdout.write(` ${c3.dim("│")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
610885
610950
  `);
610886
610951
  }
610887
- var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
610952
+ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
610888
610953
  var init_telegram_bridge = __esm({
610889
610954
  "packages/cli/src/tui/telegram-bridge.ts"() {
610890
610955
  "use strict";
@@ -611090,6 +611155,9 @@ most ambient chatter. Continue to be selective:
611090
611155
  2. Stay silent for conversation between other people, third-person commentary
611091
611156
  about the bot, status chatter, or questions clearly meant for someone else.
611092
611157
  3. Do not reply just because you know an answer or could add color.
611158
+ 4. Peer bots are labeled as bots in context. Do not reflexively answer bot
611159
+ chatter; only continue bot-to-bot exchanges when the message is clearly
611160
+ addressed to this bot or advances an active bot-mediated task.
611093
611161
 
611094
611162
  If you determine no visible reply should be sent, call task_complete with summary
611095
611163
  "no_reply". Never write "no_reply", "skipping", "no action needed", or a status
@@ -611208,6 +611276,47 @@ External acquisition contract:
611208
611276
  "you",
611209
611277
  "your"
611210
611278
  ]);
611279
+ TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS = /* @__PURE__ */ new Set([
611280
+ "active",
611281
+ "activity",
611282
+ "breakdown",
611283
+ "conversation",
611284
+ "convo",
611285
+ "count",
611286
+ "counts",
611287
+ "earliest",
611288
+ "entries",
611289
+ "entry",
611290
+ "find",
611291
+ "first",
611292
+ "give",
611293
+ "group",
611294
+ "latest",
611295
+ "memory",
611296
+ "memories",
611297
+ "mention",
611298
+ "mentioned",
611299
+ "mentions",
611300
+ "message",
611301
+ "messages",
611302
+ "most",
611303
+ "newest",
611304
+ "oldest",
611305
+ "recent",
611306
+ "recall",
611307
+ "show",
611308
+ "some",
611309
+ "stats",
611310
+ "statistics",
611311
+ "summary",
611312
+ "summarize",
611313
+ "tell",
611314
+ "today",
611315
+ "turn",
611316
+ "turns",
611317
+ "who",
611318
+ "yesterday"
611319
+ ]);
611211
611320
  TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS = {
611212
611321
  bruteForce: false,
611213
611322
  bruteForceMaxCycles: 0,
@@ -611259,6 +611368,7 @@ External acquisition contract:
611259
611368
  active: false,
611260
611369
  botUsername: "",
611261
611370
  supportsGuestQueries: false,
611371
+ canReadAllGroupMessages: false,
611262
611372
  interactionMode: "auto",
611263
611373
  startedAt: "",
611264
611374
  messagesReceived: 0,
@@ -611337,6 +611447,7 @@ External acquisition contract:
611337
611447
  * (which would cause silent 409 conflicts on getUpdates). Released in stop().
611338
611448
  */
611339
611449
  telegramOwnerLockFile = null;
611450
+ telegramOwnerLockFiles = [];
611340
611451
  /** Session keys loaded from persistent conversation memory */
611341
611452
  loadedConversationState = /* @__PURE__ */ new Set();
611342
611453
  /** True once persisted Telegram conversation scopes have been bulk-loaded. */
@@ -611665,7 +611776,7 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
611665
611776
  id: entry.fromUserId,
611666
611777
  username: entry.username,
611667
611778
  firstName: entry.firstName,
611668
- isBot: false
611779
+ isBot: Boolean(entry.isBot)
611669
611780
  };
611670
611781
  return {
611671
611782
  kind: "message",
@@ -611784,7 +611895,7 @@ ${mediaContext}` : ""
611784
611895
  id: String(msg.fromUserId),
611785
611896
  username: msg.username,
611786
611897
  displayName: msg.firstName || msg.username,
611787
- isBot: false
611898
+ isBot: Boolean(msg.isBot)
611788
611899
  };
611789
611900
  }
611790
611901
  telegramMemorySenderFromReply(msg) {
@@ -612017,6 +612128,7 @@ ${mediaContext}` : ""
612017
612128
  messageThreadId: msg.messageThreadId,
612018
612129
  replyToMessageId,
612019
612130
  username: this.state.botUsername || "omnius",
612131
+ isBot: true,
612020
612132
  text
612021
612133
  };
612022
612134
  try {
@@ -612157,7 +612269,13 @@ ${mediaContext}` : ""
612157
612269
  const parsed = JSON.parse(readFileSync92(path12, "utf8"));
612158
612270
  const loadedHistory = Array.isArray(parsed.history) ? parsed.history : [];
612159
612271
  if (Array.isArray(parsed.history)) {
612160
- this.chatHistory.set(sessionKey, loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT));
612272
+ this.chatHistory.set(
612273
+ sessionKey,
612274
+ loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT).map((entry) => ({
612275
+ ...entry,
612276
+ isBot: entry.role === "assistant" ? true : Boolean(entry.isBot)
612277
+ }))
612278
+ );
612161
612279
  }
612162
612280
  if (Array.isArray(parsed.participants)) {
612163
612281
  const participants = /* @__PURE__ */ new Map();
@@ -612165,6 +612283,7 @@ ${mediaContext}` : ""
612165
612283
  const key = String(profile.fromUserId || profile.username || profile.firstName || participants.size);
612166
612284
  participants.set(key, {
612167
612285
  ...profile,
612286
+ isBot: Boolean(profile.isBot),
612168
612287
  toneTags: new Set(Array.isArray(profile.toneTags) ? profile.toneTags : []),
612169
612288
  samples: Array.isArray(profile.samples) ? profile.samples : []
612170
612289
  });
@@ -612675,6 +612794,31 @@ ${mediaContext}` : ""
612675
612794
  const sessionKey = this.sessionKeyForMessage(msg);
612676
612795
  const mediaSummary = summarizeTelegramMessageAttachments(msg);
612677
612796
  const text = (textOverride ?? msg.text ?? "").trim() || mediaSummary || "[non-text Telegram message]";
612797
+ this.ensureTelegramConversationLoaded(sessionKey);
612798
+ const history = this.chatHistory.get(sessionKey) ?? [];
612799
+ const existing = Number.isFinite(msg.messageId) ? history.find(
612800
+ (entry2) => entry2.role === "user" && entry2.messageId === msg.messageId && String(entry2.chatId) === String(msg.chatId)
612801
+ ) : void 0;
612802
+ if (existing) {
612803
+ if (existing.mode === "ambient" || mode !== "ambient") existing.mode = mode;
612804
+ if (textOverride !== void 0 || !existing.text || existing.text === "[non-text Telegram message]") {
612805
+ existing.text = text;
612806
+ }
612807
+ existing.speaker = existing.speaker || telegramSpeakerLabel(msg);
612808
+ existing.username = msg.username || existing.username;
612809
+ existing.firstName = msg.firstName ?? existing.firstName;
612810
+ existing.fromUserId = msg.fromUserId || existing.fromUserId;
612811
+ existing.isBot = msg.isBot ?? existing.isBot;
612812
+ existing.messageThreadId = msg.messageThreadId ?? existing.messageThreadId;
612813
+ existing.replyToMessageId = msg.replyToMessageId ?? existing.replyToMessageId;
612814
+ existing.replyContext = msg.replyContext ?? existing.replyContext;
612815
+ existing.chatType = msg.chatType || existing.chatType;
612816
+ existing.chatTitle = msg.chatTitle ?? existing.chatTitle;
612817
+ existing.mediaSummary = mediaSummary || existing.mediaSummary;
612818
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, existing);
612819
+ this.saveTelegramConversationState(sessionKey);
612820
+ return;
612821
+ }
612678
612822
  const entry = {
612679
612823
  role: "user",
612680
612824
  text,
@@ -612684,6 +612828,7 @@ ${mediaContext}` : ""
612684
612828
  username: msg.username,
612685
612829
  firstName: msg.firstName,
612686
612830
  fromUserId: msg.fromUserId,
612831
+ isBot: Boolean(msg.isBot),
612687
612832
  messageId: msg.messageId,
612688
612833
  messageThreadId: msg.messageThreadId,
612689
612834
  replyToMessageId: msg.replyToMessageId,
@@ -612723,6 +612868,7 @@ ${mediaContext}` : ""
612723
612868
  mode,
612724
612869
  chatId: msg.chatId,
612725
612870
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
612871
+ isBot: true,
612726
612872
  messageId: options2.messageId ?? void 0,
612727
612873
  messageThreadId: msg.messageThreadId,
612728
612874
  replyToMessageId: options2.replyToMessageId,
@@ -612902,6 +613048,7 @@ ${mediaContext}` : ""
612902
613048
  username: msg.username || "unknown",
612903
613049
  firstName: msg.firstName,
612904
613050
  fromUserId: msg.fromUserId,
613051
+ isBot: Boolean(msg.isBot),
612905
613052
  messageCount: 0,
612906
613053
  directAddressCount: 0,
612907
613054
  replyCount: 0,
@@ -612913,6 +613060,7 @@ ${mediaContext}` : ""
612913
613060
  profile.username = msg.username || profile.username;
612914
613061
  profile.firstName = msg.firstName ?? profile.firstName;
612915
613062
  profile.fromUserId = msg.fromUserId || profile.fromUserId;
613063
+ profile.isBot = Boolean(profile.isBot || msg.isBot);
612916
613064
  profile.messageCount += 1;
612917
613065
  profile.lastSeenTs = Date.now();
612918
613066
  profile.lastMessage = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
@@ -612947,7 +613095,8 @@ ${mediaContext}` : ""
612947
613095
  messageId: entry.messageId,
612948
613096
  replyToMessageId: entry.replyToMessageId,
612949
613097
  userId: entry.fromUserId,
612950
- username: entry.username
613098
+ username: entry.username,
613099
+ isBot: entry.role === "assistant" || Boolean(entry.isBot)
612951
613100
  });
612952
613101
  if (memory.actions.length > TELEGRAM_ASSOCIATIVE_ACTION_LIMIT) {
612953
613102
  memory.actions.splice(
@@ -612961,6 +613110,7 @@ ${mediaContext}` : ""
612961
613110
  const existing = memory.users[userKey];
612962
613111
  const userMemory = existing ?? {
612963
613112
  userId: entry.fromUserId,
613113
+ isBot: Boolean(entry.isBot),
612964
613114
  username: entry.username || "unknown",
612965
613115
  displayName: entry.firstName || entry.username || speaker,
612966
613116
  aliases: [],
@@ -612976,6 +613126,7 @@ ${mediaContext}` : ""
612976
613126
  lastMessages: []
612977
613127
  };
612978
613128
  userMemory.userId = entry.fromUserId ?? userMemory.userId;
613129
+ userMemory.isBot = Boolean(userMemory.isBot || entry.isBot);
612979
613130
  userMemory.username = entry.username || userMemory.username;
612980
613131
  userMemory.displayName = entry.firstName || userMemory.displayName;
612981
613132
  for (const alias of [entry.username, entry.firstName, speaker].filter(Boolean)) {
@@ -613211,7 +613362,7 @@ ${mediaContext}` : ""
613211
613362
  const hints = user.relationshipHints.slice(-4).map((hint) => ` - relation=${telegramContextJsonString(hint, 200)}`).join("\n");
613212
613363
  const topics = user.recentTopics.slice(-8).join(", ") || "none";
613213
613364
  return [
613214
- `- ${user.username && user.username !== "unknown" ? `@${user.username}` : user.displayName || "user"}${user.userId ? ` id:${user.userId}` : ""}: messages:${user.messageCount}, direct:${user.directAddressCount}, replies:${user.replyCount}, topics:${topics}`,
613365
+ `- ${user.username && user.username !== "unknown" ? `@${user.username}` : user.displayName || "user"} [${telegramActorKindLabel(user)}]${user.userId ? ` id:${user.userId}` : ""}: messages:${user.messageCount}, direct:${user.directAddressCount}, replies:${user.replyCount}, topics:${topics}`,
613215
613366
  facts,
613216
613367
  hints
613217
613368
  ].filter(Boolean).join("\n");
@@ -613233,7 +613384,7 @@ ${lines.join("\n")}`);
613233
613384
  }
613234
613385
  if (recentActions.length > 0) {
613235
613386
  const lines = recentActions.map(
613236
- (action) => `- ${telegramHistoryTime({ ts: action.ts, role: action.role, text: action.text })} ${action.speaker}/${action.role}${action.mode ? `/${action.mode}` : ""}: ${telegramContextJsonString(action.text, 220)}`
613387
+ (action) => `- ${telegramHistoryTime({ ts: action.ts, role: action.role, text: action.text })} ${action.speaker} [${telegramActorKindLabel({ isBot: action.role === "assistant" || action.isBot })}]/${action.role}${action.mode ? `/${action.mode}` : ""}: ${telegramContextJsonString(action.text, 220)}`
613237
613388
  );
613238
613389
  sections.push(`### Durable Recent Action Ledger Recall
613239
613390
  ${lines.join("\n")}`);
@@ -613359,32 +613510,196 @@ ${lines.join("\n")}`);
613359
613510
  ORDER BY received_at DESC
613360
613511
  LIMIT ?
613361
613512
  `).all(sessionKey, limit);
613362
- return rows.reverse().map((row) => ({
613363
- role: row.role === "assistant" ? "assistant" : "user",
613364
- text: String(row.text || ""),
613365
- ts: Number(row.received_at || 0) || void 0,
613366
- chatId: row.chat_id,
613367
- speaker: row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown",
613368
- username: row.username || void 0,
613369
- firstName: row.first_name || void 0,
613370
- fromUserId: typeof row.from_user_id === "number" ? row.from_user_id : void 0,
613371
- messageId: typeof row.message_id === "number" ? row.message_id : void 0,
613372
- messageThreadId: typeof row.message_thread_id === "number" ? row.message_thread_id : void 0,
613373
- replyToMessageId: typeof row.reply_to_message_id === "number" ? row.reply_to_message_id : void 0,
613374
- chatType: row.chat_type,
613375
- chatTitle: row.chat_title || void 0,
613376
- mediaSummary: row.media_json ? "media attached in raw Telegram SQLite mirror" : void 0
613377
- }));
613513
+ return rows.reverse().map((row) => this.telegramSqliteRowToHistoryEntry(row));
613378
613514
  } catch {
613379
613515
  return [];
613380
613516
  }
613381
613517
  }
613518
+ telegramSqliteRowIsBot(row) {
613519
+ if (row.role === "assistant") return true;
613520
+ if (row.from_is_bot === 1 || row.from_is_bot === true) return true;
613521
+ for (const key of ["normalized_json", "raw_json"]) {
613522
+ const raw = row?.[key];
613523
+ if (typeof raw !== "string" || !raw.trim()) continue;
613524
+ try {
613525
+ const parsed = JSON.parse(raw);
613526
+ if (parsed?.isBot === true) return true;
613527
+ if (parsed?.from?.is_bot === true) return true;
613528
+ } catch {
613529
+ }
613530
+ }
613531
+ return false;
613532
+ }
613533
+ telegramSqliteRowToHistoryEntry(row) {
613534
+ const num = (value2) => {
613535
+ const n2 = Number(value2);
613536
+ return Number.isFinite(n2) ? n2 : void 0;
613537
+ };
613538
+ const role = row.role === "assistant" ? "assistant" : "user";
613539
+ const username = row.username ? String(row.username) : void 0;
613540
+ const fromUserId = num(row.from_user_id);
613541
+ const isBot = this.telegramSqliteRowIsBot(row);
613542
+ return {
613543
+ role,
613544
+ text: String(row.text || ""),
613545
+ ts: num(row.received_at),
613546
+ chatId: row.chat_id,
613547
+ speaker: role === "assistant" ? `@${this.state.botUsername || username || "omnius"}` : username ? `@${username}` : fromUserId !== void 0 ? isBot ? `bot:${fromUserId}` : `user:${fromUserId}` : "unknown",
613548
+ username,
613549
+ firstName: row.first_name ? String(row.first_name) : void 0,
613550
+ fromUserId,
613551
+ isBot,
613552
+ messageId: num(row.message_id),
613553
+ messageThreadId: num(row.message_thread_id),
613554
+ replyToMessageId: num(row.reply_to_message_id),
613555
+ chatType: row.chat_type,
613556
+ chatTitle: row.chat_title || void 0,
613557
+ mediaSummary: row.media_json ? "media attached in raw Telegram SQLite mirror" : void 0
613558
+ };
613559
+ }
613560
+ telegramMergedHistoryForSession(sessionKey, limit = TELEGRAM_CHAT_HISTORY_LIMIT) {
613561
+ const sqliteHistory = this.telegramSqliteHistoryForSession(sessionKey, limit);
613562
+ const rollingHistory = this.chatHistory.get(sessionKey) ?? [];
613563
+ if (sqliteHistory.length === 0) return rollingHistory.slice(-limit);
613564
+ const merged = /* @__PURE__ */ new Map();
613565
+ const keyFor = (entry) => {
613566
+ if (entry.messageId !== void 0 && entry.messageId > 0) {
613567
+ return `msg:${entry.chatId ?? sessionKey}:${entry.role}:${entry.messageId}`;
613568
+ }
613569
+ return `synthetic:${entry.role}:${entry.ts ?? ""}:${telegramHistorySpeaker(entry)}:${entry.text}`;
613570
+ };
613571
+ for (const entry of sqliteHistory) merged.set(keyFor(entry), entry);
613572
+ for (const entry of rollingHistory) merged.set(keyFor(entry), entry);
613573
+ return [...merged.values()].sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0)).slice(-limit);
613574
+ }
613575
+ telegramHistoryAnchorsForSession(sessionKey, earliestLimit = 3) {
613576
+ const db = this.telegramDb();
613577
+ if (db) {
613578
+ try {
613579
+ const earliestRows = db.prepare(`
613580
+ SELECT * FROM telegram_messages
613581
+ WHERE session_key = ?
613582
+ ORDER BY received_at ASC, rowid ASC
613583
+ LIMIT ?
613584
+ `).all(sessionKey, Math.max(1, Math.min(10, Math.floor(earliestLimit))));
613585
+ const latestRow = db.prepare(`
613586
+ SELECT * FROM telegram_messages
613587
+ WHERE session_key = ?
613588
+ ORDER BY received_at DESC, rowid DESC
613589
+ LIMIT 1
613590
+ `).get(sessionKey);
613591
+ if (earliestRows.length > 0 || latestRow) {
613592
+ return {
613593
+ earliest: earliestRows.map((row) => this.telegramSqliteRowToHistoryEntry(row)),
613594
+ latest: latestRow ? this.telegramSqliteRowToHistoryEntry(latestRow) : void 0
613595
+ };
613596
+ }
613597
+ } catch {
613598
+ }
613599
+ }
613600
+ const sorted = [...this.chatHistory.get(sessionKey) ?? []].filter((entry) => typeof entry.ts === "number" && Number.isFinite(entry.ts)).sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0));
613601
+ return {
613602
+ earliest: sorted.slice(0, Math.max(1, Math.min(10, Math.floor(earliestLimit)))),
613603
+ latest: sorted.length > 0 ? sorted[sorted.length - 1] : void 0
613604
+ };
613605
+ }
613606
+ telegramParticipantActivityStats(sessionKey, options2 = {}) {
613607
+ const limit = Math.max(1, Math.min(20, Math.floor(options2.limit ?? 8)));
613608
+ const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
613609
+ const db = this.telegramDb();
613610
+ if (db) {
613611
+ const clauses = ["session_key = ?"];
613612
+ const params = [sessionKey];
613613
+ if (options2.userId !== void 0) {
613614
+ clauses.push("from_user_id = ?");
613615
+ params.push(options2.userId);
613616
+ }
613617
+ if (username) {
613618
+ clauses.push("lower(username) = ?");
613619
+ params.push(username);
613620
+ }
613621
+ if (options2.since !== void 0) {
613622
+ clauses.push("received_at >= ?");
613623
+ params.push(options2.since);
613624
+ }
613625
+ if (options2.until !== void 0) {
613626
+ clauses.push("received_at <= ?");
613627
+ params.push(options2.until);
613628
+ }
613629
+ try {
613630
+ const rows = db.prepare(`
613631
+ SELECT
613632
+ role,
613633
+ from_user_id,
613634
+ lower(COALESCE(username, '')) AS username_key,
613635
+ COALESCE(username, '') AS username,
613636
+ COALESCE(first_name, '') AS first_name,
613637
+ MAX(CASE
613638
+ WHEN role = 'assistant' THEN 1
613639
+ WHEN normalized_json LIKE '%"isBot":true%' THEN 1
613640
+ WHEN raw_json LIKE '%"is_bot":true%' THEN 1
613641
+ ELSE 0
613642
+ END) AS from_is_bot,
613643
+ COUNT(*) AS n,
613644
+ MIN(received_at) AS first_ts,
613645
+ MAX(received_at) AS last_ts
613646
+ FROM telegram_messages
613647
+ WHERE ${clauses.join(" AND ")}
613648
+ GROUP BY role, from_user_id, username_key, first_name
613649
+ ORDER BY n DESC, last_ts DESC
613650
+ LIMIT ?
613651
+ `).all(...params, limit);
613652
+ return rows.map((row) => {
613653
+ const role = row.role === "assistant" ? "assistant" : "user";
613654
+ const fromUserId = Number(row.from_user_id);
613655
+ const isBot = role === "assistant" || row.from_is_bot === 1 || row.from_is_bot === true;
613656
+ const speaker = role === "assistant" ? `@${this.state.botUsername || row.username || "omnius"}` : row.username ? `@${row.username}` : Number.isFinite(fromUserId) ? isBot ? `bot:${fromUserId}` : `user:${fromUserId}` : row.first_name || "unknown";
613657
+ const firstTs = Number(row.first_ts);
613658
+ const lastTs = Number(row.last_ts);
613659
+ return {
613660
+ speaker,
613661
+ isBot,
613662
+ count: Number(row.n) || 0,
613663
+ firstTs: Number.isFinite(firstTs) ? firstTs : void 0,
613664
+ lastTs: Number.isFinite(lastTs) ? lastTs : void 0
613665
+ };
613666
+ });
613667
+ } catch {
613668
+ }
613669
+ }
613670
+ const stats = /* @__PURE__ */ new Map();
613671
+ for (const entry of this.telegramMergedHistoryForSession(sessionKey)) {
613672
+ if (options2.userId !== void 0 && entry.fromUserId !== options2.userId) continue;
613673
+ if (username && (entry.username || "").replace(/^@/, "").toLowerCase() !== username) continue;
613674
+ if (options2.since !== void 0 && (entry.ts ?? 0) < options2.since) continue;
613675
+ if (options2.until !== void 0 && (entry.ts ?? 0) > options2.until) continue;
613676
+ const speaker = telegramHistorySpeaker(entry);
613677
+ const current = stats.get(speaker) ?? { speaker, isBot: entry.role === "assistant" || entry.isBot, count: 0 };
613678
+ current.isBot = Boolean(current.isBot || entry.role === "assistant" || entry.isBot);
613679
+ current.count += 1;
613680
+ if (entry.ts !== void 0) {
613681
+ current.firstTs = current.firstTs === void 0 ? entry.ts : Math.min(current.firstTs, entry.ts);
613682
+ current.lastTs = current.lastTs === void 0 ? entry.ts : Math.max(current.lastTs, entry.ts);
613683
+ }
613684
+ stats.set(speaker, current);
613685
+ }
613686
+ return [...stats.values()].sort((a2, b) => b.count - a2.count || (b.lastTs ?? 0) - (a2.lastTs ?? 0)).slice(0, limit);
613687
+ }
613688
+ formatTelegramParticipantActivityStats(stats) {
613689
+ return stats.map((stat7) => {
613690
+ const first2 = stat7.firstTs ? new Date(stat7.firstTs).toISOString() : "?";
613691
+ const last2 = stat7.lastTs ? new Date(stat7.lastTs).toISOString() : "?";
613692
+ return `- ${stat7.speaker} [${telegramActorKindLabel(stat7)}]: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`;
613693
+ }).join("\n");
613694
+ }
613382
613695
  searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
613383
613696
  const db = this.telegramDb();
613384
613697
  if (!db) return [];
613385
613698
  const limit = Math.max(1, Math.min(50, Math.floor(options2.limit ?? 12)));
613386
613699
  const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
613700
+ const direction = options2.bucket === "earliest" ? "ASC" : "DESC";
613387
613701
  const tokens = [...telegramMemoryTokens(query)].slice(0, 8);
613702
+ const meaningfulTokens = [...telegramMeaningfulMemoryTokens(query)].slice(0, 8);
613388
613703
  const rows = /* @__PURE__ */ new Map();
613389
613704
  const addRows = (items) => {
613390
613705
  for (const row of items) {
@@ -613392,52 +613707,75 @@ ${lines.join("\n")}`);
613392
613707
  if (Number.isFinite(key)) rows.set(key, row);
613393
613708
  }
613394
613709
  };
613710
+ const baseWhere = (extraClauses = [], extraParams = []) => {
613711
+ const clauses = ["session_key = ?"];
613712
+ const params = [sessionKey];
613713
+ if (options2.since !== void 0) {
613714
+ clauses.push("received_at >= ?");
613715
+ params.push(options2.since);
613716
+ }
613717
+ if (options2.until !== void 0) {
613718
+ clauses.push("received_at <= ?");
613719
+ params.push(options2.until);
613720
+ }
613721
+ clauses.push(...extraClauses);
613722
+ params.push(...extraParams);
613723
+ return { where: clauses.join(" AND "), params };
613724
+ };
613725
+ const scopedWhere = (extraClauses = [], extraParams = []) => {
613726
+ const clauses = [...extraClauses];
613727
+ const params = [...extraParams];
613728
+ if (options2.userId !== void 0) {
613729
+ clauses.unshift("from_user_id = ?");
613730
+ params.unshift(options2.userId);
613731
+ }
613732
+ if (username) {
613733
+ clauses.unshift("lower(username) = ?");
613734
+ params.unshift(username);
613735
+ }
613736
+ return baseWhere(clauses, params);
613737
+ };
613738
+ const selectRows = (where, params, rowLimit = limit) => db.prepare(`
613739
+ SELECT rowid, * FROM telegram_messages
613740
+ WHERE ${where}
613741
+ ORDER BY received_at ${direction}, rowid ${direction}
613742
+ LIMIT ?
613743
+ `).all(...params, rowLimit);
613395
613744
  try {
613396
613745
  if (options2.userId !== void 0) {
613397
- addRows(db.prepare(`
613398
- SELECT rowid, * FROM telegram_messages
613399
- WHERE session_key = ? AND from_user_id = ?
613400
- ORDER BY received_at DESC
613401
- LIMIT ?
613402
- `).all(sessionKey, options2.userId, limit));
613746
+ const { where, params } = baseWhere(["from_user_id = ?"], [options2.userId]);
613747
+ addRows(selectRows(where, params));
613403
613748
  }
613404
613749
  if (username) {
613405
- addRows(db.prepare(`
613406
- SELECT rowid, * FROM telegram_messages
613407
- WHERE session_key = ? AND lower(username) = ?
613408
- ORDER BY received_at DESC
613409
- LIMIT ?
613410
- `).all(sessionKey, username, limit));
613750
+ const { where, params } = baseWhere(["lower(username) = ?"], [username]);
613751
+ addRows(selectRows(where, params));
613411
613752
  }
613412
613753
  if (tokens.length > 0) {
613413
- const clauses = tokens.map(() => "text LIKE ?").join(" OR ");
613414
- addRows(db.prepare(`
613415
- SELECT rowid, * FROM telegram_messages
613416
- WHERE session_key = ? AND (${clauses})
613417
- ORDER BY received_at DESC
613418
- LIMIT ?
613419
- `).all(sessionKey, ...tokens.map((token) => `%${token}%`), limit));
613754
+ const clauses = tokens.map(() => "(text LIKE ? OR lower(username) LIKE ? OR lower(first_name) LIKE ?)").join(" OR ");
613755
+ const tokenParams = tokens.flatMap((token) => [`%${token}%`, `%${token}%`, `%${token}%`]);
613756
+ const { where, params } = scopedWhere([`(${clauses})`], tokenParams);
613757
+ addRows(selectRows(where, params));
613420
613758
  }
613421
- if (rows.size === 0) {
613422
- addRows(db.prepare(`
613423
- SELECT rowid, * FROM telegram_messages
613424
- WHERE session_key = ?
613425
- ORDER BY received_at DESC
613426
- LIMIT ?
613427
- `).all(sessionKey, Math.min(limit, 12)));
613759
+ if (rows.size === 0 && meaningfulTokens.length === 0) {
613760
+ const { where, params } = scopedWhere();
613761
+ addRows(selectRows(where, params, Math.min(limit, 20)));
613428
613762
  }
613429
613763
  } catch {
613430
613764
  return [];
613431
613765
  }
613432
- return [...rows.values()].sort((a2, b) => Number(b.received_at ?? 0) - Number(a2.received_at ?? 0)).slice(0, limit);
613766
+ return [...rows.values()].sort((a2, b) => {
613767
+ const diff = Number(a2.received_at ?? 0) - Number(b.received_at ?? 0);
613768
+ return direction === "ASC" ? diff : -diff;
613769
+ }).slice(0, limit);
613433
613770
  }
613434
613771
  formatTelegramSqliteMirrorRows(rows, maxText = 260) {
613435
613772
  return rows.map((row) => {
613436
613773
  const when = row.received_at ? new Date(Number(row.received_at)).toISOString() : "";
613437
- const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown";
613774
+ const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? this.telegramSqliteRowIsBot(row) ? `bot:${row.from_user_id}` : `user:${row.from_user_id}` : "unknown";
613775
+ const kind = row.role === "assistant" || this.telegramSqliteRowIsBot(row) ? "bot" : "human";
613438
613776
  const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
613439
613777
  const media = row.media_json ? " media:attached" : "";
613440
- return `- ${when} ${speaker}/${row.role || "user"} msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
613778
+ return `- ${when} ${speaker}/${row.role || "user"} [${kind}] msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
613441
613779
  }).join("\n");
613442
613780
  }
613443
613781
  searchTelegramEpisodeMemory(sessionKey, query, limit = 8) {
@@ -613560,6 +613898,7 @@ ${lines.join("\n")}`);
613560
613898
  `- Associative relationships: ${relationshipCount}`,
613561
613899
  `- Per-user memories: ${userMemoryCount}`,
613562
613900
  `- Rolling history entries retained: ${historyCount}`,
613901
+ `- Addressable conversation history rows: ${Math.max(historyCount, sqliteCount)}`,
613563
613902
  `- SQLite mirror rows: ${sqliteCount}`,
613564
613903
  `- Episodes (durable, day+ scope): ${episodeCount2}`
613565
613904
  ];
@@ -613570,27 +613909,42 @@ ${lines.join("\n")}`);
613570
613909
  lines.push(` - ${topic}`);
613571
613910
  }
613572
613911
  }
613573
- const sourceHistory = this.chatHistory.get(sessionKey) ?? [];
613574
- const sortedByTs = [...sourceHistory].filter((e2) => typeof e2.ts === "number" && Number.isFinite(e2.ts)).sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0));
613912
+ const anchors = this.telegramHistoryAnchorsForSession(sessionKey, 3);
613575
613913
  const fmtHistoryAnchor = (entry) => {
613576
613914
  const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
613577
613915
  const speaker = telegramHistorySpeaker(entry);
613916
+ const kind = entry.role === "assistant" || entry.isBot ? "bot" : "human";
613578
613917
  const mode = entry.mode ? `/${entry.mode}` : "";
613579
613918
  const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
613580
- return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613919
+ return `${when} ${speaker} [${kind}]${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613581
613920
  };
613582
- if (sortedByTs.length > 0) {
613921
+ const sameHistoryAnchor = (a2, b) => {
613922
+ if (!a2 || !b) return false;
613923
+ if (a2.messageId !== void 0 && b.messageId !== void 0) return a2.messageId === b.messageId && a2.role === b.role;
613924
+ return a2.ts === b.ts && a2.role === b.role && a2.text === b.text;
613925
+ };
613926
+ if (anchors.earliest.length > 0 || anchors.latest) {
613583
613927
  lines.push("");
613584
- lines.push("Chronological anchors — Telegram conversation history (ground truth for 'oldest/newest memory' questions):");
613585
- lines.push(` EARLIEST turn: ${fmtHistoryAnchor(sortedByTs[0])}`);
613586
- if (sortedByTs.length > 1) {
613587
- lines.push(` LATEST turn: ${fmtHistoryAnchor(sortedByTs[sortedByTs.length - 1])}`);
613928
+ lines.push("Chronological anchors — Telegram conversation history (SQLite mirror preferred; ground truth for 'oldest/newest memory' questions):");
613929
+ if (anchors.earliest[0]) lines.push(` EARLIEST turn: ${fmtHistoryAnchor(anchors.earliest[0])}`);
613930
+ if (anchors.latest && !sameHistoryAnchor(anchors.earliest[0], anchors.latest)) {
613931
+ lines.push(` LATEST turn: ${fmtHistoryAnchor(anchors.latest)}`);
613588
613932
  }
613589
- if (sortedByTs.length > 2) {
613590
- lines.push(` 2nd earliest: ${fmtHistoryAnchor(sortedByTs[1])}`);
613933
+ if (anchors.earliest[1]) {
613934
+ lines.push(` 2nd earliest: ${fmtHistoryAnchor(anchors.earliest[1])}`);
613591
613935
  }
613592
- if (sortedByTs.length > 3) {
613593
- lines.push(` 3rd earliest: ${fmtHistoryAnchor(sortedByTs[2])}`);
613936
+ if (anchors.earliest[2]) {
613937
+ lines.push(` 3rd earliest: ${fmtHistoryAnchor(anchors.earliest[2])}`);
613938
+ }
613939
+ }
613940
+ const activityStats = this.telegramParticipantActivityStats(sessionKey, { limit: 8 });
613941
+ if (activityStats.length > 0) {
613942
+ lines.push("");
613943
+ lines.push("Activity anchors — participant message counts from the durable mirror/merged history:");
613944
+ for (const stat7 of activityStats) {
613945
+ const first2 = stat7.firstTs ? new Date(stat7.firstTs).toISOString() : "?";
613946
+ const last2 = stat7.lastTs ? new Date(stat7.lastTs).toISOString() : "?";
613947
+ lines.push(` - ${stat7.speaker}: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`);
613594
613948
  }
613595
613949
  }
613596
613950
  if (this.repoRoot) {
@@ -613697,7 +614051,7 @@ ${lines.join("\n")}`);
613697
614051
  `Chat: ${isGroup ? `${msg.chatType} "${msg.chatTitle || "unknown"}"` : "private DM"}`,
613698
614052
  `Retained messages in bridge memory: ${retainedCount}`,
613699
614053
  olderCount ? `Older retained messages compacted below: ${olderCount}` : "",
613700
- `Current sender: ${currentSpeaker}${msg.fromUserId ? ` (id ${msg.fromUserId})` : ""}`,
614054
+ `Current sender: ${currentSpeaker} [${telegramActorKindLabel(msg)}]${msg.fromUserId ? ` (id ${msg.fromUserId})` : ""}`,
613701
614055
  msg.messageThreadId !== void 0 ? `Current Telegram thread/topic id: ${msg.messageThreadId}` : "",
613702
614056
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : ""
613703
614057
  ].filter(Boolean).join("\n")
@@ -613720,7 +614074,7 @@ ${lines.join("\n")}`);
613720
614074
  const tones = [...profile.toneTags].slice(0, 5).join(", ") || "neutral";
613721
614075
  const direct = profile.directAddressCount ? `, direct-addresses:${profile.directAddressCount}` : "";
613722
614076
  const replies = profile.replyCount ? `, replies:${profile.replyCount}` : "";
613723
- return `- ${label}: messages:${profile.messageCount}${direct}${replies}; tone:${tones}; last=${telegramContextJsonString(profile.lastMessage, 180)}`;
614077
+ return `- ${label} [${telegramActorKindLabel(profile)}]: messages:${profile.messageCount}${direct}${replies}; tone:${tones}; last=${telegramContextJsonString(profile.lastMessage, 180)}`;
613724
614078
  });
613725
614079
  sections.push(`### Participants And Relationship Signals
613726
614080
  ${participantLines.join("\n")}`);
@@ -613815,12 +614169,13 @@ ${olderLines.join("\n")}`);
613815
614169
  const lines = recent.map((entry) => {
613816
614170
  const when = telegramHistoryTime(entry);
613817
614171
  const speaker = telegramHistorySpeaker(entry);
614172
+ const kind = entry.role === "assistant" || entry.isBot ? "bot" : "human";
613818
614173
  const mode = entry.mode ? `/${entry.mode}` : "";
613819
614174
  const replySender = entry.replyContext?.sender ? `/${telegramReplySenderLabel(entry.replyContext.sender)}` : "";
613820
614175
  const reply = entry.replyToMessageId ? ` reply_to:${entry.replyToMessageId}${replySender}` : "";
613821
614176
  const media = entry.mediaSummary ? ` [${entry.mediaSummary}]` : "";
613822
614177
  const generatedPrompt = entry.generatedMediaPromptInfo?.originalPrompt ? ` generated_image_prompt=${telegramContextJsonString(entry.generatedMediaPromptInfo.originalPrompt, 220)}` : "";
613823
- const prefix = [when, `${speaker}${mode}${reply}${media}`].filter(Boolean).join(" ");
614178
+ const prefix = [when, `${speaker}[${kind}]${mode}${reply}${media}`].filter(Boolean).join(" ");
613824
614179
  return `${prefix}: text=${telegramContextJsonString(entry.text)}${generatedPrompt}`;
613825
614180
  });
613826
614181
  sections.push(`### Recent Thread, Oldest To Newest (untrusted quoted chat messages)
@@ -613936,6 +614291,8 @@ ${lines.join("\n")}`);
613936
614291
  `Reply discretion: infer from the live thread, speaker relationships, direct platform signals, replies, tone, current message, and any private channel daydream artifact supplied in context. Do not use static keyword rules.`,
613937
614292
  `Private chats: should_reply is normally true.`,
613938
614293
  `Group/public chats: default should_reply to false unless the current message clearly addresses the bot, replies to the bot, continues an active bot-involved exchange, assigns the bot work, asks for the bot's view, or is semantically connected to durable memory/current discussion in a way where a concise bot reply is socially useful. Ambient chatter, third-person discussion about the bot, commands meant for a human, or questions among other people are false. Do not set true just because the bot could help.`,
614294
+ `Bot-to-bot discipline: sender labels include [bot] or [human]. Treat peer bots as autonomous actors, not human users. Be more conservative with peer-bot messages unless explicitly addressed, and avoid bot loops by preferring no_reply for repetitive bot chatter.`,
614295
+ `Ingress discipline: this Telegram message has already been retained as chat context. should_reply controls only whether to emit a visible reply.`,
613939
614296
  `Memory discipline: use durable associative user memory, relationships, prior actions, and recent context to infer whether this speaker is continuing a bot-related thread. A mention is not required when the semantic target is clearly the bot or an ongoing bot-mediated discussion.`,
613940
614297
  `Channel daydream discipline: a daydream artifact may highlight relationship signals, unresolved questions, or possible reply opportunities from idle reflection. It can justify analyzing this turn, but it does not force a reply. Reply only if the current user entry makes the intervention timely and socially appropriate.`,
613941
614298
  `Stimulation discipline: also set attention_state, attention_delta, and optional next_check_after_messages/next_check_after_ms. These control future analysis cadence only; they do not force a reply. Use engaged for active back-and-forth, observing for likely relevant context, cooldown for recently irrelevant context, and idle for ambient chatter.`,
@@ -613945,9 +614302,9 @@ ${lines.join("\n")}`);
613945
614302
  `Bot username: ${this.state.botUsername || "unknown"}`,
613946
614303
  `Current message directly addresses this bot: ${addressesBot ? "yes" : "no"}`,
613947
614304
  `Current chat type: ${msg.chatType}`,
613948
- `Current sender: ${telegramSpeakerLabel(msg)}`,
614305
+ `Current sender: ${telegramSpeakerLabel(msg)} [${telegramActorKindLabel(msg)}]`,
613949
614306
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : "",
613950
- msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}` : "",
614307
+ msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}${msg.replyToBot ? " [bot]" : " [human/unknown]"}` : "",
613951
614308
  currentReplyContext,
613952
614309
  (msg.mentionedUsernames ?? []).length > 0 ? `Current message mentions: ${(msg.mentionedUsernames ?? []).map((name10) => `@${name10}`).join(", ")}` : "",
613953
614310
  msg.media ? `Current message has media: ${summarizeTelegramMessageAttachments(msg)}` : "",
@@ -614318,37 +614675,23 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614318
614675
  if (!me?.ok) {
614319
614676
  throw new Error(`Invalid Telegram bot token: ${me?.description || "unknown error"}`);
614320
614677
  }
614321
- if (this.repoRoot && me.result?.id) {
614678
+ if (me.result?.id) {
614679
+ const botUserId = Number(me.result.id);
614680
+ const globalLockDir = process.env["OMNIUS_TELEGRAM_LOCK_DIR"] ? resolve43(process.env["OMNIUS_TELEGRAM_LOCK_DIR"]) : resolve43(homedir40(), ".omnius", "telegram-runner-state");
614681
+ const lockDirs = /* @__PURE__ */ new Set([globalLockDir]);
614682
+ if (this.repoRoot) {
614683
+ lockDirs.add(resolve43(this.repoRoot, ".omnius", "telegram-runner-state"));
614684
+ }
614685
+ const claimed = [];
614322
614686
  try {
614323
- const lockDir = resolve43(this.repoRoot, ".omnius", "telegram-runner-state");
614324
- mkdirSync65(lockDir, { recursive: true });
614325
- const lockFile = join127(lockDir, `bot-${me.result.id}.owner.lock`);
614326
- if (existsSync113(lockFile)) {
614327
- try {
614328
- const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
614329
- const priorAlive = typeof prior.pid === "number" && prior.pid !== process.pid ? this.processIsAlive(prior.pid) : false;
614330
- if (priorAlive) {
614331
- throw new Error(
614332
- `Telegram bot @${prior.botUsername || me.result.username || "unknown"} is already being polled by pid ${prior.pid} (cwd ${prior.cwd || "?"}). Stop that instance or use a different bot token in this project.`
614333
- );
614334
- }
614335
- } catch (e2) {
614336
- if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614337
- }
614687
+ for (const lockDir of lockDirs) {
614688
+ const lockFile = this.claimTelegramOwnerLock(lockDir, botUserId, me.result.username);
614689
+ claimed.push(lockFile);
614338
614690
  }
614339
- writeFileSync59(
614340
- lockFile,
614341
- JSON.stringify({
614342
- pid: process.pid,
614343
- cwd: this.repoRoot,
614344
- botUsername: me.result.username,
614345
- botUserId: me.result.id,
614346
- ts: Date.now()
614347
- }, null, 2),
614348
- { encoding: "utf-8", mode: 384 }
614349
- );
614350
- this.telegramOwnerLockFile = lockFile;
614691
+ this.telegramOwnerLockFiles = claimed;
614692
+ this.telegramOwnerLockFile = claimed[0] ?? null;
614351
614693
  } catch (e2) {
614694
+ for (const lockFile of claimed) this.releaseTelegramOwnerLock(lockFile);
614352
614695
  if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614353
614696
  }
614354
614697
  }
@@ -614356,6 +614699,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614356
614699
  active: true,
614357
614700
  botUsername: me.result?.username ?? "unknown",
614358
614701
  supportsGuestQueries: Boolean(me.result?.supports_guest_queries),
614702
+ canReadAllGroupMessages: me.result?.can_read_all_group_messages === true,
614359
614703
  interactionMode: this.interactionMode,
614360
614704
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
614361
614705
  messagesReceived: 0,
@@ -614406,18 +614750,57 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614406
614750
  }
614407
614751
  this.telegramSqliteDb = null;
614408
614752
  }
614409
- if (this.telegramOwnerLockFile) {
614753
+ const lockFiles = /* @__PURE__ */ new Set([
614754
+ ...this.telegramOwnerLockFiles,
614755
+ ...this.telegramOwnerLockFile ? [this.telegramOwnerLockFile] : []
614756
+ ]);
614757
+ for (const lockFile of lockFiles) this.releaseTelegramOwnerLock(lockFile);
614758
+ this.telegramOwnerLockFiles = [];
614759
+ this.telegramOwnerLockFile = null;
614760
+ this.subAgents.clear();
614761
+ this.activeChatViews.clear();
614762
+ this.refreshActiveTelegramInteractionCount();
614763
+ }
614764
+ claimTelegramOwnerLock(lockDir, botUserId, botUsername) {
614765
+ mkdirSync65(lockDir, { recursive: true });
614766
+ const lockFile = join127(lockDir, `bot-${botUserId}.owner.lock`);
614767
+ if (existsSync113(lockFile)) {
614410
614768
  try {
614411
- if (existsSync113(this.telegramOwnerLockFile)) {
614412
- unlinkSync22(this.telegramOwnerLockFile);
614769
+ const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
614770
+ const priorAlive = typeof prior.pid === "number" && prior.pid !== process.pid ? this.processIsAlive(prior.pid) : false;
614771
+ if (priorAlive) {
614772
+ throw new Error(
614773
+ `Telegram bot @${prior.botUsername || botUsername || "unknown"} is already being polled by pid ${prior.pid} (cwd ${prior.cwd || "?"}). Stop that instance or use a different bot token in this project.`
614774
+ );
614413
614775
  }
614776
+ } catch (e2) {
614777
+ if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614778
+ }
614779
+ }
614780
+ writeFileSync59(
614781
+ lockFile,
614782
+ JSON.stringify({
614783
+ pid: process.pid,
614784
+ cwd: this.repoRoot || process.cwd(),
614785
+ botUsername,
614786
+ botUserId,
614787
+ ts: Date.now()
614788
+ }, null, 2),
614789
+ { encoding: "utf-8", mode: 384 }
614790
+ );
614791
+ return lockFile;
614792
+ }
614793
+ releaseTelegramOwnerLock(lockFile) {
614794
+ try {
614795
+ if (!existsSync113(lockFile)) return;
614796
+ try {
614797
+ const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
614798
+ if (prior.pid !== process.pid) return;
614414
614799
  } catch {
614415
614800
  }
614416
- this.telegramOwnerLockFile = null;
614801
+ unlinkSync22(lockFile);
614802
+ } catch {
614417
614803
  }
614418
- this.subAgents.clear();
614419
- this.activeChatViews.clear();
614420
- this.refreshActiveTelegramInteractionCount();
614421
614804
  }
614422
614805
  /**
614423
614806
  * Cheap liveness probe: kill(pid, 0) throws ESRCH when the process is dead
@@ -614556,7 +614939,16 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614556
614939
  */
614557
614940
  async handleMessageWithSubAgent(msg) {
614558
614941
  const normalizedCommandText = this.normalizeTelegramCommandText(msg.text);
614559
- if (msg.text.trim().startsWith("/") && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614942
+ const startsWithSlash = msg.text.trim().startsWith("/");
614943
+ const commandName = normalizedCommandText.trim().toLowerCase().split(/\s+/)[0]?.split("@")[0] ?? "";
614944
+ const isAdminAuthSecretCommand = commandName === "/auth" || commandName === "/authenticate";
614945
+ if (startsWithSlash && isAdminAuthSecretCommand && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614946
+ return;
614947
+ }
614948
+ if (msg.chatType !== "private") {
614949
+ this.recordTelegramUserMessage(msg, "ambient");
614950
+ }
614951
+ if (startsWithSlash && !isAdminAuthSecretCommand && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614560
614952
  return;
614561
614953
  }
614562
614954
  const isAdmin = this.isAdminUser(msg);
@@ -615542,7 +615934,9 @@ ${result.llmContent ?? result.output}` };
615542
615934
  })(),
615543
615935
  execute: async (args) => {
615544
615936
  const rawQuery = String(args["query"] || "").trim();
615545
- const maxResults = typeof args["max_results"] === "number" && Number.isFinite(args["max_results"]) ? Math.max(1, Math.min(20, Math.floor(args["max_results"]))) : 8;
615937
+ const rawMaxResults = args["max_results"];
615938
+ const explicitMaxResults = typeof rawMaxResults === "number" && Number.isFinite(rawMaxResults);
615939
+ let maxResults = explicitMaxResults ? Math.max(1, Math.min(20, Math.floor(rawMaxResults))) : 8;
615546
615940
  if (!rawQuery) return { success: true, output: "Search query is required." };
615547
615941
  this.ensureTelegramConversationLoaded(msgSessionKey);
615548
615942
  const parseTimeArg = (v) => {
@@ -615576,6 +615970,9 @@ ${result.llmContent ?? result.output}` };
615576
615970
  bucket ? `bucket=${bucket}` : ""
615577
615971
  ].filter(Boolean).join(", ");
615578
615972
  }
615973
+ if (!explicitMaxResults && (since !== void 0 || until !== void 0 || bucket !== void 0) && /\b(summary|summarize|conversation|convo|catch\s+me\s+up|what\s+happened|oldest|earliest|newest|latest|most\s+recent)\b/i.test(rawQuery)) {
615974
+ maxResults = 20;
615975
+ }
615579
615976
  const inWindow = (ts) => {
615580
615977
  if (since === void 0 && until === void 0) return true;
615581
615978
  if (ts == null || !Number.isFinite(ts)) return false;
@@ -615612,6 +616009,7 @@ ${result.llmContent ?? result.output}` };
615612
616009
  return false;
615613
616010
  });
615614
616011
  const queryTokens = telegramMemoryTokens(query);
616012
+ const meaningfulQueryTokens = telegramMeaningfulMemoryTokens(query);
615615
616013
  const cardResults = cards.filter((card) => inWindow(card.updatedAt ?? card.createdAt)).map((card) => ({
615616
616014
  card,
615617
616015
  score: telegramMemorySimilarity(
@@ -615649,12 +616047,19 @@ ${notes2}`;
615649
616047
  const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
615650
616048
  limit: maxResults,
615651
616049
  userId: effectiveUserId,
615652
- username: effectiveUsername
616050
+ username: effectiveUsername,
616051
+ since,
616052
+ until,
616053
+ bucket
616054
+ });
616055
+ const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults).filter((episode) => inWindow(episode.timestamp)).sort((a2, b) => {
616056
+ if (bucket === "earliest") return (a2.timestamp ?? 0) - (b.timestamp ?? 0);
616057
+ if (bucket === "latest") return (b.timestamp ?? 0) - (a2.timestamp ?? 0);
616058
+ return 0;
615653
616059
  });
615654
- const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults);
615655
- const historyForScan = this.chatHistory.get(msgSessionKey) ?? [];
616060
+ const historyForScan = this.telegramMergedHistoryForSession(msgSessionKey);
615656
616061
  const lowerQuery = query.toLowerCase().trim();
615657
- const queryWords = lowerQuery.split(/\s+/).filter((w) => w.length >= 3);
616062
+ const queryWords = [...meaningfulQueryTokens];
615658
616063
  const hasTimeConstraint = since !== void 0 || until !== void 0 || bucket !== void 0;
615659
616064
  const rawHistoryMatches = [];
615660
616065
  if (historyForScan.length > 0) {
@@ -615666,7 +616071,13 @@ ${notes2}`;
615666
616071
  if (!matchesUser) continue;
615667
616072
  }
615668
616073
  if (!inWindow(entry.ts ?? null)) continue;
615669
- const hay = String(entry.text || "").toLowerCase();
616074
+ const hay = [
616075
+ entry.text || "",
616076
+ telegramHistorySpeaker(entry),
616077
+ entry.username || "",
616078
+ entry.firstName || "",
616079
+ entry.role === "assistant" || entry.isBot ? "bot" : "human"
616080
+ ].join(" ").toLowerCase();
615670
616081
  if (!hay) continue;
615671
616082
  let lexMatch = false;
615672
616083
  if (lowerQuery.length > 0) {
@@ -615674,7 +616085,7 @@ ${notes2}`;
615674
616085
  const tokenHits = queryWords.filter((w) => hay.includes(w)).length;
615675
616086
  lexMatch = hasExact || queryWords.length > 0 && tokenHits >= Math.min(2, queryWords.length);
615676
616087
  }
615677
- if (lexMatch || hasTimeConstraint) {
616088
+ if (lexMatch || hasTimeConstraint && queryWords.length === 0) {
615678
616089
  rawHistoryMatches.push(entry);
615679
616090
  }
615680
616091
  }
@@ -615687,6 +616098,44 @@ ${notes2}`;
615687
616098
  const rawHistorySliced = rawHistoryMatches.slice(0, maxResults);
615688
616099
  const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
615689
616100
  const sections = [];
616101
+ const wantsChronologicalAnchors = bucket !== void 0 || /\b(oldest|earliest|first|newest|latest|most\s+recent|how\s+far\s+back)\b/i.test(rawQuery);
616102
+ if (wantsChronologicalAnchors) {
616103
+ const anchors = this.telegramHistoryAnchorsForSession(msgSessionKey, maxResults);
616104
+ const anchorEntries = [
616105
+ ...anchors.earliest.map((entry, index) => ({ label: index === 0 ? "EARLIEST" : `${index + 1}${index === 1 ? "nd" : index === 2 ? "rd" : "th"} earliest`, entry })),
616106
+ ...anchors.latest ? [{ label: "LATEST", entry: anchors.latest }] : []
616107
+ ];
616108
+ const seen = /* @__PURE__ */ new Set();
616109
+ const lines = anchorEntries.flatMap(({ label, entry }) => {
616110
+ const key = entry.messageId !== void 0 ? `${entry.role}:${entry.messageId}` : `${entry.role}:${entry.ts ?? ""}:${entry.text}`;
616111
+ if (seen.has(key)) return [];
616112
+ seen.add(key);
616113
+ const when = entry.ts ? new Date(entry.ts).toISOString() : "";
616114
+ const speaker = telegramHistorySpeaker(entry);
616115
+ const mode = entry.mode ? `/${entry.mode}` : "";
616116
+ const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
616117
+ return [`- ${label}: ${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`];
616118
+ });
616119
+ if (lines.length > 0) {
616120
+ sections.push(`### Chronological Conversation Anchors
616121
+ SQLite mirror preferred; use these for oldest/newest recall.
616122
+ ${lines.join("\n")}`);
616123
+ }
616124
+ }
616125
+ const wantsActivityStats = /\b(activity|stats|statistics|most\s+active|message\s+counts?|participant\s+counts?|who\s+(?:has\s+been\s+)?(?:is\s+)?(?:most\s+)?active)\b/i.test(rawQuery);
616126
+ if (wantsActivityStats) {
616127
+ const stats = this.telegramParticipantActivityStats(msgSessionKey, {
616128
+ limit: Math.min(12, maxResults),
616129
+ since,
616130
+ until,
616131
+ userId: effectiveUserId,
616132
+ username: effectiveUsername
616133
+ });
616134
+ if (stats.length > 0) {
616135
+ sections.push(`### Participant Activity Stats
616136
+ ${this.formatTelegramParticipantActivityStats(stats)}`);
616137
+ }
616138
+ }
615690
616139
  if (cardLines.length > 0) {
615691
616140
  sections.push(`### Scoped Memory Cards
615692
616141
  ${cardLines.join("\n\n")}`);
@@ -616655,6 +617104,7 @@ Scoped workspace: ${scopedRoot}`,
616655
617104
  username: entry.username,
616656
617105
  first_name: entry.firstName,
616657
617106
  from_user_id: entry.fromUserId,
617107
+ is_bot: entry.role === "assistant" || Boolean(entry.isBot),
616658
617108
  message_id: entry.messageId,
616659
617109
  reply_to_message_id: entry.replyToMessageId,
616660
617110
  chat_type: entry.chatType,
@@ -617797,6 +618247,7 @@ Content-Type: ${contentType}\r
617797
618247
  mode: "action",
617798
618248
  chatId,
617799
618249
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
618250
+ isBot: true,
617800
618251
  messageId,
617801
618252
  mediaSummary: summary,
617802
618253
  generatedMediaPromptInfo: info
@@ -617856,6 +618307,7 @@ Content-Type: ${contentType}\r
617856
618307
  mode: "action",
617857
618308
  chatId,
617858
618309
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
618310
+ isBot: true,
617859
618311
  messageId,
617860
618312
  mediaSummary: summary,
617861
618313
  generatedMediaPromptInfo: info
@@ -618316,11 +618768,11 @@ ${caption}\r
618316
618768
  const msg = normalizeTelegramUpdate(update2);
618317
618769
  if (!msg) continue;
618318
618770
  this.persistTelegramRawMessage(update2, msg);
618771
+ this.state.messagesReceived++;
618319
618772
  const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
618320
618773
  if (this.adminUserId && !this.agentConfig) {
618321
618774
  if (!isAdmin) continue;
618322
618775
  }
618323
- this.state.messagesReceived++;
618324
618776
  if (this.agentConfig && this.repoRoot) {
618325
618777
  this.handleMessageWithSubAgent(msg).catch((err) => {
618326
618778
  this.tuiWrite(() => renderWarning(`Telegram sub-agent error: ${err instanceof Error ? err.message : String(err)}`));
@@ -618416,9 +618868,9 @@ import {
618416
618868
  unlinkSync as unlinkSync23
618417
618869
  } from "node:fs";
618418
618870
  import { join as join128 } from "node:path";
618419
- import { homedir as homedir40 } from "node:os";
618871
+ import { homedir as homedir41 } from "node:os";
618420
618872
  function sessionsDir() {
618421
- return join128(homedir40(), ".omnius", "chat-sessions");
618873
+ return join128(homedir41(), ".omnius", "chat-sessions");
618422
618874
  }
618423
618875
  function sessionPath(id) {
618424
618876
  const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
@@ -619058,7 +619510,7 @@ __export(projects_exports, {
619058
619510
  unregisterProject: () => unregisterProject
619059
619511
  });
619060
619512
  import { readFileSync as readFileSync94, writeFileSync as writeFileSync61, mkdirSync as mkdirSync67, existsSync as existsSync115, statSync as statSync40, renameSync as renameSync7 } from "node:fs";
619061
- import { homedir as homedir41 } from "node:os";
619513
+ import { homedir as homedir42 } from "node:os";
619062
619514
  import { basename as basename28, join as join129, resolve as resolve44 } from "node:path";
619063
619515
  import { randomUUID as randomUUID14 } from "node:crypto";
619064
619516
  function readAll2() {
@@ -619171,7 +619623,7 @@ var OMNIUS_DIR3, PROJECTS_FILE, CURRENT_FILE, currentRoot;
619171
619623
  var init_projects = __esm({
619172
619624
  "packages/cli/src/api/projects.ts"() {
619173
619625
  "use strict";
619174
- OMNIUS_DIR3 = join129(homedir41(), ".omnius");
619626
+ OMNIUS_DIR3 = join129(homedir42(), ".omnius");
619175
619627
  PROJECTS_FILE = join129(OMNIUS_DIR3, "projects.json");
619176
619628
  CURRENT_FILE = join129(OMNIUS_DIR3, "current-project");
619177
619629
  currentRoot = null;
@@ -619878,7 +620330,7 @@ var init_access_policy = __esm({
619878
620330
  // packages/cli/src/api/project-preferences.ts
619879
620331
  import { createHash as createHash24 } from "node:crypto";
619880
620332
  import { existsSync as existsSync116, mkdirSync as mkdirSync68, readFileSync as readFileSync95, renameSync as renameSync8, writeFileSync as writeFileSync62, unlinkSync as unlinkSync24 } from "node:fs";
619881
- import { homedir as homedir42 } from "node:os";
620333
+ import { homedir as homedir43 } from "node:os";
619882
620334
  import { join as join130, resolve as resolve45 } from "node:path";
619883
620335
  import { randomUUID as randomUUID15 } from "node:crypto";
619884
620336
  function projectKey(root) {
@@ -619959,7 +620411,7 @@ var OMNIUS_DIR4, PROJECTS_DIR, SCHEMA_VERSION, DEFAULT_PREFS;
619959
620411
  var init_project_preferences = __esm({
619960
620412
  "packages/cli/src/api/project-preferences.ts"() {
619961
620413
  "use strict";
619962
- OMNIUS_DIR4 = join130(homedir42(), ".omnius");
620414
+ OMNIUS_DIR4 = join130(homedir43(), ".omnius");
619963
620415
  PROJECTS_DIR = join130(OMNIUS_DIR4, "projects");
619964
620416
  SCHEMA_VERSION = 1;
619965
620417
  DEFAULT_PREFS = {
@@ -621235,7 +621687,7 @@ __export(aiwg_exports, {
621235
621687
  });
621236
621688
  import { existsSync as existsSync119, readFileSync as readFileSync97, readdirSync as readdirSync42, statSync as statSync42 } from "node:fs";
621237
621689
  import { join as join132 } from "node:path";
621238
- import { homedir as homedir43 } from "node:os";
621690
+ import { homedir as homedir44 } from "node:os";
621239
621691
  import { execSync as execSync55 } from "node:child_process";
621240
621692
  function resolveAiwgRoot() {
621241
621693
  if (_cachedAiwgRoot !== void 0) return _cachedAiwgRoot;
@@ -621244,7 +621696,7 @@ function resolveAiwgRoot() {
621244
621696
  _cachedAiwgRoot = envRoot;
621245
621697
  return envRoot;
621246
621698
  }
621247
- const shareDir = join132(homedir43(), ".local", "share", "ai-writing-guide");
621699
+ const shareDir = join132(homedir44(), ".local", "share", "ai-writing-guide");
621248
621700
  if (existsSync119(join132(shareDir, "agentic"))) {
621249
621701
  _cachedAiwgRoot = shareDir;
621250
621702
  return shareDir;
@@ -621273,8 +621725,8 @@ function resolveAiwgRoot() {
621273
621725
  }
621274
621726
  }
621275
621727
  const versionDirs = [
621276
- join132(homedir43(), ".nvm", "versions", "node"),
621277
- join132(homedir43(), ".local", "share", "fnm", "node-versions")
621728
+ join132(homedir44(), ".nvm", "versions", "node"),
621729
+ join132(homedir44(), ".local", "share", "fnm", "node-versions")
621278
621730
  ];
621279
621731
  for (const vdir of versionDirs) {
621280
621732
  if (!existsSync119(vdir)) continue;
@@ -622022,10 +622474,10 @@ __export(runtime_keys_exports, {
622022
622474
  });
622023
622475
  import { existsSync as existsSync120, readFileSync as readFileSync98, writeFileSync as writeFileSync63, mkdirSync as mkdirSync71, chmodSync as chmodSync2 } from "node:fs";
622024
622476
  import { join as join133 } from "node:path";
622025
- import { homedir as homedir44 } from "node:os";
622477
+ import { homedir as homedir45 } from "node:os";
622026
622478
  import { randomBytes as randomBytes23 } from "node:crypto";
622027
622479
  function ensureDir2() {
622028
- const dir = join133(homedir44(), ".omnius");
622480
+ const dir = join133(homedir45(), ".omnius");
622029
622481
  if (!existsSync120(dir)) mkdirSync71(dir, { recursive: true });
622030
622482
  }
622031
622483
  function loadAll() {
@@ -622106,7 +622558,7 @@ var KEYS_FILE;
622106
622558
  var init_runtime_keys = __esm({
622107
622559
  "packages/cli/src/api/runtime-keys.ts"() {
622108
622560
  "use strict";
622109
- KEYS_FILE = join133(homedir44(), ".omnius", "keys.json");
622561
+ KEYS_FILE = join133(homedir45(), ".omnius", "keys.json");
622110
622562
  }
622111
622563
  });
622112
622564
 
@@ -622118,13 +622570,13 @@ __export(tor_fallback_exports, {
622118
622570
  tunnelViaTor: () => tunnelViaTor
622119
622571
  });
622120
622572
  import { existsSync as existsSync121, readFileSync as readFileSync99 } from "node:fs";
622121
- import { homedir as homedir45 } from "node:os";
622573
+ import { homedir as homedir46 } from "node:os";
622122
622574
  import { join as join134 } from "node:path";
622123
622575
  import { createConnection as createConnection3 } from "node:net";
622124
622576
  function getLocalOnion() {
622125
622577
  const candidates = [
622126
- join134(homedir45(), "hidden_service_hostname"),
622127
- join134(homedir45(), ".omnius", "tor", "hostname"),
622578
+ join134(homedir46(), "hidden_service_hostname"),
622579
+ join134(homedir46(), ".omnius", "tor", "hostname"),
622128
622580
  "/var/lib/tor/hidden_service/hostname"
622129
622581
  ];
622130
622582
  for (const p2 of candidates) {
@@ -622393,7 +622845,7 @@ var init_graphical_sudo = __esm({
622393
622845
  // packages/cli/src/api/routes-v1.ts
622394
622846
  import { existsSync as existsSync123, readFileSync as readFileSync100, readdirSync as readdirSync43, statSync as statSync43 } from "node:fs";
622395
622847
  import { join as join136, resolve as pathResolve2 } from "node:path";
622396
- import { homedir as homedir46 } from "node:os";
622848
+ import { homedir as homedir47 } from "node:os";
622397
622849
  async function tryRouteV1(ctx3) {
622398
622850
  const { pathname, method } = ctx3;
622399
622851
  if (pathname === "/v1/skills" && method === "GET") {
@@ -622628,7 +623080,7 @@ async function handleGetSkill(ctx3, name10) {
622628
623080
  async function fallbackDiscoverSkills() {
622629
623081
  return (_root) => {
622630
623082
  const roots = [
622631
- join136(homedir46(), ".local", "share", "ai-writing-guide")
623083
+ join136(homedir47(), ".local", "share", "ai-writing-guide")
622632
623084
  ];
622633
623085
  const out = [];
622634
623086
  for (const root of roots) {
@@ -623363,7 +623815,7 @@ async function handleNexusStatus(ctx3) {
623363
623815
  try {
623364
623816
  const statePaths = [
623365
623817
  join136(process.cwd(), ".omnius", "nexus-peer-state.json"),
623366
- join136(homedir46(), ".omnius", "nexus-peer-cache.json")
623818
+ join136(homedir47(), ".omnius", "nexus-peer-cache.json")
623367
623819
  ];
623368
623820
  const states = [];
623369
623821
  for (const p2 of statePaths) {
@@ -623396,7 +623848,7 @@ async function handleNexusStatus(ctx3) {
623396
623848
  }
623397
623849
  function loadAgentName() {
623398
623850
  try {
623399
- const p2 = join136(homedir46(), ".omnius", "agent-name");
623851
+ const p2 = join136(homedir47(), ".omnius", "agent-name");
623400
623852
  if (existsSync123(p2)) return readFileSync100(p2, "utf-8").trim();
623401
623853
  } catch {
623402
623854
  }
@@ -623406,8 +623858,8 @@ async function handleSponsors(ctx3) {
623406
623858
  const { req: req2, res, url, requestId } = ctx3;
623407
623859
  try {
623408
623860
  const candidates = [
623409
- join136(homedir46(), ".omnius", "sponsor-cache.json"),
623410
- join136(homedir46(), ".omnius", "sponsors.json")
623861
+ join136(homedir47(), ".omnius", "sponsor-cache.json"),
623862
+ join136(homedir47(), ".omnius", "sponsors.json")
623411
623863
  ];
623412
623864
  let sponsors = [];
623413
623865
  for (const p2 of candidates) {
@@ -623664,9 +624116,9 @@ function resolveLocalPeerId() {
623664
624116
  const projectScoped = scope === "project" || scope === "local" || projectScopeFlag === "1" || projectScopeFlag === "true" || projectScopeFlag === "yes";
623665
624117
  const candidates = projectScoped ? [
623666
624118
  join136(process.cwd(), ".omnius", "nexus", "status.json"),
623667
- join136(homedir46(), ".omnius", "nexus", "status.json")
624119
+ join136(homedir47(), ".omnius", "nexus", "status.json")
623668
624120
  ] : [
623669
- join136(homedir46(), ".omnius", "nexus", "status.json"),
624121
+ join136(homedir47(), ".omnius", "nexus", "status.json"),
623670
624122
  join136(process.cwd(), ".omnius", "nexus", "status.json")
623671
624123
  ];
623672
624124
  for (const p2 of candidates) {
@@ -623674,7 +624126,7 @@ function resolveLocalPeerId() {
623674
624126
  if (r2) return r2;
623675
624127
  }
623676
624128
  try {
623677
- const regPath = join136(homedir46(), ".omnius", "nexus-registry.json");
624129
+ const regPath = join136(homedir47(), ".omnius", "nexus-registry.json");
623678
624130
  if (existsSync123(regPath)) {
623679
624131
  const reg = JSON.parse(readFileSync100(regPath, "utf-8"));
623680
624132
  const entries = Array.isArray(reg?.dirs) ? reg.dirs : [];
@@ -623695,7 +624147,7 @@ function resolveLocalPeerId() {
623695
624147
  let scanResult = null;
623696
624148
  try {
623697
624149
  const { execSync: execSync60 } = __require("node:child_process");
623698
- const cmd = `find "${homedir46()}" -maxdepth 4 -path '*/.omnius/nexus/status.json' -type f 2>/dev/null | head -50`;
624150
+ const cmd = `find "${homedir47()}" -maxdepth 4 -path '*/.omnius/nexus/status.json' -type f 2>/dev/null | head -50`;
623699
624151
  const out = execSync60(cmd, { encoding: "utf-8", timeout: 2e3 }).trim();
623700
624152
  for (const line of out.split("\n")) {
623701
624153
  const f2 = line.trim();
@@ -624002,9 +624454,9 @@ async function handleRemoteProxy(ctx3) {
624002
624454
  const tunnelProjectScope = tunnelScope === "project" || tunnelScope === "local" || ["1", "true", "yes"].includes((process.env["OMNIUS_NEXUS_PROJECT_SCOPE"] || "").toLowerCase());
624003
624455
  const nexusCandidates = tunnelProjectScope ? [
624004
624456
  join136(process.cwd(), ".omnius", "nexus"),
624005
- join136(homedir46(), ".omnius", "nexus")
624457
+ join136(homedir47(), ".omnius", "nexus")
624006
624458
  ] : [
624007
- join136(homedir46(), ".omnius", "nexus"),
624459
+ join136(homedir47(), ".omnius", "nexus"),
624008
624460
  join136(process.cwd(), ".omnius", "nexus")
624009
624461
  ];
624010
624462
  let nexusDirPath = null;
@@ -624692,7 +625144,7 @@ async function handleListAgentTypes(ctx3) {
624692
625144
  }
624693
625145
  async function handleListEngines(ctx3) {
624694
625146
  const { res } = ctx3;
624695
- const home = homedir46();
625147
+ const home = homedir47();
624696
625148
  sendJson(res, 200, {
624697
625149
  engines: [
624698
625150
  { name: "dream", state_file: join136(process.cwd(), ".omnius", "dreams"), controllable_via: "SSE + slash commands" },
@@ -624785,7 +625237,7 @@ async function tryAimsRoute(ctx3) {
624785
625237
  return false;
624786
625238
  }
624787
625239
  function aimsDir() {
624788
- return join136(homedir46(), ".omnius", "aims");
625240
+ return join136(homedir47(), ".omnius", "aims");
624789
625241
  }
624790
625242
  function readAimsFile(name10, fallback) {
624791
625243
  try {
@@ -625129,7 +625581,7 @@ async function handleAimsSuppliers(ctx3) {
625129
625581
  }
625130
625582
  ];
625131
625583
  const sponsorPaths = [
625132
- join136(homedir46(), ".omnius", "sponsor-cache.json")
625584
+ join136(homedir47(), ".omnius", "sponsor-cache.json")
625133
625585
  ];
625134
625586
  for (const p2 of sponsorPaths) {
625135
625587
  if (!existsSync123(p2)) continue;
@@ -634654,10 +635106,10 @@ var init_usage_tracker = __esm({
634654
635106
  // packages/cli/src/api/profiles.ts
634655
635107
  import { existsSync as existsSync125, readFileSync as readFileSync102, writeFileSync as writeFileSync66, mkdirSync as mkdirSync74, readdirSync as readdirSync44, unlinkSync as unlinkSync25 } from "node:fs";
634656
635108
  import { join as join138 } from "node:path";
634657
- import { homedir as homedir47 } from "node:os";
635109
+ import { homedir as homedir48 } from "node:os";
634658
635110
  import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes24, scryptSync as scryptSync3 } from "node:crypto";
634659
635111
  function globalProfileDir() {
634660
- return join138(homedir47(), ".omnius", "profiles");
635112
+ return join138(homedir48(), ".omnius", "profiles");
634661
635113
  }
634662
635114
  function projectProfileDir(projectDir2) {
634663
635115
  return join138(projectDir2 || process.cwd(), ".omnius", "profiles");
@@ -634911,7 +635363,7 @@ var init_profiles = __esm({
634911
635363
  import { execSync as execSync56, spawn as spawn29 } from "node:child_process";
634912
635364
  import { existsSync as existsSync126, mkdirSync as mkdirSync75, writeFileSync as writeFileSync67 } from "node:fs";
634913
635365
  import { join as join139, resolve as resolve46, dirname as dirname37 } from "node:path";
634914
- import { homedir as homedir48 } from "node:os";
635366
+ import { homedir as homedir49 } from "node:os";
634915
635367
  import { fileURLToPath as fileURLToPath16 } from "node:url";
634916
635368
  function getDockerDir() {
634917
635369
  try {
@@ -635059,7 +635511,7 @@ async function ensureOmniusImage(force = false) {
635059
635511
  if (existsSync126(join139(dockerDir, "Dockerfile"))) {
635060
635512
  buildContext = dockerDir;
635061
635513
  } else {
635062
- buildContext = join139(homedir48(), ".omnius", "docker-build");
635514
+ buildContext = join139(homedir49(), ".omnius", "docker-build");
635063
635515
  mkdirSync75(buildContext, { recursive: true });
635064
635516
  writeDockerfiles(buildContext);
635065
635517
  }
@@ -635391,7 +635843,7 @@ import * as https3 from "node:https";
635391
635843
  import { createRequire as createRequire7 } from "node:module";
635392
635844
  import { fileURLToPath as fileURLToPath17 } from "node:url";
635393
635845
  import { dirname as dirname38, join as join141, resolve as resolve47 } from "node:path";
635394
- import { homedir as homedir49 } from "node:os";
635846
+ import { homedir as homedir50 } from "node:os";
635395
635847
  import { spawn as spawn30, execSync as execSync57 } from "node:child_process";
635396
635848
  import { mkdirSync as mkdirSync76, writeFileSync as writeFileSync68, readFileSync as readFileSync103, readdirSync as readdirSync45, existsSync as existsSync127, watch as fsWatch3, renameSync as renameSync9, unlinkSync as unlinkSync26 } from "node:fs";
635397
635849
  import { randomBytes as randomBytes25, randomUUID as randomUUID16 } from "node:crypto";
@@ -635706,7 +636158,7 @@ function isOriginAllowed(origin) {
635706
636158
  if (!origin) return true;
635707
636159
  let accessMode = (process.env["OMNIUS_ACCESS"] || "").toLowerCase().trim();
635708
636160
  try {
635709
- const accessFile = join141(homedir49(), ".omnius", "access");
636161
+ const accessFile = join141(homedir50(), ".omnius", "access");
635710
636162
  if (existsSync127(accessFile)) {
635711
636163
  const persisted = readFileSync103(accessFile, "utf8").trim().toLowerCase();
635712
636164
  if (persisted === "any" || persisted === "lan" || persisted === "loopback") {
@@ -637945,10 +638397,10 @@ ${task}` : task;
637945
638397
  });
637946
638398
  }
637947
638399
  function updateStateFile() {
637948
- return join141(homedir49(), ".omnius", "update-state.json");
638400
+ return join141(homedir50(), ".omnius", "update-state.json");
637949
638401
  }
637950
638402
  function updateLogPath() {
637951
- return join141(homedir49(), ".omnius", "update.log");
638403
+ return join141(homedir50(), ".omnius", "update.log");
637952
638404
  }
637953
638405
  function readUpdateState() {
637954
638406
  try {
@@ -637961,7 +638413,7 @@ function readUpdateState() {
637961
638413
  }
637962
638414
  function writeUpdateState(state) {
637963
638415
  try {
637964
- const dir = join141(homedir49(), ".omnius");
638416
+ const dir = join141(homedir50(), ".omnius");
637965
638417
  mkdirSync76(dir, { recursive: true });
637966
638418
  const finalPath = updateStateFile();
637967
638419
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
@@ -638017,7 +638469,7 @@ async function handleV1Update(req2, res, requestId) {
638017
638469
  }
638018
638470
  if (!npmBin) npmBin = isWin2 ? "npm.cmd" : "npm";
638019
638471
  const pkgSpec = `omnius@${targetVersion}`;
638020
- const dir = join141(homedir49(), ".omnius");
638472
+ const dir = join141(homedir50(), ".omnius");
638021
638473
  fs11.mkdirSync(dir, { recursive: true });
638022
638474
  const logFd = fs11.openSync(logPath3, "w");
638023
638475
  const npmPrefix = dirname38(nodeDir);
@@ -641252,7 +641704,7 @@ ${steering}`;
641252
641704
  function getScheduleRoots() {
641253
641705
  const rootsEnv = process.env["OMNIUS_SCHEDULE_ROOTS"] || "";
641254
641706
  const roots = rootsEnv.split(rootsEnv.includes(";") ? ";" : ":").filter(Boolean);
641255
- const defaults3 = [process.cwd(), join141(homedir49(), "Documents")];
641707
+ const defaults3 = [process.cwd(), join141(homedir50(), "Documents")];
641256
641708
  const set = /* @__PURE__ */ new Set([...defaults3, ...roots]);
641257
641709
  return [...set];
641258
641710
  }
@@ -641757,7 +642209,7 @@ function fixupOrMigrateScheduled(mode, dryRun) {
641757
642209
  try {
641758
642210
  if (!f2.workingDir || !f2.task) continue;
641759
642211
  const unitBase = `omnius-${f2.id}`;
641760
- const unitDir = join141(homedir49(), ".config", "systemd", "user");
642212
+ const unitDir = join141(homedir50(), ".config", "systemd", "user");
641761
642213
  const svc = join141(unitDir, `${unitBase}.service`);
641762
642214
  const tim = join141(unitDir, `${unitBase}.timer`);
641763
642215
  const omniusBin = findOmniusBinary4();
@@ -642050,7 +642502,7 @@ function startApiServer(options2 = {}) {
642050
642502
  }
642051
642503
  let runtimeAccessMode = resolveAccessMode(process.env["OMNIUS_ACCESS"], host);
642052
642504
  try {
642053
- const accessFile = join141(homedir49(), ".omnius", "access");
642505
+ const accessFile = join141(homedir50(), ".omnius", "access");
642054
642506
  if (existsSync127(accessFile)) {
642055
642507
  const persisted = readFileSync103(accessFile, "utf8").trim();
642056
642508
  const resolved = resolveAccessMode(persisted, host);
@@ -642112,7 +642564,7 @@ function startApiServer(options2 = {}) {
642112
642564
  const previous = runtimeAccessMode;
642113
642565
  runtimeAccessMode = requested;
642114
642566
  try {
642115
- const dir = join141(homedir49(), ".omnius");
642567
+ const dir = join141(homedir50(), ".omnius");
642116
642568
  mkdirSync76(dir, { recursive: true });
642117
642569
  writeFileSync68(join141(dir, "access"), `${runtimeAccessMode}
642118
642570
  `, "utf8");
@@ -643182,7 +643634,7 @@ import {
643182
643634
  } from "node:fs";
643183
643635
  import { existsSync as existsSync128 } from "node:fs";
643184
643636
  import { execSync as execSync59 } from "node:child_process";
643185
- import { homedir as homedir50 } from "node:os";
643637
+ import { homedir as homedir51 } from "node:os";
643186
643638
  function formatTimeAgo2(date) {
643187
643639
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
643188
643640
  if (seconds < 60) return "just now";
@@ -644315,7 +644767,7 @@ function extractGeneratedAudioPath(output, repoRoot) {
644315
644767
  const match = output.match(/(?:Sound|Music|TTS) generated:\s+([^\n\r]+)/i);
644316
644768
  const raw = match?.[1]?.trim().replace(/^["']|["']$/g, "");
644317
644769
  if (!raw) return null;
644318
- return raw.startsWith("/") || raw.startsWith("~") ? raw.replace(/^~(?=\/)/, homedir50()) : join143(repoRoot, raw);
644770
+ return raw.startsWith("/") || raw.startsWith("~") ? raw.replace(/^~(?=\/)/, homedir51()) : join143(repoRoot, raw);
644319
644771
  }
644320
644772
  async function playGeneratedAudioForToolResult(toolName, output, repoRoot, writer) {
644321
644773
  if (!toolName || !["generate_audio", "generate_tts", "audio_playback"].includes(toolName) || !output) return;
@@ -646366,7 +646818,9 @@ async function startInteractive(config, repoPath) {
646366
646818
  } catch {
646367
646819
  }
646368
646820
  initOmniusDirectory(repoRoot);
646369
- const savedSettings = resolveSettings(repoRoot);
646821
+ const globalSettings = loadGlobalSettings();
646822
+ const projectSettings = loadProjectSettings(repoRoot);
646823
+ const savedSettings = { ...globalSettings, ...projectSettings };
646370
646824
  try {
646371
646825
  if (savedSettings.omniusAccess && !process.env["OMNIUS_ACCESS"]) {
646372
646826
  process.env["OMNIUS_ACCESS"] = String(savedSettings.omniusAccess);
@@ -647527,7 +647981,7 @@ This is an independent background session started from /background.`
647527
647981
  );
647528
647982
  return [hits, line];
647529
647983
  }
647530
- const HISTORY_DIR = join143(homedir50(), ".omnius");
647984
+ const HISTORY_DIR = join143(homedir51(), ".omnius");
647531
647985
  const HISTORY_FILE = join143(HISTORY_DIR, "repl-history");
647532
647986
  const MAX_HISTORY_LINES = 500;
647533
647987
  let savedHistory = [];
@@ -648389,6 +648843,32 @@ Log: ${nexusLogPath}`)
648389
648843
  }
648390
648844
  };
648391
648845
  workEvaluator.setBackend(evalBackend);
648846
+ let activeTelegramSettingsScope = null;
648847
+ const nonEmptyTelegramSetting = (value2) => {
648848
+ const trimmed = String(value2 ?? "").trim();
648849
+ return trimmed ? trimmed : void 0;
648850
+ };
648851
+ const refreshResolvedSettings = () => {
648852
+ const merged = { ...globalSettings, ...projectSettings };
648853
+ for (const key of Object.keys(savedSettings)) {
648854
+ delete savedSettings[key];
648855
+ }
648856
+ Object.assign(savedSettings, merged);
648857
+ };
648858
+ const telegramSettingsForScope = (scope = "project") => {
648859
+ if (scope === "effective") {
648860
+ const activeScope = activeTelegramSettingsScope ?? "project";
648861
+ return telegramSettingsForScope(activeScope);
648862
+ }
648863
+ const source = scope === "global" ? globalSettings : projectSettings;
648864
+ const mode = source.telegramMode ?? savedSettings.telegramMode ?? "auto";
648865
+ return {
648866
+ key: nonEmptyTelegramSetting(source.telegramKey),
648867
+ admin: nonEmptyTelegramSetting(source.telegramAdmin),
648868
+ mode,
648869
+ keyScope: scope
648870
+ };
648871
+ };
648392
648872
  const commandCtx = {
648393
648873
  get config() {
648394
648874
  return currentConfig;
@@ -648950,7 +649430,7 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
648950
649430
  return blessEngine?.isActive ?? false;
648951
649431
  },
648952
649432
  // Telegram bridge
648953
- async telegramStart(token, adminId) {
649433
+ async telegramStart(token, adminId, scope = "project") {
648954
649434
  if (telegramBridge?.isActive) {
648955
649435
  writeContent(
648956
649436
  () => renderWarning("Telegram bridge already active. Use /telegram stop before restarting.")
@@ -648990,6 +649470,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648990
649470
  currentConfig,
648991
649471
  repoRoot
648992
649472
  );
649473
+ activeTelegramSettingsScope = scope;
648993
649474
  if (resolvedContextWindowSize > 0) {
648994
649475
  telegramBridge.setContextWindowSize(resolvedContextWindowSize);
648995
649476
  }
@@ -648999,10 +649480,16 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648999
649480
  telegramBridge.setAdmin(adminId);
649000
649481
  }
649001
649482
  telegramBridge.setAdminAuthHandler((newAdminId, username) => {
649002
- savedSettings.telegramAdmin = newAdminId;
649003
- saveGlobalSettings({ telegramAdmin: newAdminId });
649483
+ if (activeTelegramSettingsScope === "global") {
649484
+ globalSettings.telegramAdmin = newAdminId;
649485
+ saveGlobalSettings({ telegramAdmin: newAdminId });
649486
+ } else {
649487
+ projectSettings.telegramAdmin = newAdminId;
649488
+ saveProjectSettings(repoRoot, { telegramAdmin: newAdminId });
649489
+ }
649490
+ refreshResolvedSettings();
649004
649491
  writeContent(
649005
- () => renderInfo(`Telegram admin authenticated: @${username || "unknown"} (${newAdminId})`)
649492
+ () => renderInfo(`Telegram admin authenticated: @${username || "unknown"} (${newAdminId}) in ${activeTelegramSettingsScope ?? "project"} settings`)
649006
649493
  );
649007
649494
  });
649008
649495
  telegramBridge.setWriteContent(writeContent);
@@ -649059,6 +649546,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649059
649546
  });
649060
649547
  });
649061
649548
  }
649549
+ } else {
649550
+ emotionEngine.setAdminOutreach(() => {
649551
+ });
649062
649552
  }
649063
649553
  if (voiceEngine.enabled && voiceEngine.ready) {
649064
649554
  telegramBridge.setVoiceEngine(voiceEngine);
@@ -649099,7 +649589,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649099
649589
  throw err;
649100
649590
  }
649101
649591
  });
649102
- await telegramBridge.start();
649592
+ try {
649593
+ await telegramBridge.start();
649594
+ } catch (err) {
649595
+ telegramBridge.stop();
649596
+ telegramBridge = null;
649597
+ activeTelegramSettingsScope = null;
649598
+ throw err;
649599
+ }
649103
649600
  statusBar.setTelegramStatus(true, telegramBridge.stats.activeSubAgents);
649104
649601
  telegramBridge.registerScopedMyCommands(adminId).catch((err) => {
649105
649602
  writeContent(
@@ -649107,7 +649604,12 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649107
649604
  );
649108
649605
  });
649109
649606
  writeContent(
649110
- () => renderTelegramStart(telegramBridge.botUsername, adminId, savedSettings.telegramMode ?? "auto")
649607
+ () => renderTelegramStart(
649608
+ telegramBridge.botUsername,
649609
+ adminId,
649610
+ savedSettings.telegramMode ?? "auto",
649611
+ telegramBridge.stats.canReadAllGroupMessages
649612
+ )
649111
649613
  );
649112
649614
  showPrompt();
649113
649615
  },
@@ -649117,41 +649619,56 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649117
649619
  telegramBridge.stop();
649118
649620
  writeContent(() => renderTelegramStop(stats));
649119
649621
  telegramBridge = null;
649622
+ activeTelegramSettingsScope = null;
649120
649623
  statusBar.setTelegramStatus(false, 0);
649121
649624
  }
649122
649625
  },
649123
649626
  isTelegramActive() {
649124
649627
  return telegramBridge?.isActive ?? false;
649125
649628
  },
649126
- getTelegramSettings() {
649127
- return {
649128
- key: savedSettings.telegramKey,
649129
- admin: savedSettings.telegramAdmin,
649130
- mode: savedSettings.telegramMode
649131
- };
649629
+ getTelegramSettings(scope = "project") {
649630
+ return telegramSettingsForScope(scope);
649132
649631
  },
649133
649632
  saveTelegramSettings(settings) {
649633
+ const targetScope = settings.local === false ? "global" : "project";
649634
+ const target = targetScope === "global" ? globalSettings : projectSettings;
649134
649635
  if (settings.key !== void 0) {
649135
- savedSettings.telegramKey = settings.key;
649636
+ target.telegramKey = settings.key;
649136
649637
  }
649137
649638
  if (settings.admin !== void 0) {
649138
- savedSettings.telegramAdmin = settings.admin;
649639
+ target.telegramAdmin = settings.admin === null ? "" : settings.admin;
649139
649640
  }
649140
649641
  if (settings.mode !== void 0) {
649141
- savedSettings.telegramMode = settings.mode;
649642
+ target.telegramMode = settings.mode;
649142
649643
  }
649143
649644
  const payload = {
649144
649645
  ...settings.key !== void 0 ? { telegramKey: settings.key } : {},
649145
- ...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {},
649646
+ ...settings.admin !== void 0 ? { telegramAdmin: settings.admin === null ? "" : settings.admin } : {},
649146
649647
  ...settings.mode !== void 0 ? { telegramMode: settings.mode } : {}
649147
649648
  };
649148
- const projectHasOmnius = existsSync128(join143(repoRoot, ".omnius"));
649149
- const useProject = settings.local === true || settings.local === void 0 && projectHasOmnius;
649150
- if (useProject) {
649649
+ if (targetScope === "project") {
649151
649650
  saveProjectSettings(repoRoot, payload);
649152
649651
  } else {
649153
649652
  saveGlobalSettings(payload);
649154
649653
  }
649654
+ refreshResolvedSettings();
649655
+ },
649656
+ telegramRevokeAdmin(scope) {
649657
+ const target = scope === "global" ? globalSettings : projectSettings;
649658
+ const previousAdmin = nonEmptyTelegramSetting(target.telegramAdmin) ?? null;
649659
+ target.telegramAdmin = "";
649660
+ if (scope === "global") {
649661
+ saveGlobalSettings({ telegramAdmin: "" });
649662
+ } else {
649663
+ saveProjectSettings(repoRoot, { telegramAdmin: "" });
649664
+ }
649665
+ refreshResolvedSettings();
649666
+ if (telegramBridge?.isActive && (activeTelegramSettingsScope ?? "project") === scope) {
649667
+ telegramBridge.setAdmin(null);
649668
+ emotionEngine.setAdminOutreach(() => {
649669
+ });
649670
+ }
649671
+ return { previousAdmin, scope };
649155
649672
  },
649156
649673
  telegramSetInteractionMode(mode) {
649157
649674
  savedSettings.telegramMode = mode;
@@ -649165,9 +649682,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649165
649682
  () => renderTelegramStatus(
649166
649683
  active,
649167
649684
  botUser,
649168
- savedSettings.telegramAdmin,
649685
+ telegramSettingsForScope(activeTelegramSettingsScope ?? "project").admin,
649169
649686
  subAgents,
649170
- savedSettings.telegramMode ?? "auto"
649687
+ savedSettings.telegramMode ?? "auto",
649688
+ active ? telegramBridge?.stats.canReadAllGroupMessages : void 0
649171
649689
  )
649172
649690
  );
649173
649691
  },
@@ -649250,7 +649768,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649250
649768
  const registry4 = createOmniusPlatformRegistry({
649251
649769
  telegram: () => ({
649252
649770
  active: telegramBridge?.isActive ?? false,
649253
- configured: !!savedSettings.telegramKey,
649771
+ configured: !!telegramSettingsForScope("project").key,
649254
649772
  enabled: telegramBridge?.isActive ?? false,
649255
649773
  botUsername: telegramBridge?.botUsername,
649256
649774
  activeSessions: telegramBridge?.stats.activeSubAgents ?? 0
@@ -650213,7 +650731,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650213
650731
  } catch {
650214
650732
  }
650215
650733
  try {
650216
- const voiceDir3 = join143(homedir50(), ".omnius", "voice");
650734
+ const voiceDir3 = join143(homedir51(), ".omnius", "voice");
650217
650735
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
650218
650736
  for (const pf of voicePidFiles) {
650219
650737
  const pidPath = join143(voiceDir3, pf);
@@ -650349,14 +650867,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650349
650867
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
650350
650868
  if (isPersonaPlexRunning2()) {
650351
650869
  const ppPidFile = join143(
650352
- homedir50(),
650870
+ homedir51(),
650353
650871
  ".omnius",
650354
650872
  "voice",
650355
650873
  "personaplex",
650356
650874
  "daemon.pid"
650357
650875
  );
650358
650876
  const ppPortFile = join143(
650359
- homedir50(),
650877
+ homedir51(),
650360
650878
  ".omnius",
650361
650879
  "voice",
650362
650880
  "personaplex",
@@ -650526,12 +651044,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650526
651044
  }, 100);
650527
651045
  }
650528
651046
  }
650529
- if (!isResumed && savedSettings.telegramKey) {
651047
+ const autoTelegramSettings = telegramSettingsForScope("project");
651048
+ if (!isResumed && autoTelegramSettings.key) {
650530
651049
  setTimeout(async () => {
650531
651050
  try {
650532
651051
  await commandCtx.telegramStart(
650533
- savedSettings.telegramKey,
650534
- savedSettings.telegramAdmin
651052
+ autoTelegramSettings.key,
651053
+ autoTelegramSettings.admin,
651054
+ "project"
650535
651055
  );
650536
651056
  } catch (err) {
650537
651057
  writeContent(
@@ -652526,7 +653046,7 @@ __export(config_exports2, {
652526
653046
  configCommand: () => configCommand
652527
653047
  });
652528
653048
  import { join as join145, resolve as resolve51 } from "node:path";
652529
- import { homedir as homedir51 } from "node:os";
653049
+ import { homedir as homedir52 } from "node:os";
652530
653050
  import { cwd as cwd3 } from "node:process";
652531
653051
  function redactIfSensitive(key, value2) {
652532
653052
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -652608,7 +653128,7 @@ function handleShow(opts, config) {
652608
653128
  }
652609
653129
  }
652610
653130
  printSection("Config File");
652611
- printInfo(`~/.omnius/config.json (${join145(homedir51(), ".omnius", "config.json")})`);
653131
+ printInfo(`~/.omnius/config.json (${join145(homedir52(), ".omnius", "config.json")})`);
652612
653132
  printSection("Priority Chain");
652613
653133
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
652614
653134
  printInfo(" 2. Project .omnius/settings.json (--local)");
@@ -653485,8 +654005,8 @@ function crashLog(label, err) {
653485
654005
  try {
653486
654006
  const { appendFileSync: appendFileSync12, mkdirSync: mkdirSync81 } = __require("node:fs");
653487
654007
  const { join: join148 } = __require("node:path");
653488
- const { homedir: homedir52 } = __require("node:os");
653489
- const logDir = join148(homedir52(), ".omnius");
654008
+ const { homedir: homedir53 } = __require("node:os");
654009
+ const logDir = join148(homedir53(), ".omnius");
653490
654010
  mkdirSync81(logDir, { recursive: true });
653491
654011
  appendFileSync12(join148(logDir, "crash.log"), logLine);
653492
654012
  } catch {