omnius 1.0.67 → 1.0.68

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 = {}) {
@@ -610049,6 +610091,13 @@ function telegramMemoryTokens(text) {
610049
610091
  }
610050
610092
  return tokens;
610051
610093
  }
610094
+ function telegramMeaningfulMemoryTokens(text) {
610095
+ const tokens = /* @__PURE__ */ new Set();
610096
+ for (const token of telegramMemoryTokens(text)) {
610097
+ if (!TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS.has(token)) tokens.add(token);
610098
+ }
610099
+ return tokens;
610100
+ }
610052
610101
  function telegramMemorySimilarity(a2, b) {
610053
610102
  if (a2.size === 0 || b.size === 0) return 0;
610054
610103
  let intersection = 0;
@@ -610884,7 +610933,7 @@ function renderTelegramSubAgentError(username, error) {
610884
610933
  process.stdout.write(` ${c3.dim("│")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
610885
610934
  `);
610886
610935
  }
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;
610936
+ 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
610937
  var init_telegram_bridge = __esm({
610889
610938
  "packages/cli/src/tui/telegram-bridge.ts"() {
610890
610939
  "use strict";
@@ -611208,6 +611257,47 @@ External acquisition contract:
611208
611257
  "you",
611209
611258
  "your"
611210
611259
  ]);
611260
+ TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS = /* @__PURE__ */ new Set([
611261
+ "active",
611262
+ "activity",
611263
+ "breakdown",
611264
+ "conversation",
611265
+ "convo",
611266
+ "count",
611267
+ "counts",
611268
+ "earliest",
611269
+ "entries",
611270
+ "entry",
611271
+ "find",
611272
+ "first",
611273
+ "give",
611274
+ "group",
611275
+ "latest",
611276
+ "memory",
611277
+ "memories",
611278
+ "mention",
611279
+ "mentioned",
611280
+ "mentions",
611281
+ "message",
611282
+ "messages",
611283
+ "most",
611284
+ "newest",
611285
+ "oldest",
611286
+ "recent",
611287
+ "recall",
611288
+ "show",
611289
+ "some",
611290
+ "stats",
611291
+ "statistics",
611292
+ "summary",
611293
+ "summarize",
611294
+ "tell",
611295
+ "today",
611296
+ "turn",
611297
+ "turns",
611298
+ "who",
611299
+ "yesterday"
611300
+ ]);
611211
611301
  TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS = {
611212
611302
  bruteForce: false,
611213
611303
  bruteForceMaxCycles: 0,
@@ -611337,6 +611427,7 @@ External acquisition contract:
611337
611427
  * (which would cause silent 409 conflicts on getUpdates). Released in stop().
611338
611428
  */
611339
611429
  telegramOwnerLockFile = null;
611430
+ telegramOwnerLockFiles = [];
611340
611431
  /** Session keys loaded from persistent conversation memory */
611341
611432
  loadedConversationState = /* @__PURE__ */ new Set();
611342
611433
  /** True once persisted Telegram conversation scopes have been bulk-loaded. */
@@ -613359,32 +613450,170 @@ ${lines.join("\n")}`);
613359
613450
  ORDER BY received_at DESC
613360
613451
  LIMIT ?
613361
613452
  `).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
- }));
613453
+ return rows.reverse().map((row) => this.telegramSqliteRowToHistoryEntry(row));
613378
613454
  } catch {
613379
613455
  return [];
613380
613456
  }
613381
613457
  }
613458
+ telegramSqliteRowToHistoryEntry(row) {
613459
+ const num = (value2) => {
613460
+ const n2 = Number(value2);
613461
+ return Number.isFinite(n2) ? n2 : void 0;
613462
+ };
613463
+ const role = row.role === "assistant" ? "assistant" : "user";
613464
+ const username = row.username ? String(row.username) : void 0;
613465
+ const fromUserId = num(row.from_user_id);
613466
+ return {
613467
+ role,
613468
+ text: String(row.text || ""),
613469
+ ts: num(row.received_at),
613470
+ chatId: row.chat_id,
613471
+ speaker: role === "assistant" ? `@${this.state.botUsername || username || "omnius"}` : username ? `@${username}` : fromUserId !== void 0 ? `user:${fromUserId}` : "unknown",
613472
+ username,
613473
+ firstName: row.first_name ? String(row.first_name) : void 0,
613474
+ fromUserId,
613475
+ messageId: num(row.message_id),
613476
+ messageThreadId: num(row.message_thread_id),
613477
+ replyToMessageId: num(row.reply_to_message_id),
613478
+ chatType: row.chat_type,
613479
+ chatTitle: row.chat_title || void 0,
613480
+ mediaSummary: row.media_json ? "media attached in raw Telegram SQLite mirror" : void 0
613481
+ };
613482
+ }
613483
+ telegramMergedHistoryForSession(sessionKey, limit = TELEGRAM_CHAT_HISTORY_LIMIT) {
613484
+ const sqliteHistory = this.telegramSqliteHistoryForSession(sessionKey, limit);
613485
+ const rollingHistory = this.chatHistory.get(sessionKey) ?? [];
613486
+ if (sqliteHistory.length === 0) return rollingHistory.slice(-limit);
613487
+ const merged = /* @__PURE__ */ new Map();
613488
+ const keyFor = (entry) => {
613489
+ if (entry.messageId !== void 0 && entry.messageId > 0) {
613490
+ return `msg:${entry.chatId ?? sessionKey}:${entry.role}:${entry.messageId}`;
613491
+ }
613492
+ return `synthetic:${entry.role}:${entry.ts ?? ""}:${telegramHistorySpeaker(entry)}:${entry.text}`;
613493
+ };
613494
+ for (const entry of sqliteHistory) merged.set(keyFor(entry), entry);
613495
+ for (const entry of rollingHistory) merged.set(keyFor(entry), entry);
613496
+ return [...merged.values()].sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0)).slice(-limit);
613497
+ }
613498
+ telegramHistoryAnchorsForSession(sessionKey, earliestLimit = 3) {
613499
+ const db = this.telegramDb();
613500
+ if (db) {
613501
+ try {
613502
+ const earliestRows = db.prepare(`
613503
+ SELECT * FROM telegram_messages
613504
+ WHERE session_key = ?
613505
+ ORDER BY received_at ASC, rowid ASC
613506
+ LIMIT ?
613507
+ `).all(sessionKey, Math.max(1, Math.min(10, Math.floor(earliestLimit))));
613508
+ const latestRow = db.prepare(`
613509
+ SELECT * FROM telegram_messages
613510
+ WHERE session_key = ?
613511
+ ORDER BY received_at DESC, rowid DESC
613512
+ LIMIT 1
613513
+ `).get(sessionKey);
613514
+ if (earliestRows.length > 0 || latestRow) {
613515
+ return {
613516
+ earliest: earliestRows.map((row) => this.telegramSqliteRowToHistoryEntry(row)),
613517
+ latest: latestRow ? this.telegramSqliteRowToHistoryEntry(latestRow) : void 0
613518
+ };
613519
+ }
613520
+ } catch {
613521
+ }
613522
+ }
613523
+ 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));
613524
+ return {
613525
+ earliest: sorted.slice(0, Math.max(1, Math.min(10, Math.floor(earliestLimit)))),
613526
+ latest: sorted.length > 0 ? sorted[sorted.length - 1] : void 0
613527
+ };
613528
+ }
613529
+ telegramParticipantActivityStats(sessionKey, options2 = {}) {
613530
+ const limit = Math.max(1, Math.min(20, Math.floor(options2.limit ?? 8)));
613531
+ const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
613532
+ const db = this.telegramDb();
613533
+ if (db) {
613534
+ const clauses = ["session_key = ?"];
613535
+ const params = [sessionKey];
613536
+ if (options2.userId !== void 0) {
613537
+ clauses.push("from_user_id = ?");
613538
+ params.push(options2.userId);
613539
+ }
613540
+ if (username) {
613541
+ clauses.push("lower(username) = ?");
613542
+ params.push(username);
613543
+ }
613544
+ if (options2.since !== void 0) {
613545
+ clauses.push("received_at >= ?");
613546
+ params.push(options2.since);
613547
+ }
613548
+ if (options2.until !== void 0) {
613549
+ clauses.push("received_at <= ?");
613550
+ params.push(options2.until);
613551
+ }
613552
+ try {
613553
+ const rows = db.prepare(`
613554
+ SELECT
613555
+ role,
613556
+ from_user_id,
613557
+ lower(COALESCE(username, '')) AS username_key,
613558
+ COALESCE(username, '') AS username,
613559
+ COALESCE(first_name, '') AS first_name,
613560
+ COUNT(*) AS n,
613561
+ MIN(received_at) AS first_ts,
613562
+ MAX(received_at) AS last_ts
613563
+ FROM telegram_messages
613564
+ WHERE ${clauses.join(" AND ")}
613565
+ GROUP BY role, from_user_id, username_key, first_name
613566
+ ORDER BY n DESC, last_ts DESC
613567
+ LIMIT ?
613568
+ `).all(...params, limit);
613569
+ return rows.map((row) => {
613570
+ const role = row.role === "assistant" ? "assistant" : "user";
613571
+ const fromUserId = Number(row.from_user_id);
613572
+ const speaker = role === "assistant" ? `@${this.state.botUsername || row.username || "omnius"}` : row.username ? `@${row.username}` : Number.isFinite(fromUserId) ? `user:${fromUserId}` : row.first_name || "unknown";
613573
+ const firstTs = Number(row.first_ts);
613574
+ const lastTs = Number(row.last_ts);
613575
+ return {
613576
+ speaker,
613577
+ count: Number(row.n) || 0,
613578
+ firstTs: Number.isFinite(firstTs) ? firstTs : void 0,
613579
+ lastTs: Number.isFinite(lastTs) ? lastTs : void 0
613580
+ };
613581
+ });
613582
+ } catch {
613583
+ }
613584
+ }
613585
+ const stats = /* @__PURE__ */ new Map();
613586
+ for (const entry of this.telegramMergedHistoryForSession(sessionKey)) {
613587
+ if (options2.userId !== void 0 && entry.fromUserId !== options2.userId) continue;
613588
+ if (username && (entry.username || "").replace(/^@/, "").toLowerCase() !== username) continue;
613589
+ if (options2.since !== void 0 && (entry.ts ?? 0) < options2.since) continue;
613590
+ if (options2.until !== void 0 && (entry.ts ?? 0) > options2.until) continue;
613591
+ const speaker = telegramHistorySpeaker(entry);
613592
+ const current = stats.get(speaker) ?? { speaker, count: 0 };
613593
+ current.count += 1;
613594
+ if (entry.ts !== void 0) {
613595
+ current.firstTs = current.firstTs === void 0 ? entry.ts : Math.min(current.firstTs, entry.ts);
613596
+ current.lastTs = current.lastTs === void 0 ? entry.ts : Math.max(current.lastTs, entry.ts);
613597
+ }
613598
+ stats.set(speaker, current);
613599
+ }
613600
+ return [...stats.values()].sort((a2, b) => b.count - a2.count || (b.lastTs ?? 0) - (a2.lastTs ?? 0)).slice(0, limit);
613601
+ }
613602
+ formatTelegramParticipantActivityStats(stats) {
613603
+ return stats.map((stat7) => {
613604
+ const first2 = stat7.firstTs ? new Date(stat7.firstTs).toISOString() : "?";
613605
+ const last2 = stat7.lastTs ? new Date(stat7.lastTs).toISOString() : "?";
613606
+ return `- ${stat7.speaker}: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`;
613607
+ }).join("\n");
613608
+ }
613382
613609
  searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
