Message Types
messages.body parts schema, message type conventions, relations, and blobs.
Conversation messages store structure in columns and relations, not envelope metadata. The runtime normalizes body on write — only parts and whitelisted meta keys are kept.
messages row
messages
├── type string column (message, reply, reaction, system, edit, …)
├── author_participant_id
├── body jsonb
├── branch_id soft fork only
└── created_atAuthor is always author_participant_id. Message kind is type.
body shape
{
"parts": [
{ "type": "text", "text": "hello", "format": "plain" }
],
"meta": {
"clientMsgId": "optional-dedup-id",
"sourceConversationId": "forum-id-for-egress-routing",
"sourceMessageId": "original-forum-message-id",
"sourceActorId": "forum-author-actor-id"
}
}Whitelisted meta keys: clientMsgId, sourceConversationId, sourceMessageId, sourceActorId, plus agent-specific keys like followUp and guardianInject.
Part types (v1)
type | Fields |
|---|---|
text | text, format (plain | mrkdwn | html) |
image | blobId, alt? |
audio | blobId, transcript? |
video | blobId, caption?, thumbBlobId? |
document | blobId, filename?, caption? |
reasoning | text |
refusal | text |
tool_call | id, name, input |
tool_result | toolCallId, content, isError? |
Media parts reference blobs.id — no inline base64 in jsonb.
Message type conventions
type | Use |
|---|---|
message | Normal line — agent LLM output, tool traffic, forum traffic |
reply | Thread/reply semantics — not the user-visible signal for agent output |
reaction | Emoji reaction message |
system | Join/leave notifications |
edit | Replacement content for earlier line |
Tool traffic in monologues uses type: message with tool_call / tool_result parts.
Agent LLM outputs are persisted as type: message on the monologue. User visibility is determined by egress: if the message is agent-authored and contains text parts, egress forwards that text to the forum.
message_relations
Post via conversation_post_message with optional relations:
await ctx.conversations.postMessage(conversationId, {
parts: [{ type: "text", text: "copied", format: "plain" }],
}, {
type: "message",
relations: [{ relationType: "mirror", targetMessageId: sourceId }],
});Threads are chains of reply relations — no parent column on messages.
blobs
Register metadata, upload bytes out of band (S3):
POST /v1/tenants/:tenant_id/blobs
Content-Type: image/png
Body: <bytes>Requires YOKE_STORAGE=s3 and YOKE_S3_BUCKET.
Three-actor pipeline (message flow)
Forum: human posts (type: message)
Ingestion: monologue copy (type: message, meta.source*)
Processor: tool_call + tool_result + agent text (type: message)
Egress: agent text → forum (type: message) — decides user visibility
Bridge: agent text on forum → chat platform (Slack, North, …)Egress listens to all monologue conversation.message.posted events. It forwards agent-authored messages that contain text parts — not tool_call-only or tool_result traffic. See Egress.
The processor and inference loop do not decide what reaches the user. They persist monologue messages; egress reads the stream and copies text to the forum.
Inference alignment
InferenceMessage uses the same body.parts schema. Processor converts transcript rows to inference messages before ctx.llm.infer. See Inference.