omnius 1.0.115 → 1.0.117

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
@@ -564766,6 +564766,9 @@ __export(render_exports, {
564766
564766
  setEmojisEnabled: () => setEmojisEnabled,
564767
564767
  ui: () => ui
564768
564768
  });
564769
+ function stdoutIsTTY() {
564770
+ return process.stdout.isTTY ?? false;
564771
+ }
564769
564772
  function accentFg() {
564770
564773
  const a2 = tuiAccent();
564771
564774
  return a2 < 0 ? "\x1B[39m" : `\x1B[38;5;${a2}m`;
@@ -564774,17 +564777,17 @@ function dimFg() {
564774
564777
  return `\x1B[38;5;${tuiTextDim()}m`;
564775
564778
  }
564776
564779
  function ansi2(code8, text) {
564777
- return isTTY2 ? `\x1B[${code8}m${text}\x1B[0m` : text;
564780
+ return stdoutIsTTY() ? `\x1B[${code8}m${text}\x1B[0m` : text;
564778
564781
  }
564779
564782
  function fg256(code8, text) {
564780
- return isTTY2 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
564783
+ return stdoutIsTTY() ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
564781
564784
  }
564782
564785
  function hyperlink(url, text) {
564783
- if (!isTTY2) return text;
564786
+ if (!stdoutIsTTY()) return text;
564784
564787
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
564785
564788
  }
564786
564789
  function fileLink(filePath) {
564787
- if (!isTTY2) return filePath;
564790
+ if (!stdoutIsTTY()) return filePath;
564788
564791
  if (filePath.startsWith("/") || filePath.startsWith("~")) {
564789
564792
  const absPath = filePath.startsWith("~") ? filePath.replace("~", process.env["HOME"] ?? "") : filePath;
564790
564793
  return hyperlink(`file://${absPath}`, filePath);
@@ -564923,11 +564926,11 @@ function toolColorCode(toolName) {
564923
564926
  return TOOL_COLOR_CODES[toolName] ?? tuiTextDim();
564924
564927
  }
564925
564928
  function toolColorSeq(code8, bold = false) {
564926
- if (!_colorsEnabled || !isTTY2) return "";
564929
+ if (!_colorsEnabled || !stdoutIsTTY()) return "";
564927
564930
  return `\x1B[${bold ? "1;" : ""}38;5;${code8}m`;
564928
564931
  }
564929
564932
  function toolResetSeq() {
564930
- return _colorsEnabled && isTTY2 ? RESET2 : "";
564933
+ return _colorsEnabled && stdoutIsTTY() ? RESET2 : "";
564931
564934
  }
564932
564935
  function visibleLen(text) {
564933
564936
  return stripAnsi(text).length;
@@ -564969,10 +564972,10 @@ function wrapToolTextLine(text, width) {
564969
564972
  out.push(remaining);
564970
564973
  return out;
564971
564974
  }
564972
- function buildToolTopBorder(title, metrics2, width, colorCode) {
564975
+ function buildToolTopBorder(title, metrics2, width, colorCode, metricsColorCode = 222) {
564973
564976
  const border = toolColorSeq(colorCode);
564974
564977
  const titleColor = toolColorSeq(colorCode, true);
564975
- const metricColor = toolColorSeq(222);
564978
+ const metricColor = toolColorSeq(metricsColorCode);
564976
564979
  const reset = toolResetSeq();
564977
564980
  const inner = Math.max(4, width - 2);
564978
564981
  const titleVisible = stripAnsi(title);
@@ -565077,7 +565080,7 @@ function buildToolBoxLines(data, width) {
565077
565080
  const w = Math.max(40, width);
565078
565081
  const innerWidth = Math.max(1, w - 4);
565079
565082
  const lines = [
565080
- buildToolTopBorder(data.title, data.metrics, w, data.colorCode),
565083
+ buildToolTopBorder(data.title, data.metrics, w, data.colorCode, data.metricsColorCode),
565081
565084
  buildToolDivider(w, data.colorCode)
565082
565085
  ];
565083
565086
  for (const bodyLine of data.body.length > 0 ? data.body : [{ text: "Done", mode: "wrap", kind: "dim" }]) {
@@ -565187,7 +565190,8 @@ function buildToolResultBoxLines(toolName, success, output, opts, width) {
565187
565190
  metrics: metrics2,
565188
565191
  body,
565189
565192
  footers,
565190
- colorCode: toolColorCode(toolName)
565193
+ colorCode: success ? toolColorCode(toolName) : TOOL_ERROR_COLOR_CODE,
565194
+ metricsColorCode: success ? void 0 : TOOL_ERROR_COLOR_CODE
565191
565195
  }, width);
565192
565196
  }
565193
565197
  function buildToolResultBody(toolName, success, output, verbose) {
@@ -565644,7 +565648,7 @@ function formatDuration3(ms) {
565644
565648
  const secs = Math.floor(totalSecs % 60);
565645
565649
  return `${mins}m ${secs}s`;
565646
565650
  }
565647
- var isTTY2, c3, ui, pastel, _emojisEnabled, _colorsEnabled, MD, TOOL_ICONS, TOOL_LABELS, TOOL_COLOR_CODES, BOX_TL2, BOX_TR2, BOX_BL2, BOX_BR2, BOX_H2, BOX_V2, BOX_TJ_L2, BOX_TJ_R2, RESET2, _contentWriteHook, HINTS, TOOL_NAMES, COMMAND_NAMES, SLASH_COMMANDS2;
565651
+ var c3, ui, pastel, _emojisEnabled, _colorsEnabled, MD, TOOL_ICONS, TOOL_LABELS, TOOL_COLOR_CODES, TOOL_ERROR_COLOR_CODE, BOX_TL2, BOX_TR2, BOX_BL2, BOX_BR2, BOX_H2, BOX_V2, BOX_TJ_L2, BOX_TJ_R2, RESET2, _contentWriteHook, HINTS, TOOL_NAMES, COMMAND_NAMES, SLASH_COMMANDS2;
565648
565652
  var init_render = __esm({
565649
565653
  "packages/cli/src/tui/render.ts"() {
565650
565654
  "use strict";
@@ -565655,10 +565659,9 @@ var init_render = __esm({
565655
565659
  init_text_selection();
565656
565660
  init_task_complete_box();
565657
565661
  init_model_picker();
565658
- isTTY2 = process.stdout.isTTY ?? false;
565659
565662
  c3 = {
565660
565663
  bold: (t2) => ansi2("1", t2),
565661
- dim: (t2) => isTTY2 ? `${dimFg()}${t2}\x1B[0m` : t2,
565664
+ dim: (t2) => stdoutIsTTY() ? `${dimFg()}${t2}\x1B[0m` : t2,
565662
565665
  italic: (t2) => ansi2("3", t2),
565663
565666
  red: (t2) => ansi2("31", t2),
565664
565667
  green: (t2) => ansi2("32", t2),
@@ -565827,6 +565830,7 @@ var init_render = __esm({
565827
565830
  transcribe_url: 43,
565828
565831
  ask_user: 44
565829
565832
  };
565833
+ TOOL_ERROR_COLOR_CODE = 198;
565830
565834
  BOX_TL2 = "╭";
565831
565835
  BOX_TR2 = "╮";
565832
565836
  BOX_BL2 = "╰";
@@ -577003,10 +577007,10 @@ ${CONTENT_BG_SEQ}`);
577003
577007
 
577004
577008
  // packages/cli/src/tui/tui-select.ts
577005
577009
  function ansi3(code8, text) {
577006
- return isTTY3 ? `\x1B[${code8}m${text}\x1B[0m` : text;
577010
+ return isTTY2 ? `\x1B[${code8}m${text}\x1B[0m` : text;
577007
577011
  }
577008
577012
  function fg2563(code8, text) {
577009
- return isTTY3 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
577013
+ return isTTY2 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
577010
577014
  }
577011
577015
  function stripAnsi3(s2) {
577012
577016
  return s2.replace(/\x1B\[[0-9;]*m/g, "");
@@ -577636,14 +577640,14 @@ ${tuiBgSeq()}`);
577636
577640
  }
577637
577641
  });
577638
577642
  }
577639
- var isTTY3, MENU_ACTIVE_GREEN_256, selectColors;
577643
+ var isTTY2, MENU_ACTIVE_GREEN_256, selectColors;
577640
577644
  var init_tui_select = __esm({
577641
577645
  "packages/cli/src/tui/tui-select.ts"() {
577642
577646
  "use strict";
577643
577647
  init_overlay_lock();
577644
577648
  init_theme();
577645
577649
  init_layout2();
577646
- isTTY3 = process.stdout.isTTY ?? false;
577650
+ isTTY2 = process.stdout.isTTY ?? false;
577647
577651
  MENU_ACTIVE_GREEN_256 = 154;
577648
577652
  selectColors = {
577649
577653
  blue: (t2) => fg2563(39, t2),
@@ -582209,7 +582213,7 @@ var init_workspace_explorer = __esm({
582209
582213
  import { existsSync as existsSync95 } from "node:fs";
582210
582214
  import { extname as extname13, resolve as resolve39 } from "node:path";
582211
582215
  function ansi4(code8, text) {
582212
- return isTTY4 ? `\x1B[${code8}m${text}\x1B[0m` : text;
582216
+ return isTTY3 ? `\x1B[${code8}m${text}\x1B[0m` : text;
582213
582217
  }
582214
582218
  function stripAnsi4(s2) {
582215
582219
  return s2.replace(/\x1B\[[0-9;]*m/g, "");
@@ -582380,13 +582384,13 @@ function showDropPanel(opts) {
582380
582384
  render2();
582381
582385
  });
582382
582386
  }
582383
- var isTTY4, dc;
582387
+ var isTTY3, dc;
582384
582388
  var init_drop_panel = __esm({
582385
582389
  "packages/cli/src/tui/drop-panel.ts"() {
582386
582390
  "use strict";
582387
582391
  init_overlay_lock();
582388
582392
  init_layout2();
582389
- isTTY4 = process.stdout.isTTY ?? false;
582393
+ isTTY3 = process.stdout.isTTY ?? false;
582390
582394
  dc = {
582391
582395
  bold: (t2) => ansi4("1", t2),
582392
582396
  dim: (t2) => ansi4("38;5;250", t2),
@@ -584736,7 +584740,7 @@ async function startNeovimMode(opts) {
584736
584740
  const ptyCols = opts.cols;
584737
584741
  const topOffset = opts.topOffset ?? 0;
584738
584742
  const ptyRows = Math.max(5, opts.contentRows);
584739
- if (isTTY5) {
584743
+ if (isTTY4) {
584740
584744
  const L = layout();
584741
584745
  const bottomBound = L.contentBottom;
584742
584746
  process.stdout.write(
@@ -584805,7 +584809,7 @@ async function startNeovimMode(opts) {
584805
584809
  }
584806
584810
  }
584807
584811
  function renderToolbar() {
584808
- if (!isTTY5) return;
584812
+ if (!isTTY4) return;
584809
584813
  const L = layout();
584810
584814
  const hdrRow = L.headerContent;
584811
584815
  const fg2 = 252;
@@ -584866,7 +584870,7 @@ async function startNeovimMode(opts) {
584866
584870
  stdin.setRawMode(true);
584867
584871
  }
584868
584872
  stdin.resume();
584869
- if (isTTY5) {
584873
+ if (isTTY4) {
584870
584874
  process.stdout.write("\x1B[?1002h\x1B[?1006h");
584871
584875
  }
584872
584876
  state.stdinHandler = (data) => {
@@ -585165,13 +585169,13 @@ function doCleanup(state) {
585165
585169
  }
585166
585170
  state.opts.onExit?.();
585167
585171
  }
585168
- var isTTY5, PTY_MODE_ENABLE_RE, STDIN_MOUSE_FOCUS_RE, _state;
585172
+ var isTTY4, PTY_MODE_ENABLE_RE, STDIN_MOUSE_FOCUS_RE, _state;
585169
585173
  var init_neovim_mode = __esm({
585170
585174
  "packages/cli/src/tui/neovim-mode.ts"() {
585171
585175
  "use strict";
585172
585176
  init_setup();
585173
585177
  init_layout2();
585174
- isTTY5 = process.stdout.isTTY ?? false;
585178
+ isTTY4 = process.stdout.isTTY ?? false;
585175
585179
  PTY_MODE_ENABLE_RE = /\x1B\[\?(?:1004|2004)h/g;
585176
585180
  STDIN_MOUSE_FOCUS_RE = /\x1B\[<[\d;]+[Mm]|\x1B\[M[\s\S]{3}|\x1B\[[IO]|\x1BO[ABCD]/g;
585177
585181
  _state = null;
@@ -604876,7 +604880,7 @@ function setCarouselWriter(writer) {
604876
604880
  chromeWrite2 = writer;
604877
604881
  }
604878
604882
  function fg(code8, text) {
604879
- return isTTY6 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
604883
+ return isTTY5 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
604880
604884
  }
604881
604885
  function displayWidth(str) {
604882
604886
  let w = 0;
@@ -604914,12 +604918,12 @@ function createRow(phraseIndices, speed, direction, bank2) {
604914
604918
  const phrases = phraseIndices.map((i2) => bank2[i2 % bank2.length]);
604915
604919
  return { phrases, offset: 0, speed, direction, renderedPlain: "" };
604916
604920
  }
604917
- var isTTY6, chromeWrite2, PHRASES, Carousel;
604921
+ var isTTY5, chromeWrite2, PHRASES, Carousel;
604918
604922
  var init_carousel = __esm({
604919
604923
  "packages/cli/src/tui/carousel.ts"() {
604920
604924
  "use strict";
604921
604925
  init_layout2();
604922
- isTTY6 = process.stdout.isTTY ?? false;
604926
+ isTTY5 = process.stdout.isTTY ?? false;
604923
604927
  chromeWrite2 = ((data) => {
604924
604928
  process.stdout.write(data);
604925
604929
  });
@@ -605031,7 +605035,7 @@ var init_carousel = __esm({
605031
605035
  * Sets scroll region to row 5+ for all content/readline.
605032
605036
  */
605033
605037
  start() {
605034
- if (!isTTY6) return 0;
605038
+ if (!isTTY5) return 0;
605035
605039
  this.started = true;
605036
605040
  setHeaderHeight(this.reservedRows);
605037
605041
  const L = layout();
@@ -605063,7 +605067,7 @@ var init_carousel = __esm({
605063
605067
  * Row 4 is left blank as a separator.
605064
605068
  */
605065
605069
  renderFrame() {
605066
- if (!isTTY6) return;
605070
+ if (!isTTY5) return;
605067
605071
  const L = layout();
605068
605072
  let buf = "\x1B7";
605069
605073
  buf += "\x1B[?7l";
@@ -605140,7 +605144,7 @@ var init_carousel = __esm({
605140
605144
  process.stdout.removeListener("resize", this.resizeHandler);
605141
605145
  this.resizeHandler = null;
605142
605146
  }
605143
- if (!isTTY6 || !this.started) return;
605147
+ if (!isTTY5 || !this.started) return;
605144
605148
  const L = layout();
605145
605149
  let buf = "\x1B7";
605146
605150
  for (let i2 = 0; i2 < this.reservedRows; i2++) {
@@ -605376,13 +605380,13 @@ function createAnimatedBanner(id, name10, frameBuilders, frameDurationMs, author
605376
605380
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
605377
605381
  };
605378
605382
  }
605379
- var isTTY7, chromeWrite3, MNEMONIC_ADJECTIVES, MNEMONIC_NOUNS, BannerRenderer;
605383
+ var isTTY6, chromeWrite3, MNEMONIC_ADJECTIVES, MNEMONIC_NOUNS, BannerRenderer;
605380
605384
  var init_banner = __esm({
605381
605385
  "packages/cli/src/tui/banner.ts"() {
605382
605386
  "use strict";
605383
605387
  init_theme();
605384
605388
  init_layout2();
605385
- isTTY7 = process.stdout.isTTY ?? false;
605389
+ isTTY6 = process.stdout.isTTY ?? false;
605386
605390
  chromeWrite3 = ((data) => {
605387
605391
  process.stdout.write(data);
605388
605392
  });
@@ -605544,7 +605548,7 @@ var init_banner = __esm({
605544
605548
  * Returns the number of rows reserved (3 banner + 1 separator = 4).
605545
605549
  */
605546
605550
  start() {
605547
- if (!isTTY7 || !this.currentDesign) return 0;
605551
+ if (!isTTY6 || !this.currentDesign) return 0;
605548
605552
  this.renderCurrentFrame();
605549
605553
  this._resizeHandler = () => {
605550
605554
  setTermSize(process.stdout.rows ?? 24, process.stdout.columns ?? 80);
@@ -605585,7 +605589,7 @@ var init_banner = __esm({
605585
605589
  }
605586
605590
  /** Render the current frame into the top 3 rows (public for refresh callbacks) */
605587
605591
  renderCurrentFrame() {
605588
- if (!isTTY7 || !this.currentDesign) return;
605592
+ if (!isTTY6 || !this.currentDesign) return;
605589
605593
  const frame = this.currentDesign.frames[this.currentFrame];
605590
605594
  if (!frame) return;
605591
605595
  this.width = termCols();
@@ -606000,13 +606004,13 @@ __export(syntax_highlight_exports, {
606000
606004
  prewarm: () => prewarm
606001
606005
  });
606002
606006
  function highlightingDisabled() {
606003
- return !isTTY8 || noColorEnv || disableEnv;
606007
+ return !isTTY7 || noColorEnv || disableEnv;
606004
606008
  }
606005
606009
  async function loadHighlighter() {
606006
606010
  if (_state2.attempted) return _state2.fn;
606007
606011
  _state2.attempted = true;
606008
606012
  if (highlightingDisabled()) {
606009
- _state2.reason = !isTTY8 ? "non-tty" : noColorEnv ? "NO_COLOR set" : "OMNIUS_TUI_HIGHLIGHT=0";
606013
+ _state2.reason = !isTTY7 ? "non-tty" : noColorEnv ? "NO_COLOR set" : "OMNIUS_TUI_HIGHLIGHT=0";
606010
606014
  return null;
606011
606015
  }
606012
606016
  try {
@@ -606053,7 +606057,7 @@ function getHighlightStatus() {
606053
606057
  available: isAvailable(),
606054
606058
  attempted: _state2.attempted,
606055
606059
  reason: _state2.reason,
606056
- isTTY: isTTY8,
606060
+ isTTY: isTTY7,
606057
606061
  noColor: noColorEnv,
606058
606062
  disabledByEnv: disableEnv
606059
606063
  };
@@ -606143,11 +606147,11 @@ function highlightBlock(code8, language) {
606143
606147
  return code8.split("\n");
606144
606148
  }
606145
606149
  }
606146
- var isTTY8, noColorEnv, disableEnv, _state2;
606150
+ var isTTY7, noColorEnv, disableEnv, _state2;
606147
606151
  var init_syntax_highlight = __esm({
606148
606152
  "packages/cli/src/tui/syntax-highlight.ts"() {
606149
606153
  "use strict";
606150
- isTTY8 = process.stdout?.isTTY ?? false;
606154
+ isTTY7 = process.stdout?.isTTY ?? false;
606151
606155
  noColorEnv = process.env["NO_COLOR"] !== void 0 && process.env["NO_COLOR"] !== "";
606152
606156
  disableEnv = process.env["OMNIUS_TUI_HIGHLIGHT"] === "0";
606153
606157
  _state2 = {
@@ -606160,21 +606164,21 @@ var init_syntax_highlight = __esm({
606160
606164
 
606161
606165
  // packages/cli/src/tui/stream-renderer.ts
606162
606166
  function fg2564(code8, text) {
606163
- return isTTY9 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
606167
+ return isTTY8 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
606164
606168
  }
606165
606169
  function dimText(text) {
606166
- return isTTY9 ? `\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
606170
+ return isTTY8 ? `\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
606167
606171
  }
606168
606172
  function italicText(text) {
606169
- return isTTY9 ? `\x1B[3m${text}\x1B[0m` : text;
606173
+ return isTTY8 ? `\x1B[3m${text}\x1B[0m` : text;
606170
606174
  }
606171
606175
  function dimItalic(text) {
606172
- return isTTY9 ? `\x1B[3m\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
606176
+ return isTTY8 ? `\x1B[3m\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
606173
606177
  }
606174
606178
  function boldText(text) {
606175
- return isTTY9 ? `\x1B[1m${text}\x1B[0m` : text;
606179
+ return isTTY8 ? `\x1B[1m${text}\x1B[0m` : text;
606176
606180
  }
606177
- var isTTY9, PASTEL, StreamRenderer;
606181
+ var isTTY8, PASTEL, StreamRenderer;
606178
606182
  var init_stream_renderer = __esm({
606179
606183
  "packages/cli/src/tui/stream-renderer.ts"() {
606180
606184
  "use strict";
@@ -606182,7 +606186,7 @@ var init_stream_renderer = __esm({
606182
606186
  init_text_selection();
606183
606187
  init_theme();
606184
606188
  init_syntax_highlight();
606185
- isTTY9 = process.stdout.isTTY ?? false;
606189
+ isTTY8 = process.stdout.isTTY ?? false;
606186
606190
  PASTEL = {
606187
606191
  key: 222,
606188
606192
  // light gold — JSON keys
@@ -606551,7 +606555,7 @@ var init_stream_renderer = __esm({
606551
606555
  * Also maintains _cursorCol so emitWrapped can decide when to force a
606552
606556
  * wrap on the NEXT partial flush (avoiding bottom-row token pile-up). */
606553
606557
  writeRaw(text) {
606554
- if (isTTY9) {
606558
+ if (isTTY8) {
606555
606559
  process.stdout.write(`\x1B[?25l\x1B[?7l${text}\x1B[?7h`);
606556
606560
  } else {
606557
606561
  process.stdout.write(text);
@@ -616349,6 +616353,34 @@ External acquisition contract:
616349
616353
  telegramRouterSessionState = /* @__PURE__ */ new Map();
616350
616354
  /** Telegram interaction routing profile */
616351
616355
  interactionMode = "auto";
616356
+ /**
616357
+ * Toggle for surfacing qwen3 `<think>` content streamed by Telegram-side
616358
+ * inferences (router, chat fast-path, follow-up). Mirrors the main TUI's
616359
+ * Ctrl+O thinking-visibility toggle but applies to the bridge's stream
616360
+ * surface (which has its own write path through tuiWrite + view
616361
+ * callbacks). Default off; flip via env `OMNIUS_TG_SHOW_THINKING=1` or
616362
+ * setTelegramThinkingVisible(). Independent of the model-side
616363
+ * `think:false` directive — that controls whether the model emits
616364
+ * thinking content at all; this controls whether the operator sees it
616365
+ * when it IS emitted.
616366
+ */
616367
+ telegramThinkingVisible = process.env["OMNIUS_TG_SHOW_THINKING"] === "1";
616368
+ /**
616369
+ * Live telemetry of every in-flight Ollama call originating from the
616370
+ * bridge. Lets the operator see WHY multiple GPUs are spun up at once
616371
+ * and HOW each call is progressing — which is the only way to debug a
616372
+ * 180s hard-deadline firing event without grepping logs.
616373
+ *
616374
+ * Each entry tracks:
616375
+ * - kind: router | chat-fast-path | followup | sub-agent
616376
+ * - sessionKey: which chat
616377
+ * - startTs: wall-clock start
616378
+ * - contentTokens / thinkingTokens: cumulative count from the stream
616379
+ * - lastTokenAt: timestamp of the most-recent chunk (staleness signal)
616380
+ * - model: the model being called (helps differentiate concurrent calls)
616381
+ */
616382
+ telegramActiveInferences = /* @__PURE__ */ new Map();
616383
+ telegramInferenceCounter = 0;
616352
616384
  /** Actual model context window discovered by the main TUI. */
616353
616385
  contextWindowSize = 0;
616354
616386
  _metricsProvider = null;
@@ -617982,16 +618014,21 @@ ${mediaContext}` : ""
617982
618014
  this.agentConfig.model,
617983
618015
  this.agentConfig.apiKey
617984
618016
  );
617985
- const result = await backend.chatCompletion(telegramThinkSuppressedRequest({
617986
- messages: [
617987
- { role: "system", content: "You are a Telegram public-follow-up discretion model. Output strict JSON only." },
617988
- { role: "user", content: prompt }
617989
- ],
617990
- tools: [],
617991
- temperature: 0.2,
617992
- maxTokens: 300,
617993
- timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 2e4)
617994
- }));
618017
+ const result = await this.telegramObservableInference(
618018
+ backend,
618019
+ telegramThinkSuppressedRequest({
618020
+ messages: [
618021
+ { role: "system", content: "You are a Telegram public-follow-up discretion model. Output strict JSON only." },
618022
+ { role: "user", content: prompt }
618023
+ ],
618024
+ tools: [],
618025
+ temperature: 0.2,
618026
+ maxTokens: 300,
618027
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 2e4)
618028
+ }),
618029
+ "followup",
618030
+ sessionKey
618031
+ );
617995
618032
  const decision2 = parseTelegramReflectionFollowupDecision(result.choices[0]?.message?.content ?? "");
617996
618033
  state.lastFollowupArtifactAt = artifact.generatedAt;
617997
618034
  if (!decision2) {
@@ -619612,15 +619649,17 @@ ${lines.join("\n")}`);
619612
619649
  nextAnalysisAfterMessages: decision2.nextCheckAfterMessages
619613
619650
  });
619614
619651
  }
619615
- async telegramRouterJsonCompletion(backend, request, diagnostics) {
619652
+ async telegramRouterJsonCompletion(backend, request, diagnostics, inferenceKind = "router", sessionKey = "__router__") {
619616
619653
  let jsonModeResult;
619617
619654
  let jsonModeError;
619618
619655
  const suppressed = telegramThinkSuppressedRequest(request);
619619
619656
  try {
619620
- jsonModeResult = await backend.chatCompletion({
619621
- ...suppressed,
619622
- responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
619623
- });
619657
+ jsonModeResult = await this.telegramObservableInference(
619658
+ backend,
619659
+ { ...suppressed, responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT },
619660
+ inferenceKind,
619661
+ sessionKey
619662
+ );
619624
619663
  const visible = jsonModeResult.choices.some(
619625
619664
  (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
619626
619665
  );
@@ -619637,7 +619676,12 @@ ${lines.join("\n")}`);
619637
619676
  }
619638
619677
  }
619639
619678
  try {
619640
- const plainResult = await backend.chatCompletion(suppressed);
619679
+ const plainResult = await this.telegramObservableInference(
619680
+ backend,
619681
+ suppressed,
619682
+ inferenceKind,
619683
+ sessionKey
619684
+ );
619641
619685
  if (diagnostics) {
619642
619686
  const plainVisible = plainResult.choices.some(
619643
619687
  (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
@@ -619654,6 +619698,205 @@ ${lines.join("\n")}`);
619654
619698
  throw err;
619655
619699
  }
619656
619700
  }
619701
+ // ─────────────────────────────────────────────────────────────────
619702
+ // Observable inference — streams chatCompletion-shaped calls so the
619703
+ // operator can SEE what's happening during a long-running router or
619704
+ // chat-fast-path call instead of waiting 180s for a hard-deadline.
619705
+ // ─────────────────────────────────────────────────────────────────
619706
+ /**
619707
+ * Wrap a chatCompletion-shaped call so the bridge can observe its token
619708
+ * stream and surface telemetry. Falls back to non-streaming if the
619709
+ * backend doesn't expose chatCompletionStream (older test stubs) or if
619710
+ * streaming throws. The returned shape matches chatCompletion exactly,
619711
+ * so callers don't have to know whether streaming was used.
619712
+ *
619713
+ * What this gives us:
619714
+ * 1. Per-call entry in the active-inferences registry (visible to the
619715
+ * operator — answers "why are 2 GPUs spun up at once?")
619716
+ * 2. Live emission of thinking + content tokens to the TUI when
619717
+ * telegramThinkingVisible is true (mirror of Ctrl+O for the bridge)
619718
+ * 3. Wall-clock observability — if the call hangs at 60s with zero
619719
+ * content tokens emitted, the registry shows it, and the
619720
+ * hard-deadline retire path becomes diagnosable instead of opaque
619721
+ */
619722
+ async telegramObservableInference(backend, request, kind, sessionKey) {
619723
+ const streamFn = backend.chatCompletionStream;
619724
+ const id = this.registerTelegramInference(kind, sessionKey, this.agentConfig?.model ?? "?");
619725
+ try {
619726
+ if (typeof streamFn !== "function") {
619727
+ const r2 = await backend.chatCompletion(request);
619728
+ this.updateTelegramInferenceFinal(id, r2);
619729
+ return r2;
619730
+ }
619731
+ try {
619732
+ const result = await this.streamTelegramInferenceToCompletion(
619733
+ streamFn.bind(backend),
619734
+ request,
619735
+ id
619736
+ );
619737
+ return result;
619738
+ } catch (streamErr) {
619739
+ const r2 = await backend.chatCompletion(request);
619740
+ this.updateTelegramInferenceFinal(id, r2);
619741
+ this.tuiWrite(() => renderTelegramSubAgentEvent(
619742
+ sessionKey,
619743
+ `inference ${id}: stream errored (${streamErr instanceof Error ? streamErr.message : String(streamErr)}); fell back to non-stream`
619744
+ ));
619745
+ return r2;
619746
+ }
619747
+ } finally {
619748
+ this.deregisterTelegramInference(id);
619749
+ }
619750
+ }
619751
+ /**
619752
+ * Drive a chatCompletionStream to exhaustion, accumulating tokens into a
619753
+ * chatCompletion-shaped result. Live-emits content + thinking tokens
619754
+ * through the TUI when telegramThinkingVisible is true, throttled to
619755
+ * avoid spamming the waterfall on fast streams.
619756
+ */
619757
+ async streamTelegramInferenceToCompletion(streamFn, request, inferenceId) {
619758
+ let contentBuf = "";
619759
+ let thinkingBuf = "";
619760
+ let finishReason;
619761
+ let usage;
619762
+ let lastEmitMs = 0;
619763
+ const EMIT_THROTTLE_MS = 500;
619764
+ const flushPreview = (force) => {
619765
+ if (!this.telegramThinkingVisible) return;
619766
+ const now = Date.now();
619767
+ if (!force && now - lastEmitMs < EMIT_THROTTLE_MS) return;
619768
+ lastEmitMs = now;
619769
+ const entry = this.telegramActiveInferences.get(inferenceId);
619770
+ if (!entry) return;
619771
+ const elapsed = ((performance.now() - entry.startTs) / 1e3).toFixed(1);
619772
+ const thinkRatio = entry.contentTokens + entry.thinkingTokens > 0 ? Math.round(entry.thinkingTokens * 100 / (entry.contentTokens + entry.thinkingTokens)) : 0;
619773
+ const preview = (thinkingBuf || contentBuf).slice(-180).replace(/\s+/g, " ");
619774
+ this.tuiWrite(() => renderTelegramSubAgentEvent(
619775
+ entry.sessionKey,
619776
+ `inference ${inferenceId} [${entry.kind}] ${elapsed}s content=${entry.contentTokens}t thinking=${entry.thinkingTokens}t (${thinkRatio}% think) live=${JSON.stringify(preview)}`
619777
+ ));
619778
+ };
619779
+ for await (const chunk of streamFn(request)) {
619780
+ if (chunk.type === "content" && chunk.content) {
619781
+ if (chunk.thinking) {
619782
+ thinkingBuf += chunk.content;
619783
+ this.bumpTelegramInferenceTokens(inferenceId, 0, 1);
619784
+ } else {
619785
+ contentBuf += chunk.content;
619786
+ this.bumpTelegramInferenceTokens(inferenceId, 1, 0);
619787
+ }
619788
+ flushPreview(false);
619789
+ } else if (chunk.type === "finish") {
619790
+ finishReason = chunk.finishReason;
619791
+ } else if (chunk.type === "usage") {
619792
+ usage = {
619793
+ prompt_tokens: chunk.promptTokens,
619794
+ completion_tokens: chunk.completionTokens,
619795
+ total_tokens: chunk.totalTokens
619796
+ };
619797
+ }
619798
+ }
619799
+ flushPreview(true);
619800
+ void finishReason;
619801
+ return {
619802
+ choices: [
619803
+ {
619804
+ message: {
619805
+ content: thinkingBuf ? `<think>${thinkingBuf}</think>${contentBuf}` : contentBuf
619806
+ }
619807
+ }
619808
+ ],
619809
+ usage: usage ? {
619810
+ totalTokens: usage.total_tokens ?? 0,
619811
+ promptTokens: usage.prompt_tokens,
619812
+ completionTokens: usage.completion_tokens
619813
+ } : void 0
619814
+ };
619815
+ }
619816
+ // ─────────────────────────────────────────────────────────────────
619817
+ // Inference telemetry registry
619818
+ // ─────────────────────────────────────────────────────────────────
619819
+ registerTelegramInference(kind, sessionKey, model) {
619820
+ const id = `inf-${++this.telegramInferenceCounter}`;
619821
+ const now = performance.now();
619822
+ this.telegramActiveInferences.set(id, {
619823
+ id,
619824
+ kind,
619825
+ sessionKey,
619826
+ model,
619827
+ startTs: now,
619828
+ lastTokenAt: now,
619829
+ contentTokens: 0,
619830
+ thinkingTokens: 0,
619831
+ streaming: true
619832
+ });
619833
+ return id;
619834
+ }
619835
+ bumpTelegramInferenceTokens(id, contentDelta, thinkingDelta) {
619836
+ const entry = this.telegramActiveInferences.get(id);
619837
+ if (!entry) return;
619838
+ entry.contentTokens += contentDelta;
619839
+ entry.thinkingTokens += thinkingDelta;
619840
+ entry.lastTokenAt = performance.now();
619841
+ }
619842
+ /**
619843
+ * Called when a non-streaming chatCompletion returns. Walks the completion
619844
+ * to extract a rough token count from the visible content so the registry
619845
+ * has SOME size signal even for non-streamed calls.
619846
+ */
619847
+ updateTelegramInferenceFinal(id, result) {
619848
+ const entry = this.telegramActiveInferences.get(id);
619849
+ if (!entry) return;
619850
+ entry.streaming = false;
619851
+ const text = result.choices[0]?.message?.content ?? "";
619852
+ const thinkMatch = text.match(/<think>([\s\S]*?)<\/think>/);
619853
+ const thinkingText = thinkMatch ? thinkMatch[1] : "";
619854
+ const contentText = thinkMatch ? text.replace(thinkMatch[0], "") : text;
619855
+ entry.thinkingTokens = Math.ceil(thinkingText.length / 4);
619856
+ entry.contentTokens = Math.ceil(contentText.length / 4);
619857
+ entry.lastTokenAt = performance.now();
619858
+ }
619859
+ deregisterTelegramInference(id) {
619860
+ const entry = this.telegramActiveInferences.get(id);
619861
+ if (!entry) return;
619862
+ this.telegramActiveInferences.delete(id);
619863
+ if (this.telegramThinkingVisible) {
619864
+ const dur = ((performance.now() - entry.startTs) / 1e3).toFixed(1);
619865
+ const totalTokens = entry.contentTokens + entry.thinkingTokens;
619866
+ const ratio = totalTokens > 0 ? Math.round(entry.thinkingTokens * 100 / totalTokens) : 0;
619867
+ this.tuiWrite(() => renderTelegramSubAgentEvent(
619868
+ entry.sessionKey,
619869
+ `inference ${id} [${entry.kind}] done in ${dur}s — ${entry.contentTokens}t content / ${entry.thinkingTokens}t thinking (${ratio}% think)`
619870
+ ));
619871
+ }
619872
+ }
619873
+ /**
619874
+ * Snapshot of every in-flight Telegram-originated inference. The TUI
619875
+ * dashboard / status line can call this to display "why are 2 GPUs spun
619876
+ * up?" — each entry includes the kind, session, model, elapsed seconds,
619877
+ * and token counts so the operator can correlate Ollama load to bridge
619878
+ * activity.
619879
+ */
619880
+ getTelegramActiveInferences() {
619881
+ const now = performance.now();
619882
+ return Array.from(this.telegramActiveInferences.values()).map((e2) => ({
619883
+ ...e2,
619884
+ elapsedSec: (now - e2.startTs) / 1e3,
619885
+ idleSec: (now - e2.lastTokenAt) / 1e3
619886
+ }));
619887
+ }
619888
+ /**
619889
+ * Toggle thinking visibility for the Telegram bridge. Mirrors the main
619890
+ * TUI's Ctrl+O semantics but applies to bridge-side streams. Returns the
619891
+ * new state so a binding can echo it back to the operator.
619892
+ */
619893
+ setTelegramThinkingVisible(visible) {
619894
+ this.telegramThinkingVisible = visible;
619895
+ return this.telegramThinkingVisible;
619896
+ }
619897
+ getTelegramThinkingVisible() {
619898
+ return this.telegramThinkingVisible;
619899
+ }
619657
619900
  async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs, diagnostics) {
619658
619901
  const rawPreview = telegramRouterRawPreview(rawOutput, 4e3);
619659
619902
  if (!rawPreview || telegramDecisionOutputHasDanglingJson(rawOutput)) {
@@ -620666,6 +620909,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
620666
620909
  }
620667
620910
  this.stopTelegramSubAgentWatchdog();
620668
620911
  this.cancelTelegramRouterSessionState("bridge stop");
620912
+ this.telegramActiveInferences.clear();
620669
620913
  if (this.telegramSqliteDb && this.telegramSqliteDb !== false) {
620670
620914
  try {
620671
620915
  this.telegramSqliteDb.close();
@@ -621482,35 +621726,55 @@ ${conversationStream}`
621482
621726
  });
621483
621727
  let accumulated = "";
621484
621728
  let streamError;
621729
+ const sessionKey = this.sessionKeyForMessage(msg);
621730
+ const inferenceId = this.registerTelegramInference("chat-fast-path", sessionKey, config.model);
621485
621731
  const streamable = backend;
621486
621732
  const stream = typeof streamable.chatCompletionStream === "function" ? streamable.chatCompletionStream(request) : null;
621487
- if (stream && typeof stream[Symbol.asyncIterator] === "function") {
621488
- try {
621489
- for await (const chunk of stream) {
621490
- if (chunk.type === "content" && !chunk.thinking && chunk.content) {
621491
- accumulated += chunk.content;
621492
- await onToken(accumulated);
621733
+ try {
621734
+ if (stream && typeof stream[Symbol.asyncIterator] === "function") {
621735
+ try {
621736
+ for await (const chunk of stream) {
621737
+ if (chunk.type !== "content") continue;
621738
+ const piece = chunk.content;
621739
+ if (!piece) continue;
621740
+ if (chunk.thinking) {
621741
+ this.bumpTelegramInferenceTokens(inferenceId, 0, 1);
621742
+ if (this.telegramThinkingVisible) {
621743
+ const preview = piece.slice(0, 120);
621744
+ this.tuiWrite(() => renderTelegramSubAgentEvent(
621745
+ msg.username,
621746
+ `chat-fast-path thinking: ${JSON.stringify(preview)}`
621747
+ ));
621748
+ }
621749
+ } else {
621750
+ this.bumpTelegramInferenceTokens(inferenceId, 1, 0);
621751
+ accumulated += piece;
621752
+ await onToken(accumulated);
621753
+ }
621493
621754
  }
621755
+ } catch (err) {
621756
+ streamError = err;
621757
+ accumulated = "";
621494
621758
  }
621495
- } catch (err) {
621496
- streamError = err;
621497
- accumulated = "";
621498
621759
  }
621499
- }
621500
- if (!accumulated.trim()) {
621501
- let result;
621502
- try {
621503
- result = await backend.chatCompletion(request);
621504
- } catch (err) {
621505
- if (streamError) {
621506
- const streamMsg = streamError instanceof Error ? streamError.message : String(streamError);
621507
- const retryMsg = err instanceof Error ? err.message : String(err);
621508
- throw new Error(`streaming failed (${streamMsg}); non-stream retry failed (${retryMsg})`);
621760
+ if (!accumulated.trim()) {
621761
+ let result;
621762
+ try {
621763
+ result = await backend.chatCompletion(request);
621764
+ } catch (err) {
621765
+ if (streamError) {
621766
+ const streamMsg = streamError instanceof Error ? streamError.message : String(streamError);
621767
+ const retryMsg = err instanceof Error ? err.message : String(err);
621768
+ throw new Error(`streaming failed (${streamMsg}); non-stream retry failed (${retryMsg})`);
621769
+ }
621770
+ throw err;
621509
621771
  }
621510
- throw err;
621772
+ this.updateTelegramInferenceFinal(inferenceId, result);
621773
+ accumulated = result.choices[0]?.message?.content ?? "";
621774
+ if (accumulated) await onToken(accumulated);
621511
621775
  }
621512
- accumulated = result.choices[0]?.message?.content ?? "";
621513
- if (accumulated) await onToken(accumulated);
621776
+ } finally {
621777
+ this.deregisterTelegramInference(inferenceId);
621514
621778
  }
621515
621779
  return stripTelegramHiddenThinking(accumulated).trim();
621516
621780
  }
@@ -621665,6 +621929,13 @@ ${conversationStream}`
621665
621929
  if (event.type === "stream_token" && event.streamKind === "content" && event.content) {
621666
621930
  subAgent.accumulated += event.content;
621667
621931
  }
621932
+ if (event.type === "stream_token" && event.streamKind === "thinking" && event.content && this.telegramThinkingVisible) {
621933
+ const trimmed = event.content.replace(/\s+/g, " ").slice(0, 200);
621934
+ this.subAgentViewCallbacks?.onWrite(
621935
+ subAgent.viewId,
621936
+ `thinking: ${trimmed}`
621937
+ );
621938
+ }
621668
621939
  const intermediateLine = formatTelegramProgressEvent(event);
621669
621940
  if (intermediateLine && (isAdminDM || event.type !== "status")) {
621670
621941
  subAgent.intermediateLines.push(intermediateLine);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.115",
3
+ "version": "1.0.117",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.115",
9
+ "version": "1.0.117",
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.115",
3
+ "version": "1.0.117",
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",