Skip to content

Chat Spec

The chat layer defines the message exchange and agent metadata used by both the protocol-level wire stream and the UI's ChatMessage[] state.

ChatMessage

Field Type Required Notes
id string yes Stable identity across re-renders
role 'user' \| 'assistant' \| 'tool' \| 'system' yes UI-rendered role
content string yes Plain-text body (UI may render as Markdown)
createdAt string (ISO 8601) yes UTC timestamp
chatId string no Owning chat
agentId string no Owning agent (multi-agent mode)
toolCalls ToolCall[] no Calls emitted by the assistant
toolResult ToolResult no Result attached inline (or use a separate tool row)
updatedAt string (ISO 8601) no Last edit time

additionalProperties is enabled. UI consumers (e.g. deeppath/apps/web) layer additional render-only fields on top via intersection types — those don't go on the wire.

ChatAgent

Field Type Required Notes
id string yes
name string yes Display name
createdAt string yes
updatedAt string yes
rolePrompt string no System-level role prompt
forbiddenPrompt string no Negative constraints
skillIds string[] no Tool / skill allowlist
allowExternalSkills boolean no Permit any registered skill
isBuiltin boolean no Hidden from user-facing agent picker
isArchived boolean no
sortOrder number no
icon string no Emoji or icon URL
color string no Hex color for the agent badge

The schema is intentionally broad — products layer their own routing, billing, and access control on top via the metadata escape hatch (open for forward-compatibility).

Conversational ownership

Every ChatMessage belongs to at most one ChatAgent. In a multi-agent orchestration run, the agent SSE event flips the "currently speaking" agent identity, and downstream content events inherit that ownership until the next agent event.

Variants (UI extension)

The framework's protocol intentionally does not define activeVariantId / variantsCount — those are UI-layer concepts (ChatGPT-style "regenerate" branches) carried in @steerable/agent-ui's extended message type. They live in your ChatMessage extension, not on the wire.

Example exchange

[
  {"id":"m1","role":"user","content":"Find me a Mars mission timeline","createdAt":"2026-05-14T10:00:00Z"},
  {"id":"m2","role":"assistant","agentId":"researcher","content":"Sure — searching…","createdAt":"2026-05-14T10:00:01Z",
   "toolCalls":[{"id":"c1","name":"web_search","arguments":{"q":"Mars mission timeline"}}],
   "toolResult":{"success":true,"data":{"results":[]}}},
  {"id":"m3","role":"assistant","agentId":"researcher","content":"Here's a 2024-2030 plan: …","createdAt":"2026-05-14T10:00:05Z"}
]