Skip to main content

Message

The Message class is the data model that AgentFlow uses to represent every message in a conversation — user inputs, assistant responses, tool calls, and tool results. It is defined in src/message.ts and exported from @10xscale/agentflow-client.


Import

import {
Message,
TextBlock,
ImageBlock,
AudioBlock,
VideoBlock,
DocumentBlock,
DataBlock,
ToolCallBlock,
RemoteToolCallBlock,
ToolResultBlock,
ReasoningBlock,
AnnotationBlock,
ErrorBlock,
MediaRef,
AnnotationRef,
TokenUsages,
} from '@10xscale/agentflow-client';

Message class

class Message {
message_id: string | null;
role: 'user' | 'assistant' | 'system' | 'tool';
content: ContentBlock[];
delta: boolean;
tools_calls?: Record<string, any>[];
timestamp: number;
metadata: Record<string, any>;
usages?: TokenUsages;
raw?: Record<string, any>;

constructor(
role: 'user' | 'assistant' | 'system' | 'tool',
content: ContentBlock[],
message_id?: string | null
);
}

Fields

FieldTypeDescription
message_idstring | nullServer-assigned message ID. Use "0" or null when creating new messages — the server assigns the real ID.
role'user' | 'assistant' | 'system' | 'tool'Who produced this message.
contentContentBlock[]Array of content blocks. A single message can have multiple blocks (e.g. text + image).
deltabooleantrue when this message is a streaming partial update rather than a final message. Set by the server.
tools_callsRecord<string, any>[]Raw tool call array from the underlying LLM response. Populated by the server; do not set this manually.
timestampnumberUnix timestamp in milliseconds when the message was created. Defaults to Date.now() at construction time.
metadataRecord<string, any>Arbitrary key-value metadata you can attach to messages.
usagesTokenUsagesToken usage information returned by the LLM. Only present on assistant messages.
rawRecord<string, any>The raw LLM response object, if the server passes it through. Useful for debugging.

Static factory methods

Message.text_message(content, role?, message_id?)

The most common way to create a message. Creates a Message with a single TextBlock.

const userMsg = Message.text_message('What is the capital of France?');
// role defaults to 'user'

const systemMsg = Message.text_message(
'You are a helpful geography tutor.',
'system'
);

Parameters:

ParameterTypeDefaultDescription
contentstringThe text content.
role'user' | 'assistant' | 'system' | 'tool''user'The role for the message.
message_idstring | nullnullOptional message ID. Use null to let the server assign one.

Message.tool_message(content, message_id?, meta?)

Creates a tool role message containing ToolResultBlock instances. Typically you do not create these manually — when you use remote tools the client creates them for you.

const toolResult = Message.tool_message([
new ToolResultBlock({
call_id: 'call_abc123',
output: { result: 42 },
status: 'completed',
is_error: false,
}),
]);

Message.withImage(text, imageUrl, role?)

Creates a user message containing a TextBlock followed by an ImageBlock. The image is referenced by URL, so pass either an http(s) URL the server can fetch or a data: URI with inline base64.

const msg = Message.withImage('Describe this', 'https://example.com/photo.jpg');
const inline = Message.withImage('Describe this', 'data:image/png;base64,iVBORw0...');

Parameters:

ParameterTypeDefaultDescription
textstringThe prompt that accompanies the image.
imageUrlstringURL or data: URI. Stored as MediaRef with kind: 'url'.
role'user' | 'assistant' | 'system''user'Role for the message. Note there is no 'tool' option here.

To attach an image you uploaded through client.uploadFile(), use Message.withFile() instead — it builds a file_id reference, which is what the server resolves at LLM-call time.

Message.withFile(text, fileId, mimeType?, role?)

Creates a message containing a TextBlock plus one media block referencing an uploaded file by id. The block type is picked from mimeType:

mimeType starts withBlock created
image/ImageBlock
audio/AudioBlock
video/VideoBlock
anything else, or omittedDocumentBlock
const upload = await client.uploadFile(pdfFile);
const msg = Message.withFile('Summarize this PDF', upload.data.file_id, 'application/pdf');

Parameters:

ParameterTypeDefaultDescription
textstringThe prompt that accompanies the file.
fileIdstringfile_id from client.uploadFile() (response.data.file_id).
mimeTypestringundefinedUsed to select the block type and stored on the MediaRef. Omitting it produces a DocumentBlock.
role'user' | 'assistant' | 'system''user'Role for the message.

Pass the real MIME type. Uploading a PNG and omitting mimeType produces a DocumentBlock, which is not what a vision model expects.

Message.multimodal(blocks, role?)

Creates a message from an arbitrary array of content blocks. Use it when the shape does not fit withImage or withFile: several images at once, an image between two pieces of text, or a mix of URL and file_id references.

