omnius 1.0.66 → 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
@@ -552883,6 +552883,11 @@ var init_sessionMetrics = __esm({
552883
552883
  toolCallsByName = {};
552884
552884
  inputTokens = 0;
552885
552885
  outputTokens = 0;
552886
+ latestEstimatedContextTokens = 0;
552887
+ peakEstimatedContextTokens = 0;
552888
+ measuredOutputTokens = 0;
552889
+ totalGenerationMs = 0;
552890
+ generationSamples = 0;
552886
552891
  filesModified = /* @__PURE__ */ new Set();
552887
552892
  tasks = [];
552888
552893
  currentTask = null;
@@ -552903,8 +552908,24 @@ var init_sessionMetrics = __esm({
552903
552908
  }
552904
552909
  /** Record token usage. */
552905
552910
  recordTokenUsage(inputTokens, outputTokens) {
552906
- this.inputTokens += inputTokens;
552907
- this.outputTokens += outputTokens;
552911
+ this.inputTokens += Math.max(0, Math.trunc(inputTokens || 0));
552912
+ this.outputTokens += Math.max(0, Math.trunc(outputTokens || 0));
552913
+ }
552914
+ /** Record the runner's latest context-token estimate. */
552915
+ recordContextEstimate(tokens) {
552916
+ const normalized = Math.max(0, Math.trunc(tokens || 0));
552917
+ this.latestEstimatedContextTokens = normalized;
552918
+ this.peakEstimatedContextTokens = Math.max(this.peakEstimatedContextTokens, normalized);
552919
+ }
552920
+ /** Record measured generation throughput for one response. */
552921
+ recordGeneration(outputTokens, durationMs) {
552922
+ const tokens = Math.max(0, Math.trunc(outputTokens || 0));
552923
+ const duration = Math.max(0, Math.trunc(durationMs || 0));
552924
+ if (tokens <= 0 || duration <= 0)
552925
+ return;
552926
+ this.measuredOutputTokens += tokens;
552927
+ this.totalGenerationMs += duration;
552928
+ this.generationSamples++;
552908
552929
  }
552909
552930
  /** Record a file modification. */
552910
552931
  recordFileModified(filePath) {
@@ -552912,6 +552933,9 @@ var init_sessionMetrics = __esm({
552912
552933
  }
552913
552934
  /** Start tracking a new task. */
552914
552935
  startTask(description) {
552936
+ if (this.currentTask) {
552937
+ this.endTask();
552938
+ }
552915
552939
  this.currentTask = {
552916
552940
  description,
552917
552941
  startTime: Date.now(),
@@ -552942,6 +552966,13 @@ var init_sessionMetrics = __esm({
552942
552966
  toolCallsByName: { ...this.toolCallsByName },
552943
552967
  totalInputTokens: this.inputTokens,
552944
552968
  totalOutputTokens: this.outputTokens,
552969
+ totalTokens: this.inputTokens + this.outputTokens,
552970
+ latestEstimatedContextTokens: this.latestEstimatedContextTokens,
552971
+ peakEstimatedContextTokens: this.peakEstimatedContextTokens,
552972
+ measuredOutputTokens: this.measuredOutputTokens,
552973
+ totalGenerationMs: this.totalGenerationMs,
552974
+ generationSamples: this.generationSamples,
552975
+ averageOutputTokensPerSecond: this.totalGenerationMs > 0 ? this.measuredOutputTokens / (this.totalGenerationMs / 1e3) : void 0,
552945
552976
  filesModified: [...this.filesModified],
552946
552977
  tasksCompleted: this.tasks.length,
552947
552978
  tasks: [...this.tasks],
@@ -560860,6 +560891,7 @@ function toTelegramBotCommandName(name10) {
560860
560891
  function buildTelegramBotCommands(opts = {}) {
560861
560892
  const maxCommands = Math.max(1, Math.min(100, opts.maxCommands ?? 100));
560862
560893
  const includeAuth = opts.includeAuth !== false;
560894
+ const scope = opts.scope ?? "admin";
560863
560895
  const commands = [];
560864
560896
  const seen = /* @__PURE__ */ new Set();
560865
560897
  const add2 = (command, description, canonical = command) => {
@@ -560872,6 +560904,14 @@ function buildTelegramBotCommands(opts = {}) {
560872
560904
  canonical
560873
560905
  });
560874
560906
  };
560907
+ if (scope === "public") {
560908
+ for (const cmd of TELEGRAM_PUBLIC_BOT_COMMANDS) {
560909
+ if (!includeAuth && cmd.command === "auth") continue;
560910
+ add2(cmd.command, cmd.description, cmd.canonical);
560911
+ if (commands.length >= maxCommands) break;
560912
+ }
560913
+ return commands.slice(0, maxCommands);
560914
+ }
560875
560915
  if (includeAuth) {
560876
560916
  add2("auth", "Authenticate this Telegram user as the bot admin", "auth");
560877
560917
  }
@@ -561071,7 +561111,7 @@ function inferArgsHint(signature) {
561071
561111
  function normalizeCommandName(name10) {
561072
561112
  return name10.replace(/^\//, "").trim().toLowerCase();
561073
561113
  }
561074
- var COMMAND_SIGNATURES, PLANNED_SIGNATURES, CATEGORY_OVERRIDES, ALIASES, USER_ONLY, DESTRUCTIVE, NETWORKED, SECRET_BEARING, PROFILE_GATED, SELF_MODIFY_ALLOWED, REST_BLOCKED, CANONICAL_BY_ALIAS, DYNAMIC_COMMANDS, SLASH_COMMANDS;
561114
+ var COMMAND_SIGNATURES, PLANNED_SIGNATURES, CATEGORY_OVERRIDES, ALIASES, USER_ONLY, DESTRUCTIVE, NETWORKED, SECRET_BEARING, PROFILE_GATED, SELF_MODIFY_ALLOWED, REST_BLOCKED, TELEGRAM_PUBLIC_BOT_COMMANDS, CANONICAL_BY_ALIAS, DYNAMIC_COMMANDS, SLASH_COMMANDS;
561075
561115
  var init_command_registry = __esm({
561076
561116
  "packages/cli/src/tui/command-registry.ts"() {
561077
561117
  "use strict";
@@ -561158,6 +561198,7 @@ var init_command_registry = __esm({
561158
561198
  ["/score", "Show inference capability scorecard (memory, compute, speed, models)"],
561159
561199
  ["/task-type", "Set task type (code, document, analysis, plan, general, auto)"],
561160
561200
  ["/stats", "Show session dashboard (metrics, tool usage, task history)"],
561201
+ ["/metrics", "Show runtime metrics: tokens, context window, throughput, and tool usage"],
561161
561202
  ["/sessions", "Browse saved sessions"],
561162
561203
  ["/pause", "Pause after current turn finishes (gentle halt, /resume to continue)"],
561163
561204
  ["/stop", "Kill current inference immediately and save state (/resume to continue)"],
@@ -561440,7 +561481,9 @@ var init_command_registry = __esm({
561440
561481
  selfmodify: "runtime",
561441
561482
  debug: "runtime",
561442
561483
  selfmod: "runtime",
561443
- "self-modify": "runtime"
561484
+ "self-modify": "runtime",
561485
+ stats: "session",
561486
+ metrics: "session"
561444
561487
  };
561445
561488
  ALIASES = {
561446
561489
  help: ["h", "?"],
@@ -561463,7 +561506,7 @@ var init_command_registry = __esm({
561463
561506
  evaluate: ["eval"],
561464
561507
  score: ["inference"],
561465
561508
  "task-type": ["tasktype", "tt"],
561466
- stats: ["metrics", "dashboard"],
561509
+ stats: ["dashboard"],
561467
561510
  memory: ["mem"],
561468
561511
  files: ["workspace"],
561469
561512
  skills: ["skill"],
@@ -561627,6 +561670,7 @@ var init_command_registry = __esm({
561627
561670
  "selfmodify",
561628
561671
  "debug",
561629
561672
  "stats",
561673
+ "metrics",
561630
561674
  "stream",
561631
561675
  "style",
561632
561676
  "task-type",
@@ -561646,6 +561690,20 @@ var init_command_registry = __esm({
561646
561690
  "bg",
561647
561691
  "paste"
561648
561692
  ]);
561693
+ TELEGRAM_PUBLIC_BOT_COMMANDS = [
561694
+ { command: "start", description: "Show Telegram bridge status", canonical: "start" },
561695
+ { command: "help", description: "Show available Telegram commands", canonical: "help" },
561696
+ { command: "commands", description: "Show available Telegram commands", canonical: "commands" },
561697
+ { command: "auth", description: "Authenticate this Telegram user as bot admin", canonical: "auth" },
561698
+ { command: "call", description: "Get or request the active voice call link", canonical: "call" },
561699
+ { command: "reflect", description: "Run scoped Telegram chat reflection", canonical: "reflect" },
561700
+ { command: "reflection", description: "Alias for scoped Telegram chat reflection", canonical: "reflect" },
561701
+ { command: "daydream", description: "Alias for scoped Telegram chat reflection", canonical: "reflect" },
561702
+ { command: "remind", description: "Set a scoped Telegram reminder", canonical: "remind" },
561703
+ { command: "reminder", description: "List or update scoped Telegram reminders", canonical: "reminder" },
561704
+ { command: "reminders", description: "Alias for scoped Telegram reminders", canonical: "reminder" },
561705
+ { command: "image", description: "Generate a scoped image from a prompt", canonical: "image" }
561706
+ ];
561649
561707
  CANONICAL_BY_ALIAS = /* @__PURE__ */ new Map();
561650
561708
  for (const [canonical, aliases] of Object.entries(ALIASES)) {
561651
561709
  for (const alias of aliases) CANONICAL_BY_ALIAS.set(alias, canonical);
@@ -588762,10 +588820,10 @@ async function handleSlashCommand(input, ctx3) {
588762
588820
  let key = process.env["OMNIUS_API_KEY"] || "";
588763
588821
  if (!key) {
588764
588822
  try {
588765
- const { homedir: homedir52 } = await import("node:os");
588823
+ const { homedir: homedir53 } = await import("node:os");
588766
588824
  const { readFileSync: readFileSync107, existsSync: existsSync131 } = await import("node:fs");
588767
588825
  const { join: join148 } = await import("node:path");
588768
- const p2 = join148(homedir52(), ".omnius", "api.key");
588826
+ const p2 = join148(homedir53(), ".omnius", "api.key");
588769
588827
  if (existsSync131(p2)) key = readFileSync107(p2, "utf8").trim();
588770
588828
  } catch {
588771
588829
  }
@@ -588808,12 +588866,12 @@ async function handleSlashCommand(input, ctx3) {
588808
588866
  if (action === "new") {
588809
588867
  try {
588810
588868
  const { randomBytes: randomBytes27 } = await import("node:crypto");
588811
- const { homedir: homedir52 } = await import("node:os");
588869
+ const { homedir: homedir53 } = await import("node:os");
588812
588870
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
588813
588871
  const { join: join148 } = await import("node:path");
588814
588872
  const newKey = randomBytes27(16).toString("hex");
588815
588873
  process.env["OMNIUS_API_KEY"] = newKey;
588816
- const dir = join148(homedir52(), ".omnius");
588874
+ const dir = join148(homedir53(), ".omnius");
588817
588875
  mkdirSync81(dir, { recursive: true });
588818
588876
  writeFileSync73(join148(dir, "api.key"), newKey + "\n", "utf8");
588819
588877
  renderInfo(`New API key: ${c3.bold(c3.yellow(newKey))}`);
@@ -589079,10 +589137,10 @@ async function handleSlashCommand(input, ctx3) {
589079
589137
  "Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client."
589080
589138
  );
589081
589139
  try {
589082
- const { homedir: homedir53 } = await import("node:os");
589140
+ const { homedir: homedir54 } = await import("node:os");
589083
589141
  const { mkdirSync: mkdirSync82, writeFileSync: writeFileSync74 } = await import("node:fs");
589084
589142
  const { join: join149 } = await import("node:path");
589085
- const dir = join149(homedir53(), ".omnius");
589143
+ const dir = join149(homedir54(), ".omnius");
589086
589144
  mkdirSync82(dir, { recursive: true });
589087
589145
  writeFileSync74(join149(dir, "api.key"), apiKey + "\n", "utf8");
589088
589146
  } catch {
@@ -589095,10 +589153,10 @@ async function handleSlashCommand(input, ctx3) {
589095
589153
  }
589096
589154
  const port2 = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
589097
589155
  try {
589098
- const { homedir: homedir53 } = await import("node:os");
589156
+ const { homedir: homedir54 } = await import("node:os");
589099
589157
  const { mkdirSync: mkdirSync82, writeFileSync: writeFileSync74 } = await import("node:fs");
589100
589158
  const { join: join149 } = await import("node:path");
589101
- const dir = join149(homedir53(), ".omnius");
589159
+ const dir = join149(homedir54(), ".omnius");
589102
589160
  mkdirSync82(dir, { recursive: true });
589103
589161
  writeFileSync74(join149(dir, "access"), `${val2}
589104
589162
  `, "utf8");
@@ -589199,10 +589257,10 @@ async function handleSlashCommand(input, ctx3) {
589199
589257
  "Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client."
589200
589258
  );
589201
589259
  try {
589202
- const { homedir: homedir53 } = await import("node:os");
589260
+ const { homedir: homedir54 } = await import("node:os");
589203
589261
  const { mkdirSync: mkdirSync82, writeFileSync: writeFileSync74 } = await import("node:fs");
589204
589262
  const { join: join149 } = await import("node:path");
589205
- const dir = join149(homedir53(), ".omnius");
589263
+ const dir = join149(homedir54(), ".omnius");
589206
589264
  mkdirSync82(dir, { recursive: true });
589207
589265
  writeFileSync74(join149(dir, "api.key"), apiKey + "\n", "utf8");
589208
589266
  } catch {
@@ -589214,11 +589272,11 @@ async function handleSlashCommand(input, ctx3) {
589214
589272
  ctx3.saveSettings({ omniusAccess: val });
589215
589273
  }
589216
589274
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
589217
- const { homedir: homedir52 } = await import("node:os");
589275
+ const { homedir: homedir53 } = await import("node:os");
589218
589276
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
589219
589277
  const { join: join148 } = await import("node:path");
589220
589278
  try {
589221
- const dir = join148(homedir52(), ".omnius");
589279
+ const dir = join148(homedir53(), ".omnius");
589222
589280
  mkdirSync81(dir, { recursive: true });
589223
589281
  writeFileSync73(join148(dir, "access"), `${val}
589224
589282
  `, "utf8");
@@ -589641,7 +589699,7 @@ async function handleSlashCommand(input, ctx3) {
589641
589699
  );
589642
589700
  }
589643
589701
  } else if (sub === "name") {
589644
- const { homedir: homedir52 } = __require("node:os");
589702
+ const { homedir: homedir53 } = __require("node:os");
589645
589703
  const {
589646
589704
  existsSync: ex,
589647
589705
  readFileSync: rf,
@@ -589649,7 +589707,7 @@ async function handleSlashCommand(input, ctx3) {
589649
589707
  mkdirSync: mkd
589650
589708
  } = __require("node:fs");
589651
589709
  const namePath = __require("node:path").join(
589652
- homedir52(),
589710
+ homedir53(),
589653
589711
  ".omnius",
589654
589712
  "agent-name"
589655
589713
  );
@@ -589812,11 +589870,32 @@ async function handleSlashCommand(input, ctx3) {
589812
589870
  }
589813
589871
  const summary = ctx3.sessionMetrics.getSummary();
589814
589872
  const dur = SessionMetrics.formatDuration(summary.sessionDurationMs);
589873
+ const contextWindowSize = Math.max(0, Math.trunc(ctx3.getContextWindowSize?.() ?? 0));
589874
+ const contextLabel = contextWindowSize > 0 ? formatContextLength(contextWindowSize) : "unknown";
589875
+ const latestContextPct = contextWindowSize > 0 && summary.latestEstimatedContextTokens > 0 ? ` (${Math.round(summary.latestEstimatedContextTokens / contextWindowSize * 100)}%)` : "";
589876
+ const peakContextPct = contextWindowSize > 0 && summary.peakEstimatedContextTokens > 0 ? ` (${Math.round(summary.peakEstimatedContextTokens / contextWindowSize * 100)}%)` : "";
589877
+ const sessionSeconds = Math.max(1, summary.sessionDurationMs / 1e3);
589878
+ const measuredTokPerSec = summary.averageOutputTokensPerSecond;
589879
+ const sessionOutputTokPerSec = summary.totalOutputTokens / sessionSeconds;
589880
+ const avgInputPerTurn = summary.totalTurns > 0 ? summary.totalInputTokens / summary.totalTurns : 0;
589881
+ const avgOutputPerTurn = summary.totalTurns > 0 ? summary.totalOutputTokens / summary.totalTurns : 0;
589882
+ const avgToolsPerTurn = summary.totalTurns > 0 ? summary.totalToolCalls / summary.totalTurns : 0;
589815
589883
  const lines = [];
589816
589884
  lines.push(`
589817
- ${c3.bold("Session Dashboard")}
589885
+ ${c3.bold(cmd === "metrics" ? "Runtime Metrics" : "Session Dashboard")}
589818
589886
  `);
589819
589887
  lines.push(` Duration: ${c3.bold(dur)}`);
589888
+ lines.push(` Context window: ${c3.bold(contextLabel)}`);
589889
+ if (summary.latestEstimatedContextTokens > 0) {
589890
+ lines.push(
589891
+ ` Context now: ${c3.bold(`~${summary.latestEstimatedContextTokens.toLocaleString()}`)} tokens${latestContextPct}`
589892
+ );
589893
+ }
589894
+ if (summary.peakEstimatedContextTokens > 0) {
589895
+ lines.push(
589896
+ ` Context peak: ${c3.bold(`~${summary.peakEstimatedContextTokens.toLocaleString()}`)} tokens${peakContextPct}`
589897
+ );
589898
+ }
589820
589899
  lines.push(
589821
589900
  ` Tasks completed: ${c3.bold(String(summary.tasksCompleted))}`
589822
589901
  );
@@ -589828,6 +589907,18 @@ async function handleSlashCommand(input, ctx3) {
589828
589907
  lines.push(
589829
589908
  ` Output tokens: ${c3.bold(summary.totalOutputTokens.toLocaleString())}`
589830
589909
  );
589910
+ lines.push(
589911
+ ` Total tokens: ${c3.bold(summary.totalTokens.toLocaleString())}`
589912
+ );
589913
+ lines.push(
589914
+ ` Avg / turn: ${c3.bold(`${Math.round(avgInputPerTurn).toLocaleString()} in / ${Math.round(avgOutputPerTurn).toLocaleString()} out`)}`
589915
+ );
589916
+ lines.push(
589917
+ ` Tool / turn: ${c3.bold(avgToolsPerTurn.toFixed(2))}`
589918
+ );
589919
+ lines.push(
589920
+ measuredTokPerSec !== void 0 ? ` Output speed: ${c3.bold(`${measuredTokPerSec.toFixed(1)} tok/s`)} ${c3.dim(`(${summary.generationSamples} measured response${summary.generationSamples === 1 ? "" : "s"})`)}` : ` Output speed: ${c3.bold(`${sessionOutputTokPerSec.toFixed(1)} tok/s`)} ${c3.dim("session average")}`
589921
+ );
589831
589922
  if (ctx3.costTracker?.hasPricing) {
589832
589923
  lines.push(
589833
589924
  ` Estimated cost: ${c3.bold(c3.yellow(ctx3.costTracker.formatCost()))}`
@@ -592504,9 +592595,9 @@ sleep 1
592504
592595
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
592505
592596
  if (!sponsorName || sponsorName.length < 2) {
592506
592597
  try {
592507
- const { homedir: homedir52 } = __require("os");
592598
+ const { homedir: homedir53 } = __require("os");
592508
592599
  const namePath = __require("path").join(
592509
- homedir52(),
592600
+ homedir53(),
592510
592601
  ".omnius",
592511
592602
  "agent-name"
592512
592603
  );
@@ -593276,6 +593367,21 @@ sleep 1
593276
593367
  ctx3.telegramStatus?.();
593277
593368
  return "handled";
593278
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
+ }
593279
593385
  if (parts[0] === "mode" || parts[0] === "profile") {
593280
593386
  const requested = parts.slice(1).find((part) => !part.startsWith("--"));
593281
593387
  if (!requested) {
@@ -593590,6 +593696,24 @@ sleep 1
593590
593696
  renderInfo("Use /telegram to start.");
593591
593697
  return "handled";
593592
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
+ }
593593
593717
  if (!arg) {
593594
593718
  if (ctx3.isTelegramActive?.()) {
593595
593719
  ctx3.telegramStop?.();
@@ -593599,7 +593723,13 @@ sleep 1
593599
593723
  if (!settings.key) {
593600
593724
  renderWarning("No Telegram bot token configured.");
593601
593725
  renderInfo("Set one first: /telegram --key <bot-token>");
593602
- 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
+ }
593603
593733
  return "handled";
593604
593734
  }
593605
593735
  try {
@@ -593624,7 +593754,7 @@ sleep 1
593624
593754
  } catch {
593625
593755
  }
593626
593756
  try {
593627
- await ctx3.telegramStart?.(settings.key, settings.admin);
593757
+ await ctx3.telegramStart?.(settings.key, settings.admin, settings.keyScope ?? "project");
593628
593758
  } catch (err) {
593629
593759
  renderError(
593630
593760
  `Telegram error: ${err instanceof Error ? err.message : String(err)}`
@@ -593636,7 +593766,9 @@ sleep 1
593636
593766
  renderInfo("Usage:");
593637
593767
  renderInfo(" /telegram --key <token> Save bot token (project-local when .omnius/ exists)");
593638
593768
  renderInfo(" /telegram --admin <id> Set admin filter (project-local by default)");
593769
+ renderInfo(" /telegram revoke [--global] Revoke saved admin access");
593639
593770
  renderInfo(" /telegram Toggle on/off");
593771
+ renderInfo(" /telegram --global Start/stop legacy shared global token explicitly");
593640
593772
  renderInfo(" /telegram stop Stop bridge");
593641
593773
  renderInfo(" /telegram status Show status");
593642
593774
  renderInfo(" /telegram mode auto|chat|action Set interaction routing profile");
@@ -594519,7 +594651,7 @@ async function showPlatformOnboardingMenu(ctx3, id) {
594519
594651
  if (!settings.key) renderWarning("No Telegram bot token configured.");
594520
594652
  else if (ctx3.isTelegramActive?.()) {
594521
594653
  renderWarning("Telegram bridge already active. Use /telegram stop before restarting.");
594522
- } else await ctx3.telegramStart?.(settings.key, settings.admin);
594654
+ } else await ctx3.telegramStart?.(settings.key, settings.admin, settings.keyScope ?? "project");
594523
594655
  } else if (result.key === "telegram-stop") {
594524
594656
  ctx3.telegramStop?.();
594525
594657
  }
@@ -596669,13 +596801,13 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
596669
596801
  mkdirSync: mkdirSync81,
596670
596802
  existsSync: exists2
596671
596803
  } = await import("node:fs");
596672
- const { homedir: homedir52 } = await import("node:os");
596804
+ const { homedir: homedir53 } = await import("node:os");
596673
596805
  const modelName = basename30(onnxDrop.path, ".onnx").replace(
596674
596806
  /[^a-zA-Z0-9_-]/g,
596675
596807
  "-"
596676
596808
  );
596677
596809
  const destDir = pathJoin(
596678
- homedir52(),
596810
+ homedir53(),
596679
596811
  ".omnius",
596680
596812
  "voice",
596681
596813
  "models",
@@ -600202,12 +600334,12 @@ var init_commands = __esm({
600202
600334
  if (val === "any" && !process.env["OMNIUS_API_KEY"]) {
600203
600335
  try {
600204
600336
  const { randomBytes: randomBytes27 } = await import("node:crypto");
600205
- const { homedir: homedir52 } = await import("node:os");
600337
+ const { homedir: homedir53 } = await import("node:os");
600206
600338
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
600207
600339
  const { join: join148 } = await import("node:path");
600208
600340
  const apiKey = randomBytes27(16).toString("hex");
600209
600341
  process.env["OMNIUS_API_KEY"] = apiKey;
600210
- const dir = join148(homedir52(), ".omnius");
600342
+ const dir = join148(homedir53(), ".omnius");
600211
600343
  mkdirSync81(dir, { recursive: true });
600212
600344
  writeFileSync73(join148(dir, "api.key"), apiKey + "\n", "utf8");
600213
600345
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
@@ -600227,10 +600359,10 @@ var init_commands = __esm({
600227
600359
  }
600228
600360
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
600229
600361
  try {
600230
- const { homedir: homedir52 } = await import("node:os");
600362
+ const { homedir: homedir53 } = await import("node:os");
600231
600363
  const { mkdirSync: mkdirSync81, writeFileSync: writeFileSync73 } = await import("node:fs");
600232
600364
  const { join: join148 } = await import("node:path");
600233
- const dir = join148(homedir52(), ".omnius");
600365
+ const dir = join148(homedir53(), ".omnius");
600234
600366
  mkdirSync81(dir, { recursive: true });
600235
600367
  writeFileSync73(join148(dir, "access"), `${val}
600236
600368
  `, "utf8");
@@ -609439,6 +609571,7 @@ var init_vision_ingress = __esm({
609439
609571
  // packages/cli/src/tui/telegram-bridge.ts
609440
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";
609441
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";
609442
609575
  import { writeFile as writeFileAsync } from "node:fs/promises";
609443
609576
  import { createHash as createHash23, randomBytes as randomBytes22, randomInt } from "node:crypto";
609444
609577
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
@@ -609958,6 +610091,13 @@ function telegramMemoryTokens(text) {
609958
610091
  }
609959
610092
  return tokens;
609960
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
+ }
609961
610101
  function telegramMemorySimilarity(a2, b) {
609962
610102
  if (a2.size === 0 || b.size === 0) return 0;
609963
610103
  let intersection = 0;
@@ -610096,8 +610236,7 @@ function telegramSyntheticHelpSignatures() {
610096
610236
  function telegramHelpCommandAllowed(cmd, scope) {
610097
610237
  if (cmd.name === "dream") return false;
610098
610238
  if (scope === "admin") return cmd.implementationStatus === "implemented";
610099
- if (TELEGRAM_PUBLIC_HELP_COMMANDS.has(cmd.name)) return true;
610100
- return cmd.surfaces.agentTool && !cmd.safety.secretBearing && !cmd.safety.destructive && !cmd.safety.profileGated;
610239
+ return TELEGRAM_PUBLIC_HELP_COMMANDS.has(cmd.name) || TELEGRAM_PUBLIC_BOT_COMMAND_NAMES.has(cmd.name);
610101
610240
  }
610102
610241
  function buildTelegramHelpHTML(scope, maxPublicCommands = 24) {
610103
610242
  const commands = listCommandRegistry({ includePlanned: false }).filter((cmd) => telegramHelpCommandAllowed(cmd, scope));
@@ -610794,7 +610933,7 @@ function renderTelegramSubAgentError(username, error) {
610794
610933
  process.stdout.write(` ${c3.dim("│")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
610795
610934
  `);
610796
610935
  }
610797
- 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_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;
610798
610937
  var init_telegram_bridge = __esm({
610799
610938
  "packages/cli/src/tui/telegram-bridge.ts"() {
610800
610939
  "use strict";
@@ -611118,6 +611257,47 @@ External acquisition contract:
611118
611257
  "you",
611119
611258
  "your"
611120
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
+ ]);
611121
611301
  TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS = {
611122
611302
  bruteForce: false,
611123
611303
  bruteForceMaxCycles: 0,
@@ -611126,6 +611306,9 @@ External acquisition contract:
611126
611306
  TELEGRAM_PUBLIC_HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "start", "auth", "call"]);
611127
611307
  TELEGRAM_REMINDER_SLASH_COMMANDS = /* @__PURE__ */ new Set(["remind", "reminder", "reminders"]);
611128
611308
  TELEGRAM_REFLECTION_SLASH_COMMANDS = /* @__PURE__ */ new Set(["reflect", "reflection", "daydream", "dream"]);
611309
+ TELEGRAM_PUBLIC_BOT_COMMAND_NAMES = new Set(
611310
+ buildTelegramBotCommands({ scope: "public" }).map((cmd) => cmd.command)
611311
+ );
611129
611312
  TELEGRAM_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".tif", ".svg"]);
611130
611313
  MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
611131
611314
  TELEGRAM_CHANNEL_DMN_SWEEP_MS = 2 * 60 * 1e3;
@@ -611153,6 +611336,7 @@ External acquisition contract:
611153
611336
  this.telegramConversationDir = resolve43(repoRoot || ".", ".omnius", "telegram-conversations");
611154
611337
  this.telegramSqlitePath = resolve43(repoRoot || ".", ".omnius", "telegram.sqlite");
611155
611338
  this.telegramToolButtonDir = resolve43(repoRoot || ".", ".omnius", "telegram-tool-buttons");
611339
+ this.hydrateTelegramCommandMap(buildTelegramBotCommands({ scope: "admin" }));
611156
611340
  }
611157
611341
  botToken;
611158
611342
  onMessage;
@@ -611243,6 +611427,7 @@ External acquisition contract:
611243
611427
  * (which would cause silent 409 conflicts on getUpdates). Released in stop().
611244
611428
  */
611245
611429
  telegramOwnerLockFile = null;
611430
+ telegramOwnerLockFiles = [];
611246
611431
  /** Session keys loaded from persistent conversation memory */
611247
611432
  loadedConversationState = /* @__PURE__ */ new Set();
611248
611433
  /** True once persisted Telegram conversation scopes have been bulk-loaded. */
@@ -611381,6 +611566,9 @@ External acquisition contract:
611381
611566
  const botless = first2.startsWith("/") ? first2.slice(1).split("@")[0]?.toLowerCase() : "";
611382
611567
  return !!botless && this.telegramCommandMap.has(botless);
611383
611568
  }
611569
+ isPublicTelegramSlashName(name10) {
611570
+ return TELEGRAM_PUBLIC_BOT_COMMAND_NAMES.has(name10);
611571
+ }
611384
611572
  async handleTelegramReminderSlash(msg, commandText, context2) {
611385
611573
  if (!this.repoRoot) {
611386
611574
  await this.replyToTelegramMessage(msg, "Reminder storage is not available yet.");
@@ -613262,32 +613450,170 @@ ${lines.join("\n")}`);
613262
613450
  ORDER BY received_at DESC
613263
613451
  LIMIT ?
613264
613452
  `).all(sessionKey, limit);
613265
- return rows.reverse().map((row) => ({
613266
- role: row.role === "assistant" ? "assistant" : "user",
613267
- text: String(row.text || ""),
613268
- ts: Number(row.received_at || 0) || void 0,
613269
- chatId: row.chat_id,
613270
- speaker: row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown",
613271
- username: row.username || void 0,
613272
- firstName: row.first_name || void 0,
613273
- fromUserId: typeof row.from_user_id === "number" ? row.from_user_id : void 0,
613274
- messageId: typeof row.message_id === "number" ? row.message_id : void 0,
613275
- messageThreadId: typeof row.message_thread_id === "number" ? row.message_thread_id : void 0,
613276
- replyToMessageId: typeof row.reply_to_message_id === "number" ? row.reply_to_message_id : void 0,
613277
- chatType: row.chat_type,
613278
- chatTitle: row.chat_title || void 0,
613279
- mediaSummary: row.media_json ? "media attached in raw Telegram SQLite mirror" : void 0
613280
- }));
613453
+ return rows.reverse().map((row) => this.telegramSqliteRowToHistoryEntry(row));
613281
613454
  } catch {
613282
613455
  return [];
613283
613456
  }
613284
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
+ }
613285
613609
  searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
613286
613610
  const db = this.telegramDb();
613287
613611
  if (!db) return [];
613288
613612
  const limit = Math.max(1, Math.min(50, Math.floor(options2.limit ?? 12)));
613289
613613
  const username = (options2.username || "").replace(/^@/, "").trim().toLowerCase();
613614
+ const direction = options2.bucket === "earliest" ? "ASC" : "DESC";
613290
613615
  const tokens = [...telegramMemoryTokens(query)].slice(0, 8);
613616
+ const meaningfulTokens = [...telegramMeaningfulMemoryTokens(query)].slice(0, 8);
613291
613617
  const rows = /* @__PURE__ */ new Map();
613292
613618
  const addRows = (items) => {
613293
613619
  for (const row of items) {
@@ -613295,44 +613621,66 @@ ${lines.join("\n")}`);
613295
613621
  if (Number.isFinite(key)) rows.set(key, row);
613296
613622
  }
613297
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);
613298
613658
  try {
613299
613659
  if (options2.userId !== void 0) {
613300
- addRows(db.prepare(`
613301
- SELECT rowid, * FROM telegram_messages
613302
- WHERE session_key = ? AND from_user_id = ?
613303
- ORDER BY received_at DESC
613304
- LIMIT ?
613305
- `).all(sessionKey, options2.userId, limit));
613660
+ const { where, params } = baseWhere(["from_user_id = ?"], [options2.userId]);
613661
+ addRows(selectRows(where, params));
613306
613662
  }
613307
613663
  if (username) {
613308
- addRows(db.prepare(`
613309
- SELECT rowid, * FROM telegram_messages
613310
- WHERE session_key = ? AND lower(username) = ?
613311
- ORDER BY received_at DESC
613312
- LIMIT ?
613313
- `).all(sessionKey, username, limit));
613664
+ const { where, params } = baseWhere(["lower(username) = ?"], [username]);
613665
+ addRows(selectRows(where, params));
613314
613666
  }
613315
613667
  if (tokens.length > 0) {
613316
- const clauses = tokens.map(() => "text LIKE ?").join(" OR ");
613317
- addRows(db.prepare(`
613318
- SELECT rowid, * FROM telegram_messages
613319
- WHERE session_key = ? AND (${clauses})
613320
- ORDER BY received_at DESC
613321
- LIMIT ?
613322
- `).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));
613323
613672
  }
613324
- if (rows.size === 0) {
613325
- addRows(db.prepare(`
613326
- SELECT rowid, * FROM telegram_messages
613327
- WHERE session_key = ?
613328
- ORDER BY received_at DESC
613329
- LIMIT ?
613330
- `).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)));
613331
613676
  }
613332
613677
  } catch {
613333
613678
  return [];
613334
613679
  }
613335
- 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);
613336
613684
  }
613337
613685
  formatTelegramSqliteMirrorRows(rows, maxText = 260) {
613338
613686
  return rows.map((row) => {
@@ -613463,6 +613811,7 @@ ${lines.join("\n")}`);
613463
613811
  `- Associative relationships: ${relationshipCount}`,
613464
613812
  `- Per-user memories: ${userMemoryCount}`,
613465
613813
  `- Rolling history entries retained: ${historyCount}`,
613814
+ `- Addressable conversation history rows: ${Math.max(historyCount, sqliteCount)}`,
613466
613815
  `- SQLite mirror rows: ${sqliteCount}`,
613467
613816
  `- Episodes (durable, day+ scope): ${episodeCount2}`
613468
613817
  ];
@@ -613473,8 +613822,7 @@ ${lines.join("\n")}`);
613473
613822
  lines.push(` - ${topic}`);
613474
613823
  }
613475
613824
  }
613476
- const sourceHistory = this.chatHistory.get(sessionKey) ?? [];
613477
- 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);
613478
613826
  const fmtHistoryAnchor = (entry) => {
613479
613827
  const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
613480
613828
  const speaker = telegramHistorySpeaker(entry);
@@ -613482,18 +613830,33 @@ ${lines.join("\n")}`);
613482
613830
  const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
613483
613831
  return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613484
613832
  };
613485
- 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) {
613486
613839
  lines.push("");
613487
- lines.push("Chronological anchors — Telegram conversation history (ground truth for 'oldest/newest memory' questions):");
613488
- lines.push(` EARLIEST turn: ${fmtHistoryAnchor(sortedByTs[0])}`);
613489
- if (sortedByTs.length > 1) {
613490
- 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)}`);
613844
+ }
613845
+ if (anchors.earliest[1]) {
613846
+ lines.push(` 2nd earliest: ${fmtHistoryAnchor(anchors.earliest[1])}`);
613491
613847
  }
613492
- if (sortedByTs.length > 2) {
613493
- lines.push(` 2nd earliest: ${fmtHistoryAnchor(sortedByTs[1])}`);
613848
+ if (anchors.earliest[2]) {
613849
+ lines.push(` 3rd earliest: ${fmtHistoryAnchor(anchors.earliest[2])}`);
613494
613850
  }
613495
- if (sortedByTs.length > 3) {
613496
- lines.push(` 3rd earliest: ${fmtHistoryAnchor(sortedByTs[2])}`);
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})`);
613497
613860
  }
613498
613861
  }
613499
613862
  if (this.repoRoot) {
@@ -614221,37 +614584,23 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614221
614584
  if (!me?.ok) {
614222
614585
  throw new Error(`Invalid Telegram bot token: ${me?.description || "unknown error"}`);
614223
614586
  }
614224
- 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 = [];
614225
614595
  try {
614226
- const lockDir = resolve43(this.repoRoot, ".omnius", "telegram-runner-state");
614227
- mkdirSync65(lockDir, { recursive: true });
614228
- const lockFile = join127(lockDir, `bot-${me.result.id}.owner.lock`);
614229
- if (existsSync113(lockFile)) {
614230
- try {
614231
- const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
614232
- const priorAlive = typeof prior.pid === "number" && prior.pid !== process.pid ? this.processIsAlive(prior.pid) : false;
614233
- if (priorAlive) {
614234
- throw new Error(
614235
- `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.`
614236
- );
614237
- }
614238
- } catch (e2) {
614239
- if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614240
- }
614596
+ for (const lockDir of lockDirs) {
614597
+ const lockFile = this.claimTelegramOwnerLock(lockDir, botUserId, me.result.username);
614598
+ claimed.push(lockFile);
614241
614599
  }
614242
- writeFileSync59(
614243
- lockFile,
614244
- JSON.stringify({
614245
- pid: process.pid,
614246
- cwd: this.repoRoot,
614247
- botUsername: me.result.username,
614248
- botUserId: me.result.id,
614249
- ts: Date.now()
614250
- }, null, 2),
614251
- { encoding: "utf-8", mode: 384 }
614252
- );
614253
- this.telegramOwnerLockFile = lockFile;
614600
+ this.telegramOwnerLockFiles = claimed;
614601
+ this.telegramOwnerLockFile = claimed[0] ?? null;
614254
614602
  } catch (e2) {
614603
+ for (const lockFile of claimed) this.releaseTelegramOwnerLock(lockFile);
614255
614604
  if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
614256
614605
  }
614257
614606
  }
@@ -614309,18 +614658,57 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614309
614658
  }
614310
614659
  this.telegramSqliteDb = null;
614311
614660
  }
614312
- 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)) {
614313
614676
  try {
614314
- if (existsSync113(this.telegramOwnerLockFile)) {
614315
- 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
+ );
614316
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;
614317
614707
  } catch {
614318
614708
  }
614319
- this.telegramOwnerLockFile = null;
614709
+ unlinkSync22(lockFile);
614710
+ } catch {
614320
614711
  }
614321
- this.subAgents.clear();
614322
- this.activeChatViews.clear();
614323
- this.refreshActiveTelegramInteractionCount();
614324
614712
  }
614325
614713
  /**
614326
614714
  * Cheap liveness probe: kill(pid, 0) throws ESRCH when the process is dead
@@ -614551,8 +614939,7 @@ Join: ${newUrl}`);
614551
614939
  return;
614552
614940
  }
614553
614941
  }
614554
- const publicCreativeSlash = telegramSlash === "image";
614555
- if (!isAdmin && msg.text.trim().startsWith("/") && this.isKnownTelegramSlash(normalizedCommandText) && !publicCreativeSlash) {
614942
+ if (!isAdmin && msg.text.trim().startsWith("/") && this.isKnownTelegramSlash(normalizedCommandText) && !this.isPublicTelegramSlashName(telegramSlash)) {
614556
614943
  await this.replyToTelegramMessage(
614557
614944
  msg,
614558
614945
  "That command requires Telegram admin authentication. Run /telegram auth in the TUI, then send /auth <code> here."
@@ -615446,7 +615833,9 @@ ${result.llmContent ?? result.output}` };
615446
615833
  })(),
615447
615834
  execute: async (args) => {
615448
615835
  const rawQuery = String(args["query"] || "").trim();
615449
- 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;
615450
615839
  if (!rawQuery) return { success: true, output: "Search query is required." };
615451
615840
  this.ensureTelegramConversationLoaded(msgSessionKey);
615452
615841
  const parseTimeArg = (v) => {
@@ -615480,6 +615869,9 @@ ${result.llmContent ?? result.output}` };
615480
615869
  bucket ? `bucket=${bucket}` : ""
615481
615870
  ].filter(Boolean).join(", ");
615482
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
+ }
615483
615875
  const inWindow = (ts) => {
615484
615876
  if (since === void 0 && until === void 0) return true;
615485
615877
  if (ts == null || !Number.isFinite(ts)) return false;
@@ -615516,6 +615908,7 @@ ${result.llmContent ?? result.output}` };
615516
615908
  return false;
615517
615909
  });
615518
615910
  const queryTokens = telegramMemoryTokens(query);
615911
+ const meaningfulQueryTokens = telegramMeaningfulMemoryTokens(query);
615519
615912
  const cardResults = cards.filter((card) => inWindow(card.updatedAt ?? card.createdAt)).map((card) => ({
615520
615913
  card,
615521
615914
  score: telegramMemorySimilarity(
@@ -615553,12 +615946,19 @@ ${notes2}`;
615553
615946
  const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
615554
615947
  limit: maxResults,
615555
615948
  userId: effectiveUserId,
615556
- username: effectiveUsername
615949
+ username: effectiveUsername,
615950
+ since,
615951
+ until,
615952
+ bucket
615557
615953
  });
615558
- const episodeResults = this.searchTelegramEpisodeMemory(msgSessionKey, query, maxResults);
615559
- 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);
615560
615960
  const lowerQuery = query.toLowerCase().trim();
615561
- const queryWords = lowerQuery.split(/\s+/).filter((w) => w.length >= 3);
615961
+ const queryWords = [...meaningfulQueryTokens];
615562
615962
  const hasTimeConstraint = since !== void 0 || until !== void 0 || bucket !== void 0;
615563
615963
  const rawHistoryMatches = [];
615564
615964
  if (historyForScan.length > 0) {
@@ -615570,7 +615970,12 @@ ${notes2}`;
615570
615970
  if (!matchesUser) continue;
615571
615971
  }
615572
615972
  if (!inWindow(entry.ts ?? null)) continue;
615573
- const hay = String(entry.text || "").toLowerCase();
615973
+ const hay = [
615974
+ entry.text || "",
615975
+ telegramHistorySpeaker(entry),
615976
+ entry.username || "",
615977
+ entry.firstName || ""
615978
+ ].join(" ").toLowerCase();
615574
615979
  if (!hay) continue;
615575
615980
  let lexMatch = false;
615576
615981
  if (lowerQuery.length > 0) {
@@ -615578,7 +615983,7 @@ ${notes2}`;
615578
615983
  const tokenHits = queryWords.filter((w) => hay.includes(w)).length;
615579
615984
  lexMatch = hasExact || queryWords.length > 0 && tokenHits >= Math.min(2, queryWords.length);
615580
615985
  }
615581
- if (lexMatch || hasTimeConstraint) {
615986
+ if (lexMatch || hasTimeConstraint && queryWords.length === 0) {
615582
615987
  rawHistoryMatches.push(entry);
615583
615988
  }
615584
615989
  }
@@ -615591,6 +615996,44 @@ ${notes2}`;
615591
615996
  const rawHistorySliced = rawHistoryMatches.slice(0, maxResults);
615592
615997
  const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
615593
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
+ }
615594
616037
  if (cardLines.length > 0) {
615595
616038
  sections.push(`### Scoped Memory Cards
615596
616039
  ${cardLines.join("\n\n")}`);
@@ -617905,21 +618348,45 @@ Content-Type: ${mimeForPath(pathOrFileId, field === "photo" ? "image" : "video")
617905
618348
  }
617906
618349
  return this.sendMessage(`@${clean5}`, text);
617907
618350
  }
617908
- async setMyCommands(commands) {
618351
+ hydrateTelegramCommandMap(commands) {
618352
+ for (const cmd of commands) {
618353
+ this.telegramCommandMap.set(cmd.command, cmd.canonical);
618354
+ }
618355
+ this.telegramCommandMap.set("auth", "auth");
618356
+ this.telegramCommandMap.set("start", "start");
618357
+ }
618358
+ async setMyCommands(commands, options2 = {}) {
617909
618359
  const apiCommands = commands.map((cmd) => ({
617910
618360
  command: cmd.command,
617911
618361
  description: cmd.description
617912
618362
  }));
617913
- const result = await this.apiCall("setMyCommands", { commands: apiCommands });
618363
+ const payload = { commands: apiCommands };
618364
+ if (options2.scope) payload["scope"] = options2.scope;
618365
+ if (options2.languageCode) payload["language_code"] = options2.languageCode;
618366
+ const result = await this.apiCall("setMyCommands", payload);
617914
618367
  if (result.ok) {
617915
- for (const cmd of commands) {
617916
- this.telegramCommandMap.set(cmd.command, cmd.canonical);
617917
- }
617918
- this.telegramCommandMap.set("auth", "auth");
617919
- this.telegramCommandMap.set("start", "start");
618368
+ this.hydrateTelegramCommandMap(commands);
617920
618369
  }
617921
618370
  return Boolean(result.ok);
617922
618371
  }
618372
+ async registerScopedMyCommands(adminUserId = this.adminUserId) {
618373
+ const publicCommands = buildTelegramBotCommands({ scope: "public" });
618374
+ const adminCommands = buildTelegramBotCommands({ scope: "admin" });
618375
+ this.hydrateTelegramCommandMap([...publicCommands, ...adminCommands]);
618376
+ const publicOk = await this.setMyCommands(publicCommands, { scope: { type: "default" } });
618377
+ const adminId = adminUserId?.trim();
618378
+ if (!adminId || !/^\d+$/.test(adminId)) {
618379
+ return publicOk;
618380
+ }
618381
+ const adminChatId = Number(adminId);
618382
+ if (!Number.isSafeInteger(adminChatId)) {
618383
+ return publicOk;
618384
+ }
618385
+ const adminOk = await this.setMyCommands(adminCommands, {
618386
+ scope: { type: "chat", chat_id: adminChatId }
618387
+ });
618388
+ return publicOk && adminOk;
618389
+ }
617923
618390
  async getMyCommands() {
617924
618391
  const result = await this.apiCall("getMyCommands");
617925
618392
  return Array.isArray(result.result) ? result.result : [];
@@ -618296,9 +618763,9 @@ import {
618296
618763
  unlinkSync as unlinkSync23
618297
618764
  } from "node:fs";
618298
618765
  import { join as join128 } from "node:path";
618299
- import { homedir as homedir40 } from "node:os";
618766
+ import { homedir as homedir41 } from "node:os";
618300
618767
  function sessionsDir() {
618301
- return join128(homedir40(), ".omnius", "chat-sessions");
618768
+ return join128(homedir41(), ".omnius", "chat-sessions");
618302
618769
  }
618303
618770
  function sessionPath(id) {
618304
618771
  const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
@@ -618938,7 +619405,7 @@ __export(projects_exports, {
618938
619405
  unregisterProject: () => unregisterProject
618939
619406
  });
618940
619407
  import { readFileSync as readFileSync94, writeFileSync as writeFileSync61, mkdirSync as mkdirSync67, existsSync as existsSync115, statSync as statSync40, renameSync as renameSync7 } from "node:fs";
618941
- import { homedir as homedir41 } from "node:os";
619408
+ import { homedir as homedir42 } from "node:os";
618942
619409
  import { basename as basename28, join as join129, resolve as resolve44 } from "node:path";
618943
619410
  import { randomUUID as randomUUID14 } from "node:crypto";
618944
619411
  function readAll2() {
@@ -619051,7 +619518,7 @@ var OMNIUS_DIR3, PROJECTS_FILE, CURRENT_FILE, currentRoot;
619051
619518
  var init_projects = __esm({
619052
619519
  "packages/cli/src/api/projects.ts"() {
619053
619520
  "use strict";
619054
- OMNIUS_DIR3 = join129(homedir41(), ".omnius");
619521
+ OMNIUS_DIR3 = join129(homedir42(), ".omnius");
619055
619522
  PROJECTS_FILE = join129(OMNIUS_DIR3, "projects.json");
619056
619523
  CURRENT_FILE = join129(OMNIUS_DIR3, "current-project");
619057
619524
  currentRoot = null;
@@ -619758,7 +620225,7 @@ var init_access_policy = __esm({
619758
620225
  // packages/cli/src/api/project-preferences.ts
619759
620226
  import { createHash as createHash24 } from "node:crypto";
619760
620227
  import { existsSync as existsSync116, mkdirSync as mkdirSync68, readFileSync as readFileSync95, renameSync as renameSync8, writeFileSync as writeFileSync62, unlinkSync as unlinkSync24 } from "node:fs";
619761
- import { homedir as homedir42 } from "node:os";
620228
+ import { homedir as homedir43 } from "node:os";
619762
620229
  import { join as join130, resolve as resolve45 } from "node:path";
619763
620230
  import { randomUUID as randomUUID15 } from "node:crypto";
619764
620231
  function projectKey(root) {
@@ -619839,7 +620306,7 @@ var OMNIUS_DIR4, PROJECTS_DIR, SCHEMA_VERSION, DEFAULT_PREFS;
619839
620306
  var init_project_preferences = __esm({
619840
620307
  "packages/cli/src/api/project-preferences.ts"() {
619841
620308
  "use strict";
619842
- OMNIUS_DIR4 = join130(homedir42(), ".omnius");
620309
+ OMNIUS_DIR4 = join130(homedir43(), ".omnius");
619843
620310
  PROJECTS_DIR = join130(OMNIUS_DIR4, "projects");
619844
620311
  SCHEMA_VERSION = 1;
619845
620312
  DEFAULT_PREFS = {
@@ -621115,7 +621582,7 @@ __export(aiwg_exports, {
621115
621582
  });
621116
621583
  import { existsSync as existsSync119, readFileSync as readFileSync97, readdirSync as readdirSync42, statSync as statSync42 } from "node:fs";
621117
621584
  import { join as join132 } from "node:path";
621118
- import { homedir as homedir43 } from "node:os";
621585
+ import { homedir as homedir44 } from "node:os";
621119
621586
  import { execSync as execSync55 } from "node:child_process";
621120
621587
  function resolveAiwgRoot() {
621121
621588
  if (_cachedAiwgRoot !== void 0) return _cachedAiwgRoot;
@@ -621124,7 +621591,7 @@ function resolveAiwgRoot() {
621124
621591
  _cachedAiwgRoot = envRoot;
621125
621592
  return envRoot;
621126
621593
  }
621127
- const shareDir = join132(homedir43(), ".local", "share", "ai-writing-guide");
621594
+ const shareDir = join132(homedir44(), ".local", "share", "ai-writing-guide");
621128
621595
  if (existsSync119(join132(shareDir, "agentic"))) {
621129
621596
  _cachedAiwgRoot = shareDir;
621130
621597
  return shareDir;
@@ -621153,8 +621620,8 @@ function resolveAiwgRoot() {
621153
621620
  }
621154
621621
  }
621155
621622
  const versionDirs = [
621156
- join132(homedir43(), ".nvm", "versions", "node"),
621157
- join132(homedir43(), ".local", "share", "fnm", "node-versions")
621623
+ join132(homedir44(), ".nvm", "versions", "node"),
621624
+ join132(homedir44(), ".local", "share", "fnm", "node-versions")
621158
621625
  ];
621159
621626
  for (const vdir of versionDirs) {
621160
621627
  if (!existsSync119(vdir)) continue;
@@ -621902,10 +622369,10 @@ __export(runtime_keys_exports, {
621902
622369
  });
621903
622370
  import { existsSync as existsSync120, readFileSync as readFileSync98, writeFileSync as writeFileSync63, mkdirSync as mkdirSync71, chmodSync as chmodSync2 } from "node:fs";
621904
622371
  import { join as join133 } from "node:path";
621905
- import { homedir as homedir44 } from "node:os";
622372
+ import { homedir as homedir45 } from "node:os";
621906
622373
  import { randomBytes as randomBytes23 } from "node:crypto";
621907
622374
  function ensureDir2() {
621908
- const dir = join133(homedir44(), ".omnius");
622375
+ const dir = join133(homedir45(), ".omnius");
621909
622376
  if (!existsSync120(dir)) mkdirSync71(dir, { recursive: true });
621910
622377
  }
621911
622378
  function loadAll() {
@@ -621986,7 +622453,7 @@ var KEYS_FILE;
621986
622453
  var init_runtime_keys = __esm({
621987
622454
  "packages/cli/src/api/runtime-keys.ts"() {
621988
622455
  "use strict";
621989
- KEYS_FILE = join133(homedir44(), ".omnius", "keys.json");
622456
+ KEYS_FILE = join133(homedir45(), ".omnius", "keys.json");
621990
622457
  }
621991
622458
  });
621992
622459
 
@@ -621998,13 +622465,13 @@ __export(tor_fallback_exports, {
621998
622465
  tunnelViaTor: () => tunnelViaTor
621999
622466
  });
622000
622467
  import { existsSync as existsSync121, readFileSync as readFileSync99 } from "node:fs";
622001
- import { homedir as homedir45 } from "node:os";
622468
+ import { homedir as homedir46 } from "node:os";
622002
622469
  import { join as join134 } from "node:path";
622003
622470
  import { createConnection as createConnection3 } from "node:net";
622004
622471
  function getLocalOnion() {
622005
622472
  const candidates = [
622006
- join134(homedir45(), "hidden_service_hostname"),
622007
- join134(homedir45(), ".omnius", "tor", "hostname"),
622473
+ join134(homedir46(), "hidden_service_hostname"),
622474
+ join134(homedir46(), ".omnius", "tor", "hostname"),
622008
622475
  "/var/lib/tor/hidden_service/hostname"
622009
622476
  ];
622010
622477
  for (const p2 of candidates) {
@@ -622273,7 +622740,7 @@ var init_graphical_sudo = __esm({
622273
622740
  // packages/cli/src/api/routes-v1.ts
622274
622741
  import { existsSync as existsSync123, readFileSync as readFileSync100, readdirSync as readdirSync43, statSync as statSync43 } from "node:fs";
622275
622742
  import { join as join136, resolve as pathResolve2 } from "node:path";
622276
- import { homedir as homedir46 } from "node:os";
622743
+ import { homedir as homedir47 } from "node:os";
622277
622744
  async function tryRouteV1(ctx3) {
622278
622745
  const { pathname, method } = ctx3;
622279
622746
  if (pathname === "/v1/skills" && method === "GET") {
@@ -622508,7 +622975,7 @@ async function handleGetSkill(ctx3, name10) {
622508
622975
  async function fallbackDiscoverSkills() {
622509
622976
  return (_root) => {
622510
622977
  const roots = [
622511
- join136(homedir46(), ".local", "share", "ai-writing-guide")
622978
+ join136(homedir47(), ".local", "share", "ai-writing-guide")
622512
622979
  ];
622513
622980
  const out = [];
622514
622981
  for (const root of roots) {
@@ -623243,7 +623710,7 @@ async function handleNexusStatus(ctx3) {
623243
623710
  try {
623244
623711
  const statePaths = [
623245
623712
  join136(process.cwd(), ".omnius", "nexus-peer-state.json"),
623246
- join136(homedir46(), ".omnius", "nexus-peer-cache.json")
623713
+ join136(homedir47(), ".omnius", "nexus-peer-cache.json")
623247
623714
  ];
623248
623715
  const states = [];
623249
623716
  for (const p2 of statePaths) {
@@ -623276,7 +623743,7 @@ async function handleNexusStatus(ctx3) {
623276
623743
  }
623277
623744
  function loadAgentName() {
623278
623745
  try {
623279
- const p2 = join136(homedir46(), ".omnius", "agent-name");
623746
+ const p2 = join136(homedir47(), ".omnius", "agent-name");
623280
623747
  if (existsSync123(p2)) return readFileSync100(p2, "utf-8").trim();
623281
623748
  } catch {
623282
623749
  }
@@ -623286,8 +623753,8 @@ async function handleSponsors(ctx3) {
623286
623753
  const { req: req2, res, url, requestId } = ctx3;
623287
623754
  try {
623288
623755
  const candidates = [
623289
- join136(homedir46(), ".omnius", "sponsor-cache.json"),
623290
- join136(homedir46(), ".omnius", "sponsors.json")
623756
+ join136(homedir47(), ".omnius", "sponsor-cache.json"),
623757
+ join136(homedir47(), ".omnius", "sponsors.json")
623291
623758
  ];
623292
623759
  let sponsors = [];
623293
623760
  for (const p2 of candidates) {
@@ -623544,9 +624011,9 @@ function resolveLocalPeerId() {
623544
624011
  const projectScoped = scope === "project" || scope === "local" || projectScopeFlag === "1" || projectScopeFlag === "true" || projectScopeFlag === "yes";
623545
624012
  const candidates = projectScoped ? [
623546
624013
  join136(process.cwd(), ".omnius", "nexus", "status.json"),
623547
- join136(homedir46(), ".omnius", "nexus", "status.json")
624014
+ join136(homedir47(), ".omnius", "nexus", "status.json")
623548
624015
  ] : [
623549
- join136(homedir46(), ".omnius", "nexus", "status.json"),
624016
+ join136(homedir47(), ".omnius", "nexus", "status.json"),
623550
624017
  join136(process.cwd(), ".omnius", "nexus", "status.json")
623551
624018
  ];
623552
624019
  for (const p2 of candidates) {
@@ -623554,7 +624021,7 @@ function resolveLocalPeerId() {
623554
624021
  if (r2) return r2;
623555
624022
  }
623556
624023
  try {
623557
- const regPath = join136(homedir46(), ".omnius", "nexus-registry.json");
624024
+ const regPath = join136(homedir47(), ".omnius", "nexus-registry.json");
623558
624025
  if (existsSync123(regPath)) {
623559
624026
  const reg = JSON.parse(readFileSync100(regPath, "utf-8"));
623560
624027
  const entries = Array.isArray(reg?.dirs) ? reg.dirs : [];
@@ -623575,7 +624042,7 @@ function resolveLocalPeerId() {
623575
624042
  let scanResult = null;
623576
624043
  try {
623577
624044
  const { execSync: execSync60 } = __require("node:child_process");
623578
- 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`;
623579
624046
  const out = execSync60(cmd, { encoding: "utf-8", timeout: 2e3 }).trim();
623580
624047
  for (const line of out.split("\n")) {
623581
624048
  const f2 = line.trim();
@@ -623882,9 +624349,9 @@ async function handleRemoteProxy(ctx3) {
623882
624349
  const tunnelProjectScope = tunnelScope === "project" || tunnelScope === "local" || ["1", "true", "yes"].includes((process.env["OMNIUS_NEXUS_PROJECT_SCOPE"] || "").toLowerCase());
623883
624350
  const nexusCandidates = tunnelProjectScope ? [
623884
624351
  join136(process.cwd(), ".omnius", "nexus"),
623885
- join136(homedir46(), ".omnius", "nexus")
624352
+ join136(homedir47(), ".omnius", "nexus")
623886
624353
  ] : [
623887
- join136(homedir46(), ".omnius", "nexus"),
624354
+ join136(homedir47(), ".omnius", "nexus"),
623888
624355
  join136(process.cwd(), ".omnius", "nexus")
623889
624356
  ];
623890
624357
  let nexusDirPath = null;
@@ -624572,7 +625039,7 @@ async function handleListAgentTypes(ctx3) {
624572
625039
  }
624573
625040
  async function handleListEngines(ctx3) {
624574
625041
  const { res } = ctx3;
624575
- const home = homedir46();
625042
+ const home = homedir47();
624576
625043
  sendJson(res, 200, {
624577
625044
  engines: [
624578
625045
  { name: "dream", state_file: join136(process.cwd(), ".omnius", "dreams"), controllable_via: "SSE + slash commands" },
@@ -624665,7 +625132,7 @@ async function tryAimsRoute(ctx3) {
624665
625132
  return false;
624666
625133
  }
624667
625134
  function aimsDir() {
624668
- return join136(homedir46(), ".omnius", "aims");
625135
+ return join136(homedir47(), ".omnius", "aims");
624669
625136
  }
624670
625137
  function readAimsFile(name10, fallback) {
624671
625138
  try {
@@ -625009,7 +625476,7 @@ async function handleAimsSuppliers(ctx3) {
625009
625476
  }
625010
625477
  ];
625011
625478
  const sponsorPaths = [
625012
- join136(homedir46(), ".omnius", "sponsor-cache.json")
625479
+ join136(homedir47(), ".omnius", "sponsor-cache.json")
625013
625480
  ];
625014
625481
  for (const p2 of sponsorPaths) {
625015
625482
  if (!existsSync123(p2)) continue;
@@ -634534,10 +635001,10 @@ var init_usage_tracker = __esm({
634534
635001
  // packages/cli/src/api/profiles.ts
634535
635002
  import { existsSync as existsSync125, readFileSync as readFileSync102, writeFileSync as writeFileSync66, mkdirSync as mkdirSync74, readdirSync as readdirSync44, unlinkSync as unlinkSync25 } from "node:fs";
634536
635003
  import { join as join138 } from "node:path";
634537
- import { homedir as homedir47 } from "node:os";
635004
+ import { homedir as homedir48 } from "node:os";
634538
635005
  import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes24, scryptSync as scryptSync3 } from "node:crypto";
634539
635006
  function globalProfileDir() {
634540
- return join138(homedir47(), ".omnius", "profiles");
635007
+ return join138(homedir48(), ".omnius", "profiles");
634541
635008
  }
634542
635009
  function projectProfileDir(projectDir2) {
634543
635010
  return join138(projectDir2 || process.cwd(), ".omnius", "profiles");
@@ -634791,7 +635258,7 @@ var init_profiles = __esm({
634791
635258
  import { execSync as execSync56, spawn as spawn29 } from "node:child_process";
634792
635259
  import { existsSync as existsSync126, mkdirSync as mkdirSync75, writeFileSync as writeFileSync67 } from "node:fs";
634793
635260
  import { join as join139, resolve as resolve46, dirname as dirname37 } from "node:path";
634794
- import { homedir as homedir48 } from "node:os";
635261
+ import { homedir as homedir49 } from "node:os";
634795
635262
  import { fileURLToPath as fileURLToPath16 } from "node:url";
634796
635263
  function getDockerDir() {
634797
635264
  try {
@@ -634939,7 +635406,7 @@ async function ensureOmniusImage(force = false) {
634939
635406
  if (existsSync126(join139(dockerDir, "Dockerfile"))) {
634940
635407
  buildContext = dockerDir;
634941
635408
  } else {
634942
- buildContext = join139(homedir48(), ".omnius", "docker-build");
635409
+ buildContext = join139(homedir49(), ".omnius", "docker-build");
634943
635410
  mkdirSync75(buildContext, { recursive: true });
634944
635411
  writeDockerfiles(buildContext);
634945
635412
  }
@@ -635271,7 +635738,7 @@ import * as https3 from "node:https";
635271
635738
  import { createRequire as createRequire7 } from "node:module";
635272
635739
  import { fileURLToPath as fileURLToPath17 } from "node:url";
635273
635740
  import { dirname as dirname38, join as join141, resolve as resolve47 } from "node:path";
635274
- import { homedir as homedir49 } from "node:os";
635741
+ import { homedir as homedir50 } from "node:os";
635275
635742
  import { spawn as spawn30, execSync as execSync57 } from "node:child_process";
635276
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";
635277
635744
  import { randomBytes as randomBytes25, randomUUID as randomUUID16 } from "node:crypto";
@@ -635586,7 +636053,7 @@ function isOriginAllowed(origin) {
635586
636053
  if (!origin) return true;
635587
636054
  let accessMode = (process.env["OMNIUS_ACCESS"] || "").toLowerCase().trim();
635588
636055
  try {
635589
- const accessFile = join141(homedir49(), ".omnius", "access");
636056
+ const accessFile = join141(homedir50(), ".omnius", "access");
635590
636057
  if (existsSync127(accessFile)) {
635591
636058
  const persisted = readFileSync103(accessFile, "utf8").trim().toLowerCase();
635592
636059
  if (persisted === "any" || persisted === "lan" || persisted === "loopback") {
@@ -637825,10 +638292,10 @@ ${task}` : task;
637825
638292
  });
637826
638293
  }
637827
638294
  function updateStateFile() {
637828
- return join141(homedir49(), ".omnius", "update-state.json");
638295
+ return join141(homedir50(), ".omnius", "update-state.json");
637829
638296
  }
637830
638297
  function updateLogPath() {
637831
- return join141(homedir49(), ".omnius", "update.log");
638298
+ return join141(homedir50(), ".omnius", "update.log");
637832
638299
  }
637833
638300
  function readUpdateState() {
637834
638301
  try {
@@ -637841,7 +638308,7 @@ function readUpdateState() {
637841
638308
  }
637842
638309
  function writeUpdateState(state) {
637843
638310
  try {
637844
- const dir = join141(homedir49(), ".omnius");
638311
+ const dir = join141(homedir50(), ".omnius");
637845
638312
  mkdirSync76(dir, { recursive: true });
637846
638313
  const finalPath = updateStateFile();
637847
638314
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
@@ -637897,7 +638364,7 @@ async function handleV1Update(req2, res, requestId) {
637897
638364
  }
637898
638365
  if (!npmBin) npmBin = isWin2 ? "npm.cmd" : "npm";
637899
638366
  const pkgSpec = `omnius@${targetVersion}`;
637900
- const dir = join141(homedir49(), ".omnius");
638367
+ const dir = join141(homedir50(), ".omnius");
637901
638368
  fs11.mkdirSync(dir, { recursive: true });
637902
638369
  const logFd = fs11.openSync(logPath3, "w");
637903
638370
  const npmPrefix = dirname38(nodeDir);
@@ -641132,7 +641599,7 @@ ${steering}`;
641132
641599
  function getScheduleRoots() {
641133
641600
  const rootsEnv = process.env["OMNIUS_SCHEDULE_ROOTS"] || "";
641134
641601
  const roots = rootsEnv.split(rootsEnv.includes(";") ? ";" : ":").filter(Boolean);
641135
- const defaults3 = [process.cwd(), join141(homedir49(), "Documents")];
641602
+ const defaults3 = [process.cwd(), join141(homedir50(), "Documents")];
641136
641603
  const set = /* @__PURE__ */ new Set([...defaults3, ...roots]);
641137
641604
  return [...set];
641138
641605
  }
@@ -641637,7 +642104,7 @@ function fixupOrMigrateScheduled(mode, dryRun) {
641637
642104
  try {
641638
642105
  if (!f2.workingDir || !f2.task) continue;
641639
642106
  const unitBase = `omnius-${f2.id}`;
641640
- const unitDir = join141(homedir49(), ".config", "systemd", "user");
642107
+ const unitDir = join141(homedir50(), ".config", "systemd", "user");
641641
642108
  const svc = join141(unitDir, `${unitBase}.service`);
641642
642109
  const tim = join141(unitDir, `${unitBase}.timer`);
641643
642110
  const omniusBin = findOmniusBinary4();
@@ -641930,7 +642397,7 @@ function startApiServer(options2 = {}) {
641930
642397
  }
641931
642398
  let runtimeAccessMode = resolveAccessMode(process.env["OMNIUS_ACCESS"], host);
641932
642399
  try {
641933
- const accessFile = join141(homedir49(), ".omnius", "access");
642400
+ const accessFile = join141(homedir50(), ".omnius", "access");
641934
642401
  if (existsSync127(accessFile)) {
641935
642402
  const persisted = readFileSync103(accessFile, "utf8").trim();
641936
642403
  const resolved = resolveAccessMode(persisted, host);
@@ -641992,7 +642459,7 @@ function startApiServer(options2 = {}) {
641992
642459
  const previous = runtimeAccessMode;
641993
642460
  runtimeAccessMode = requested;
641994
642461
  try {
641995
- const dir = join141(homedir49(), ".omnius");
642462
+ const dir = join141(homedir50(), ".omnius");
641996
642463
  mkdirSync76(dir, { recursive: true });
641997
642464
  writeFileSync68(join141(dir, "access"), `${runtimeAccessMode}
641998
642465
  `, "utf8");
@@ -643062,7 +643529,7 @@ import {
643062
643529
  } from "node:fs";
643063
643530
  import { existsSync as existsSync128 } from "node:fs";
643064
643531
  import { execSync as execSync59 } from "node:child_process";
643065
- import { homedir as homedir50 } from "node:os";
643532
+ import { homedir as homedir51 } from "node:os";
643066
643533
  function formatTimeAgo2(date) {
643067
643534
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
643068
643535
  if (seconds < 60) return "just now";
@@ -644195,7 +644662,7 @@ function extractGeneratedAudioPath(output, repoRoot) {
644195
644662
  const match = output.match(/(?:Sound|Music|TTS) generated:\s+([^\n\r]+)/i);
644196
644663
  const raw = match?.[1]?.trim().replace(/^["']|["']$/g, "");
644197
644664
  if (!raw) return null;
644198
- return raw.startsWith("/") || raw.startsWith("~") ? raw.replace(/^~(?=\/)/, homedir50()) : join143(repoRoot, raw);
644665
+ return raw.startsWith("/") || raw.startsWith("~") ? raw.replace(/^~(?=\/)/, homedir51()) : join143(repoRoot, raw);
644199
644666
  }
644200
644667
  async function playGeneratedAudioForToolResult(toolName, output, repoRoot, writer) {
644201
644668
  if (!toolName || !["generate_audio", "generate_tts", "audio_playback"].includes(toolName) || !output) return;
@@ -644249,7 +644716,7 @@ async function runSelfImprovementCycle(repoRoot) {
644249
644716
  } catch {
644250
644717
  }
644251
644718
  }
644252
- function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback, selfModifyEnabled) {
644719
+ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback, selfModifyEnabled, sessionMetrics) {
644253
644720
  const voiceStyleMap = {
644254
644721
  concise: 1,
644255
644722
  balanced: 3,
@@ -644271,6 +644738,9 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
644271
644738
  }
644272
644739
  const modelTier = getModelTier(config.model);
644273
644740
  const sessionId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
644741
+ const taskMetricDescription = cleanForStorage(task).replace(/\s+/g, " ").slice(0, 160) || "task";
644742
+ costTracker?.startTask(taskMetricDescription);
644743
+ sessionMetrics?.startTask(taskMetricDescription);
644274
644744
  const projectCtx = buildProjectContext(repoRoot, taskStores?.contextStores);
644275
644745
  let dynamicContext = formatContextForPrompt(projectCtx, modelTier);
644276
644746
  try {
@@ -645230,6 +645700,7 @@ ${entry.fullContent}`
645230
645700
  let lastToolCall = null;
645231
645701
  let toolCallStartMs = 0;
645232
645702
  let streamStartMs = 0;
645703
+ let lastStreamDurationMs = 0;
645233
645704
  let streamTextBuffer = "";
645234
645705
  let lastAssistantText = "";
645235
645706
  let lastProvenancePath = null;
@@ -645291,6 +645762,9 @@ ${entry.fullContent}`
645291
645762
  case "tool_call":
645292
645763
  if (_apiCallbacks?.onToolCall)
645293
645764
  _apiCallbacks.onToolCall(event.toolName ?? "unknown", event.toolArgs);
645765
+ if (event.toolName && event.toolName !== "task_complete") {
645766
+ sessionMetrics?.recordToolCall(event.toolName);
645767
+ }
645294
645768
  toolSequence.push({
645295
645769
  tool: event.toolName ?? "unknown",
645296
645770
  argKeys: Object.keys(event.toolArgs ?? {})
@@ -645312,6 +645786,7 @@ ${entry.fullContent}`
645312
645786
  const name10 = event.toolName ?? "";
645313
645787
  if (name10 === "file_write" || name10 === "file_edit" || name10 === "file_patch" || name10 === "batch_edit") {
645314
645788
  filesTouched.add(event.toolArgs.path);
645789
+ sessionMetrics?.recordFileModified(event.toolArgs.path);
645315
645790
  if (isNeovimActive()) {
645316
645791
  notifyNeovimFileChange(event.toolArgs.path).catch(
645317
645792
  () => {
@@ -645540,6 +646015,7 @@ ${entry.fullContent}`
645540
646015
  break;
645541
646016
  case "stream_end": {
645542
646017
  const streamDurationMs = streamStartMs > 0 ? Date.now() - streamStartMs : 0;
646018
+ lastStreamDurationMs = streamDurationMs;
645543
646019
  streamStartMs = 0;
645544
646020
  if (isNeovimActive()) {
645545
646021
  writeToNeovimOutput("\r\n");
@@ -645618,17 +646094,28 @@ ${entry.fullContent}`
645618
646094
  }
645619
646095
  break;
645620
646096
  case "token_usage":
645621
- if (statusBar && event.tokenUsage) {
646097
+ if (event.tokenUsage) {
646098
+ sessionMetrics?.recordTurn();
645622
646099
  const lastPromptTokens = event.tokenUsage.lastPromptTokens ?? event.tokenUsage.promptTokens;
645623
646100
  const lastCompletionTokens = event.tokenUsage.lastCompletionTokens ?? event.tokenUsage.completionTokens;
646101
+ if (lastPromptTokens > 0 || lastCompletionTokens > 0) {
646102
+ sessionMetrics?.recordTokenUsage(lastPromptTokens, lastCompletionTokens);
646103
+ }
646104
+ sessionMetrics?.recordContextEstimate(event.tokenUsage.estimatedContextTokens);
646105
+ if (lastCompletionTokens > 0 && lastStreamDurationMs > 0) {
646106
+ sessionMetrics?.recordGeneration(lastCompletionTokens, lastStreamDurationMs);
646107
+ lastStreamDurationMs = 0;
646108
+ }
645624
646109
  if (costTracker && (lastPromptTokens > 0 || lastCompletionTokens > 0)) {
645625
646110
  costTracker.trackTokens(lastPromptTokens, lastCompletionTokens);
645626
646111
  }
645627
- statusBar.updateMetrics({
645628
- ...event.tokenUsage,
645629
- estimatedCost: costTracker?.currentCost,
645630
- hasPricing: costTracker?.hasPricing
645631
- });
646112
+ if (statusBar) {
646113
+ statusBar.updateMetrics({
646114
+ ...event.tokenUsage,
646115
+ estimatedCost: costTracker?.currentCost,
646116
+ hasPricing: costTracker?.hasPricing
646117
+ });
646118
+ }
645632
646119
  if (config.verbose) {
645633
646120
  const tu = event.tokenUsage;
645634
646121
  const ctxPct = tu.estimatedContextTokens > 0 && statusBar ? ` (ctx: ~${tu.estimatedContextTokens.toLocaleString()} tokens)` : "";
@@ -645741,6 +646228,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
645741
646228
  total: result.totalTokens,
645742
646229
  estimated: result.estimatedTokens
645743
646230
  };
646231
+ costTracker?.endTask();
646232
+ sessionMetrics?.endTask();
645744
646233
  if (result.completed) {
645745
646234
  statusBar?.removeRecentContentMatchingText(result.summary);
645746
646235
  }
@@ -646224,7 +646713,9 @@ async function startInteractive(config, repoPath) {
646224
646713
  } catch {
646225
646714
  }
646226
646715
  initOmniusDirectory(repoRoot);
646227
- const savedSettings = resolveSettings(repoRoot);
646716
+ const globalSettings = loadGlobalSettings();
646717
+ const projectSettings = loadProjectSettings(repoRoot);
646718
+ const savedSettings = { ...globalSettings, ...projectSettings };
646228
646719
  try {
646229
646720
  if (savedSettings.omniusAccess && !process.env["OMNIUS_ACCESS"]) {
646230
646721
  process.env["OMNIUS_ACCESS"] = String(savedSettings.omniusAccess);
@@ -647385,7 +647876,7 @@ This is an independent background session started from /background.`
647385
647876
  );
647386
647877
  return [hits, line];
647387
647878
  }
647388
- const HISTORY_DIR = join143(homedir50(), ".omnius");
647879
+ const HISTORY_DIR = join143(homedir51(), ".omnius");
647389
647880
  const HISTORY_FILE = join143(HISTORY_DIR, "repl-history");
647390
647881
  const MAX_HISTORY_LINES = 500;
647391
647882
  let savedHistory = [];
@@ -648247,6 +648738,32 @@ Log: ${nexusLogPath}`)
648247
648738
  }
648248
648739
  };
648249
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
+ };
648250
648767
  const commandCtx = {
648251
648768
  get config() {
648252
648769
  return currentConfig;
@@ -648808,7 +649325,7 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
648808
649325
  return blessEngine?.isActive ?? false;
648809
649326
  },
648810
649327
  // Telegram bridge
648811
- async telegramStart(token, adminId) {
649328
+ async telegramStart(token, adminId, scope = "project") {
648812
649329
  if (telegramBridge?.isActive) {
648813
649330
  writeContent(
648814
649331
  () => renderWarning("Telegram bridge already active. Use /telegram stop before restarting.")
@@ -648848,6 +649365,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648848
649365
  currentConfig,
648849
649366
  repoRoot
648850
649367
  );
649368
+ activeTelegramSettingsScope = scope;
648851
649369
  if (resolvedContextWindowSize > 0) {
648852
649370
  telegramBridge.setContextWindowSize(resolvedContextWindowSize);
648853
649371
  }
@@ -648857,10 +649375,16 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648857
649375
  telegramBridge.setAdmin(adminId);
648858
649376
  }
648859
649377
  telegramBridge.setAdminAuthHandler((newAdminId, username) => {
648860
- savedSettings.telegramAdmin = newAdminId;
648861
- 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();
648862
649386
  writeContent(
648863
- () => renderInfo(`Telegram admin authenticated: @${username || "unknown"} (${newAdminId})`)
649387
+ () => renderInfo(`Telegram admin authenticated: @${username || "unknown"} (${newAdminId}) in ${activeTelegramSettingsScope ?? "project"} settings`)
648864
649388
  );
648865
649389
  });
648866
649390
  telegramBridge.setWriteContent(writeContent);
@@ -648917,6 +649441,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648917
649441
  });
648918
649442
  });
648919
649443
  }
649444
+ } else {
649445
+ emotionEngine.setAdminOutreach(() => {
649446
+ });
648920
649447
  }
648921
649448
  if (voiceEngine.enabled && voiceEngine.ready) {
648922
649449
  telegramBridge.setVoiceEngine(voiceEngine);
@@ -648957,9 +649484,16 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648957
649484
  throw err;
648958
649485
  }
648959
649486
  });
648960
- 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
+ }
648961
649495
  statusBar.setTelegramStatus(true, telegramBridge.stats.activeSubAgents);
648962
- telegramBridge.setMyCommands(buildTelegramBotCommands()).catch((err) => {
649496
+ telegramBridge.registerScopedMyCommands(adminId).catch((err) => {
648963
649497
  writeContent(
648964
649498
  () => renderWarning(`Telegram command registration failed: ${err instanceof Error ? err.message : String(err)}`)
648965
649499
  );
@@ -648975,41 +649509,56 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648975
649509
  telegramBridge.stop();
648976
649510
  writeContent(() => renderTelegramStop(stats));
648977
649511
  telegramBridge = null;
649512
+ activeTelegramSettingsScope = null;
648978
649513
  statusBar.setTelegramStatus(false, 0);
648979
649514
  }
648980
649515
  },
648981
649516
  isTelegramActive() {
648982
649517
  return telegramBridge?.isActive ?? false;
648983
649518
  },
648984
- getTelegramSettings() {
648985
- return {
648986
- key: savedSettings.telegramKey,
648987
- admin: savedSettings.telegramAdmin,
648988
- mode: savedSettings.telegramMode
648989
- };
649519
+ getTelegramSettings(scope = "project") {
649520
+ return telegramSettingsForScope(scope);
648990
649521
  },
648991
649522
  saveTelegramSettings(settings) {
649523
+ const targetScope = settings.local === false ? "global" : "project";
649524
+ const target = targetScope === "global" ? globalSettings : projectSettings;
648992
649525
  if (settings.key !== void 0) {
648993
- savedSettings.telegramKey = settings.key;
649526
+ target.telegramKey = settings.key;
648994
649527
  }
648995
649528
  if (settings.admin !== void 0) {
648996
- savedSettings.telegramAdmin = settings.admin;
649529
+ target.telegramAdmin = settings.admin === null ? "" : settings.admin;
648997
649530
  }
648998
649531
  if (settings.mode !== void 0) {
648999
- savedSettings.telegramMode = settings.mode;
649532
+ target.telegramMode = settings.mode;
649000
649533
  }
649001
649534
  const payload = {
649002
649535
  ...settings.key !== void 0 ? { telegramKey: settings.key } : {},
649003
- ...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {},
649536
+ ...settings.admin !== void 0 ? { telegramAdmin: settings.admin === null ? "" : settings.admin } : {},
649004
649537
  ...settings.mode !== void 0 ? { telegramMode: settings.mode } : {}
649005
649538
  };
649006
- const projectHasOmnius = existsSync128(join143(repoRoot, ".omnius"));
649007
- const useProject = settings.local === true || settings.local === void 0 && projectHasOmnius;
649008
- if (useProject) {
649539
+ if (targetScope === "project") {
649009
649540
  saveProjectSettings(repoRoot, payload);
649010
649541
  } else {
649011
649542
  saveGlobalSettings(payload);
649012
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 };
649013
649562
  },
649014
649563
  telegramSetInteractionMode(mode) {
649015
649564
  savedSettings.telegramMode = mode;
@@ -649023,7 +649572,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649023
649572
  () => renderTelegramStatus(
649024
649573
  active,
649025
649574
  botUser,
649026
- savedSettings.telegramAdmin,
649575
+ telegramSettingsForScope(activeTelegramSettingsScope ?? "project").admin,
649027
649576
  subAgents,
649028
649577
  savedSettings.telegramMode ?? "auto"
649029
649578
  )
@@ -649108,7 +649657,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649108
649657
  const registry4 = createOmniusPlatformRegistry({
649109
649658
  telegram: () => ({
649110
649659
  active: telegramBridge?.isActive ?? false,
649111
- configured: !!savedSettings.telegramKey,
649660
+ configured: !!telegramSettingsForScope("project").key,
649112
649661
  enabled: telegramBridge?.isActive ?? false,
649113
649662
  botUsername: telegramBridge?.botUsername,
649114
649663
  activeSessions: telegramBridge?.stats.activeSubAgents ?? 0
@@ -649952,6 +650501,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649952
650501
  statusBar.setContextWindowSize(size);
649953
650502
  telegramBridge?.setContextWindowSize(size);
649954
650503
  },
650504
+ getContextWindowSize: () => resolvedContextWindowSize,
649955
650505
  setCapabilities: (caps) => {
649956
650506
  resolvedCaps = caps;
649957
650507
  statusBar.setCapabilities(caps);
@@ -650070,7 +650620,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650070
650620
  } catch {
650071
650621
  }
650072
650622
  try {
650073
- const voiceDir3 = join143(homedir50(), ".omnius", "voice");
650623
+ const voiceDir3 = join143(homedir51(), ".omnius", "voice");
650074
650624
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
650075
650625
  for (const pf of voicePidFiles) {
650076
650626
  const pidPath = join143(voiceDir3, pf);
@@ -650206,14 +650756,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650206
650756
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
650207
650757
  if (isPersonaPlexRunning2()) {
650208
650758
  const ppPidFile = join143(
650209
- homedir50(),
650759
+ homedir51(),
650210
650760
  ".omnius",
650211
650761
  "voice",
650212
650762
  "personaplex",
650213
650763
  "daemon.pid"
650214
650764
  );
650215
650765
  const ppPortFile = join143(
650216
- homedir50(),
650766
+ homedir51(),
650217
650767
  ".omnius",
650218
650768
  "voice",
650219
650769
  "personaplex",
@@ -650383,12 +650933,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
650383
650933
  }, 100);
650384
650934
  }
650385
650935
  }
650386
- if (!isResumed && savedSettings.telegramKey) {
650936
+ const autoTelegramSettings = telegramSettingsForScope("project");
650937
+ if (!isResumed && autoTelegramSettings.key) {
650387
650938
  setTimeout(async () => {
650388
650939
  try {
650389
650940
  await commandCtx.telegramStart(
650390
- savedSettings.telegramKey,
650391
- savedSettings.telegramAdmin
650941
+ autoTelegramSettings.key,
650942
+ autoTelegramSettings.admin,
650943
+ "project"
650392
650944
  );
650393
650945
  } catch (err) {
650394
650946
  writeContent(
@@ -650649,7 +651201,8 @@ Execute this skill now. Follow the behavioral guidance above.`;
650649
651201
  buildSlashCommandHandler(),
650650
651202
  thinkingEnabled,
650651
651203
  handleAskUser,
650652
- selfModifyEnabled
651204
+ selfModifyEnabled,
651205
+ sessionMetrics
650653
651206
  );
650654
651207
  activeTask = task;
650655
651208
  setTerminalTitle(input.slice(0, 60), version4);
@@ -651077,7 +651630,8 @@ NEW TASK: ${fullInput}`;
651077
651630
  buildSlashCommandHandler(),
651078
651631
  thinkingEnabled,
651079
651632
  handleAskUser,
651080
- selfModifyEnabled
651633
+ selfModifyEnabled,
651634
+ sessionMetrics
651081
651635
  );
651082
651636
  activeTask = task;
651083
651637
  _recallText = null;
@@ -652381,7 +652935,7 @@ __export(config_exports2, {
652381
652935
  configCommand: () => configCommand
652382
652936
  });
652383
652937
  import { join as join145, resolve as resolve51 } from "node:path";
652384
- import { homedir as homedir51 } from "node:os";
652938
+ import { homedir as homedir52 } from "node:os";
652385
652939
  import { cwd as cwd3 } from "node:process";
652386
652940
  function redactIfSensitive(key, value2) {
652387
652941
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -652463,7 +653017,7 @@ function handleShow(opts, config) {
652463
653017
  }
652464
653018
  }
652465
653019
  printSection("Config File");
652466
- printInfo(`~/.omnius/config.json (${join145(homedir51(), ".omnius", "config.json")})`);
653020
+ printInfo(`~/.omnius/config.json (${join145(homedir52(), ".omnius", "config.json")})`);
652467
653021
  printSection("Priority Chain");
652468
653022
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
652469
653023
  printInfo(" 2. Project .omnius/settings.json (--local)");
@@ -653340,8 +653894,8 @@ function crashLog(label, err) {
653340
653894
  try {
653341
653895
  const { appendFileSync: appendFileSync12, mkdirSync: mkdirSync81 } = __require("node:fs");
653342
653896
  const { join: join148 } = __require("node:path");
653343
- const { homedir: homedir52 } = __require("node:os");
653344
- const logDir = join148(homedir52(), ".omnius");
653897
+ const { homedir: homedir53 } = __require("node:os");
653898
+ const logDir = join148(homedir53(), ".omnius");
653345
653899
  mkdirSync81(logDir, { recursive: true });
653346
653900
  appendFileSync12(join148(logDir, "crash.log"), logLine);
653347
653901
  } catch {