omnius 1.0.68 → 1.0.70

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}
@@ -610905,6 +610921,15 @@ function renderTelegramMessage(username, text) {
610905
610921
  process.stdout.write(` ${c3.cyan("✈")} ${c3.bold(`@${username}`)}: ${preview}
610906
610922
  `);
610907
610923
  }
610924
+ function renderTelegramIngressMessage(msg) {
610925
+ const body = (msg.text || summarizeTelegramMessageAttachments(msg) || "[non-text Telegram message]").replace(/\s+/g, " ").trim();
610926
+ const preview = body.length > 96 ? body.slice(0, 93) + "..." : body;
610927
+ const chat = msg.chatType !== "private" ? ` ${c3.dim(`[${msg.chatTitle || msg.chatType}]`)}` : "";
610928
+ process.stdout.write(
610929
+ ` ${c3.cyan("✈")} ${c3.dim("heard")}${chat} ${c3.bold(`@${msg.username || "unknown"}`)} ${c3.dim(`[${telegramActorKindLabel(msg)}]`)}: ${preview}
610930
+ `
610931
+ );
610932
+ }
610908
610933
  function renderTelegramSubAgentStart(username, text, isAdmin) {
610909
610934
  const preview = text.length > 60 ? text.slice(0, 57) + "..." : text;
610910
610935
  const mode = isAdmin ? c3.green("admin") : c3.yellow("public");
@@ -611139,6 +611164,9 @@ most ambient chatter. Continue to be selective:
611139
611164
  2. Stay silent for conversation between other people, third-person commentary
611140
611165
  about the bot, status chatter, or questions clearly meant for someone else.
611141
611166
  3. Do not reply just because you know an answer or could add color.
611167
+ 4. Peer bots are labeled as bots in context. Do not reflexively answer bot
611168
+ chatter; only continue bot-to-bot exchanges when the message is clearly
611169
+ addressed to this bot or advances an active bot-mediated task.
611142
611170
 
611143
611171
  If you determine no visible reply should be sent, call task_complete with summary
611144
611172
  "no_reply". Never write "no_reply", "skipping", "no action needed", or a status
@@ -611349,6 +611377,7 @@ External acquisition contract:
611349
611377
  active: false,
611350
611378
  botUsername: "",
611351
611379
  supportsGuestQueries: false,
611380
+ canReadAllGroupMessages: false,
611352
611381
  interactionMode: "auto",
611353
611382
  startedAt: "",
611354
611383
  messagesReceived: 0,
@@ -611756,7 +611785,7 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
611756
611785
  id: entry.fromUserId,
611757
611786
  username: entry.username,
611758
611787
  firstName: entry.firstName,
611759
- isBot: false
611788
+ isBot: Boolean(entry.isBot)
611760
611789
  };
611761
611790
  return {
611762
611791
  kind: "message",
@@ -611875,7 +611904,7 @@ ${mediaContext}` : ""
611875
611904
  id: String(msg.fromUserId),
611876
611905
  username: msg.username,
611877
611906
  displayName: msg.firstName || msg.username,
611878
- isBot: false
611907
+ isBot: Boolean(msg.isBot)
611879
611908
  };
611880
611909
  }
611881
611910
  telegramMemorySenderFromReply(msg) {
@@ -612108,6 +612137,7 @@ ${mediaContext}` : ""
612108
612137
  messageThreadId: msg.messageThreadId,
612109
612138
  replyToMessageId,
612110
612139
  username: this.state.botUsername || "omnius",
612140
+ isBot: true,
612111
612141
  text
612112
612142
  };
