omnius 1.0.79 → 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,23 +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
- const result = await backend.chatCompletion({
615419
+ jsonModeResult = await backend.chatCompletion({
615362
615420
  ...request,
615363
615421
  responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
615364
615422
  });
615365
- const visible = result.choices.some(
615423
+ const visible = jsonModeResult.choices.some(
615366
615424
  (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
615367
615425
  );
615368
- if (visible) return result;
615369
- } catch {
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;
615370
615454
  }
615371
- return backend.chatCompletion(request);
615372
615455
  }
615373
- async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs) {
615456
+ async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs, diagnostics) {
615374
615457
  const rawPreview = telegramRouterRawPreview(rawOutput, 4e3);
615375
- 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
+ }
615376
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.`;
615377
615466
  const prompt = [
615378
615467
  `Repair this Telegram attention-router output into strict JSON.`,
@@ -615384,7 +615473,9 @@ ${lines.join("\n")}`);
615384
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"}`,
615385
615474
  ``,
615386
615475
  `Original router output:`,
615387
- rawPreview
615476
+ rawPreview,
615477
+ ``,
615478
+ `/no_think`
615388
615479
  ].join("\n");
615389
615480
  try {
615390
615481
  const result = await this.telegramRouterJsonCompletion(backend, {
@@ -615402,11 +615493,18 @@ ${lines.join("\n")}`);
615402
615493
  think: false
615403
615494
  });
615404
615495
  const repairedText = result.choices[0]?.message?.content ?? "";
615405
- if (telegramDecisionRecoverableFlag(repairedText) === false) return null;
615496
+ if (telegramDecisionRecoverableFlag(repairedText) === false) {
615497
+ if (diagnostics) diagnostics.repairStatus = "no-recoverable-output";
615498
+ return null;
615499
+ }
615406
615500
  const parsed = parseTelegramInteractionDecision(repairedText, forcedRoute, {
615407
615501
  defaultShouldReply: false
615408
615502
  });
615409
- 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";
615410
615508
  return {
615411
615509
  ...parsed,
615412
615510
  reason: `recovered router decision: ${parsed.reason}`.slice(0, 240),
@@ -615417,17 +615515,23 @@ ${repairedText}`,
615417
615515
  mentalNote: parsed.mentalNote ?? "router decision recovered from non-JSON model output",
615418
615516
  memoryNote: parsed.memoryNote ?? "router repair preserved the model-derived attention decision"
615419
615517
  };
615420
- } catch {
615518
+ } catch (err) {
615519
+ if (diagnostics) {
615520
+ diagnostics.repairStatus = "threw";
615521
+ diagnostics.repairError = err instanceof Error ? err.message : String(err);
615522
+ }
615421
615523
  return null;
615422
615524
  }
615423
615525
  }
615424
- async retryTelegramInteractionDecisionStrict(backend, userPrompt, rawOutput, forcedRoute, timeoutMs) {
615526
+ async retryTelegramInteractionDecisionStrict(backend, userPrompt, rawOutput, forcedRoute, timeoutMs, diagnostics) {
615425
615527
  const invalidPreview = telegramRouterRawPreview(rawOutput, 1200) ?? "(empty assistant content)";
615426
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;
615427
615531
  const retryPrompt = [
615428
615532
  `The previous Telegram attention-router response was not usable JSON.`,
615429
- `Make a fresh model-derived attention decision from the full context below. Do not use hard-coded mention or keyword triggers.`,
615430
- `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.`,
615431
615535
  routeInstruction,
615432
615536
  ``,
615433
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"}`,
@@ -615435,15 +615539,17 @@ ${repairedText}`,
615435
615539
  `Invalid previous output, for diagnostics only:`,
615436
615540
  invalidPreview,
615437
615541
  ``,
615438
- `Full router context:`,
615439
- userPrompt
615542
+ `Router context (trailing-window):`,
615543
+ trimmedUserPrompt,
615544
+ ``,
615545
+ `/no_think`
615440
615546
  ].join("\n");
615441
615547
  try {
615442
615548
  const result = await this.telegramRouterJsonCompletion(backend, {
615443
615549
  messages: [
615444
615550
  {
615445
615551
  role: "system",
615446
- 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."
615447
615553
  },
615448
615554
  { role: "user", content: retryPrompt }
615449
615555
  ],
@@ -615454,10 +615560,18 @@ ${repairedText}`,
615454
615560
  think: false
615455
615561
  });
615456
615562
  const retryText = result.choices[0]?.message?.content ?? "";
615563
+ if (diagnostics) diagnostics.strictRetryPreview = telegramRouterRawPreview(retryText, 320);
615457
615564
  const parsed = parseTelegramInteractionDecision(retryText, forcedRoute, {
615458
615565
  defaultShouldReply: false
615459
615566
  });
615460
- 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";
615461
615575
  return {
615462
615576
  ...parsed,
615463
615577
  reason: `strict router retry: ${parsed.reason}`.slice(0, 240),
@@ -615467,7 +615581,11 @@ ${repairedText}`,
615467
615581
  ${retryText}`,
615468
615582
  mentalNote: parsed.mentalNote ?? "strict router retry produced a valid attention decision"
615469
615583
  };
615470
- } catch {
615584
+ } catch (err) {
615585
+ if (diagnostics) {
615586
+ diagnostics.strictRetryStatus = "threw";
615587
+ diagnostics.strictRetryError = err instanceof Error ? err.message : String(err);
615588
+ }
615471
615589
  return null;
615472
615590
  }
615473
615591
  }
@@ -615488,7 +615606,7 @@ ${retryText}`,
615488
615606
  forceAnalyze: daydreamForceCheck
615489
615607
  });
615490
615608
  if (!config) {
615491
- const fallback2 = {
615609
+ const fallback = {
615492
615610
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
615493
615611
  shouldReply: false,
615494
615612
  confidence: 0,
@@ -615497,8 +615615,8 @@ ${retryText}`,
615497
615615
  silentDisposition: "retained as context without replying",
615498
615616
  mentalNote: "router unavailable, so no model-derived attention note was produced"
615499
615617
  };
615500
- this.applyTelegramStimulationDecision(sessionKey, fallback2);
615501
- return fallback2;
615618
+ this.applyTelegramStimulationDecision(sessionKey, fallback);
615619
+ return fallback;
615502
615620
  }
615503
615621
  const backend = new OllamaAgenticBackend(
615504
615622
  config.backendUrl,
@@ -615553,6 +615671,7 @@ ${stimulationProbe.context}`,
615553
615671
  `Current Telegram message text (untrusted user data):
615554
615672
  ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615555
615673
  ].filter(Boolean).join("\n");
615674
+ const diagnostics = {};
615556
615675
  try {
615557
615676
  const result = await this.telegramRouterJsonCompletion(backend, {
615558
615677
  messages: [
@@ -615567,7 +615686,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615567
615686
  maxTokens: 700,
615568
615687
  timeoutMs: Math.min(Math.max(config.timeoutMs ?? 3e4, 5e3), 15e3),
615569
615688
  think: false
615570
- });
615689
+ }, diagnostics);
615571
615690
  const text = result.choices[0]?.message?.content ?? "";
615572
615691
  const parsed = parseTelegramInteractionDecision(text, forcedRoute, {
615573
615692
  defaultShouldReply: false
@@ -615580,7 +615699,8 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615580
615699
  backend,
615581
615700
  text,
615582
615701
  forcedRoute,
615583
- config.timeoutMs ?? 3e4
615702
+ config.timeoutMs ?? 3e4,
615703
+ diagnostics
615584
615704
  );
615585
615705
  if (repaired) {
615586
615706
  this.applyTelegramStimulationDecision(sessionKey, repaired);
@@ -615591,39 +615711,122 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615591
615711
  userPrompt,
615592
615712
  text,
615593
615713
  forcedRoute,
615594
- config.timeoutMs ?? 3e4
615714
+ config.timeoutMs ?? 3e4,
615715
+ diagnostics
615595
615716
  );
615596
615717
  if (strictRetry) {
615597
615718
  this.applyTelegramStimulationDecision(sessionKey, strictRetry);
615598
615719
  return strictRetry;
615599
615720
  }
615600
615721
  const invalidRouterPreview = telegramRouterRawPreview(text);
615601
- const fallback2 = {
615722
+ const failureNarrative = this.summarizeTelegramRouterFailure(diagnostics);
615723
+ const fallback = {
615602
615724
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
615603
615725
  shouldReply: false,
615604
615726
  confidence: 0,
615605
615727
  reason: "router output was not valid decision JSON after repair/retry; no model-derived reply decision",
615606
615728
  source: "inference-unavailable",
615607
615729
  silentDisposition: "retained as context without replying because the router decision could not be parsed",
615608
- 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",
615609
- 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,
615610
615736
  raw: text
615611
615737
  };
615612
- this.applyTelegramStimulationDecision(sessionKey, fallback2);
615613
- return fallback2;
615614
- } 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;
615615
615756
  }
615616
- const fallback = {
615617
- route: forcedRoute ?? (isGroup ? "action" : "chat"),
615618
- shouldReply: false,
615619
- confidence: 0,
615620
- reason: "router inference failed; no model-derived reply decision",
615621
- source: "inference-unavailable",
615622
- silentDisposition: "retained as context without replying",
615623
- mentalNote: "router failed, so no model-derived attention note was produced"
615624
- };
615625
- this.applyTelegramStimulationDecision(sessionKey, fallback);
615626
- return fallback;
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";
615817
+ }
615818
+ return {
615819
+ summary: parts.join("; "),
615820
+ detail: detailParts.join("; "),
615821
+ relationshipHint
615822
+ };
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(" | ");
615627
615830
  }
615628
615831
  buildTelegramWorkspaceContext(modelTier, budget = 14e3) {
615629
615832
  if (!this.repoRoot) return "";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.79",
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.79",
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.79",
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",