Skip to content

bug(cli-sessions): tool calls and tool results are not rendered when opening a CLI session — get_cli_session_messages() drops the tool_calls / tool_call_id / tool_name columns #1772

@nesquena-hermes

Description

@nesquena-hermes

Bug Description

Reporter: Cygnus (Discord; please ping in replies) via the WebUI testers thread.

Cygnus is now seeing CLI sessions appear inline in her sidebar (this is the recent feature shipping correctly), but when she opens one she sees only the user/assistant prose — none of the tool calls that the agent actually made during the CLI run are rendered. For short tool-heavy CLI sessions this leaves the transcript almost empty.

Ooo, finally experiencing the CLI Sessions opening in new *chats. Neat...

  • I remember an update about some way to filter them out - where do I find that?
  • How do I make CLI tools show everything they're doing? I'm not seeing their tool calls. Since they're so short, I'd like to see everything
  • I feel like this is something another sidebar would be excellent for, maybe
    (May 7 2026, 01:32 UTC)

Root cause — get_cli_session_messages() strips tool-call columns

api/models.py:1621 get_cli_session_messages() reads CLI session content from state.db.messages with this query:

cur.execute("""
    SELECT role, content, timestamp
    FROM messages
    WHERE session_id = ?
    ORDER BY timestamp ASC
""", (sid,))
msgs = []
for row in cur.fetchall():
    msgs.append({
        'role': row['role'],
        'content': row['content'],
        'timestamp': row['timestamp'],
    })

But the actual state.db.messages schema (verified live on this host) has all the metadata we need:

0   id                    INTEGER
1   session_id            TEXT NOT NULL
2   role                  TEXT NOT NULL
3   content               TEXT
4   tool_call_id          TEXT
5   tool_calls            TEXT     ← assistant message's tool_calls JSON, dropped today
6   tool_name             TEXT     ← tool-result row's tool name, dropped today
7   timestamp             REAL NOT NULL
8   token_count           INTEGER
9   finish_reason         TEXT
10  reasoning             TEXT
11  reasoning_details     TEXT
12  codex_reasoning_items TEXT
13  reasoning_content     TEXT
14  codex_message_items   TEXT

The tool_calls column on assistant rows holds the JSON of every tool call the agent made for that turn; the tool_call_id + tool_name + content columns on role='tool' rows hold the result. Both halves are dropped on the floor by the current SELECT.

I verified live on this host's state.db:

('assistant', 110)   # text-bearing assistant reply
('assistant', 0)     # text-empty assistant — these are pure tool_calls turns!
('assistant', 0)
('tool',   '{"success": true, "name": "hermes-agent-setup", ...}')
('tool',   '{"success": false, "error": "Skill ... not found"}')

Two of those five recent assistant rows have zero text content — they're tool-only turns whose entire payload lives in the tool_calls column the loader doesn't pull. The corresponding role='tool' rows that hold the results are also being read but their tool_name / tool_call_id are stripped, so the WebUI's tool-card renderer (static/messages.js near tool_calls handling) has nothing to attach them to even if they reached the client.

Suggested fix

  1. Extend the SELECT to pull every column the WebUI's tool-call renderer expects. Add tool_call_id, tool_calls, tool_name (at minimum). Optionally also pull reasoning / reasoning_content / reasoning_details so CLI sessions that use Anthropic's extended thinking show their reasoning blocks too.

  2. Pass them through into the per-message dicts. The frontend already understands tool_calls metadata on assistant messages (see static/sessions.js:968-973 hasMessageToolMetadata check). For role='tool' rows, the existing tool-result rendering path should pick them up once the fields arrive.

  3. Bump the version stamp on import_cli_session() so re-imports rebuild with the richer metadata. Otherwise a previously-imported CLI session stays empty even after the loader is fixed.

A defensive companion: when role='assistant' AND content is empty AND tool_calls is empty too (a malformed row from an interrupted CLI turn), don't render the empty bubble — currently we'd render a blank assistant message.

Severity

M2 — visible data loss for any user opening a CLI/external session in the WebUI. Doesn't affect WebUI-native sessions (those have full metadata in their JSON files). Cygnus's framing — "since they're so short, I'd like to see everything" — is exactly the use case this is broken for: short tool-heavy quick CLI invocations are exactly the ones where text content is minimal and tool calls ARE the content.

Reporter

@CygnusIgnis via WebUI Discord testers thread, May 7 2026.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions