omnius 1.0.333 → 1.0.335

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
@@ -279541,19 +279541,26 @@ import { execFileSync as execFileSync6 } from "node:child_process";
279541
279541
  import { existsSync as existsSync42, readFileSync as readFileSync31, writeFileSync as writeFileSync21, mkdirSync as mkdirSync23, statfsSync as statfsSync3 } from "node:fs";
279542
279542
  import { join as join51 } from "node:path";
279543
279543
  import { cpus, totalmem as totalmem2 } from "node:os";
279544
+ function gpuComputeUsable(computeCap) {
279545
+ if (!computeCap)
279546
+ return true;
279547
+ const v = parseFloat(computeCap);
279548
+ return Number.isFinite(v) ? v >= MIN_COMPUTE_CAP : true;
279549
+ }
279544
279550
  function bytesToGiB(bytes) {
279545
279551
  return Math.round(bytes / 1024 ** 3 * 10) / 10;
279546
279552
  }
279547
279553
  function probeGpus() {
279548
279554
  try {
279549
- const out = execFileSync6("nvidia-smi", ["--query-gpu=index,name,memory.total,memory.free", "--format=csv,noheader,nounits"], { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] });
279555
+ const out = execFileSync6("nvidia-smi", ["--query-gpu=index,name,memory.total,memory.free,compute_cap", "--format=csv,noheader,nounits"], { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] });
279550
279556
  return out.trim().split("\n").map((line) => {
279551
- const [index, name10, total, free] = line.split(",").map((s2) => s2.trim());
279557
+ const [index, name10, total, free, cap] = line.split(",").map((s2) => s2.trim());
279552
279558
  return {
279553
279559
  index: parseInt(index || "0", 10),
279554
279560
  name: name10 || "GPU",
279555
279561
  memTotalMiB: parseInt(total || "0", 10),
279556
- memFreeMiB: parseInt(free || "0", 10)
279562
+ memFreeMiB: parseInt(free || "0", 10),
279563
+ computeCap: cap || ""
279557
279564
  };
279558
279565
  }).filter((g) => Number.isFinite(g.memTotalMiB) && g.memTotalMiB > 0);
279559
279566
  } catch {
@@ -279577,9 +279584,10 @@ function diskFreeGiB(path12) {
279577
279584
  }
279578
279585
  }
