> For the complete documentation index, see [llms.txt](https://www.opencodebook.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.opencodebook.xyz/en/chapter_04_session_system/4.2_message_model.md).

# 4.2 Message Model

> **Model**: claude-opus-4-6 (anthropic/claude-opus-4-6) **Generation Date**: 2025-02-17

***

A Message is the smallest unit of information exchange within a session. OpenCode's message model is elegantly designed, supporting rich content types through the Part system.

## 4.2.1 The `MessageV2` Namespace in Detail

The message system is defined in `session/message-v2.ts`. Messages are divided into two roles:

**User Messages**:

```typescript
export const User = Base.extend({
  role: z.literal("user"),
  time: z.object({ created: z.number() }),
  summary: z.object({           // Change summary (optional)
    title: z.string().optional(),
    body: z.string().optional(),
    diffs: Snapshot.FileDiff.array(),
  }).optional(),
  agent: z.string(),             // Name of the Agent being used
  model: z.object({              // Model being used
    providerID: z.string(),
    modelID: z.string(),
  }),
  system: z.string().optional(), // Additional system instructions
  tools: z.record(z.string(), z.boolean()).optional(), // Tool toggles (deprecated)
  variant: z.string().optional(), // Variant identifier
})
```

**Assistant Messages**:

```typescript
export const Assistant = Base.extend({
  role: z.literal("assistant"),
  time: z.object({
    created: z.number(),
    completed: z.number().optional(),  // Generation completion time
  }),
  error: z.discriminatedUnion("name", [
    AuthError.Schema,
    NamedError.Unknown.Schema,
    OutputLengthError.Schema,
    AbortedError.Schema,
    ContextOverflowError.Schema,
    APIError.Schema,
  ]).optional(),
  parentID: z.string(),          // ID of the corresponding User message
  modelID: z.string(),
  providerID: z.string(),
  agent: z.string(),
  cost: z.number(),              // Cost of this response (in USD)
  tokens: z.object({
    total: z.number().optional(),
    input: z.number(),           // Input token count
    output: z.number(),          // Output token count
    reasoning: z.number(),       // Chain-of-thought token count
    cache: z.object({
      read: z.number(),          // Cache read token count
      write: z.number(),         // Cache write token count
    }),
  }),
  summary: z.boolean().optional(), // Whether this is a compaction summary
  finish: z.string().optional(),   // Finish reason
})
```

**Token Statistics Field Breakdown**:

The `tokens` object records the token consumption for this LLM call:

* `input`: The number of input tokens sent to the model (including system prompt, message history, tool results, etc.)
* `output`: The number of output tokens generated by the model
* `reasoning`: The number of chain-of-thought tokens used by the model (only for models that support chain-of-thought, such as Claude's extended thinking)
* `cache.read`: The number of tokens read from the Prompt Cache
* `cache.write`: The number of tokens written to the Prompt Cache
* `total`: Total token count (some Providers return the total directly)

> **Extended Explanation: Prompt Cache**
>
> In multi-turn conversations, each request sends the full message history. The System Prompt and earlier messages are identical in every request. Prompt Cache is an optimization offered by LLM Providers -- it caches these repeated portions on the server side to avoid redundant computation.
>
> `cache.write` indicates how many tokens were cached for the first time in this request; `cache.read` indicates how many tokens hit the cache in this request. Cache-hit tokens are billed at a much lower rate than regular tokens, significantly reducing costs.

## 4.2.2 The Multimodal Part Type System

The actual content of a message is not stored directly on the Message object; instead, it is organized through the **Part** system. Each message can contain multiple Parts of different types. All Parts share a common base structure:

```typescript
const PartBase = z.object({
  id: z.string(),         // Unique Part identifier
  sessionID: z.string(),  // ID of the owning session
  messageID: z.string(),  // ID of the owning message
})
```

Complete Part types (12 types):

| Part Type          | Purpose           | Key Fields                                                   |
| ------------------ | ----------------- | ------------------------------------------------------------ |
| **TextPart**       | Text content      | `text`, `time`, `synthetic` (whether synthesized), `ignored` |
| **ReasoningPart**  | Chain of thought  | `text`, `time.start`, `time.end`, `metadata`                 |
| **ToolPart**       | Tool invocation   | `tool`, `callID`, `state` (four states)                      |
| **FilePart**       | File attachment   | `mime`, `filename`, `url`, `source`                          |
| **AgentPart**      | Agent reference   | `name`, `source`                                             |
| **SubtaskPart**    | Subtask           | `prompt`, `description`, `agent`, `model`                    |
| **CompactionPart** | Compaction marker | `auto` (whether auto-triggered)                              |
| **StepStartPart**  | Step start        | `snapshot`                                                   |
| **StepFinishPart** | Step finish       | `reason`, `snapshot`, `cost`, `tokens`                       |
| **SnapshotPart**   | Snapshot          | `snapshot`                                                   |
| **PatchPart**      | Patch             | `hash`, `files`                                              |
| **RetryPart**      | Retry marker      | `attempt`, `error`, `time`                                   |

**The Four States of ToolPart**:

ToolPart is the most complex Part type, tracking the full lifecycle of a tool invocation via its `state` field:

```
pending ---> running ---> completed
                    +---> error
```

```typescript
// 1. Pending: Tool call identified, arguments still being received
ToolStatePending = { status: "pending", input: {}, raw: "" }

// 2. Running: Arguments complete, execution begins
ToolStateRunning = { status: "running", input: {...}, time: { start } }

// 3. Completed: Execution succeeded
ToolStateCompleted = {
  status: "completed",
  input: {...}, output: "...",
  title: "Read file /src/index.ts",
  metadata: { truncated: false },
  time: { start, end, compacted? }  // compacted: Prune timestamp
}

// 4. Error: Execution failed
ToolStateError = {
  status: "error",
  input: {...}, error: "Permission denied",
  time: { start, end }
}
```

## 4.2.3 Message Version Migration Strategy

The `v2` in the filename hints that this is not the first version of the message model. The coexistence of `message.ts` (legacy) and `message-v2.ts` (current) indicates that OpenCode underwent a major refactoring of its message model.

Key improvements in the new version:

1. **Part System**: The legacy version stored all content directly on the message; the new version achieves content modularity through Parts
2. **Finer-Grained State Tracking**: The four-state model for ToolPart, StepStart/StepFinish markers
3. **Cost Tracking**: Each Assistant message records precise token consumption and cost

This incremental migration strategy (keeping the old version, introducing the new version, and gradually switching over) is a common practice in large projects -- it ensures that all existing data is not broken at once.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://www.opencodebook.xyz/en/chapter_04_session_system/4.2_message_model.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
