Skip to content

Proposal: ChatSummary message preview for catalog rendering without subscribing #273

Description

@colbylwilliams

Summary

Add an optional, short, server-truncated last-message preview to the catalog representation of a chat (ChatSummary) so a client can render a conversation list — the newest line of each thread — without subscribing to every chat channel. This mirrors the existing precedent of ChangesSummary (#191) and the session-level inputNeeded aggregate (#265): surface a cheap rollup on the catalog so constrained clients avoid the heavy per-chat subscription.

Motivation

A session with many chats (sub-agent fan-out, parallel threads, an agent team) renders in a UI as a list, each row showing the thread's latest activity. Today ChatSummary carries title, status, activity?, and modifiedAt — but no content peek. To show "…the last thing said in this thread," a client must subscribe to the chat and pull its ChatState (the full turn history). For a list of N chats that is N subscriptions and N histories to render N preview lines.

The catalog is meant to be the cheap layer that lets a client decide what to drill into; a preview keeps it cheap. activity does not fill this role: it is the in-progress status verb ("Editing files…"), not message content, and it is absent when the chat is idle — which is exactly when a "last message" preview is most useful.

Why this belongs in AHP (not a per-host _meta convention)

The catalog/summary split is already an AHP concept, and AHP already places derived rollups on summaries for precisely this reason:

  • ChangesSummary on SessionSummary"a quick at-a-glance view of the session's footprint… without requiring the client to subscribe to a changeset."
  • AnnotationsSummary on SessionSummary — counts surfaced "so badge UI can render… without subscribing."
  • SessionState.inputNeeded (sessions: add session-level inputNeeded aggregate #265) — outstanding input surfaced on the session channel so a client can act "without subscribing to each chat."

A last-message preview is the same shape of thing and should be modeled the same, native way — not as a per-host metadata convention that every client has to special-case to render a basic chat list.

Proposed shape (illustrative — open to bikeshedding)

export interface ChatSummary {
  // ...existing fields...
  /**
   * Optional short preview of the most recent message/response in the chat,
   * for list rendering. Server-truncated; clients MUST treat it as lossy and
   * MUST NOT parse it for structure. Absent ⇒ no preview available (e.g. an
   * empty chat, or a host that chooses not to surface content into the catalog).
   */
  preview?: MessagePreview;
}

export interface MessagePreview {
  /** Who produced the previewed content. Reuses the MessageKind vocabulary. */
  role: MessageKind; // 'user' | 'agent' | 'tool' | 'systemNotification'
  /** Plain-text, server-truncated snippet (no markdown-structure guarantee). */
  text: string;
  /** Optional timestamp of the previewed message/response (ISO-8601). */
  at?: string;
}
  • Optional and additive — old peers ignore it.
  • Small by contract — it rides in session/chat lists and in root/sessionSummaryChanged-style notifications, the same place the existing "producers SHOULD keep the payload small" guidance already applies to SessionSummary._meta.
  • Reuses MessageKind rather than inventing a fresh role enum.
  • Privacy-aware — a preview lifts conversation content into the catalog/list layer. A host that treats the catalog as lower-trust than the chat channel MAY omit it; absence is always valid.

Relationship to existing / in-flight work

Open questions

  • Bubble to SessionSummary? Should a session's most-recently-active chat preview also surface on SessionSummary so a session list (not just a chat list within one session) can show a line? Likely yes, sourced from the session's default/most-recent chat.
  • Part kind. Include a tiny discriminator (text vs tool-result vs reasoning) so a UI can render 🔧 ran tests vs a text snippet, or keep it pure text for v1?
  • Length contract. Leave truncation length entirely to the host (recommended), or suggest a soft cap so list rows are visually consistent across hosts?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions