omnius 1.0.169 → 1.0.171

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
@@ -546736,14 +546736,30 @@ function estimateTokens2(text) {
546736
546736
  function normalizeSignalContent(content) {
546737
546737
  return content.replace(/\r\n/g, "\n").split("\n").map((line) => line.trimEnd()).join("\n").trim();
546738
546738
  }
546739
+ function urlSpanAt(text, index) {
546740
+ URL_RE.lastIndex = 0;
546741
+ for (const match of text.matchAll(URL_RE)) {
546742
+ const start2 = match.index ?? 0;
546743
+ const end = start2 + match[0].length;
546744
+ if (start2 < index && index < end)
546745
+ return { start: start2, end };
546746
+ }
546747
+ return null;
546748
+ }
546739
546749
  function truncateText2(text, maxChars) {
546740
546750
  if (text.length <= maxChars)
546741
546751
  return { text, truncated: false };
546742
546752
  if (maxChars <= 40)
546743
546753
  return { text: text.slice(0, maxChars), truncated: true };
546754
+ const suffix = CONTEXT_FABRIC_TRUNCATION_SUFFIX;
546755
+ let cutIndex = maxChars - suffix.length;
546756
+ const urlSpan = urlSpanAt(text, cutIndex);
546757
+ if (urlSpan) {
546758
+ cutIndex = urlSpan.start;
546759
+ }
546760
+ const prefix = text.slice(0, Math.max(0, cutIndex)).trimEnd();
546744
546761
  return {
546745
- text: `${text.slice(0, maxChars - 36).trimEnd()}
546746
- ... [truncated by context fabric]`,
546762
+ text: `${prefix}${suffix}`,
546747
546763
  truncated: true
546748
546764
  };
546749
546765
  }
@@ -546812,7 +546828,7 @@ function signalFromBlock(kind, source, content, options2 = {}) {
546812
546828
  metadata: options2.metadata
546813
546829
  };
546814
546830
  }
546815
- var KIND_ORDER, KIND_LABELS, DEFAULT_KIND_CHAR_BUDGET, ContextLedger, ContextFrameBuilder;
546831
+ var KIND_ORDER, KIND_LABELS, DEFAULT_KIND_CHAR_BUDGET, URL_RE, CONTEXT_FABRIC_TRUNCATION_SUFFIX, ContextLedger, ContextFrameBuilder;
546816
546832
  var init_context_fabric = __esm({
546817
546833
  "packages/orchestrator/dist/context-fabric.js"() {
546818
546834
  "use strict";
@@ -546864,6 +546880,8 @@ var init_context_fabric = __esm({
546864
546880
  environment: 900,
546865
546881
  compaction_summary: 1200
546866
546882
  };
546883
+ URL_RE = /\bhttps?:\/\/[^\s<>"'()[\]{}]+/gi;
546884
+ CONTEXT_FABRIC_TRUNCATION_SUFFIX = "\n... [truncated by context fabric]";
546867
546885
  ContextLedger = class {
546868
546886
  signals = /* @__PURE__ */ new Map();
546869
546887
  sequence = 0;
@@ -592291,7 +592309,7 @@ function extractMediaReferences(text) {
592291
592309
  const offset = match[0].indexOf("MEDIA:");
592292
592310
  addCandidate(value2, match[0].slice(offset), match.index + offset, match.index + match[0].length);
592293
592311
  }
592294
- for (const match of matchAll(URL_RE, text)) {
592312
+ for (const match of matchAll(URL_RE2, text)) {
592295
592313
  const original = trimTrailingPunctuation(match[0]);
592296
592314
  if (original.length !== match[0].length) {
592297
592315
  addCandidate(original, original, match.index, match.index + original.length);
@@ -592415,7 +592433,7 @@ function decodeUri(value2) {
592415
592433
  function trimTrailingPunctuation(value2) {
592416
592434
  return value2.replace(/[),.;:!?]+$/, "");
592417
592435
  }
592418
- var IMAGE_EXT, AUDIO_EXT, VIDEO_EXT, DOC_EXT, MEDIA_DIRECTIVE_RE, AUDIO_AS_VOICE_RE, MARKDOWN_IMAGE_RE, HTML_IMG_RE, URL_RE, LOCAL_PATH_RE;
592436
+ var IMAGE_EXT, AUDIO_EXT, VIDEO_EXT, DOC_EXT, MEDIA_DIRECTIVE_RE, AUDIO_AS_VOICE_RE, MARKDOWN_IMAGE_RE, HTML_IMG_RE, URL_RE2, LOCAL_PATH_RE;
592419
592437
  var init_media_routing = __esm({
592420
592438
  "packages/cli/src/tui/media-routing.ts"() {
592421
592439
  "use strict";
@@ -592447,7 +592465,7 @@ var init_media_routing = __esm({
592447
592465
  AUDIO_AS_VOICE_RE = /\[\[audio_as_voice\]\]/gi;
592448
592466
  MARKDOWN_IMAGE_RE = /!\[[^\]]*]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
592449
592467
  HTML_IMG_RE = /<img\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;
592450
- URL_RE = /\bhttps?:\/\/[^\s<>"')]+/gi;
592468
+ URL_RE2 = /\bhttps?:\/\/[^\s<>"')]+/gi;
592451
592469
  LOCAL_PATH_RE = /(?:^|[\s([])((?:~|\/)[^\s<>"')\]]+\.(?:png|jpe?g|webp|gif|bmp|tiff?|svg|wav|mp3|ogg|oga|opus|m4a|flac|aac|mp4|mov|mkv|webm|avi|m4v|pdf|txt|md|markdown|csv|tsv|jsonl?|xlsx?|docx?|pptx?|html?|xml|zip|tar|gz))(?:$|[\s),.\]])/gim;
592452
592470
  }
592453
592471
  });
@@ -615859,9 +615877,10 @@ function buildRealtimeSystemPrompt(opts) {
615859
615877
  `- Produce one natural spoken turn, normally ${maxReplyWords} words or fewer.`,
615860
615878
  "- Use one sentence when possible; two short sentences only when repair or confirmation needs it.",
615861
615879
  "- Lead with the answer. Do not preface with status, analysis, summaries, or implementation narration.",
615862
- "- No markdown, bullets, tables, headings, citations, code blocks, JSON, or labels like 'Assistant:'.",
615880
+ "- No markdown, bullets, tables, headings, citations, inline code, code blocks, JSON, or labels like 'Assistant:'.",
615863
615881
  "- Sound like a person on a live call: brief acknowledgment, direct answer, one focused follow-up only if needed.",
615864
615882
  "- If the ASR text is garbled or underspecified, ask a single compact repair question.",
615883
+ "- Do not invent app modes, method names, settings, or implementation details when the caller has not supplied them.",
615865
615884
  "- Do not mention ASR, TTS, prompts, realtime mode, hidden reasoning, tools, or policy unless the caller explicitly asks.",
615866
615885
  "- If a request needs work outside this text-only exchange, say the next handoff in one short sentence."
615867
615886
  ].join("\n"),
@@ -615952,7 +615971,7 @@ function wordParts(text) {
615952
615971
  }
615953
615972
  function finalizeRealtimeReply(text, opts = {}) {
615954
615973
  const maxWords = clampInt2(opts.maxReplyWords, DEFAULT_REALTIME_MAX_REPLY_WORDS, 8, 80);
615955
- let clean5 = stripHiddenThinking(String(text ?? "")).replace(/```[\s\S]*?```/g, "").split("\n").map((line) => line.replace(/^\s*(?:[-*]+|\d+[.)])\s+/, "").trim()).filter(Boolean).join(" ").replace(/^(?:assistant|omnius|agent)\s*:\s*/i, "").replace(/\s+/g, " ").trim();
615974
+ let clean5 = stripHiddenThinking(String(text ?? "")).replace(/```[\s\S]*?```/g, "").split("\n").map((line) => line.replace(/^\s*(?:[-*]+|\d+[.)])\s+/, "").trim()).filter(Boolean).join(" ").replace(/^(?:assistant|omnius|agent)\s*:\s*/i, "").replace(/`([^`]+)`/g, "$1").replace(/\s+/g, " ").trim();
615956
615975
  if (!clean5) return "I didn't catch that. Can you say it again?";
615957
615976
  const sentences = clean5.match(/[^.!?]+[.!?]+(?=\s|$)|[^.!?]+$/g) ?? [clean5];
615958
615977
  const selected = [];
@@ -627419,7 +627438,7 @@ function parseTelegramReflectionFollowupDecision(text) {
627419
627438
  }
627420
627439
  }
627421
627440
  function convertMarkdownToTelegramHTML(md) {
627422
- let html = md;
627441
+ let html = normalizeTelegramOutboundLinks(md);
627423
627442
  html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
627424
627443
  html = html.replace(
627425
627444
  /```[\w]*\n([\s\S]*?)```/g,
@@ -627439,6 +627458,13 @@ function convertMarkdownToTelegramHTML(md) {
627439
627458
  function escapeTelegramHTML(text) {
627440
627459
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
627441
627460
  }
627461
+ function normalizeTelegramOutboundLinks(text) {
627462
+ if (!text) return text;
627463
+ return text.replace(
627464
+ TELEGRAM_SPACED_URL_RE,
627465
+ (match) => match.replace(/^(https?:\/\/)\s*/i, "$1").replace(/\s*\.\s*/g, ".").replace(/\s*:\s*(\d+)/g, ":$1")
627466
+ );
627467
+ }
627442
627468
  function splitTelegramMessageText(text, maxLength = 3900) {
627443
627469
  const trimmed = text.trim();
627444
627470
  if (!trimmed) return [];
@@ -627544,7 +627570,7 @@ function stripTelegramStuckSelfTalk(text) {
627544
627570
  return kept.join("\n\n");
627545
627571
  }
627546
627572
  function cleanTelegramVisibleReply(text, options2 = {}) {
627547
- const clean5 = stripTelegramHiddenThinking(text).trim();
627573
+ const clean5 = normalizeTelegramOutboundLinks(stripTelegramHiddenThinking(text)).trim();
627548
627574
  if (!clean5) return "";
627549
627575
  if (options2.suppressPotentialNoReplyPrefix && isTelegramPotentialNoReplyPrefix(clean5)) return "";
627550
627576
  if (isTelegramInternalStatusText(clean5)) return "";
@@ -628621,7 +628647,7 @@ function renderTelegramSubAgentError(username, error) {
628621
628647
  process.stdout.write(` ${c3.dim("│")} ${c3.magenta("✘")} @${username}: ${c3.dim(preview)}
628622
628648
  `);
628623
628649
  }
628624
- var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT, TELEGRAM_INTERACTION_DECISION_MINIMAL_SCHEMA, TELEGRAM_INTERACTION_DECISION_REPAIR_SCHEMA, TELEGRAM_CHAT_REPLY_RESPONSE_FORMAT, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_SUB_AGENT_DEFAULT_LIMIT, TELEGRAM_SUB_AGENT_MAX_LIMIT, TELEGRAM_SUB_AGENT_BURST_CONTEXT_LIMIT, TELEGRAM_PUBLIC_HELP_COMMANDS2, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_ALLOWED_UPDATES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
628650
+ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_LINK_INTEGRITY_CONTRACT, TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT, TELEGRAM_INTERACTION_DECISION_MINIMAL_SCHEMA, TELEGRAM_INTERACTION_DECISION_REPAIR_SCHEMA, TELEGRAM_CHAT_REPLY_RESPONSE_FORMAT, TELEGRAM_SPACED_URL_RE, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_SUB_AGENT_DEFAULT_LIMIT, TELEGRAM_SUB_AGENT_MAX_LIMIT, TELEGRAM_SUB_AGENT_BURST_CONTEXT_LIMIT, TELEGRAM_PUBLIC_HELP_COMMANDS2, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_ALLOWED_UPDATES, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
628625
628651
  var init_telegram_bridge = __esm({
628626
628652
  "packages/cli/src/tui/telegram-bridge.ts"() {
628627
628653
  "use strict";
@@ -628889,6 +628915,13 @@ External acquisition contract:
628889
628915
  - Treat repeated invalid downloads (saved HTML/JSON/login pages instead of the expected file type) as the same blocker; do not retry past a small bounded number of attempts.
628890
628916
  - The browser tool is for interactive web work; it does not save arbitrary rendered files to disk. For file acquisition, use the download/file tool and verify content-type + size before reporting success.
628891
628917
  - Report the exact blocker concisely and offer lawful alternatives (library hold, official store, summary/discussion if the user supplies an authorized copy).
628918
+ `.trim();
628919
+ TELEGRAM_LINK_INTEGRITY_CONTRACT = `
628920
+ Telegram link integrity contract:
628921
+ - When sending public http(s) URLs, copy the URL exactly from source/tool output.
628922
+ - Never defang, space out, redact, or rewrite dots, slashes, host labels, or TLDs in normal public URLs.
628923
+ - Prefer Markdown links when helpful, but keep the href URL contiguous and parseable.
628924
+ - If you are uncertain a URL is real, say so or omit it instead of reconstructing a broken URL.
628892
628925
  `.trim();
628893
628926
  TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT = {
628894
628927
  type: "json_object"
@@ -628913,6 +628946,7 @@ External acquisition contract:
628913
628946
  }
628914
628947
  }
628915
628948
  };
628949
+ TELEGRAM_SPACED_URL_RE = /\bhttps?:\/\/\s*[A-Za-z0-9-]+(?:\s*\.\s*[A-Za-z0-9-]+)+(?:\s*:\s*\d+)?(?:[/?#][^\s<>"'()[\]{}]*)?/gi;
628916
628950
  TELEGRAM_STUCK_SELF_TALK_PREFIXES = [
628917
628951
  /^i'?ve been stuck for\b/i,
628918
628952
  /^i am (still |currently )?stuck\b/i,
@@ -634268,7 +634302,8 @@ Todo/session id: ${sessionId}
634268
634302
  Profile: ${profile}
634269
634303
  Tool context: ${toolContext}
634270
634304
  ${chatLabel}`,
634271
- TELEGRAM_ACTION_RESPONSE_CONTRACT
634305
+ TELEGRAM_ACTION_RESPONSE_CONTRACT,
634306
+ TELEGRAM_LINK_INTEGRITY_CONTRACT
634272
634307
  ];
634273
634308
  sections.push(conversationStream);
634274
634309
  const sendTargetContext = this.buildTelegramSendTargetContext(msg, isAdminDM);
@@ -634637,10 +634672,11 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
634637
634672
  * streamed content. Returns the message_id for subsequent edits.
634638
634673
  */
634639
634674
  async sendLiveMessage(chatId, initialText, replyToMessageId) {
634675
+ const normalizedInitialText = normalizeTelegramOutboundLinks(initialText);
634640
634676
  try {
634641
634677
  const body = {
634642
634678
  chat_id: chatId,
634643
- text: initialText,
634679
+ text: normalizedInitialText,
634644
634680
  parse_mode: "HTML"
634645
634681
  };
634646
634682
  const replyParameters = telegramReplyParameters(replyToMessageId);
@@ -634657,7 +634693,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
634657
634693
  try {
634658
634694
  const body = {
634659
634695
  chat_id: chatId,
634660
- text: initialText
634696
+ text: normalizedInitialText
634661
634697
  };
634662
634698
  const replyParameters = telegramReplyParameters(replyToMessageId);
634663
634699
  if (replyParameters) body["reply_parameters"] = replyParameters;
@@ -634692,7 +634728,8 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
634692
634728
  * Handles rate limits and Telegram's "message is not modified" error silently.
634693
634729
  */
634694
634730
  async editLiveMessage(chatId, messageId, html) {
634695
- const truncated = html.length > 4e3 ? html.slice(0, 3950) + "\n\n<i>... (streaming)</i>" : html;
634731
+ const normalizedHtml = normalizeTelegramOutboundLinks(html);
634732
+ const truncated = normalizedHtml.length > 4e3 ? normalizedHtml.slice(0, 3950) + "\n\n<i>... (streaming)</i>" : normalizedHtml;
634696
634733
  try {
634697
634734
  const result = await this.apiCall("editMessageText", {
634698
634735
  chat_id: chatId,
@@ -634705,7 +634742,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
634705
634742
  return true;
634706
634743
  } catch {
634707
634744
  try {
634708
- const plain = html.replace(/<[^>]+>/g, "");
634745
+ const plain = normalizedHtml.replace(/<[^>]+>/g, "");
634709
634746
  const truncPlain = plain.length > 4e3 ? plain.slice(0, 3950) + "\n\n... (streaming)" : plain;
634710
634747
  const result = await this.apiCall("editMessageText", {
634711
634748
  chat_id: chatId,
@@ -635404,6 +635441,8 @@ ${TELEGRAM_PUBLIC_VISION_STACK_CONTRACT}`;
635404
635441
  role: "system",
635405
635442
  content: `${TELEGRAM_CHAT_MODE_PROMPT}
635406
635443
 
635444
+ ${TELEGRAM_LINK_INTEGRITY_CONTRACT}
635445
+
635407
635446
  ## Runtime Context
635408
635447
 
635409
635448
  ${runtime}
@@ -635729,6 +635768,8 @@ ${GROUP_REPLY_DISCRETION_PROMPT}` : "";
635729
635768
 
635730
635769
  ${TELEGRAM_ACTION_RESPONSE_CONTRACT}
635731
635770
 
635771
+ ${TELEGRAM_LINK_INTEGRITY_CONTRACT}
635772
+
635732
635773
  ${TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT}
635733
635774
 
635734
635775
  ${reminderToolContract}
@@ -635741,6 +635782,7 @@ ${currentTelegramPrompt}`;
635741
635782
  "You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
635742
635783
  "memory_search may use scope=group/current_chat for this group or scope=user with user_id/username for a participant in this same group. Other groups, admin chats, and private DMs are not accessible here.",
635743
635784
  "You can remember facts about users and retrieve them later. Durable associative memory in the prompt includes participant profiles, relationships, scoped facts, and prior actions retained across days, sessions, and Omnius updates. You also have web_search and web_fetch to look up information.",
635785
+ TELEGRAM_LINK_INTEGRITY_CONTRACT,
635744
635786
  "If a user explicitly states a durable preference for reply cadence/order, call telegram_preference_set. Do not infer or classify reply-mode preferences from keywords, style, tone, or task type.",
635745
635787
  "You have the full scoped Telegram media-analysis stack by default: telegram_media_recent, image_read, ocr, ocr_image_advanced, vision, pdf_to_text, ocr_pdf, transcribe_file, video_understand, audio_analyze, and identity_memory. For complex textual imagery, screenshots, forms, scans, or dense labels, prefer ocr_image_advanced after resolving media with path='reply' or path='latest'.",
635746
635788
  formatIdentityMemoryContext(chatLabel || "Telegram private chat"),
@@ -638321,9 +638363,10 @@ ${text}`.trim());
638321
638363
  return result.messageId ?? null;
638322
638364
  }
638323
638365
  async sendMessageHTMLDetailed(chatId, html, replyToMessageId, options2 = {}) {
638324
- const extracted = extractMediaReferences(html);
638366
+ const normalizedHtml = normalizeTelegramOutboundLinks(html);
638367
+ const extracted = extractMediaReferences(normalizedHtml);
638325
638368
  const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
638326
- const sendHtml = extracted.text || (extracted.media.length > 0 ? "" : html);
638369
+ const sendHtml = extracted.text || (extracted.media.length > 0 ? "" : normalizedHtml);
638327
638370
  let sentId = null;
638328
638371
  if (!sendHtml.trim()) {
638329
638372
  for (let idx = 0; idx < mediaRefs.length; idx++) {
@@ -638790,7 +638833,8 @@ Content-Type: ${mimeForPath(pathOrFileId, field === "photo" ? "image" : "video")
638790
638833
  * article carrying the message text Telegram should send to the guest chat.
638791
638834
  */
638792
638835
  async answerGuestQuery(guestQueryId, text, options2 = {}) {
638793
- const truncated = text.length > 4e3 ? text.slice(0, 3950) + "\n\n... (truncated)" : text;
638836
+ const normalizedText = normalizeTelegramOutboundLinks(text);
638837
+ const truncated = normalizedText.length > 4e3 ? normalizedText.slice(0, 3950) + "\n\n... (truncated)" : normalizedText;
638794
638838
  const inputMessageContent = {
638795
638839
  message_text: truncated
638796
638840
  };
@@ -657279,6 +657323,49 @@ function bodyString(body, keys) {
657279
657323
  }
657280
657324
  return "";
657281
657325
  }
657326
+ function realtimeFallbackCacheKey(ollamaUrl, missingModel) {
657327
+ return `${ollamaUrl}
657328
+ ${missingModel}`;
657329
+ }
657330
+ function isOllamaMissingModelError(body) {
657331
+ return /model ['\"]?[^'\"]+['\"]? not found/i.test(body);
657332
+ }
657333
+ async function resolveRealtimeOllamaFallbackModel(ollamaUrl, timeoutMs, missingModel) {
657334
+ try {
657335
+ const cacheKey = realtimeFallbackCacheKey(ollamaUrl, missingModel);
657336
+ const cached = realtimeOllamaFallbackCache.get(cacheKey);
657337
+ if (cached) return cached;
657338
+ const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET", void 0, Math.min(timeoutMs, 1e4));
657339
+ if (result.status >= 400) return null;
657340
+ const parsed = JSON.parse(result.body);
657341
+ const names = (parsed.models ?? []).map((entry) => typeof entry.name === "string" ? entry.name : typeof entry.model === "string" ? entry.model : "").filter(Boolean);
657342
+ if (!names.length) return null;
657343
+ const remember = (name10) => {
657344
+ realtimeOllamaFallbackCache.set(cacheKey, name10);
657345
+ return name10;
657346
+ };
657347
+ const exactLatest = `${missingModel}:latest`;
657348
+ if (names.includes(exactLatest)) return remember(exactLatest);
657349
+ const preferred = [
657350
+ "qwen3.5-9b-r10:q4km",
657351
+ "open-agents-qwen35-9b-r10-q4km:latest",
657352
+ "open-agents-qwen35-9b-r10-parsed-q4km:latest",
657353
+ "open-agents-qwen35-9b-r9-q4km:latest",
657354
+ "qwen3:8b",
657355
+ "open-agents-qwen3-8b:latest",
657356
+ "omnius-qwen36-35b:latest",
657357
+ "open-agents-qwen36:latest",
657358
+ "qwen3.6:35b"
657359
+ ];
657360
+ for (const name10 of preferred) {
657361
+ if (names.includes(name10)) return remember(name10);
657362
+ }
657363
+ const fallback = names.find((name10) => /qwen/i.test(name10) && !/embed|vision/i.test(name10)) ?? names.find((name10) => !/embed|vision|moondream/i.test(name10)) ?? null;
657364
+ return fallback ? remember(fallback) : null;
657365
+ } catch {
657366
+ return null;
657367
+ }
657368
+ }
657282
657369
  function realtimeEndpointMessages(body) {
657283
657370
  const messages2 = [];
657284
657371
  const suppliedSoul = bodyString(body, ["soul_md", "soul", "soulMd"]);
@@ -657305,13 +657392,14 @@ ${suppliedContext}` });
657305
657392
  }
657306
657393
  async function completeRealtimeTextOnly(opts) {
657307
657394
  const cfg = loadConfig();
657308
- const model = bodyString(opts.body, ["model"]) || cfg.model;
657395
+ const requestedModel = bodyString(opts.body, ["model"]);
657396
+ const model = requestedModel || opts.defaultModel || cfg.model;
657309
657397
  const route = resolveModelEndpoint(model);
657310
657398
  const limitErr = route?.endpoint ? checkEndpointRateLimit(route.endpoint) : null;
657311
657399
  if (limitErr) throw new Error(limitErr);
657312
657400
  const targetUrl = route?.endpoint.url ?? opts.ollamaUrl;
657313
- const targetType = route?.endpoint.type ?? cfg.backendType ?? "ollama";
657314
- const originalModel = route?.originalId ?? model.replace(/^[a-z]+\//, "");
657401
+ const targetType = route?.endpoint.type ?? opts.defaultBackendType ?? cfg.backendType ?? "ollama";
657402
+ let originalModel = route?.originalId ?? model.replace(/^[a-z]+\//, "");
657315
657403
  const realtimeOpts = {
657316
657404
  ...realtimeOptionsFromBody(opts.body, process.cwd(), opts.sessionId),
657317
657405
  surface: "voice_adapter"
@@ -657333,13 +657421,24 @@ async function completeRealtimeTextOnly(opts) {
657333
657421
  }
657334
657422
  const maxTokens = typeof requestBody["max_tokens"] === "number" ? requestBody["max_tokens"] : 120;
657335
657423
  const temperature = typeof requestBody["temperature"] === "number" ? requestBody["temperature"] : 0.6;
657336
- const result = await ollamaRequest(targetUrl, "/api/chat", "POST", JSON.stringify({
657337
- model: originalModel,
657424
+ if (!requestedModel) {
657425
+ originalModel = realtimeOllamaFallbackCache.get(realtimeFallbackCacheKey(targetUrl, originalModel)) ?? originalModel;
657426
+ }
657427
+ const makeOllamaChatBody = (modelName) => JSON.stringify({
657428
+ model: modelName,
657338
657429
  messages: requestBody["messages"],
657339
657430
  stream: false,
657340
657431
  think: false,
657341
657432
  options: { temperature, num_predict: maxTokens }
657342
- }), timeoutMs, route?.endpoint);
657433
+ });
657434
+ let result = await ollamaRequest(targetUrl, "/api/chat", "POST", makeOllamaChatBody(originalModel), timeoutMs, route?.endpoint);
657435
+ if (result.status >= 400 && !requestedModel && isOllamaMissingModelError(result.body)) {
657436
+ const fallbackModel = await resolveRealtimeOllamaFallbackModel(targetUrl, timeoutMs, originalModel);
657437
+ if (fallbackModel && fallbackModel !== originalModel) {
657438
+ originalModel = fallbackModel;
657439
+ result = await ollamaRequest(targetUrl, "/api/chat", "POST", makeOllamaChatBody(originalModel), timeoutMs, route?.endpoint);
657440
+ }
657441
+ }
657343
657442
  if (result.status >= 400) throw new Error(`Backend HTTP ${result.status}: ${result.body.slice(0, 300)}`);
657344
657443
  const parsed = JSON.parse(result.body);
657345
657444
  const rawReply = String(parsed?.message?.content ?? "").trim();
@@ -657354,7 +657453,7 @@ async function completeRealtimeTextOnly(opts) {
657354
657453
  }
657355
657454
  };
657356
657455
  }
657357
- async function handleRealtimeText(req2, res, ollamaUrl) {
657456
+ async function handleRealtimeText(req2, res, ollamaUrl, defaults3 = {}) {
657358
657457
  const body = await parseJsonBody(req2);
657359
657458
  if (!body || typeof body !== "object") {
657360
657459
  jsonResponse(res, 400, { error: "invalid_request", message: "Expected a JSON object." });
@@ -657367,7 +657466,14 @@ async function handleRealtimeText(req2, res, ollamaUrl) {
657367
657466
  }
657368
657467
  try {
657369
657468
  const sessionId = typeof body["session_id"] === "string" ? body["session_id"] : void 0;
657370
- const result = await completeRealtimeTextOnly({ body, messages: messages2, ollamaUrl, sessionId });
657469
+ const result = await completeRealtimeTextOnly({
657470
+ body,
657471
+ messages: messages2,
657472
+ ollamaUrl,
657473
+ defaultModel: defaults3.model,
657474
+ defaultBackendType: defaults3.backendType,
657475
+ sessionId
657476
+ });
657371
657477
  const wantsPlain = String(req2.headers["accept"] ?? "").includes("text/plain") || body["format"] === "text";
657372
657478
  if (wantsPlain) {
657373
657479
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" });
@@ -660331,7 +660437,7 @@ async function handlePostCommand(res, cmd) {
660331
660437
  });
660332
660438
  }
660333
660439
  }
660334
- async function handleRequest(req2, res, ollamaUrl, verbose) {
660440
+ async function handleRequest(req2, res, ollamaUrl, verbose, runtimeDefaults = {}) {
660335
660441
  try {
660336
660442
  const _liveCfg = loadConfig();
660337
660443
  if (_liveCfg.backendUrl) ollamaUrl = _liveCfg.backendUrl;
@@ -660606,7 +660712,7 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
660606
660712
  status = 401;
660607
660713
  return;
660608
660714
  }
660609
- await handleRealtimeText(req2, res, ollamaUrl);
660715
+ await handleRealtimeText(req2, res, ollamaUrl, runtimeDefaults);
660610
660716
  return;
660611
660717
  }
660612
660718
  if (pathname === "/v1/files" && method === "GET") {
@@ -662614,13 +662720,14 @@ ${historyLines}
662614
662720
  }));
662615
662721
  }
662616
662722
  } finally {
662617
- recordMetric(method, pathname, status);
662723
+ const finalStatus = res.headersSent ? res.statusCode : status;
662724
+ recordMetric(method, pathname, finalStatus);
662618
662725
  const latencyMs = Math.round(performance.now() - startMs);
662619
662726
  logRequest({
662620
662727
  requestId,
662621
662728
  method,
662622
662729
  path: pathname,
662623
- status,
662730
+ status: finalStatus,
662624
662731
  latencyMs,
662625
662732
  user: req2._authUser ?? "anonymous",
662626
662733
  scope: req2._authScope ?? "none"
@@ -662630,7 +662737,7 @@ ${historyLines}
662630
662737
  requestId,
662631
662738
  method,
662632
662739
  path: pathname,
662633
- status,
662740
+ status: finalStatus,
662634
662741
  user: req2._authUser ?? "anonymous",
662635
662742
  scope: req2._authScope ?? "none",
662636
662743
  latencyMs: Math.round(performance.now() - startMs),
@@ -663552,7 +663659,10 @@ function startApiServer(options2 = {}) {
663552
663659
  }
663553
663660
  } catch {
663554
663661
  }
663555
- handleRequest(req2, res, ollamaUrl, verbose).catch((err) => {
663662
+ handleRequest(req2, res, ollamaUrl, verbose, {
663663
+ model: options2.model ?? config.model,
663664
+ backendType: options2.backendType ?? config.backendType
663665
+ }).catch((err) => {
663556
663666
  metrics.totalErrors++;
663557
663667
  try {
663558
663668
  jsonResponse(res, 500, {
@@ -664374,7 +664484,9 @@ async function apiServeCommand(opts, config) {
664374
664484
  port: opts.port,
664375
664485
  // Let startApiServer() parse OMNIUS_HOST env if no explicit --port
664376
664486
  verbose: opts.verbose,
664377
- ollamaUrl: config.backendUrl
664487
+ ollamaUrl: config.backendUrl,
664488
+ model: config.model,
664489
+ backendType: config.backendType
664378
664490
  });
664379
664491
  await new Promise((resolve57) => {
664380
664492
  server2.on("close", resolve57);
@@ -664429,7 +664541,7 @@ function setTimerEnabled(name10, enabled2) {
664429
664541
  return false;
664430
664542
  }
664431
664543
  }
664432
- var require4, NEXUS_DIRECTORY_ORIGIN2, NEXUS_SPONSORS_URL2, endpointRegistry, modelRouteMap, endpointUsage, _lastEndpointDiagnostics, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, MODEL_LIST_TIMEOUT_DEFAULT_MS, metrics, startedAt, runningProcesses, perKeyUsage, CRON_MARKER2;
664544
+ var require4, NEXUS_DIRECTORY_ORIGIN2, NEXUS_SPONSORS_URL2, endpointRegistry, modelRouteMap, endpointUsage, _lastEndpointDiagnostics, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, MODEL_LIST_TIMEOUT_DEFAULT_MS, metrics, startedAt, realtimeOllamaFallbackCache, runningProcesses, perKeyUsage, CRON_MARKER2;
664433
664545
  var init_serve = __esm({
664434
664546
  "packages/cli/src/api/serve.ts"() {
664435
664547
  "use strict";
@@ -664477,6 +664589,7 @@ var init_serve = __esm({
664477
664589
  totalErrors: 0
664478
664590
  };
664479
664591
  startedAt = Date.now();
664592
+ realtimeOllamaFallbackCache = /* @__PURE__ */ new Map();
664480
664593
  runningProcesses = /* @__PURE__ */ new Map();
664481
664594
  perKeyUsage = /* @__PURE__ */ new Map();
664482
664595
  CRON_MARKER2 = "# OMNIUS-SCHEDULED:";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.169",
3
+ "version": "1.0.171",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.169",
9
+ "version": "1.0.171",
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.169",
3
+ "version": "1.0.171",
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",