Skip to content

[Feature]: Improve session derived titles to show conversation core content, not just first user message #81781

Description

@zhuwz90

title: "[Feature]: Improve session derived titles to show conversation core content, not just first user message"
labels: ["enhancement"]

Summary

Improve session derivedTitle generation to use compaction summaries and/or last meaningful user message as the primary title source, instead of only the first user message, so the session picker shows what the conversation is actually about.

Problem to solve

When switching sessions via /session in TUI or Control UI, the session picker shows titles that often don't reflect the actual content of the conversation. This is because the deriveSessionTitle function (session-utils.ts) currently uses, in order:

  1. entry.displayName (usually empty for DM sessions)
  2. entry.subject (usually empty for DM sessions)
  3. First user message from transcript (truncated to 60 chars)
  4. sessionId[0:8] + date (format like df33e0c5 (2026-05-14) — not helpful)

The first user message is often just "Hi", "在吗", "/help", or a simple greeting — it rarely captures the core topic of the session. For users with many sessions (especially active Bot users across QQ, Telegram, etc.), the session picker becomes a wall of cryptic prefixes and greeting messages, making it impossible to quickly find a relevant conversation.

Affected users: All OpenClaw users who use session switching (TUI, Control UI, CLI)
Severity: Medium — annoying workflow friction
Frequency: Every time a user has multiple active sessions and needs to switch
Consequence: Extra mental overhead, wrong session selected, time wasted scrolling through unhelpful titles

Proposed solution

Enhance deriveSessionTitle() with two additional, higher-quality title sources:

Priority order (new)

  1. entry.displayName (unchanged — group/channel metadata)
  2. entry.subject (unchanged)
  3. Compaction summary (NEW) — If the session has gone through compaction, the compaction checkpoint stores a meaningful summary of the conversation. Use that as the title (truncated to 60 chars).
  4. Last meaningful user message (IMPROVED) — Instead of only the first user message, try:
    • First, check if the first user message has meaningful content (e.g., not just greetings, short commands, or single words)
    • If not, fall back to reading recent user messages (last message or second message) from the transcript tail
  5. sessionId[0:8] + date (unchanged — final fallback)

Implementation notes

The existing code has most of the infrastructure:

  • deriveSessionTitle() in src/session/session-utils.ts
  • readSessionTitleFieldsFromTranscript() reads the first few lines of the transcript JSONL for the firstUserMessage
  • The TUI already uses session.derivedTitle ?? session.displayName with includeDerivedTitles: true

What needs to change:

A) Compaction summary as title (recommended path):

  • sessions.json entries already have a compactionCheckpoints array
  • Each checkpoint has a checkpointId referencing the compaction summary in the transcript
  • Read the compaction summary entry from the transcript and extract a meaningful title (e.g., first sentence, or the summary itself truncated to 60 chars)
  • This gives the best quality title because compaction already summarizes the entire conversation

B) Better message selection:

  • Add a heuristic to skip first user messages that are likely meaningless (single word, under 5 chars, known greeting patterns like "hi", "hello", "hey", "在吗", "/session", "/help", etc.)
  • Fall back to scanning the transcript for the first multi-word user message with substantial content

C) The TUI label format is already good:

{title} ({sessionKey})

With compact title + preview, this works well once titles are meaningful.

Alternatives considered

  1. LLM-generated session titles — On session reset or idle, run a one-shot LLM call to generate a title and store it in sessions.json. This would give the best quality but adds cost and latency.
  2. Manual title setting — Add a /rename slash command or session patch API. Useful but requires user action; doesn't solve the default case.
  3. Last message preview — Already available via lastMessagePreview in the session list (used in TUI description line). This is a good complement but not a replacement for the title.
  4. Just increase derived title length — Would still show meaningless first messages, just longer.

The compaction summary approach has the best quality/cost ratio since compaction already runs on long sessions.

Evidence/examples

Current behavior in TUI session picker (pseudocode):

sessions list → derivedTitle = "在吗" (first user message)
               → display = "在吗 (agent:main:main)"
               → Not helpful — this doesn't tell me what we talked about

sessions list → derivedTitle = "df33e0c5 (14 hours ago)" (fallback when first message absent)
               → display = "df33e0c5 (14 hours ago) (agent:main:main)"
               → Completely opaque

Desired behavior:

sessions list → derivedTitle = "讨论 SNMP 部署包的打包流程" (compaction summary)
               → display = "讨论 SNMP 部署包的打包流程 (agent:main:main)"
               → User immediately knows what this session was about

Relevant code locations (based on source analysis):

Function File Purpose
deriveSessionTitle() src/config/session-utils.ts Core title derivation
readSessionTitleFieldsFromTranscript() src/config/session-utils.ts Reads first message from transcript
extractFirstUserMessageFromTranscriptChunk() src/config/session-utils.ts Parses first user message
Compaction checkpoint src/config/session-types.ts compactionCheckpoints in session entry
TUI session picker rendering src/tui/tui.ts session.derivedTitle ?? session.displayName

Additional information

  • The sessions_list tool already has includeDerivedTitles and includeLastMessage support
  • The TUI picker already shows a description line with lastMessagePreview and timeAgo — the title just needs to be more useful
  • Compatibility: Backward-compatible — old sessions without compaction would still get first message or fallback behavior
  • This does NOT require changing the session store schema; derivedTitle is computed dynamically at list time

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low-priority cleanup, docs, polish, ergonomics, or speculative work.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions