omnius 1.0.86 โ†’ 1.0.87

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
@@ -608078,6 +608078,395 @@ var init_telegram_help_menu = __esm({
608078
608078
  }
608079
608079
  });
608080
608080
 
608081
+ // packages/cli/src/tui/telegram-stats-menu.ts
608082
+ function emptySnapshot() {
608083
+ return {
608084
+ totalInferences: 0,
608085
+ totalPromptTokens: 0,
608086
+ totalCompletionTokens: 0,
608087
+ totalTokens: 0,
608088
+ avgTokensPerSecond: 0,
608089
+ peakTokensPerSecond: 0,
608090
+ avgInferenceDurationMs: 0,
608091
+ totalInferenceDurationMs: 0,
608092
+ totalToolCalls: 0,
608093
+ successfulToolCalls: 0,
608094
+ failedToolCalls: 0,
608095
+ toolCallBreakdown: [],
608096
+ contextWindowSize: 0,
608097
+ estimatedContextTokens: 0,
608098
+ peakContextTokens: 0,
608099
+ contextUtilizationPct: 0,
608100
+ sessionDurationMs: 0,
608101
+ sessionStartAt: Date.now(),
608102
+ turnCount: 0,
608103
+ compactionCount: 0,
608104
+ model: "unknown",
608105
+ backend: "ollama",
608106
+ gpuName: null,
608107
+ gpuVramUsedMb: null,
608108
+ gpuVramTotalMb: null
608109
+ };
608110
+ }
608111
+ function fmtDuration(ms) {
608112
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
608113
+ const s2 = ms / 1e3;
608114
+ if (s2 < 60) return `${s2.toFixed(1)}s`;
608115
+ const m2 = Math.floor(s2 / 60);
608116
+ const rs = Math.round(s2 % 60);
608117
+ if (m2 < 60) return `${m2}m ${rs}s`;
608118
+ const h = Math.floor(m2 / 60);
608119
+ const rm4 = m2 % 60;
608120
+ return `${h}h ${rm4}m`;
608121
+ }
608122
+ function fmtTokens2(n2) {
608123
+ if (n2 < 1e3) return String(n2);
608124
+ if (n2 < 1e6) return `${(n2 / 1e3).toFixed(1)}K`;
608125
+ return `${(n2 / 1e6).toFixed(2)}M`;
608126
+ }
608127
+ function fmtPct(n2) {
608128
+ return `${n2.toFixed(1)}%`;
608129
+ }
608130
+ function escapeHTML2(text) {
608131
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
608132
+ }
608133
+ function buildMetricEntries(snap, scope) {
608134
+ const entries = [];
608135
+ entries.push({
608136
+ icon: "๐Ÿง ",
608137
+ label: "Model",
608138
+ value: escapeHTML2(snap.model),
608139
+ category: "inference"
608140
+ });
608141
+ entries.push({
608142
+ icon: "โšก",
608143
+ label: "Inferences",
608144
+ value: String(snap.totalInferences),
608145
+ detail: `Total duration: ${fmtDuration(snap.totalInferenceDurationMs)}`,
608146
+ category: "inference"
608147
+ });
608148
+ entries.push({
608149
+ icon: "๐Ÿ”ค",
608150
+ label: "Tokens",
608151
+ value: fmtTokens2(snap.totalTokens),
608152
+ detail: `Prompt: ${fmtTokens2(snap.totalPromptTokens)} ยท Completion: ${fmtTokens2(snap.totalCompletionTokens)}`,
608153
+ category: "inference"
608154
+ });
608155
+ entries.push({
608156
+ icon: "๐Ÿš€",
608157
+ label: "Avg Speed",
608158
+ value: `${snap.avgTokensPerSecond.toFixed(1)} tok/s`,
608159
+ detail: `Peak: ${snap.peakTokensPerSecond.toFixed(1)} tok/s`,
608160
+ category: "inference"
608161
+ });
608162
+ entries.push({
608163
+ icon: "โฑ",
608164
+ label: "Avg Inference",
608165
+ value: fmtDuration(snap.avgInferenceDurationMs),
608166
+ category: "inference"
608167
+ });
608168
+ entries.push({
608169
+ icon: "๐Ÿ”ง",
608170
+ label: "Tool Calls",
608171
+ value: String(snap.totalToolCalls),
608172
+ detail: `โœ… ${snap.successfulToolCalls} ยท โŒ ${snap.failedToolCalls}`,
608173
+ category: "tools"
608174
+ });
608175
+ const topTools = snap.toolCallBreakdown.sort((a2, b) => b.count - a2.count).slice(0, scope === "admin" ? 5 : 3);
608176
+ for (const t2 of topTools) {
608177
+ entries.push({
608178
+ icon: "๐Ÿ”ฉ",
608179
+ label: t2.name,
608180
+ value: `${t2.count}ร—`,
608181
+ detail: `Avg: ${fmtDuration(t2.avgDurationMs)}`,
608182
+ category: "tools"
608183
+ });
608184
+ }
608185
+ entries.push({
608186
+ icon: "๐Ÿ“",
608187
+ label: "Context Window",
608188
+ value: fmtTokens2(snap.contextWindowSize),
608189
+ category: "context"
608190
+ });
608191
+ entries.push({
608192
+ icon: "๐Ÿ“Š",
608193
+ label: "Context Used",
608194
+ value: fmtTokens2(snap.estimatedContextTokens),
608195
+ detail: `Utilization: ${fmtPct(snap.contextUtilizationPct)} ยท Peak: ${fmtTokens2(snap.peakContextTokens)}`,
608196
+ category: "context"
608197
+ });
608198
+ entries.push({
608199
+ icon: "๐Ÿ—œ",
608200
+ label: "Compactions",
608201
+ value: String(snap.compactionCount),
608202
+ category: "context"
608203
+ });
608204
+ entries.push({
608205
+ icon: "๐Ÿ•",
608206
+ label: "Session",
608207
+ value: fmtDuration(snap.sessionDurationMs),
608208
+ category: "session"
608209
+ });
608210
+ entries.push({
608211
+ icon: "๐Ÿ”„",
608212
+ label: "Turns",
608213
+ value: String(snap.turnCount),
608214
+ category: "session"
608215
+ });
608216
+ if (scope === "admin") {
608217
+ entries.push({
608218
+ icon: "๐Ÿ–ฅ",
608219
+ label: "Backend",
608220
+ value: escapeHTML2(snap.backend),
608221
+ category: "system"
608222
+ });
608223
+ if (snap.gpuName) {
608224
+ entries.push({
608225
+ icon: "๐ŸŽฎ",
608226
+ label: "GPU",
608227
+ value: escapeHTML2(snap.gpuName),
608228
+ category: "system"
608229
+ });
608230
+ }
608231
+ if (snap.gpuVramTotalMb != null) {
608232
+ const used = snap.gpuVramUsedMb ?? 0;
608233
+ entries.push({
608234
+ icon: "๐Ÿ’พ",
608235
+ label: "VRAM",
608236
+ value: `${Math.round(used)}/${Math.round(snap.gpuVramTotalMb)} MB`,
608237
+ detail: `Usage: ${fmtPct(used / snap.gpuVramTotalMb * 100)}`,
608238
+ category: "system"
608239
+ });
608240
+ }
608241
+ }
608242
+ return entries;
608243
+ }
608244
+ function encodeStatsCallback(action, value2) {
608245
+ return `${CB_PREFIX}${action}:${value2}`;
608246
+ }
608247
+ function decodeStatsCallback(data) {
608248
+ if (!data.startsWith(CB_PREFIX)) return null;
608249
+ const body = data.slice(CB_PREFIX.length);
608250
+ const colonIdx = body.indexOf(":");
608251
+ if (colonIdx === -1) return null;
608252
+ const action = body.slice(0, colonIdx);
608253
+ if (action !== "page" && action !== "close") return null;
608254
+ const value2 = parseInt(body.slice(colonIdx + 1), 10);
608255
+ if (isNaN(value2)) return null;
608256
+ return { action, value: value2 };
608257
+ }
608258
+ function buildStatsKeyboard(page2, totalPages, countdown) {
608259
+ const rows = [];
608260
+ const navRow = [];
608261
+ if (page2 === 0) {
608262
+ navRow.push({
608263
+ text: countdown != null ? `โœ– Close (${countdown}s)` : "โœ– Close",
608264
+ callback_data: encodeStatsCallback("close", 0)
608265
+ });
608266
+ }
608267
+ navRow.push({
608268
+ text: `${page2 + 1}/${totalPages}`,
608269
+ callback_data: encodeStatsCallback("page", page2)
608270
+ // current page = no-op refresh
608271
+ });
608272
+ if (page2 > 0) {
608273
+ navRow.unshift({ text: "โ—€๏ธ", callback_data: encodeStatsCallback("page", page2 - 1) });
608274
+ }
608275
+ if (page2 < totalPages - 1) {
608276
+ navRow.push({ text: "โ–ถ๏ธ", callback_data: encodeStatsCallback("page", page2 + 1) });
608277
+ }
608278
+ rows.push(navRow);
608279
+ return rows;
608280
+ }
608281
+ function buildStatsPageText(entries, page2, countdown) {
608282
+ const start2 = page2 * PAGE_SIZE;
608283
+ const pageEntries = entries.slice(start2, start2 + PAGE_SIZE);
608284
+ const lines = [];
608285
+ lines.push("<b>๐Ÿ“Š Session Metrics</b>");
608286
+ lines.push("");
608287
+ let currentCategory = "";
608288
+ for (const e2 of pageEntries) {
608289
+ if (e2.category !== currentCategory) {
608290
+ currentCategory = e2.category;
608291
+ lines.push(`<b>${CATEGORY_LABELS2[e2.category]}</b>`);
608292
+ }
608293
+ let line = ` ${e2.icon} <b>${e2.label}:</b> ${e2.value}`;
608294
+ lines.push(line);
608295
+ if (e2.detail) {
608296
+ lines.push(` <i>${escapeHTML2(e2.detail)}</i>`);
608297
+ }
608298
+ }
608299
+ if (countdown != null) {
608300
+ lines.push("");
608301
+ lines.push(`<i>โณ Auto-closing in ${countdown}sโ€ฆ</i>`);
608302
+ }
608303
+ return lines.join("\n");
608304
+ }
608305
+ function renderStatsMenu(scope, snapshot) {
608306
+ return renderStatsMenuPage(scope, 0, snapshot);
608307
+ }
608308
+ function renderStatsMenuPage(scope, page2, snapshot, countdown = null) {
608309
+ const entries = buildMetricEntries(snapshot, scope);
608310
+ const totalPages = Math.max(1, Math.ceil(entries.length / PAGE_SIZE));
608311
+ const safePage = Math.min(page2, totalPages - 1);
608312
+ const text = buildStatsPageText(entries, safePage, countdown);
608313
+ const keyboard = buildStatsKeyboard(safePage, totalPages, countdown);
608314
+ return { text, reply_markup: { inline_keyboard: keyboard } };
608315
+ }
608316
+ function handleStatsCallback(data, currentState, snapshot) {
608317
+ const decoded = decodeStatsCallback(data);
608318
+ if (!decoded) return null;
608319
+ switch (decoded.action) {
608320
+ case "page": {
608321
+ const page2 = decoded.value;
608322
+ const render2 = renderStatsMenuPage(currentState.scope, page2, snapshot);
608323
+ const newState = {
608324
+ ...currentState,
608325
+ page: page2,
608326
+ lastInteractionAt: Date.now()
608327
+ };
608328
+ return { render: render2, newState };
608329
+ }
608330
+ case "close": {
608331
+ return { render: null, newState: currentState, close: true };
608332
+ }
608333
+ default:
608334
+ return null;
608335
+ }
608336
+ }
608337
+ var PAGE_SIZE, StatsMenuStateStore, CB_PREFIX, CATEGORY_LABELS2, INACTIVITY_TIMEOUT_MS2, COUNTDOWN_SECONDS2, StatsMenuTimerManager;
608338
+ var init_telegram_stats_menu = __esm({
608339
+ "packages/cli/src/tui/telegram-stats-menu.ts"() {
608340
+ "use strict";
608341
+ PAGE_SIZE = 6;
608342
+ StatsMenuStateStore = class {
608343
+ states = /* @__PURE__ */ new Map();
608344
+ key(chatId, messageId) {
608345
+ return `${chatId}:${messageId}`;
608346
+ }
608347
+ get(chatId, messageId) {
608348
+ return this.states.get(this.key(chatId, messageId));
608349
+ }
608350
+ set(state) {
608351
+ this.states.set(this.key(state.chatId, state.messageId), state);
608352
+ }
608353
+ delete(chatId, messageId) {
608354
+ this.states.delete(this.key(chatId, messageId));
608355
+ }
608356
+ prune(maxAgeMs) {
608357
+ const cutoff = Date.now() - maxAgeMs;
608358
+ for (const [k, v] of this.states) {
608359
+ if (v.lastInteractionAt < cutoff) this.states.delete(k);
608360
+ }
608361
+ }
608362
+ };
608363
+ CB_PREFIX = "st_";
608364
+ CATEGORY_LABELS2 = {
608365
+ inference: "๐Ÿง  Inference",
608366
+ tools: "๐Ÿ”ง Tools",
608367
+ context: "๐Ÿ“ Context",
608368
+ session: "๐Ÿ• Session",
608369
+ system: "๐Ÿ–ฅ System"
608370
+ };
608371
+ INACTIVITY_TIMEOUT_MS2 = 6e4;
608372
+ COUNTDOWN_SECONDS2 = 10;
608373
+ StatsMenuTimerManager = class {
608374
+ constructor(states, callbacks, getSnapshot) {
608375
+ this.states = states;
608376
+ this.callbacks = callbacks;
608377
+ this.getSnapshot = getSnapshot;
608378
+ }
608379
+ states;
608380
+ callbacks;
608381
+ getSnapshot;
608382
+ inactivityTimers = /* @__PURE__ */ new Map();
608383
+ countdownTimers = /* @__PURE__ */ new Map();
608384
+ countdownValues = /* @__PURE__ */ new Map();
608385
+ key(chatId, messageId) {
608386
+ return `${chatId}:${messageId}`;
608387
+ }
608388
+ startTimer(state) {
608389
+ this.resetTimer(state.chatId, state.messageId);
608390
+ }
608391
+ resetTimer(chatId, messageId) {
608392
+ const k = this.key(chatId, messageId);
608393
+ const existingInactivity = this.inactivityTimers.get(k);
608394
+ if (existingInactivity) clearTimeout(existingInactivity);
608395
+ this.cancelCountdown(chatId, messageId);
608396
+ this.inactivityTimers.set(
608397
+ k,
608398
+ setTimeout(() => {
608399
+ this.startCountdown(
608400
+ this.states.get(chatId, messageId) ?? {
608401
+ chatId,
608402
+ messageId,
608403
+ scope: "admin",
608404
+ page: 0,
608405
+ lastInteractionAt: Date.now()
608406
+ }
608407
+ );
608408
+ }, INACTIVITY_TIMEOUT_MS2)
608409
+ );
608410
+ }
608411
+ startCountdown(state) {
608412
+ const k = this.key(state.chatId, state.messageId);
608413
+ this.countdownValues.set(k, COUNTDOWN_SECONDS2);
608414
+ this.renderCountdown(state).catch(() => {
608415
+ });
608416
+ this.countdownTimers.set(
608417
+ k,
608418
+ setInterval(() => {
608419
+ const remaining = (this.countdownValues.get(k) ?? 0) - 1;
608420
+ if (remaining <= 0) {
608421
+ this.deleteMenu(state.chatId, state.messageId);
608422
+ return;
608423
+ }
608424
+ this.countdownValues.set(k, remaining);
608425
+ this.renderCountdown(state).catch(() => {
608426
+ });
608427
+ }, 1e3)
608428
+ );
608429
+ }
608430
+ cancelCountdown(chatId, messageId) {
608431
+ const k = this.key(chatId, messageId);
608432
+ const timer = this.countdownTimers.get(k);
608433
+ if (timer) {
608434
+ clearInterval(timer);
608435
+ this.countdownTimers.delete(k);
608436
+ this.countdownValues.delete(k);
608437
+ }
608438
+ }
608439
+ async renderCountdown(state) {
608440
+ const k = this.key(state.chatId, state.messageId);
608441
+ const countdown = this.countdownValues.get(k) ?? null;
608442
+ const snap = this.getSnapshot();
608443
+ const entries = buildMetricEntries(snap, state.scope);
608444
+ const totalPages = Math.max(1, Math.ceil(entries.length / PAGE_SIZE));
608445
+ const text = buildStatsPageText(entries, state.page, countdown);
608446
+ const keyboard = buildStatsKeyboard(state.page, totalPages, countdown);
608447
+ try {
608448
+ await this.callbacks.editMessageText(state.chatId, state.messageId, text, {
608449
+ inline_keyboard: keyboard
608450
+ });
608451
+ } catch {
608452
+ }
608453
+ }
608454
+ async deleteMenu(chatId, messageId) {
608455
+ const k = this.key(chatId, messageId);
608456
+ this.cancelCountdown(chatId, messageId);
608457
+ const inactivity = this.inactivityTimers.get(k);
608458
+ if (inactivity) clearTimeout(inactivity);
608459
+ this.inactivityTimers.delete(k);
608460
+ this.states.delete(chatId, messageId);
608461
+ try {
608462
+ await this.callbacks.deleteMessage(chatId, messageId);
608463
+ } catch {
608464
+ }
608465
+ }
608466
+ };
608467
+ }
608468
+ });
608469
+
608081
608470
  // packages/cli/src/tui/telegram-creative-tools.ts
608082
608471
  import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes21 } from "node:crypto";
608083
608472
  import {
@@ -611750,6 +612139,8 @@ function splitTelegramReminderDue(raw) {
611750
612139
  function telegramSyntheticHelpSignatures2() {
611751
612140
  return [
611752
612141
  { signature: "/help", description: "Show Telegram command help" },
612142
+ { signature: "/stats", description: "Show session metrics and inference statistics" },
612143
+ { signature: "/metrics", description: "Alias for /stats โ€” show session metrics" },
611753
612144
  { signature: "/start", description: "Show Telegram bridge status and authentication instructions" },
611754
612145
  { signature: "/auth <code>", description: "Authenticate this Telegram user as bot admin using the TUI code" },
611755
612146
  { signature: "/call", description: "Get the active voice call link when a call session is running" },
@@ -612495,6 +612886,7 @@ var init_telegram_bridge = __esm({
612495
612886
  init_media_routing();
612496
612887
  init_command_registry();
612497
612888
  init_telegram_help_menu();
612889
+ init_telegram_stats_menu();
612498
612890
  init_scoped_personality();
612499
612891
  init_telegram_creative_tools();
612500
612892
  init_omnius_directory();
@@ -612961,6 +613353,12 @@ External acquisition contract:
612961
613353
  helpMenuTimers = null;
612962
613354
  /** Prune expired help menu states every 5 minutes */
612963
613355
  helpMenuPruneTimer = null;
613356
+ /** Interactive stats menu state store (inline keyboard navigation) */
613357
+ statsMenuStates = new StatsMenuStateStore();
613358
+ /** Auto-close timer manager for stats menus (inactivity โ†’ countdown โ†’ delete) */
613359
+ statsMenuTimers = null;
613360
+ /** Prune expired stats menu states every 5 minutes */
613361
+ statsMenuPruneTimer = null;
612964
613362
  /** Command handler for admin DM slash commands (wired from interactive.ts) */
612965
613363
  commandHandler = null;
612966
613364
  /** Callback fired after a Telegram user completes the TUI-only admin auth challenge */
@@ -612970,7 +613368,9 @@ External acquisition contract:
612970
613368
  /** Telegram command menu names mapped back to canonical TUI command names. */
612971
613369
  telegramCommandMap = /* @__PURE__ */ new Map([
612972
613370
  ["auth", "auth"],
612973
- ["start", "start"]
613371
+ ["start", "start"],
613372
+ ["stats", "stats"],
613373
+ ["metrics", "stats"]
612974
613374
  ]);
612975
613375
  /** TUI agent-view callbacks for Telegram sub-agent registration. */
612976
613376
  subAgentViewCallbacks = null;
@@ -613245,6 +613645,10 @@ External acquisition contract:
613245
613645
  const name10 = this.telegramSlashName(input);
613246
613646
  return name10 === "help" || name10 === "h" || name10 === "commands" || name10 === "cmds";
613247
613647
  }
613648
+ isTelegramStatsCommand(input) {
613649
+ const name10 = this.telegramSlashName(input);
613650
+ return name10 === "stats" || name10 === "metrics";
613651
+ }
613248
613652
  isKnownTelegramSlash(input) {
613249
613653
  const first2 = input.trim().split(/\s+/)[0] ?? "";
613250
613654
  const botless = first2.startsWith("/") ? first2.slice(1).split("@")[0]?.toLowerCase() : "";
@@ -613431,6 +613835,94 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
613431
613835
  this.helpMenuTimers.startTimer(state);
613432
613836
  }
613433
613837
  }
613838
+ collectSessionMetricsSnapshot() {
613839
+ const snap = emptySnapshot();
613840
+ try {
613841
+ const runner = this.activeRunner;
613842
+ if (runner) {
613843
+ snap.model = runner.model ?? runner.modelName ?? "unknown";
613844
+ snap.backend = runner.backend ?? "ollama";
613845
+ snap.totalInferences = runner.inferenceCount ?? runner.totalInferences ?? 0;
613846
+ snap.totalPromptTokens = runner.totalPromptTokens ?? 0;
613847
+ snap.totalCompletionTokens = runner.totalCompletionTokens ?? 0;
613848
+ snap.totalTokens = snap.totalPromptTokens + snap.totalCompletionTokens;
613849
+ snap.totalInferenceDurationMs = runner.totalInferenceDurationMs ?? 0;
613850
+ snap.avgInferenceDurationMs = snap.totalInferences > 0 ? snap.totalInferenceDurationMs / snap.totalInferences : 0;
613851
+ snap.avgTokensPerSecond = runner.avgTokensPerSecond ?? 0;
613852
+ snap.peakTokensPerSecond = runner.peakTokensPerSecond ?? 0;
613853
+ snap.totalToolCalls = runner.toolCallCount ?? runner.totalToolCalls ?? 0;
613854
+ snap.successfulToolCalls = runner.successfulToolCalls ?? snap.totalToolCalls;
613855
+ snap.failedToolCalls = runner.failedToolCalls ?? 0;
613856
+ snap.toolCallBreakdown = runner.toolCallBreakdown ?? [];
613857
+ snap.contextWindowSize = runner.contextWindowSize ?? runner.maxContextTokens ?? 0;
613858
+ snap.estimatedContextTokens = runner.estimatedContextTokens ?? runner.latestEstimatedContextTokens ?? 0;
613859
+ snap.peakContextTokens = runner.peakEstimatedContextTokens ?? snap.estimatedContextTokens;
613860
+ snap.contextUtilizationPct = snap.contextWindowSize > 0 ? snap.estimatedContextTokens / snap.contextWindowSize * 100 : 0;
613861
+ snap.turnCount = runner.turnCount ?? 0;
613862
+ snap.compactionCount = runner.compactionCount ?? 0;
613863
+ snap.sessionStartAt = runner.sessionStartAt ?? Date.now();
613864
+ snap.sessionDurationMs = Date.now() - snap.sessionStartAt;
613865
+ }
613866
+ const env2 = this.envInfo;
613867
+ if (env2) {
613868
+ snap.gpuName = env2.gpuName ?? env2.gpu ?? null;
613869
+ snap.gpuVramUsedMb = env2.gpuVramUsedMb ?? env2.vramUsedMb ?? null;
613870
+ snap.gpuVramTotalMb = env2.gpuVramTotalMb ?? env2.vramTotalMb ?? null;
613871
+ }
613872
+ } catch {
613873
+ }
613874
+ if (snap.sessionDurationMs === 0 && snap.sessionStartAt > 0) {
613875
+ snap.sessionDurationMs = Date.now() - snap.sessionStartAt;
613876
+ }
613877
+ return snap;
613878
+ }
613879
+ async replyWithTelegramStats(msg, isAdmin) {
613880
+ const scope = isAdmin ? "admin" : "public";
613881
+ const snapshot = this.collectSessionMetricsSnapshot();
613882
+ const menu = renderStatsMenu(scope, snapshot);
613883
+ if (msg.guestQueryId) {
613884
+ const plainText = menu.text.replace(/<[^>]+>/g, "");
613885
+ await this.answerGuestQuery(msg.guestQueryId, plainText, { parseMode: "HTML" });
613886
+ return;
613887
+ }
613888
+ const sent = await this.apiCall("sendMessage", {
613889
+ chat_id: msg.chatId,
613890
+ text: menu.text,
613891
+ parse_mode: "HTML",
613892
+ reply_markup: JSON.stringify(menu.reply_markup),
613893
+ ...msg.chatType !== "private" ? { reply_to_message_id: msg.messageId } : {}
613894
+ });
613895
+ if (sent.ok && sent.result?.message_id) {
613896
+ const state = {
613897
+ chatId: msg.chatId,
613898
+ messageId: sent.result.message_id,
613899
+ scope,
613900
+ page: 0,
613901
+ lastInteractionAt: Date.now()
613902
+ };
613903
+ this.statsMenuStates.set(state);
613904
+ if (!this.statsMenuTimers) {
613905
+ this.statsMenuTimers = new StatsMenuTimerManager(this.statsMenuStates, {
613906
+ editMessageText: async (chatId, messageId, text, replyMarkup) => {
613907
+ await this.apiCall("editMessageText", {
613908
+ chat_id: chatId,
613909
+ message_id: messageId,
613910
+ text,
613911
+ parse_mode: "HTML",
613912
+ reply_markup: JSON.stringify(replyMarkup)
613913
+ });
613914
+ },
613915
+ deleteMessage: async (chatId, messageId) => {
613916
+ await this.apiCall("deleteMessage", {
613917
+ chat_id: chatId,
613918
+ message_id: messageId
613919
+ });
613920
+ }
613921
+ }, () => this.collectSessionMetricsSnapshot());
613922
+ }
613923
+ this.statsMenuTimers.startTimer(state);
613924
+ }
613925
+ }
613434
613926
  recordChatHistory(sessionKey, entry) {
613435
613927
  this.ensureTelegramConversationLoaded(sessionKey);
613436
613928
  const stamped = { ...entry, ts: entry.ts ?? Date.now() };
@@ -617192,6 +617684,10 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
617192
617684
  await this.replyWithTelegramHelp(msg, isAdmin);
617193
617685
  return;
617194
617686
  }
617687
+ if (msg.text.trim().startsWith("/") && this.isTelegramStatsCommand(normalizedCommandText)) {
617688
+ await this.replyWithTelegramStats(msg, isAdmin);
617689
+ return;
617690
+ }
617195
617691
  const telegramSlash = this.telegramSlashName(normalizedCommandText);
617196
617692
  if (msg.text.trim().startsWith("/") && TELEGRAM_REFLECTION_SLASH_COMMANDS.has(telegramSlash)) {
617197
617693
  await this.handleTelegramReflectionSlash(msg, normalizedCommandText);
@@ -619806,6 +620302,72 @@ Scoped workspace: ${scopedRoot}`,
619806
620302
  }
619807
620303
  return;
619808
620304
  }
620305
+ const statsDecoded = decodeStatsCallback(callback.data);
620306
+ if (statsDecoded) {
620307
+ let answerText2 = "";
620308
+ let alert2 = false;
620309
+ try {
620310
+ const chatId = callback.chatId;
620311
+ const messageId = callback.messageId;
620312
+ if (!chatId || !messageId) {
620313
+ answerText2 = "Cannot identify menu message.";
620314
+ alert2 = true;
620315
+ return;
620316
+ }
620317
+ const menuState = this.statsMenuStates.get(chatId, messageId);
620318
+ if (!menuState) {
620319
+ answerText2 = "This stats menu expired. Send /stats for a fresh one.";
620320
+ alert2 = true;
620321
+ return;
620322
+ }
620323
+ const isAdmin = this.isAdminActor(callback.fromUserId, callback.username);
620324
+ if (!isAdmin && menuState.scope === "admin") {
620325
+ answerText2 = "Only admin can interact with this menu.";
620326
+ alert2 = true;
620327
+ return;
620328
+ }
620329
+ const snapshot = this.collectSessionMetricsSnapshot();
620330
+ const result = handleStatsCallback(callback.data, menuState, snapshot);
620331
+ if (!result) {
620332
+ answerText2 = "Unknown menu action.";
620333
+ alert2 = true;
620334
+ return;
620335
+ }
620336
+ if (result.close) {
620337
+ this.statsMenuStates.delete(chatId, messageId);
620338
+ this.statsMenuTimers?.deleteMenu(chatId, messageId);
620339
+ try {
620340
+ await this.apiCall("deleteMessage", {
620341
+ chat_id: chatId,
620342
+ message_id: messageId
620343
+ });
620344
+ } catch {
620345
+ }
620346
+ await this.answerCallbackQuery(callback.id).catch(() => false);
620347
+ return;
620348
+ }
620349
+ this.statsMenuStates.set(result.newState);
620350
+ this.statsMenuTimers?.resetTimer(chatId, messageId);
620351
+ await this.apiCall("editMessageText", {
620352
+ chat_id: chatId,
620353
+ message_id: messageId,
620354
+ text: result.render.text,
620355
+ parse_mode: "HTML",
620356
+ reply_markup: JSON.stringify(result.render.reply_markup)
620357
+ });
620358
+ answerText2 = "";
620359
+ } catch (err) {
620360
+ answerText2 = err instanceof Error ? err.message : String(err);
620361
+ alert2 = true;
620362
+ } finally {
620363
+ if (answerText2) {
620364
+ await this.answerCallbackQuery(callback.id, answerText2.slice(0, 180), alert2).catch(() => false);
620365
+ } else {
620366
+ await this.answerCallbackQuery(callback.id).catch(() => false);
620367
+ }
620368
+ }
620369
+ return;
620370
+ }
619809
620371
  let answerText = "Updated.";
619810
620372
  let alert = false;
619811
620373
  try {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.86",
3
+ "version": "1.0.87",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.86",
9
+ "version": "1.0.87",
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.86",
3
+ "version": "1.0.87",
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",