const msg = Message.multimodal([
new TextBlock('Which of these two rooms is brighter?'),
new ImageBlock(new MediaRef('url', 'https://example.com/a.jpg')),
new ImageBlock(new MediaRef('url', 'https://example.com/b.jpg')),
]);

Parameters:

ParameterTypeDefaultDescription
blocksContentBlock[]Blocks in the order the model should see them.
role'user' | 'assistant' | 'system''user'Role for the message.

Instance methods

text()

Flattens the message's content down to a single string. TextBlock blocks contribute their text; ToolResultBlock blocks contribute their output (stringified with JSON.stringify when it is not already a string). Every other block type contributes an empty string, so an image-only message returns ''.

const answer = result.messages.at(-1)?.text();

This is the method to use when rendering a reply, including for streaming deltas.

attach_media(media, as_type)

Appends one media block to an existing message's content array, in place. Returns nothing.

const msg = Message.text_message('Compare these two charts');

msg.attach_media(new MediaRef('file_id', undefined, firstId, undefined, 'image/png'), 'image');
msg.attach_media(new MediaRef('file_id', undefined, secondId, undefined, 'image/png'), 'image');

Parameters:

ParameterTypeDescription
mediaMediaRefThe reference to attach.
as_type'image' | 'audio' | 'video' | 'document'Which block class to wrap it in. Any other value throws Unsupported media type: <value>.

The MediaRef constructor is positional — (kind, url, file_id, data_base64, mime_type, ...) — so a file_id reference needs undefined in the url slot. Assigning the fields by name is easier to read:

const media = new MediaRef('file_id');
media.file_id = upload.data.file_id;
media.mime_type = upload.data.mime_type;
msg.attach_media(media, 'image');

ContentBlock union type

The content field of a Message is an array of ContentBlock values. Each block has a type discriminant field. The full union is:

type ContentBlock =
| TextBlock
| ImageBlock
| AudioBlock
| VideoBlock
| DocumentBlock
| DataBlock
| ToolCallBlock
| RemoteToolCallBlock
| ToolResultBlock
| ReasoningBlock
| AnnotationBlock
| ErrorBlock;

Block types

TextBlock

Plain text content.

class TextBlock {
type: 'text';
text: string;
annotations: AnnotationRef[];
}
const block = new TextBlock('Hello, world!');
FieldTypeDescription
type'text'Discriminant. Always 'text'.
textstringThe text content.
annotationsAnnotationRef[]Optional citation or note annotations appended to this block.

ImageBlock

An image attached to a message.

class ImageBlock {
type: 'image';
media: MediaRef;
alt_text?: string;
bbox?: number[];
}
const block = new ImageBlock(
new MediaRef('url', 'https://example.com/photo.jpg'),
'A photo of a cat'
);
FieldTypeDescription
type'image'Discriminant.
mediaMediaRefReference to the image data.
alt_textstringAccessible alternative text.
bboxnumber[]Optional bounding box [x, y, w, h] within a parent image.

AudioBlock

An audio clip attached to a message.

class AudioBlock {
type: 'audio';
media: MediaRef;
transcript?: string;
sample_rate?: number;
channels?: number;
}
FieldTypeDescription
mediaMediaRefReference to the audio data.
transcriptstringOptional pre-computed transcript of the audio.
sample_ratenumberSample rate in Hz.
channelsnumberNumber of audio channels (1 = mono, 2 = stereo).

VideoBlock

A video clip attached to a message.

class VideoBlock {
type: 'video';
media: MediaRef;
thumbnail?: MediaRef;
}

DocumentBlock

A document (PDF, DOCX, etc.) attached to a message.

class DocumentBlock {
type: 'document';
media: MediaRef;
pages?: number[];
excerpt?: string;
}
FieldTypeDescription
pagesnumber[]Which pages of the document are relevant (1-indexed).
excerptstringOptional extracted text excerpt from the document.

DataBlock

Raw binary or structured data encoded as base64 or referenced via MediaRef.

class DataBlock {
type: 'data';
mime_type: string;
data_base64?: string;
media?: MediaRef;
}

ToolCallBlock

Represents a tool/function call made by the assistant. Populated by the server; you typically do not create these manually.

class ToolCallBlock {
type: 'tool_call';
id: string;
name: string;
args: Record<string, any>;
tool_type?: string;
}
FieldTypeDescription
idstringUnique ID for this tool call (used to match with ToolResultBlock.call_id).
namestringName of the tool to call.
argsRecord<string, any>Arguments to pass to the tool function.
tool_typestringOptional tool type tag (e.g. 'mcp', 'function').

RemoteToolCallBlock

Like ToolCallBlock but specifically for tools that execute on the client side (browser). When the server needs the client to run a tool, it returns a message containing RemoteToolCallBlock entries. The client's invoke() loop detects these, executes the registered handlers, and sends the results back.

class RemoteToolCallBlock {
type: 'remote_tool_call';
id: string;
name: string;
args: Record<string, any>;
tool_type: string; // always 'remote'
}

You do not create these manually. They arrive from the server inside invoke() responses.


ToolResultBlock

The result of executing a tool call. When using remote tools the client creates these automatically. You only need to construct them manually if you are building custom tool-loop logic.

class ToolResultBlock {
type: 'tool_result';
call_id: string;
output: any;
is_error: boolean;
status?: 'completed' | 'failed';
}

// Constructor takes an object:
new ToolResultBlock({
call_id: 'call_abc123',
output: { temperature: 22.5 },
status: 'completed',
is_error: false,
})
FieldTypeDescription
call_idstringMust match the id of the corresponding ToolCallBlock or RemoteToolCallBlock.
outputanyThe return value of the tool function. Can be any JSON-serialisable value.
is_errorbooleanSet to true when the tool execution failed.
status'completed' | 'failed'Status signal sent back to the server.

ReasoningBlock

Extended-thinking / chain-of-thought reasoning steps emitted by models that support it (e.g. Claude 3+).

class ReasoningBlock {
type: 'reasoning';
summary: string;
details?: string[];
}
FieldTypeDescription
summarystringShort summary of the reasoning step.
detailsstring[]Optional detailed reasoning steps.

AnnotationBlock

Citations or notes appended to message content.

class AnnotationBlock {
type: 'annotation';
kind: 'citation' | 'note';
refs: AnnotationRef[];
spans?: [number, number][];
}
FieldTypeDescription
kind'citation' | 'note'Whether this is a citation to a source or an inline note.
refsAnnotationRef[]List of sources or note references.
spans[number, number][]Character offset spans within TextBlock.text that this annotation covers.

ErrorBlock

Represents an error that occurred during graph execution, embedded in a message.

class ErrorBlock {
type: 'error';
message: string;
code?: string;
data?: Record<string, any>;
}

Helper types

MediaRef

A reference to a media file. Supports three kinds: an external URL, an uploaded file_id, or inline base64 data.

class MediaRef {
kind: 'url' | 'file_id' | 'data';
url?: string; // when kind = 'url'
file_id?: string; // when kind = 'file_id'
data_base64?: string; // when kind = 'data'
mime_type?: string;
size_bytes?: number;
sha256?: string;
filename?: string;
width?: number;
height?: number;
duration_ms?: number;
page?: number;
}

Create a URL reference:

const media = new MediaRef('url', 'https://example.com/image.png');
media.mime_type = 'image/png';

Create a file_id reference (after uploading):

const uploaded = await client.uploadFile(file);
const media = new MediaRef('file_id');
media.file_id = uploaded.data.file_id;
media.mime_type = uploaded.data.mime_type;

AnnotationRef

A reference to a source or note target.

class AnnotationRef {
url?: string;
file_id?: string;
page?: number;
index?: number;
title?: string;
}

TokenUsages

LLM token usage statistics attached to assistant messages.

class TokenUsages {
completion_tokens: number;
prompt_tokens: number;
total_tokens: number;
reasoning_tokens: number;
cache_creation_input_tokens: number;
cache_read_input_tokens: number;
image_tokens?: number;
audio_tokens?: number;
}

Complete example

import {
Message,
TextBlock,
ImageBlock,
MediaRef,
} from '@10xscale/agentflow-client';

// 1. Simple text message
const greeting = Message.text_message('Hello!');

// 2. Message with mixed text and image
const multimodal = new Message('user', [
new TextBlock('What is in this image?'),
new ImageBlock(
new MediaRef('url', 'https://example.com/photo.jpg'),
'A street photo'
),
]);

// 3. System prompt
const system = Message.text_message(
'You are a helpful assistant. Be concise.',
'system'
);

// 4. Send them to the agent
const result = await client.invoke([system, greeting]);
console.log(result.messages);

What you learned

  • Message has four roles: user, assistant, system, and tool.
  • content is always an array of typed ContentBlock objects.
  • Message.text_message() is the easiest way to create a plain text message.
  • RemoteToolCallBlock arrives from the server when a remote tool needs executing — the invoke() loop handles this automatically.
  • Use MediaRef with kind: 'file_id' to reference files you have uploaded via uploadFile().
  • Message.withImage(), Message.withFile(), and Message.multimodal() build multimodal messages without hand-assembling blocks; attach_media() adds one to a message you already have.
  • message.text() flattens content blocks to a string and is what you render for both final messages and streaming deltas.

Next step

See how-to/client/send-images-and-documents for the end-to-end upload-and-send flow, or reference/client/invoke to learn how to send messages and receive responses.