612113
612143
  try {
@@ -612248,7 +612278,13 @@ ${mediaContext}` : ""
612248
612278
  const parsed = JSON.parse(readFileSync92(path12, "utf8"));
612249
612279
  const loadedHistory = Array.isArray(parsed.history) ? parsed.history : [];
612250
612280
  if (Array.isArray(parsed.history)) {
612251
- this.chatHistory.set(sessionKey, loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT));
612281
+ this.chatHistory.set(
612282
+ sessionKey,
612283
+ loadedHistory.slice(-TELEGRAM_CHAT_HISTORY_LIMIT).map((entry) => ({
612284
+ ...entry,
612285
+ isBot: entry.role === "assistant" ? true : Boolean(entry.isBot)
612286
+ }))
612287
+ );
612252
612288
  }
612253
612289
  if (Array.isArray(parsed.participants)) {
612254
612290
  const participants = /* @__PURE__ */ new Map();
@@ -612256,6 +612292,7 @@ ${mediaContext}` : ""
612256
612292
  const key = String(profile.fromUserId || profile.username || profile.firstName || participants.size);
612257
612293
  participants.set(key, {
612258
612294
  ...profile,
612295
+ isBot: Boolean(profile.isBot),
612259
612296
  toneTags: new Set(Array.isArray(profile.toneTags) ? profile.toneTags : []),
612260
612297
  samples: Array.isArray(profile.samples) ? profile.samples : []
612261
612298
  });
@@ -612766,6 +612803,31 @@ ${mediaContext}` : ""
612766
612803
  const sessionKey = this.sessionKeyForMessage(msg);
612767
612804
  const mediaSummary = summarizeTelegramMessageAttachments(msg);
612768
612805
  const text = (textOverride ?? msg.text ?? "").trim() || mediaSummary || "[non-text Telegram message]";
612806
+ this.ensureTelegramConversationLoaded(sessionKey);
612807
+ const history = this.chatHistory.get(sessionKey) ?? [];
612808
+ const existing = Number.isFinite(msg.messageId) ? history.find(
612809
+ (entry2) => entry2.role === "user" && entry2.messageId === msg.messageId && String(entry2.chatId) === String(msg.chatId)
612810
+ ) : void 0;
612811
+ if (existing) {
612812
+ if (existing.mode === "ambient" || mode !== "ambient") existing.mode = mode;
612813
+ if (textOverride !== void 0 || !existing.text || existing.text === "[non-text Telegram message]") {
612814
+ existing.text = text;
612815
+ }
612816
+ existing.speaker = existing.speaker || telegramSpeakerLabel(msg);
612817
+ existing.username = msg.username || existing.username;
612818
+ existing.firstName = msg.firstName ?? existing.firstName;
612819
+ existing.fromUserId = msg.fromUserId || existing.fromUserId;
612820
+ existing.isBot = msg.isBot ?? existing.isBot;
612821
+ existing.messageThreadId = msg.messageThreadId ?? existing.messageThreadId;
612822
+ existing.replyToMessageId = msg.replyToMessageId ?? existing.replyToMessageId;
612823
+ existing.replyContext = msg.replyContext ?? existing.replyContext;
612824
+ existing.chatType = msg.chatType || existing.chatType;
612825
+ existing.chatTitle = msg.chatTitle ?? existing.chatTitle;
612826
+ existing.mediaSummary = mediaSummary || existing.mediaSummary;
612827
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, existing);
612828
+ this.saveTelegramConversationState(sessionKey);
612829
+ return;
612830
+ }
612769
612831
  const entry = {
612770
612832
  role: "user",
612771
612833
  text,
@@ -612775,6 +612837,7 @@ ${mediaContext}` : ""
612775
612837
  username: msg.username,
612776
612838
  firstName: msg.firstName,
612777
612839
  fromUserId: msg.fromUserId,
612840
+ isBot: Boolean(msg.isBot),
612778
612841
  messageId: msg.messageId,
612779
612842
  messageThreadId: msg.messageThreadId,
612780
612843
  replyToMessageId: msg.replyToMessageId,
@@ -612814,6 +612877,7 @@ ${mediaContext}` : ""
612814
612877
  mode,
612815
612878
  chatId: msg.chatId,
612816
612879
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
612880
+ isBot: true,
612817
612881
  messageId: options2.messageId ?? void 0,
612818
612882
  messageThreadId: msg.messageThreadId,
612819
612883
  replyToMessageId: options2.replyToMessageId,
@@ -612993,6 +613057,7 @@ ${mediaContext}` : ""
612993
613057
  username: msg.username || "unknown",
612994
613058
  firstName: msg.firstName,
612995
613059
  fromUserId: msg.fromUserId,
613060
+ isBot: Boolean(msg.isBot),
612996
613061
  messageCount: 0,
612997
613062
  directAddressCount: 0,
612998
613063
  replyCount: 0,
@@ -613004,6 +613069,7 @@ ${mediaContext}` : ""
613004
613069
  profile.username = msg.username || profile.username;
613005
613070
  profile.firstName = msg.firstName ?? profile.firstName;
613006
613071
  profile.fromUserId = msg.fromUserId || profile.fromUserId;
613072
+ profile.isBot = Boolean(profile.isBot || msg.isBot);
613007
613073
  profile.messageCount += 1;
613008
613074
  profile.lastSeenTs = Date.now();
613009
613075
  profile.lastMessage = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
@@ -613038,7 +613104,8 @@ ${mediaContext}` : ""
613038
613104
  messageId: entry.messageId,
613039
613105
  replyToMessageId: entry.replyToMessageId,
613040
613106
  userId: entry.fromUserId,
613041
- username: entry.username
613107
+ username: entry.username,
613108
+ isBot: entry.role === "assistant" || Boolean(entry.isBot)
613042
613109
  });