613383
613610
  const db = this.telegramDb();
613384
613611
  if (!db) return [];
613385
613612
  const limit = Math.max(1, Math.min(50, Math.floor(options2.limit ?? 12)));
613386
613613
  const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
613614
+ const direction = options2.bucket === "earliest" ? "ASC" : "DESC";
613387
613615
  const tokens = [...telegramMemoryTokens(query)].slice(0, 8);
613616
+ const meaningfulTokens = [...telegramMeaningfulMemoryTokens(query)].slice(0, 8);
613388
613617
  const rows = /* @__PURE__ */ new Map();
613389
613618
  const addRows = (items) => {
613390
613619
  for (const row of items) {
@@ -613392,44 +613621,66 @@ ${lines.join("\n")}`);
613392
613621
  if (Number.isFinite(key)) rows.set(key, row);
613393
613622
  }
613394
613623
  };
613624
+ const baseWhere = (extraClauses = [], extraParams = []) => {
613625
+ const clauses = ["session_key = ?"];
613626
+ const params = [sessionKey];
613627
+ if (options2.since !== void 0) {
613628
+ clauses.push("received_at >= ?");
613629
+ params.push(options2.since);
613630
+ }
613631
+ if (options2.until !== void 0) {
613632
+ clauses.push("received_at <= ?");
613633
+ params.push(options2.until);
613634
+ }
613635
+ clauses.push(...extraClauses);
613636
+ params.push(...extraParams);
613637
+ return { where: clauses.join(" AND "), params };
613638
+ };
613639
+ const scopedWhere = (extraClauses = [], extraParams = []) => {
613640
+ const clauses = [...extraClauses];
613641
+ const params = [...extraParams];
613642
+ if (options2.userId !== void 0) {
613643
+ clauses.unshift("from_user_id = ?");
613644
+ params.unshift(options2.userId);
613645
+ }
613646
+ if (username) {
613647
+ clauses.unshift("lower(username) = ?");
613648
+ params.unshift(username);
613649
+ }
613650
+ return baseWhere(clauses, params);
613651
+ };
613652
+ const selectRows = (where, params, rowLimit = limit) => db.prepare(`
613653
+ SELECT rowid, * FROM telegram_messages
613654
+ WHERE ${where}
613655
+ ORDER BY received_at ${direction}, rowid ${direction}
613656
+ LIMIT ?
613657
+ `).all(...params, rowLimit);
613395
613658
  try {
613396
613659
  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));
613660
+ const { where, params } = baseWhere(["from_user_id = ?"], [options2.userId]);
613661
+ addRows(selectRows(where, params));
613403
613662
  }
613404
613663
  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));
613664
+ const { where, params } = baseWhere(["lower(username) = ?"], [username]);
613665
+ addRows(selectRows(where, params));
613411
613666
  }
613412
613667
  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));
613668
+ const clauses = tokens.map(() => "(text LIKE ? OR lower(username) LIKE ? OR lower(first_name) LIKE ?)").join(" OR ");
613669
+ const tokenParams = tokens.flatMap((token) => [`%${token}%`, `%${token}%`, `%${token}%`]);
613670
+ const { where, params } = scopedWhere([`(${clauses})`], tokenParams);
613671
+ addRows(selectRows(where, params));
613420
613672
  }
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)));
613673
+ if (rows.size === 0 && meaningfulTokens.length === 0) {
613674
+ const { where, params } = scopedWhere();
613675
+ addRows(selectRows(where, params, Math.min(limit, 20)));
613428
613676
  }
613429
613677
  } catch {
613430
613678
  return [];
613431
613679
  }
613432
- return [...rows.values()].sort((a2, b) => Number(b.received_at ?? 0) - Number(a2.received_at ?? 0)).slice(0, limit);
613680
+ return [...rows.values()].sort((a2, b) => {
613681
+ const diff = Number(a2.received_at ?? 0) - Number(b.received_at ?? 0);
613682
+ return direction === "ASC" ? diff : -diff;
613683
+ }).slice(0, limit);
613433
613684
  }
613434
613685
  formatTelegramSqliteMirrorRows(rows, maxText = 260) {
613435
613686
  return rows.map((row) => {
@@ -613560,6 +613811,7 @@ ${lines.join("\n")}`);
613560
613811
  `- Associative relationships: ${relationshipCount}`,
613561
613812
  `- Per-user memories: ${userMemoryCount}`,
613562
613813
  `- Rolling history entries retained: ${historyCount}`,
613814
+ `- Addressable conversation history rows: ${Math.max(historyCount, sqliteCount)}`,
613563
613815
  `- SQLite mirror rows: ${sqliteCount}`,
613564
613816
  `- Episodes (durable, day+ scope): ${episodeCount2}`
613565
613817
  ];
@@ -613570,8 +613822,7 @@ ${lines.join("\n")}`);
613570
613822
  lines.push(` - ${topic}`);
613571
613823
  }
613572
613824
  }
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));
613825
+ const anchors = this.telegramHistoryAnchorsForSession(sessionKey, 3);
613575
613826
  const fmtHistoryAnchor = (entry) => {
613576
613827
  const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
613577
613828
  const speaker = telegramHistorySpeaker(entry);
@@ -613579,18 +613830,33 @@ ${lines.join("\n")}`);
613579
613830
  const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
613580
613831
  return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613581
