omnius 1.0.29 → 1.0.30

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
@@ -598971,6 +598971,182 @@ ${result.output}`,
598971
598971
  }
598972
598972
  });
598973
598973
 
598974
+ // packages/cli/src/tui/stimulation.ts
598975
+ function clamp016(value2) {
598976
+ return Math.max(0, Math.min(1, value2));
598977
+ }
598978
+ function cloneState(state) {
598979
+ return { ...state };
598980
+ }
598981
+ function normalizePhase(value2) {
598982
+ const raw = String(value2 ?? "").trim().toLowerCase();
598983
+ return raw === "idle" || raw === "observing" || raw === "engaged" || raw === "cooldown" ? raw : void 0;
598984
+ }
598985
+ function phaseFromAttention(attention) {
598986
+ if (attention >= 0.72) return "engaged";
598987
+ if (attention >= 0.38) return "observing";
598988
+ if (attention >= 0.14) return "cooldown";
598989
+ return "idle";
598990
+ }
598991
+ function parseStimulationPhase(value2) {
598992
+ return normalizePhase(value2);
598993
+ }
598994
+ var DEFAULT_STATE, PHASE_DEFAULTS, PHASE_FLOORS, StimulationController;
598995
+ var init_stimulation = __esm({
598996
+ "packages/cli/src/tui/stimulation.ts"() {
598997
+ "use strict";
598998
+ DEFAULT_STATE = {
598999
+ phase: "idle",
599000
+ attention: 0,
599001
+ lastStimulusAtMs: 0,
599002
+ messagesSinceAnalysis: 0,
599003
+ messagesSinceAgentOutput: 0,
599004
+ consecutiveNoReply: 0,
599005
+ updatedAtMs: 0
599006
+ };
599007
+ PHASE_DEFAULTS = {
599008
+ idle: { messages: 6, ms: 10 * 6e4 },
599009
+ cooldown: { messages: 4, ms: 5 * 6e4 },
599010
+ observing: { messages: 2, ms: 2 * 6e4 },
599011
+ engaged: { messages: 1, ms: 45e3 }
599012
+ };
599013
+ PHASE_FLOORS = {
599014
+ idle: 0,
599015
+ cooldown: 0.18,
599016
+ observing: 0.45,
599017
+ engaged: 0.78
599018
+ };
599019
+ StimulationController = class {
599020
+ states = /* @__PURE__ */ new Map();
599021
+ getState(channelId) {
599022
+ return cloneState(this.stateFor(channelId, Date.now()));
599023
+ }
599024
+ setState(channelId, state) {
599025
+ if (!state) return;
599026
+ const now = Date.now();
599027
+ this.states.set(channelId, {
599028
+ ...DEFAULT_STATE,
599029
+ ...state,
599030
+ phase: normalizePhase(state.phase) ?? DEFAULT_STATE.phase,
599031
+ attention: clamp016(Number.isFinite(state.attention) ? Number(state.attention) : DEFAULT_STATE.attention),
599032
+ updatedAtMs: Number.isFinite(state.updatedAtMs) ? Number(state.updatedAtMs) : now,
599033
+ lastStimulusAtMs: Number.isFinite(state.lastStimulusAtMs) ? Number(state.lastStimulusAtMs) : now,
599034
+ messagesSinceAnalysis: Math.max(0, Math.floor(Number(state.messagesSinceAnalysis ?? 0))),
599035
+ messagesSinceAgentOutput: Math.max(0, Math.floor(Number(state.messagesSinceAgentOutput ?? 0))),
599036
+ consecutiveNoReply: Math.max(0, Math.floor(Number(state.consecutiveNoReply ?? 0)))
599037
+ });
599038
+ }
599039
+ observe(input) {
599040
+ const now = input.nowMs ?? Date.now();
599041
+ const state = this.stateFor(input.channelId, now);
599042
+ this.applyTimeDecay(state, now);
599043
+ state.lastStimulusAtMs = now;
599044
+ state.updatedAtMs = now;
599045
+ state.messagesSinceAnalysis += 1;
599046
+ state.messagesSinceAgentOutput += 1;
599047
+ const metadataStimulus = input.privateChannel || input.directSignal || input.replyToAgent || input.activeAgent || input.forceAnalyze;
599048
+ if (input.privateChannel) state.attention = Math.max(state.attention, 0.82);
599049
+ if (input.directSignal) state.attention = Math.max(state.attention, 0.76);
599050
+ if (input.replyToAgent) state.attention = Math.max(state.attention, 0.84);
599051
+ if (input.activeAgent) state.attention = Math.max(state.attention, 0.68);
599052
+ state.phase = phaseFromAttention(state.attention);
599053
+ const cadence = PHASE_DEFAULTS[state.phase];
599054
+ const messageBudget = state.nextAnalysisAfterMessages ?? cadence.messages;
599055
+ const timeDue = state.nextAnalysisAtMs !== void 0 ? now >= state.nextAnalysisAtMs : state.lastAnalysisAtMs !== void 0 && now - state.lastAnalysisAtMs >= cadence.ms;
599056
+ const shouldAnalyze = !state.lastAnalysisAtMs || metadataStimulus || state.messagesSinceAnalysis >= messageBudget || timeDue;
599057
+ let reason = "cadence-hold";
599058
+ if (!state.lastAnalysisAtMs) reason = "initial-analysis";
599059
+ else if (input.privateChannel) reason = "private-channel";
599060
+ else if (input.replyToAgent) reason = "reply-to-agent";
599061
+ else if (input.directSignal) reason = "direct-platform-signal";
599062
+ else if (input.activeAgent) reason = "active-agent-thread";
599063
+ else if (state.messagesSinceAnalysis >= messageBudget) reason = "chronological-sample";
599064
+ else if (timeDue) reason = "time-sample";
599065
+ if (shouldAnalyze) {
599066
+ state.lastAnalysisAtMs = now;
599067
+ state.messagesSinceAnalysis = 0;
599068
+ }
599069
+ this.states.set(input.channelId, cloneState(state));
599070
+ return {
599071
+ shouldAnalyze,
599072
+ reason,
599073
+ state: cloneState(state),
599074
+ context: this.formatContext(input.channelId)
599075
+ };
599076
+ }
599077
+ applyAgentDecision(channelId, decision, nowMs = Date.now()) {
599078
+ const state = this.stateFor(channelId, nowMs);
599079
+ this.applyTimeDecay(state, nowMs);
599080
+ if (Number.isFinite(decision.attentionScore)) {
599081
+ state.attention = clamp016(Number(decision.attentionScore));
599082
+ } else if (Number.isFinite(decision.attentionDelta)) {
599083
+ state.attention = clamp016(state.attention + Number(decision.attentionDelta));
599084
+ } else {
599085
+ state.attention = clamp016(state.attention + (decision.shouldReply ? 0.22 : -0.1));
599086
+ }
599087
+ if (decision.phase) {
599088
+ state.attention = Math.max(state.attention, PHASE_FLOORS[decision.phase]);
599089
+ state.phase = decision.phase;
599090
+ } else {
599091
+ state.phase = phaseFromAttention(state.attention);
599092
+ }
599093
+ state.consecutiveNoReply = decision.shouldReply ? 0 : state.consecutiveNoReply + 1;
599094
+ state.nextAnalysisAfterMessages = Number.isFinite(decision.nextAnalysisAfterMessages) ? Math.max(1, Math.floor(Number(decision.nextAnalysisAfterMessages))) : void 0;
599095
+ state.nextAnalysisAtMs = Number.isFinite(decision.nextAnalysisAfterMs) ? nowMs + Math.max(0, Number(decision.nextAnalysisAfterMs)) : void 0;
599096
+ state.updatedAtMs = nowMs;
599097
+ this.states.set(channelId, cloneState(state));
599098
+ return cloneState(state);
599099
+ }
599100
+ recordAgentOutput(channelId, nowMs = Date.now()) {
599101
+ const state = this.stateFor(channelId, nowMs);
599102
+ this.applyTimeDecay(state, nowMs);
599103
+ state.phase = "engaged";
599104
+ state.attention = Math.max(state.attention, 0.78);
599105
+ state.lastAgentOutputAtMs = nowMs;
599106
+ state.messagesSinceAgentOutput = 0;
599107
+ state.consecutiveNoReply = 0;
599108
+ state.updatedAtMs = nowMs;
599109
+ this.states.set(channelId, cloneState(state));
599110
+ return cloneState(state);
599111
+ }
599112
+ formatContext(channelId) {
599113
+ const state = this.stateFor(channelId, Date.now());
599114
+ const sinceAnalysis = state.lastAnalysisAtMs ? `${Math.max(0, Math.round((Date.now() - state.lastAnalysisAtMs) / 1e3))}s` : "never";
599115
+ const sinceAgent = state.lastAgentOutputAtMs ? `${Math.max(0, Math.round((Date.now() - state.lastAgentOutputAtMs) / 1e3))}s` : "never";
599116
+ return [
599117
+ `Stimulation phase: ${state.phase}`,
599118
+ `Attention score: ${state.attention.toFixed(2)}`,
599119
+ `Messages since analysis: ${state.messagesSinceAnalysis}`,
599120
+ `Messages since agent output: ${state.messagesSinceAgentOutput}`,
599121
+ `Consecutive no-reply decisions: ${state.consecutiveNoReply}`,
599122
+ `Last analysis: ${sinceAnalysis}`,
599123
+ `Last agent output: ${sinceAgent}`,
599124
+ state.nextAnalysisAfterMessages ? `Agent-requested next check after messages: ${state.nextAnalysisAfterMessages}` : "",
599125
+ state.nextAnalysisAtMs ? `Agent-requested next check at: ${new Date(state.nextAnalysisAtMs).toISOString()}` : ""
599126
+ ].filter(Boolean).join("\n");
599127
+ }
599128
+ stateFor(channelId, nowMs) {
599129
+ const existing = this.states.get(channelId);
599130
+ if (existing) return cloneState(existing);
599131
+ return {
599132
+ ...DEFAULT_STATE,
599133
+ lastStimulusAtMs: nowMs,
599134
+ updatedAtMs: nowMs
599135
+ };
599136
+ }
599137
+ applyTimeDecay(state, nowMs) {
599138
+ const elapsedMs2 = Math.max(0, nowMs - (state.updatedAtMs || nowMs));
599139
+ if (elapsedMs2 <= 0) return;
599140
+ const halfLives = elapsedMs2 / (12 * 6e4);
599141
+ state.attention = clamp016(state.attention * Math.pow(0.5, halfLives));
599142
+ if (state.phase !== "engaged" || elapsedMs2 > 4 * 6e4) {
599143
+ state.phase = phaseFromAttention(state.attention);
599144
+ }
599145
+ }
599146
+ };
599147
+ }
599148
+ });
599149
+
598974
599150
  // packages/cli/src/tui/vision-ingress.ts
598975
599151
  var vision_ingress_exports = {};
598976
599152
  __export(vision_ingress_exports, {
@@ -599143,13 +599319,22 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
599143
599319
  const confidenceRaw = Number(parsed["confidence"]);
599144
599320
  const confidence = Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : 0;
599145
599321
  const reason = String(parsed["reason"] ?? "live inference decision").slice(0, 240);
599322
+ const attentionDeltaRaw = Number(parsed["attention_delta"] ?? parsed["attentionDelta"]);
599323
+ const attentionScoreRaw = Number(parsed["attention_score"] ?? parsed["attentionScore"]);
599324
+ const nextMessagesRaw = Number(parsed["next_check_after_messages"] ?? parsed["nextCheckAfterMessages"]);
599325
+ const nextMsRaw = Number(parsed["next_check_after_ms"] ?? parsed["nextCheckAfterMs"]);
599146
599326
  return {
599147
599327
  route,
599148
599328
  shouldReply,
599149
599329
  confidence,
599150
599330
  reason,
599151
599331
  source: forcedRoute ? "forced-mode" : "live-inference",
599152
- raw: text
599332
+ raw: text,
599333
+ attentionState: parseStimulationPhase(parsed["attention_state"] ?? parsed["attentionState"]),
599334
+ attentionDelta: Number.isFinite(attentionDeltaRaw) ? Math.max(-1, Math.min(1, attentionDeltaRaw)) : void 0,
599335
+ attentionScore: Number.isFinite(attentionScoreRaw) ? Math.max(0, Math.min(1, attentionScoreRaw)) : void 0,
599336
+ nextCheckAfterMessages: Number.isFinite(nextMessagesRaw) ? Math.max(1, Math.floor(nextMessagesRaw)) : void 0,
599337
+ nextCheckAfterMs: Number.isFinite(nextMsRaw) ? Math.max(0, Math.floor(nextMsRaw)) : void 0
599153
599338
  };
599154
599339
  } catch {
599155
599340
  return null;
@@ -599982,6 +600167,7 @@ var init_telegram_bridge = __esm({
599982
600167
  init_scoped_personality();
599983
600168
  init_telegram_creative_tools();
599984
600169
  init_omnius_directory();
600170
+ init_stimulation();
599985
600171
  TELEGRAM_SAFETY_PROMPT = `
599986
600172
  CRITICAL SAFETY NOTICE — PUBLIC TELEGRAM CHANNEL
599987
600173
 
@@ -600206,6 +600392,8 @@ Telegram response contract:
600206
600392
  chatParticipants = /* @__PURE__ */ new Map();
600207
600393
  /** Lightweight Zettelkasten-style memory cards by chat/guest session key */
600208
600394
  chatMemoryCards = /* @__PURE__ */ new Map();
600395
+ /** Generic chronological attention cadence shared by live surfaces. */
600396
+ stimulation = new StimulationController();
600209
600397
  /** Throttles noisy "skipped group chatter" waterfall logs */
600210
600398
  groupSkipLogAt = /* @__PURE__ */ new Map();
600211
600399
  /** Telegram interaction routing profile */
@@ -600495,6 +600683,9 @@ Telegram response contract:
600495
600683
  if (Array.isArray(parsed.memoryCards)) {
600496
600684
  this.chatMemoryCards.set(sessionKey, parsed.memoryCards.slice(0, TELEGRAM_MEMORY_CARD_LIMIT));
600497
600685
  }
600686
+ if (parsed.stimulation) {
600687
+ this.stimulation.setState(sessionKey, parsed.stimulation);
600688
+ }
600498
600689
  } catch {
600499
600690
  }
600500
600691
  }
@@ -600528,7 +600719,8 @@ Telegram response contract:
600528
600719
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
600529
600720
  history: this.chatHistory.get(sessionKey) ?? [],
600530
600721
  participants,
600531
- memoryCards: this.chatMemoryCards.get(sessionKey) ?? []
600722
+ memoryCards: this.chatMemoryCards.get(sessionKey) ?? [],
600723
+ stimulation: this.stimulation.getState(sessionKey)
600532
600724
  };
600533
600725
  writeFileSync57(this.telegramConversationPath(sessionKey), JSON.stringify(payload, null, 2) + "\n", "utf8");
600534
600726
  } catch {
@@ -600593,6 +600785,7 @@ Telegram response contract:
600593
600785
  chatTitle: msg.chatTitle
600594
600786
  };
600595
600787
  this.recordChatHistory(sessionKey, entry);
600788
+ this.stimulation.recordAgentOutput(sessionKey);
600596
600789
  this.updateTelegramMemoryCards(sessionKey, entry);
600597
600790
  try {
600598
600791
  updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
@@ -600952,21 +601145,53 @@ ${lines.join("\n")}`);
600952
601145
  if (msg.replyToUsername && msg.replyToUsername.trim().replace(/^@/, "").toLowerCase() === bot) return true;
600953
601146
  return false;
600954
601147
  }
601148
+ telegramMessageRepliesToBot(msg) {
601149
+ const bot = this.state.botUsername.trim().replace(/^@/, "").toLowerCase();
601150
+ return !!bot && !!msg.replyToUsername && msg.replyToUsername.trim().replace(/^@/, "").toLowerCase() === bot;
601151
+ }
601152
+ applyTelegramStimulationDecision(sessionKey, decision) {
601153
+ this.stimulation.applyAgentDecision(sessionKey, {
601154
+ shouldReply: decision.shouldReply,
601155
+ phase: decision.attentionState,
601156
+ attentionDelta: decision.attentionDelta,
601157
+ attentionScore: decision.attentionScore,
601158
+ nextAnalysisAfterMessages: decision.nextCheckAfterMessages,
601159
+ nextAnalysisAfterMs: decision.nextCheckAfterMs
601160
+ });
601161
+ }
600955
601162
  async inferTelegramInteractionDecision(msg, toolContext) {
600956
601163
  const config = this.agentConfig;
600957
601164
  const forcedRoute = this.interactionMode === "chat" || this.interactionMode === "action" ? this.interactionMode : null;
600958
601165
  const isGroup = msg.chatType !== "private";
600959
601166
  const addressesBot = this.telegramMessageAddressesBot(msg);
600960
- if (!config) {
601167
+ const sessionKey = this.sessionKeyForMessage(msg);
601168
+ const stimulationProbe = this.stimulation.observe({
601169
+ channelId: sessionKey,
601170
+ privateChannel: !isGroup,
601171
+ directSignal: addressesBot,
601172
+ replyToAgent: this.telegramMessageRepliesToBot(msg),
601173
+ activeAgent: this.subAgents.has(sessionKey)
601174
+ });
601175
+ if (isGroup && !stimulationProbe.shouldAnalyze) {
600961
601176
  return {
601177
+ route: forcedRoute ?? "chat",
601178
+ shouldReply: false,
601179
+ confidence: 0.35,
601180
+ reason: `stimulation cadence held analysis (${stimulationProbe.reason}); context retained`,
601181
+ source: "attention-gated"
601182
+ };
601183
+ }
601184
+ if (!config) {
601185
+ const fallback2 = {
600962
601186
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
600963
601187
  shouldReply: !isGroup || addressesBot,
600964
601188
  confidence: 0,
600965
601189
  reason: isGroup ? addressesBot ? "router inference unavailable; Telegram message directly addresses the bot" : "router inference unavailable; public group fails closed without keyword heuristics" : "router inference unavailable; private chat defaults to quick reply",
600966
601190
  source: "inference-unavailable"
600967
601191
  };
601192
+ this.applyTelegramStimulationDecision(sessionKey, fallback2);
601193
+ return fallback2;
600968
601194
  }
600969
- const sessionKey = this.sessionKeyForMessage(msg);
600970
601195
  const backend = new OllamaAgenticBackend(
600971
601196
  config.backendUrl,
600972
601197
  config.model,
@@ -600978,16 +601203,17 @@ ${lines.join("\n")}`);
600978
601203
  `You are the Telegram live routing and reply-discretion model.`,
