omnius 1.0.68 → 1.0.69

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
@@ -609957,11 +609957,14 @@ function redactTelegramLocalPaths(text) {
609957
609957
  function telegramContextJsonString(text, maxLength = TELEGRAM_CONTEXT_LINE_LIMIT) {
609958
609958
  return JSON.stringify(truncateTelegramContextLine(redactTelegramLocalPaths(text), maxLength));
609959
609959
  }
609960
+ function telegramActorKindLabel(actor) {
609961
+ return actor?.isBot ? "bot" : "human";
609962
+ }
609960
609963
  function telegramSpeakerLabel(msg) {
609961
609964
  if (msg.username && msg.username !== "unknown") return `@${msg.username}`;
609962
609965
  if (msg.firstName) return msg.firstName;
609963
- if (msg.fromUserId) return `user:${msg.fromUserId}`;
609964
- return "Telegram user";
609966
+ if (msg.fromUserId) return msg.isBot ? `bot:${msg.fromUserId}` : `user:${msg.fromUserId}`;
609967
+ return msg.isBot ? "Telegram bot" : "Telegram user";
609965
609968
  }
609966
609969
  function telegramReplySenderLabel(sender) {
609967
609970
  if (!sender) return "unknown sender";
@@ -609983,8 +609986,8 @@ function telegramHistorySpeaker(entry) {
609983
609986
  if (entry.speaker) return entry.speaker;
609984
609987
  if (entry.username) return `@${entry.username}`;
609985
609988
  if (entry.firstName) return entry.firstName;
609986
- if (entry.fromUserId) return `user:${entry.fromUserId}`;
609987
- return "Telegram user";
609989
+ if (entry.fromUserId) return entry.isBot ? `bot:${entry.fromUserId}` : `user:${entry.fromUserId}`;
609990
+ return entry.isBot ? "Telegram bot" : "Telegram user";
609988
609991
  }
609989
609992
  function telegramHistoryTime(entry) {
609990
609993
  if (!entry.ts) return "";
@@ -610738,6 +610741,7 @@ function normalizeTelegramUpdate(update2) {
610738
610741
  if (!message2 || typeof message2 !== "object") return null;
610739
610742
  const fromId = message2.from?.id ?? message2.sender_chat?.id ?? 0;
610740
610743
  const username = message2.from?.username ?? message2.sender_chat?.username ?? "";
610744
+ const isBot = message2.from?.is_bot === true;
610741
610745
  const chatType = message2.chat?.type ?? "private";
610742
610746
  const media = normalizeTelegramMedia(message2);
610743
610747
  const replyContext = normalizeTelegramReplyContext(message2);
@@ -610752,6 +610756,7 @@ function normalizeTelegramUpdate(update2) {
610752
610756
  firstName: message2.from?.first_name,
610753
610757
  messageId: message2.message_id ?? 0,
610754
610758
  fromUserId: fromId,
610759
+ isBot,
610755
610760
  chatType,
610756
610761
  chatTitle: message2.chat?.title,
610757
610762
  media,
@@ -610843,7 +610848,7 @@ function normalizeTelegramSendKind(rawKind, path12) {
610843
610848
  }
610844
610849
  return classifyMedia(path12) ?? "document";
610845
610850
  }
610846
- function renderTelegramStart(botUsername, adminId, mode = "auto") {
610851
+ function renderTelegramStart(botUsername, adminId, mode = "auto", canReadAllGroupMessages) {
610847
610852
  process.stdout.write(`
610848
610853
  ${c3.cyan("✈")} ${c3.bold("Telegram Bridge")} connected as @${botUsername}
610849
610854
  `);
@@ -610851,6 +610856,15 @@ function renderTelegramStart(botUsername, adminId, mode = "auto") {
610851
610856
  `);
610852
610857
  process.stdout.write(` ${c3.dim("Auto mode uses quick chat for conversational turns and sub-agents for action requests")}
610853
610858
  `);
610859
+ if (canReadAllGroupMessages) {
610860
+ process.stdout.write(` ${c3.dim("Group ingest: all delivered group messages are visible to the bridge")}
610861
+ `);
610862
+ } else {
610863
+ process.stdout.write(` ${c3.yellow("Group ingest: Telegram may only deliver commands/mentions/replies while BotFather privacy mode is enabled")}
610864
+ `);
610865
+ process.stdout.write(` ${c3.dim("Disable privacy with @BotFather /setprivacy for full ambient group memory.")}
610866
+ `);
610867
+ }
610854
610868
  if (adminId) {
610855
610869
  process.stdout.write(` ${c3.dim(`Admin: ${adminId} (full memory + tools)`)}
610856
610870
  `);
@@ -610863,12 +610877,14 @@ function renderTelegramStart(botUsername, adminId, mode = "auto") {
610863
610877
 
610864
610878
  `);
610865
610879
  }
610866
- function renderTelegramStatus(active, botUsername, adminId, activeSubAgents, mode = "auto") {
610880
+ function renderTelegramStatus(active, botUsername, adminId, activeSubAgents, mode = "auto", canReadAllGroupMessages) {
610867
610881
  if (active) {
610868
610882
  process.stdout.write(`
610869
610883
  ${c3.green("●")} Telegram bridge: ${c3.bold("ACTIVE")} (@${botUsername ?? "?"})
610870
610884
  `);
610871
610885
  process.stdout.write(` Mode: ${mode}
610886
+ `);
610887
+ process.stdout.write(` Group ingest: ${canReadAllGroupMessages ? "all delivered group messages" : "privacy-limited by Telegram/BotFather"}
610872
610888
  `);
610873
610889
  if (adminId) {
610874
610890
  process.stdout.write(` Admin: ${adminId}
@@ -611139,6 +611155,9 @@ most ambient chatter. Continue to be selective:
611139
611155
  2. Stay silent for conversation between other people, third-person commentary
611140
611156
  about the bot, status chatter, or questions clearly meant for someone else.
611141
611157
  3. Do not reply just because you know an answer or could add color.
611158
+ 4. Peer bots are labeled as bots in context. Do not reflexively answer bot
611159
+ chatter; only continue bot-to-bot exchanges when the message is clearly
611160
+ addressed to this bot or advances an active bot-mediated task.
611142
611161
 
611143
611162
  If you determine no visible reply should be sent, call task_complete with summary
611144
611163
  "no_reply". Never write "no_reply", "skipping", "no action needed", or a status
@@ -611349,6 +611368,7 @@ External acquisition contract:
611349
611368
  active: false,
611350
611369
  botUsername: "",
611351
611370
  supportsGuestQueries: false,
611371
+ canReadAllGroupMessages: false,
611352
611372
  interactionMode: "auto",
611353
611373
  startedAt: "",
611354
611374
  messagesReceived: 0,
@@ -611756,7 +611776,7 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
611756
611776
  id: entry.fromUserId,
611757
611777
  username: entry.username,
611758
611778
  firstName: entry.firstName,
611759
- isBot: false
611779
+ isBot: Boolean(entry.isBot)
611760
611780
  };
611761
611781
  return {
611762
611782
  kind: "message",
@@ -611875,7 +611895,7 @@ ${mediaContext}` : ""
611875
611895
  id: String(msg.fromUserId),
611876
611896
  username: msg.username,
611877
611897
  displayName: msg.firstName || msg.username,
611878
- isBot: false
611898
+ isBot: Boolean(msg.isBot)
611879
611899
  };
611880
611900
  }
611881
611901
  telegramMemorySenderFromReply(msg) {
@@ -612108,6 +612128,7 @@ ${mediaContext}` : ""
612108
612128
  messageThreadId: msg.messageThreadId,
612109
612129
  replyToMessageId,
612110
612130
  username: this.state.botUsername || "omnius",
612131
+ isBot: true,
612111
612132
  text
612112
612133
  };
612113
612134
  try {
@@ -612248,7 +612269,13 @@ ${mediaContext}` : ""
612248
612269
  const parsed = JSON.parse(readFileSync92(path12, "utf8"));
612249
612270
  const loadedHistory = Array.isArray(parsed.history) ? parsed.history : [];
612250
612271
  if (Array.isArray(parsed.history)) {
612251
- this.chatHistory.set(sessionKey, loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT));
612272
+ this.chatHistory.set(
612273
+ sessionKey,
612274
+ loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT).map((entry) => ({
612275
+ ...entry,
612276
+ isBot: entry.role === "assistant" ? true : Boolean(entry.isBot)
612277
+ }))
612278
+ );
612252
612279
  }
612253
612280
  if (Array.isArray(parsed.participants)) {
612254
612281
  const participants = /* @__PURE__ */ new Map();
@@ -612256,6 +612283,7 @@ ${mediaContext}` : ""
612256
612283
  const key = String(profile.fromUserId || profile.username || profile.firstName || participants.size);
612257
612284
  participants.set(key, {
612258
612285
  ...profile,
612286
+ isBot: Boolean(profile.isBot),
612259
612287
  toneTags: new Set(Array.isArray(profile.toneTags) ? profile.toneTags : []),
612260
612288
  samples: Array.isArray(profile.samples) ? profile.samples : []
612261
612289
  });
@@ -612766,6 +612794,31 @@ ${mediaContext}` : ""
612766
612794
  const sessionKey = this.sessionKeyForMessage(msg);
612767
612795
  const mediaSummary = summarizeTelegramMessageAttachments(msg);
612768
612796
  const text = (textOverride ?? msg.text ?? "").trim() || mediaSummary || "[non-text Telegram message]";
612797
+ this.ensureTelegramConversationLoaded(sessionKey);
612798
+ const history = this.chatHistory.get(sessionKey) ?? [];
612799
+ const existing = Number.isFinite(msg.messageId) ? history.find(
612800
+ (entry2) => entry2.role === "user" && entry2.messageId === msg.messageId && String(entry2.chatId) === String(msg.chatId)
612801
+ ) : void 0;
612802
+ if (existing) {
612803
+ if (existing.mode === "ambient" || mode !== "ambient") existing.mode = mode;
612804
+ if (textOverride !== void 0 || !existing.text || existing.text === "[non-text Telegram message]") {
612805
+ existing.text = text;
612806
+ }
612807
+ existing.speaker = existing.speaker || telegramSpeakerLabel(msg);
612808
+ existing.username = msg.username || existing.username;
612809
+ existing.firstName = msg.firstName ?? existing.firstName;
612810
+ existing.fromUserId = msg.fromUserId || existing.fromUserId;
612811
+ existing.isBot = msg.isBot ?? existing.isBot;
612812
+ existing.messageThreadId = msg.messageThreadId ?? existing.messageThreadId;
612813
+ existing.replyToMessageId = msg.replyToMessageId ?? existing.replyToMessageId;
612814
+ existing.replyContext = msg.replyContext ?? existing.replyContext;
612815
+ existing.chatType = msg.chatType || existing.chatType;
612816
+ existing.chatTitle = msg.chatTitle ?? existing.chatTitle;
612817
+ existing.mediaSummary = mediaSummary || existing.mediaSummary;
612818
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, existing);
612819
+ this.saveTelegramConversationState(sessionKey);
612820
+ return;
612821
+ }
612769
612822
  const entry = {
612770
612823
  role: "user",
612771
612824
  text,
@@ -612775,6 +612828,7 @@ ${mediaContext}` : ""
612775
612828
  username: msg.username,
612776
612829
  firstName: msg.firstName,
612777
612830
  fromUserId: msg.fromUserId,
612831
+ isBot: Boolean(msg.isBot),
612778
612832
  messageId: msg.messageId,
612779
612833
  messageThreadId: msg.messageThreadId,
612780
612834
  replyToMessageId: msg.replyToMessageId,
@@ -612814,6 +612868,7 @@ ${mediaContext}` : ""
612814
612868
  mode,
612815
612869
  chatId: msg.chatId,
612816
612870
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
612871
+ isBot: true,
612817
612872
  messageId: options2.messageId ?? void 0,
612818
612873
  messageThreadId: msg.messageThreadId,
612819
612874
  replyToMessageId: options2.replyToMessageId,
@@ -612993,6 +613048,7 @@ ${mediaContext}` : ""
612993
613048
  username: msg.username || "unknown",
612994
613049
  firstName: msg.firstName,
612995
613050
  fromUserId: msg.fromUserId,
613051
+ isBot: Boolean(msg.isBot),
612996
613052
  messageCount: 0,
612997
613053
  directAddressCount: 0,
612998
613054
  replyCount: 0,
@@ -613004,6 +613060,7 @@ ${mediaContext}` : ""
613004
613060
  profile.username = msg.username || profile.username;
613005
613061
  profile.firstName = msg.firstName ?? profile.firstName;
613006
613062
  profile.fromUserId = msg.fromUserId || profile.fromUserId;
613063
+ profile.isBot = Boolean(profile.isBot || msg.isBot);
613007
613064
  profile.messageCount += 1;
613008
613065
  profile.lastSeenTs = Date.now();
613009
613066
  profile.lastMessage = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
@@ -613038,7 +613095,8 @@ ${mediaContext}` : ""
613038
613095
  messageId: entry.messageId,
613039
613096
  replyToMessageId: entry.replyToMessageId,
613040
613097
  userId: entry.fromUserId,
613041
- username: entry.username
613098
+ username: entry.username,
613099
+ isBot: entry.role === "assistant" || Boolean(entry.isBot)
613042
613100
  });
613043
613101
  if (memory.actions.length > TELEGRAM_ASSOCIATIVE_ACTION_LIMIT) {
613044
613102
  memory.actions.splice(
@@ -613052,6 +613110,7 @@ ${mediaContext}` : ""
613052
613110
  const existing = memory.users[userKey];
613053
613111
  const userMemory = existing ?? {
613054
613112
  userId: entry.fromUserId,
613113
+ isBot: Boolean(entry.isBot),
613055
613114
  username: entry.username || "unknown",
613056
613115
  displayName: entry.firstName || entry.username || speaker,
613057
613116
  aliases: [],
@@ -613067,6 +613126,7 @@ ${mediaContext}` : ""
613067
613126
  lastMessages: []
613068
613127
  };
613069
613128
  userMemory.userId = entry.fromUserId ?? userMemory.userId;
613129
+ userMemory.isBot = Boolean(userMemory.isBot || entry.isBot);
613070
613130
  userMemory.username = entry.username || userMemory.username;
613071
613131
  userMemory.displayName = entry.firstName || userMemory.displayName;
613072
613132
  for (const alias of [entry.username, entry.firstName, speaker].filter(Boolean)) {
@@ -613302,7 +613362,7 @@ ${mediaContext}` : ""
613302
613362
  const hints = user.relationshipHints.slice(-4).map((hint) => ` - relation=${telegramContextJsonString(hint, 200)}`).join("\n");
613303
613363
  const topics = user.recentTopics.slice(-8).join(", ") || "none";
613304
613364
  return [
613305
- `- ${user.username && user.username !== "unknown" ? `@${user.username}` : user.displayName || "user"}${user.userId ? ` id:${user.userId}` : ""}: messages:${user.messageCount}, direct:${user.directAddressCount}, replies:${user.replyCount}, topics:${topics}`,
613365
+ `- ${user.username && user.username !== "unknown" ? `@${user.username}` : user.displayName || "user"} [${telegramActorKindLabel(user)}]${user.userId ? ` id:${user.userId}` : ""}: messages:${user.messageCount}, direct:${user.directAddressCount}, replies:${user.replyCount}, topics:${topics}`,
613306
613366
  facts,
613307
613367
  hints
613308
613368
  ].filter(Boolean).join("\n");
@@ -613324,7 +613384,7 @@ ${lines.join("\n")}`);
613324
613384
  }
613325
613385
  if (recentActions.length > 0) {
613326
613386
  const lines = recentActions.map(
613327
- (action) => `- ${telegramHistoryTime({ ts: action.ts, role: action.role, text: action.text })} ${action.speaker}/${action.role}${action.mode ? `/${action.mode}` : ""}: ${telegramContextJsonString(action.text, 220)}`
613387
+ (action) => `- ${telegramHistoryTime({ ts: action.ts, role: action.role, text: action.text })} ${action.speaker} [${telegramActorKindLabel({ isBot: action.role === "assistant" || action.isBot })}]/${action.role}${action.mode ? `/${action.mode}` : ""}: ${telegramContextJsonString(action.text, 220)}`
613328
613388
  );
613329
613389
  sections.push(`### Durable Recent Action Ledger Recall
613330
613390
  ${lines.join("\n")}`);
@@ -613455,6 +613515,21 @@ ${lines.join("\n")}`);
613455
613515
  return [];
613456
613516
  }
613457
613517
  }
613518
+ telegramSqliteRowIsBot(row) {
613519
+ if (row.role === "assistant") return true;
613520
+ if (row.from_is_bot === 1 || row.from_is_bot === true) return true;
613521
+ for (const key of ["normalized_json", "raw_json"]) {
613522
+ const raw = row?.[key];
613523
+ if (typeof raw !== "string" || !raw.trim()) continue;
613524
+ try {
613525
+ const parsed = JSON.parse(raw);
613526
+ if (parsed?.isBot === true) return true;
613527
+ if (parsed?.from?.is_bot === true) return true;
613528
+ } catch {
613529
+ }
613530
+ }
613531
+ return false;
613532
+ }
613458
613533
  telegramSqliteRowToHistoryEntry(row) {
613459
613534
  const num = (value2) => {
613460
613535
  const n2 = Number(value2);
@@ -613463,15 +613538,17 @@ ${lines.join("\n")}`);
613463
613538
  const role = row.role === "assistant" ? "assistant" : "user";
613464
613539
  const username = row.username ? String(row.username) : void 0;
613465
613540
  const fromUserId = num(row.from_user_id);
613541
+ const isBot = this.telegramSqliteRowIsBot(row);
613466
613542
  return {
613467
613543
  role,
613468
613544
  text: String(row.text || ""),
613469
613545
  ts: num(row.received_at),
613470
613546
  chatId: row.chat_id,
613471
- speaker: role === "assistant" ? `@${this.state.botUsername || username || "omnius"}` : username ? `@${username}` : fromUserId !== void 0 ? `user:${fromUserId}` : "unknown",
613547
+ speaker: role === "assistant" ? `@${this.state.botUsername || username || "omnius"}` : username ? `@${username}` : fromUserId !== void 0 ? isBot ? `bot:${fromUserId}` : `user:${fromUserId}` : "unknown",
613472
613548
  username,
613473
613549
  firstName: row.first_name ? String(row.first_name) : void 0,
613474
613550
  fromUserId,
613551
+ isBot,
613475
613552
  messageId: num(row.message_id),
613476
613553
  messageThreadId: num(row.message_thread_id),
613477
613554
  replyToMessageId: num(row.reply_to_message_id),
@@ -613557,6 +613634,12 @@ ${lines.join("\n")}`);
613557
613634
  lower(COALESCE(username, '')) AS username_key,
613558
613635
  COALESCE(username, '') AS username,
613559
613636
  COALESCE(first_name, '') AS first_name,
613637
+ MAX(CASE
613638
+ WHEN role = 'assistant' THEN 1
613639
+ WHEN normalized_json LIKE '%"isBot":true%' THEN 1
613640
+ WHEN raw_json LIKE '%"is_bot":true%' THEN 1
613641
+ ELSE 0
613642
+ END) AS from_is_bot,
613560
613643
  COUNT(*) AS n,
613561
613644
  MIN(received_at) AS first_ts,
613562
613645
  MAX(received_at) AS last_ts
@@ -613569,11 +613652,13 @@ ${lines.join("\n")}`);
613569
613652
  return rows.map((row) => {
613570
613653
  const role = row.role === "assistant" ? "assistant" : "user";
613571
613654
  const fromUserId = Number(row.from_user_id);
613572
- const speaker = role === "assistant" ? `@${this.state.botUsername || row.username || "omnius"}` : row.username ? `@${row.username}` : Number.isFinite(fromUserId) ? `user:${fromUserId}` : row.first_name || "unknown";
613655
+ const isBot = role === "assistant" || row.from_is_bot === 1 || row.from_is_bot === true;
613656
+ const speaker = role === "assistant" ? `@${this.state.botUsername || row.username || "omnius"}` : row.username ? `@${row.username}` : Number.isFinite(fromUserId) ? isBot ? `bot:${fromUserId}` : `user:${fromUserId}` : row.first_name || "unknown";
613573
613657
  const firstTs = Number(row.first_ts);
613574
613658
  const lastTs = Number(row.last_ts);
613575
613659
  return {
613576
613660
  speaker,
613661
+ isBot,
613577
613662
  count: Number(row.n) || 0,
613578
613663
  firstTs: Number.isFinite(firstTs) ? firstTs : void 0,
613579
613664
  lastTs: Number.isFinite(lastTs) ? lastTs : void 0
@@ -613589,7 +613674,8 @@ ${lines.join("\n")}`);
613589
613674
  if (options2.since !== void 0 && (entry.ts ?? 0) < options2.since) continue;
613590
613675
  if (options2.until !== void 0 && (entry.ts ?? 0) > options2.until) continue;
613591
613676
  const speaker = telegramHistorySpeaker(entry);
613592
- const current = stats.get(speaker) ?? { speaker, count: 0 };
613677
+ const current = stats.get(speaker) ?? { speaker, isBot: entry.role === "assistant" || entry.isBot, count: 0 };
613678
+ current.isBot = Boolean(current.isBot || entry.role === "assistant" || entry.isBot);
613593
613679
  current.count += 1;
613594
613680
  if (entry.ts !== void 0) {
613595
613681
  current.firstTs = current.firstTs === void 0 ? entry.ts : Math.min(current.firstTs, entry.ts);
@@ -613603,7 +613689,7 @@ ${lines.join("\n")}`);
613603
613689
  return stats.map((stat7) => {
613604
613690
  const first2 = stat7.firstTs ? new Date(stat7.firstTs).toISOString() : "?";
613605
613691
  const last2 = stat7.lastTs ? new Date(stat7.lastTs).toISOString() : "?";
613606
- return `- ${stat7.speaker}: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`;
613692
+ return `- ${stat7.speaker} [${telegramActorKindLabel(stat7)}]: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`;
613607
613693
  }).join("\n");
613608
613694
  }
613609
613695
  searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
@@ -613685,10 +613771,11 @@ ${lines.join("\n")}`);
613685
613771
  formatTelegramSqliteMirrorRows(rows, maxText = 260) {
613686
613772
  return rows.map((row) => {
613687
613773
  const when = row.received_at ? new Date(Number(row.received_at)).toISOString() : "";
613688
- const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? `user:${row.from_user_id}` : "unknown";
613774
+ const speaker = row.role === "assistant" ? `@${this.state.botUsername || "omnius"}` : row.username ? `@${row.username}` : row.from_user_id ? this.telegramSqliteRowIsBot(row) ? `bot:${row.from_user_id}` : `user:${row.from_user_id}` : "unknown";
613775
+ const kind = row.role === "assistant" || this.telegramSqliteRowIsBot(row) ? "bot" : "human";
613689
613776
  const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
613690
613777
  const media = row.media_json ? " media:attached" : "";
613691
- return `- ${when} ${speaker}/${row.role || "user"} msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
613778
+ return `- ${when} ${speaker}/${row.role || "user"} [${kind}] msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
613692
613779
  }).join("\n");
613693
613780
  }
613694
613781
  searchTelegramEpisodeMemory(sessionKey, query, limit = 8) {
@@ -613826,9 +613913,10 @@ ${lines.join("\n")}`);
613826
613913
  const fmtHistoryAnchor = (entry) => {
613827
613914
  const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
613828
613915
  const speaker = telegramHistorySpeaker(entry);
613916
+ const kind = entry.role === "assistant" || entry.isBot ? "bot" : "human";
613829
613917
  const mode = entry.mode ? `/${entry.mode}` : "";
613830
613918
  const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
613831
- return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613919
+ return `${when} ${speaker} [${kind}]${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613832
613920
  };
613833
613921
  const sameHistoryAnchor = (a2, b) => {
613834
613922
  if (!a2 || !b) return false;
@@ -613963,7 +614051,7 @@ ${lines.join("\n")}`);
613963
614051
  `Chat: ${isGroup ? `${msg.chatType} "${msg.chatTitle || "unknown"}"` : "private DM"}`,
613964
614052
  `Retained messages in bridge memory: ${retainedCount}`,
613965
614053
  olderCount ? `Older retained messages compacted below: ${olderCount}` : "",
613966
- `Current sender: ${currentSpeaker}${msg.fromUserId ? ` (id ${msg.fromUserId})` : ""}`,
614054
+ `Current sender: ${currentSpeaker} [${telegramActorKindLabel(msg)}]${msg.fromUserId ? ` (id ${msg.fromUserId})` : ""}`,
613967
614055
  msg.messageThreadId !== void 0 ? `Current Telegram thread/topic id: ${msg.messageThreadId}` : "",
613968
614056
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : ""
613969
614057
  ].filter(Boolean).join("\n")
@@ -613986,7 +614074,7 @@ ${lines.join("\n")}`);
613986
614074
  const tones = [...profile.toneTags].slice(0, 5).join(", ") || "neutral";
613987
614075
  const direct = profile.directAddressCount ? `, direct-addresses:${profile.directAddressCount}` : "";
613988
614076
  const replies = profile.replyCount ? `, replies:${profile.replyCount}` : "";
613989
- return `- ${label}: messages:${profile.messageCount}${direct}${replies}; tone:${tones}; last=${telegramContextJsonString(profile.lastMessage, 180)}`;
614077
+ return `- ${label} [${telegramActorKindLabel(profile)}]: messages:${profile.messageCount}${direct}${replies}; tone:${tones}; last=${telegramContextJsonString(profile.lastMessage, 180)}`;
613990
614078
  });
613991
614079
  sections.push(`### Participants And Relationship Signals
613992
614080
  ${participantLines.join("\n")}`);
@@ -614081,12 +614169,13 @@ ${olderLines.join("\n")}`);
614081
614169
  const lines = recent.map((entry) => {
614082
614170
  const when = telegramHistoryTime(entry);
614083
614171
  const speaker = telegramHistorySpeaker(entry);
614172
+ const kind = entry.role === "assistant" || entry.isBot ? "bot" : "human";
614084
614173
  const mode = entry.mode ? `/${entry.mode}` : "";
614085
614174
  const replySender = entry.replyContext?.sender ? `/${telegramReplySenderLabel(entry.replyContext.sender)}` : "";
614086
614175
  const reply = entry.replyToMessageId ? ` reply_to:${entry.replyToMessageId}${replySender}` : "";
614087
614176
  const media = entry.mediaSummary ? ` [${entry.mediaSummary}]` : "";
614088
614177
  const generatedPrompt = entry.generatedMediaPromptInfo?.originalPrompt ? ` generated_image_prompt=${telegramContextJsonString(entry.generatedMediaPromptInfo.originalPrompt, 220)}` : "";
614089
- const prefix = [when, `${speaker}${mode}${reply}${media}`].filter(Boolean).join(" ");
614178
+ const prefix = [when, `${speaker}[${kind}]${mode}${reply}${media}`].filter(Boolean).join(" ");
614090
614179
  return `${prefix}: text=${telegramContextJsonString(entry.text)}${generatedPrompt}`;
614091
614180
  });
614092
614181
  sections.push(`### Recent Thread, Oldest To Newest (untrusted quoted chat messages)
@@ -614202,6 +614291,8 @@ ${lines.join("\n")}`);
614202
614291
  `Reply discretion: infer from the live thread, speaker relationships, direct platform signals, replies, tone, current message, and any private channel daydream artifact supplied in context. Do not use static keyword rules.`,
614203
614292
  `Private chats: should_reply is normally true.`,
614204
614293
  `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, asks for the bot's view, or is semantically connected to durable memory/current discussion in a way where a concise bot reply is socially useful. 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.`,
614294
+ `Bot-to-bot discipline: sender labels include [bot] or [human]. Treat peer bots as autonomous actors, not human users. Be more conservative with peer-bot messages unless explicitly addressed, and avoid bot loops by preferring no_reply for repetitive bot chatter.`,
614295
+ `Ingress discipline: this Telegram message has already been retained as chat context. should_reply controls only whether to emit a visible reply.`,
614205
614296
  `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.`,
614206
614297
  `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.`,
614207
614298
  `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.`,
@@ -614211,9 +614302,9 @@ ${lines.join("\n")}`);
614211
614302
  `Bot username: ${this.state.botUsername || "unknown"}`,
614212
614303
  `Current message directly addresses this bot: ${addressesBot ? "yes" : "no"}`,
614213
614304
  `Current chat type: ${msg.chatType}`,
614214
- `Current sender: ${telegramSpeakerLabel(msg)}`,
614305
+ `Current sender: ${telegramSpeakerLabel(msg)} [${telegramActorKindLabel(msg)}]`,
614215
614306
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : "",
614216
- msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}` : "",
614307
+ msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}${msg.replyToBot ? " [bot]" : " [human/unknown]"}` : "",
614217
614308
  currentReplyContext,
614218
614309
  (msg.mentionedUsernames ?? []).length > 0 ? `Current message mentions: ${(msg.mentionedUsernames ?? []).map((name10) => `@${name10}`).join(", ")}` : "",
614219
614310
  msg.media ? `Current message has media: ${summarizeTelegramMessageAttachments(msg)}` : "",
@@ -614608,6 +614699,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614608
614699
  active: true,
614609
614700
  botUsername: me.result?.username ?? "unknown",
614610
614701
  supportsGuestQueries: Boolean(me.result?.supports_guest_queries),
614702
+ canReadAllGroupMessages: me.result?.can_read_all_group_messages === true,
614611
614703
  interactionMode: this.interactionMode,
614612
614704
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
614613
614705
  messagesReceived: 0,
@@ -614847,7 +614939,16 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614847
614939
  */
614848
614940
  async handleMessageWithSubAgent(msg) {
614849
614941
  const normalizedCommandText = this.normalizeTelegramCommandText(msg.text);
614850
- if (msg.text.trim().startsWith("/") && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614942
+ const startsWithSlash = msg.text.trim().startsWith("/");
614943
+ const commandName = normalizedCommandText.trim().toLowerCase().split(/\s+/)[0]?.split("@")[0] ?? "";
614944
+ const isAdminAuthSecretCommand = commandName === "/auth" || commandName === "/authenticate";
614945
+ if (startsWithSlash && isAdminAuthSecretCommand && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614946
+ return;
614947
+ }
614948
+ if (msg.chatType !== "private") {
614949
+ this.recordTelegramUserMessage(msg, "ambient");
614950
+ }
614951
+ if (startsWithSlash && !isAdminAuthSecretCommand && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614851
614952
  return;
614852
614953
  }
614853
614954
  const isAdmin = this.isAdminUser(msg);
@@ -615974,7 +616075,8 @@ ${notes2}`;
615974
616075
  entry.text || "",
615975
616076
  telegramHistorySpeaker(entry),
615976
616077
  entry.username || "",
615977
- entry.firstName || ""
616078
+ entry.firstName || "",
616079
+ entry.role === "assistant" || entry.isBot ? "bot" : "human"
615978
616080
  ].join(" ").toLowerCase();
615979
616081
  if (!hay) continue;
615980
616082
  let lexMatch = false;
@@ -617002,6 +617104,7 @@ Scoped workspace: ${scopedRoot}`,
617002
617104
  username: entry.username,
617003
617105
  first_name: entry.firstName,
617004
617106
  from_user_id: entry.fromUserId,
617107
+ is_bot: entry.role === "assistant" || Boolean(entry.isBot),
617005
617108
  message_id: entry.messageId,
617006
617109
  reply_to_message_id: entry.replyToMessageId,
617007
617110
  chat_type: entry.chatType,
@@ -618144,6 +618247,7 @@ Content-Type: ${contentType}\r
618144
618247
  mode: "action",
618145
618248
  chatId,
618146
618249
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
618250
+ isBot: true,
618147
618251
  messageId,
618148
618252
  mediaSummary: summary,
618149
618253
  generatedMediaPromptInfo: info
@@ -618203,6 +618307,7 @@ Content-Type: ${contentType}\r
618203
618307
  mode: "action",
618204
618308
  chatId,
618205
618309
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
618310
+ isBot: true,
618206
618311
  messageId,
618207
618312
  mediaSummary: summary,
618208
618313
  generatedMediaPromptInfo: info
@@ -618663,11 +618768,11 @@ ${caption}\r
618663
618768
  const msg = normalizeTelegramUpdate(update2);
618664
618769
  if (!msg) continue;
618665
618770
  this.persistTelegramRawMessage(update2, msg);
618771
+ this.state.messagesReceived++;
618666
618772
  const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
618667
618773
  if (this.adminUserId && !this.agentConfig) {
618668
618774
  if (!isAdmin) continue;
618669
618775
  }
618670
- this.state.messagesReceived++;
618671
618776
  if (this.agentConfig && this.repoRoot) {
618672
618777
  this.handleMessageWithSubAgent(msg).catch((err) => {
618673
618778
  this.tuiWrite(() => renderWarning(`Telegram sub-agent error: ${err instanceof Error ? err.message : String(err)}`));
@@ -649499,7 +649604,12 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649499
649604
  );
649500
649605
  });
649501
649606
  writeContent(
649502
- () => renderTelegramStart(telegramBridge.botUsername, adminId, savedSettings.telegramMode ?? "auto")
649607
+ () => renderTelegramStart(
649608
+ telegramBridge.botUsername,
649609
+ adminId,
649610
+ savedSettings.telegramMode ?? "auto",
649611
+ telegramBridge.stats.canReadAllGroupMessages
649612
+ )
649503
649613
  );
649504
649614
  showPrompt();
649505
649615
  },
@@ -649574,7 +649684,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649574
649684
  botUser,
649575
649685
  telegramSettingsForScope(activeTelegramSettingsScope ?? "project").admin,
649576
649686
  subAgents,
649577
- savedSettings.telegramMode ?? "auto"
649687
+ savedSettings.telegramMode ?? "auto",
649688
+ active ? telegramBridge?.stats.canReadAllGroupMessages : void 0
649578
649689
  )
649579
649690
  );
649580
649691
  },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.68",
3
+ "version": "1.0.69",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.68",
9
+ "version": "1.0.69",
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.68",
3
+ "version": "1.0.69",
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",