613043
613110
  if (memory.actions.length > TELEGRAM_ASSOCIATIVE_ACTION_LIMIT) {
613044
613111
  memory.actions.splice(
@@ -613052,6 +613119,7 @@ ${mediaContext}` : ""
613052
613119
  const existing = memory.users[userKey];
613053
613120
  const userMemory = existing ?? {
613054
613121
  userId: entry.fromUserId,
613122
+ isBot: Boolean(entry.isBot),
613055
613123
  username: entry.username || "unknown",
613056
613124
  displayName: entry.firstName || entry.username || speaker,
613057
613125
  aliases: [],
@@ -613067,6 +613135,7 @@ ${mediaContext}` : ""
613067
613135
  lastMessages: []
613068
613136
  };
613069
613137
  userMemory.userId = entry.fromUserId ?? userMemory.userId;
613138
+ userMemory.isBot = Boolean(userMemory.isBot || entry.isBot);
613070
613139
  userMemory.username = entry.username || userMemory.username;
613071
613140
  userMemory.displayName = entry.firstName || userMemory.displayName;
613072
613141
  for (const alias of [entry.username, entry.firstName, speaker].filter(Boolean)) {
@@ -613302,7 +613371,7 @@ ${mediaContext}` : ""
613302
613371
  const hints = user.relationshipHints.slice(-4).map((hint) => ` - relation=${telegramContextJsonString(hint, 200)}`).join("\n");
613303
613372
  const topics = user.recentTopics.slice(-8).join(", ") || "none";
613304
613373
  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}`,
613374
+ `- ${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
613375
  facts,
613307
613376
  hints
613308
613377
  ].filter(Boolean).join("\n");
@@ -613324,7 +613393,7 @@ ${lines.join("\n")}`);
613324
613393
  }
613325
613394
  if (recentActions.length > 0) {
613326
613395
  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)}`
613396
+ (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
613397
  );
613329
613398
  sections.push(`### Durable Recent Action Ledger Recall
613330
613399
  ${lines.join("\n")}`);
@@ -613455,6 +613524,21 @@ ${lines.join("\n")}`);
613455
613524
  return [];
613456
613525
  }
613457
613526
  }
613527
+ telegramSqliteRowIsBot(row) {
613528
+ if (row.role === "assistant") return true;
613529
+ if (row.from_is_bot === 1 || row.from_is_bot === true) return true;
613530
+ for (const key of ["normalized_json", "raw_json"]) {
613531
+ const raw = row?.[key];
613532
+ if (typeof raw !== "string" || !raw.trim()) continue;
613533
+ try {
613534
+ const parsed = JSON.parse(raw);
613535
+ if (parsed?.isBot === true) return true;
613536
+ if (parsed?.from?.is_bot === true) return true;
613537
+ } catch {
613538
+ }
613539
+ }
613540
+ return false;
613541
+ }
613458
613542
  telegramSqliteRowToHistoryEntry(row) {
613459
613543
  const num = (value2) => {
613460
613544
  const n2 = Number(value2);
@@ -613463,15 +613547,17 @@ ${lines.join("\n")}`);
613463
613547
  const role = row.role === "assistant" ? "assistant" : "user";
613464
613548
  const username = row.username ? String(row.username) : void 0;
613465
613549
  const fromUserId = num(row.from_user_id);
613550
+ const isBot = this.telegramSqliteRowIsBot(row);
613466
613551
  return {
613467
613552
  role,
613468
613553
  text: String(row.text || ""),
613469
613554
  ts: num(row.received_at),
613470
613555
  chatId: row.chat_id,
613471
- speaker: role === "assistant" ? `@${this.state.botUsername || username || "omnius"}` : username ? `@${username}` : fromUserId !== void 0 ? `user:${fromUserId}` : "unknown",
613556
+ speaker: role === "assistant" ? `@${this.state.botUsername || username || "omnius"}` : username ? `@${username}` : fromUserId !== void 0 ? isBot ? `bot:${fromUserId}` : `user:${fromUserId}` : "unknown",
613472
613557
  username,
613473
613558
  firstName: row.first_name ? String(row.first_name) : void 0,
613474
613559
  fromUserId,
613560
+ isBot,
613475
613561
  messageId: num(row.message_id),
613476
613562
  messageThreadId: num(row.message_thread_id),
613477
613563
  replyToMessageId: num(row.reply_to_message_id),
@@ -613557,6 +613643,12 @@ ${lines.join("\n")}`);
613557
613643
  lower(COALESCE(username, '')) AS username_key,
613558
613644
  COALESCE(username, '') AS username,
613559
613645
  COALESCE(first_name, '') AS first_name,
613646
+ MAX(CASE
613647
+ WHEN role = 'assistant' THEN 1
613648
+ WHEN normalized_json LIKE '%"isBot":true%' THEN 1
613649
+ WHEN raw_json LIKE '%"is_bot":true%' THEN 1
613650
+ ELSE 0
613651
+ END) AS from_is_bot,
613560
613652
  COUNT(*) AS n,
613561
613653
  MIN(received_at) AS first_ts,
613562
613654
  MAX(received_at) AS last_ts
@@ -613569,11 +613661,13 @@ ${lines.join("\n")}`);
613569
613661
  return rows.map((row) => {
613570
613662
  const role = row.role === "assistant" ? "assistant" : "user";
613571
613663
  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";
613664
+ const isBot = role === "assistant" || row.from_is_bot === 1 || row.from_is_bot === true;
613665
+ 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
613666
  const firstTs = Number(row.first_ts);
613574
613667
  const lastTs = Number(row.last_ts);
613575
613668
  return {
613576
613669
  speaker,
613670
+ isBot,
613577
613671
  count: Number(row.n) || 0,
613578
613672
  firstTs: Number.isFinite(firstTs) ? firstTs : void 0,
613579
613673
  lastTs: Number.isFinite(lastTs) ? lastTs : void 0
@@ -613589,7 +613683,8 @@ ${lines.join("\n")}`);
613589
613683
  if (options2.since !== void 0 && (entry.ts ?? 0) < options2.since) continue;
613590
613684
  if (options2.until !== void 0 && (entry.ts ?? 0) > options2.until) continue;
613591
613685
  const speaker = telegramHistorySpeaker(entry);
613592
- const current = stats.get(speaker) ?? { speaker, count: 0 };
613686
+ const current = stats.get(speaker) ?? { speaker, isBot: entry.role === "assistant" || entry.isBot, count: 0 };
613687
+ current.isBot = Boolean(current.isBot || entry.role === "assistant" || entry.isBot);
613593
613688
  current.count += 1;
613594
613689
  if (entry.ts !== void 0) {
613595
613690
  current.firstTs = current.firstTs === void 0 ? entry.ts : Math.min(current.firstTs, entry.ts);
@@ -613603,7 +613698,7 @@ ${lines.join("\n")}`);
613603
613698
  return stats.map((stat7) => {
613604
613699
  const first2 = stat7.firstTs ? new Date(stat7.firstTs).toISOString() : "?";
613605
613700
  const last2 = stat7.lastTs ? new Date(stat7.lastTs).toISOString() : "?";
613606
- return `- ${stat7.speaker}: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`;
613701
+ return `- ${stat7.speaker} [${telegramActorKindLabel(stat7)}]: ${stat7.count} message${stat7.count === 1 ? "" : "s"} (first:${first2}, last:${last2})`;
613607
613702
  }).join("\n");
613608
613703
  }
613609
613704
  searchTelegramSqliteMirrorRows(sessionKey, query, options2 = {}) {
@@ -613685,10 +613780,11 @@ ${lines.join("\n")}`);
613685
613780
  formatTelegramSqliteMirrorRows(rows, maxText = 260) {
613686
613781
  return rows.map((row) => {
613687
613782
  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";
613783
+ 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";
613784
+ const kind = row.role === "assistant" || this.telegramSqliteRowIsBot(row) ? "bot" : "human";
613689
613785
  const reply = row.reply_to_message_id ? ` reply_to:${row.reply_to_message_id}` : "";
613690
613786
  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)}`;
613787
+ return `- ${when} ${speaker}/${row.role || "user"} [${kind}] msg:${row.message_id}${reply}${media}: ${telegramContextJsonString(String(row.text || ""), maxText)}`;
613692
613788
  }).join("\n");
613693
613789
  }
613694
613790
  searchTelegramEpisodeMemory(sessionKey, query, limit = 8) {
@@ -613826,9 +613922,10 @@ ${lines.join("\n")}`);
613826
613922
  const fmtHistoryAnchor = (entry) => {
613827
613923
  const when = entry.ts ? new Date(entry.ts).toISOString() : "(unknown ts)";
613828
613924
  const speaker = telegramHistorySpeaker(entry);
613925
+ const kind = entry.role === "assistant" || entry.isBot ? "bot" : "human";
613829
613926
  const mode = entry.mode ? `/${entry.mode}` : "";
613830
613927
  const messageId = entry.messageId ? ` msg:${entry.messageId}` : "";
613831
- return `${when} ${speaker}${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613928
+ return `${when} ${speaker} [${kind}]${mode}${messageId}: ${telegramContextJsonString(String(entry.text || ""), 320)}`;
613832
613929
  };
613833
613930
  const sameHistoryAnchor = (a2, b) => {
613834
613931
  if (!a2 || !b) return false;
@@ -613963,7 +614060,7 @@ ${lines.join("\n")}`);
613963
614060
  `Chat: ${isGroup ? `${msg.chatType} "${msg.chatTitle || "unknown"}"` : "private DM"}`,
613964
614061
  `Retained messages in bridge memory: ${retainedCount}`,
613965
614062
  olderCount ? `Older retained messages compacted below: ${olderCount}` : "",
613966
- `Current sender: ${currentSpeaker}${msg.fromUserId ? ` (id ${msg.fromUserId})` : ""}`,
614063
+ `Current sender: ${currentSpeaker} [${telegramActorKindLabel(msg)}]${msg.fromUserId ? ` (id ${msg.fromUserId})` : ""}`,
613967
614064
  msg.messageThreadId !== void 0 ? `Current Telegram thread/topic id: ${msg.messageThreadId}` : "",
613968
614065
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : ""
613969
614066
  ].filter(Boolean).join("\n")
@@ -613986,7 +614083,7 @@ ${lines.join("\n")}`);
613986
614083
  const tones = [...profile.toneTags].slice(0, 5).join(", ") || "neutral";
613987
614084
  const direct = profile.directAddressCount ? `, direct-addresses:${profile.directAddressCount}` : "";
613988
614085
  const replies = profile.replyCount ? `, replies:${profile.replyCount}` : "";
613989
- return `- ${label}: messages:${profile.messageCount}${direct}${replies}; tone:${tones}; last=${telegramContextJsonString(profile.lastMessage, 180)}`;
614086
+ return `- ${label} [${telegramActorKindLabel(profile)}]: messages:${profile.messageCount}${direct}${replies}; tone:${tones}; last=${telegramContextJsonString(profile.lastMessage, 180)}`;
613990
614087
  });
613991
614088
  sections.push(`### Participants And Relationship Signals
613992
614089
  ${participantLines.join("\n")}`);
@@ -614081,12 +614178,13 @@ ${olderLines.join("\n")}`);
614081
614178
  const lines = recent.map((entry) => {
614082
614179
  const when = telegramHistoryTime(entry);
614083
614180
  const speaker = telegramHistorySpeaker(entry);
614181
+ const kind = entry.role === "assistant" || entry.isBot ? "bot" : "human";
614084
614182
  const mode = entry.mode ? `/${entry.mode}` : "";
614085
614183
  const replySender = entry.replyContext?.sender ? `/${telegramReplySenderLabel(entry.replyContext.sender)}` : "";
614086
614184
  const reply = entry.replyToMessageId ? ` reply_to:${entry.replyToMessageId}${replySender}` : "";
614087
614185
  const media = entry.mediaSummary ? ` [${entry.mediaSummary}]` : "";
614088
614186
  const generatedPrompt = entry.generatedMediaPromptInfo?.originalPrompt ? ` generated_image_prompt=${telegramContextJsonString(entry.generatedMediaPromptInfo.originalPrompt, 220)}` : "";
614089
- const prefix = [when, `${speaker}${mode}${reply}${media}`].filter(Boolean).join(" ");
614187
+ const prefix = [when, `${speaker}[${kind}]${mode}${reply}${media}`].filter(Boolean).join(" ");
614090
614188
  return `${prefix}: text=${telegramContextJsonString(entry.text)}${generatedPrompt}`;
614091
614189
  });
614092
614190
  sections.push(`### Recent Thread, Oldest To Newest (untrusted quoted chat messages)
@@ -614202,6 +614300,8 @@ ${lines.join("\n")}`);
614202
614300
  `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
614301
  `Private chats: should_reply is normally true.`,
614204
614302
  `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.`,
614303
+ `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.`,
614304
+ `Ingress discipline: this Telegram message has already been retained as chat context. should_reply controls only whether to emit a visible reply.`,
614205
614305
  `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
614306
  `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
614307
  `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 +614311,9 @@ ${lines.join("\n")}`);
614211
614311
  `Bot username: ${this.state.botUsername || "unknown"}`,
614212
614312
  `Current message directly addresses this bot: ${addressesBot ? "yes" : "no"}`,
614213
614313
  `Current chat type: ${msg.chatType}`,
614214
- `Current sender: ${telegramSpeakerLabel(msg)}`,
614314
+ `Current sender: ${telegramSpeakerLabel(msg)} [${telegramActorKindLabel(msg)}]`,
614215
614315
  msg.replyToMessageId ? `Current message replies to message_id ${msg.replyToMessageId}` : "",
614216
- msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}` : "",
614316
+ msg.replyToUsername ? `Current message replies to @${msg.replyToUsername}${msg.replyToBot ? " [bot]" : " [human/unknown]"}` : "",
614217
614317
  currentReplyContext,
614218
614318
  (msg.mentionedUsernames ?? []).length > 0 ? `Current message mentions: ${(msg.mentionedUsernames ?? []).map((name10) => `@${name10}`).join(", ")}` : "",
614219
614319
  msg.media ? `Current message has media: ${summarizeTelegramMessageAttachments(msg)}` : "",
@@ -614608,6 +614708,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614608
614708
  active: true,
614609
614709
  botUsername: me.result?.username ?? "unknown",
614610
614710
  supportsGuestQueries: Boolean(me.result?.supports_guest_queries),
614711
+ canReadAllGroupMessages: me.result?.can_read_all_group_messages === true,
614611
614712
  interactionMode: this.interactionMode,
614612
614713
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
614613
614714
  messagesReceived: 0,
@@ -614847,7 +614948,17 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
614847
614948
  */