600979
601204
  `Return JSON only, with no markdown and no explanation outside JSON.`,
600980
601205
  ``,
600981
- `Schema: {"route":"chat"|"action","should_reply":true|false,"confidence":0.0-1.0,"reason":"short reason"}`,
601206
+ `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}`,
600982
601207
  ``,
600983
601208
  `Route meanings:`,
600984
601209
  `- chat: a short conversational answer can be produced without tools.`,
600985
601210
  `- action: tools, workspace context, media processing, web lookup, delegation, or a multi-step agent loop may be needed.`,
600986
601211
  `Route discipline: greetings, acknowledgements, casual tone/style discussion, and simple conversational questions are chat. Use action only when the message asks you to inspect, create, change, send, remember, search, analyze media, or otherwise do tool-backed work.`,
600987
601212
  ``,
600988
- `Reply discretion: infer from the live thread, speaker relationships, direct mentions, replies, tone, and current message. Do not use static keyword rules.`,
601213
+ `Reply discretion: infer from the live thread, speaker relationships, direct platform signals, replies, tone, and current message. Do not use static keyword rules.`,
600989
601214
  `Private chats: should_reply is normally true.`,
600990
601215
  `Group/public chats: default should_reply to false unless the current message clearly addresses the bot, replies to the bot, continues an active bot-involved exchange, assigns the bot work, or asks for the bot's view. Ambient chatter, third-person discussion about the bot, commands meant for a human, or questions among other people are false. Do not set true just because the bot could help.`,
601216
+ `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.`,
600991
601217
  forcedLine,
600992
601218
  ``,
600993
601219
  `Tool context: ${toolContext}`,
@@ -600999,6 +601225,9 @@ ${lines.join("\n")}`);
600999
601225
  msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}` : "",
601000
601226
  (msg.mentionedUsernames ?? []).length > 0 ? `Current message mentions: ${(msg.mentionedUsernames ?? []).map((name10) => `@${name10}`).join(", ")}` : "",
601001
601227
  msg.media ? `Current message has media: ${summarizeTelegramMessageAttachments(msg)}` : "",
601228
+ ``,
601229
+ `Current stimulation state before semantic decision:
601230
+ ${stimulationProbe.context}`,
601002
601231
  ``,
601003
601232
  context2,
601004
601233
  ``,
@@ -601010,7 +601239,7 @@ ${msg.text}`
601010
601239
  messages: [
601011
601240
  {
601012
601241
  role: "system",
601013
- content: "You perform live Telegram route inference. Output strict JSON only."
601242
+ content: "You perform live Telegram route and stimulation inference. Output strict JSON only."
601014
601243
  },
601015
601244
  { role: "user", content: userPrompt }
601016
601245
  ],
@@ -601024,16 +601253,21 @@ ${msg.text}`
601024
601253
  const parsed = parseTelegramInteractionDecision(text, forcedRoute, {
601025
601254
  defaultShouldReply: !isGroup
601026
601255
  });
601027
- if (parsed) return parsed;
601256
+ if (parsed) {
601257
+ this.applyTelegramStimulationDecision(sessionKey, parsed);
601258
+ return parsed;
601259
+ }
601028
601260
  } catch {
601029
601261
  }
601030
- return {
601262
+ const fallback = {
601031
601263
  route: forcedRoute ?? (isGroup ? "action" : "chat"),
601032
601264
  shouldReply: !isGroup || addressesBot,
601033
601265
  confidence: 0,
601034
601266
  reason: isGroup ? addressesBot ? "router inference failed; Telegram message directly addresses the bot" : "router inference failed; public group fails closed without keyword heuristics" : "router inference failed; private chat defaults to quick reply",
601035
601267
  source: "inference-unavailable"
601036
601268
  };
601269
+ this.applyTelegramStimulationDecision(sessionKey, fallback);
601270
+ return fallback;
601037
601271
  }
601038
601272
  buildTelegramWorkspaceContext(modelTier, budget = 14e3) {
601039
601273
  if (!this.repoRoot) return "";
@@ -605361,7 +605595,7 @@ __export(voicechat_exports, {
605361
605595
  VoiceChatSession: () => VoiceChatSession
605362
605596
  });
605363
605597
  import { EventEmitter as EventEmitter11 } from "node:events";
605364
- function clamp016(x) {
605598
+ function clamp017(x) {
605365
605599
  return x < 0 ? 0 : x > 1 ? 1 : x;
605366
605600
  }
605367
605601
  function alnumRatio(s2) {
@@ -605400,9 +605634,9 @@ function computeSignalFromText(text, confidence) {
605400
605634
  else score = 0.15;
605401
605635
  score -= repeatingCharPenalty(t2) * 0.4;
605402
605636
  if (typeof confidence === "number" && !Number.isNaN(confidence)) {
605403
- score = 0.7 * score + 0.3 * clamp016(confidence);
605637
+ score = 0.7 * score + 0.3 * clamp017(confidence);
605404
605638
  }
605405
- return clamp016(score);
605639
+ return clamp017(score);
605406
605640
  }
605407
605641
  function truncateForLog(s2, n2) {
605408
605642
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "…";
@@ -605672,7 +605906,7 @@ Rules:
605672
605906
  }, MAX_SEGMENT_MS);
605673
605907
  }
605674
605908
  this.captureBuffer = text;
605675
- this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp016(snr) : computeSignalFromText(text, confidence);
605909
+ this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp017(snr) : computeSignalFromText(text, confidence);
605676
605910
  this.emit("snr", { score: this.lastSignalScore });
605677
605911
  this.onPartialTranscript(text);
605678
605912
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
@@ -612629,7 +612863,7 @@ const $config = writable(null); // {endpoint, model, ...}
612629
612863
  const $activeTab = writable('chat');
612630
612864
 
612631
612865
  function parseInitialGuiRoute() {
612632
- const path = (location.pathname || '/').replace(//+$/, '') || '/';
612866
+ const path = (location.pathname || '/').replace(/\\/+$/, '') || '/';
612633
612867
  const pathToTab = {
612634
612868
  '/chat': 'chat',
612635
612869
  '/agent': 'agent',
@@ -612644,7 +612878,7 @@ function parseInitialGuiRoute() {
612644
612878
  const tab = pathToTab[path] || null;
612645
612879
  let chatSession = null;
612646
612880
  if (tab === 'chat') {
612647
- const qs = location.search.replace(/^?/, '').trim();
612881
+ const qs = location.search.replace(/^\\?/, '').trim();
612648
612882
  if (qs) {
612649
612883
  try {
612650
612884
  const params = new URLSearchParams(location.search);
@@ -614786,7 +615020,7 @@ function routePathForTab(tab) {
614786
615020
  return map[tab] || '/chat';
614787
615021
  }
614788
615022
  function tabForRoutePath(pathname) {
614789
- const path = (pathname || '/').replace(//+$/, '') || '/';
615023
+ const path = (pathname || '/').replace(/\\/+$/, '') || '/';
614790
615024
  const map = {
614791
615025
  '/': 'chat',
614792
615026
  '/chat': 'chat',
@@ -614802,7 +615036,7 @@ function tabForRoutePath(pathname) {
614802
615036
  return map[path] || 'chat';
614803
615037
  }
614804
615038
  function chatSessionFromRouteSearch(search) {
614805
- const qs = String(search || '').replace(/^?/, '').trim();
615039
+ const qs = String(search || '').replace(/^\\?/, '').trim();
614806
615040
  if (!qs) return null;
614807
615041
  try {
614808
615042
  const params = new URLSearchParams(search);
@@ -614854,6 +615088,7 @@ function switchTab(tab, opts) {
614854
615088
  try { loadVoiceTab(); } catch {}
614855
615089
  }
614856
615090
  }
615091
+ window.switchTab = switchTab;
614857
615092
  window.addEventListener('popstate', () => {
614858
615093
  const tab = tabForRoutePath(location.pathname);
614859
615094
  if (tab === 'chat') {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.29",
9
+ "version": "1.0.30",
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.29",
3
+ "version": "1.0.30",
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",