613832
  };
613582
- if (sortedByTs.length > 0) {
613833
+ const sameHistoryAnchor = (a2, b) => {
613834
+ if (!a2 || !b) return false;
613835
+ if (a2.messageId !== void 0 && b.messageId !== void 0) return a2.messageId === b.messageId && a2.role === b.role;
613836
+ return a2.ts === b.ts && a2.role === b.role && a2.text === b.text;
613837
+ };
613838
+ if (anchors.earliest.length > 0 || anchors.latest) {
613583
613839
  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])}`);
613840
+ lines.push("Chronological anchors — Telegram conversation history (SQLite mirror preferred; ground truth for 'oldest/newest memory' questions):");
613841
+ if (anchors.earliest[0]) lines.push(` EARLIEST turn: ${fmtHistoryAnchor(anchors.earliest[0])}`);
613842
+ if (anchors.latest && !sameHistoryAnchor(anchors.earliest[0], anchors.latest)) {
613843
+ lines.push(` LATEST turn: ${fmtHistoryAnchor(anchors.latest)}`);
613588
613844
  }
613589
- if (sortedByTs.length > 2) {
613590
- lines.push(` 2nd earliest: ${fmtHistoryAnchor(sortedByTs[1])}`);
613845
+ if (anchors.earliest[1]) {
613846
+ lines.push(` 2nd earliest: ${fmtHistoryAnchor(anchors.earliest[1])}`);
613591
613847
  }
613592
- if (sortedByTs.length > 3) {
613593
- lines.push(` 3rd earliest: ${fmtHistoryAnchor(sortedByTs[2])}`);
613848
+ if (anchors.earliest[2]) {
613849
+ lines.push(` 3rd earliest: ${fmtHistoryAnchor(anchors.earliest[2])}`);
613850
+ }
613851
+ }
613852
+ const activityStats = this.telegramParticipantActivityStats(sessionKey, { limit: 8 });
613853
+ if (activityStats.length > 0) {
613854
+ lines.push("");
613855
+ lines.push("Activity anchors — participant message counts from the durable mirror/merged history:");
613856
+ for (const stat7 of activityStats) {
613857
+ const first2 = stat7.firstTs ? new Date(stat7.firstTs).toISOString() : "?";
613858
+ const last2 = stat7.lastTs ? new Date(stat7.lastTs).toISOString() : "?";
613859
+ lines.push(` - ${stat7.speaker}: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`);
613594
613860
  }
613595
613861
  }
613596
613862
  if (this.repoRoot) {
@@ -614318,37 +614584,23 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614318
614584
  if (!me?.ok) {
614319
614585
  throw new Error(`Invalid Telegram bot token: ${me?.description || "unknown error"}`);
614320
614586
  }
614321
- if (this.repoRoot && me.result?.id) {
614587
+ if (me.result?.id) {
614588
+ const botUserId = Number(me.result.id);
614589
+ const globalLockDir = process.env["OMNIUS_TELEGRAM_LOCK_DIR"] ? resolve43(process.env["OMNIUS_TELEGRAM_LOCK_DIR"]) : resolve43(homedir40(), ".omnius", "telegram-runner-state");
614590
+ const lockDirs = /* @__PURE__ */ new Set([globalLockDir]);
614591
+ if (this.repoRoot) {
614592
+ lockDirs.add(resolve43(this.repoRoot, ".omnius", "telegram-runner-state"));
614593
+ }
614594
+ const claimed = [];
614322
614595
  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
- }
614596
+ for (const lockDir of lockDirs) {
614597
+ const lockFile = this.claimTelegramOwnerLock(lockDir, botUserId, me.result.username);
614598
+ claimed.push(lockFile);
614338
614599
  }
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;
614600
+ this.telegramOwnerLockFiles = claimed;
614601
+ this.telegramOwnerLockFile = claimed[0] ?? null;
614351
614602
  } catch (e2) {
614603
+ for (const lockFile of claimed) this.releaseTelegramOwnerLock(lockFile);
614352
614604
  if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614353
614605
  }
614354
614606
  }
@@ -614406,18 +614658,57 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614406
614658
  }
614407
614659
  this.telegramSqliteDb = null;
614408
614660
  }
614409
- if (this.telegramOwnerLockFile) {
614661
+ const lockFiles = /* @__PURE__ */ new Set([
614662
+ ...this.telegramOwnerLockFiles,
614663
+ ...this.telegramOwnerLockFile ? [this.telegramOwnerLockFile] : []
614664
+ ]);
614665
+ for (const lockFile of lockFiles) this.releaseTelegramOwnerLock(lockFile);
614666
+ this.telegramOwnerLockFiles = [];
614667
+ this.telegramOwnerLockFile = null;
614668
+ this.subAgents.clear();
614669
+ this.activeChatViews.clear();
614670
+ this.refreshActiveTelegramInteractionCount();
614671
+ }
614672
+ claimTelegramOwnerLock(lockDir, botUserId, botUsername) {
614673
+ mkdirSync65(lockDir, { recursive: true });
614674
+ const lockFile = join127(lockDir, `bot-${botUserId}.owner.lock`);
614675
+ if (existsSync113(lockFile)) {
614410
614676
  try {
614411
- if (existsSync113(this.telegramOwnerLockFile)) {
614412
- unlinkSync22(this.telegramOwnerLockFile);
614677
+ const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
614678
+ const priorAlive = typeof prior.pid === "number" && prior.pid !== process.pid ? this.processIsAlive(prior.pid) : false;
614679
+ if (priorAlive) {
614680
+ throw new Error(
614681
+ `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.`
614682
+ );
614413
614683
  }
614684
+ } catch (e2) {
614685
+ if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614686
+ }
614687
+ }
614688
+ writeFileSync59(
614689
+ lockFile,
614690
+ JSON.stringify({
614691
+ pid: process.pid,
614692
+ cwd: this.repoRoot || process.cwd(),
614693
+ botUsername,
614694
+ botUserId,
614695
+ ts: Date.now()
614696
+ }, null, 2),
614697
+ { encoding: "utf-8", mode: 384 }
614698
+ );
614699
+ return lockFile;
614700
+ }
614701
+ releaseTelegramOwnerLock(lockFile) {
614702
+ try {
614703
+ if (!existsSync113(lockFile)) return;
614704
+ try {
614705
+ const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
614706
+ if (prior.pid !== process.pid) return;
614414
614707
  } catch {
614415
614708
  }
614416
- this.telegramOwnerLockFile = null;
614709
+ unlinkSync22(lockFile);
614710
+ } catch {
614417
614711
  }
614418
- this.subAgents.clear();
614419
- this.activeChatViews.clear();
614420
- this.refreshActiveTelegramInteractionCount();
614421
614712
  }
614422
614713
  /**
614423
614714
  * Cheap liveness probe: kill(pid, 0) throws ESRCH when the process is dead
@@ -615542,7 +615833,9 @@ ${result.llmContent ?? result.output}` };
615542
615833
  })(),
615543
615834
  execute: async (args) => {
615544
615835
  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;
615836
+ const rawMaxResults = args["max_results"];
615837
+ const explicitMaxResults = typeof rawMaxResults === "number" && Number.isFinite(rawMaxResults);
615838
+ let maxResults = explicitMaxResults ? Math.max(1, Math.min(20, Math.floor(rawMaxResults))) : 8;
615546
615839
  if (!rawQuery) return { success: true, output: "Search query is required." };
615547
615840
  this.ensureTelegramConversationLoaded(msgSessionKey);
615548
615841
  const parseTimeArg = (v) => {
@@ -615576,6 +615869,9 @@ ${result.llmContent ?? result.output}` };
615576
615869
  bucket ? `bucket=${bucket}` : ""
615577
615870
  ].filter(Boolean).join(", ");
615578
615871
  }
615872
+ 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)) {
615873
+ maxResults = 20;
615874
+ }
615579
615875
  const inWindow = (ts) => {
615580
615876
  if (since === void 0 && until === void 0) return true;
615581
615877
  if (ts == null || !Number.isFinite(ts)) return false;
@@ -615612,6 +615908,7 @@ ${result.llmContent ?? result.output}` };
615612
615908
  return false;
615613
615909
  });
615614
615910
  const queryTokens = telegramMemoryTokens(query);
