Skip to content

Improve forum topic session routing and add interactive /history pager#6

Merged
Headcrab merged 6 commits into
Headcrab:masterfrom
DeadMorose777:pr/forum-routing-history
Apr 9, 2026
Merged

Improve forum topic session routing and add interactive /history pager#6
Headcrab merged 6 commits into
Headcrab:masterfrom
DeadMorose777:pr/forum-routing-history

Conversation

@DeadMorose777
Copy link
Copy Markdown
Contributor

Что меняется

PR улучшает поведение Telecodex в Telegram forum mode и добавляет удобный просмотр истории выбранной Codex-сессии.

Основные изменения

  • /status больше не пробрасывается в Codex CLI и обрабатывается самим Telecodex
  • исправлено поведение forum sync, из-за которого ручной выбор сессии через /use мог тихо затираться
  • сохранение fresh-session режима через /new и /clear больше не перетирается фоновым sync
  • /copy теперь соответствует реально выбранной Codex-сессии
  • /sessions в root dashboard больше не создаёт ощущение самопроизвольного переключения
  • добавлена команда /history с интерактивным pager:
    • показывает сообщения только текущей выбранной Codex-сессии
    • листает итоговые assistant-сообщения
    • открывается с самого нового сообщения
    • навигация циклическая, по кругу
    • обновляет одно и то же Telegram-сообщение через edit

Зачем

В forum dashboard были странности с routing/UX:

  • после /use выбранная сессия могла незаметно переключиться обратно
  • /status местами уходил в сам Codex вместо bot-layer
  • /copy мог показывать ответ не из той сессии
  • /sessions в dashboard и work topic вёл себя слишком неоднозначно

Этот PR делает поведение topic-сессий предсказуемее и добавляет нормальный способ смотреть историю выбранного thread прямо из Telegram.

Проверка

Проверено локально:

  • cargo test -> все тесты проходят
  • ручной сценарий в Telegram:
    • выбор Codex thread через /sessions / /use
    • повторная проверка через /status
    • /copy из выбранной сессии
    • /history с кнопками Prev / Next

Ограничения

  • /sessions по-прежнему контекстная команда:
    • в root dashboard показывает Telegram topic sessions
    • в work topic показывает Codex sessions для текущего cwd
  • /history сейчас показывает только итоговые assistant-сообщения, а не полный transcript

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

Walkthrough

This pull request introduces interactive session history browsing (/history command) and session status display (/status command) for a Telegram-Codex bridge bot. It refactors session context resolution, adds history pagination with wraparound controls, and updates command routing to distinguish between dashboard-root and work-topic contexts.

Changes

Cohort / File(s) Summary
Documentation
README.md, README.ru.md
Updated command documentation to describe /history (interactive assistant message browsing) and /status (session info display), clarified /sessions behavior based on forum context (dashboard root vs. work topic), and revised unauthenticated behavior guidance.
Command Parsing & Routing
src/commands.rs
Added BridgeCommand::History and Status variants, removed /status from forwarded commands (now bridge commands), and added corresponding help text and bot command entries.
Core Application Logic
src/app.rs
Introduced ensure_resolved_session() method, added history callback handling with modulo index wrapping, implemented History and Status command handlers with dashboard-root context detection, and added history rendering helpers (render_history_page, render_stale_history_page).
Session & Environment Binding
src/app/forum.rs
Added environment_sync_thread_binding() helper to conditionally bind Codex threads only when session lacks existing binding and fresh-thread override is false, preventing unintended thread rebinding.
Message Editing & I/O
src/app/io.rs
Added edit_markdown_message() method with fallback HTML-escaped retry logic, handles "message not modified" and rate-limit errors gracefully, and propagates optional inline keyboards to edit requests.
Presentation & Formatting
src/app/presentation.rs
Added history callback parsing (parse_history_callback_data), history pagination keyboard (history_keyboard with wraparound), updated chat_sessions_keyboard to support dashboard-root URL navigation, and added formatting helpers for session status and paginated history entries.
Session Context Filtering
src/app/support.rs
Updated command_uses_session_context to exclude Environments, Sessions, and Stop commands from session-context requirement.
Codex History Filtering
src/codex_history.rs
Modified history_entry_from_response_payload to skip non-final assistant messages (filtering by phase == "final_answer"), and added role/type matching for content blocks (user entries require input_text, assistant entries require output_text).
Store & Data Updates
src/store.rs, src/telegram.rs, src/app/turns.rs
Updated set_last_assistant_text to accept Option<&str> (enabling NULL values), added optional reply_markup field to EditMessageText, and updated turn finalization to pass Some(&text) instead of raw reference.
Test Coverage
src/app/tests.rs
Added extensive tests for forum sync behavior, history pagination, callback matching, stale-history detection, and command context filtering; updated existing tests for new command variants.

