Skip to content

Message Parts ​

Every message — user or agent, streaming or final, WebSocket or HTTP — carries a parts: BridlePart[] array alongside the plain text shorthand. Parts are how Bridle handles rich content (images, files) without losing the simplicity of text.

Part types ​

ts
enum BridlePartTypes {
  Text     = 'text',
  Image    = 'image',
  File     = 'file',
  Ui       = 'ui',          // agent → browser: interactive form
  UiSubmit = 'ui_submit',   // browser → agent: form answers
}

For the ui / ui_submit round-trip used to render interactive forms inside chat bubbles, see Protocol → Interactive UI.

Text ​

ts
{ type: 'text', text: 'Hello' }

The bread-and-butter part. Most messages are a single text part.

Image ​

ts
{
  type: 'image',
  base64: '/9j/4AAQSkZJ...',         // raw base64 (no data: prefix)
  mediaType: 'image/jpeg',           // MIME type
}

Inline base64 is used for two reasons:

  1. The image flows through the relay without extra storage.
  2. End-to-end privacy — the image bytes never touch a CDN.

Trade-off: large images bloat the WebSocket frame. For multi-megabyte attachments, use a file part with a URL instead.

File ​

ts
{
  type: 'file',
  url: 'https://your-cdn.example.com/files/doc.pdf',
  name: 'invoice.pdf',
  mimeType: 'application/pdf',       // optional but recommended
}

The agent (or your app) uploads the file to your storage of choice (S3, R2, etc.) and sends the URL. The browser renders it as a download link or, for image/* mime types, an inline preview.

How parts compose ​

A single message can carry any combination of parts:

ts
{
  text: 'Here is the result and the source data:',
  parts: [
    { type: 'text', text: 'Here is the result and the source data:' },
    { type: 'image', base64: '...', mediaType: 'image/png' },
    { type: 'file', url: 'https://.../report.pdf', name: 'report.pdf' },
  ],
}

The text field is the plain-text rollup of all text-typed parts, used for transcript persistence and for clients that don't render parts.

Building parts ​

The SDK ships a helper:

ts
import { buildParts } from '@cleanslice/bridle' // (planned export)

buildParts('Here is the image', [
  { base64: '...', mediaType: 'image/jpeg' },
])
// → [
//     { type: 'text',  text: 'Here is the image' },
//     { type: 'image', base64: '...', mediaType: 'image/jpeg' },
//   ]

The hub also accepts the legacy { text, images: [...] } shape and converts it to parts internally — you don't need to upgrade existing call sites in lockstep.

Wire flow ​

DirectionCarries parts?
Browser → Hub (message)yes
Hub → Agent (message)yes
Agent → Hub (message, stream, stream_end)yes
Hub → Browser (message, stream, stream_end)yes
HTTP POST /api/agent/:agentId/message[/sync]yes (body.parts)

If parts is omitted but text is present, the receiver synthesizes a single text part from the text. If both are omitted, the message is dropped.

Client rendering ​

The embedded SDK renders text parts as plain paragraphs (with whitespace preserved). Images render inline; files render as download links. For richer rendering (Markdown, code blocks, tool-call traces), use the headless client and render messages yourself.

Released under the MIT License.