Skip to content

TUI /sessions list: gateway-injected inbound metadata block used as session title (shows 'untrusted metadata' for all sessions) #39722

Description

@bryanbaer

Summary

Every session in the TUI /sessions list displays Sender (untrusted metadata):… as its title (truncated by the TUI column width to show untrusted metadata). Affects all sessions routed through the gateway client.

Version

2026.3.2

Root cause (two bugs)

Bug 1 — extractFirstUserMessageFromTranscriptChunk does not strip the injected inbound metadata block

The gateway prepends an inbound context block to every user message before it is written to the transcript:

Sender (untrusted metadata):
```json
{
  "label": "openclaw-tui (gateway-client)",
  "id": "gateway-client",
  "name": "openclaw-tui",
  "username": "openclaw-tui"
}

[Sun 2026-03-08 06:03 EDT] actual user message text here


`extractTextFromContent` returns this block verbatim (it only strips `[[...]] ` inline directive tags, not the metadata envelope). The result becomes `firstUserMessage`, which `deriveSessionTitle` then truncates to 60 chars:

`Sender (untrusted metadata): ```json { "label": "openclaw-t…`

In the TUI's narrow column the visible slice is `untrusted metadata`.

**Affected path:**
`session-utils-CJILahHK.js` → `readSessionTitleFieldsFromTranscript` → `extractFirstUserMessageFromTranscriptChunk` → `extractTextFromContent` → `stripInlineDirectiveTagsForDisplay` (does not strip inbound metadata block)

### Bug 2 — `deriveSessionTitle` checks `entry.displayName` but the displayName that IS set lives on the outer session record

```js
// session-utils-CJILahHK.js
function deriveSessionTitle(entry, firstUserMessage) {
  if (!entry) return;
  if (entry.displayName?.trim()) return entry.displayName.trim();  // ← guard
  ...
  if (firstUserMessage?.trim()) return truncateTitle(...);          // ← falls through here
}

entry is the inner session file metadata. The displayName value ("openclaw-tui") is on the outer session record (rest.displayName in listSessionsFromStore), not on entry. The guard never fires, so every gateway session falls through to firstUserMessage.

Evidence: sessions_list API returns "displayName": "openclaw-tui" correctly on the outer record; entry.displayName is undefined.

Reproduction

  1. Connect any gateway client (e.g. openclaw-tui) and send one message
  2. Open /sessions in the TUI
  3. Every session shows Sender (untrusted metadata):… as title

Expected behaviour

Session title should show:

  1. entry.displayName or outer record displayName (whichever is populated) — "openclaw-tui" in this case
  2. Fallback to subject, then a stripped/sanitised excerpt of the actual user message (with the inbound metadata envelope removed)

Suggested fixes

Fix 1 (Bug 2 — quick): In deriveSessionTitle, also accept the outer record's displayName as a parameter or merge it into entry before the call.

Fix 2 (Bug 1 — correct): In extractFirstUserMessageFromTranscriptChunk / extractTextFromContent, strip the inbound metadata envelope pattern before returning the title candidate. The envelope is consistently formatted:

Sender (untrusted metadata):\n```json\n{...}\n```\n\n

Strip everything up to and including the closing fence + blank line, then take the remainder as the message text.

Fix 3 (defence-in-depth): DERIVED_TITLE_MAX_LEN is 60 chars. Even after the above fixes, the title should prefer displayName / subject over raw message content for gateway sessions where those fields are populated.

Additional note: session name length in TUI

Even with a correct title, 60 chars is too wide for the TUI's session column in typical terminal widths. The formatted label is ${title} (${formattedKey}) which pushes total visible width well past a standard 80-col pane. Consider a shorter DERIVED_TITLE_MAX_LEN (e.g. 32–40) or truncation at render time based on actual column width.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.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.impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.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.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions