omnius 1.0.60 → 1.0.61

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
@@ -12159,17 +12159,26 @@ process.on('unhandledRejection', (reason) => {
12159
12159
 
12160
12160
  dlog('COHERE response: ' + _cData.queryId + ' model=' + _cModel + ' tier=' + ['trivial','moderate','complex','expert'][_cTier] + ' ' + _cLatency + 'ms' + (_cReviewed ? ' (reviewed)' : ''));
12161
12161
  // IK-01: Update identity on query success
12162
+ // Store full query + response content; downstream renderers (narrative summary, prompts)
12163
+ // are responsible for compaction. Truncating at write time destroys identity history.
12164
+ // NOTE: '\\n' is double-escaped because this whole block runs inside the
12165
+ // outer DAEMON_SCRIPT template literal — see comment near line 440.
12162
12166
  _updateIdentity({ type: 'query_served', outcome: 1.0, details: {
12163
12167
  latencyMs: _cLatency, toolsUsed: _cToolsUsed, model: _cModel,
12164
- summary: (_cData.query || '').slice(0, 60)
12168
+ query: _cData.query || '',
12169
+ response: _cFinalContent || '',
12170
+ summary: (_cData.query || '') + (_cFinalContent ? '\\n→ ' + _cFinalContent : '')
12165
12171
  }});
12166
12172
  } catch (_cErr) {
12167
12173
  dlog('COHERE inference error: ' + (_cErr.message || _cErr));
12168
12174
  _cohereStats.queriesErrors++;
12169
12175
  _saveStats();
12170
12176
  // IK-01: Update identity on query failure
12177
+ // Preserve the full error so post-hoc diagnosis is possible.
12171
12178
  _updateIdentity({ type: 'query_failed', outcome: 0, details: {
12172
- error: (_cErr.message || String(_cErr)).slice(0, 100), summary: 'inference error'
12179
+ error: (_cErr && _cErr.message) || String(_cErr),
12180
+ query: _cData.query || '',
12181
+ summary: 'inference error: ' + ((_cErr && _cErr.message) || String(_cErr))
12173
12182
  }});
12174
12183
  }
12175
12184
  } catch (_cParseErr) {
@@ -593168,12 +593177,16 @@ sleep 1
593168
593177
  return "handled";
593169
593178
  }
593170
593179
  if (requested !== "auto" && requested !== "chat" && requested !== "action") {
593171
- renderWarning("Usage: /telegram mode auto|chat|action [--local]");
593180
+ renderWarning("Usage: /telegram mode auto|chat|action [--global]");
593172
593181
  return "handled";
593173
593182
  }
593174
- ctx3.saveTelegramSettings?.({ mode: requested, local: isLocal });
593183
+ const hasGlobalFlagMode = parts.includes("--global");
593184
+ const projectRootMode = ctx3.repoRoot || process.cwd();
593185
+ const projectHasOmniusMode = existsSync100(join114(projectRootMode, ".omnius"));
593186
+ const wantsLocalMode = hasGlobalFlagMode ? false : isLocal || projectHasOmniusMode;
593187
+ ctx3.saveTelegramSettings?.({ mode: requested, local: wantsLocalMode });
593175
593188
  ctx3.telegramSetInteractionMode?.(requested);
593176
- renderInfo(`Telegram interaction mode set to ${c3.bold(requested)}${isLocal ? " (project)" : " (global)"}.`);
593189
+ renderInfo(`Telegram interaction mode set to ${c3.bold(requested)}${wantsLocalMode ? " (project)" : " (global)"}.`);
593177
593190
  return "handled";
593178
593191
  }
593179
593192
  if (parts[0] === "auth" || parts[0] === "authenticate") {
@@ -593420,18 +593433,23 @@ sleep 1
593420
593433
  }
593421
593434
  const keyIdx = parts.indexOf("--key");
593422
593435
  const adminIdx = parts.indexOf("--admin");
593436
+ const hasGlobalFlag = parts.includes("--global");
593437
+ const projectRoot = ctx3.repoRoot || process.cwd();
593438
+ const projectHasOmnius = existsSync100(join114(projectRoot, ".omnius"));
593439
+ const wantsLocal = hasGlobalFlag ? false : isLocal || projectHasOmnius;
593423
593440
  if (keyIdx !== -1 || adminIdx !== -1) {
593424
593441
  const settings = {
593425
- local: isLocal
593442
+ local: wantsLocal
593426
593443
  };
593427
- const scope = isLocal ? "project" : "global";
593444
+ const scope = wantsLocal ? "project" : "global";
593428
593445
  if (keyIdx !== -1) {
593429
593446
  const token = parts[keyIdx + 1];
593430
593447
  if (!token || token.startsWith("--")) {
593431
593448
  renderWarning(
593432
- "Usage: /telegram --key <bot-token> [--admin <id>] [--local]"
593449
+ "Usage: /telegram --key <bot-token> [--admin <id>] [--global]"
593433
593450
  );
593434
593451
  renderInfo("Get a bot token from @BotFather on Telegram.");
593452
+ renderInfo("Default scope: project-local when .omnius/ exists; use --global to override.");
593435
593453
  return "handled";
593436
593454
  }
593437
593455
  settings.key = token;
@@ -593440,7 +593458,7 @@ sleep 1
593440
593458
  const userId = parts[adminIdx + 1];
593441
593459
  if (!userId || userId.startsWith("--")) {
593442
593460
  renderWarning(
593443
- "Usage: /telegram --admin <user-id-or-username> [--key <token>] [--local]"
593461
+ "Usage: /telegram --admin <user-id-or-username> [--key <token>] [--global]"
593444
593462
  );
593445
593463
  return "handled";
593446
593464
  }
@@ -593455,6 +593473,11 @@ sleep 1
593455
593473
  renderInfo(
593456
593474
  `Telegram admin set to ${c3.bold(settings.admin)} (${scope}).`
593457
593475
  );
593476
+ if (wantsLocal && !projectHasOmnius) {
593477
+ renderWarning(
593478
+ "No .omnius/ in the current directory; this 'local' setting was written to ./.omnius/settings.json and only applies when omnius is invoked from this folder."
593479
+ );
593480
+ }
593458
593481
  if (!ctx3.isTelegramActive?.() && settings.key)
593459
593482
  renderInfo("Use /telegram to start.");
593460
593483
  return "handled";
@@ -593468,9 +593491,30 @@ sleep 1
593468
593491
  if (!settings.key) {
593469
593492
  renderWarning("No Telegram bot token configured.");
593470
593493
  renderInfo("Set one first: /telegram --key <bot-token>");
593471
- renderInfo("Get a token from @BotFather on Telegram.");
593494
+ renderInfo("Get a token from @BotFather on Telegram. The token saves project-locally by default when .omnius/ exists.");
593472
593495
  return "handled";
593473
593496
  }
593497
+ try {
593498
+ const startProjectRoot = ctx3.repoRoot || process.cwd();
593499
+ if (existsSync100(join114(startProjectRoot, ".omnius"))) {
593500
+ const projectSettingsPath = join114(startProjectRoot, ".omnius", "settings.json");
593501
+ let projectHasKey = false;
593502
+ if (existsSync100(projectSettingsPath)) {
593503
+ try {
593504
+ const projectJson = JSON.parse(readFileSync81(projectSettingsPath, "utf8"));
593505
+ projectHasKey = typeof projectJson?.telegramKey === "string" && projectJson.telegramKey.length > 0;
593506
+ } catch {
593507
+ }
593508
+ }
593509
+ if (!projectHasKey) {
593510
+ renderWarning(
593511
+ "Using GLOBAL Telegram token in a project that has no local key. Another omnius instance running with the same token will steal getUpdates and you'll see 409 conflicts."
593512
+ );
593513
+ renderInfo("Scope it locally: /telegram --key <token> (writes to ./.omnius/settings.json)");
593514
+ }
593515
+ }
593516
+ } catch {
593517
+ }
593474
593518
  try {
593475
593519
  await ctx3.telegramStart?.(settings.key, settings.admin);
593476
593520
  } catch (err) {
@@ -593482,8 +593526,8 @@ sleep 1
593482
593526
  }
593483
593527
  renderWarning(`Unknown argument: "${arg}"`);
593484
593528
  renderInfo("Usage:");
593485
- renderInfo(" /telegram --key <token> Save bot token (global)");
593486
- renderInfo(" /telegram --admin <id> Set admin filter (global)");
593529
+ renderInfo(" /telegram --key <token> Save bot token (project-local when .omnius/ exists)");
593530
+ renderInfo(" /telegram --admin <id> Set admin filter (project-local by default)");
593487
593531
  renderInfo(" /telegram Toggle on/off");
593488
593532
  renderInfo(" /telegram stop Stop bridge");
593489
593533
  renderInfo(" /telegram status Show status");
@@ -593502,7 +593546,7 @@ sleep 1
593502
593546
  renderInfo(" /telegram delete-messages <chat> <msg,msg> [reason] Delete multiple messages");
593503
593547
  renderInfo(" /telegram delete-reaction <chat> <msg> --user <id> Delete a reaction");
593504
593548
  renderInfo(" /telegram delete-reactions <chat> --user <id> Delete recent reactions");
593505
- renderInfo(" Add --local to scope settings to this project only");
593549
+ renderInfo(" Add --global to write to ~/.omnius/ (shared across all omnius instances)");
593506
593550
  return "handled";
593507
593551
  }
593508
593552
  case "platforms":
@@ -610944,6 +610988,12 @@ External acquisition contract:
610944
610988
  /** Durable SQLite mirror for raw Telegram messages and metadata. */
610945
610989
  telegramSqlitePath;
610946
610990
  telegramSqliteDb = null;
610991
+ /**
610992
+ * Per-bot ownership lockfile under .omnius/telegram-runner-state/.
610993
+ * Prevents two omnius instances from polling the same bot token concurrently
610994
+ * (which would cause silent 409 conflicts on getUpdates). Released in stop().
610995
+ */
610996
+ telegramOwnerLockFile = null;
610947
610997
  /** Session keys loaded from persistent conversation memory */
610948
610998
  loadedConversationState = /* @__PURE__ */ new Set();
610949
610999
  /** True once persisted Telegram conversation scopes have been bulk-loaded. */
@@ -611775,10 +611825,19 @@ ${mediaContext}` : ""
611775
611825
  }
611776
611826
  this.chatParticipants.set(sessionKey, participants);
611777
611827
  }
611778
- if (Array.isArray(parsed.memoryCards)) {
611828
+ const cardsLookTruncated = Array.isArray(parsed.memoryCards) && parsed.memoryCards.some((card) => Array.isArray(card.notes) && card.notes.some((note) => typeof note === "string" && note.endsWith("...")));
611829
+ if (Array.isArray(parsed.memoryCards) && !cardsLookTruncated) {
611779
611830
  this.chatMemoryCards.set(sessionKey, parsed.memoryCards.slice(0, TELEGRAM_MEMORY_CARD_LIMIT));
611831
+ } else if (loadedHistory.length > 0) {
611832
+ this.chatMemoryCards.set(sessionKey, []);
611833
+ for (const entry of loadedHistory) {
611834
+ this.updateTelegramMemoryCards(sessionKey, entry);
611835
+ }
611780
611836
  }
611781
- if (parsed.associativeMemory) {
611837
+ const associativeLooksTruncated = parsed.associativeMemory && Array.isArray(parsed.associativeMemory.facts) && parsed.associativeMemory.facts.some(
611838
+ (fact) => typeof fact?.text === "string" && fact.text.endsWith("...")
611839
+ );
611840
+ if (parsed.associativeMemory && !associativeLooksTruncated) {
611782
611841
  this.chatAssociativeMemory.set(
611783
611842
  sessionKey,
611784
611843
  this.normalizeTelegramAssociativeMemory(parsed.associativeMemory)
@@ -612488,14 +612547,14 @@ ${mediaContext}` : ""
612488
612547
  profile.fromUserId = msg.fromUserId || profile.fromUserId;
612489
612548
  profile.messageCount += 1;
612490
612549
  profile.lastSeenTs = Date.now();
612491
- profile.lastMessage = truncateTelegramContextLine(text, 220);
612550
+ profile.lastMessage = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
612492
612551
  if (msg.replyToMessageId) profile.replyCount += 1;
612493
612552
  if (this.state.botUsername && text.toLowerCase().includes(`@${this.state.botUsername.toLowerCase()}`)) {
612494
612553
  profile.directAddressCount += 1;
612495
612554
  }
612496
612555
  for (const tag of inferTelegramToneTags(text)) profile.toneTags.add(tag);
612497
612556
  if (text.trim()) {
612498
- profile.samples.push(truncateTelegramContextLine(text, 160));
612557
+ profile.samples.push(stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim());
612499
612558
  if (profile.samples.length > TELEGRAM_CONTEXT_SAMPLE_LIMIT) {
612500
612559
  profile.samples.splice(0, profile.samples.length - TELEGRAM_CONTEXT_SAMPLE_LIMIT);
612501
612560
  }
@@ -612516,7 +612575,7 @@ ${mediaContext}` : ""
612516
612575
  role: entry.role,
612517
612576
  speaker,
612518
612577
  mode: entry.mode,
612519
- text: truncateTelegramContextLine(entry.text, 900),
612578
+ text: stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim(),
612520
612579
  messageId: entry.messageId,
612521
612580
  replyToMessageId: entry.replyToMessageId,
612522
612581
  userId: entry.fromUserId,
@@ -612566,7 +612625,7 @@ ${mediaContext}` : ""
612566
612625
  if (!userMemory.toneTags.includes(tag)) userMemory.toneTags.push(tag);
612567
612626
  }
612568
612627
  userMemory.toneTags = userMemory.toneTags.slice(0, 20);
612569
- const compact2 = truncateTelegramContextLine(entry.text, 240);
612628
+ const compact2 = stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim();
612570
612629
  if (compact2) {
612571
612630
  userMemory.lastMessages.push(compact2);
612572
612631
  userMemory.lastMessages = userMemory.lastMessages.slice(-40);
@@ -612582,7 +612641,7 @@ ${mediaContext}` : ""
612582
612641
  }
612583
612642
  if (entry.replyContext?.sender || entry.replyToMessageId) {
612584
612643
  const target = entry.replyContext?.sender ? telegramReplySenderLabel(entry.replyContext.sender) : `message ${entry.replyToMessageId}`;
612585
- const hint = `${speaker} replied to ${target}: ${truncateTelegramContextLine(entry.text, 180)}`;
612644
+ const hint = `${speaker} replied to ${target}: ${stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim()}`;
612586
612645
  if (!userMemory.relationshipHints.includes(hint)) {
612587
612646
  userMemory.relationshipHints.push(hint);
612588
612647
  userMemory.relationshipHints = userMemory.relationshipHints.slice(-80);
@@ -612600,7 +612659,7 @@ ${mediaContext}` : ""
612600
612659
  }
612601
612660
  extractTelegramAssociativeFacts(entry, speaker) {
612602
612661
  if (entry.role !== "user") return [];
612603
- const text = truncateTelegramContextLine(entry.text, 500);
612662
+ const text = stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim();
612604
612663
  const facts = /* @__PURE__ */ new Set();
612605
612664
  const patterns = [
612606
612665
  /\b(?:remember|note|keep in mind|for future reference)\s+(?:that\s+)?(.{4,260})/i,
@@ -612621,7 +612680,7 @@ ${mediaContext}` : ""
612621
612680
  return [...facts].slice(0, 8);
612622
612681
  }
612623
612682
  upsertTelegramAssociativeFact(facts, text, entry, speaker, weight = 1) {
612624
- const clean5 = truncateTelegramContextLine(text, 500);
612683
+ const clean5 = stripTelegramHiddenThinking(text || "").replace(/\s+/g, " ").trim();
612625
612684
  const key = clean5.toLowerCase();
612626
612685
  const now = entry.ts ?? Date.now();
612627
612686
  let fact = facts.find((item) => item.text.toLowerCase() === key);
@@ -612658,7 +612717,7 @@ ${mediaContext}` : ""
612658
612717
  return fact;
612659
612718
  }
612660
612719
  updateTelegramMemoryCards(sessionKey, entry) {
612661
- const text = truncateTelegramContextLine(entry.text, 500);
612720
+ const text = stripTelegramHiddenThinking(entry.text || "").replace(/\s+/g, " ").trim();
612662
612721
  if (!text || text.length < 3) return;
612663
612722
  const speaker = telegramHistorySpeaker(entry);
612664
612723
  const tags = telegramMemoryTags(text, entry.mediaSummary);
@@ -612867,6 +612926,61 @@ ${lines.join("\n")}`);
612867
612926
  lines.join("\n")
612868
612927
  ].join("\n");
612869
612928
  }
612929
+ /**
612930
+ * Always-on episodic memory recall for the active Telegram session.
612931
+ * Pulls scored episodes from .omnius/episodes.db where session_id = sessionKey,
612932
+ * including social / tool_result / reflection modalities. This surfaces day-old
612933
+ * context the rolling 36-turn window has already shed.
612934
+ */
612935
+ relevantTelegramEpisodicMemoryContext(sessionKey, msg, limit = 8) {
612936
+ if (!this.repoRoot) return "";
612937
+ const paths = omniusMemoryDbPaths(this.repoRoot);
612938
+ if (!existsSync112(paths.episodes)) return "";
612939
+ let episodes = [];
612940
+ const graph = new TemporalGraph(paths.knowledge);
612941
+ const store2 = new EpisodeStore(paths.episodes, graph);
612942
+ try {
612943
+ const recent = store2.search({ sessionId: sessionKey, limit: Math.max(limit * 3, 24) }) ?? [];
612944
+ const queryText = (msg.text || "").toLowerCase().trim();
612945
+ if (queryText.length > 3) {
612946
+ const qTokens = queryText.split(/\s+/).filter((t2) => t2.length >= 4);
612947
+ const scored = recent.map((ep) => {
612948
+ const content = String(ep.content || "").toLowerCase();
612949
+ const hits = qTokens.filter((t2) => content.includes(t2)).length;
612950
+ return { ep, score: hits };
612951
+ }).sort((a2, b) => b.score - a2.score || b.ep.timestamp - a2.ep.timestamp);
612952
+ episodes = scored.slice(0, limit).map((s2) => s2.ep);
612953
+ } else {
612954
+ episodes = recent.slice(0, limit);
612955
+ }
612956
+ } catch {
612957
+ return "";
612958
+ } finally {
612959
+ try {
612960
+ store2.close();
612961
+ } catch {
612962
+ }
612963
+ try {
612964
+ graph.close();
612965
+ } catch {
612966
+ }
612967
+ }
612968
+ if (episodes.length === 0) return "";
612969
+ const lines = episodes.map((ep) => {
612970
+ const when = ep.timestamp ? new Date(ep.timestamp).toISOString() : "";
612971
+ const meta = ep.metadata || {};
612972
+ const tg = meta.telegram && typeof meta.telegram === "object" ? meta.telegram : {};
612973
+ const speaker = tg.speaker || tg.username || (ep.modality === "tool_result" ? `tool:${ep.toolName || "?"}` : "agent");
612974
+ const mode = tg.mode ? `/${tg.mode}` : "";
612975
+ const text = ep.gist || ep.content;
612976
+ return `- ${when} ${speaker}${mode} [${ep.modality}]: ${telegramContextJsonString(String(text || ""), 260)}`;
612977
+ });
612978
+ return [
612979
+ "### Episodic Memory Recall (durable, day+ scope)",
612980
+ "Scored episodes for this Telegram session from episodes.db. These are PERSISTENT across restarts and survive the rolling-context window. Treat as canonical for older facts.",
612981
+ lines.join("\n")
612982
+ ].join("\n");
612983
+ }
612870
612984
  telegramSqliteHistoryForSession(sessionKey, limit = 1e3) {
612871
612985
  const db = this.telegramDb();
612872
612986
  if (!db) return [];
@@ -613047,6 +613161,17 @@ ${participantLines.join("\n")}`);
613047
613161
  if (sqliteMirrorContext) {
613048
613162
  sections.push(sqliteMirrorContext);
613049
613163
  }
613164
+ try {
613165
+ const episodicContext = this.relevantTelegramEpisodicMemoryContext(
613166
+ sessionKey,
613167
+ msg,
613168
+ isGroup ? 10 : 6
613169
+ );
613170
+ if (episodicContext) {
613171
+ sections.push(episodicContext);
613172
+ }
613173
+ } catch {
613174
+ }
613050
613175
  const memoryCards = this.relevantTelegramMemoryCards(sessionKey, msg, isGroup ? 10 : 6);
613051
613176
  if (memoryCards.length > 0) {
613052
613177
  const cardLines = memoryCards.map(({ card, score }) => {
@@ -613613,6 +613738,40 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
613613
613738
  if (!me?.ok) {
613614
613739
  throw new Error(`Invalid Telegram bot token: ${me?.description || "unknown error"}`);
613615
613740
  }
613741
+ if (this.repoRoot && me.result?.id) {
613742
+ try {
613743
+ const lockDir = resolve43(this.repoRoot, ".omnius", "telegram-runner-state");
613744
+ mkdirSync65(lockDir, { recursive: true });
613745
+ const lockFile = join127(lockDir, `bot-${me.result.id}.owner.lock`);
613746
+ if (existsSync112(lockFile)) {
613747
+ try {
613748
+ const prior = JSON.parse(readFileSync92(lockFile, "utf8"));
613749
+ const priorAlive = typeof prior.pid === "number" && prior.pid !== process.pid ? this.processIsAlive(prior.pid) : false;
613750
+ if (priorAlive) {
613751
+ throw new Error(
613752
+ `Telegram bot @${prior.botUsername || me.result.username || "unknown"} is already being polled by pid ${prior.pid} (cwd ${prior.cwd || "?"}). Stop that instance or use a different bot token in this project.`
613753
+ );
613754
+ }
613755
+ } catch (e2) {
613756
+ if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
613757
+ }
613758
+ }
613759
+ writeFileSync59(
613760
+ lockFile,
613761
+ JSON.stringify({
613762
+ pid: process.pid,
613763
+ cwd: this.repoRoot,
613764
+ botUsername: me.result.username,
613765
+ botUserId: me.result.id,
613766
+ ts: Date.now()
613767
+ }, null, 2),
613768
+ { encoding: "utf-8", mode: 384 }
613769
+ );
613770
+ this.telegramOwnerLockFile = lockFile;
613771
+ } catch (e2) {
613772
+ if (e2 instanceof Error && e2.message.startsWith("Telegram bot @")) throw e2;
613773
+ }
613774
+ }
613616
613775
  this.state = {
613617
613776
  active: true,
613618
613777
  botUsername: me.result?.username ?? "unknown",
@@ -613663,10 +613822,33 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
613663
613822
  }
613664
613823
  this.telegramSqliteDb = null;
613665
613824
  }
613825
+ if (this.telegramOwnerLockFile) {
613826
+ try {
613827
+ if (existsSync112(this.telegramOwnerLockFile)) {
613828
+ unlinkSync22(this.telegramOwnerLockFile);
613829
+ }
613830
+ } catch {
613831
+ }
613832
+ this.telegramOwnerLockFile = null;
613833
+ }
613666
613834
  this.subAgents.clear();
613667
613835
  this.activeChatViews.clear();
613668
613836
  this.refreshActiveTelegramInteractionCount();
613669
613837
  }
613838
+ /**
613839
+ * Cheap liveness probe: kill(pid, 0) throws ESRCH when the process is dead
613840
+ * and EPERM when it exists but we can't signal it (still alive from our POV).
613841
+ */
613842
+ processIsAlive(pid) {
613843
+ if (!pid || !Number.isFinite(pid)) return false;
613844
+ try {
613845
+ process.kill(pid, 0);
613846
+ return true;
613847
+ } catch (e2) {
613848
+ if (e2 && e2.code === "EPERM") return true;
613849
+ return false;
613850
+ }
613851
+ }
613670
613852
  // ── Typing indicator ──────────────────────────────────────────────────
613671
613853
  /** Start sending "typing" indicator every 4 seconds */
613672
613854
  startTypingIndicator(chatId) {
@@ -648230,7 +648412,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
648230
648412
  ...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {},
648231
648413
  ...settings.mode !== void 0 ? { telegramMode: settings.mode } : {}
648232
648414
  };
648233
- if (settings.local) {
648415
+ const projectHasOmnius = existsSync127(join143(repoRoot, ".omnius"));
648416
+ const useProject = settings.local === true || settings.local === void 0 && projectHasOmnius;
648417
+ if (useProject) {
648234
648418
  saveProjectSettings(repoRoot, payload);
648235
648419
  } else {
648236
648420
  saveGlobalSettings(payload);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.60",
3
+ "version": "1.0.61",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.60",
9
+ "version": "1.0.61",
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.60",
3
+ "version": "1.0.61",
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",