omnius 1.0.79 → 1.0.81

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;
@@ -607978,7 +608034,7 @@ function phaseFromAttention(attention) {
607978
608034
  function parseStimulationPhase(value2) {
607979
608035
  return normalizePhase(value2);
607980
608036
  }
607981
- var DEFAULT_STATE, PHASE_DEFAULTS, PHASE_FLOORS, StimulationController;
608037
+ var DEFAULT_STATE, PHASE_MESSAGE_BUDGETS, PHASE_FLOORS, StimulationController;
607982
608038
  var init_stimulation = __esm({
607983
608039
  "packages/cli/src/tui/stimulation.ts"() {
607984
608040
  "use strict";
@@ -607991,11 +608047,11 @@ var init_stimulation = __esm({
607991
608047
  consecutiveNoReply: 0,
607992
608048
  updatedAtMs: 0
607993
608049
  };
607994
- PHASE_DEFAULTS = {
607995
- idle: { messages: 6, ms: 10 * 6e4 },
607996
- cooldown: { messages: 4, ms: 5 * 6e4 },
607997
- observing: { messages: 2, ms: 2 * 6e4 },
607998
- engaged: { messages: 1, ms: 45e3 }
608050
+ PHASE_MESSAGE_BUDGETS = {
608051
+ idle: 6,
608052
+ cooldown: 4,
608053
+ observing: 2,
608054
+ engaged: 1
607999
608055
  };
608000
608056
  PHASE_FLOORS = {
608001
608057
  idle: 0,
@@ -608026,7 +608082,6 @@ var init_stimulation = __esm({
608026
608082
  observe(input) {
608027
608083
  const now = input.nowMs ?? Date.now();
608028
608084
  const state = this.stateFor(input.channelId, now);
608029
- this.applyTimeDecay(state, now);
608030
608085
  state.lastStimulusAtMs = now;
608031
608086
  state.updatedAtMs = now;
608032
608087
  state.messagesSinceAnalysis += 1;
@@ -608037,22 +608092,15 @@ var init_stimulation = __esm({
608037
608092
  if (input.replyToAgent) state.attention = Math.max(state.attention, 0.84);
608038
608093
  if (input.activeAgent) state.attention = Math.max(state.attention, 0.68);
608039
608094
  state.phase = phaseFromAttention(state.attention);
608040
- const cadence = PHASE_DEFAULTS[state.phase];
608041
- const messageBudget = state.nextAnalysisAfterMessages ?? cadence.messages;
608042
- const timeDue = state.nextAnalysisAtMs !== void 0 ? now >= state.nextAnalysisAtMs : state.lastAnalysisAtMs !== void 0 && now - state.lastAnalysisAtMs >= cadence.ms;
608043
- const shouldAnalyze = !state.lastAnalysisAtMs || metadataStimulus || state.messagesSinceAnalysis >= messageBudget || timeDue;
608095
+ const messageBudget = state.nextAnalysisAfterMessages ?? PHASE_MESSAGE_BUDGETS[state.phase];
608096
+ const shouldAnalyze = !state.lastAnalysisAtMs || metadataStimulus || state.messagesSinceAnalysis >= messageBudget;
608044
608097
  let reason = "cadence-hold";
608045
608098
  if (!state.lastAnalysisAtMs) reason = "initial-analysis";
608046
608099
  else if (input.privateChannel) reason = "private-channel";
608047
608100
  else if (input.replyToAgent) reason = "reply-to-agent";
608048
608101
  else if (input.directSignal) reason = "direct-platform-signal";
608049
608102
  else if (input.activeAgent) reason = "active-agent-thread";
608050
- else if (state.messagesSinceAnalysis >= messageBudget) reason = "chronological-sample";
608051
- else if (timeDue) reason = "time-sample";
608052
- if (shouldAnalyze) {
608053
- state.lastAnalysisAtMs = now;
608054
- state.messagesSinceAnalysis = 0;
608055
- }
608103
+ else if (state.messagesSinceAnalysis >= messageBudget) reason = "message-sample";
608056
608104
  this.states.set(input.channelId, cloneState(state));
608057
608105
  return {
608058
608106
  shouldAnalyze,
@@ -608063,7 +608111,6 @@ var init_stimulation = __esm({
608063
608111
  }
608064
608112
  applyAgentDecision(channelId, decision, nowMs = Date.now()) {
608065
608113
  const state = this.stateFor(channelId, nowMs);
608066
- this.applyTimeDecay(state, nowMs);
608067
608114
  if (Number.isFinite(decision.attentionScore)) {
608068
608115
  state.attention = clamp017(Number(decision.attentionScore));
608069
608116
  } else if (Number.isFinite(decision.attentionDelta)) {
@@ -608079,14 +608126,14 @@ var init_stimulation = __esm({
608079
608126
  }
608080
608127
  state.consecutiveNoReply = decision.shouldReply ? 0 : state.consecutiveNoReply + 1;
608081
608128
  state.nextAnalysisAfterMessages = Number.isFinite(decision.nextAnalysisAfterMessages) ? Math.max(1, Math.floor(Number(decision.nextAnalysisAfterMessages))) : void 0;
608082
- state.nextAnalysisAtMs = Number.isFinite(decision.nextAnalysisAfterMs) ? nowMs + Math.max(0, Number(decision.nextAnalysisAfterMs)) : void 0;
608129
+ state.lastAnalysisAtMs = nowMs;
608130
+ state.messagesSinceAnalysis = 0;
608083
608131
  state.updatedAtMs = nowMs;
608084
608132
  this.states.set(channelId, cloneState(state));
608085
608133
  return cloneState(state);
608086
608134
  }
608087
608135
  recordAgentOutput(channelId, nowMs = Date.now()) {
608088
608136
  const state = this.stateFor(channelId, nowMs);
608089
- this.applyTimeDecay(state, nowMs);
608090
608137
  state.phase = "engaged";
608091
608138
  state.attention = Math.max(state.attention, 0.78);
608092
608139
  state.lastAgentOutputAtMs = nowMs;
@@ -608108,8 +608155,7 @@ var init_stimulation = __esm({
608108
608155
  `Consecutive no-reply decisions: ${state.consecutiveNoReply}`,
608109
608156
  `Last analysis: ${sinceAnalysis}`,
608110
608157
  `Last agent output: ${sinceAgent}`,
608111
- state.nextAnalysisAfterMessages ? `Agent-requested next check after messages: ${state.nextAnalysisAfterMessages}` : "",
608112
- state.nextAnalysisAtMs ? `Agent-requested next check at: ${new Date(state.nextAnalysisAtMs).toISOString()}` : ""
608158
+ state.nextAnalysisAfterMessages ? `Agent-requested next check after messages: ${state.nextAnalysisAfterMessages}` : ""
608113
608159
  ].filter(Boolean).join("\n");
608114
608160
  }
608115
608161
  stateFor(channelId, nowMs) {
@@ -608121,15 +608167,6 @@ var init_stimulation = __esm({
608121
608167
  updatedAtMs: nowMs
608122
608168
  };
608123
608169
  }
608124
- applyTimeDecay(state, nowMs) {
608125
- const elapsedMs2 = Math.max(0, nowMs - (state.updatedAtMs || nowMs));
608126
- if (elapsedMs2 <= 0) return;
608127
- const halfLives = elapsedMs2 / (12 * 6e4);
608128
- state.attention = clamp017(state.attention * Math.pow(0.5, halfLives));
608129
- if (state.phase !== "engaged" || elapsedMs2 > 4 * 6e4) {
608130
- state.phase = phaseFromAttention(state.attention);
608131
- }
608132
- }
608133
608170
  };
608134
608171
  }
608135
608172
  });
@@ -610364,6 +610401,9 @@ function telegramDecisionRecoverableFlag(text) {
610364
610401
  }
610365
610402
  return void 0;
610366
610403
  }
610404
+ function telegramRouterTimeoutMs(configTimeoutMs, minMs = 15e3, maxMs = 6e4) {
610405
+ return Math.min(Math.max(configTimeoutMs ?? 3e4, minMs), maxMs);
610406
+ }
610367
610407
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
610368
610408
  for (const jsonText of telegramDecisionJsonCandidates(text)) {
610369
610409
  try {
@@ -610380,7 +610420,6 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
610380
610420
  const attentionDeltaRaw = Number(parsed["attention_delta"] ?? parsed["attentionDelta"]);
610381
610421
  const attentionScoreRaw = Number(parsed["attention_score"] ?? parsed["attentionScore"]);
610382
610422
  const nextMessagesRaw = Number(parsed["next_check_after_messages"] ?? parsed["nextCheckAfterMessages"]);
610383
- const nextMsRaw = Number(parsed["next_check_after_ms"] ?? parsed["nextCheckAfterMs"]);
610384
610423
  return {
610385
610424
  route,
610386
610425
  shouldReply,
@@ -610392,7 +610431,6 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
610392
610431
  attentionDelta: Number.isFinite(attentionDeltaRaw) ? Math.max(-1, Math.min(1, attentionDeltaRaw)) : void 0,
610393
610432
  attentionScore: Number.isFinite(attentionScoreRaw) ? Math.max(0, Math.min(1, attentionScoreRaw)) : void 0,
610394
610433
  nextCheckAfterMessages: Number.isFinite(nextMessagesRaw) ? Math.max(1, Math.floor(nextMessagesRaw)) : void 0,
610395
- nextCheckAfterMs: Number.isFinite(nextMsRaw) ? Math.max(0, Math.floor(nextMsRaw)) : void 0,
610396
610434
  silentDisposition: telegramDecisionNote(parsed, ["silent_disposition", "silentDisposition", "disposition"]),
610397
610435
  mentalNote: telegramDecisionNote(parsed, ["mental_note", "mentalNote", "observation", "insight"]),
610398
610436
  memoryNote: telegramDecisionNote(parsed, ["memory_note", "memoryNote", "memory"]),
@@ -612211,7 +612249,7 @@ External acquisition contract:
612211
612249
  chatAssociativeMemory = /* @__PURE__ */ new Map();
612212
612250
  /** Durable social cognition state by scoped Telegram chat key. */
612213
612251
  chatSocialState = /* @__PURE__ */ new Map();
612214
- /** Generic chronological attention cadence shared by live surfaces. */
612252
+ /** Generic deliverable/message attention cadence shared by live surfaces. */
612215
612253
  stimulation = new StimulationController();
612216
612254
  /** Throttles noisy "skipped group chatter" waterfall logs */
612217
612255
  groupSkipLogAt = /* @__PURE__ */ new Map();
@@ -612449,13 +612487,13 @@ External acquisition contract:
612449
612487
  decision.attentionScore !== void 0 ? `score=${decision.attentionScore.toFixed(2)}` : ""
612450
612488
  ].filter(Boolean).join(", ");
612451
612489
  const cadence = [
612452
- decision.nextCheckAfterMessages !== void 0 ? `after ${decision.nextCheckAfterMessages} message(s)` : "",
612453
- decision.nextCheckAfterMs !== void 0 ? `after ${Math.round(decision.nextCheckAfterMs / 1e3)}s` : ""
612490
+ decision.nextCheckAfterMessages !== void 0 ? `after ${decision.nextCheckAfterMessages} message(s)` : ""
612454
612491
  ].filter(Boolean).join(" or ");
612455
612492
  const lines = [
612456
612493
  `decision: ${route} (${decision.source}, confidence ${decision.confidence.toFixed(2)})`,
612457
612494
  attention ? `attention: ${attention}` : "",
612458
612495
  `reason: ${decision.reason}`,
612496
+ decision.diagnosticNote ? `router diagnostic: ${decision.diagnosticNote}` : "",
612459
612497
  decision.silentDisposition ? `silent disposition: ${decision.silentDisposition}` : "",
612460
612498
  decision.mentalNote ? `mental note: ${decision.mentalNote}` : "",
612461
612499
  decision.memoryNote ? `memory note: ${decision.memoryNote}` : "",
@@ -612472,6 +612510,7 @@ External acquisition contract:
612472
612510
  const route = decision.shouldReply ? `reply via ${decision.route}` : "silent";
612473
612511
  const primary = `attention decision: ${route} (${decision.source}, confidence ${decision.confidence.toFixed(2)}) - ${decision.reason}`;
612474
612512
  const notes2 = [
612513
+ decision.diagnosticNote ? `router diagnostic: ${decision.diagnosticNote}` : "",
612475
612514
  decision.silentDisposition ? `silent reflection: ${decision.silentDisposition}` : "",
612476
612515
  decision.mentalNote ? `mental note: ${decision.mentalNote}` : "",
612477
612516
  decision.memoryNote ? `memory note: ${decision.memoryNote}` : "",
@@ -612479,11 +612518,23 @@ External acquisition contract:
612479
612518
  ].filter(Boolean);
612480
612519
  this.tuiWrite(() => {
612481
612520
  renderTelegramSubAgentEvent(msg.username, primary);
612482
- for (const note of notes2.slice(0, 4)) {
612521
+ for (const note of notes2.slice(0, 5)) {
612483
612522
  renderTelegramSubAgentEvent(msg.username, note);
612484
612523
  }
612485
612524
  });
612486
612525
  }
612526
+ deliverTelegramAttentionDecision(sessionKey, msg, viewId, decision, salienceSignals, daydreamOpportunities = this.latestTelegramDaydreamOpportunityInputs(sessionKey)) {
612527
+ this.writeTelegramAttentionDecision(viewId, decision);
612528
+ this.mirrorTelegramAttentionDecision(msg, decision);
612529
+ this.commitTelegramSocialDecision(
612530
+ sessionKey,
612531
+ msg,
612532
+ decision,
612533
+ salienceSignals,
612534
+ daydreamOpportunities
612535
+ );
612536
+ this.applyTelegramStimulationDecision(sessionKey, decision);
612537
+ }
612487
612538
  normalizeTelegramCommandText(input) {
612488
612539
  const trimmed = input.trim();
612489
612540
  if (!trimmed.startsWith("/")) return input;
@@ -615352,27 +615403,59 @@ ${lines.join("\n")}`);
615352
615403
  phase: decision.attentionState,
615353
615404
  attentionDelta: decision.attentionDelta,
615354
615405
  attentionScore: decision.attentionScore,
615355
- nextAnalysisAfterMessages: decision.nextCheckAfterMessages,
615356
- nextAnalysisAfterMs: decision.nextCheckAfterMs
615406
+ nextAnalysisAfterMessages: decision.nextCheckAfterMessages
615357
615407
  });
615358
615408
  }
615359
- async telegramRouterJsonCompletion(backend, request) {
615409
+ async telegramRouterJsonCompletion(backend, request, diagnostics) {
615410
+ let jsonModeResult;
615411
+ let jsonModeError;
615360
615412
  try {
615361
- const result = await backend.chatCompletion({
615413
+ jsonModeResult = await backend.chatCompletion({
615362
615414
  ...request,
615363
615415
  responseFormat: TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT
615364
615416
  });
615365
- const visible = result.choices.some(
615417
+ const visible = jsonModeResult.choices.some(
615366
615418
  (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
615367
615419
  );
615368
- if (visible) return result;
615369
- } catch {
615420
+ if (visible) {
615421
+ if (diagnostics) diagnostics.jsonModeStatus = "visible";
615422
+ return jsonModeResult;
615423
+ }
615424
+ if (diagnostics) diagnostics.jsonModeStatus = "empty-after-strip";
615425
+ } catch (err) {
615426
+ jsonModeError = err;
615427
+ if (diagnostics) {
615428
+ diagnostics.jsonModeStatus = "threw";
615429
+ diagnostics.jsonModeError = err instanceof Error ? err.message : String(err);
615430
+ }
615431
+ }
615432
+ try {
615433
+ const plainResult = await backend.chatCompletion(request);
615434
+ if (diagnostics) {
615435
+ const plainVisible = plainResult.choices.some(
615436
+ (choice) => stripTelegramHiddenThinking(choice.message.content ?? "").trim().length > 0
615437
+ );
615438
+ diagnostics.plainStatus = plainVisible ? "visible" : "empty-after-strip";
615439
+ }
615440
+ return plainResult;
615441
+ } catch (err) {
615442
+ if (diagnostics) {
615443
+ diagnostics.plainStatus = "threw";
615444
+ diagnostics.plainError = err instanceof Error ? err.message : String(err);
615445
+ }
615446
+ if (jsonModeError instanceof Error && !(err instanceof Error)) throw jsonModeError;
615447
+ throw err;
615370
615448
  }
615371
- return backend.chatCompletion(request);
615372
615449
  }
615373
- async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs) {
615450
+ async repairTelegramInteractionDecision(backend, rawOutput, forcedRoute, timeoutMs, diagnostics) {
615374
615451
  const rawPreview = telegramRouterRawPreview(rawOutput, 4e3);
615375
- if (!rawPreview || telegramDecisionOutputHasDanglingJson(rawOutput)) return null;
615452
+ if (!rawPreview || telegramDecisionOutputHasDanglingJson(rawOutput)) {
615453
+ if (diagnostics) {
615454
+ diagnostics.repairStatus = "skipped";
615455
+ 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";
615456
+ }
615457
+ return null;
615458
+ }
615376
615459
  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
615460
  const prompt = [
615378
615461
  `Repair this Telegram attention-router output into strict JSON.`,
@@ -615384,7 +615467,9 @@ ${lines.join("\n")}`);
615384
615467
  `{"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
615468
  ``,
615386
615469
  `Original router output:`,
615387
- rawPreview
615470
+ rawPreview,
615471
+ ``,
615472
+ `/no_think`
615388
615473
  ].join("\n");
615389
615474
  try {
615390
615475
  const result = await this.telegramRouterJsonCompletion(backend, {
@@ -615398,15 +615483,22 @@ ${lines.join("\n")}`);
615398
615483
  tools: [],
615399
615484
  temperature: 0,
615400
615485
  maxTokens: 500,
615401
- timeoutMs: Math.min(Math.max(timeoutMs, 3e3), 8e3),
615486
+ timeoutMs: telegramRouterTimeoutMs(timeoutMs, 8e3, 2e4),
615402
615487
  think: false
615403
615488
  });
615404
615489
  const repairedText = result.choices[0]?.message?.content ?? "";
615405
- if (telegramDecisionRecoverableFlag(repairedText) === false) return null;
615490
+ if (telegramDecisionRecoverableFlag(repairedText) === false) {
615491
+ if (diagnostics) diagnostics.repairStatus = "no-recoverable-output";
615492
+ return null;
615493
+ }
615406
615494
  const parsed = parseTelegramInteractionDecision(repairedText, forcedRoute, {
615407
615495
  defaultShouldReply: false
615408
615496
  });
615409
- if (!parsed) return null;
615497
+ if (!parsed) {
615498
+ if (diagnostics) diagnostics.repairStatus = "no-recoverable-output";
615499
+ return null;
615500
+ }
615501
+ if (diagnostics) diagnostics.repairStatus = "recovered";
615410
615502
  return {
615411
615503
  ...parsed,
615412
615504
  reason: `recovered router decision: ${parsed.reason}`.slice(0, 240),
@@ -615414,20 +615506,26 @@ ${lines.join("\n")}`);
615414
615506
 
615415
615507
  [repaired router decision]
615416
615508
  ${repairedText}`,
615417
- mentalNote: parsed.mentalNote ?? "router decision recovered from non-JSON model output",
615418
- memoryNote: parsed.memoryNote ?? "router repair preserved the model-derived attention decision"
615509
+ mentalNote: parsed.mentalNote,
615510
+ memoryNote: parsed.memoryNote
615419
615511
  };
615420
- } catch {
615512
+ } catch (err) {
615513
+ if (diagnostics) {
615514
+ diagnostics.repairStatus = "threw";
615515
+ diagnostics.repairError = err instanceof Error ? err.message : String(err);
615516
+ }
615421
615517
  return null;
615422
615518
  }
615423
615519
  }
615424
- async retryTelegramInteractionDecisionStrict(backend, userPrompt, rawOutput, forcedRoute, timeoutMs) {
615520
+ async retryTelegramInteractionDecisionStrict(backend, userPrompt, rawOutput, forcedRoute, timeoutMs, diagnostics) {
615425
615521
  const invalidPreview = telegramRouterRawPreview(rawOutput, 1200) ?? "(empty assistant content)";
615426
615522
  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.`;
615523
+ const trimmedUserPrompt = userPrompt.length > 4e3 ? `…
615524
+ ${userPrompt.slice(-4e3)}` : userPrompt;
615427
615525
  const retryPrompt = [
615428
615526
  `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.`,
615527
+ `Make a fresh model-derived attention decision from the context below. Do not use hard-coded mention or keyword triggers.`,
615528
+ `Return exactly one JSON object and no prose. No <think> tags. No commentary.`,
615431
615529
  routeInstruction,
615432
615530
  ``,
615433
615531
  `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,29 +615533,39 @@ ${repairedText}`,
615435
615533
  `Invalid previous output, for diagnostics only:`,
615436
615534
  invalidPreview,
615437
615535
  ``,
615438
- `Full router context:`,
615439
- userPrompt
615536
+ `Router context (trailing-window):`,
615537
+ trimmedUserPrompt,
615538
+ ``,
615539
+ `/no_think`
615440
615540
  ].join("\n");
615441
615541
  try {
615442
615542
  const result = await this.telegramRouterJsonCompletion(backend, {
615443
615543
  messages: [
615444
615544
  {
615445
615545
  role: "system",
615446
- content: "You are a strict JSON Telegram attention router. Output one valid JSON object only."
615546
+ content: "You are a strict JSON Telegram attention router. Output one valid JSON object only. Never emit <think> tags."
615447
615547
  },
615448
615548
  { role: "user", content: retryPrompt }
615449
615549
  ],
615450
615550
  tools: [],
615451
615551
  temperature: 0,
615452
615552
  maxTokens: 1200,
615453
- timeoutMs: Math.min(Math.max(timeoutMs, 5e3), 15e3),
615553
+ timeoutMs: telegramRouterTimeoutMs(timeoutMs, 1e4, 3e4),
615454
615554
  think: false
615455
615555
  });
615456
615556
  const retryText = result.choices[0]?.message?.content ?? "";
615557
+ if (diagnostics) diagnostics.strictRetryPreview = telegramRouterRawPreview(retryText, 320);
615457
615558
  const parsed = parseTelegramInteractionDecision(retryText, forcedRoute, {
615458
615559
  defaultShouldReply: false
615459
615560
  });
615460
- if (!parsed) return null;
615561
+ if (!parsed) {
615562
+ if (diagnostics) {
615563
+ const cleaned = stripTelegramHiddenThinking(retryText).trim();
615564
+ diagnostics.strictRetryStatus = cleaned ? "unparseable" : "empty";
615565
+ }
615566
+ return null;
615567
+ }
615568
+ if (diagnostics) diagnostics.strictRetryStatus = "recovered";
615461
615569
  return {
615462
615570
  ...parsed,
615463
615571
  reason: `strict router retry: ${parsed.reason}`.slice(0, 240),
@@ -615465,9 +615573,13 @@ ${repairedText}`,
615465
615573
 
615466
615574
  [strict router retry]
615467
615575
  ${retryText}`,
615468
- mentalNote: parsed.mentalNote ?? "strict router retry produced a valid attention decision"
615576
+ mentalNote: parsed.mentalNote
615469
615577
  };
615470
- } catch {
615578
+ } catch (err) {
615579
+ if (diagnostics) {
615580
+ diagnostics.strictRetryStatus = "threw";
615581
+ diagnostics.strictRetryError = err instanceof Error ? err.message : String(err);
615582
+ }
615471
615583
  return null;
615472
615584
  }
615473
615585
  }
@@ -615488,17 +615600,16 @@ ${retryText}`,
615488
615600
  forceAnalyze: daydreamForceCheck
615489
615601
  });
615490
615602
  if (!config) {
615491
- const fallback2 = {
615603
+ const fallback = {
615492
615604
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
615493
615605
  shouldReply: false,
615494
615606
  confidence: 0,
615495
615607
  reason: "router inference unavailable; no model-derived reply decision",
615496
615608
  source: "inference-unavailable",
615497
615609
  silentDisposition: "retained as context without replying",
615498
- mentalNote: "router unavailable, so no model-derived attention note was produced"
615610
+ diagnosticNote: "router unavailable; live mental, memory, and relationship notes were not generated"
615499
615611
  };
615500
- this.applyTelegramStimulationDecision(sessionKey, fallback2);
615501
- return fallback2;
615612
+ return fallback;
615502
615613
  }
615503
615614
  const backend = new OllamaAgenticBackend(
615504
615615
  config.backendUrl,
@@ -615529,7 +615640,7 @@ ${retryText}`,
615529
615640
  `Ingress discipline: this Telegram message has already been retained as chat context. should_reply controls only whether to emit a visible reply.`,
615530
615641
  `Memory discipline: use durable associative user memory, relationships, prior actions, and recent context to infer whether this speaker is continuing a bot-related thread. A mention is not required when the semantic target is clearly the bot or an ongoing bot-mediated discussion.`,
615531
615642
  `Channel daydream discipline: a daydream artifact may highlight relationship signals, unresolved questions, or possible reply opportunities from idle reflection. It can justify analyzing this turn, but it does not force a reply. Reply only if the current user entry makes the intervention timely and socially appropriate.`,
615532
- `Stimulation discipline: also set attention_state, attention_delta, and optional next_check_after_messages/next_check_after_ms. These control future analysis cadence only; they do not force a reply. Use engaged for active back-and-forth, observing for likely relevant context, cooldown for recently irrelevant context, and idle for ambient chatter.`,
615643
+ `Stimulation discipline: also set attention_state, attention_delta, and optional next_check_after_messages. This is a message-deliverable cadence only; do not use elapsed time as an analysis trigger. Use engaged for active back-and-forth, observing for likely relevant context, cooldown for recently irrelevant context, and idle for ambient chatter.`,
615533
615644
  forcedLine,
615534
615645
  ``,
615535
615646
  `Tool context: ${toolContext}`,
@@ -615553,6 +615664,7 @@ ${stimulationProbe.context}`,
615553
615664
  `Current Telegram message text (untrusted user data):
615554
615665
  ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615555
615666
  ].filter(Boolean).join("\n");
615667
+ const diagnostics = {};
615556
615668
  try {
615557
615669
  const result = await this.telegramRouterJsonCompletion(backend, {
615558
615670
  messages: [
@@ -615564,26 +615676,25 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615564
615676
  ],
615565
615677
  tools: [],
615566
615678
  temperature: 0,
615567
- maxTokens: 700,
615568
- timeoutMs: Math.min(Math.max(config.timeoutMs ?? 3e4, 5e3), 15e3),
615679
+ maxTokens: 1e3,
615680
+ timeoutMs: telegramRouterTimeoutMs(config.timeoutMs),
615569
615681
  think: false
615570
- });
615682
+ }, diagnostics);
615571
615683
  const text = result.choices[0]?.message?.content ?? "";
615572
615684
  const parsed = parseTelegramInteractionDecision(text, forcedRoute, {
615573
615685
  defaultShouldReply: false
615574
615686
  });
615575
615687
  if (parsed) {
615576
- this.applyTelegramStimulationDecision(sessionKey, parsed);
615577
615688
  return parsed;
615578
615689
  }
615579
615690
  const repaired = await this.repairTelegramInteractionDecision(
615580
615691
  backend,
615581
615692
  text,
615582
615693
  forcedRoute,
615583
- config.timeoutMs ?? 3e4
615694
+ config.timeoutMs ?? 3e4,
615695
+ diagnostics
615584
615696
  );
615585
615697
  if (repaired) {
615586
- this.applyTelegramStimulationDecision(sessionKey, repaired);
615587
615698
  return repaired;
615588
615699
  }
615589
615700
  const strictRetry = await this.retryTelegramInteractionDecisionStrict(
@@ -615591,39 +615702,122 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615591
615702
  userPrompt,
615592
615703
  text,
615593
615704
  forcedRoute,
615594
- config.timeoutMs ?? 3e4
615705
+ config.timeoutMs ?? 3e4,
615706
+ diagnostics
615595
615707
  );
615596
615708
  if (strictRetry) {
615597
- this.applyTelegramStimulationDecision(sessionKey, strictRetry);
615598
615709
  return strictRetry;
615599
615710
  }
615600
615711
  const invalidRouterPreview = telegramRouterRawPreview(text);
615601
- const fallback2 = {
615712
+ const failureNarrative = this.summarizeTelegramRouterFailure(diagnostics);
615713
+ const fallback = {
615602
615714
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
615603
615715
  shouldReply: false,
615604
615716
  confidence: 0,
615605
615717
  reason: "router output was not valid decision JSON after repair/retry; no model-derived reply decision",
615606
615718
  source: "inference-unavailable",
615607
615719
  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,
615720
+ diagnosticNote: this.composeTelegramRouterDiagnosticNote(
615721
+ invalidRouterPreview,
615722
+ failureNarrative,
615723
+ 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"
615724
+ ),
615610
615725
  raw: text
615611
615726
  };
615612
- this.applyTelegramStimulationDecision(sessionKey, fallback2);
615613
- return fallback2;
615614
- } catch {
615727
+ return fallback;
615728
+ } catch (err) {
615729
+ const failureNarrative = this.summarizeTelegramRouterFailure(diagnostics);
615730
+ const errMsg = err instanceof Error ? err.message : String(err);
615731
+ const fallback = {
615732
+ route: forcedRoute ?? (isGroup ? "action" : "chat"),
615733
+ shouldReply: false,
615734
+ confidence: 0,
615735
+ reason: `router inference failed; no model-derived reply decision (${errMsg.slice(0, 160)})`,
615736
+ source: "inference-unavailable",
615737
+ silentDisposition: "retained as context without replying",
615738
+ diagnosticNote: this.composeTelegramRouterDiagnosticNote(
615739
+ void 0,
615740
+ failureNarrative,
615741
+ `router failed before live notes were generated: ${errMsg.slice(0, 160)}`
615742
+ )
615743
+ };
615744
+ return fallback;
615615
615745
  }
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"
615746
+ }
615747
+ /**
615748
+ * Reduce captured per-step diagnostics into:
615749
+ * - `summary`: a short outcome-level diagnostic
615750
+ * - `detail`: a longer ordered trace for operator debugging
615751
+ * - `operatorHint`: an operational hint (e.g. "ollama backend appears to be injecting <think> tags despite think:false")
615752
+ */
615753
+ summarizeTelegramRouterFailure(diag) {
615754
+ const parts = [];
615755
+ const detailParts = [];
615756
+ let thinkInjectionSuspected = false;
615757
+ let networkErrorSeen = false;
615758
+ if (diag.jsonModeStatus === "threw") {
615759
+ parts.push(`json-mode call threw`);
615760
+ detailParts.push(`json-mode: threw (${diag.jsonModeError ?? "no detail"})`);
615761
+ networkErrorSeen = true;
615762
+ } else if (diag.jsonModeStatus === "empty-after-strip") {
615763
+ parts.push(`json-mode returned empty content (likely <think>-only)`);
615764
+ detailParts.push(`json-mode: empty-after-strip`);
615765
+ thinkInjectionSuspected = true;
615766
+ } else if (diag.jsonModeStatus === "visible") {
615767
+ detailParts.push(`json-mode: visible`);
615768
+ }
615769
+ if (diag.plainStatus === "threw") {
615770
+ parts.push(`plain call threw`);
615771
+ detailParts.push(`plain: threw (${diag.plainError ?? "no detail"})`);
615772
+ networkErrorSeen = true;
615773
+ } else if (diag.plainStatus === "empty-after-strip") {
615774
+ parts.push(`plain call returned empty content`);
615775
+ detailParts.push(`plain: empty-after-strip`);
615776
+ thinkInjectionSuspected = true;
615777
+ } else if (diag.plainStatus === "visible") {
615778
+ detailParts.push(`plain: visible-but-unparseable`);
615779
+ }
615780
+ if (diag.repairStatus === "skipped") {
615781
+ detailParts.push(`repair: skipped (${diag.repairError ?? "no recoverable input"})`);
615782
+ } else if (diag.repairStatus === "no-recoverable-output") {
615783
+ detailParts.push(`repair: returned non-recoverable JSON`);
615784
+ } else if (diag.repairStatus === "threw") {
615785
+ detailParts.push(`repair: threw (${diag.repairError ?? "no detail"})`);
615786
+ networkErrorSeen = true;
615787
+ } else if (diag.repairStatus === "recovered") {
615788
+ detailParts.push(`repair: recovered`);
615789
+ }
615790
+ if (diag.strictRetryStatus === "empty") {
615791
+ detailParts.push(`strict-retry: empty`);
615792
+ thinkInjectionSuspected = true;
615793
+ } else if (diag.strictRetryStatus === "unparseable") {
615794
+ detailParts.push(`strict-retry: unparseable (preview="${diag.strictRetryPreview ?? ""}")`);
615795
+ } else if (diag.strictRetryStatus === "threw") {
615796
+ detailParts.push(`strict-retry: threw (${diag.strictRetryError ?? "no detail"})`);
615797
+ networkErrorSeen = true;
615798
+ } else if (diag.strictRetryStatus === "recovered") {
615799
+ detailParts.push(`strict-retry: recovered`);
615800
+ }
615801
+ let operatorHint;
615802
+ if (networkErrorSeen) {
615803
+ operatorHint = "router backend appears unreachable or rate-limited; continued conversation depends on recovery";
615804
+ } else if (thinkInjectionSuspected) {
615805
+ operatorHint = "router model emitted <think>-only or unclosed-think output; conversation continuity preserved but inference is degraded";
615806
+ }
615807
+ return {
615808
+ summary: parts.join("; "),
615809
+ detail: detailParts.join("; "),
615810
+ operatorHint
615624
615811
  };
615625
- this.applyTelegramStimulationDecision(sessionKey, fallback);
615626
- return fallback;
615812
+ }
615813
+ composeTelegramRouterDiagnosticNote(invalidRouterPreview, failureNarrative, headline) {
615814
+ const segments = [];
615815
+ segments.push(headline);
615816
+ if (failureNarrative.summary) segments.push(failureNarrative.summary);
615817
+ if (invalidRouterPreview) segments.push(`invalid router output preview: ${invalidRouterPreview}`);
615818
+ if (failureNarrative.detail) segments.push(`router-failure trace: ${failureNarrative.detail}`);
615819
+ if (failureNarrative.operatorHint) segments.push(failureNarrative.operatorHint);
615820
+ return segments.join(" | ").slice(0, 900);
615627
615821
  }
615628
615822
  buildTelegramWorkspaceContext(modelTier, budget = 14e3) {
615629
615823
  if (!this.repoRoot) return "";
@@ -616389,11 +616583,10 @@ Join: ${newUrl}`);
616389
616583
  if (isGroup) {
616390
616584
  const attentionViewId2 = this.registerTelegramAttentionView(msg, existing.toolContext || toolContext, "active Telegram thread");
616391
616585
  const decision2 = await this.inferTelegramInteractionDecision(msg, existing.toolContext || toolContext);
616392
- this.writeTelegramAttentionDecision(attentionViewId2, decision2);
616393
- this.mirrorTelegramAttentionDecision(msg, decision2);
616394
- this.commitTelegramSocialDecision(
616586
+ this.deliverTelegramAttentionDecision(
616395
616587
  sessionKey,
616396
616588
  msg,
616589
+ attentionViewId2,
616397
616590
  decision2,
616398
616591
  this.telegramMessageIdentitySalienceSignals(msg),
616399
616592
  this.markLatestTelegramDaydreamOpportunitiesConsidered(sessionKey, msg)
@@ -616434,11 +616627,10 @@ Join: ${newUrl}`);
616434
616627
  }
616435
616628
  const attentionViewId = this.registerTelegramAttentionView(msg, toolContext);
616436
616629
  const decision = await this.inferTelegramInteractionDecision(msg, toolContext);
616437
- this.writeTelegramAttentionDecision(attentionViewId, decision);
616438
- this.mirrorTelegramAttentionDecision(msg, decision);
616439
- this.commitTelegramSocialDecision(
616630
+ this.deliverTelegramAttentionDecision(
616440
616631
  sessionKey,
616441
616632
  msg,
616633
+ attentionViewId,
616442
616634
  decision,
616443
616635
  this.telegramMessageIdentitySalienceSignals(msg),
616444
616636
  this.markLatestTelegramDaydreamOpportunitiesConsidered(sessionKey, msg)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.79",
3
+ "version": "1.0.81",
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.81",
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.81",
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",