279579
279586
  function selectStack(facts) {
279580
- const usable = facts.gpus.filter((g) => g.memTotalMiB >= USABLE_GPU_MIN_MIB);
279587
+ const usable = facts.gpus.filter((g) => g.memTotalMiB >= USABLE_GPU_MIN_MIB && gpuComputeUsable(g.computeCap));
279581
279588
  const maxVramGiB = usable.length > 0 ? Math.round(Math.max(...usable.map((g) => g.memTotalMiB)) / 1024) : 0;
279582
279589
  const gpuCount = usable.length;
279590
+ const cudaVisibleDevices = usable.map((g) => g.index).join(",");
279583
279591
  if (gpuCount === 0) {
279584
279592
  return {
279585
279593
  stack: "edge",
@@ -279587,7 +279595,8 @@ function selectStack(facts) {
279587
279595
  precision: "fp32",
279588
279596
  vramGiB: 0,
279589
279597
  gpuCount: 0,
279590
- reason: `No usable GPU (>=8 GiB) detected; CPU edge stack on ${facts.cpuCores} cores / ${facts.ramGiB} GiB RAM.`
279598
+ cudaVisibleDevices: "",
279599
+ reason: `No usable GPU (>=8 GiB, compute>=7.0) detected; CPU edge stack on ${facts.cpuCores} cores / ${facts.ramGiB} GiB RAM.`
279591
279600
  };
279592
279601
  }
279593
279602
  const precision = maxVramGiB >= 24 ? "bf16" : "fp16";
@@ -279598,7 +279607,8 @@ function selectStack(facts) {
279598
279607
  precision,
279599
279608
  vramGiB: maxVramGiB,
279600
279609
  gpuCount,
279601
- reason: `Forensic stack: ${gpuCount} usable GPU(s), max ${maxVramGiB} GiB VRAM — multi-pass + thinking-variant LALM + high-res reinspection.`
279610
+ cudaVisibleDevices,
279611
+ reason: `Forensic stack: ${gpuCount} usable GPU(s) [${cudaVisibleDevices}], max ${maxVramGiB} GiB VRAM — multi-pass + thinking-variant LALM + high-res reinspection.`
279602
279612
  };
279603
279613
  }
279604
279614
  if (maxVramGiB >= 16) {
@@ -279608,7 +279618,8 @@ function selectStack(facts) {
279608
279618
  precision,
279609
279619
  vramGiB: maxVramGiB,
279610
279620
  gpuCount,
279611
- reason: `Baseline stack: one ${maxVramGiB} GiB GPU — V-JEPA2-L/VideoPrism + YOLOE+SAM2 + event-triggered 7B VLM/LALM.`
279621
+ cudaVisibleDevices,
279622
+ reason: `Baseline stack: one ${maxVramGiB} GiB GPU [${cudaVisibleDevices}] — V-JEPA2-L/VideoPrism + YOLOE+SAM2 + event-triggered 7B VLM/LALM.`
279612
279623
  };
279613
279624
  }
279614
279625
  return {
@@ -279617,7 +279628,8 @@ function selectStack(facts) {
279617
279628
  precision,
279618
279629
  vramGiB: maxVramGiB,
279619
279630
  gpuCount,
279620
- reason: `Edge stack on GPU: only ${maxVramGiB} GiB VRAM — keyframe detectors + lite encoders, small models on event windows.`
279631
+ cudaVisibleDevices,
279632
+ reason: `Edge stack on GPU [${cudaVisibleDevices}]: only ${maxVramGiB} GiB VRAM — keyframe detectors + lite encoders, small models on event windows.`
279621
279633
  };
279622
279634
  }
279623
279635
  function adapterPlacement(cap, role) {
@@ -279664,11 +279676,12 @@ function getMediaCapability(opts = {}) {
279664
279676
  }
279665
279677
  return cap;
279666
279678
  }
279667
- var USABLE_GPU_MIN_MIB, CACHE_FILE2, CACHE_TTL_MS2;
279679
+ var MIN_COMPUTE_CAP, USABLE_GPU_MIN_MIB, CACHE_FILE2, CACHE_TTL_MS2;
279668
279680
  var init_media_capability = __esm({
279669
279681
  "packages/execution/dist/av/media-capability.js"() {
279670
279682
  "use strict";
279671
279683
  init_model_store();
279684
+ MIN_COMPUTE_CAP = 7;
279672
279685
  USABLE_GPU_MIN_MIB = 8 * 1024;
279673
279686
  CACHE_FILE2 = "av-capability.json";
279674
279687
  CACHE_TTL_MS2 = 60 * 60 * 1e3;
@@ -553530,6 +553543,7 @@ __export(dist_exports2, {
553530
553543
  gigabytesToBytes: () => gigabytesToBytes,
553531
553544
  globalMediaDir: () => globalMediaDir,
553532
553545
  globalMediaRootDir: () => globalMediaRootDir,
553546
+ gpuComputeUsable: () => gpuComputeUsable,
553533
553547
  hasMaterialAmbiguity: () => hasMaterialAmbiguity,
553534
553548
  hashGeneratedArtifactContent: () => hashGeneratedArtifactContent,
553535
553549
  hashProcessCommand: () => hashProcessCommand,
@@ -599722,8 +599736,8 @@ function codePreviewLines(output, maxLines) {
599722
599736
  return [{ text: "(empty file)", mode: "wrap", kind: "dim" }];
599723
599737
  let hlLines = null;
599724
599738
  if (isAvailable()) {
599725
- const codeBlock = shown.join("\n");
599726
- const hl = highlightBlock(codeBlock);
599739
+ const codeBlock2 = shown.join("\n");
599740
+ const hl = highlightBlock(codeBlock2);
599727
599741
  if (hl.length === shown.length) hlLines = hl;
599728
599742
  }
599729
599743
  const body = shown.map((line, i2) => ({
@@ -610144,6 +610158,34 @@ var init_status_bar = __esm({
610144
610158
  unregisterDynamicBlock(id) {
610145
610159
  this._dynamicBlocks.delete(id);
610146
610160
  }
610161
+ /**
610162
+ * Expand every dynamic-block sentinel in `lines` into its registered render
610163
+ * output. Used at SESSION-SAVE time so the persisted transcript carries the
610164
+ * REAL tool / shell / task-complete content instead of `\x01DYNBLOCK:<id>\x01`
610165
+ * sentinels whose content otherwise lives only in this in-memory store and is
610166
+ * lost on reload. Stale/unknown sentinels are dropped (not left as dead
610167
+ * markers). ANSI is stripped by the persistence layer afterwards.
610168
+ */
610169
+ expandDynamicBlockSentinels(lines, width = 100) {
610170
+ const out = [];
610171
+ for (const line of lines) {
610172
+ if (typeof line === "string" && line.startsWith(this.DYNAMIC_BLOCK_MARK_PREFIX) && line.endsWith(this.DYNAMIC_BLOCK_MARK_SUFFIX) && line.length > this.DYNAMIC_BLOCK_MARK_PREFIX.length + this.DYNAMIC_BLOCK_MARK_SUFFIX.length) {
610173
+ const id = line.slice(
610174
+ this.DYNAMIC_BLOCK_MARK_PREFIX.length,
610175
+ line.length - this.DYNAMIC_BLOCK_MARK_SUFFIX.length
610176
+ );
610177
+ const renderer = this._dynamicBlocks.get(id);
610178
+ if (!renderer) continue;
610179
+ try {
610180
+ for (const seg of renderer(width)) out.push(seg);
610181
+ } catch {
610182
+ }
610183
+ } else {
610184
+ out.push(line);
610185
+ }
610186
+ }
610187
+ return out;
610188
+ }
610147
610189
  /**
610148
610190
  * Append a previously-registered dynamic block's sentinel to scrollback
610149
610191
  * and trigger a repaint. The block's renderer fires immediately at the
@@ -654909,6 +654951,140 @@ var init_emotion_engine = __esm({
654909
654951
  }
654910
654952
  });
654911
654953
 
654954
+ // packages/cli/src/tui/telegram-format.ts
654955
+ function escapeHtml2(text2) {
654956
+ return String(text2 ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
654957
+ }
654958
+ function toolCallBlock(name10, args) {
654959
+ const header = `<b>Tool call</b> <code>${escapeHtml2(name10 || "tool")}</code>`;
654960
+ if (args == null || typeof args === "object" && Object.keys(args).length === 0) {
654961
+ return header;
654962
+ }
654963
+ const compact3 = typeof args === "string" ? args : safeJson(args, false);
654964
+ if (compact3.length <= 120 && !compact3.includes("\n")) {
654965
+ return `${header} <code>${escapeHtml2(compact3)}</code>`;
654966
+ }
654967
+ const pretty = typeof args === "string" ? args : safeJson(args, true);
654968
+ return `${header}
654969
+ ${codeBlock(pretty, "json")}`;
654970
+ }
654971
+ function toolResultBlock(name10, ok3, output) {
654972
+ const header = `<b>Tool result</b> <code>${escapeHtml2(name10 || "tool")}</code>`;
654973
+ const body = String(output ?? "").trim();
654974
+ if (!body) return `${header}
654975
+ <i>${ok3 ? "completed" : "failed"}</i>`;
654976
+ if (body.length <= 160 && !body.includes("\n")) {
654977
+ return `${header}
654978
+ <blockquote>${escapeHtml2(body)}</blockquote>`;
654979
+ }
654980
+ return `${header}
654981
+ <blockquote expandable>${escapeHtml2(body)}</blockquote>`;
654982
+ }
654983
+ function codeBlock(code8, language = "") {
654984
+ const inner = escapeHtml2(String(code8 ?? "").replace(/\s+$/, ""));
654985
+ return language ? `<pre><code class="language-${escapeHtml2(language)}">${inner}</code></pre>` : `<pre>${inner}</pre>`;
654986
+ }
654987
+ function safeJson(v, pretty) {
654988
+ try {
654989
+ return pretty ? JSON.stringify(v, null, 2) : JSON.stringify(v);
654990
+ } catch {
654991
+ return String(v);
654992
+ }
654993
+ }
654994
+ function tokenizeTelegramHtml(html) {
654995
+ const tokens = [];
654996
+ const re = /<(\/?)([a-zA-Z][\w-]*)((?:\s[^>]*)?)>/g;
654997
+ let last2 = 0;
654998
+ let m2;
654999
+ while ((m2 = re.exec(html)) !== null) {
655000
+ if (m2.index > last2) tokens.push({ type: "text", raw: html.slice(last2, m2.index) });
655001
+ const closing = m2[1] === "/";
655002
+ const tag = (m2[2] || "").toLowerCase();
655003
+ if (SPAN_TAGS.has(tag)) {
655004
+ tokens.push(closing ? { type: "close", tag, raw: m2[0] } : { type: "open", tag, raw: m2[0] });
655005
+ } else {
655006
+ tokens.push({ type: "text", raw: m2[0] });
655007
+ }
655008
+ last2 = re.lastIndex;
655009
+ }
655010
+ if (last2 < html.length) tokens.push({ type: "text", raw: html.slice(last2) });
655011
+ return tokens;
655012
+ }
655013
+ function segmentTelegramHtml(html, maxLength = SAFE_LIMIT) {
655014
+ const text2 = String(html ?? "");
655015
+ if (text2.length <= maxLength) return text2.trim() ? [text2] : [];
655016
+ const tokens = tokenizeTelegramHtml(text2);
655017
+ const out = [];
655018
+ let cur = "";
655019
+ const stack = [];
655020
+ const closeAll = () => stack.slice().reverse().map((t2) => `</${t2.tag}>`).join("");
655021
+ const reopen = () => stack.map((t2) => t2.raw).join("");
655022
+ const closeLen = () => stack.reduce((n2, t2) => n2 + t2.tag.length + 3, 0);
655023
+ const flush3 = () => {
655024
+ const finished = cur + closeAll();
655025
+ if (finished.trim()) out.push(finished);
655026
+ cur = reopen();
655027
+ };
655028
+ for (const tok of tokens) {
655029
+ if (tok.type === "open") {
655030
+ if (cur.length + tok.raw.length + closeLen() > maxLength) flush3();
655031
+ cur += tok.raw;
655032
+ stack.push({ tag: tok.tag, raw: tok.raw });
655033
+ } else if (tok.type === "close") {
655034
+ cur += tok.raw;
655035
+ for (let i2 = stack.length - 1; i2 >= 0; i2--) {
655036
+ if (stack[i2].tag === tok.tag) {
655037
+ stack.splice(i2, 1);
655038
+ break;
655039
+ }
655040
+ }
655041
+ } else {
655042
+ let body = tok.raw;
655043
+ while (cur.length + body.length + closeLen() > maxLength) {
655044
+ const room = maxLength - cur.length - closeLen();
655045
+ if (room <= 1) {
655046
+ flush3();
655047
+ continue;
655048
+ }
655049
+ let bp = body.lastIndexOf("\n", room);
655050
+ if (bp < room * 0.5) bp = body.lastIndexOf(" ", room);
655051
+ if (bp < room * 0.5) bp = room;
655052
+ cur += body.slice(0, bp);
655053
+ flush3();
655054
+ body = body.slice(bp).replace(/^\s+/, "");
655055
+ }
655056
+ cur += body;
655057
+ }
655058
+ }
655059
+ const tail = cur + closeAll();
655060
+ if (tail.trim()) out.push(tail);
655061
+ return out;
655062
+ }
655063
+ var SAFE_LIMIT, SPAN_TAGS;
655064
+ var init_telegram_format = __esm({
655065
+ "packages/cli/src/tui/telegram-format.ts"() {
655066
+ "use strict";
655067
+ SAFE_LIMIT = 3900;
655068
+ SPAN_TAGS = /* @__PURE__ */ new Set([
655069
+ "b",
655070
+ "strong",
655071
+ "i",
655072
+ "em",
655073
+ "u",
655074
+ "ins",
655075
+ "s",
655076
+ "strike",
655077
+ "del",
655078
+ "code",
655079
+ "pre",
655080
+ "blockquote",
655081
+ "tg-spoiler",
655082
+ "a",
655083
+ "span"
655084
+ ]);
655085
+ }
655086
+ });
655087
+
654912
655088
  // packages/cli/src/tui/tool-policy.ts
654913
655089
  function getDefaultPolicy(context2) {
654914
655090
  switch (context2) {
@@ -660540,8 +660716,8 @@ function convertMarkdownToTelegramHTML(md) {
660540
660716
  let html = normalizeTelegramOutboundLinks(md);
660541
660717
  html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
660542
660718
  html = html.replace(
660543
- /```[\w]*\n([\s\S]*?)```/g,
660544
- (_m, code8) => `<pre>${code8.trimEnd()}</pre>`
660719
+ /```([\w+-]*)\n?([\s\S]*?)```/g,
660720
+ (_m, lang, code8) => lang ? `<pre><code class="language-${lang}">${code8.trimEnd()}</code></pre>` : `<pre>${code8.trimEnd()}</pre>`
660545
660721
  );
660546
660722
  html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
660547
660723
  html = html.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
@@ -660590,6 +660766,9 @@ function truncateTelegramUrlSafe(text2, maxLength, suffix = "...") {
660590
660766
  function splitTelegramMessageText(text2, maxLength = 3900) {
660591
660767
  const trimmed = text2.trim();
660592
660768
  if (!trimmed) return [];
660769
+ if (/<\/?[a-zA-Z][\w-]*(?:\s[^>]*)?>/.test(trimmed)) {
660770
+ return segmentTelegramHtml(trimmed, maxLength);
660771
+ }
660593
660772
  const chunks = [];
660594
660773
  let remaining = trimmed;
660595
660774
  while (remaining.length > maxLength) {
@@ -661038,20 +661217,12 @@ function formatTelegramProgressEvent(event) {
661038
661217
  if (event.type === "tool_result" && event.toolName === "task_complete")
661039
661218
  return null;
661040
661219
  if (event.type === "tool_call") {
661041
- return `<b>Tool call</b> <code>${escapeTelegramHTML(event.toolName || "tool")}</code>`;
661220
+ return toolCallBlock(event.toolName || "tool");
661042
661221
  }
661043
661222
  if (event.type === "tool_result") {
661044
- const preview = sanitizeTelegramProgressText(event.content || "", 180);
661223
+ const preview = sanitizeTelegramProgressText(event.content || "", 1500);
661045
661224
  if (isTelegramInternalStatusText(preview)) return null;
661046
- const toolName = escapeTelegramHTML(event.toolName || "tool");
661047
- if (preview) {
661048
- return [
661049
- `<b>Tool result</b> <code>${toolName}</code>`,
661050
- `<blockquote>${escapeTelegramHTML(preview)}</blockquote>`
661051
- ].join("\n");
661052
- }
661053
- return `<b>Tool result</b> <code>${toolName}</code>
661054
- <i>${event.success ? "completed" : "failed"}</i>`;
661225
+ return toolResultBlock(event.toolName || "tool", Boolean(event.success), preview);
661055
661226
  }
661056
661227
  if (event.type === "status") {
661057
661228
  if (isCodebaseMemoryStatus(event.content || "")) {
@@ -662165,6 +662336,7 @@ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTAT
662165
662336
  var init_telegram_bridge = __esm({
662166
662337
  "packages/cli/src/tui/telegram-bridge.ts"() {
662167
662338
  "use strict";
662339
+ init_telegram_format();
662168
662340
  init_dist8();
662169
662341
  init_dist5();
662170
662342
  init_project_context();
@@ -693946,6 +694118,64 @@ window.refreshTodos = refreshTodos;
693946
694118
  // own process group)
693947
694119
  // - Frontend re-attaches via this restore path and shows the partial
693948
694120
  // output via partial_output_tail in the in_flight payload
694121
+ // Parse a raw rendered-TUI transcript into clean GUI chat bubbles. Strips the
694122
+ // DYNBLOCK:<id> sentinels (no content in the persisted log) and TUI
694123
+ // chrome, then emits user (▹/>) and assistant (│) messages.
694124
+ function renderRecoveredTranscript(raw, conv, title) {
694125
+ const SOH = String.fromCharCode(1);
694126
+ const NL = String.fromCharCode(10);
694127
+ const sentRe = new RegExp(SOH + 'DYNBLOCK:[^' + SOH + ']*' + SOH, 'g');
694128
+ const lines = String(raw).replace(new RegExp(String.fromCharCode(13), 'g'), '').split(NL);
694129
+ let mode = null; // 'user' | 'assistant'
694130
+ let buf = [];
694131
+ let toolCount = 0;
694132
+ const out = []; // [{role, text}] or {tools:n}
694133
+ const flush = () => {
694134
+ const text = buf.join(NL).trim();
694135
+ if (text && mode) out.push({ role: mode, text: text });
694136
+ buf = [];
694137
+ };
694138
+ for (let line of lines) {
694139
+ // Count + drop dynamic-block sentinels (tool/shell/etc. — their content is
694140
+ // not in the log; surface them as a compact "tool activity" marker).
694141
+ const sentinels = (line.match(sentRe) || []).length;
694142
+ if (sentinels) toolCount += sentinels;
694143
+ line = line.replace(sentRe, '');
694144
+ const t = line.trim();
694145
+ if (!t) continue;
694146
+ // Skip pure box-border lines from expanded tool/task boxes (╭─╮ ╰─╯ etc.).
694147
+ if (/^[─-╿s]+$/.test(t)) continue;
694148
+ if (/^[▹>]s?/.test(t)) { // user prompt
694149
+ flush(); if (toolCount) { out.push({ tools: toolCount }); toolCount = 0; }
694150
+ out.push({ role: 'user', text: t.replace(/^[▹>]s?/, '') }); mode = null; continue;
694151
+ }
694152
+ if (/^│/.test(t)) { // assistant / box content
694153
+ if (mode !== 'assistant') { flush(); mode = 'assistant'; }
694154
+ buf.push(t.replace(/^│s?/, '').replace(/s?│$/, '')); continue; // strip both borders
694155
+ }
694156
+ if (/^[∙!]/.test(t)) continue; // status / internal noise — drop
694157
+ if (mode === 'assistant') buf.push(t); // continuation
694158
+ }
694159
+ flush(); if (toolCount) out.push({ tools: toolCount });
694160
+
694161
+ if (out.length === 0) return; // nothing meaningful — show nothing rather than garbage
694162
+ const note = document.createElement('div');
694163
+ note.style.cssText = 'font-size:0.6rem;color:var(--color-fg-faint);margin:4px 0 8px;text-align:center';
694164
+ note.textContent = 'recovered session' + (title ? ' — ' + title : '');
694165
+ conv.appendChild(note);
694166
+ for (const item of out) {
694167
+ if (item.tools) {
694168
+ const td = document.createElement('div');
694169
+ td.style.cssText = 'font-size:0.6rem;color:var(--color-fg-faint);margin:2px 0 6px;padding-left:8px';
694170
+ td.textContent = '⚙ ' + item.tools + ' tool action' + (item.tools === 1 ? '' : 's');
694171
+ conv.appendChild(td);
694172
+ } else {
694173
+ addMessage(item.role, item.text);
694174
+ }
694175
+ }
694176
+ }
694177
+ window.renderRecoveredTranscript = renderRecoveredTranscript;
694178
+
693949
694179
  async function restoreChatSession() {
693950
694180
  // chatSessionId is the window-property shim → $chatSessionId.get().
693951
694181
  // Project-scoped storage already hydrated this on $currentProject.set,
@@ -693982,18 +694212,15 @@ async function restoreChatSession() {
693982
694212
  // decisions, sub-agent activity — everything that scrolled past in the
693983
694213
  // TUI) as a visible, scrollable block at the top so opening the session
693984
694214
  // actually shows what happened, instead of just a "loaded" notice.
693985
- if (data.transcript && String(data.transcript).trim() && data.transcript !== '(empty transcript)') {
693986
- const wrap = document.createElement('div');
693987
- wrap.style.cssText = 'margin:6px 0 14px';
693988
- const head = document.createElement('div');
693989
- head.style.cssText = 'font-size:0.66rem;color:var(--color-fg-muted);margin-bottom:4px;display:flex;justify-content:space-between;align-items:center';
693990
- head.innerHTML = '<span>Recovered session transcript' + (data.title ? ' — ' + escapeHtml(data.title) : '') + '</span>';
693991
- const pre = document.createElement('pre');
693992
- pre.style.cssText = 'max-height:380px;overflow:auto;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);padding:10px 12px;font-size:0.7rem;line-height:1.35;white-space:pre-wrap;word-break:break-word;color:var(--color-fg-muted);margin:0';
693993
- pre.textContent = String(data.transcript);
693994
- wrap.appendChild(head);
693995
- wrap.appendChild(pre);
693996
- conv.appendChild(wrap);
694215
+ if (data.transcript && String(data.transcript).trim() && data.transcript !== '(empty transcript)'
694216
+ && (!allMessages || allMessages.length === 0)) {
694217
+ // TUI/raw transcript: PARSE the rendered-TUI scrollback into real chat
694218
+ // bubbles instead of dumping the raw log. The persisted log carries
694219
+ // DYNBLOCK:<id> sentinels (dynamic blocks expanded only at TUI
694220
+ // paint time no content here) plus chrome ( user, assistant,
694221
+ // status, ! internal). We strip the dead sentinels + noise and emit
694222
+ // user/assistant messages so it reads like a GUI chat.
694223
+ renderRecoveredTranscript(String(data.transcript), conv, data.title || '');
693997
694224
  }
693998
694225
  // WO-CHAT-RESUME-TOOLS — replay the FULL intermediate flow on
693999
694226
  // restore: user/assistant text bubbles AND tool_call/tool_result
@@ -711978,7 +712205,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
711978
712205
  }
711979
712206
  try {
711980
712207
  const historySessionId = process.env["OMNIUS_SESSION_ID"] || `session-${Date.now().toString(36)}`;
711981
- const contentLines = statusBar?._contentLines ?? [];
712208
+ const rawContentLines = statusBar?._contentLines ?? [];
712209
+ const contentLines = typeof statusBar?.expandDynamicBlockSentinels === "function" ? statusBar.expandDynamicBlockSentinels(rawContentLines, 100) : rawContentLines;
711982
712210
  if (contentLines.length > 0) {
711983
712211
  const description = cleanPromptForDiary(task).slice(0, 240);
711984
712212
  const historyTitle = description.slice(0, 80) || void 0;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.333",
3
+ "version": "1.0.335",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.333",
9
+ "version": "1.0.335",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
@@ -3986,9 +3986,9 @@
3986
3986
  }
3987
3987
  },
3988
3988
  "node_modules/hono": {
3989
- "version": "4.12.26",
3990
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.26.tgz",
3991
- "integrity": "sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==",
3989
+ "version": "4.12.27",
3990
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz",
3991
+ "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==",
3992
3992
  "license": "MIT",
3993
3993
  "engines": {
3994
3994
  "node": ">=16.9.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.333",
3
+ "version": "1.0.335",
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",