615911
+ const meaningfulQueryTokens = telegramMeaningfulMemoryTokens(query);
615615
615912
  const cardResults = cards.filter((card) => inWindow(card.updatedAt ?? card.createdAt)).map((card) => ({
615616
615913
  card,
615617
615914
  score: telegramMemorySimilarity(
@@ -615649,12 +615946,19 @@ ${notes2}`;
615649
615946
  const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
615650
615947
  limit: maxResults,
615651
615948
  userId: effectiveUserId,
615652
- username: effectiveUsername
615949
+ username: effectiveUsername,
615950
+ since,
615951
+ until,
615952
+ bucket
615653
615953
  });
615654
- const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults);
615655
- const historyForScan = this.chatHistory.get(msgSessionKey) ?? [];
615954
+ const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults).filter((episode) => inWindow(episode.timestamp)).sort((a2, b) => {
615955
+ if (bucket === "earliest") return (a2.timestamp ?? 0) - (b.timestamp ?? 0);
615956
+ if (bucket === "latest") return (b.timestamp ?? 0) - (a2.timestamp ?? 0);
615957
+ return 0;
615958
+ });
615959
+ const historyForScan = this.telegramMergedHistoryForSession(msgSessionKey);
615656
615960
  const lowerQuery = query.toLowerCase().trim();
615657
- const queryWords = lowerQuery.split(/\s+/).filter((w) => w.length >= 3);
615961
+ const queryWords = [...meaningfulQueryTokens];
615658
615962
  const hasTimeConstraint = since !== void 0 || until !== void 0 || bucket !== void 0;
615659
615963
  const rawHistoryMatches = [];
615660
615964
  if (historyForScan.length > 0) {
@@ -615666,7 +615970,12 @@ ${notes2}`;
615666
615970
  if (!matchesUser) continue;
615667
615971
  }
615668
615972
  if (!inWindow(entry.ts ?? null)) continue;
615669
- const hay = String(entry.text || "").toLowerCase();
615973
+ const hay = [
615974
+ entry.text || "",
615975
+ telegramHistorySpeaker(entry),
615976
+ entry.username || "",
615977
+ entry.firstName || ""
615978
+ ].join(" ").toLowerCase();
615670
615979
  if (!hay) continue;
615671
615980
  let lexMatch = false;
615672
615981
  if (lowerQuery.length > 0) {
@@ -615674,7 +615983,7 @@ ${notes2}`;
615674
615983
  const tokenHits = queryWords.filter((w) => hay.includes(w)).length;
615675
615984
  lexMatch = hasExact || queryWords.length > 0 && tokenHits >= Math.min(2, queryWords.length);
615676
615985
  }
615677
- if (lexMatch || hasTimeConstraint) {
615986
+ if (lexMatch || hasTimeConstraint && queryWords.length === 0) {
615678
615987
  rawHistoryMatches.push(entry);
615679
615988
  }
615680
615989
  }
@@ -615687,6 +615996,44 @@ ${notes2}`;
615687
615996
  const rawHistorySliced = rawHistoryMatches.slice(0, maxResults);
615688
615997
  const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
615689
615998
  const sections = [];
615999
+ const wantsChronologicalAnchors = bucket !== void 0 || /\b(oldest|earliest|first|newest|latest|most\s+recent|how\s+far\s+back)\b/i.test(rawQuery);
616000
+ if (wantsChronologicalAnchors) {
616001
+ const anchors = this.telegramHistoryAnchorsForSession(msgSessionKey, maxResults);
616002
+ const anchorEntries = [
616003
+ ...anchors.earliest.map((entry, index) => ({ label: index === 0 ? "EARLIEST" : `${index + 1}${index === 1 ? "nd" : index === 2 ? "rd" : "th"} earliest`, entry })),
616004
+ ...anchors.latest ? [{ label: "LATEST", entry: anchors.latest }] : []
616005
+ ];
616006
+ const seen = /* @__PURE__ */ new Set();
616007
+ const lines = anchorEntries.flatMap(({ label, entry }) => {
616008
+ const key = entry.messageId !== void 0 ? `${entry.role}:${entry.messageId}` : `${entry.role}:${entry.ts ?? ""}:${entry.text}`;
616009
+ if (seen.has(key)) return [];
616010
+ seen.add(key);
616011
+ const when = entry.ts ? new Date(entry.ts).toISOString() : "";
616012
+ const speaker = telegramHistorySpeaker(entry);
616013
+ const mode = entry.mode ? `/${entry.mode}` : "";
616014
+ const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
616015
+ return [`- ${label}: ${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`];
616016
+ });
616017
+ if (lines.length > 0) {
616018
+ sections.push(`### Chronological Conversation Anchors
616019
+ SQLite mirror preferred; use these for oldest/newest recall.
616020
+ ${lines.join("\n")}`);
616021
+ }
616022
+ }
616023
+ 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);
616024
+ if (wantsActivityStats) {
616025
+ const stats = this.telegramParticipantActivityStats(msgSessionKey, {
616026
+ limit: Math.min(12, maxResults),
616027
+ since,
616028
+ until,
616029
+ userId: effectiveUserId,
616030
+ username: effectiveUsername
616031
+ });
616032
+ if (stats.length > 0) {
616033
+ sections.push(`### Participant Activity Stats
616034
+ ${this.formatTelegramParticipantActivityStats(stats)}`);
616035
+ }
616036
+ }
615690
616037
  if (cardLines.length > 0) {
615691
616038
  sections.push(`### Scoped Memory Cards
615692
616039
  ${cardLines.join("\n\n")}`);
@@ -618416,9 +618763,9 @@ import {
618416
618763
  unlinkSync as unlinkSync23
618417
618764
  } from "node:fs";
618418
618765
  import { join as join128 } from "node:path";
618419
- import { homedir as homedir40 } from "node:os";
618766
+ import { homedir as homedir41 } from "node:os";
618420
618767
  function sessionsDir() {
618421
- return join128(homedir40(), ".omnius", "chat-sessions");
618768
+ return join128(homedir41(), ".omnius", "chat-sessions");
618422
618769
  }
618423
618770
  function sessionPath(id) {
618424
618771
  const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
@@ -619058,7 +619405,7 @@ __export(projects_exports, {
619058
619405
  unregisterProject: () => unregisterProject
619059
619406
  });
619060
619407
  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";
619408
+ import { homedir as homedir42 } from "node:os";
619062
619409
  import { basename as basename28, join as join129, resolve as resolve44 } from "node:path";
619063
619410
  import { randomUUID as randomUUID14 } from "node:crypto";
619064
619411
  function readAll2() {
@@ -619171,7 +619518,7 @@ var OMNIUS_DIR3, PROJECTS_FILE, CURRENT_FILE, currentRoot;
619171
619518
  var init_projects = __esm({
619172
619519
  "packages/cli/src/api/projects.ts"() {
619173
619520
  "use strict";
619174
- OMNIUS_DIR3 = join129(homedir41(), ".omnius");
619521
+ OMNIUS_DIR3 = join129(homedir42(), ".omnius");
619175
619522
  PROJECTS_FILE = join129(OMNIUS_DIR3, "projects.json");
619176
619523
  CURRENT_FILE = join129(OMNIUS_DIR3, "current-project");
619177
619524
  currentRoot = null;
@@ -619878,7 +620225,7 @@ var init_access_policy = __esm({
619878
620225
  // packages/cli/src/api/project-preferences.ts
619879
620226
  import { createHash as createHash24 } from "node:crypto";
619880
620227
  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";
620228
+ import { homedir as homedir43 } from "node:os";
619882
620229
  import { join as join130, resolve as resolve45 } from "node:path";
619883
620230
  import { randomUUID as randomUUID15 } from "node:crypto";
619884
620231
  function projectKey(root) {
@@ -619959,7 +620306,7 @@ var OMNIUS_DIR4, PROJECTS_DIR, SCHEMA_VERSION, DEFAULT_PREFS;
619959
620306
  var init_project_preferences = __esm({
619960
620307
  "packages/cli/src/api/project-preferences.ts"() {
619961
620308
  "use strict";
619962
- OMNIUS_DIR4 = join130(homedir42(), ".omnius");
620309
+ OMNIUS_DIR4 = join130(homedir43(), ".omnius");
619963
620310
  PROJECTS_DIR = join130(OMNIUS_DIR4, "projects");
619964
620311
  SCHEMA_VERSION = 1;
619965
620312
  DEFAULT_PREFS = {
@@ -621235,7 +621582,7 @@ __export(aiwg_exports, {
621235
621582
  });
621236
621583
  import { existsSync as existsSync119, readFileSync as readFileSync97, readdirSync as readdirSync42, statSync as statSync42 } from "node:fs";
621237
621584
  import { join as join132 } from "node:path";
621238
- import { homedir as homedir43 } from "node:os";
621585
+ import { homedir as homedir44 } from "node:os";
621239
621586
  import { execSync as execSync55 } from "node:child_process";
621240
621587
  function resolveAiwgRoot() {
621241
621588
  if (_cachedAiwgRoot !== void 0) return _cachedAiwgRoot;
@@ -621244,7 +621591,7 @@ function resolveAiwgRoot() {
621244
621591
  _cachedAiwgRoot = envRoot;
621245
621592
  return envRoot;
621246
621593
  }
621247
- const shareDir = join132(homedir43(), ".local", "share", "ai-writing-guide");
621594
+ const shareDir = join132(homedir44(), ".local", "share", "ai-writing-guide");
621248
621595
  if (existsSync119(join132(shareDir, "agentic"))) {
621249
621596
  _cachedAiwgRoot = shareDir;
621250
621597
  return shareDir;
@@ -621273,8 +621620,8 @@ function resolveAiwgRoot() {
621273
621620
  }
621274
621621
  }
621275
621622
  const versionDirs = [
621276
- join132(homedir43(), ".nvm", "versions", "node"),
621277
- join132(homedir43(), ".local", "share", "fnm", "node-versions")
621623
+ join132(homedir44(), ".nvm", "versions", "node"),
621624
+ join132(homedir44(), ".local", "share", "fnm", "node-versions")
621278
621625
  ];
621279
621626
  for (const vdir of versionDirs) {
621280
621627
  if (!existsSync119(vdir)) continue;
@@ -622022,10 +622369,10 @@ __export(runtime_keys_exports, {
622022
622369
  });
622023
622370
  import { existsSync as existsSync120, readFileSync as readFileSync98, writeFileSync as writeFileSync63, mkdirSync as mkdirSync71, chmodSync as chmodSync2 } from "node:fs";
622024
622371
  import { join as join133 } from "node:path";
622025
- import { homedir as homedir44 } from "node:os";
622372
+ import { homedir as homedir45 } from "node:os";
622026
622373
  import { randomBytes as randomBytes23 } from "node:crypto";
622027
622374
  function ensureDir2() {
622028
- const dir = join133(homedir44(), ".omnius");
622375
+ const dir = join133(homedir45(), ".omnius");
622029
622376
  if (!existsSync120(dir)) mkdirSync71(dir, { recursive: true });
622030
622377
  }
622031
622378
  function loadAll() {
@@ -622106,7 +622453,7 @@ var KEYS_FILE;
622106
622453
  var init_runtime_keys = __esm({
622107
622454
  "packages/cli/src/api/runtime-keys.ts"() {
622108
622455
  "use strict";
622109
- KEYS_FILE = join133(homedir44(), ".omnius", "keys.json");
622456
+ KEYS_FILE = join133(homedir45(), ".omnius", "keys.json");
622110
622457
  }
622111
622458
  });
622112
622459
 
@@ -622118,13 +622465,13 @@ __export(tor_fallback_exports, {
622118
622465
  tunnelViaTor: () => tunnelViaTor
622119
622466
  });
622120
622467
  import { existsSync as existsSync121, readFileSync as readFileSync99 } from "node:fs";
622121
- import { homedir as homedir45 } from "node:os";
622468
+ import { homedir as homedir46 } from "node:os";
622122
622469
  import { join as join134 } from "node:path";
622123
622470
  import { createConnection as createConnection3 } from "node:net";
622124
622471
  function getLocalOnion() {
622125
622472
  const candidates = [
622126
- join134(homedir45(), "hidden_service_hostname"),
622127
- join134(homedir45(), ".omnius", "tor", "hostname"),
622473
+ join134(homedir46(), "hidden_service_hostname"),
622474
+ join134(homedir46(), ".omnius", "tor", "hostname"),
622128
622475
  "/var/lib/tor/hidden_service/hostname"
622129
622476
  ];
622130
622477
  for (const p2 of candidates) {
@@ -622393,7 +622740,7 @@ var init_graphical_sudo = __esm({
622393
622740
  // packages/cli/src/api/routes-v1.ts
622394
622741
  import { existsSync as existsSync123, readFileSync as readFileSync100, readdirSync as readdirSync43, statSync as statSync43 } from "node:fs";
622395
622742
  import { join as join136, resolve as pathResolve2 } from "node:path";
622396
- import { homedir as homedir46 } from "node:os";
622743
+ import { homedir as homedir47 } from "node:os";
622397
622744
  async function tryRouteV1(ctx3) {
622398
622745
  const { pathname, method } = ctx3;
622399
622746
  if (pathname === "/v1/skills" && method === "GET") {
@@ -622628,7 +622975,7 @@ async function handleGetSkill(ctx3, name10) {
622628
622975
  async function fallbackDiscoverSkills() {
622629
622976
  return (_root) => {
622630
622977
  const roots = [
622631
- join136(homedir46(), ".local", "share", "ai-writing-guide")
622978
+ join136(homedir47(), ".local", "share", "ai-writing-guide")
622632
622979
  ];
622633
622980
  const out = [];
622634
622981
  for (const root of roots) {
@@ -623363,7 +623710,7 @@ async function handleNexusStatus(ctx3) {
623363
623710
  try {
623364
623711
  const statePaths = [
623365
623712
  join136(process.cwd(), ".omnius", "nexus-peer-state.json"),
623366
- join136(homedir46(), ".omnius", "nexus-peer-cache.json")
623713
+ join136(homedir47(), ".omnius", "nexus-peer-cache.json")
623367
623714
  ];
623368
623715
  const states = [];
623369
623716
  for (const p2 of statePaths) {
@@ -623396,7 +623743,7 @@ async function handleNexusStatus(ctx3) {
623396
623743
  }
623397
623744
  function loadAgentName() {
623398
623745
  try {
623399
- const p2 = join136(homedir46(), ".omnius", "agent-name");
623746
+ const p2 = join136(homedir47(), ".omnius", "agent-name");
623400
623747
  if (existsSync123(p2)) return readFileSync100(p2, "utf-8").trim();
623401
623748
  } catch {
623402
623749
  }
@@ -623406,8 +623753,8 @@ async function handleSponsors(ctx3) {
623406
623753
  const { req: req2, res, url, requestId } = ctx3;
623407
623754
  try {
623408
623755
  const candidates = [
623409
- join136(homedir46(), ".omnius", "sponsor-cache.json"),
623410
- join136(homedir46(), ".omnius", "sponsors.json")
623756
+ join136(homedir47(), ".omnius", "sponsor-cache.json"),
623757
+ join136(homedir47(), ".omnius", "sponsors.json")
623411
623758
  ];
623412
623759
  let sponsors = [];
623413
623760
  for (const p2 of candidates) {
@@ -623664,9 +624011,9 @@ function resolveLocalPeerId() {
623664
624011
  const projectScoped = scope === "project" || scope === "local" || projectScopeFlag === "1" || projectScopeFlag === "true" || projectScopeFlag === "yes";
623665
624012
  const candidates = projectScoped ? [
623666
624013
  join136(process.cwd(), ".omnius", "nexus", "status.json"),
623667
- join136(homedir46(), ".omnius", "nexus", "status.json")
624014
+ join136(homedir47(), ".omnius", "nexus", "status.json")
623668
624015
  ] : [
623669
- join136(homedir46(), ".omnius", "nexus", "status.json"),
624016
+ join136(homedir47(), ".omnius", "nexus", "status.json"),
623670
624017
  join136(process.cwd(), ".omnius", "nexus", "status.json")
623671
624018
  ];
623672
624019
  for (const p2 of candidates) {
@@ -623674,7 +624021,7 @@ function resolveLocalPeerId() {
623674
624021
  if (r2) return r2;
623675
624022
  }
623676
624023
  try {
623677
- const regPath = join136(homedir46(), ".omnius", "nexus-registry.json");
624024
+ const regPath = join136(homedir47(), ".omnius", "nexus-registry.json");
623678
624025
  if (existsSync123(regPath)) {
623679
624026
  const reg = JSON.parse(readFileSync100(regPath, "utf-8"));
623680
624027
  const entries = Array.isArray(reg?.dirs) ? reg.dirs : [];
@@ -623695,7 +624042,7 @@ function resolveLocalPeerId() {
623695
624042
  let scanResult = null;
623696
624043
  try {
623697
624044
  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`;
624045
+ const cmd = `find "${homedir47()}" -maxdepth 4 -path '*/.omnius/nexus/status.json' -type f 2>/dev/null | head -50`;
623699
624046
  const out = execSync60(cmd, { encoding: "utf-8", timeout: 2e3 }).trim();
623700
624047
  for (const line of out.split("\n")) {
623701
624048
  const f2 = line.trim();
@@ -624002,9 +624349,9 @@ async function handleRemoteProxy(ctx3) {
624002
624349
  const tunnelProjectScope = tunnelScope === "project" || tunnelScope === "local" || ["1", "true", "yes"].includes((process.env["OMNIUS_NEXUS_PROJECT_SCOPE"] || "").toLowerCase());
624003
624350
  const nexusCandidates = tunnelProjectScope ? [
624004
624351
  join136(process.cwd(), ".omnius", "nexus"),
624005
- join136(homedir46(), ".omnius", "nexus")
624352
+ join136(homedir47(), ".omnius", "nexus")
624006
624353
  ] : [
624007
- join136(homedir46(), ".omnius", "nexus"),
624354
+ join136(homedir47(), ".omnius", "nexus"),
624008
624355
  join136(process.cwd(), ".omnius", "nexus")
624009
624356
  ];
624010
624357
  let nexusDirPath = null;
@@ -624692,7 +625039,7 @@ async function handleListAgentTypes(ctx3) {
624692
625039
  }
624693
625040
  async function handleListEngines(ctx3) {
624694
625041
  const { res } = ctx3;
624695
- const home = homedir46();
625042
+ const home = homedir47();
624696
625043
  sendJson(res, 200, {
624697
625044
  engines: [
624698
625045
  { name: "dream", state_file: join136(process.cwd(), ".omnius", "dreams"), controllable_via: "SSE + slash commands" },
@@ -624785,7 +625132,7 @@ async function tryAimsRoute(ctx3) {
624785
625132
  return false;
624786
625133
  }
624787
625134
  function aimsDir() {
624788
- return join136(homedir46(), ".omnius", "aims");
625135
+ return join136(homedir47(), ".omnius", "aims");
624789
625136
  }
624790
625137
  function readAimsFile(name10, fallback) {
624791
625138
  try {
@@ -625129,7 +625476,7 @@ async function handleAimsSuppliers(ctx3) {
625129
625476
  }
625130
625477
  ];
625131
625478
  const sponsorPaths = [
625132
- join136(homedir46(), ".omnius", "sponsor-cache.json")
625479
+ join136(homedir47(), ".omnius", "sponsor-cache.json")
625133
625480
  ];
625134
625481
  for (const p2 of sponsorPaths) {
625135
625482
  if (!existsSync123(p2)) continue;
@@ -634654,10 +635001,10 @@ var init_usage_tracker = __esm({
634654
635001
  // packages/cli/src/api/profiles.ts
634655
635002
  import { existsSync as existsSync125, readFileSync as readFileSync102, writeFileSync as writeFileSync66, mkdirSync as mkdirSync74, readdirSync as readdirSync44, unlinkSync as unlinkSync25 } from "node:fs";
634656
635003
  import { join as join138 } from "node:path";
634657
- import { homedir as homedir47 } from "node:os";
635004
+ import { homedir as homedir48 } from "node:os";
634658
635005
  import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes24, scryptSync as scryptSync3 } from "node:crypto";
634659
635006
  function globalProfileDir() {
634660
- return join138(homedir47(), ".omnius", "profiles");
635007
+ return join138(homedir48(), ".omnius", "profiles");
634661
635008
  }
634662
635009
  function projectProfileDir(projectDir2) {
634663
635010
  return join138(projectDir2 || process.cwd(), ".omnius", "profiles");
@@ -634911,7 +635258,7 @@ var init_profiles = __esm({
634911
635258
  import { execSync as execSync56, spawn as spawn29 } from "node:child_process";
634912
635259
  import { existsSync as existsSync126, mkdirSync as mkdirSync75, writeFileSync as writeFileSync67 } from "node:fs";
634913
635260
  import { join as join139, resolve as resolve46, dirname as dirname37 } from "node:path";
634914
- import { homedir as homedir48 } from "node:os";
635261
+ import { homedir as homedir49 } from "node:os";
634915
635262
  import { fileURLToPath as fileURLToPath16 } from "node:url";
634916
635263
  function getDockerDir() {
634917
635264
  try {
@@ -635059,7 +635406,7 @@ async function ensureOmniusImage(force = false) {
635059
635406
  if (existsSync126(join139(dockerDir, "Dockerfile"))) {
635060
635407
  buildContext = dockerDir;
635061
635408
  } else {
635062
- buildContext = join139(homedir48(), ".omnius", "docker-build");
635409
+ buildContext = join139(homedir49(), ".omnius", "docker-build");
635063
635410
  mkdirSync75(buildContext, { recursive: true });
635064
635411
  writeDockerfiles(buildContext);
635065
635412
  }
@@ -635391,7 +635738,7 @@ import * as https3 from "node:https";
635391
635738
  import { createRequire as createRequire7 } from "node:module";
635392
635739
  import { fileURLToPath as fileURLToPath17 } from "node:url";
635393
635740
  import { dirname as dirname38, join as join141, resolve as resolve47 } from "node:path";
635394
- import { homedir as homedir49 } from "node:os";
635741
+ import { homedir as homedir50 } from "node:os";
635395
635742
  import { spawn as spawn30, execSync as execSync57 } from "node:child_process";
635396
635743
  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
635744
  import { randomBytes as randomBytes25, randomUUID as randomUUID16 } from "node:crypto";
@@ -635706,7 +636053,7 @@ function isOriginAllowed(origin) {
635706
636053
  if (!origin) return true;
635707
636054
  let accessMode = (process.env["OMNIUS_ACCESS"] || "").toLowerCase().trim();
635708
636055
  try {
635709
- const accessFile = join141(homedir49(), ".omnius", "access");
636056
+ const accessFile = join141(homedir50(), ".omnius", "access");
635710
636057
  if (existsSync127(accessFile)) {
635711
636058
  const persisted = readFileSync103(accessFile, "utf8").trim().toLowerCase();
635712
636059
  if (persisted === "any" || persisted === "lan" || persisted === "loopback") {
@@ -637945,10 +638292,10 @@ ${task}` : task;
637945
638292
  });
637946
638293
  }
637947
638294
  function updateStateFile() {
637948
- return join141(homedir49(), ".omnius", "update-state.json");
638295
+ return join141(homedir50(), ".omnius", "update-state.json");
637949
638296
  }
637950
638297
  function updateLogPath() {
637951
- return join141(homedir49(), ".omnius", "update.log");
638298
+ return join141(homedir50(), ".omnius", "update.log");
637952
638299
  }
637953
638300
  function readUpdateState() {
637954
638301
  try {
@@ -637961,7 +638308,7 @@ function readUpdateState() {
637961
638308
  }
637962
638309
  function writeUpdateState(state) {
637963
638310
  try {
637964
- const dir = join141(homedir49(), ".omnius");
638311
+ const dir = join141(homedir50(), ".omnius");
637965
638312
  mkdirSync76(dir, { recursive: true });
637966
638313
  const finalPath = updateStateFile();
637967
638314
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
@@ -638017,7 +638364,7 @@ async function handleV1Update(req2, res, requestId) {
638017
638364
  }
638018
638365
  if (!npmBin) npmBin = isWin2 ? "npm.cmd" : "npm";
638019
638366
  const pkgSpec = `omnius@${targetVersion}`;
638020
- const dir = join141(homedir49(), ".omnius");
638367
+ const dir = join141(homedir50(), ".omnius");
638021
638368
  fs11.mkdirSync(dir, { recursive: true });
638022
638369
  const logFd = fs11.openSync(logPath3, "w");
638023
638370
  const npmPrefix = dirname38(nodeDir);
@@ -641252,7 +641599,7 @@ ${steering}`;
641252
641599
  function getScheduleRoots() {
641253
641600
  const rootsEnv = process.env["OMNIUS_SCHEDULE_ROOTS"] || "";
641254
641601
  const roots = rootsEnv.split(rootsEnv.includes(";") ? ";" : ":").filter(Boolean);
641255
- const defaults3 = [process.cwd(), join141(homedir49(), "Documents")];
641602
+ const defaults3 = [process.cwd(), join141(homedir50(), "Documents")];
641256
641603
  const set = /* @__PURE__ */ new Set([...defaults3, ...roots]);
641257
641604
  return [...set];
641258
641605
  }
@@ -641757,7 +642104,7 @@ function fixupOrMigrateScheduled(mode, dryRun) {
641757
642104
  try {
641758
642105
  if (!f2.workingDir || !f2.task) continue;
641759
642106
  const unitBase = `omnius-${f2.id}`;
641760
- const unitDir = join141(homedir49(), ".config", "systemd", "user");
642107
+ const unitDir = join141(homedir50(), ".config", "systemd", "user");
641761
642108
  const svc = join141(unitDir, `${unitBase}.service`);
641762
642109
  const tim = join141(unitDir, `${unitBase}.timer`);
641763
642110
  const omniusBin = findOmniusBinary4();
@@ -642050,7 +642397,7 @@ function startApiServer(options2 = {}) {
642050
642397
  }
642051
642398
  let runtimeAccessMode = resolveAccessMode(process.env["OMNIUS_ACCESS"], host);
642052
642399
  try {
642053
- const accessFile = join141(homedir49(), ".omnius", "access");
642400
+ const accessFile = join141(homedir50(), ".omnius", "access");
642054
642401
  if (existsSync127(accessFile)) {
642055
642402
  const persisted = readFileSync103(accessFile, "utf8").trim();
642056
642403
  const resolved = resolveAccessMode(persisted, host);
@@ -642112,7 +642459,7 @@ function startApiServer(options2 = {}) {
642112
642459
  const previous = runtimeAccessMode;
642113
642460
  runtimeAccessMode = requested;
642114
642461
  try {
642115
- const dir = join141(homedir49(), ".omnius");
642462
+ const dir = join141(homedir50(), ".omnius");
642116
642463
  mkdirSync76(dir, { recursive: true });
642117
642464
  writeFileSync68(join141(dir, "access"), `${runtimeAccessMode}
642118
642465
  `, "utf8");
@@ -643182,7 +643529,7 @@ import {
643182
643529
  } from "node:fs";
643183
643530
  import { existsSync as existsSync128 } from "node:fs";
643184
643531
  import { execSync as execSync59 } from "node:child_process";
643185
- import { homedir as homedir50 } from "node:os";
643532
+ import { homedir as homedir51 } from "node:os";
643186
643533
  function formatTimeAgo2(date) {
643187
643534
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
643188
643535
  if (seconds < 60) return "just now";
@@ -644315,7 +644662,7 @@ function extractGeneratedAudioPath(output, repoRoot) {
644315
644662
  const match = output.match(/(?:Sound|Music|TTS) generated:\s+([^\n\r]+)/i);
644316
644663
  const raw = match?.[1]?.trim().replace(/^["']|["']$/g, "");
644317
644664
  if (!raw) return null;
644318
- return raw.startsWith("/") || raw.startsWith("~") ? raw.replace(/^~(?=\/)/, homedir50()) : join143(repoRoot, raw);
644665
+ return raw.startsWith("/") || raw.startsWith("~") ? raw.replace(/^~(?=\/)/, homedir51()) : join143(repoRoot, raw);
644319
644666
  }
644320
644667
  async function playGeneratedAudioForToolResult(toolName, output, repoRoot, writer) {
644321
644668
  if (!toolName || !["generate_audio", "generate_tts", "audio_playback"].includes(toolName) || !output) return;
@@ -646366,7 +646713,9 @@ async function startInteractive(config, repoPath) {
646366
646713
  } catch {
646367
646714
  }
646368
646715
  initOmniusDirectory(repoRoot);
646369
- const savedSettings = resolveSettings(repoRoot);
646716
+ const globalSettings = loadGlobalSettings();
646717
+ const projectSettings = loadProjectSettings(repoRoot);
646718
+ const savedSettings = { ...globalSettings, ...projectSettings };
646370
646719
  try {
646371
646720
  if (savedSettings.omniusAccess && !process.env["OMNIUS_ACCESS"]) {
646372
646721
  process.env["OMNIUS_ACCESS"] = String(savedSettings.omniusAccess);
@@ -647527,7 +647876,7 @@ This is an independent background session started from /background.`
647527
647876
  );
647528
647877
  return [hits, line];
647529
647878
  }
647530
- const HISTORY_DIR = join143(homedir50(), ".omnius");
647879
+ const HISTORY_DIR = join143(homedir51(), ".omnius");
647531
647880
  const HISTORY_FILE = join143(HISTORY_DIR, "repl-history");
647532
647881
  const MAX_HISTORY_LINES = 500;
647533
647882
  let savedHistory = [];
@@ -648389,6 +648738,32 @@ Log: ${nexusLogPath}`)
648389
648738
  }
648390
648739
  };
648391
648740
  workEvaluator.setBackend(evalBackend);
648741
+ let activeTelegramSettingsScope = null;
648742
+ const nonEmptyTelegramSetting = (value2) => {
648743
+ const trimmed = String(value2 ?? "").trim();
648744
+ return trimmed ? trimmed : void 0;
648745
+ };
648746
+ const refreshResolvedSettings = () => {
648747
+ const merged = { ...globalSettings, ...projectSettings };
648748
+ for (const key of Object.keys(savedSettings)) {
648749
+ delete savedSettings[key];
648750
+ }
648751
+ Object.assign(savedSettings, merged);
648752
+ };
648753
+ const telegramSettingsForScope = (scope = "project") => {
648754
+ if (scope === "effective") {
648755
+ const activeScope = activeTelegramSettingsScope ?? "project";
648756
+ return telegramSettingsForScope(activeScope);
648757
+ }
648758
+ const source = scope === "global" ? globalSettings : projectSettings;
648759
+ const mode = source.telegramMode ?? savedSettings.telegramMode ?? "auto";
648760
+ return {
648761
+ key: nonEmptyTelegramSetting(source.telegramKey),
648762
+ admin: nonEmptyTelegramSetting(source.telegramAdmin),
648763
+ mode,
648764
+ keyScope: scope
648765
+ };
648766
+ };
648392
648767
  const commandCtx = {
648393
648768
  get config() {
648394
648769
  return currentConfig;
@@ -648950,7 +649325,7 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
648950
649325
  return blessEngine?.isActive ?? false;
648951
649326
  },
648952
649327
  // Telegram bridge
648953
- async telegramStart(token, adminId) {
649328
+ async telegramStart(token, adminId, scope = "project") {
648954
649329
  if (telegramBridge?.isActive) {
648955
649330
  writeContent(
648956
649331
  () => renderWarning("Telegram bridge already active. Use /telegram stop before restarting.")
@@ -648990,6 +649365,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648990
649365
  currentConfig,
648991
649366
  repoRoot
648992
649367
  );
649368
+ activeTelegramSettingsScope = scope;
648993
649369
  if (resolvedContextWindowSize > 0) {
648994
649370
  telegramBridge.setContextWindowSize(resolvedContextWindowSize);
648995
649371
  }
@@ -648999,10 +649375,16 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648999
649375
  telegramBridge.setAdmin(adminId);
649000
649376
  }
649001
649377
  telegramBridge.setAdminAuthHandler((newAdminId, username) => {
649002
- savedSettings.telegramAdmin = newAdminId;
649003
- saveGlobalSettings({ telegramAdmin: newAdminId });
649378
+ if (activeTelegramSettingsScope === "global") {
649379
+ globalSettings.telegramAdmin = newAdminId;
649380
+ saveGlobalSettings({ telegramAdmin: newAdminId });
649381
+ } else {
649382
+ projectSettings.telegramAdmin = newAdminId;
649383
+ saveProjectSettings(repoRoot, { telegramAdmin: newAdminId });
649384
+ }
649385
+ refreshResolvedSettings();
649004
649386
  writeContent(
649005
- () => renderInfo(`Telegram admin authenticated: @${username || "unknown"} (${newAdminId})`)
649387
+ () => renderInfo(`Telegram admin authenticated: @${username || "unknown"} (${newAdminId}) in ${activeTelegramSettingsScope ?? "project"} settings`)
649006
649388
  );
649007
649389
  });
649008
649390
  telegramBridge.setWriteContent(writeContent);
@@ -649059,6 +649441,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649059
649441
  });
649060
649442
  });
649061
649443
  }
649444
+ } else {
649445
+ emotionEngine.setAdminOutreach(() => {
649446
+ });
649062
649447
  }
649063
649448
  if (voiceEngine.enabled && voiceEngine.ready) {
649064
649449
  telegramBridge.setVoiceEngine(voiceEngine);
@@ -649099,7 +649484,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649099
649484
  throw err;
649100
649485
  }
649101
649486
  });
649102
- await telegramBridge.start();
649487
+ try {
649488
+ await telegramBridge.start();
649489
+ } catch (err) {
649490
+ telegramBridge.stop();
649491
+ telegramBridge = null;
649492
+ activeTelegramSettingsScope = null;
649493
+ throw err;
649494
+ }
649103
649495
  statusBar.setTelegramStatus(true, telegramBridge.stats.activeSubAgents);
649104
649496
  telegramBridge.registerScopedMyCommands(adminId).catch((err) => {
649105
649497
  writeContent(
@@ -649117,41 +649509,56 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649117
649509
  telegramBridge.stop();
649118
649510
  writeContent(() => renderTelegramStop(stats));
649119
649511
  telegramBridge = null;
649512
+ activeTelegramSettingsScope = null;
649120
649513
  statusBar.setTelegramStatus(false, 0);
649121
649514
  }
649122
649515
  },
649123
649516
  isTelegramActive() {
649124
649517
  return telegramBridge?.isActive ?? false;
649125
649518
  },
649126
- getTelegramSettings() {
649127
- return {
649128
- key: savedSettings.telegramKey,
649129
- admin: savedSettings.telegramAdmin,
649130
- mode: savedSettings.telegramMode
649131
- };
649519
+ getTelegramSettings(scope = "project") {
649520
+ return telegramSettingsForScope(scope);
649132
649521
  },
649133
649522
  saveTelegramSettings(settings) {
649523
+ const targetScope = settings.local === false ? "global" : "project";
649524
+ const target = targetScope === "global" ? globalSettings : projectSettings;
649134
649525
  if (settings.key !== void 0) {
649135
- savedSettings.telegramKey = settings.key;
649526
+ target.telegramKey = settings.key;
649136
649527
  }
649137
649528
  if (settings.admin !== void 0) {
649138
- savedSettings.telegramAdmin = settings.admin;
649529
+ target.telegramAdmin = settings.admin === null ? "" : settings.admin;
649139
649530
  }
649140
649531
  if (settings.mode !== void 0) {
649141
- savedSettings.telegramMode = settings.mode;
649532
+ target.telegramMode = settings.mode;
649142
649533
  }
649143
649534
  const payload = {
649144
649535
  ...settings.key !== void 0 ? { telegramKey: settings.key } : {},
649145
- ...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {},
649536
+ ...settings.admin !== void 0 ? { telegramAdmin: settings.admin === null ? "" : settings.admin } : {},
649146
649537
  ...settings.mode !== void 0 ? { telegramMode: settings.mode } : {}
649147
649538
  };
649148
- const projectHasOmnius = existsSync128(join143(repoRoot, ".omnius"));
649149
- const useProject = settings.local === true || settings.local === void 0 && projectHasOmnius;
649150
- if (useProject) {
649539
+ if (targetScope === "project") {
649151
649540
  saveProjectSettings(repoRoot, payload);
649152
649541
  } else {
649153
649542
  saveGlobalSettings(payload);
649154
649543
  }
649544
+ refreshResolvedSettings();
649545
+ },
649546
+ telegramRevokeAdmin(scope) {
649547
+ const target = scope === "global" ? globalSettings : projectSettings;
649548
+ const previousAdmin = nonEmptyTelegramSetting(target.telegramAdmin) ?? null;
649549
+ target.telegramAdmin = "";
649550
+ if (scope === "global") {
649551
+ saveGlobalSettings({ telegramAdmin: "" });
649552
+ } else {
649553
+ saveProjectSettings(repoRoot, { telegramAdmin: "" });
649554
+ }
649555
+ refreshResolvedSettings();
649556
+ if (telegramBridge?.isActive && (activeTelegramSettingsScope ?? "project") === scope) {
649557
+ telegramBridge.setAdmin(null);
649558
+ emotionEngine.setAdminOutreach(() => {
649559
+ });
649560
+ }
649561
+ return { previousAdmin, scope };
649155
649562
  },
649156
649563
  telegramSetInteractionMode(mode) {
649157
649564
  savedSettings.telegramMode = mode;
@@ -649165,7 +649572,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649165
649572
  () => renderTelegramStatus(
649166
649573
  active,
649167
649574
  botUser,
649168
- savedSettings.telegramAdmin,
649575
+ telegramSettingsForScope(activeTelegramSettingsScope ?? "project").admin,
649169
649576
  subAgents,
649170
649577
  savedSettings.telegramMode ?? "auto"
649171
649578
  )
@@ -649250,7 +649657,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649250
649657
  const registry4 = createOmniusPlatformRegistry({
649251
649658
  telegram: () => ({
649252
649659
  active: telegramBridge?.isActive ?? false,
649253
- configured: !!savedSettings.telegramKey,
649660
+ configured: !!telegramSettingsForScope("project").key,
649254
649661
  enabled: telegramBridge?.isActive ?? false,
649255
649662
  botUsername: telegramBridge?.botUsername,
649256
649663
  activeSessions: telegramBridge?.stats.activeSubAgents ?? 0
@@ -650213,7 +650620,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650213
650620
  } catch {
650214
650621
  }
650215
650622
  try {
650216
- const voiceDir3 = join143(homedir50(), ".omnius", "voice");
650623
+ const voiceDir3 = join143(homedir51(), ".omnius", "voice");
650217
650624
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
650218
650625
  for (const pf of voicePidFiles) {
650219
650626
  const pidPath = join143(voiceDir3, pf);
@@ -650349,14 +650756,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650349
650756
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
650350
650757
  if (isPersonaPlexRunning2()) {
650351
650758
  const ppPidFile = join143(
650352
- homedir50(),
650759
+ homedir51(),
650353
650760
  ".omnius",
650354
650761
  "voice",
650355
650762
  "personaplex",
650356
650763
  "daemon.pid"
650357
650764
  );
650358
650765
  const ppPortFile = join143(
650359
- homedir50(),
650766
+ homedir51(),
650360
650767
  ".omnius",
650361
650768
  "voice",
650362
650769
  "personaplex",
@@ -650526,12 +650933,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650526
650933
  }, 100);
650527
650934
  }
650528
650935
  }
650529
- if (!isResumed && savedSettings.telegramKey) {
650936
+ const autoTelegramSettings = telegramSettingsForScope("project");
650937
+ if (!isResumed && autoTelegramSettings.key) {
650530
650938
  setTimeout(async () => {
650531
650939
  try {
650532
650940
  await commandCtx.telegramStart(
650533
- savedSettings.telegramKey,
650534
- savedSettings.telegramAdmin
650941
+ autoTelegramSettings.key,
650942
+ autoTelegramSettings.admin,
650943
+ "project"
650535
650944
  );
650536
650945
  } catch (err) {
650537
650946
  writeContent(
@@ -652526,7 +652935,7 @@ __export(config_exports2, {
652526
652935
  configCommand: () => configCommand
652527
652936
  });
652528
652937
  import { join as join145, resolve as resolve51 } from "node:path";
652529
- import { homedir as homedir51 } from "node:os";
652938
+ import { homedir as homedir52 } from "node:os";
652530
652939
  import { cwd as cwd3 } from "node:process";
652531
652940
  function redactIfSensitive(key, value2) {
652532
652941
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -652608,7 +653017,7 @@ function handleShow(opts, config) {
652608
653017
  }
652609
653018
  }
652610
653019
  printSection("Config File");
652611
- printInfo(`~/.omnius/config.json (${join145(homedir51(), ".omnius", "config.json")})`);
653020
+ printInfo(`~/.omnius/config.json (${join145(homedir52(), ".omnius", "config.json")})`);
652612
653021
  printSection("Priority Chain");
652613
653022
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
652614
653023
  printInfo(" 2. Project .omnius/settings.json (--local)");
@@ -653485,8 +653894,8 @@ function crashLog(label, err) {
653485
653894
  try {
653486
653895
  const { appendFileSync: appendFileSync12, mkdirSync: mkdirSync81 } = __require("node:fs");
653487
653896
  const { join: join148 } = __require("node:path");
653488
- const { homedir: homedir52 } = __require("node:os");
653489
- const logDir = join148(homedir52(), ".omnius");
653897
+ const { homedir: homedir53 } = __require("node:os");
653898
+ const logDir = join148(homedir53(), ".omnius");
653490
653899
  mkdirSync81(logDir, { recursive: true });
653491
653900
  appendFileSync12(join148(logDir, "crash.log"), logLine);
653492
653901
  } catch {