614848
614949
  async handleMessageWithSubAgent(msg) {
614849
614950
  const normalizedCommandText = this.normalizeTelegramCommandText(msg.text);
614850
- if (msg.text.trim().startsWith("/") && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614951
+ const startsWithSlash = msg.text.trim().startsWith("/");
614952
+ const commandName = normalizedCommandText.trim().toLowerCase().split(/\s+/)[0]?.split("@")[0] ?? "";
614953
+ const isAdminAuthSecretCommand = commandName === "/auth" || commandName === "/authenticate";
614954
+ if (startsWithSlash && isAdminAuthSecretCommand && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614955
+ return;
614956
+ }
614957
+ if (msg.chatType !== "private") {
614958
+ this.recordTelegramUserMessage(msg, "ambient");
614959
+ this.tuiWrite(() => renderTelegramIngressMessage(msg));
614960
+ }
614961
+ if (startsWithSlash && !isAdminAuthSecretCommand && await this.handleAdminAuthCommand({ ...msg, text: normalizedCommandText })) {
614851
614962
  return;
614852
614963
  }
614853
614964
  const isAdmin = this.isAdminUser(msg);
@@ -615974,7 +616085,8 @@ ${notes2}`;
615974
616085
  entry.text || "",
615975
616086
  telegramHistorySpeaker(entry),
615976
616087
  entry.username || "",
615977
- entry.firstName || ""
616088
+ entry.firstName || "",
616089
+ entry.role === "assistant" || entry.isBot ? "bot" : "human"
615978
616090
  ].join(" ").toLowerCase();
615979
616091
  if (!hay) continue;
615980
616092
  let lexMatch = false;
@@ -617002,6 +617114,7 @@ Scoped workspace: ${scopedRoot}`,
617002
617114
  username: entry.username,
617003
617115
  first_name: entry.firstName,
617004
617116
  from_user_id: entry.fromUserId,
617117
+ is_bot: entry.role === "assistant" || Boolean(entry.isBot),
617005
617118
  message_id: entry.messageId,
617006
617119
  reply_to_message_id: entry.replyToMessageId,
617007
617120
  chat_type: entry.chatType,
@@ -618144,6 +618257,7 @@ Content-Type: ${contentType}\r
618144
618257
  mode: "action",
618145
618258
  chatId,
618146
618259
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
618260
+ isBot: true,
618147
618261
  messageId,
618148
618262
  mediaSummary: summary,
618149
618263
  generatedMediaPromptInfo: info
@@ -618203,6 +618317,7 @@ Content-Type: ${contentType}\r
618203
618317
  mode: "action",
618204
618318
  chatId,
618205
618319
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
618320
+ isBot: true,
618206
618321
  messageId,
618207
618322
  mediaSummary: summary,
618208
618323
  generatedMediaPromptInfo: info
@@ -618663,11 +618778,11 @@ ${caption}\r
618663
618778
  const msg = normalizeTelegramUpdate(update2);
618664
618779
  if (!msg) continue;
618665
618780
  this.persistTelegramRawMessage(update2, msg);
618781
+ this.state.messagesReceived++;
618666
618782
  const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
618667
618783
  if (this.adminUserId && !this.agentConfig) {
618668
618784
  if (!isAdmin) continue;
618669
618785
  }
618670
- this.state.messagesReceived++;
618671
618786
  if (this.agentConfig && this.repoRoot) {
618672
618787
  this.handleMessageWithSubAgent(msg).catch((err) => {
618673
618788
  this.tuiWrite(() => renderWarning(`Telegram sub-agent error: ${err instanceof Error ? err.message : String(err)}`));
@@ -649499,7 +649614,12 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649499
649614
  );
649500
649615
  });
649501
649616
  writeContent(
649502
- () => renderTelegramStart(telegramBridge.botUsername, adminId, savedSettings.telegramMode ?? "auto")
649617
+ () => renderTelegramStart(
649618
+ telegramBridge.botUsername,
649619
+ adminId,
649620
+ savedSettings.telegramMode ?? "auto",
649621
+ telegramBridge.stats.canReadAllGroupMessages
649622
+ )
649503
649623
  );
649504
649624
  showPrompt();
649505
649625
  },
@@ -649574,7 +649694,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
649574
649694
  botUser,
649575
649695
  telegramSettingsForScope(activeTelegramSettingsScope ?? "project").admin,
649576
649696
  subAgents,
649577
- savedSettings.telegramMode ?? "auto"
649697
+ savedSettings.telegramMode ?? "auto",
649698
+ active ? telegramBridge?.stats.canReadAllGroupMessages : void 0
649578
649699
  )
649579
649700
  );
649580
649701
  },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.68",
3
+ "version": "1.0.70",
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.70",
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.70",
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",