omnius 1.0.65 → 1.0.67

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);
@@ -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()))}`
@@ -609476,6 +609567,147 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
609476
609567
  return null;
609477
609568
  }
609478
609569
  }
609570
+ function parseTelegramTimeRangeQuery(query, now = /* @__PURE__ */ new Date()) {
609571
+ const original = String(query || "");
609572
+ const lower = original.toLowerCase();
609573
+ const nowMs = now.getTime();
609574
+ const dayMs = 864e5;
609575
+ const hourMs = 36e5;
609576
+ const minMs = 6e4;
609577
+ const weekMs = 7 * dayMs;
609578
+ const startOfDay = (d2) => {
609579
+ const x = new Date(d2);
609580
+ x.setHours(0, 0, 0, 0);
609581
+ return x;
609582
+ };
609583
+ const endOfDay = (d2) => {
609584
+ const x = new Date(d2);
609585
+ x.setHours(23, 59, 59, 999);
609586
+ return x;
609587
+ };
609588
+ let since;
609589
+ let until;
609590
+ let bucket;
609591
+ let label = "";
609592
+ let residual = original;
609593
+ const strip = (re) => {
609594
+ residual = residual.replace(re, " ").replace(/\s+/g, " ").trim();
609595
+ };
609596
+ if (/\b(oldest|earliest|first)\b/.test(lower)) {
609597
+ bucket = "earliest";
609598
+ label = label || "earliest";
609599
+ strip(/\b(?:the\s+)?(?:oldest|earliest|first)(?:\s+(?:memory|entry|turn|message|memories))?\b/gi);
609600
+ } else if (/\b(newest|latest|most\s+recent|last\s+memory|last\s+turn|last\s+message)\b/.test(lower)) {
609601
+ bucket = "latest";
609602
+ label = label || "latest";
609603
+ strip(/\b(?:the\s+)?(?:newest|latest|most\s+recent|last\s+(?:memory|turn|message))\b/gi);
609604
+ }
609605
+ if (/\byesterday\b/.test(lower)) {
609606
+ const y = new Date(now);
609607
+ y.setDate(y.getDate() - 1);
609608
+ since = startOfDay(y).getTime();
609609
+ until = endOfDay(y).getTime();
609610
+ label = label ? `${label}, yesterday` : "yesterday";
609611
+ strip(/\byesterday\b/gi);
609612
+ } else if (/\btoday\b/.test(lower)) {
609613
+ since = startOfDay(now).getTime();
609614
+ until = endOfDay(now).getTime();
609615
+ label = label ? `${label}, today` : "today";
609616
+ strip(/\btoday\b/gi);
609617
+ }
609618
+ const partMatch = lower.match(/\bthis\s+(morning|afternoon|evening|night|tonight)\b/);
609619
+ if (partMatch) {
609620
+ const today0 = startOfDay(now).getTime();
609621
+ if (partMatch[1] === "morning") {
609622
+ since = today0;
609623
+ until = today0 + 12 * hourMs;
609624
+ } else if (partMatch[1] === "afternoon") {
609625
+ since = today0 + 12 * hourMs;
609626
+ until = today0 + 18 * hourMs;
609627
+ } else if (partMatch[1] === "evening") {
609628
+ since = today0 + 17 * hourMs;
609629
+ until = today0 + 22 * hourMs;
609630
+ } else {
609631
+ since = today0 + 20 * hourMs;
609632
+ until = endOfDay(now).getTime();
609633
+ }
609634
+ label = label ? `${label}, this ${partMatch[1]}` : `this ${partMatch[1]}`;
609635
+ strip(/\bthis\s+(?:morning|afternoon|evening|night|tonight)\b/gi);
609636
+ }
609637
+ const nMatch = lower.match(/\b(?:last|past|in\s+the\s+last|in\s+the\s+past)\s+(\d+)?\s*(minute|min|hour|hr|day|week|wk)s?\b/);
609638
+ if (nMatch) {
609639
+ const n2 = nMatch[1] ? parseInt(nMatch[1], 10) : 1;
609640
+ const u = nMatch[2];
609641
+ const stepMs = u.startsWith("min") ? minMs : u.startsWith("h") ? hourMs : u.startsWith("d") ? dayMs : weekMs;
609642
+ since = nowMs - n2 * stepMs;
609643
+ until = nowMs;
609644
+ label = label ? `${label}, last ${n2} ${u}${n2 === 1 ? "" : "s"}` : `last ${n2} ${u}${n2 === 1 ? "" : "s"}`;
609645
+ strip(/\b(?:last|past|in\s+the\s+last|in\s+the\s+past)\s+\d*\s*(?:minute|min|hour|hr|day|week|wk)s?\b/gi);
609646
+ }
609647
+ const agoMatch = lower.match(/\b(\d+)\s+(minute|min|hour|hr|day|week|wk)s?\s+ago\b/);
609648
+ if (agoMatch) {
609649
+ const n2 = parseInt(agoMatch[1], 10);
609650
+ const u = agoMatch[2];
609651
+ const stepMs = u.startsWith("min") ? minMs : u.startsWith("h") ? hourMs : u.startsWith("d") ? dayMs : weekMs;
609652
+ const center = nowMs - n2 * stepMs;
609653
+ if (u.startsWith("d")) {
609654
+ const at = new Date(center);
609655
+ since = startOfDay(at).getTime();
609656
+ until = endOfDay(at).getTime();
609657
+ } else {
609658
+ since = center - stepMs;
609659
+ until = center + stepMs;
609660
+ }
609661
+ label = label ? `${label}, ${n2} ${u}${n2 === 1 ? "" : "s"} ago` : `${n2} ${u}${n2 === 1 ? "" : "s"} ago`;
609662
+ strip(/\b\d+\s+(?:minute|min|hour|hr|day|week|wk)s?\s+ago\b/gi);
609663
+ }
609664
+ if (/\bearlier\s+today\b/.test(lower)) {
609665
+ const today0 = startOfDay(now).getTime();
609666
+ since = today0;
609667
+ until = nowMs - 30 * minMs;
609668
+ label = label ? `${label}, earlier today` : "earlier today";
609669
+ strip(/\bearlier\s+today\b/gi);
609670
+ } else if (/\bearlier\b/.test(lower) && since === void 0 && bucket === void 0) {
609671
+ since = nowMs - dayMs;
609672
+ until = nowMs - 30 * minMs;
609673
+ label = label ? `${label}, earlier` : "earlier";
609674
+ strip(/\bearlier\b/gi);
609675
+ }
609676
+ const sinceIsoMatch = original.match(/\bsince\s+(\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}(?::\d{2})?)?)\b/i);
609677
+ if (sinceIsoMatch) {
609678
+ const t2 = Date.parse(sinceIsoMatch[1]);
609679
+ if (Number.isFinite(t2)) {
609680
+ since = t2;
609681
+ until = nowMs;
609682
+ label = label ? `${label}, since ${sinceIsoMatch[1]}` : `since ${sinceIsoMatch[1]}`;
609683
+ strip(new RegExp(`\\bsince\\s+${sinceIsoMatch[1].replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "gi"));
609684
+ }
609685
+ }
609686
+ const onIsoMatch = original.match(/\bon\s+(\d{4}-\d{2}-\d{2})\b/i);
609687
+ if (onIsoMatch) {
609688
+ const t2 = Date.parse(onIsoMatch[1]);
609689
+ if (Number.isFinite(t2)) {
609690
+ const d2 = new Date(t2);
609691
+ since = startOfDay(d2).getTime();
609692
+ until = endOfDay(d2).getTime();
609693
+ label = label ? `${label}, on ${onIsoMatch[1]}` : `on ${onIsoMatch[1]}`;
609694
+ strip(new RegExp(`\\bon\\s+${onIsoMatch[1]}\\b`, "gi"));
609695
+ }
609696
+ }
609697
+ const betweenMatch = original.match(/\bbetween\s+(\d{4}-\d{2}-\d{2})\s+and\s+(\d{4}-\d{2}-\d{2})\b/i);
609698
+ if (betweenMatch) {
609699
+ const a2 = Date.parse(betweenMatch[1]);
609700
+ const b = Date.parse(betweenMatch[2]);
609701
+ if (Number.isFinite(a2) && Number.isFinite(b)) {
609702
+ since = Math.min(a2, b);
609703
+ until = Math.max(a2, b) + dayMs;
609704
+ label = label ? `${label}, between ${betweenMatch[1]} and ${betweenMatch[2]}` : `between ${betweenMatch[1]} and ${betweenMatch[2]}`;
609705
+ strip(/\bbetween\s+\d{4}-\d{2}-\d{2}\s+and\s+\d{4}-\d{2}-\d{2}\b/gi);
609706
+ }
609707
+ }
609708
+ if (since === void 0 && until === void 0 && bucket === void 0) return null;
609709
+ return { since, until, bucket, label: label || "time-range", residualQuery: residual };
609710
+ }
609479
609711
  function parseTelegramReflectionFollowupDecision(text) {
609480
609712
  const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
609481
609713
  const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
@@ -609955,8 +610187,7 @@ function telegramSyntheticHelpSignatures() {
609955
610187
  function telegramHelpCommandAllowed(cmd, scope) {
609956
610188
  if (cmd.name === "dream") return false;
609957
610189
  if (scope === "admin") return cmd.implementationStatus === "implemented";
609958
- if (TELEGRAM_PUBLIC_HELP_COMMANDS.has(cmd.name)) return true;
609959
- return cmd.surfaces.agentTool && !cmd.safety.secretBearing && !cmd.safety.destructive && !cmd.safety.profileGated;
610190
+ return TELEGRAM_PUBLIC_HELP_COMMANDS.has(cmd.name) || TELEGRAM_PUBLIC_BOT_COMMAND_NAMES.has(cmd.name);
609960
610191
  }
609961
610192
  function buildTelegramHelpHTML(scope, maxPublicCommands = 24) {
609962
610193
  const commands = listCommandRegistry({ includePlanned: false }).filter((cmd) => telegramHelpCommandAllowed(cmd, scope));
@@ -610653,7 +610884,7 @@ function renderTelegramSubAgentError(username, error) {
610653
610884
  process.stdout.write(` ${c3.dim("│")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
610654
610885
  `);
610655
610886
  }
610656
- 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;
610887
+ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
610657
610888
  var init_telegram_bridge = __esm({
610658
610889
  "packages/cli/src/tui/telegram-bridge.ts"() {
610659
610890
  "use strict";
@@ -610985,6 +611216,9 @@ External acquisition contract:
610985
611216
  TELEGRAM_PUBLIC_HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "start", "auth", "call"]);
610986
611217
  TELEGRAM_REMINDER_SLASH_COMMANDS = /* @__PURE__ */ new Set(["remind", "reminder", "reminders"]);
610987
611218
  TELEGRAM_REFLECTION_SLASH_COMMANDS = /* @__PURE__ */ new Set(["reflect", "reflection", "daydream", "dream"]);
611219
+ TELEGRAM_PUBLIC_BOT_COMMAND_NAMES = new Set(
611220
+ buildTelegramBotCommands({ scope: "public" }).map((cmd) => cmd.command)
611221
+ );
610988
611222
  TELEGRAM_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".tif", ".svg"]);
610989
611223
  MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
610990
611224
  TELEGRAM_CHANNEL_DMN_SWEEP_MS = 2 * 60 * 1e3;
@@ -611012,6 +611246,7 @@ External acquisition contract:
611012
611246
  this.telegramConversationDir = resolve43(repoRoot || ".", ".omnius", "telegram-conversations");
611013
611247
  this.telegramSqlitePath = resolve43(repoRoot || ".", ".omnius", "telegram.sqlite");
611014
611248
  this.telegramToolButtonDir = resolve43(repoRoot || ".", ".omnius", "telegram-tool-buttons");
611249
+ this.hydrateTelegramCommandMap(buildTelegramBotCommands({ scope: "admin" }));
611015
611250
  }
611016
611251
  botToken;
611017
611252
  onMessage;
@@ -611240,6 +611475,9 @@ External acquisition contract:
611240
611475
  const botless = first2.startsWith("/") ? first2.slice(1).split("@")[0]?.toLowerCase() : "";
611241
611476
  return !!botless && this.telegramCommandMap.has(botless);
611242
611477
  }
611478
+ isPublicTelegramSlashName(name10) {
611479
+ return TELEGRAM_PUBLIC_BOT_COMMAND_NAMES.has(name10);
611480
+ }
611243
611481
  async handleTelegramReminderSlash(msg, commandText, context2) {
611244
611482
  if (!this.repoRoot) {
611245
611483
  await this.replyToTelegramMessage(msg, "Reminder storage is not available yet.");
@@ -613332,11 +613570,116 @@ ${lines.join("\n")}`);
613332
613570
  lines.push(` - ${topic}`);
613333
613571
  }
613334
613572
  }
613573
+ const sourceHistory = this.chatHistory.get(sessionKey) ?? [];
613574
+ const sortedByTs = [...sourceHistory].filter((e2) => typeof e2.ts === "number" && Number.isFinite(e2.ts)).sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0));
613575
+ const fmtHistoryAnchor = (entry) => {
613576
+ const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
613577
+ const speaker = telegramHistorySpeaker(entry);
613578
+ const mode = entry.mode ? `/${entry.mode}` : "";
613579
+ const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
613580
+ return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613581
+ };
613582
+ if (sortedByTs.length > 0) {
613583
+ lines.push("");
613584
+ lines.push("Chronological anchors — Telegram conversation history (ground truth for 'oldest/newest memory' questions):");
613585
+ lines.push(` EARLIEST turn: ${fmtHistoryAnchor(sortedByTs[0])}`);
613586
+ if (sortedByTs.length > 1) {
613587
+ lines.push(` LATEST turn: ${fmtHistoryAnchor(sortedByTs[sortedByTs.length - 1])}`);
613588
+ }
613589
+ if (sortedByTs.length > 2) {
613590
+ lines.push(` 2nd earliest: ${fmtHistoryAnchor(sortedByTs[1])}`);
613591
+ }
613592
+ if (sortedByTs.length > 3) {
613593
+ lines.push(` 3rd earliest: ${fmtHistoryAnchor(sortedByTs[2])}`);
613594
+ }
613595
+ }
613596
+ if (this.repoRoot) {
613597
+ try {
613598
+ const paths = omniusMemoryDbPaths(this.repoRoot);
613599
+ if (existsSync113(paths.episodes)) {
613600
+ const graph = new TemporalGraph(paths.knowledge);
613601
+ const store2 = new EpisodeStore(paths.episodes, graph);
613602
+ try {
613603
+ const dbAny = store2.db;
613604
+ if (dbAny && typeof dbAny.prepare === "function") {
613605
+ const earliest = dbAny.prepare(
613606
+ "SELECT timestamp, modality, tool_name, content, gist FROM episodes WHERE session_id = ? ORDER BY timestamp ASC LIMIT 1"
613607
+ ).get(sessionKey);
613608
+ const latest = dbAny.prepare(
613609
+ "SELECT timestamp, modality, tool_name, content, gist FROM episodes WHERE session_id = ? ORDER BY timestamp DESC LIMIT 1"
613610
+ ).get(sessionKey);
613611
+ if (earliest || latest) {
613612
+ lines.push("");
613613
+ lines.push("Chronological anchors — episodes.db (durable, may reach further back than rolling history):");
613614
+ const fmtEp = (row) => {
613615
+ const when = row.timestamp ? new Date(row.timestamp).toISOString() : "(unknown ts)";
613616
+ const tag = `[${row.modality || "?"}${row.tool_name ? ":" + row.tool_name : ""}]`;
613617
+ const text = (row.gist || row.content || "").split("\n").filter((ln) => !/^(Telegram|session:|chat:|message_id:|thread_id:|speaker:|mode:)/i.test(ln.trim())).join(" ").replace(/\s+/g, " ").trim();
613618
+ return `${when} ${tag} ${telegramContextJsonString(text, 320)}`;
613619
+ };
613620
+ if (earliest) lines.push(` EARLIEST episode: ${fmtEp(earliest)}`);
613621
+ if (latest && (!earliest || earliest.timestamp !== latest.timestamp)) lines.push(` LATEST episode: ${fmtEp(latest)}`);
613622
+ }
613623
+ }
613624
+ } finally {
613625
+ try {
613626
+ store2.close();
613627
+ } catch {
613628
+ }
613629
+ try {
613630
+ graph.close();
613631
+ } catch {
613632
+ }
613633
+ }
613634
+ }
613635
+ } catch {
613636
+ }
613637
+ }
613638
+ if (this.repoRoot && chatId !== void 0 && topicFiles.length > 0) {
613639
+ try {
613640
+ const memDir = resolve43(this.repoRoot, ".omnius", "memory");
613641
+ const prefix = this.telegramScopedMemoryPrefix(chatId);
613642
+ let earliestEntry = null;
613643
+ let latestEntry = null;
613644
+ for (const topic of topicFiles) {
613645
+ const file = join127(memDir, `${prefix}${topic}.json`);
613646
+ if (!existsSync113(file)) continue;
613647
+ let parsed;
613648
+ try {
613649
+ parsed = JSON.parse(readFileSync92(file, "utf8"));
613650
+ } catch {
613651
+ continue;
613652
+ }
613653
+ for (const [key, entry] of Object.entries(parsed)) {
613654
+ if (!entry || typeof entry !== "object") continue;
613655
+ const rawTs = entry.timestamp;
613656
+ const ts = typeof rawTs === "string" ? Date.parse(rawTs) : typeof rawTs === "number" ? rawTs : NaN;
613657
+ if (!Number.isFinite(ts)) continue;
613658
+ const value2 = String(entry.value ?? "");
613659
+ if (!earliestEntry || ts < earliestEntry.ts) earliestEntry = { topic, key, value: value2, ts };
613660
+ if (!latestEntry || ts > latestEntry.ts) latestEntry = { topic, key, value: value2, ts };
613661
+ }
613662
+ }
613663
+ if (earliestEntry || latestEntry) {
613664
+ lines.push("");
613665
+ lines.push("Chronological anchors — memory_write entries (most-trusted, agent-asserted):");
613666
+ const fmtMem = (e2) => {
613667
+ const when = new Date(e2.ts).toISOString();
613668
+ return `${when} topic="${e2.topic}" key="${e2.key}" → ${telegramContextJsonString(e2.value, 320)}`;
613669
+ };
613670
+ if (earliestEntry) lines.push(` EARLIEST memory_write: ${fmtMem(earliestEntry)}`);
613671
+ if (latestEntry && (!earliestEntry || earliestEntry.ts !== latestEntry.ts)) lines.push(` LATEST memory_write: ${fmtMem(latestEntry)}`);
613672
+ }
613673
+ } catch {
613674
+ }
613675
+ }
613335
613676
  lines.push("");
613336
613677
  lines.push("RULES:");
613337
613678
  lines.push(" 1. NEVER tell the user 'memory is empty' or 'nothing has been stored' for this chat without first calling memory_search and memory_read on a relevant topic from the list above.");
613338
613679
  lines.push(" 2. If the structured sections (cards/facts/sqlite/episodes) above did not surface what the user asked about, that is a SCORING miss, not absence. Call memory_search with broader tokens or pick a topic above with memory_read.");
613339
613680
  lines.push(" 3. The rolling-history block is base context; the cards/facts/episodes are retrieval-augmented. Treat them as the same memory, surfaced different ways.");
613681
+ lines.push(" 4. For 'what is your oldest/earliest memory' or 'most recent memory' questions: answer DIRECTLY from the 'Chronological anchors' lines above. Quote the timestamp and content. Do NOT call tools first and do NOT report 'empty'.");
613682
+ lines.push(" 5. memory_search accepts NATURAL-LANGUAGE TIME phrases inside the `query` argument or explicit `since`/`until`/`bucket` args. Examples: query='what did manitcor say yesterday', query='last 3 hours', query='earlier today', query='2 days ago', query='since 2026-05-15', query='between 2026-05-15 and 2026-05-16', query='oldest memory about github', query='most recent flux discussion'. Use these for chronological/'how far back' style queries instead of guessing — the tool parses the phrase, filters by time, and returns the right window.");
613340
613683
  return lines.join("\n");
613341
613684
  }
613342
613685
  buildTelegramConversationContextStream(sessionKey, msg, maxRecent = TELEGRAM_CONTEXT_RECENT_DEFAULT) {
@@ -614305,8 +614648,7 @@ Join: ${newUrl}`);
614305
614648
  return;
614306
614649
  }
614307
614650
  }
614308
- const publicCreativeSlash = telegramSlash === "image";
614309
- if (!isAdmin && msg.text.trim().startsWith("/") && this.isKnownTelegramSlash(normalizedCommandText) && !publicCreativeSlash) {
614651
+ if (!isAdmin && msg.text.trim().startsWith("/") && this.isKnownTelegramSlash(normalizedCommandText) && !this.isPublicTelegramSlashName(telegramSlash)) {
614310
614652
  await this.replyToTelegramMessage(
614311
614653
  msg,
614312
614654
  "That command requires Telegram admin authentication. Run /telegram auth in the TUI, then send /auth <code> here."
@@ -615180,7 +615522,7 @@ ${result.llmContent ?? result.output}` };
615180
615522
  if (tool.name === "memory_search") {
615181
615523
  return {
615182
615524
  ...tool,
615183
- description: "Search only this Telegram chat's isolated durable memory: raw SQLite message mirror, episode/knowledge graph recall, associative user facts, and memory cards. Supports scope=group/current_chat or scope=user with user_id/username, but never crosses into admin/private/global memory.",
615525
+ description: "Search only this Telegram chat's isolated durable memory: raw SQLite message mirror, episode/knowledge graph recall, associative user facts, and memory cards. Supports scope=group/current_chat or scope=user with user_id/username; also supports natural-language time phrases inside the query string ('yesterday', 'last 3 hours', '2 days ago', 'earlier today', 'since 2026-05-15', 'between A and B', 'oldest', 'most recent') OR explicit since/until/bucket arguments. Never crosses into admin/private/global memory.",
615184
615526
  parameters: (() => {
615185
615527
  const base3 = tool.parameters ?? {};
615186
615528
  const props = base3["properties"] && typeof base3["properties"] === "object" && !Array.isArray(base3["properties"]) ? base3["properties"] : {};
@@ -615191,15 +615533,57 @@ ${result.llmContent ?? result.output}` };
615191
615533
  scope: { type: "string", enum: ["group", "current_chat", "user"], description: "Search scope inside the current Telegram chat. scope=user defaults to the current sender when no user_id/username is supplied." },
615192
615534
  user_id: { type: "number", description: "Optional Telegram user id to search within the current group only." },
615193
615535
  username: { type: "string", description: "Optional Telegram username to search within the current group only." },
615194
- group_id: { type: "string", description: "Optional Telegram group/chat id. Must match the current chat id; other groups are not accessible here." }
615536
+ group_id: { type: "string", description: "Optional Telegram group/chat id. Must match the current chat id; other groups are not accessible here." },
615537
+ since: { type: "string", description: "Optional lower-bound timestamp (ISO 8601 or epoch ms). Combine with `until` for an explicit window. If omitted, natural-language phrases inside `query` are auto-parsed (e.g. 'yesterday', '3 days ago')." },
615538
+ until: { type: "string", description: "Optional upper-bound timestamp (ISO 8601 or epoch ms)." },
615539
+ bucket: { type: "string", enum: ["earliest", "latest"], description: "Chronological ordering hint when there is no specific window. 'earliest' returns the oldest entries on record; 'latest' returns the newest." }
615195
615540
  }
615196
615541
  };
615197
615542
  })(),
615198
615543
  execute: async (args) => {
615199
- const query = String(args["query"] || "").trim();
615544
+ const rawQuery = String(args["query"] || "").trim();
615200
615545
  const maxResults = typeof args["max_results"] === "number" && Number.isFinite(args["max_results"]) ? Math.max(1, Math.min(20, Math.floor(args["max_results"]))) : 8;
615201
- if (!query) return { success: true, output: "Search query is required." };
615546
+ if (!rawQuery) return { success: true, output: "Search query is required." };
615202
615547
  this.ensureTelegramConversationLoaded(msgSessionKey);
615548
+ const parseTimeArg = (v) => {
615549
+ if (v === void 0 || v === null || v === "") return void 0;
615550
+ if (typeof v === "number" && Number.isFinite(v)) return Math.trunc(v);
615551
+ if (typeof v === "string") {
615552
+ if (/^\d+$/.test(v.trim())) return Number(v.trim());
615553
+ const t2 = Date.parse(v);
615554
+ if (Number.isFinite(t2)) return t2;
615555
+ }
615556
+ return void 0;
615557
+ };
615558
+ let since = parseTimeArg(args["since"]);
615559
+ let until = parseTimeArg(args["until"]);
615560
+ let bucket = args["bucket"] === "earliest" || args["bucket"] === "latest" ? args["bucket"] : void 0;
615561
+ let timeLabel = "";
615562
+ let effectiveQuery = rawQuery;
615563
+ if (since === void 0 && until === void 0 && bucket === void 0) {
615564
+ const parsed = parseTelegramTimeRangeQuery(rawQuery);
615565
+ if (parsed) {
615566
+ since = parsed.since;
615567
+ until = parsed.until;
615568
+ bucket = parsed.bucket;
615569
+ timeLabel = parsed.label;
615570
+ effectiveQuery = parsed.residualQuery || rawQuery;
615571
+ }
615572
+ } else if (since !== void 0 || until !== void 0 || bucket !== void 0) {
615573
+ timeLabel = [
615574
+ since !== void 0 ? `since=${new Date(since).toISOString()}` : "",
615575
+ until !== void 0 ? `until=${new Date(until).toISOString()}` : "",
615576
+ bucket ? `bucket=${bucket}` : ""
615577
+ ].filter(Boolean).join(", ");
615578
+ }
615579
+ const inWindow = (ts) => {
615580
+ if (since === void 0 && until === void 0) return true;
615581
+ if (ts == null || !Number.isFinite(ts)) return false;
615582
+ if (since !== void 0 && ts < since) return false;
615583
+ if (until !== void 0 && ts > until) return false;
615584
+ return true;
615585
+ };
615586
+ const query = effectiveQuery.trim();
615203
615587
  const currentGroupId = chatId === void 0 ? "" : String(chatId);
615204
615588
  const requestedGroupId = String(args["group_id"] ?? args["chat_id"] ?? "").trim();
615205
615589
  if (requestedGroupId && currentGroupId && requestedGroupId !== currentGroupId) {
@@ -615228,13 +615612,17 @@ ${result.llmContent ?? result.output}` };
615228
615612
  return false;
615229
615613
  });
615230
615614
  const queryTokens = telegramMemoryTokens(query);
615231
- const cardResults = cards.map((card) => ({
615615
+ const cardResults = cards.filter((card) => inWindow(card.updatedAt ?? card.createdAt)).map((card) => ({
615232
615616
  card,
615233
615617
  score: telegramMemorySimilarity(
615234
615618
  queryTokens,
615235
615619
  telegramMemoryTokens([card.title, card.tags.join(" "), card.speakers.join(" "), card.notes.join(" ")].join(" "))
615236
615620
  )
615237
- })).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.card.updatedAt - a2.card.updatedAt).slice(0, maxResults);
615621
+ })).filter((item) => item.score > 0 || since !== void 0 || until !== void 0 || bucket !== void 0).sort((a2, b) => {
615622
+ if (bucket === "earliest") return (a2.card.updatedAt ?? 0) - (b.card.updatedAt ?? 0);
615623
+ if (bucket === "latest") return (b.card.updatedAt ?? 0) - (a2.card.updatedAt ?? 0);
615624
+ return b.score - a2.score || b.card.updatedAt - a2.card.updatedAt;
615625
+ }).slice(0, maxResults);
615238
615626
  const cardLines = cardResults.map(({ card, score }) => {
615239
615627
  const notes2 = card.notes.slice(-4).map((note) => ` - ${truncateTelegramContextLine(note, 220)}`).join("\n");
615240
615628
  const tags = card.tags.length ? ` tags:${card.tags.slice(0, 8).join(",")}` : "";
@@ -615247,13 +615635,17 @@ ${notes2}`;
615247
615635
  if (!wantsUserScope) return true;
615248
615636
  if (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId)) return true;
615249
615637
  return !!effectiveUsername && fact.usernames.includes(effectiveUsername);
615250
- }).map((fact) => ({
615638
+ }).filter((fact) => inWindow(fact.updatedAt ?? fact.createdAt)).map((fact) => ({
615251
615639
  fact,
615252
615640
  score: telegramMemorySimilarity(
615253
615641
  queryTokens,
615254
615642
  telegramMemoryTokens([fact.text, fact.tags.join(" "), fact.speakers.join(" ")].join(" "))
615255
615643
  ) + (effectiveUserId !== void 0 && fact.userIds.includes(effectiveUserId) ? 0.3 : 0) + (effectiveUsername && fact.usernames.includes(effectiveUsername) ? 0.3 : 0)
615256
- })).filter((item) => item.score > 0).sort((a2, b) => b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt).slice(0, maxResults) : [];
615644
+ })).filter((item) => item.score > 0 || since !== void 0 || until !== void 0 || bucket !== void 0).sort((a2, b) => {
615645
+ if (bucket === "earliest") return (a2.fact.updatedAt ?? 0) - (b.fact.updatedAt ?? 0);
615646
+ if (bucket === "latest") return (b.fact.updatedAt ?? 0) - (a2.fact.updatedAt ?? 0);
615647
+ return b.score - a2.score || b.fact.updatedAt - a2.fact.updatedAt;
615648
+ }).slice(0, maxResults) : [];
615257
615649
  const rawRows = this.searchTelegramSqliteMirrorRows(msgSessionKey, query, {
615258
615650
  limit: maxResults,
615259
615651
  userId: effectiveUserId,
@@ -615263,8 +615655,9 @@ ${notes2}`;
615263
615655
  const historyForScan = this.chatHistory.get(msgSessionKey) ?? [];
615264
615656
  const lowerQuery = query.toLowerCase().trim();
615265
615657
  const queryWords = lowerQuery.split(/\s+/).filter((w) => w.length >= 3);
615658
+ const hasTimeConstraint = since !== void 0 || until !== void 0 || bucket !== void 0;
615266
615659
  const rawHistoryMatches = [];
615267
- if (historyForScan.length > 0 && lowerQuery.length > 0) {
615660
+ if (historyForScan.length > 0) {
615268
615661
  for (const entry of historyForScan) {
615269
615662
  if (wantsUserScope) {
615270
615663
  const eUser = entry.fromUserId;
@@ -615272,15 +615665,24 @@ ${notes2}`;
615272
615665
  const matchesUser = effectiveUserId !== void 0 && eUser === effectiveUserId || effectiveUsername.length > 0 && eName === effectiveUsername;
615273
615666
  if (!matchesUser) continue;
615274
615667
  }
615668
+ if (!inWindow(entry.ts ?? null)) continue;
615275
615669
  const hay = String(entry.text || "").toLowerCase();
615276
615670
  if (!hay) continue;
615277
- const hasExact = hay.includes(lowerQuery);
615278
- const tokenHits = queryWords.filter((w) => hay.includes(w)).length;
615279
- if (hasExact || queryWords.length > 0 && tokenHits >= Math.min(2, queryWords.length)) {
615671
+ let lexMatch = false;
615672
+ if (lowerQuery.length > 0) {
615673
+ const hasExact = hay.includes(lowerQuery);
615674
+ const tokenHits = queryWords.filter((w) => hay.includes(w)).length;
615675
+ lexMatch = hasExact || queryWords.length > 0 && tokenHits >= Math.min(2, queryWords.length);
615676
+ }
615677
+ if (lexMatch || hasTimeConstraint) {
615280
615678
  rawHistoryMatches.push(entry);
615281
615679
  }
615282
615680
  }
615283
- rawHistoryMatches.sort((a2, b) => (b.ts ?? 0) - (a2.ts ?? 0));
615681
+ if (bucket === "earliest") {
615682
+ rawHistoryMatches.sort((a2, b) => (a2.ts ?? 0) - (b.ts ?? 0));
615683
+ } else {
615684
+ rawHistoryMatches.sort((a2, b) => (b.ts ?? 0) - (a2.ts ?? 0));
615685
+ }
615284
615686
  }
615285
615687
  const rawHistorySliced = rawHistoryMatches.slice(0, maxResults);
615286
615688
  const scopeLabel = wantsUserScope ? `user ${effectiveUsername ? `@${effectiveUsername}` : `id ${effectiveUserId}`}` : `chat ${currentGroupId || msgSessionKey}`;
@@ -615327,8 +615729,9 @@ ${lines.join("\n")}`
615327
615729
  output: `No structured matches for "${query}" in ${scopeLabel}, but the scope IS populated: ${scannedCount} memory cards, ${associativeCount} associative facts, ${historyCount} history entries scanned. Try a broader query (single keyword), a different angle (related concept), or memory_read with topic="${this.telegramScopedTopic(chatId, "general")}" to enumerate. If you're looking for an older turn, ask for the speaker name plus a topic word.`
615328
615730
  };
615329
615731
  }
615732
+ const timeBanner = timeLabel ? ` (time-range: ${timeLabel}${since !== void 0 || until !== void 0 ? `; window=${since !== void 0 ? new Date(since).toISOString() : "−∞"} → ${until !== void 0 ? new Date(until).toISOString() : "+∞"}` : ""})` : "";
615330
615733
  const output = [
615331
- `Scoped Telegram memory search for "${query}" in ${scopeLabel}.`,
615734
+ `Scoped Telegram memory search for "${query}" in ${scopeLabel}${timeBanner}.`,
615332
615735
  "Results are scoped to this Telegram chat and may include raw message evidence, graph episodes, associative facts, and memory cards.",
615333
615736
  "",
615334
615737
  sections.join("\n\n")
@@ -617598,21 +618001,45 @@ Content-Type: ${mimeForPath(pathOrFileId, field === "photo" ? "image" : "video")
617598
618001
  }
617599
618002
  return this.sendMessage(`@${clean5}`, text);
617600
618003
  }
617601
- async setMyCommands(commands) {
618004
+ hydrateTelegramCommandMap(commands) {
618005
+ for (const cmd of commands) {
618006
+ this.telegramCommandMap.set(cmd.command, cmd.canonical);
618007
+ }
618008
+ this.telegramCommandMap.set("auth", "auth");
618009
+ this.telegramCommandMap.set("start", "start");
618010
+ }
618011
+ async setMyCommands(commands, options2 = {}) {
617602
618012
  const apiCommands = commands.map((cmd) => ({
617603
618013
  command: cmd.command,
617604
618014
  description: cmd.description
617605
618015
  }));
617606
- const result = await this.apiCall("setMyCommands", { commands: apiCommands });
618016
+ const payload = { commands: apiCommands };
618017
+ if (options2.scope) payload["scope"] = options2.scope;
618018
+ if (options2.languageCode) payload["language_code"] = options2.languageCode;
618019
+ const result = await this.apiCall("setMyCommands", payload);
617607
618020
  if (result.ok) {
617608
- for (const cmd of commands) {
617609
- this.telegramCommandMap.set(cmd.command, cmd.canonical);
617610
- }
617611
- this.telegramCommandMap.set("auth", "auth");
617612
- this.telegramCommandMap.set("start", "start");
618021
+ this.hydrateTelegramCommandMap(commands);
617613
618022
  }
617614
618023
  return Boolean(result.ok);
617615
618024
  }
618025
+ async registerScopedMyCommands(adminUserId = this.adminUserId) {
618026
+ const publicCommands = buildTelegramBotCommands({ scope: "public" });
618027
+ const adminCommands = buildTelegramBotCommands({ scope: "admin" });
618028
+ this.hydrateTelegramCommandMap([...publicCommands, ...adminCommands]);
618029
+ const publicOk = await this.setMyCommands(publicCommands, { scope: { type: "default" } });
618030
+ const adminId = adminUserId?.trim();
618031
+ if (!adminId || !/^\d+$/.test(adminId)) {
618032
+ return publicOk;
618033
+ }
618034
+ const adminChatId = Number(adminId);
618035
+ if (!Number.isSafeInteger(adminChatId)) {
618036
+ return publicOk;
618037
+ }
618038
+ const adminOk = await this.setMyCommands(adminCommands, {
618039
+ scope: { type: "chat", chat_id: adminChatId }
618040
+ });
618041
+ return publicOk && adminOk;
618042
+ }
617616
618043
  async getMyCommands() {
617617
618044
  const result = await this.apiCall("getMyCommands");
617618
618045
  return Array.isArray(result.result) ? result.result : [];
@@ -643942,7 +644369,7 @@ async function runSelfImprovementCycle(repoRoot) {
643942
644369
  } catch {
643943
644370
  }
643944
644371
  }
643945
- 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) {
644372
+ 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) {
643946
644373
  const voiceStyleMap = {
643947
644374
  concise: 1,
643948
644375
  balanced: 3,
@@ -643964,6 +644391,9 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
643964
644391
  }
643965
644392
  const modelTier = getModelTier(config.model);
643966
644393
  const sessionId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
644394
+ const taskMetricDescription = cleanForStorage(task).replace(/\s+/g, " ").slice(0, 160) || "task";
644395
+ costTracker?.startTask(taskMetricDescription);
644396
+ sessionMetrics?.startTask(taskMetricDescription);
643967
644397
  const projectCtx = buildProjectContext(repoRoot, taskStores?.contextStores);
643968
644398
  let dynamicContext = formatContextForPrompt(projectCtx, modelTier);
643969
644399
  try {
@@ -644923,6 +645353,7 @@ ${entry.fullContent}`
644923
645353
  let lastToolCall = null;
644924
645354
  let toolCallStartMs = 0;
644925
645355
  let streamStartMs = 0;
645356
+ let lastStreamDurationMs = 0;
644926
645357
  let streamTextBuffer = "";
644927
645358
  let lastAssistantText = "";
644928
645359
  let lastProvenancePath = null;
@@ -644984,6 +645415,9 @@ ${entry.fullContent}`
644984
645415
  case "tool_call":
644985
645416
  if (_apiCallbacks?.onToolCall)
644986
645417
  _apiCallbacks.onToolCall(event.toolName ?? "unknown", event.toolArgs);
645418
+ if (event.toolName && event.toolName !== "task_complete") {
645419
+ sessionMetrics?.recordToolCall(event.toolName);
645420
+ }
644987
645421
  toolSequence.push({
644988
645422
  tool: event.toolName ?? "unknown",
644989
645423
  argKeys: Object.keys(event.toolArgs ?? {})
@@ -645005,6 +645439,7 @@ ${entry.fullContent}`
645005
645439
  const name10 = event.toolName ?? "";
645006
645440
  if (name10 === "file_write" || name10 === "file_edit" || name10 === "file_patch" || name10 === "batch_edit") {
645007
645441
  filesTouched.add(event.toolArgs.path);
645442
+ sessionMetrics?.recordFileModified(event.toolArgs.path);
645008
645443
  if (isNeovimActive()) {
645009
645444
  notifyNeovimFileChange(event.toolArgs.path).catch(
645010
645445
  () => {
@@ -645233,6 +645668,7 @@ ${entry.fullContent}`
645233
645668
  break;
645234
645669
  case "stream_end": {
645235
645670
  const streamDurationMs = streamStartMs > 0 ? Date.now() - streamStartMs : 0;
645671
+ lastStreamDurationMs = streamDurationMs;
645236
645672
  streamStartMs = 0;
645237
645673
  if (isNeovimActive()) {
645238
645674
  writeToNeovimOutput("\r\n");
@@ -645311,17 +645747,28 @@ ${entry.fullContent}`
645311
645747
  }
645312
645748
  break;
645313
645749
  case "token_usage":
645314
- if (statusBar && event.tokenUsage) {
645750
+ if (event.tokenUsage) {
645751
+ sessionMetrics?.recordTurn();
645315
645752
  const lastPromptTokens = event.tokenUsage.lastPromptTokens ?? event.tokenUsage.promptTokens;
645316
645753
  const lastCompletionTokens = event.tokenUsage.lastCompletionTokens ?? event.tokenUsage.completionTokens;
645754
+ if (lastPromptTokens > 0 || lastCompletionTokens > 0) {
645755
+ sessionMetrics?.recordTokenUsage(lastPromptTokens, lastCompletionTokens);
645756
+ }
645757
+ sessionMetrics?.recordContextEstimate(event.tokenUsage.estimatedContextTokens);
645758
+ if (lastCompletionTokens > 0 && lastStreamDurationMs > 0) {
645759
+ sessionMetrics?.recordGeneration(lastCompletionTokens, lastStreamDurationMs);
645760
+ lastStreamDurationMs = 0;
645761
+ }
645317
645762
  if (costTracker && (lastPromptTokens > 0 || lastCompletionTokens > 0)) {
645318
645763
  costTracker.trackTokens(lastPromptTokens, lastCompletionTokens);
645319
645764
  }
645320
- statusBar.updateMetrics({
645321
- ...event.tokenUsage,
645322
- estimatedCost: costTracker?.currentCost,
645323
- hasPricing: costTracker?.hasPricing
645324
- });
645765
+ if (statusBar) {
645766
+ statusBar.updateMetrics({
645767
+ ...event.tokenUsage,
645768
+ estimatedCost: costTracker?.currentCost,
645769
+ hasPricing: costTracker?.hasPricing
645770
+ });
645771
+ }
645325
645772
  if (config.verbose) {
645326
645773
  const tu = event.tokenUsage;
645327
645774
  const ctxPct = tu.estimatedContextTokens > 0 && statusBar ? ` (ctx: ~${tu.estimatedContextTokens.toLocaleString()} tokens)` : "";
@@ -645434,6 +645881,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
645434
645881
  total: result.totalTokens,
645435
645882
  estimated: result.estimatedTokens
645436
645883
  };
645884
+ costTracker?.endTask();
645885
+ sessionMetrics?.endTask();
645437
645886
  if (result.completed) {
645438
645887
  statusBar?.removeRecentContentMatchingText(result.summary);
645439
645888
  }
@@ -648652,7 +649101,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648652
649101
  });
648653
649102
  await telegramBridge.start();
648654
649103
  statusBar.setTelegramStatus(true, telegramBridge.stats.activeSubAgents);
648655
- telegramBridge.setMyCommands(buildTelegramBotCommands()).catch((err) => {
649104
+ telegramBridge.registerScopedMyCommands(adminId).catch((err) => {
648656
649105
  writeContent(
648657
649106
  () => renderWarning(`Telegram command registration failed: ${err instanceof Error ? err.message : String(err)}`)
648658
649107
  );
@@ -649645,6 +650094,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649645
650094
  statusBar.setContextWindowSize(size);
649646
650095
  telegramBridge?.setContextWindowSize(size);
649647
650096
  },
650097
+ getContextWindowSize: () => resolvedContextWindowSize,
649648
650098
  setCapabilities: (caps) => {
649649
650099
  resolvedCaps = caps;
649650
650100
  statusBar.setCapabilities(caps);
@@ -650342,7 +650792,8 @@ Execute this skill now. Follow the behavioral guidance above.`;
650342
650792
  buildSlashCommandHandler(),
650343
650793
  thinkingEnabled,
650344
650794
  handleAskUser,
650345
- selfModifyEnabled
650795
+ selfModifyEnabled,
650796
+ sessionMetrics
650346
650797
  );
650347
650798
  activeTask = task;
650348
650799
  setTerminalTitle(input.slice(0, 60), version4);
@@ -650770,7 +651221,8 @@ NEW TASK: ${fullInput}`;
650770
651221
  buildSlashCommandHandler(),
650771
651222
  thinkingEnabled,
650772
651223
  handleAskUser,
650773
- selfModifyEnabled
651224
+ selfModifyEnabled,
651225
+ sessionMetrics
650774
651226
  );
650775
651227
  activeTask = task;
650776
651228
  _recallText = null;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.65",
3
+ "version": "1.0.67",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.65",
9
+ "version": "1.0.67",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.65",
3
+ "version": "1.0.67",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",