Sequence Diagram(s)

sequenceDiagram
    participant User as Telegram User
    participant Bot as Telecodex Bot
    participant Store as Session Store
    participant Codex as Codex API

    User->>Bot: /history (in work topic)
    Bot->>Store: ensure_resolved_session(user)
    Store-->>Bot: SessionRecord with codex_thread_id
    Bot->>Codex: fetch history from thread
    Codex-->>Bot: CodexHistoryEntry list
    Bot->>Bot: render_history_page(entries, index=0)
    Bot->>User: Display page 0 with prev/next buttons
    
    User->>Bot: Click next button (callback: his:thread_id:1)
    Bot->>Bot: parse_history_callback_data()
    Bot->>Store: verify current codex_thread_id
    Store-->>Bot: SessionRecord
    Bot->>Bot: validate callback thread matches session
    alt Thread mismatch detected
        Bot->>Bot: render_stale_history_page()
        Bot->>User: Display stale history warning
    else Thread matches
        Bot->>Codex: fetch history again
        Codex-->>Bot: CodexHistoryEntry list
        Bot->>Bot: render_history_page(entries, index=1)
        Bot->>User: Display page 1 with pagination
    end
Loading
sequenceDiagram
    participant User as Telegram User
    participant Bot as Telecodex Bot
    participant Store as Session Store

    User->>Bot: /status (in work topic)
    Bot->>Store: ensure_resolved_session(user)
    Store-->>Bot: SessionRecord
    Bot->>Bot: format_session_status(session)
    Bot->>User: Display Telegram session, Codex thread, model, settings
    
    User->>Bot: /status (in dashboard root)
    Bot->>Bot: Detect dashboard_root context
    Bot->>User: Return instructive message: use /status in work topic
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 A history most neat,
Pages turning, line by line,
Assistant words we greet—
Pagination divine!
Status, history aligned.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: improving forum topic session routing and adding an interactive /history pager feature.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the motivation, main changes, verification steps, and limitations of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/app.rs (1)

1428-1455: Avoid reloading the full thread on every pager click.

render_history_page() rereads the entire history with usize::MAX and rebuilds the assistant page list for each callback. On long Codex threads, Prev/Next will degrade into repeated full disk scans and slow message edits. A small cache keyed by (codex_thread_id, message_id) or a bounded precomputed page list would keep navigation responsive.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app.rs` around lines 1428 - 1455, render_history_page currently calls
read_thread_history(..., usize::MAX) and assistant_history_pages on every pager
callback which causes repeated full-disk scans; change it to look up a
short-lived/bounded in-memory cache (keyed by codex_thread_id and message_id or
codex_thread_id and initial render timestamp) and only reload from
read_thread_history when the cache miss occurs or the cached entry is
stale/evicted; populate the cache when you first render the history page (in the
same function or its caller) with the precomputed pages (the
assistant_history_pages result) and have subsequent calls to render_history_page
read pages from that cache and use history_keyboard/edit/send functions as
before, ensuring the cache is bounded (LRU or size limit) and entries expire to
avoid unbounded memory growth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/presentation.rs`:
- Around line 761-794: The code currently shows a "codex session title" even
when session.codex_thread_id is None because current_session_label() falls back
to the Telegram topic; update format_session_status so codex_title is derived
only when a Codex thread is actually bound: check session.codex_thread_id (e.g.,
via session.codex_thread_id.is_some() or matching the Option) and set
codex_title = escape_markdown_label(&current_session_label(...)) only when
present, otherwise set an explicit marker like "unbound" or "fresh" (escaped),
and leave the rest of the formatting (codex_thread variable/fields) unchanged so
the status accurately reflects an unbound session.

---

Nitpick comments:
In `@src/app.rs`:
- Around line 1428-1455: render_history_page currently calls
read_thread_history(..., usize::MAX) and assistant_history_pages on every pager
callback which causes repeated full-disk scans; change it to look up a
short-lived/bounded in-memory cache (keyed by codex_thread_id and message_id or
codex_thread_id and initial render timestamp) and only reload from
read_thread_history when the cache miss occurs or the cached entry is
stale/evicted; populate the cache when you first render the history page (in the
same function or its caller) with the precomputed pages (the
assistant_history_pages result) and have subsequent calls to render_history_page
read pages from that cache and use history_keyboard/edit/send functions as
before, ensuring the cache is bounded (LRU or size limit) and entries expire to
avoid unbounded memory growth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 133da092-44ae-48fc-81e1-2f656e8afea4

📥 Commits

Reviewing files that changed from the base of the PR and between 1c940d8 and 36a0906.

📒 Files selected for processing (13)
  • README.md
  • README.ru.md
  • src/app.rs
  • src/app/forum.rs
  • src/app/io.rs
  • src/app/presentation.rs
  • src/app/support.rs
  • src/app/tests.rs
  • src/app/turns.rs
  • src/codex_history.rs
  • src/commands.rs
  • src/store.rs
  • src/telegram.rs
💤 Files with no reviewable changes (1)
  • src/app/support.rs

Comment thread src/app/presentation.rs
Comment on lines +761 to +794
pub(super) fn format_session_status(
session: &crate::models::SessionRecord,
chat: &crate::telegram::Chat,
) -> String {
let telegram_title = escape_markdown_label(&session_title_label(session, chat));
let codex_title = escape_markdown_label(&current_session_label(session, chat));
let state = if session.busy { "busy" } else { "idle" };
let codex_thread = session
.codex_thread_id
.as_deref()
.map(short_codex_thread_id)
.unwrap_or_else(|| "new".to_string());
let model = session.model.as_deref().unwrap_or("default");
let reasoning = session.reasoning_effort.as_deref().unwrap_or("default");
let prompt = if session
.session_prompt
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
.is_some()
{
"set"
} else {
"none"
};

format!(
"**Current Telegram session:** {telegram_title}\n- codex session title: {codex_title}\n- state: `{state}`\n- cwd: `{}`\n- codex thread: `{}`\n- model: `{model}`\n- reasoning: `{reasoning}`\n- approval: `{}`\n- sandbox: `{}`\n- search: `{}`\n- prompt: `{prompt}`",
session.cwd.display(),
codex_thread,
session.approval_policy,
session.sandbox_mode,
session.search_mode.as_codex_value(),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Don't label an unbound topic as a Codex session.

When session.codex_thread_id is None, current_session_label() falls back to the Telegram topic title, so /status can report a "codex session title" even immediately after /new or /clear. Show an explicit fresh/unbound state here, or omit that field until a Codex thread is selected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/presentation.rs` around lines 761 - 794, The code currently shows a
"codex session title" even when session.codex_thread_id is None because
current_session_label() falls back to the Telegram topic; update
format_session_status so codex_title is derived only when a Codex thread is
actually bound: check session.codex_thread_id (e.g., via
session.codex_thread_id.is_some() or matching the Option) and set codex_title =
escape_markdown_label(&current_session_label(...)) only when present, otherwise
set an explicit marker like "unbound" or "fresh" (escaped), and leave the rest
of the formatting (codex_thread variable/fields) unchanged so the status
accurately reflects an unbound session.

@Headcrab Headcrab merged commit 81898ba into Headcrab:master Apr 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants