omnius 1.0.78 → 1.0.80

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
@@ -538985,6 +538985,28 @@ function stripThinkBlocks(s2) {
538985
538985
  return s2;
538986
538986
  return s2.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
538987
538987
  }
538988
+ function injectNoThinkDirective(messages2) {
538989
+ if (!Array.isArray(messages2) || messages2.length === 0)
538990
+ return messages2;
538991
+ let lastUserIdx = -1;
538992
+ for (let i2 = messages2.length - 1; i2 >= 0; i2--) {
538993
+ if (messages2[i2]?.role === "user") {
538994
+ lastUserIdx = i2;
538995
+ break;
538996
+ }
538997
+ }
538998
+ if (lastUserIdx === -1)
538999
+ return messages2;
539000
+ const target = messages2[lastUserIdx];
539001
+ if (!target || typeof target.content !== "string")
539002
+ return messages2;
539003
+ if (/\/no_think\b/i.test(target.content))
539004
+ return messages2;
539005
+ const annotated = `${target.content}
539006
+
539007
+ /no_think`;
539008
+ return messages2.map((m2, i2) => i2 === lastUserIdx ? { ...m2, content: annotated } : m2);
539009
+ }
538988
539010
  function computeEffectiveThink(params) {
538989
539011
  if (process.env["OMNIUS_FORCE_NO_THINK"] === "1")
538990
539012
  return false;
@@ -551435,14 +551457,40 @@ ${description}`
551435
551457
  }) : null;
551436
551458
  const requestBaseUrl = poolSlot?.baseUrl ?? this.baseUrl;
551437
551459
  let poolSuccess = false;
551460
+ const combineAbortSignals = (signals) => {
551461
+ const filtered = signals.filter((s2) => s2 instanceof AbortSignal);
551462
+ if (filtered.length === 0)
551463
+ return void 0;
551464
+ if (filtered.length === 1)
551465
+ return filtered[0];
551466
+ const anyFn = AbortSignal.any;
551467
+ if (typeof anyFn === "function")
551468
+ return anyFn(filtered);
551469
+ const controller = new AbortController();
551470
+ for (const sig of filtered) {
551471
+ if (sig.aborted) {
551472
+ controller.abort(sig.reason);
551473
+ break;
551474
+ }
551475
+ sig.addEventListener("abort", () => {
551476
+ if (!controller.signal.aborted) {
551477
+ controller.abort(sig.reason);
551478
+ }
551479
+ });
551480
+ }
551481
+ return controller.signal;
551482
+ };
551483
+ const effectiveTimeoutMs = Number.isFinite(request.timeoutMs) && request.timeoutMs > 0 ? request.timeoutMs : 0;
551484
+ const timeoutSignal = effectiveTimeoutMs > 0 && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(effectiveTimeoutMs) : void 0;
551485
+ const combinedAbortSignal = combineAbortSignals([this._abortSignal, timeoutSignal]);
551438
551486
  try {
551439
551487
  const fetchOpts = {
551440
551488
  method: "POST",
551441
551489
  headers: this.authHeaders(),
551442
551490
  body: JSON.stringify(body)
551443
551491
  };
551444
- if (this._abortSignal)
551445
- fetchOpts.signal = this._abortSignal;
551492
+ if (combinedAbortSignal)
551493
+ fetchOpts.signal = combinedAbortSignal;
551446
551494
  const resp = await fetch(`${requestBaseUrl}/v1/chat/completions`, fetchOpts);
551447
551495
  if (!resp.ok) {
551448
551496
  const text = await resp.text().catch(() => "");
@@ -551456,34 +551504,42 @@ ${description}`
551456
551504
  const firstChoice = choices[0];
551457
551505
  const responseText = firstChoice ? String(firstChoice.message?.content ?? "") : "";
551458
551506
  const outcome = this.recordThinkOutcome(responseText, effectiveThink === true);
551459
- if (outcome !== null && effectiveThink === true) {
551460
- const justSuppressed = this._thinkSuppressed && this._thinkFailStreak === _OllamaAgenticBackend._thinkFailThreshold;
551461
- if (justSuppressed || outcome === "empty_after_strip" || outcome === "unclosed_think") {
551462
- const retryBody = {
551463
- model: this.model,
551464
- messages: cleanedMessages,
551465
- tools: request.tools,
551466
- temperature: request.temperature,
551467
- max_tokens: request.maxTokens,
551468
- think: false
551507
+ const independentOutcome = effectiveThink !== true ? classifyThinkOutcome(responseText) : null;
551508
+ const shouldRecoverFromEmpty = responseFormat !== void 0 && independentOutcome !== null && (independentOutcome === "empty_after_strip" || independentOutcome === "unclosed_think");
551509
+ const justSuppressed = this._thinkSuppressed && this._thinkFailStreak === _OllamaAgenticBackend._thinkFailThreshold;
551510
+ const shouldRetryThinkGuard = outcome !== null && effectiveThink === true && (justSuppressed || outcome === "empty_after_strip" || outcome === "unclosed_think");
551511
+ if (shouldRetryThinkGuard || shouldRecoverFromEmpty) {
551512
+ const retryMessages = injectNoThinkDirective(cleanedMessages);
551513
+ const retryBody = {
551514
+ model: this.model,
551515
+ messages: retryMessages,
551516
+ tools: request.tools,
551517
+ temperature: request.temperature,
551518
+ max_tokens: request.maxTokens,
551519
+ think: false
551520
+ };
551521
+ if (responseFormat !== void 0 && shouldRetryThinkGuard && !shouldRecoverFromEmpty) {
551522
+ retryBody["response_format"] = responseFormat;
551523
+ }
551524
+ try {
551525
+ const retryOpts = {
551526
+ method: "POST",
551527
+ headers: this.authHeaders(),
551528
+ body: JSON.stringify(retryBody)
551469
551529
  };
551470
- if (responseFormat !== void 0) {
551471
- retryBody["response_format"] = responseFormat;
551472
- }
551473
- try {
551474
- const retryOpts = {
551475
- method: "POST",
551476
- headers: this.authHeaders(),
551477
- body: JSON.stringify(retryBody)
551478
- };
551479
- if (this._abortSignal)
551480
- retryOpts.signal = this._abortSignal;
551481
- const retryResp = await fetch(`${requestBaseUrl}/v1/chat/completions`, retryOpts);
551482
- if (retryResp.ok) {
551483
- const retryData = await retryResp.json();
551484
- const retryChoices = retryData.choices ?? [];
551485
- const retryUsage = retryData.usage;
551486
- if (retryChoices.length > 0) {
551530
+ if (combinedAbortSignal)
551531
+ retryOpts.signal = combinedAbortSignal;
551532
+ const retryResp = await fetch(`${requestBaseUrl}/v1/chat/completions`, retryOpts);
551533
+ if (retryResp.ok) {
551534
+ const retryData = await retryResp.json();
551535
+ const retryChoices = retryData.choices ?? [];
551536
+ const retryUsage = retryData.usage;
551537
+ if (retryChoices.length > 0) {
551538
+ const retryFirst = retryChoices[0];
551539
+ const retryText = retryFirst ? String(retryFirst.message?.content ?? "") : "";
551540
+ const retryClass = classifyThinkOutcome(retryText);
551541
+ const retryUsable = retryClass !== "empty_after_strip" && retryClass !== "unclosed_think";
551542
+ if (retryUsable) {
551487
551543
  poolSuccess = true;
551488
551544
  return {
551489
551545
  choices: retryChoices.map((c9) => {
@@ -551501,8 +551557,8 @@ ${description}`
551501
551557
  };
551502
551558
  }
551503
551559
  }
551504
- } catch {
551505
551560
  }
551561
+ } catch {
551506
551562
  }
551507
551563
  }
551508
551564
  poolSuccess = true;
@@ -615356,19 +615412,56 @@ ${lines.join("\n")}`);
615356
615412
  nextAnalysisAfterMs: decision.nextCheckAfterMs
615357
615413
  });
615358
615414
  }
615359
- async telegramRouterJsonCompletion(backend, request) {
615415
+ async telegramRouterJsonCompletion(backend, request, diagnostics) {
615416
+ let jsonModeResult;
615417
+ let jsonModeError;
615360
615418
  try {
615361
- return await backend.chatCompletion({
615419
+ jsonModeResult = await backend.chatCompletion({
615362
615420
  ...request,
615363
615421
  responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
615364
615422
  });
615365
- } catch {
615366
- return backend.chatCompletion(request);
615423
+ const visible = jsonModeResult.choices.some(
615424
+ (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
615425
+ );
615426
+ if (visible) {
615427
+ if (diagnostics) diagnostics.jsonModeStatus = "visible";
615428
+ return jsonModeResult;
615429
+ }
615430
+ if (diagnostics) diagnostics.jsonModeStatus = "empty-after-strip";
615431
+ } catch (err) {
615432
+ jsonModeError = err;
615433
+ if (diagnostics) {
615434
+ diagnostics.jsonModeStatus = "threw";
615435
+ diagnostics.jsonModeError = err instanceof Error ? err.message : String(err);
615436
+ }
615437
+ }
615438
+ try {
615439
+ const plainResult = await backend.chatCompletion(request);
615440
+ if (diagnostics) {
615441
+ const plainVisible = plainResult.choices.some(
615442
+ (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
615443
+ );
615444
+ diagnostics.plainStatus = plainVisible ? "visible" : "empty-after-strip";
615445
+ }
615446
+ return plainResult;
615447
+ } catch (err) {
615448
+ if (diagnostics) {
615449
+ diagnostics.plainStatus = "threw";
615450
+ diagnostics.plainError = err instanceof Error ? err.message : String(err);
615451
+ }
615452
+ if (jsonModeError instanceof Error && !(err instanceof Error)) throw jsonModeError;
615453
+ throw err;
615367
615454
  }
615368
615455
  }
615369
- async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs) {
615456
+ async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs, diagnostics) {
615370
615457
  const rawPreview = telegramRouterRawPreview(rawOutput, 4e3);
615371
- if (!rawPreview || telegramDecisionOutputHasDanglingJson(rawOutput)) return null;
615458
+ if (!rawPreview || telegramDecisionOutputHasDanglingJson(rawOutput)) {
615459
+ if (diagnostics) {
615460
+ diagnostics.repairStatus = "skipped";
615461
+ diagnostics.repairError = !rawPreview ? "no recoverable text in router output (empty after <think> strip)" : "router output was dangling JSON; repair would only re-produce a truncation";
615462
+ }
615463
+ return null;
615464
+ }
615372
615465
  const routeInstruction = forcedRoute ? `The route is operator-forced and must be "${forcedRoute}".` : `Preserve the original route if present; otherwise choose chat or action only if the original output clearly implies one.`;
615373
615466
  const prompt = [
615374
615467
  `Repair this Telegram attention-router output into strict JSON.`,
@@ -615380,7 +615473,9 @@ ${lines.join("\n")}`);
615380
615473
  `{"recoverable":true|false,"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note"}`,
615381
615474
  ``,
615382
615475
  `Original router output:`,
615383
- rawPreview
615476
+ rawPreview,
615477
+ ``,
615478
+ `/no_think`
615384
615479
  ].join("\n");
615385
615480
  try {
615386
615481
  const result = await this.telegramRouterJsonCompletion(backend, {
@@ -615398,11 +615493,18 @@ ${lines.join("\n")}`);
615398
615493
  think: false
615399
615494
  });
615400
615495
  const repairedText = result.choices[0]?.message?.content ?? "";
615401
- if (telegramDecisionRecoverableFlag(repairedText) === false) return null;
615496
+ if (telegramDecisionRecoverableFlag(repairedText) === false) {
615497
+ if (diagnostics) diagnostics.repairStatus = "no-recoverable-output";
615498
+ return null;
615499
+ }
615402
615500
  const parsed = parseTelegramInteractionDecision(repairedText, forcedRoute, {
615403
615501
  defaultShouldReply: false
615404
615502
  });
615405
- if (!parsed) return null;
615503
+ if (!parsed) {
615504
+ if (diagnostics) diagnostics.repairStatus = "no-recoverable-output";
615505
+ return null;
615506
+ }
615507
+ if (diagnostics) diagnostics.repairStatus = "recovered";
615406
615508
  return {
615407
615509
  ...parsed,
615408
615510
  reason: `recovered router decision: ${parsed.reason}`.slice(0, 240),
@@ -615413,17 +615515,23 @@ ${repairedText}`,
615413
615515
  mentalNote: parsed.mentalNote ?? "router decision recovered from non-JSON model output",
615414
615516
  memoryNote: parsed.memoryNote ?? "router repair preserved the model-derived attention decision"
615415
615517
  };
615416
- } catch {
615518
+ } catch (err) {
615519
+ if (diagnostics) {
615520
+ diagnostics.repairStatus = "threw";
615521
+ diagnostics.repairError = err instanceof Error ? err.message : String(err);
615522
+ }
615417
615523
  return null;
615418
615524
  }
615419
615525
  }
615420
- async retryTelegramInteractionDecisionStrict(backend, userPrompt, rawOutput, forcedRoute, timeoutMs) {
615526
+ async retryTelegramInteractionDecisionStrict(backend, userPrompt, rawOutput, forcedRoute, timeoutMs, diagnostics) {
615421
615527
  const invalidPreview = telegramRouterRawPreview(rawOutput, 1200) ?? "(empty assistant content)";
615422
615528
  const routeInstruction = forcedRoute ? `The operator selected Telegram mode "${forcedRoute}". The route field must be "${forcedRoute}", but should_reply must still be inferred from context.` : `Infer route live from context.`;
615529
+ const trimmedUserPrompt = userPrompt.length > 4e3 ? `…
615530
+ ${userPrompt.slice(-4e3)}` : userPrompt;
615423
615531
  const retryPrompt = [
615424
615532
  `The previous Telegram attention-router response was not usable JSON.`,
615425
- `Make a fresh model-derived attention decision from the full context below. Do not use hard-coded mention or keyword triggers.`,
615426
- `Return exactly one JSON object and no prose.`,
615533
+ `Make a fresh model-derived attention decision from the context below. Do not use hard-coded mention or keyword triggers.`,
615534
+ `Return exactly one JSON object and no prose. No <think> tags. No commentary.`,
615427
615535
  routeInstruction,
615428
615536
  ``,
615429
615537
  `Required schema: {"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason","attention_state":"idle"|"observing"|"engaged"|"cooldown","attention_delta":-1.0..1.0,"next_check_after_messages":1..12,"silent_disposition":"short outcome-level disposition","mental_note":"short outcome-level observation","memory_note":"short memory/summary update","relationship_note":"short relationship/thread note"}`,
@@ -615431,15 +615539,17 @@ ${repairedText}`,
615431
615539
  `Invalid previous output, for diagnostics only:`,
615432
615540
  invalidPreview,
615433
615541
  ``,
615434
- `Full router context:`,
615435
- userPrompt
615542
+ `Router context (trailing-window):`,
615543
+ trimmedUserPrompt,
615544
+ ``,
615545
+ `/no_think`
615436
615546
  ].join("\n");
615437
615547
  try {
615438
615548
  const result = await this.telegramRouterJsonCompletion(backend, {
615439
615549
  messages: [
615440
615550
  {
615441
615551
  role: "system",
615442
- content: "You are a strict JSON Telegram attention router. Output one valid JSON object only."
615552
+ content: "You are a strict JSON Telegram attention router. Output one valid JSON object only. Never emit <think> tags."
615443
615553
  },
615444
615554
  { role: "user", content: retryPrompt }
615445
615555
  ],
@@ -615450,10 +615560,18 @@ ${repairedText}`,
615450
615560
  think: false
615451
615561
  });
615452
615562
  const retryText = result.choices[0]?.message?.content ?? "";
615563
+ if (diagnostics) diagnostics.strictRetryPreview = telegramRouterRawPreview(retryText, 320);
615453
615564
  const parsed = parseTelegramInteractionDecision(retryText, forcedRoute, {
615454
615565
  defaultShouldReply: false
615455
615566
  });
615456
- if (!parsed) return null;
615567
+ if (!parsed) {
615568
+ if (diagnostics) {
615569
+ const cleaned = stripTelegramHiddenThinking(retryText).trim();
615570
+ diagnostics.strictRetryStatus = cleaned ? "unparseable" : "empty";
615571
+ }
615572
+ return null;
615573
+ }
615574
+ if (diagnostics) diagnostics.strictRetryStatus = "recovered";
615457
615575
  return {
615458
615576
  ...parsed,
615459
615577
  reason: `strict router retry: ${parsed.reason}`.slice(0, 240),
@@ -615463,7 +615581,11 @@ ${repairedText}`,
615463
615581
  ${retryText}`,
615464
615582
  mentalNote: parsed.mentalNote ?? "strict router retry produced a valid attention decision"
615465
615583
  };
615466
- } catch {
615584
+ } catch (err) {
615585
+ if (diagnostics) {
615586
+ diagnostics.strictRetryStatus = "threw";
615587
+ diagnostics.strictRetryError = err instanceof Error ? err.message : String(err);
615588
+ }
615467
615589
  return null;
615468
615590
  }
615469
615591
  }
@@ -615484,7 +615606,7 @@ ${retryText}`,
615484
615606
  forceAnalyze: daydreamForceCheck
615485
615607
  });
615486
615608
  if (!config) {
615487
- const fallback2 = {
615609
+ const fallback = {
615488
615610
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
615489
615611
  shouldReply: false,
615490
615612
  confidence: 0,
@@ -615493,8 +615615,8 @@ ${retryText}`,
615493
615615
  silentDisposition: "retained as context without replying",
615494
615616
  mentalNote: "router unavailable, so no model-derived attention note was produced"
615495
615617
  };
615496
- this.applyTelegramStimulationDecision(sessionKey, fallback2);
615497
- return fallback2;
615618
+ this.applyTelegramStimulationDecision(sessionKey, fallback);
615619
+ return fallback;
615498
615620
  }
615499
615621
  const backend = new OllamaAgenticBackend(
615500
615622
  config.backendUrl,
@@ -615549,6 +615671,7 @@ ${stimulationProbe.context}`,
615549
615671
  `Current Telegram message text (untrusted user data):
615550
615672
  ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615551
615673
  ].filter(Boolean).join("\n");
615674
+ const diagnostics = {};
615552
615675
  try {
615553
615676
  const result = await this.telegramRouterJsonCompletion(backend, {
615554
615677
  messages: [
@@ -615563,7 +615686,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615563
615686
  maxTokens: 700,
615564
615687
  timeoutMs: Math.min(Math.max(config.timeoutMs ?? 3e4, 5e3), 15e3),
615565
615688
  think: false
615566
- });
615689
+ }, diagnostics);
615567
615690
  const text = result.choices[0]?.message?.content ?? "";
615568
615691
  const parsed = parseTelegramInteractionDecision(text, forcedRoute, {
615569
615692
  defaultShouldReply: false
@@ -615576,7 +615699,8 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615576
615699
  backend,
615577
615700
  text,
615578
615701
  forcedRoute,
615579
- config.timeoutMs ?? 3e4
615702
+ config.timeoutMs ?? 3e4,
615703
+ diagnostics
615580
615704
  );
615581
615705
  if (repaired) {
615582
615706
  this.applyTelegramStimulationDecision(sessionKey, repaired);
@@ -615587,39 +615711,122 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615587
615711
  userPrompt,
615588
615712
  text,
615589
615713
  forcedRoute,
615590
- config.timeoutMs ?? 3e4
615714
+ config.timeoutMs ?? 3e4,
615715
+ diagnostics
615591
615716
  );
615592
615717
  if (strictRetry) {
615593
615718
  this.applyTelegramStimulationDecision(sessionKey, strictRetry);
615594
615719
  return strictRetry;
615595
615720
  }
615596
615721
  const invalidRouterPreview = telegramRouterRawPreview(text);
615597
- const fallback2 = {
615722
+ const failureNarrative = this.summarizeTelegramRouterFailure(diagnostics);
615723
+ const fallback = {
615598
615724
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
615599
615725
  shouldReply: false,
615600
615726
  confidence: 0,
615601
615727
  reason: "router output was not valid decision JSON after repair/retry; no model-derived reply decision",
615602
615728
  source: "inference-unavailable",
615603
615729
  silentDisposition: "retained as context without replying because the router decision could not be parsed",
615604
- mentalNote: invalidRouterPreview ? "router produced an invalid attention decision payload; repair and strict retry did not recover it" : "router produced an empty attention decision payload; strict retry did not recover it",
615605
- memoryNote: invalidRouterPreview ? `invalid router output preview: ${invalidRouterPreview}` : void 0,
615730
+ // Preserve the well-known mental-note strings (existing tests rely
615731
+ // on them) but append the per-step diagnostic so operators get a
615732
+ // real explanation of what actually failed.
615733
+ mentalNote: (invalidRouterPreview ? "router produced an invalid attention decision payload; repair and strict retry did not recover it" : "router produced an empty attention decision payload; strict retry did not recover it") + (failureNarrative.summary ? ` — ${failureNarrative.summary}` : ""),
615734
+ memoryNote: this.composeTelegramRouterMemoryNote(invalidRouterPreview, failureNarrative.detail),
615735
+ relationshipNote: failureNarrative.relationshipHint,
615606
615736
  raw: text
615607
615737
  };
615608
- this.applyTelegramStimulationDecision(sessionKey, fallback2);
615609
- return fallback2;
615610
- } catch {
615738
+ this.applyTelegramStimulationDecision(sessionKey, fallback);
615739
+ return fallback;
615740
+ } catch (err) {
615741
+ const failureNarrative = this.summarizeTelegramRouterFailure(diagnostics);
615742
+ const errMsg = err instanceof Error ? err.message : String(err);
615743
+ const fallback = {
615744
+ route: forcedRoute ?? (isGroup ? "action" : "chat"),
615745
+ shouldReply: false,
615746
+ confidence: 0,
615747
+ reason: `router inference failed; no model-derived reply decision (${errMsg.slice(0, 160)})`,
615748
+ source: "inference-unavailable",
615749
+ silentDisposition: "retained as context without replying",
615750
+ mentalNote: `router failed, so no model-derived attention note was produced` + (failureNarrative.summary ? ` — ${failureNarrative.summary}` : ` — ${errMsg.slice(0, 160)}`),
615751
+ memoryNote: failureNarrative.detail ? `router-failure trace: ${failureNarrative.detail}` : `router-failure trace: ${errMsg.slice(0, 240)}`,
615752
+ relationshipNote: failureNarrative.relationshipHint
615753
+ };
615754
+ this.applyTelegramStimulationDecision(sessionKey, fallback);
615755
+ return fallback;
615756
+ }
615757
+ }
615758
+ /**
615759
+ * Reduce captured per-step diagnostics into:
615760
+ * - `summary`: a short outcome-level line for the mental note
615761
+ * - `detail`: a longer ordered trace for the memory note
615762
+ * - `relationshipHint`: an operational hint (e.g. "ollama backend appears to be injecting <think> tags despite think:false")
615763
+ */
615764
+ summarizeTelegramRouterFailure(diag) {
615765
+ const parts = [];
615766
+ const detailParts = [];
615767
+ let thinkInjectionSuspected = false;
615768
+ let networkErrorSeen = false;
615769
+ if (diag.jsonModeStatus === "threw") {
615770
+ parts.push(`json-mode call threw`);
615771
+ detailParts.push(`json-mode: threw (${diag.jsonModeError ?? "no detail"})`);
615772
+ networkErrorSeen = true;
615773
+ } else if (diag.jsonModeStatus === "empty-after-strip") {
615774
+ parts.push(`json-mode returned empty content (likely <think>-only)`);
615775
+ detailParts.push(`json-mode: empty-after-strip`);
615776
+ thinkInjectionSuspected = true;
615777
+ } else if (diag.jsonModeStatus === "visible") {
615778
+ detailParts.push(`json-mode: visible`);
615779
+ }
615780
+ if (diag.plainStatus === "threw") {
615781
+ parts.push(`plain call threw`);
615782
+ detailParts.push(`plain: threw (${diag.plainError ?? "no detail"})`);
615783
+ networkErrorSeen = true;
615784
+ } else if (diag.plainStatus === "empty-after-strip") {
615785
+ parts.push(`plain call returned empty content`);
615786
+ detailParts.push(`plain: empty-after-strip`);
615787
+ thinkInjectionSuspected = true;
615788
+ } else if (diag.plainStatus === "visible") {
615789
+ detailParts.push(`plain: visible-but-unparseable`);
615790
+ }
615791
+ if (diag.repairStatus === "skipped") {
615792
+ detailParts.push(`repair: skipped (${diag.repairError ?? "no recoverable input"})`);
615793
+ } else if (diag.repairStatus === "no-recoverable-output") {
615794
+ detailParts.push(`repair: returned non-recoverable JSON`);
615795
+ } else if (diag.repairStatus === "threw") {
615796
+ detailParts.push(`repair: threw (${diag.repairError ?? "no detail"})`);
615797
+ networkErrorSeen = true;
615798
+ } else if (diag.repairStatus === "recovered") {
615799
+ detailParts.push(`repair: recovered`);
615800
+ }
615801
+ if (diag.strictRetryStatus === "empty") {
615802
+ detailParts.push(`strict-retry: empty`);
615803
+ thinkInjectionSuspected = true;
615804
+ } else if (diag.strictRetryStatus === "unparseable") {
615805
+ detailParts.push(`strict-retry: unparseable (preview="${diag.strictRetryPreview ?? ""}")`);
615806
+ } else if (diag.strictRetryStatus === "threw") {
615807
+ detailParts.push(`strict-retry: threw (${diag.strictRetryError ?? "no detail"})`);
615808
+ networkErrorSeen = true;
615809
+ } else if (diag.strictRetryStatus === "recovered") {
615810
+ detailParts.push(`strict-retry: recovered`);
615811
+ }
615812
+ let relationshipHint;
615813
+ if (networkErrorSeen) {
615814
+ relationshipHint = "router backend appears unreachable or rate-limited; continued conversation depends on recovery";
615815
+ } else if (thinkInjectionSuspected) {
615816
+ relationshipHint = "router model emitted <think>-only or unclosed-think output; conversation continuity preserved but inference is degraded";
615611
615817
  }
615612
- const fallback = {
615613
- route: forcedRoute ?? (isGroup ? "action" : "chat"),
615614
- shouldReply: false,
615615
- confidence: 0,
615616
- reason: "router inference failed; no model-derived reply decision",
615617
- source: "inference-unavailable",
615618
- silentDisposition: "retained as context without replying",
615619
- mentalNote: "router failed, so no model-derived attention note was produced"
615818
+ return {
615819
+ summary: parts.join("; "),
615820
+ detail: detailParts.join("; "),
615821
+ relationshipHint
615620
615822
  };
615621
- this.applyTelegramStimulationDecision(sessionKey, fallback);
615622
- return fallback;
615823
+ }
615824
+ composeTelegramRouterMemoryNote(invalidRouterPreview, detail) {
615825
+ const segments = [];
615826
+ if (invalidRouterPreview) segments.push(`invalid router output preview: ${invalidRouterPreview}`);
615827
+ if (detail) segments.push(`router-failure trace: ${detail}`);
615828
+ if (segments.length === 0) return void 0;
615829
+ return segments.join(" | ");
615623
615830
  }
615624
615831
  buildTelegramWorkspaceContext(modelTier, budget = 14e3) {
615625
615832
  if (!this.repoRoot) return "";
@@ -637795,6 +638002,7 @@ async function directChatBackend(opts) {
637795
638002
  if (typeof ef["presence_penalty"] === "number") ollamaOpts["presence_penalty"] = ef["presence_penalty"];
637796
638003
  if (Array.isArray(ef["stop"]) || typeof ef["stop"] === "string") ollamaOpts["stop"] = ef["stop"];
637797
638004
  const hasTools = Array.isArray(ef["tools"]) && ef["tools"].length > 0;
638005
+ const ollamaFormat = ollamaFormatFromOpenAIResponseFormat(ef["response_format"]);
637798
638006
  const reqBody = JSON.stringify({
637799
638007
  model: cleanModel,
637800
638008
  messages: messages2,
@@ -637804,7 +638012,7 @@ async function directChatBackend(opts) {
637804
638012
  ...hasTools ? {} : { think: false },
637805
638013
  ...hasTools ? { tools: ef["tools"] } : {},
637806
638014
  ...ef["tool_choice"] !== void 0 ? { tool_choice: ef["tool_choice"] } : {},
637807
- ...ef["response_format"] !== void 0 ? { format: ef["response_format"] } : {},
638015
+ ...ollamaFormat !== void 0 ? { format: ollamaFormat } : {},
637808
638016
  options: ollamaOpts
637809
638017
  });
637810
638018
  if (stream) {
@@ -637907,6 +638115,22 @@ async function directChatBackend(opts) {
637907
638115
  }
637908
638116
  }
637909
638117
  }
638118
+ function ollamaFormatFromOpenAIResponseFormat(value2) {
638119
+ if (typeof value2 === "string") return value2;
638120
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return void 0;
638121
+ const record = value2;
638122
+ if (record["type"] === "json_object") return "json";
638123
+ if (record["type"] === "json_schema") {
638124
+ const jsonSchema = record["json_schema"];
638125
+ if (jsonSchema && typeof jsonSchema === "object" && !Array.isArray(jsonSchema)) {
638126
+ const schemaRecord = jsonSchema;
638127
+ return schemaRecord["schema"] ?? jsonSchema;
638128
+ }
638129
+ return "json";
638130
+ }
638131
+ if (record["type"] === "object" || record["properties"] !== void 0) return record;
638132
+ return void 0;
638133
+ }
637910
638134
  function backendAuthHeaders(endpoint) {
637911
638135
  const key = endpoint?.authKey ?? loadConfig().apiKey;
637912
638136
  if (key) return { Authorization: `Bearer ${key}` };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.78",
9
+ "version": "1.0.80",
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.78",
3
+ "version": "1.0.80",
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",