fix: _default_chat_id не выбирает tui:*-чат личкой Alex

Тестовое TUI-сообщение (t-261004132716, chat_id=tui:local) стало
последним в inbox → notify_chat/send_document целились в несуществующий
Telegram-чат → уведомления тихо SKIP'ались при живом чате. Фильтр
локальных каналов расширен: portal:* И tui:*.
This commit is contained in:
Simbiot Harness 2026-10-04 15:28:00 +00:00
parent 3f2300700c
commit 7e19e1b3f6

View file

@ -228,6 +228,15 @@ def send_reply(msg_id: str, text: str, timeout: int = 35):
"to_chat": m["chat_id"], "text": text[:2000], "channel": "portal", "to_chat": m["chat_id"], "text": text[:2000], "channel": "portal",
}) })
return msg_id return msg_id
# ── TUI-канал (t-261004132716): чат 'tui:*' — локальный терминал агента.
# Доставка = запись в этот же стор: TUI-процесс (tools/tui_chat.py) читает
# reply_text проекции и печатает. Внешнего транспорта нет — ReplySent
# пишем сразу, ответ не может «потеряться в сети».
if str(m.get("chat_id") or "").startswith("tui:"):
es.append_event("inbox", msg_id, "ReplySent", {
"to_chat": m["chat_id"], "text": text[:2000], "channel": "tui",
})
return msg_id
_load_env() # sendChatAction идёт до send_message: в свежем процессе _load_env() # sendChatAction идёт до send_message: в свежем процессе
# (демон/сек-рефлекс) токен ещё не загружен — иначе «печатает…» молча теряется # (демон/сек-рефлекс) токен ещё не загружен — иначе «печатает…» молча теряется
try: # индикатор — best effort: недоступность TG-метода не мешает ответу try: # индикатор — best effort: недоступность TG-метода не мешает ответу
@ -303,6 +312,18 @@ def send_batch_reply(msg_ids: list, text: str, timeout: int = 35):
}) })
return head_mid return head_mid
for chat_id, items in by_chat.items(): for chat_id, items in by_chat.items():
if str(chat_id).startswith("tui:"):
# TUI-канал (t-261004132716): локальная доставка событием в стор,
# без Telegram — TUI-процесс читает reply_text проекции.
for mid, m in items:
es.append_event("inbox", mid, "ReplySent", {
"to_chat": chat_id,
"text": (text if mid == head_mid
else f"(сводный ответ батча {batch_id} — см. {head_mid})")[:2000],
"channel": "tui",
"batch": {"id": batch_id, "size": len(pending), "head": head_mid},
})
continue
try: # индикатор — best effort, как в send_reply try: # индикатор — best effort, как в send_reply
_api("sendChatAction", {"chat_id": chat_id, "action": "typing"}, timeout=10) _api("sendChatAction", {"chat_id": chat_id, "action": "typing"}, timeout=10)
except Exception: except Exception:
@ -413,12 +434,14 @@ def _default_chat_id():
if not TG_TOKEN: if not TG_TOKEN:
return None return None
model = es.run_projections() model = es.run_projections()
# portal:* — это портал-канал (t-261001123121), не Telegram: файлы/тексты # portal:* — портал-канал (t-261001123121), tui:* — терминал-чат
# туда идут через push portal_channel, а не через API Telegram. # (t-261004132716): оба НЕ Telegram, уходят своими транспортами.
# Без фильтра E2E/чат портала «перебивает» личку Alex дефолтным чатом # Без фильтра E2E/чат портала или TUI «перебивает» личку Alex
# → send_message/send_document падают 'chat not found' (найдено 02.10). # дефолтным чатом → send_message/send_document падают 'chat not found'
# (портал — найдено 02.10; tui — 04.10, SKIP уведомлений при живом чате).
inbox = [m for m in (model.get("inbox") or {}).values() inbox = [m for m in (model.get("inbox") or {}).values()
if m.get("chat_id") and not str(m.get("chat_id")).startswith("portal") if m.get("chat_id")
and not str(m.get("chat_id")).startswith(("portal", "tui"))
and not m.get("ignored")] and not m.get("ignored")]
if not inbox: if not inbox:
return None # некому: чат ещё не известен return None # некому: чат ещё не известен