omnius 1.0.80 → 1.0.82

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
@@ -539007,6 +539007,18 @@ function injectNoThinkDirective(messages2) {
539007
539007
  /no_think`;
539008
539008
  return messages2.map((m2, i2) => i2 === lastUserIdx ? { ...m2, content: annotated } : m2);
539009
539009
  }
539010
+ function backendHttpErrorDetail(text) {
539011
+ const trimmed = text.trimStart();
539012
+ const isHtml = trimmed.startsWith("<!") || trimmed.startsWith("<html");
539013
+ return isHtml ? `(received HTML error page — backend may be behind a proxy/CDN that is timing out)` : text.slice(0, 200);
539014
+ }
539015
+ function isOllamaModelNotFoundResponse(status, text, model) {
539016
+ if (status !== 404)
539017
+ return false;
539018
+ const lower = text.toLowerCase();
539019
+ const modelLower = model.toLowerCase();
539020
+ return lower.includes("model") && lower.includes("not found") || lower.includes("not_found_error") || modelLower.length > 0 && lower.includes(modelLower) && lower.includes("not found");
539021
+ }
539010
539022
  function computeEffectiveThink(params) {
539011
539023
  if (process.env["OMNIUS_FORCE_NO_THINK"] === "1")
539012
539024
  return false;
@@ -551452,11 +551464,17 @@ ${description}`
551452
551464
  if (responseFormat !== void 0) {
551453
551465
  body["response_format"] = responseFormat;
551454
551466
  }
551455
- const poolSlot = shouldUseOllamaPoolForBaseUrl(this.baseUrl) ? await getOllamaPool({ baseInstanceUrl: this.baseUrl }).acquire({
551467
+ let poolSlot = shouldUseOllamaPoolForBaseUrl(this.baseUrl) ? await getOllamaPool({ baseInstanceUrl: this.baseUrl }).acquire({
551456
551468
  model: this.model
551457
551469
  }) : null;
551458
- const requestBaseUrl = poolSlot?.baseUrl ?? this.baseUrl;
551470
+ let requestBaseUrl = poolSlot?.baseUrl ?? this.baseUrl;
551459
551471
  let poolSuccess = false;
551472
+ const releasePoolSlot = (success) => {
551473
+ if (!poolSlot)
551474
+ return;
551475
+ poolSlot.release(success);
551476
+ poolSlot = null;
551477
+ };
551460
551478
  const combineAbortSignals = (signals) => {
551461
551479
  const filtered = signals.filter((s2) => s2 instanceof AbortSignal);
551462
551480
  if (filtered.length === 0)
@@ -551491,11 +551509,26 @@ ${description}`
551491
551509
  };
551492
551510
  if (combinedAbortSignal)
551493
551511
  fetchOpts.signal = combinedAbortSignal;
551494
- const resp = await fetch(`${requestBaseUrl}/v1/chat/completions`, fetchOpts);
551512
+ let resp = await fetch(`${requestBaseUrl}/v1/chat/completions`, fetchOpts);
551513
+ if (!resp.ok) {
551514
+ const text = await resp.text().catch(() => "");
551515
+ if (poolSlot?.poolOwned && isOllamaModelNotFoundResponse(resp.status, text, this.model)) {
551516
+ releasePoolSlot(false);
551517
+ requestBaseUrl = this.baseUrl;
551518
+ resp = await fetch(`${requestBaseUrl}/v1/chat/completions`, fetchOpts);
551519
+ if (resp.ok) {
551520
+ } else {
551521
+ const retryText = await resp.text().catch(() => "");
551522
+ throw new Error(`Backend HTTP ${resp.status}: ${backendHttpErrorDetail(retryText)}`);
551523
+ }
551524
+ } else {
551525
+ const detail = backendHttpErrorDetail(text);
551526
+ throw new Error(`Backend HTTP ${resp.status}: ${detail}`);
551527
+ }
551528
+ }
551495
551529
  if (!resp.ok) {
551496
551530
  const text = await resp.text().catch(() => "");
551497
- const isHtml = text.trimStart().startsWith("<!") || text.trimStart().startsWith("<html");
551498
- const detail = isHtml ? `(received HTML error page — backend may be behind a proxy/CDN that is timing out)` : text.slice(0, 200);
551531
+ const detail = backendHttpErrorDetail(text);
551499
551532
  throw new Error(`Backend HTTP ${resp.status}: ${detail}`);
551500
551533
  }
551501
551534
  const data = await resp.json();
@@ -551577,7 +551610,7 @@ ${description}`
551577
551610
  } : void 0
551578
551611
  };
551579
551612
  } finally {
551580
- poolSlot?.release(poolSuccess);
551613
+ releasePoolSlot(poolSuccess);
551581
551614
  }
551582
551615
  }
551583
551616
  /** Anthropic Messages API translation — converts our standard format to/from Anthropic's. */
@@ -551686,8 +551719,8 @@ ${description}`
551686
551719
  }
551687
551720
  /**
551688
551721
  * SSE streaming variant — yields StreamChunks as tokens arrive.
551689
- * Uses `stream: true` and the current thinking setting.
551690
- * The existing chatCompletion() method is completely unmodified.
551722
+ * Uses `stream: true`, the current thinking setting, and the same
551723
+ * Ollama pool routing as non-stream completions.
551691
551724
  */
551692
551725
  async *chatCompletionStream(request) {
551693
551726
  const cleanedMessages = normalizeMessagesForStrictOpenAI(request.messages.map((m2) => m2.role === "assistant" && typeof m2.content === "string" ? { ...m2, content: stripThinkBlocks(m2.content) } : m2));
@@ -551715,100 +551748,125 @@ ${description}`
551715
551748
  stream_options: { include_usage: true },
551716
551749
  think: effectiveThink
551717
551750
  };
551718
- const streamFetchOpts = {
551719
- method: "POST",
551720
- headers: this.authHeaders(),
551721
- body: JSON.stringify(body)
551751
+ let poolSlot = shouldUseOllamaPoolForBaseUrl(this.baseUrl) ? await getOllamaPool({ baseInstanceUrl: this.baseUrl }).acquire({
551752
+ model: this.model
551753
+ }) : null;
551754
+ let requestBaseUrl = poolSlot?.baseUrl ?? this.baseUrl;
551755
+ let poolSuccess = false;
551756
+ const releasePoolSlot = (success) => {
551757
+ if (!poolSlot)
551758
+ return;
551759
+ poolSlot.release(success);
551760
+ poolSlot = null;
551722
551761
  };
551723
- if (this._abortSignal)
551724
- streamFetchOpts.signal = this._abortSignal;
551725
- const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, streamFetchOpts);
551726
- if (!resp.ok) {
551727
- const text = await resp.text().catch(() => "");
551728
- const isHtml = text.trimStart().startsWith("<!") || text.trimStart().startsWith("<html");
551729
- const detail = isHtml ? `(received HTML error page — backend may be behind a proxy/CDN that is timing out)` : text.slice(0, 200);
551730
- throw new Error(`Backend HTTP ${resp.status}: ${detail}`);
551731
- }
551732
- let sseBuffer = "";
551733
- const decoder = new TextDecoder();
551734
- let accumulatedContent = "";
551735
- let accumulatedThinking = "";
551736
- let sawReasoningTokens = false;
551737
- for await (const rawChunk of resp.body) {
551738
- sseBuffer += decoder.decode(rawChunk, { stream: true });
551739
- const parts = sseBuffer.split("\n\n");
551740
- sseBuffer = parts.pop();
551741
- for (const part of parts) {
551742
- const line = part.trim();
551743
- if (!line)
551744
- continue;
551745
- if (line === "data: [DONE]") {
551746
- this._finalizeStreamGuard(effectiveThink, accumulatedContent, accumulatedThinking, sawReasoningTokens);
551747
- return;
551762
+ try {
551763
+ const streamFetchOpts = {
551764
+ method: "POST",
551765
+ headers: this.authHeaders(),
551766
+ body: JSON.stringify(body)
551767
+ };
551768
+ if (this._abortSignal)
551769
+ streamFetchOpts.signal = this._abortSignal;
551770
+ let resp = await fetch(`${requestBaseUrl}/v1/chat/completions`, streamFetchOpts);
551771
+ if (!resp.ok) {
551772
+ const text = await resp.text().catch(() => "");
551773
+ if (poolSlot?.poolOwned && isOllamaModelNotFoundResponse(resp.status, text, this.model)) {
551774
+ releasePoolSlot(false);
551775
+ requestBaseUrl = this.baseUrl;
551776
+ resp = await fetch(`${requestBaseUrl}/v1/chat/completions`, streamFetchOpts);
551777
+ if (!resp.ok) {
551778
+ const retryText = await resp.text().catch(() => "");
551779
+ throw new Error(`Backend HTTP ${resp.status}: ${backendHttpErrorDetail(retryText)}`);
551780
+ }
551781
+ } else {
551782
+ throw new Error(`Backend HTTP ${resp.status}: ${backendHttpErrorDetail(text)}`);
551748
551783
  }
551749
- if (!line.startsWith("data: "))
551750
- continue;
551751
- try {
551752
- const data = JSON.parse(line.slice(6));
551753
- const choices = data.choices ?? [];
551754
- const chunkUsageEarly = data.usage;
551755
- if (chunkUsageEarly) {
551756
- yield {
551757
- type: "usage",
551758
- usage: {
551759
- promptTokens: chunkUsageEarly.prompt_tokens ?? 0,
551760
- completionTokens: chunkUsageEarly.completion_tokens ?? 0,
551761
- totalTokens: chunkUsageEarly.total_tokens ?? 0
551762
- }
551763
- };
551784
+ }
551785
+ let sseBuffer = "";
551786
+ const decoder = new TextDecoder();
551787
+ let accumulatedContent = "";
551788
+ let accumulatedThinking = "";
551789
+ let sawReasoningTokens = false;
551790
+ for await (const rawChunk of resp.body) {
551791
+ sseBuffer += decoder.decode(rawChunk, { stream: true });
551792
+ const parts = sseBuffer.split("\n\n");
551793
+ sseBuffer = parts.pop();
551794
+ for (const part of parts) {
551795
+ const line = part.trim();
551796
+ if (!line)
551797
+ continue;
551798
+ if (line === "data: [DONE]") {
551799
+ this._finalizeStreamGuard(effectiveThink, accumulatedContent, accumulatedThinking, sawReasoningTokens);
551800
+ poolSuccess = true;
551801
+ return;
551764
551802
  }
551765
- const choice = choices[0];
551766
- if (!choice)
551803
+ if (!line.startsWith("data: "))
551767
551804
  continue;
551768
- const delta = choice.delta;
551769
- const finishReason = choice.finish_reason;
551770
- const reasoningToken = delta?.reasoning ?? delta?.reasoning_content;
551771
- if (reasoningToken && effectiveThink) {
551772
- sawReasoningTokens = true;
551773
- accumulatedThinking += reasoningToken;
551774
- yield { type: "content", content: reasoningToken, thinking: true };
551775
- }
551776
- if (delta?.content) {
551777
- accumulatedContent += delta.content;
551778
- yield { type: "content", content: delta.content };
551779
- }
551780
- const tcDeltas = delta?.tool_calls;
551781
- if (tcDeltas) {
551782
- for (const tcd of tcDeltas) {
551783
- const fn = tcd.function;
551805
+ try {
551806
+ const data = JSON.parse(line.slice(6));
551807
+ const choices = data.choices ?? [];
551808
+ const chunkUsageEarly = data.usage;
551809
+ if (chunkUsageEarly) {
551784
551810
  yield {
551785
- type: "tool_call_delta",
551786
- toolCallIndex: tcd.index ?? 0,
551787
- toolCallId: tcd.id || void 0,
551788
- toolCallName: fn?.name || void 0,
551789
- toolCallArgs: fn?.arguments || void 0
551811
+ type: "usage",
551812
+ usage: {
551813
+ promptTokens: chunkUsageEarly.prompt_tokens ?? 0,
551814
+ completionTokens: chunkUsageEarly.completion_tokens ?? 0,
551815
+ totalTokens: chunkUsageEarly.total_tokens ?? 0
551816
+ }
551790
551817
  };
551791
551818
  }
551792
- }
551793
- const chunkUsage = data.usage;
551794
- if (chunkUsage) {
551795
- yield {
551796
- type: "usage",
551797
- usage: {
551798
- promptTokens: chunkUsage.prompt_tokens ?? 0,
551799
- completionTokens: chunkUsage.completion_tokens ?? 0,
551800
- totalTokens: chunkUsage.total_tokens ?? 0
551819
+ const choice = choices[0];
551820
+ if (!choice)
551821
+ continue;
551822
+ const delta = choice.delta;
551823
+ const finishReason = choice.finish_reason;
551824
+ const reasoningToken = delta?.reasoning ?? delta?.reasoning_content;
551825
+ if (reasoningToken && effectiveThink) {
551826
+ sawReasoningTokens = true;
551827
+ accumulatedThinking += reasoningToken;
551828
+ yield { type: "content", content: reasoningToken, thinking: true };
551829
+ }
551830
+ if (delta?.content) {
551831
+ accumulatedContent += delta.content;
551832
+ yield { type: "content", content: delta.content };
551833
+ }
551834
+ const tcDeltas = delta?.tool_calls;
551835
+ if (tcDeltas) {
551836
+ for (const tcd of tcDeltas) {
551837
+ const fn = tcd.function;
551838
+ yield {
551839
+ type: "tool_call_delta",
551840
+ toolCallIndex: tcd.index ?? 0,
551841
+ toolCallId: tcd.id || void 0,
551842
+ toolCallName: fn?.name || void 0,
551843
+ toolCallArgs: fn?.arguments || void 0
551844
+ };
551801
551845
  }
551802
- };
551803
- }
551804
- if (finishReason) {
551805
- yield { type: "finish", finishReason };
551846
+ }
551847
+ const chunkUsage = data.usage;
551848
+ if (chunkUsage) {
551849
+ yield {
551850
+ type: "usage",
551851
+ usage: {
551852
+ promptTokens: chunkUsage.prompt_tokens ?? 0,
551853
+ completionTokens: chunkUsage.completion_tokens ?? 0,
551854
+ totalTokens: chunkUsage.total_tokens ?? 0
551855
+ }
551856
+ };
551857
+ }
551858
+ if (finishReason) {
551859
+ yield { type: "finish", finishReason };
551860
+ }
551861
+ } catch {
551806
551862
  }
551807
- } catch {
551808
551863
  }
551809
551864
  }
551865
+ this._finalizeStreamGuard(effectiveThink, accumulatedContent, accumulatedThinking, sawReasoningTokens);
551866
+ poolSuccess = true;
551867
+ } finally {
551868
+ releasePoolSlot(poolSuccess);
551810
551869
  }
551811
- this._finalizeStreamGuard(effectiveThink, accumulatedContent, accumulatedThinking, sawReasoningTokens);
551812
551870
  }
551813
551871
  /** Reconstruct a raw-looking assistant response from the streamed
551814
551872
  * parts, then feed it into the loop-guard. Used at stream end (both
@@ -608034,7 +608092,7 @@ function phaseFromAttention(attention) {
608034
608092
  function parseStimulationPhase(value2) {
608035
608093
  return normalizePhase(value2);
608036
608094
  }
608037
- var DEFAULT_STATE, PHASE_DEFAULTS, PHASE_FLOORS, StimulationController;
608095
+ var DEFAULT_STATE, PHASE_MESSAGE_BUDGETS, PHASE_FLOORS, StimulationController;
608038
608096
  var init_stimulation = __esm({
608039
608097
  "packages/cli/src/tui/stimulation.ts"() {
608040
608098
  "use strict";
@@ -608047,11 +608105,11 @@ var init_stimulation = __esm({
608047
608105
  consecutiveNoReply: 0,
608048
608106
  updatedAtMs: 0
608049
608107
  };
608050
- PHASE_DEFAULTS = {
608051
- idle: { messages: 6, ms: 10 * 6e4 },
608052
- cooldown: { messages: 4, ms: 5 * 6e4 },
608053
- observing: { messages: 2, ms: 2 * 6e4 },
608054
- engaged: { messages: 1, ms: 45e3 }
608108
+ PHASE_MESSAGE_BUDGETS = {
608109
+ idle: 6,
608110
+ cooldown: 4,
608111
+ observing: 2,
608112
+ engaged: 1
608055
608113
  };
608056
608114
  PHASE_FLOORS = {
608057
608115
  idle: 0,
@@ -608082,7 +608140,6 @@ var init_stimulation = __esm({
608082
608140
  observe(input) {
608083
608141
  const now = input.nowMs ?? Date.now();
608084
608142
  const state = this.stateFor(input.channelId, now);
608085
- this.applyTimeDecay(state, now);
608086
608143
  state.lastStimulusAtMs = now;
608087
608144
  state.updatedAtMs = now;
608088
608145
  state.messagesSinceAnalysis += 1;
@@ -608093,22 +608150,15 @@ var init_stimulation = __esm({
608093
608150
  if (input.replyToAgent) state.attention = Math.max(state.attention, 0.84);
608094
608151
  if (input.activeAgent) state.attention = Math.max(state.attention, 0.68);
608095
608152
  state.phase = phaseFromAttention(state.attention);
608096
- const cadence = PHASE_DEFAULTS[state.phase];
608097
- const messageBudget = state.nextAnalysisAfterMessages ?? cadence.messages;
608098
- const timeDue = state.nextAnalysisAtMs !== void 0 ? now >= state.nextAnalysisAtMs : state.lastAnalysisAtMs !== void 0 && now - state.lastAnalysisAtMs >= cadence.ms;
608099
- const shouldAnalyze = !state.lastAnalysisAtMs || metadataStimulus || state.messagesSinceAnalysis >= messageBudget || timeDue;
608153
+ const messageBudget = state.nextAnalysisAfterMessages ?? PHASE_MESSAGE_BUDGETS[state.phase];
608154
+ const shouldAnalyze = !state.lastAnalysisAtMs || metadataStimulus || state.messagesSinceAnalysis >= messageBudget;
608100
608155
  let reason = "cadence-hold";
608101
608156
  if (!state.lastAnalysisAtMs) reason = "initial-analysis";
608102
608157
  else if (input.privateChannel) reason = "private-channel";
608103
608158
  else if (input.replyToAgent) reason = "reply-to-agent";
608104
608159
  else if (input.directSignal) reason = "direct-platform-signal";
608105
608160
  else if (input.activeAgent) reason = "active-agent-thread";
608106
- else if (state.messagesSinceAnalysis >= messageBudget) reason = "chronological-sample";
608107
- else if (timeDue) reason = "time-sample";
608108
- if (shouldAnalyze) {
608109
- state.lastAnalysisAtMs = now;
608110
- state.messagesSinceAnalysis = 0;
608111
- }
608161
+ else if (state.messagesSinceAnalysis >= messageBudget) reason = "message-sample";
608112
608162
  this.states.set(input.channelId, cloneState(state));
608113
608163
  return {
608114
608164
  shouldAnalyze,
@@ -608119,7 +608169,6 @@ var init_stimulation = __esm({
608119
608169
  }
608120
608170
  applyAgentDecision(channelId, decision, nowMs = Date.now()) {
608121
608171
  const state = this.stateFor(channelId, nowMs);
608122
- this.applyTimeDecay(state, nowMs);
608123
608172
  if (Number.isFinite(decision.attentionScore)) {
608124
608173
  state.attention = clamp017(Number(decision.attentionScore));
608125
608174
  } else if (Number.isFinite(decision.attentionDelta)) {
@@ -608135,14 +608184,14 @@ var init_stimulation = __esm({
608135
608184
  }
608136
608185
  state.consecutiveNoReply = decision.shouldReply ? 0 : state.consecutiveNoReply + 1;
608137
608186
  state.nextAnalysisAfterMessages = Number.isFinite(decision.nextAnalysisAfterMessages) ? Math.max(1, Math.floor(Number(decision.nextAnalysisAfterMessages))) : void 0;
608138
- state.nextAnalysisAtMs = Number.isFinite(decision.nextAnalysisAfterMs) ? nowMs + Math.max(0, Number(decision.nextAnalysisAfterMs)) : void 0;
608187
+ state.lastAnalysisAtMs = nowMs;
608188
+ state.messagesSinceAnalysis = 0;
608139
608189
  state.updatedAtMs = nowMs;
608140
608190
  this.states.set(channelId, cloneState(state));
608141
608191
  return cloneState(state);
608142
608192
  }
608143
608193
  recordAgentOutput(channelId, nowMs = Date.now()) {
608144
608194
  const state = this.stateFor(channelId, nowMs);
608145
- this.applyTimeDecay(state, nowMs);
608146
608195
  state.phase = "engaged";
608147
608196
  state.attention = Math.max(state.attention, 0.78);
608148
608197
  state.lastAgentOutputAtMs = nowMs;
@@ -608164,8 +608213,7 @@ var init_stimulation = __esm({
608164
608213
  `Consecutive no-reply decisions: ${state.consecutiveNoReply}`,
608165
608214
  `Last analysis: ${sinceAnalysis}`,
608166
608215
  `Last agent output: ${sinceAgent}`,
608167
- state.nextAnalysisAfterMessages ? `Agent-requested next check after messages: ${state.nextAnalysisAfterMessages}` : "",
608168
- state.nextAnalysisAtMs ? `Agent-requested next check at: ${new Date(state.nextAnalysisAtMs).toISOString()}` : ""
608216
+ state.nextAnalysisAfterMessages ? `Agent-requested next check after messages: ${state.nextAnalysisAfterMessages}` : ""
608169
608217
  ].filter(Boolean).join("\n");
608170
608218
  }
608171
608219
  stateFor(channelId, nowMs) {
@@ -608177,15 +608225,6 @@ var init_stimulation = __esm({
608177
608225
  updatedAtMs: nowMs
608178
608226
  };
608179
608227
  }
608180
- applyTimeDecay(state, nowMs) {
608181
- const elapsedMs2 = Math.max(0, nowMs - (state.updatedAtMs || nowMs));
608182
- if (elapsedMs2 <= 0) return;
608183
- const halfLives = elapsedMs2 / (12 * 6e4);
608184
- state.attention = clamp017(state.attention * Math.pow(0.5, halfLives));
608185
- if (state.phase !== "engaged" || elapsedMs2 > 4 * 6e4) {
608186
- state.phase = phaseFromAttention(state.attention);
608187
- }
608188
- }
608189
608228
  };
608190
608229
  }
608191
608230
  });
@@ -610420,6 +610459,10 @@ function telegramDecisionRecoverableFlag(text) {
610420
610459
  }
610421
610460
  return void 0;
610422
610461
  }
610462
+ function telegramRouterTimeoutMs(configTimeoutMs, minMs = 15e3, _legacyMaxMs) {
610463
+ const configured = Number.isFinite(configTimeoutMs) && (configTimeoutMs ?? 0) > 0 ? configTimeoutMs : 3e5;
610464
+ return Math.max(configured, minMs);
610465
+ }
610423
610466
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
610424
610467
  for (const jsonText of telegramDecisionJsonCandidates(text)) {
610425
610468
  try {
@@ -610436,7 +610479,6 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
610436
610479
  const attentionDeltaRaw = Number(parsed["attention_delta"] ?? parsed["attentionDelta"]);
610437
610480
  const attentionScoreRaw = Number(parsed["attention_score"] ?? parsed["attentionScore"]);
610438
610481
  const nextMessagesRaw = Number(parsed["next_check_after_messages"] ?? parsed["nextCheckAfterMessages"]);
610439
- const nextMsRaw = Number(parsed["next_check_after_ms"] ?? parsed["nextCheckAfterMs"]);
610440
610482
  return {
610441
610483
  route,
610442
610484
  shouldReply,
@@ -610448,7 +610490,6 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
610448
610490
  attentionDelta: Number.isFinite(attentionDeltaRaw) ? Math.max(-1, Math.min(1, attentionDeltaRaw)) : void 0,
610449
610491
  attentionScore: Number.isFinite(attentionScoreRaw) ? Math.max(0, Math.min(1, attentionScoreRaw)) : void 0,
610450
610492
  nextCheckAfterMessages: Number.isFinite(nextMessagesRaw) ? Math.max(1, Math.floor(nextMessagesRaw)) : void 0,
610451
- nextCheckAfterMs: Number.isFinite(nextMsRaw) ? Math.max(0, Math.floor(nextMsRaw)) : void 0,
610452
610493
  silentDisposition: telegramDecisionNote(parsed, ["silent_disposition", "silentDisposition", "disposition"]),
610453
610494
  mentalNote: telegramDecisionNote(parsed, ["mental_note", "mentalNote", "observation", "insight"]),
610454
610495
  memoryNote: telegramDecisionNote(parsed, ["memory_note", "memoryNote", "memory"]),
@@ -610758,6 +610799,21 @@ function cleanTelegramVisibleReply(text, options2 = {}) {
610758
610799
  if (!filtered) return "";
610759
610800
  return dedupeTelegramVisibleReply(filtered);
610760
610801
  }
610802
+ function summarizeTelegramInferenceError(message2) {
610803
+ if (/aborted due to timeout|aborterror|timed? out/i.test(message2)) {
610804
+ return "backend inference timed out before a reply was delivered";
610805
+ }
610806
+ if (/model ['"]?[^'"]+['"]? not found|not_found_error/i.test(message2)) {
610807
+ return "the configured model was not available on the selected Ollama runner";
610808
+ }
610809
+ if (/Backend HTTP 5\d\d/i.test(message2)) {
610810
+ return "the backend returned a transient server error";
610811
+ }
610812
+ if (/Backend HTTP 4\d\d/i.test(message2)) {
610813
+ return message2.slice(0, 180);
610814
+ }
610815
+ return message2.slice(0, 180) || "unknown backend failure";
610816
+ }
610761
610817
  function dedupeTelegramVisibleReply(text) {
610762
610818
  const paragraphs = text.split(/\n{2,}/);
610763
610819
  const seenParagraphs = /* @__PURE__ */ new Set();
@@ -612267,7 +612323,7 @@ External acquisition contract:
612267
612323
  chatAssociativeMemory = /* @__PURE__ */ new Map();
612268
612324
  /** Durable social cognition state by scoped Telegram chat key. */
612269
612325
  chatSocialState = /* @__PURE__ */ new Map();
612270
- /** Generic chronological attention cadence shared by live surfaces. */
612326
+ /** Generic deliverable/message attention cadence shared by live surfaces. */
612271
612327
  stimulation = new StimulationController();
612272
612328
  /** Throttles noisy "skipped group chatter" waterfall logs */
612273
612329
  groupSkipLogAt = /* @__PURE__ */ new Map();
@@ -612505,13 +612561,13 @@ External acquisition contract:
612505
612561
  decision.attentionScore !== void 0 ? `score=${decision.attentionScore.toFixed(2)}` : ""
612506
612562
  ].filter(Boolean).join(", ");
612507
612563
  const cadence = [
612508
- decision.nextCheckAfterMessages !== void 0 ? `after ${decision.nextCheckAfterMessages} message(s)` : "",
612509
- decision.nextCheckAfterMs !== void 0 ? `after ${Math.round(decision.nextCheckAfterMs / 1e3)}s` : ""
612564
+ decision.nextCheckAfterMessages !== void 0 ? `after ${decision.nextCheckAfterMessages} message(s)` : ""
612510
612565
  ].filter(Boolean).join(" or ");
612511
612566
  const lines = [
612512
612567
  `decision: ${route} (${decision.source}, confidence ${decision.confidence.toFixed(2)})`,
612513
612568
  attention ? `attention: ${attention}` : "",
612514
612569
  `reason: ${decision.reason}`,
612570
+ decision.diagnosticNote ? `router diagnostic: ${decision.diagnosticNote}` : "",
612515
612571
  decision.silentDisposition ? `silent disposition: ${decision.silentDisposition}` : "",
612516
612572
  decision.mentalNote ? `mental note: ${decision.mentalNote}` : "",
612517
612573
  decision.memoryNote ? `memory note: ${decision.memoryNote}` : "",
@@ -612528,6 +612584,7 @@ External acquisition contract:
612528
612584
  const route = decision.shouldReply ? `reply via ${decision.route}` : "silent";
612529
612585
  const primary = `attention decision: ${route} (${decision.source}, confidence ${decision.confidence.toFixed(2)}) - ${decision.reason}`;
612530
612586
  const notes2 = [
612587
+ decision.diagnosticNote ? `router diagnostic: ${decision.diagnosticNote}` : "",
612531
612588
  decision.silentDisposition ? `silent reflection: ${decision.silentDisposition}` : "",
612532
612589
  decision.mentalNote ? `mental note: ${decision.mentalNote}` : "",
612533
612590
  decision.memoryNote ? `memory note: ${decision.memoryNote}` : "",
@@ -612535,11 +612592,23 @@ External acquisition contract:
612535
612592
  ].filter(Boolean);
612536
612593
  this.tuiWrite(() => {
612537
612594
  renderTelegramSubAgentEvent(msg.username, primary);
612538
- for (const note of notes2.slice(0, 4)) {
612595
+ for (const note of notes2.slice(0, 5)) {
612539
612596
  renderTelegramSubAgentEvent(msg.username, note);
612540
612597
  }
612541
612598
  });
612542
612599
  }
612600
+ deliverTelegramAttentionDecision(sessionKey, msg, viewId, decision, salienceSignals, daydreamOpportunities = this.latestTelegramDaydreamOpportunityInputs(sessionKey)) {
612601
+ this.writeTelegramAttentionDecision(viewId, decision);
612602
+ this.mirrorTelegramAttentionDecision(msg, decision);
612603
+ this.commitTelegramSocialDecision(
612604
+ sessionKey,
612605
+ msg,
612606
+ decision,
612607
+ salienceSignals,
612608
+ daydreamOpportunities
612609
+ );
612610
+ this.applyTelegramStimulationDecision(sessionKey, decision);
612611
+ }
612543
612612
  normalizeTelegramCommandText(input) {
612544
612613
  const trimmed = input.trim();
612545
612614
  if (!trimmed.startsWith("/")) return input;
@@ -615408,8 +615477,7 @@ ${lines.join("\n")}`);
615408
615477
  phase: decision.attentionState,
615409
615478
  attentionDelta: decision.attentionDelta,
615410
615479
  attentionScore: decision.attentionScore,
615411
- nextAnalysisAfterMessages: decision.nextCheckAfterMessages,
615412
- nextAnalysisAfterMs: decision.nextCheckAfterMs
615480
+ nextAnalysisAfterMessages: decision.nextCheckAfterMessages
615413
615481
  });
615414
615482
  }
615415
615483
  async telegramRouterJsonCompletion(backend, request, diagnostics) {
@@ -615489,7 +615557,7 @@ ${lines.join("\n")}`);
615489
615557
  tools: [],
615490
615558
  temperature: 0,
615491
615559
  maxTokens: 500,
615492
- timeoutMs: Math.min(Math.max(timeoutMs, 3e3), 8e3),
615560
+ timeoutMs: telegramRouterTimeoutMs(timeoutMs, 8e3, 2e4),
615493
615561
  think: false
615494
615562
  });
615495
615563
  const repairedText = result.choices[0]?.message?.content ?? "";
@@ -615512,8 +615580,8 @@ ${lines.join("\n")}`);
615512
615580
 
615513
615581
  [repaired router decision]
615514
615582
  ${repairedText}`,
615515
- mentalNote: parsed.mentalNote ?? "router decision recovered from non-JSON model output",
615516
- memoryNote: parsed.memoryNote ?? "router repair preserved the model-derived attention decision"
615583
+ mentalNote: parsed.mentalNote,
615584
+ memoryNote: parsed.memoryNote
615517
615585
  };
615518
615586
  } catch (err) {
615519
615587
  if (diagnostics) {
@@ -615556,7 +615624,7 @@ ${userPrompt.slice(-4e3)}` : userPrompt;
615556
615624
  tools: [],
615557
615625
  temperature: 0,
615558
615626
  maxTokens: 1200,
615559
- timeoutMs: Math.min(Math.max(timeoutMs, 5e3), 15e3),
615627
+ timeoutMs: telegramRouterTimeoutMs(timeoutMs, 1e4, 3e4),
615560
615628
  think: false
615561
615629
  });
615562
615630
  const retryText = result.choices[0]?.message?.content ?? "";
@@ -615579,7 +615647,7 @@ ${userPrompt.slice(-4e3)}` : userPrompt;
615579
615647
 
615580
615648
  [strict router retry]
615581
615649
  ${retryText}`,
615582
- mentalNote: parsed.mentalNote ?? "strict router retry produced a valid attention decision"
615650
+ mentalNote: parsed.mentalNote
615583
615651
  };
615584
615652
  } catch (err) {
615585
615653
  if (diagnostics) {
@@ -615613,9 +615681,8 @@ ${retryText}`,
615613
615681
  reason: "router inference unavailable; no model-derived reply decision",
615614
615682
  source: "inference-unavailable",
615615
615683
  silentDisposition: "retained as context without replying",
615616
- mentalNote: "router unavailable, so no model-derived attention note was produced"
615684
+ diagnosticNote: "router unavailable; live mental, memory, and relationship notes were not generated"
615617
615685
  };
615618
- this.applyTelegramStimulationDecision(sessionKey, fallback);
615619
615686
  return fallback;
615620
615687
  }
615621
615688
  const backend = new OllamaAgenticBackend(
@@ -615647,7 +615714,7 @@ ${retryText}`,
615647
615714
  `Ingress discipline: this Telegram message has already been retained as chat context. should_reply controls only whether to emit a visible reply.`,
615648
615715
  `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.`,
615649
615716
  `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.`,
615650
- `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.`,
615717
+ `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.`,
615651
615718
  forcedLine,
615652
615719
  ``,
615653
615720
  `Tool context: ${toolContext}`,
@@ -615683,8 +615750,8 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615683
615750
  ],
615684
615751
  tools: [],
615685
615752
  temperature: 0,
615686
- maxTokens: 700,
615687
- timeoutMs: Math.min(Math.max(config.timeoutMs ?? 3e4, 5e3), 15e3),
615753
+ maxTokens: 1e3,
615754
+ timeoutMs: telegramRouterTimeoutMs(config.timeoutMs),
615688
615755
  think: false
615689
615756
  }, diagnostics);
615690
615757
  const text = result.choices[0]?.message?.content ?? "";
@@ -615692,7 +615759,6 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615692
615759
  defaultShouldReply: false
615693
615760
  });
615694
615761
  if (parsed) {
615695
- this.applyTelegramStimulationDecision(sessionKey, parsed);
615696
615762
  return parsed;
615697
615763
  }
615698
615764
  const repaired = await this.repairTelegramInteractionDecision(
@@ -615703,7 +615769,6 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615703
615769
  diagnostics
615704
615770
  );
615705
615771
  if (repaired) {
615706
- this.applyTelegramStimulationDecision(sessionKey, repaired);
615707
615772
  return repaired;
615708
615773
  }
615709
615774
  const strictRetry = await this.retryTelegramInteractionDecisionStrict(
@@ -615715,7 +615780,6 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615715
615780
  diagnostics
615716
615781
  );
615717
615782
  if (strictRetry) {
615718
- this.applyTelegramStimulationDecision(sessionKey, strictRetry);
615719
615783
  return strictRetry;
615720
615784
  }
615721
615785
  const invalidRouterPreview = telegramRouterRawPreview(text);
@@ -615727,15 +615791,13 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615727
615791
  reason: "router output was not valid decision JSON after repair/retry; no model-derived reply decision",
615728
615792
  source: "inference-unavailable",
615729
615793
  silentDisposition: "retained as context without replying because the router decision could not be parsed",
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,
615794
+ diagnosticNote: this.composeTelegramRouterDiagnosticNote(
615795
+ invalidRouterPreview,
615796
+ failureNarrative,
615797
+ 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"
615798
+ ),
615736
615799
  raw: text
615737
615800
  };
615738
- this.applyTelegramStimulationDecision(sessionKey, fallback);
615739
615801
  return fallback;
615740
615802
  } catch (err) {
615741
615803
  const failureNarrative = this.summarizeTelegramRouterFailure(diagnostics);
@@ -615747,19 +615809,20 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615747
615809
  reason: `router inference failed; no model-derived reply decision (${errMsg.slice(0, 160)})`,
615748
615810
  source: "inference-unavailable",
615749
615811
  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
615812
+ diagnosticNote: this.composeTelegramRouterDiagnosticNote(
615813
+ void 0,
615814
+ failureNarrative,
615815
+ `router failed before live notes were generated: ${errMsg.slice(0, 160)}`
615816
+ )
615753
615817
  };
615754
- this.applyTelegramStimulationDecision(sessionKey, fallback);
615755
615818
  return fallback;
615756
615819
  }
615757
615820
  }
615758
615821
  /**
615759
615822
  * 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")
615823
+ * - `summary`: a short outcome-level diagnostic
615824
+ * - `detail`: a longer ordered trace for operator debugging
615825
+ * - `operatorHint`: an operational hint (e.g. "ollama backend appears to be injecting <think> tags despite think:false")
615763
615826
  */
615764
615827
  summarizeTelegramRouterFailure(diag) {
615765
615828
  const parts = [];
@@ -615809,24 +615872,26 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
615809
615872
  } else if (diag.strictRetryStatus === "recovered") {
615810
615873
  detailParts.push(`strict-retry: recovered`);
615811
615874
  }
615812
- let relationshipHint;
615875
+ let operatorHint;
615813
615876
  if (networkErrorSeen) {
615814
- relationshipHint = "router backend appears unreachable or rate-limited; continued conversation depends on recovery";
615877
+ operatorHint = "router backend appears unreachable or rate-limited; continued conversation depends on recovery";
615815
615878
  } else if (thinkInjectionSuspected) {
615816
- relationshipHint = "router model emitted <think>-only or unclosed-think output; conversation continuity preserved but inference is degraded";
615879
+ operatorHint = "router model emitted <think>-only or unclosed-think output; conversation continuity preserved but inference is degraded";
615817
615880
  }
615818
615881
  return {
615819
615882
  summary: parts.join("; "),
615820
615883
  detail: detailParts.join("; "),
615821
- relationshipHint
615884
+ operatorHint
615822
615885
  };
615823
615886
  }
615824
- composeTelegramRouterMemoryNote(invalidRouterPreview, detail) {
615887
+ composeTelegramRouterDiagnosticNote(invalidRouterPreview, failureNarrative, headline) {
615825
615888
  const segments = [];
615889
+ segments.push(headline);
615890
+ if (failureNarrative.summary) segments.push(failureNarrative.summary);
615826
615891
  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(" | ");
615892
+ if (failureNarrative.detail) segments.push(`router-failure trace: ${failureNarrative.detail}`);
615893
+ if (failureNarrative.operatorHint) segments.push(failureNarrative.operatorHint);
615894
+ return segments.join(" | ").slice(0, 900);
615830
615895
  }
615831
615896
  buildTelegramWorkspaceContext(modelTier, budget = 14e3) {
615832
615897
  if (!this.repoRoot) return "";
@@ -616592,11 +616657,10 @@ Join: ${newUrl}`);
616592
616657
  if (isGroup) {
616593
616658
  const attentionViewId2 = this.registerTelegramAttentionView(msg, existing.toolContext || toolContext, "active Telegram thread");
616594
616659
  const decision2 = await this.inferTelegramInteractionDecision(msg, existing.toolContext || toolContext);
616595
- this.writeTelegramAttentionDecision(attentionViewId2, decision2);
616596
- this.mirrorTelegramAttentionDecision(msg, decision2);
616597
- this.commitTelegramSocialDecision(
616660
+ this.deliverTelegramAttentionDecision(
616598
616661
  sessionKey,
616599
616662
  msg,
616663
+ attentionViewId2,
616600
616664
  decision2,
616601
616665
  this.telegramMessageIdentitySalienceSignals(msg),
616602
616666
  this.markLatestTelegramDaydreamOpportunitiesConsidered(sessionKey, msg)
@@ -616637,11 +616701,10 @@ Join: ${newUrl}`);
616637
616701
  }
616638
616702
  const attentionViewId = this.registerTelegramAttentionView(msg, toolContext);
616639
616703
  const decision = await this.inferTelegramInteractionDecision(msg, toolContext);
616640
- this.writeTelegramAttentionDecision(attentionViewId, decision);
616641
- this.mirrorTelegramAttentionDecision(msg, decision);
616642
- this.commitTelegramSocialDecision(
616704
+ this.deliverTelegramAttentionDecision(
616643
616705
  sessionKey,
616644
616706
  msg,
616707
+ attentionViewId,
616645
616708
  decision,
616646
616709
  this.telegramMessageIdentitySalienceSignals(msg),
616647
616710
  this.markLatestTelegramDaydreamOpportunitiesConsidered(sessionKey, msg)
@@ -616993,7 +617056,8 @@ Join: ${newUrl}`);
616993
617056
  await this.editLiveMessage(msg.chatId, liveMessageId, `Error: ${escapeTelegramHTML(errMsg)}`).catch(() => {
616994
617057
  });
616995
617058
  } else {
616996
- await this.replyToTelegramMessage(msg, "Sorry, I couldn't process that quick chat message.").catch(() => {
617059
+ const summary = summarizeTelegramInferenceError(errMsg);
617060
+ await this.replyToTelegramMessage(msg, `Sorry, quick chat inference failed: ${summary}.`).catch(() => {
616997
617061
  });
616998
617062
  }
616999
617063
  } finally {
@@ -617071,10 +617135,11 @@ ${conversationStream}`
617071
617135
  tools: [],
617072
617136
  temperature: 0.4,
617073
617137
  maxTokens: 700,
617074
- timeoutMs: Math.min(config.timeoutMs ?? 3e4, 3e4),
617138
+ timeoutMs: Math.max(config.timeoutMs ?? 3e5, 6e4),
617075
617139
  think: false
617076
617140
  };
617077
617141
  let accumulated = "";
617142
+ let streamError;
617078
617143
  const streamable = backend;
617079
617144
  const stream = typeof streamable.chatCompletionStream === "function" ? streamable.chatCompletionStream(request) : null;
617080
617145
  if (stream && typeof stream[Symbol.asyncIterator] === "function") {
@@ -617085,12 +617150,23 @@ ${conversationStream}`
617085
617150
  await onToken(accumulated);
617086
617151
  }
617087
617152
  }
617088
- } catch {
617153
+ } catch (err) {
617154
+ streamError = err;
617089
617155
  accumulated = "";
617090
617156
  }
617091
617157
  }
617092
617158
  if (!accumulated.trim()) {
617093
- const result = await backend.chatCompletion(request);
617159
+ let result;
617160
+ try {
617161
+ result = await backend.chatCompletion(request);
617162
+ } catch (err) {
617163
+ if (streamError) {
617164
+ const streamMsg = streamError instanceof Error ? streamError.message : String(streamError);
617165
+ const retryMsg = err instanceof Error ? err.message : String(err);
617166
+ throw new Error(`streaming failed (${streamMsg}); non-stream retry failed (${retryMsg})`);
617167
+ }
617168
+ throw err;
617169
+ }
617094
617170
  accumulated = result.choices[0]?.message?.content ?? "";
617095
617171
  if (accumulated) await onToken(accumulated);
617096
617172
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.80",
3
+ "version": "1.0.82",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.80",
9
+ "version": "1.0.82",
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.80",
3
+ "version": "1.0.82",
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",