omnius 1.0.334 → 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
@@ -599736,8 +599736,8 @@ function codePreviewLines(output, maxLines) {
599736
599736
  return [{ text: "(empty file)", mode: "wrap", kind: "dim" }];
599737
599737
  let hlLines = null;
599738
599738
  if (isAvailable()) {
599739
- const codeBlock = shown.join("\n");
599740
- const hl = highlightBlock(codeBlock);
599739
+ const codeBlock2 = shown.join("\n");
599740
+ const hl = highlightBlock(codeBlock2);
599741
599741
  if (hl.length === shown.length) hlLines = hl;
599742
599742
  }
599743
599743
  const body = shown.map((line, i2) => ({
@@ -610158,6 +610158,34 @@ var init_status_bar = __esm({
610158
610158
  unregisterDynamicBlock(id) {
610159
610159
  this._dynamicBlocks.delete(id);
610160
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
+ }
610161
610189
  /**
610162
610190
  * Append a previously-registered dynamic block's sentinel to scrollback
610163
610191
  * and trigger a repaint. The block's renderer fires immediately at the
@@ -654923,6 +654951,140 @@ var init_emotion_engine = __esm({
654923
654951
  }
654924
654952
  });
654925
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
+
654926
655088
  // packages/cli/src/tui/tool-policy.ts
654927
655089
  function getDefaultPolicy(context2) {
654928
655090
  switch (context2) {
@@ -660554,8 +660716,8 @@ function convertMarkdownToTelegramHTML(md) {
660554
660716
  let html = normalizeTelegramOutboundLinks(md);
660555
660717
  html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
660556
660718
  html = html.replace(
660557
- /```[\w]*\n([\s\S]*?)```/g,
660558
- (_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>`
660559
660721
  );
660560
660722
  html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
660561
660723
  html = html.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
@@ -660604,6 +660766,9 @@ function truncateTelegramUrlSafe(text2, maxLength, suffix = "...") {
660604
660766
  function splitTelegramMessageText(text2, maxLength = 3900) {
660605
660767
  const trimmed = text2.trim();
660606
660768
  if (!trimmed) return [];
660769
+ if (/<\/?[a-zA-Z][\w-]*(?:\s[^>]*)?>/.test(trimmed)) {
660770
+ return segmentTelegramHtml(trimmed, maxLength);
660771
+ }
660607
660772
  const chunks = [];
660608
660773
  let remaining = trimmed;
660609
660774
  while (remaining.length > maxLength) {
@@ -661052,20 +661217,12 @@ function formatTelegramProgressEvent(event) {
661052
661217
  if (event.type === "tool_result" && event.toolName === "task_complete")
661053
661218
  return null;
661054
661219
  if (event.type === "tool_call") {
661055
- return `<b>Tool call</b> <code>${escapeTelegramHTML(event.toolName || "tool")}</code>`;
661220
+ return toolCallBlock(event.toolName || "tool");
661056
661221
  }
661057
661222
  if (event.type === "tool_result") {
661058
- const preview = sanitizeTelegramProgressText(event.content || "", 180);
661223
+ const preview = sanitizeTelegramProgressText(event.content || "", 1500);
661059
661224
  if (isTelegramInternalStatusText(preview)) return null;
661060
- const toolName = escapeTelegramHTML(event.toolName || "tool");
661061
- if (preview) {
661062
- return [
661063
- `<b>Tool result</b> <code>${toolName}</code>`,
661064
- `<blockquote>${escapeTelegramHTML(preview)}</blockquote>`
661065
- ].join("\n");
661066
- }
661067
- return `<b>Tool result</b> <code>${toolName}</code>
661068
- <i>${event.success ? "completed" : "failed"}</i>`;
661225
+ return toolResultBlock(event.toolName || "tool", Boolean(event.success), preview);
661069
661226
  }
661070
661227
  if (event.type === "status") {
661071
661228
  if (isCodebaseMemoryStatus(event.content || "")) {
@@ -662179,6 +662336,7 @@ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTAT
662179
662336
  var init_telegram_bridge = __esm({
662180
662337
  "packages/cli/src/tui/telegram-bridge.ts"() {
662181
662338
  "use strict";
662339
+ init_telegram_format();
662182
662340
  init_dist8();
662183
662341
  init_dist5();
662184
662342
  init_project_context();
@@ -693960,6 +694118,64 @@ window.refreshTodos = refreshTodos;
693960
694118
  // own process group)
693961
694119
  // - Frontend re-attaches via this restore path and shows the partial
693962
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
+
693963
694179
  async function restoreChatSession() {
693964
694180
  // chatSessionId is the window-property shim → $chatSessionId.get().
693965
694181
  // Project-scoped storage already hydrated this on $currentProject.set,
@@ -693996,18 +694212,15 @@ async function restoreChatSession() {
693996
694212
  // decisions, sub-agent activity — everything that scrolled past in the
693997
694213
  // TUI) as a visible, scrollable block at the top so opening the session
693998
694214
  // actually shows what happened, instead of just a "loaded" notice.
693999
- if (data.transcript && String(data.transcript).trim() && data.transcript !== '(empty transcript)') {
694000
- const wrap = document.createElement('div');
694001
- wrap.style.cssText = 'margin:6px 0 14px';
694002
- const head = document.createElement('div');
694003
- head.style.cssText = 'font-size:0.66rem;color:var(--color-fg-muted);margin-bottom:4px;display:flex;justify-content:space-between;align-items:center';
694004
- head.innerHTML = '<span>Recovered session transcript' + (data.title ? ' — ' + escapeHtml(data.title) : '') + '</span>';
694005
- const pre = document.createElement('pre');
694006
- 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';
694007
- pre.textContent = String(data.transcript);
694008
- wrap.appendChild(head);
694009
- wrap.appendChild(pre);
694010
- 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 || '');
694011
694224
  }
694012
694225
  // WO-CHAT-RESUME-TOOLS — replay the FULL intermediate flow on
694013
694226
  // restore: user/assistant text bubbles AND tool_call/tool_result
@@ -711992,7 +712205,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
711992
712205
  }
711993
712206
  try {
711994
712207
  const historySessionId = process.env["OMNIUS_SESSION_ID"] || `session-${Date.now().toString(36)}`;
711995
- const contentLines = statusBar?._contentLines ?? [];
712208
+ const rawContentLines = statusBar?._contentLines ?? [];
712209
+ const contentLines = typeof statusBar?.expandDynamicBlockSentinels === "function" ? statusBar.expandDynamicBlockSentinels(rawContentLines, 100) : rawContentLines;
711996
712210
  if (contentLines.length > 0) {
711997
712211
  const description = cleanPromptForDiary(task).slice(0, 240);
711998
712212
  const historyTitle = description.slice(0, 80) || void 0;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.334",
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.334",
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.334",
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",