diff --git a/docs/docs.json b/docs/docs.json
index 5e5ef1e9..c2e09de1 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -193,6 +193,7 @@
"rfds/custom-llm-endpoint",
"rfds/model-config-category",
"rfds/plan-operations",
+ "rfds/message-id",
{
"group": "v2 Draft",
"expanded": true,
@@ -202,7 +203,6 @@
"rfds/v2/enum-variant-extension",
"rfds/v2/client-filesystem-terminal-capabilities",
"rfds/v2/plan-variants",
- "rfds/message-id",
"rfds/streamable-http-websocket-transport"
]
}
diff --git a/docs/protocol/v1/draft/schema.mdx b/docs/protocol/v1/draft/schema.mdx
index 16028c7f..57056a93 100644
--- a/docs/protocol/v1/draft/schema.mdx
+++ b/docs/protocol/v1/draft/schema.mdx
@@ -3512,7 +3512,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d
ContentBlock} required>
A single item of content
-
+MessageId | null>} >
**UNSTABLE**
This capability is not part of the spec yet, and may be removed or changed at any point.
@@ -4869,6 +4869,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d
Human-readable name identifying this MCP server.
+## MessageId
+
+Unique identifier for a message within a session.
+
+**Type:** `string`
+
## MultiSelectItems
Items for a multi-select (array) property schema.
@@ -7160,7 +7166,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d
ContentBlock} required>
A single item of content
-
+MessageId | null>} >
**UNSTABLE**
This capability is not part of the spec yet, and may be removed or changed at any point.
@@ -7194,7 +7200,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d
ContentBlock} required>
A single item of content
-
+MessageId | null>} >
**UNSTABLE**
This capability is not part of the spec yet, and may be removed or changed at any point.
@@ -7228,7 +7234,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d
ContentBlock} required>
A single item of content
-
+MessageId | null>} >
**UNSTABLE**
This capability is not part of the spec yet, and may be removed or changed at any point.
diff --git a/docs/protocol/v2/draft/prompt-turn.mdx b/docs/protocol/v2/draft/prompt-turn.mdx
index 54bb27a9..833b5d2c 100644
--- a/docs/protocol/v2/draft/prompt-turn.mdx
+++ b/docs/protocol/v2/draft/prompt-turn.mdx
@@ -168,7 +168,7 @@ The Agent then reports text responses from the model:
}
```
-The Agent **MAY** include an opaque `messageId` on message chunks. Chunks with the same `messageId` belong to the same message; a changed `messageId` indicates a new message.
+The Agent **MUST** include an opaque `messageId` on message chunks. Chunks with the same `messageId` belong to the same message; a changed `messageId` indicates a new message.
If the model requested tool calls, these are also reported immediately:
diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx
index a0da1ee7..c9afeced 100644
--- a/docs/protocol/v2/draft/schema.mdx
+++ b/docs/protocol/v2/draft/schema.mdx
@@ -3074,12 +3074,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d
ContentBlock} required>
A single item of content
-
- **UNSTABLE**
-
-This capability is not part of the spec yet, and may be removed or changed at any point.
-
-A unique identifier for the message this chunk belongs to.
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
All chunks belonging to the same message share the same `messageId`.
A change in `messageId` indicates a new message has started.
@@ -4383,6 +4379,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d
Human-readable name identifying this MCP server.
+## MessageId
+
+Unique identifier for a message within a session.
+
+**Type:** `string`
+
## MultiSelectItems
Items for a multi-select (array) property schema.
@@ -6706,12 +6708,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d
ContentBlock} required>
A single item of content
-
- **UNSTABLE**
-
-This capability is not part of the spec yet, and may be removed or changed at any point.
-
-A unique identifier for the message this chunk belongs to.
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
All chunks belonging to the same message share the same `messageId`.
A change in `messageId` indicates a new message has started.
@@ -6740,12 +6738,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d
ContentBlock} required>
A single item of content
-
- **UNSTABLE**
-
-This capability is not part of the spec yet, and may be removed or changed at any point.
-
-A unique identifier for the message this chunk belongs to.
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
All chunks belonging to the same message share the same `messageId`.
A change in `messageId` indicates a new message has started.
@@ -6774,12 +6768,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d
ContentBlock} required>
A single item of content
-
- **UNSTABLE**
-
-This capability is not part of the spec yet, and may be removed or changed at any point.
-
-A unique identifier for the message this chunk belongs to.
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
All chunks belonging to the same message share the same `messageId`.
A change in `messageId` indicates a new message has started.
diff --git a/docs/protocol/v2/draft/session-setup.mdx b/docs/protocol/v2/draft/session-setup.mdx
index b9df33d3..9a4219d7 100644
--- a/docs/protocol/v2/draft/session-setup.mdx
+++ b/docs/protocol/v2/draft/session-setup.mdx
@@ -175,7 +175,7 @@ Followed by the agent's response:
}
```
-If the Agent provides message IDs during replay, each `messageId` is an opaque, unique identifier for the replayed message.
+During replay, the Agent **MUST** include an opaque, unique `messageId` for each replayed message.
When **all** the conversation entries have been streamed to the Client, the
Agent **MUST** respond to the original `session/load` request.
diff --git a/docs/protocol/v2/schema.mdx b/docs/protocol/v2/schema.mdx
index 95665481..1eb3f54a 100644
--- a/docs/protocol/v2/schema.mdx
+++ b/docs/protocol/v2/schema.mdx
@@ -1443,6 +1443,13 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e
ContentBlock} required>
A single item of content
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
+
+All chunks belonging to the same message share the same `messageId`.
+A change in `messageId` indicates a new message has started.
+
+
## Diff
@@ -1980,6 +1987,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e
Human-readable name identifying this MCP server.
+## MessageId
+
+Unique identifier for a message within a session.
+
+**Type:** `string`
+
## PermissionOption
An option presented to the user when requesting permission.
@@ -2904,6 +2917,13 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e
ContentBlock} required>
A single item of content
+
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
+
+All chunks belonging to the same message share the same `messageId`.
+A change in `messageId` indicates a new message has started.
+
The discriminator value. Must be `"user_message_chunk"`.
@@ -2927,6 +2947,13 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e
ContentBlock} required>
A single item of content
+
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
+
+All chunks belonging to the same message share the same `messageId`.
+A change in `messageId` indicates a new message has started.
+
The discriminator value. Must be `"agent_message_chunk"`.
@@ -2950,6 +2977,13 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e
ContentBlock} required>
A single item of content
+
+MessageId} required>
+ A unique identifier for the message this chunk belongs to.
+
+All chunks belonging to the same message share the same `messageId`.
+A change in `messageId` indicates a new message has started.
+
The discriminator value. Must be `"agent_thought_chunk"`.
diff --git a/docs/rfds/message-id.mdx b/docs/rfds/message-id.mdx
index e2902006..24424657 100644
--- a/docs/rfds/message-id.mdx
+++ b/docs/rfds/message-id.mdx
@@ -114,13 +114,14 @@ For agent message chunks, the Agent also generates and includes a `messageId`:
The `messageId` field would be:
-- **Optional** on `agent_message_chunk`, `user_message_chunk`, and `agent_thought_chunk` updates
+- **Optional in v1** on `agent_message_chunk`, `user_message_chunk`, and `agent_thought_chunk` updates. An omitted `messageId` and an explicit `null` are equivalent and both mean the Agent did not provide a message ID for that chunk.
+- **Required in v2** on `agent_message_chunk`, `user_message_chunk`, and `agent_thought_chunk` updates. The field MUST be present and MUST be a non-null string.
- **Agent-generated** - the Agent is the only participant that creates protocol message IDs
- **Unique per message** within a session
- **Stable across chunks** - all chunks belonging to the same message share the same `messageId`
- **Opaque** - Implementations treat it as an identifier without parsing its structure
-If an Agent supports message IDs and emits an accepted or replayed user message through `session/update`, it SHOULD include `messageId` on that user message update.
+In v1, if an Agent supports message IDs and emits an accepted or replayed user message through `session/update`, it SHOULD include `messageId` on that user message update. In v2, every streamed user, agent, and thought message chunk MUST include a `messageId`.
## Shiny future
@@ -168,11 +169,14 @@ Example future editing capability:
### Phase 1: Core Protocol Changes
1. **Update schema** (`schema/schema.json`):
- - Add optional `messageId` field (type: `string`) to `ContentChunk` (used by `AgentMessageChunk`, `UserMessageChunk`, `AgentThoughtChunk`)
+ - Add optional `messageId` field (type: `string`) to v1 `ContentChunk` (used by `AgentMessageChunk`, `UserMessageChunk`, `AgentThoughtChunk`)
+ - Add required `messageId` field (type: `string`) to v2 `ContentChunk`
2. **Update Rust SDK** (`rust/client.rs` and `rust/agent.rs`):
- - Add `message_id: Option` field to `ContentChunk` struct
- - Update serialization to include `messageId` in JSON output when present
+ - Add a dedicated `MessageId` newtype
+ - Add `message_id: Option` field to the v1 `ContentChunk` struct
+ - Add `message_id: MessageId` field to the v2 `ContentChunk` struct
+ - Update serialization to include `messageId` in JSON output when present for v1 and always for v2
3. **Update TypeScript SDK** (if applicable):
- Add `messageId` field to corresponding session update types
@@ -195,9 +199,9 @@ Example future editing capability:
### Backward Compatibility
-The `messageId` field is **optional** to ensure this is a non-breaking change. Agents SHOULD include the `messageId` field on message chunks they can identify, but it is not required for v1 compatibility. Features that rely on `messageId` (such as future message editing capabilities) will implicitly require the field to be present - Agents that don't provide it simply won't support those features.
+The `messageId` field is **optional in v1** to ensure this is a non-breaking change. Agents SHOULD include the `messageId` field on message chunks they can identify, but it is not required for v1 compatibility. Features that rely on `messageId` (such as future message editing capabilities) will implicitly require the field to be present - Agents that don't provide it simply won't support those features.
-Making this field required will be considered for a future v2 version of the protocol after wide adoption.
+The field is **required in v2**, where breaking protocol changes are allowed. v2 agents MUST include `messageId` on every streamed message chunk, and v2 clients can reject chunks where the field is omitted or `null`.
## Frequently asked questions
@@ -245,9 +249,9 @@ This matches other protocol identifiers (`sessionId`, `terminalId`, `toolCallId`
### Should this field be required or optional?
-The field is **optional** for v1 to ensure backward compatibility. Agents SHOULD include `messageId`, but it is not required. Features that depend on `messageId` (such as message editing) will implicitly require it - if an Agent doesn't provide `messageId`, those features simply won't be available.
+The field is **optional** for v1 to ensure backward compatibility. Agents SHOULD include `messageId`, but it is not required. In v1, omitting `messageId` and sending `null` are equivalent and both mean no message ID was provided.
-Making this field required will be considered for a future v2 version of the protocol.
+The field is **required** for v2. The key MUST be present and its value MUST be a non-null string.
### What format should message IDs use?
@@ -278,6 +282,7 @@ Future RFDs may propose extending `messageId` to other update types if use cases
## Revision history
+- **2026-06-03**: Moved the RFD out of the v2 Draft group while keeping v2-specific behavior; added a dedicated `MessageId` type, and made v2 require message IDs on streamed message chunks while v1 keeps optional message IDs for compatibility
- **2026-06-02**: Updated the proposal so message IDs are Agent-generated only, removed client-provided prompt IDs and prompt response acknowledgments, and removed the UUID requirement in favor of opaque strings
- **2026-02-17**: Added "Message ID Acknowledgment" section to clarify that presence/absence of `userMessageId` in response indicates whether the Agent recorded the ID; clarified that UUID format is MUST (not SHOULD) since both sides generate IDs; renamed response field to `userMessageId` for clarity (request keeps `messageId`)
- **2026-01-29**: Updated to allow both clients and agents to generate message IDs using UUID format
diff --git a/docs/rfds/v2/overview.mdx b/docs/rfds/v2/overview.mdx
index 7f3bdb10..4d146353 100644
--- a/docs/rfds/v2/overview.mdx
+++ b/docs/rfds/v2/overview.mdx
@@ -32,8 +32,6 @@ Current RFDs accepted as Drafts that are targeting v2 release
- [Enum Variant Extension](./enum-variant-extension.mdx)
- [Client Filesystem and Terminal Surface](./client-filesystem-terminal-capabilities.mdx)
- [Plan Variants](./plan-variants.mdx)
-- [Message IDs](../message-id.md)
- - [Fork from specified IDs](../session-fork.mds)
- [Remote Transports](../streamable-http-websocket-transport.mdx)
Other RFDs will progress separately and are not dependent on breaking changes (specifically the new prompt lifecycle) and can land in either or both v1 and v2.
@@ -44,6 +42,7 @@ Other RFDs will progress separately and are not dependent on breaking changes (s
- Agents should expose mode-like and model-related state through [Session Config Options](../session-config-options.mdx) instead of dedicated mode or model selector APIs.
- Remove the v1 Client filesystem and terminal execution surface from v2. This includes `clientCapabilities.fs`, the top-level `clientCapabilities.terminal` field, `fs/*` methods, `terminal/*` methods, and terminal tool-call content. Terminal authentication remains separate under `clientCapabilities.auth.terminal`.
- Make [plan variants](./plan-variants.mdx) the default v2 plan shape by replacing the old `plan` session update with item-based `plan_update`.
+- Require [message IDs](../message-id.mdx) on streamed message chunks.
- Follow JSON-RPC 2.0 batch request and notification behavior.
### RFDs to be Written
diff --git a/docs/rfds/v2/prompt.mdx b/docs/rfds/v2/prompt.mdx
index 3d0785b5..599800ad 100644
--- a/docs/rfds/v2/prompt.mdx
+++ b/docs/rfds/v2/prompt.mdx
@@ -44,7 +44,7 @@ The agent will respond once the prompt has been _accepted_, not when the turn is
}
```
-Given there will need to be some more exploration for Message IDs in general, I won't tie that RFD to this one unnecessarily. The accepted user message notification becomes the natural point to provide the ID if the agent has one.
+Given there will need to be some more exploration for Message IDs in general, I won't tie that RFD to this one unnecessarily. The accepted user message notification becomes the natural point to provide the required v2 message ID.
Queueing messages (still an ongoing discussion) would also fit much nicer in this pattern. Either by adding additional parameters or a separate method, we can allow a client to submit queued messages and either cancel or edit them before they get accepted without having to mess with the turn semantics of the previous prompt request approach. Again, queueing is decidedly not part of this RFD, but it seems to fall more naturally into this semantic change in the prompt request.
@@ -119,7 +119,7 @@ And also send the notification:
}
```
-The client can then make sure the prompt sits nicely in the feed at the right place, and also allows for multiple clients to be attached to the same session because they would receive a user message that they didn't prompt. If the agent includes a `messageId` in that notification, the client can use that agent-provided ID for future message-specific operations.
+The client can then make sure the prompt sits nicely in the feed at the right place, and also allows for multiple clients to be attached to the same session because they would receive a user message that they didn't prompt. The agent-provided `messageId` in that notification gives the client the ID for future message-specific operations.
This is a new message type as well. Not a `user_message_chunk` but just a `user_message` that allows for sending the entire message at once. For v2 we will make sure to allow for the agent to do the same on their messages as well, providing both full and partial streaming update patterns for a given message.
diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json
index ee20144e..819a566c 100644
--- a/schema/schema.unstable.json
+++ b/schema/schema.unstable.json
@@ -1846,8 +1846,15 @@
"description": "A single item of content"
},
"messageId": {
- "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA unique identifier for the message this chunk belongs to.\n\nAll chunks belonging to the same message share the same `messageId`.\nA change in `messageId` indicates a new message has started.",
- "type": ["string", "null"]
+ "anyOf": [
+ {
+ "$ref": "#/$defs/MessageId"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA unique identifier for the message this chunk belongs to.\n\nAll chunks belonging to the same message share the same `messageId`.\nA change in `messageId` indicates a new message has started."
}
},
"required": ["content"],
@@ -3726,6 +3733,10 @@
"required": ["name", "command", "args", "env"],
"type": "object"
},
+ "MessageId": {
+ "description": "Unique identifier for a message within a session.",
+ "type": "string"
+ },
"MessageMcpNotification": {
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNotification parameters for `mcp/message`.\n\nThis is used when the wrapped MCP message is a notification and the outer JSON-RPC\nenvelope has no `id`.",
"properties": {
diff --git a/schema/schema.v2.unstable.json b/schema/schema.v2.unstable.json
index 73407fc5..2e3f7a1c 100644
--- a/schema/schema.v2.unstable.json
+++ b/schema/schema.v2.unstable.json
@@ -1836,11 +1836,15 @@
"description": "A single item of content"
},
"messageId": {
- "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA unique identifier for the message this chunk belongs to.\n\nAll chunks belonging to the same message share the same `messageId`.\nA change in `messageId` indicates a new message has started.",
- "type": ["string", "null"]
+ "allOf": [
+ {
+ "$ref": "#/$defs/MessageId"
+ }
+ ],
+ "description": "A unique identifier for the message this chunk belongs to.\n\nAll chunks belonging to the same message share the same `messageId`.\nA change in `messageId` indicates a new message has started."
}
},
- "required": ["content"],
+ "required": ["content", "messageId"],
"type": "object"
},
"Cost": {
@@ -3539,6 +3543,10 @@
"required": ["name", "command", "args", "env"],
"type": "object"
},
+ "MessageId": {
+ "description": "Unique identifier for a message within a session.",
+ "type": "string"
+ },
"MessageMcpNotification": {
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNotification parameters for `mcp/message`.\n\nThis is used when the wrapped MCP message is a notification and the outer JSON-RPC\nenvelope has no `id`.",
"properties": {
diff --git a/src/v1/client.rs b/src/v1/client.rs
index 0313ef45..32d899b3 100644
--- a/src/v1/client.rs
+++ b/src/v1/client.rs
@@ -379,7 +379,7 @@ pub struct ContentChunk {
/// All chunks belonging to the same message share the same `messageId`.
/// A change in `messageId` indicates a new message has started.
#[cfg(feature = "unstable_message_id")]
- pub message_id: Option,
+ pub message_id: Option,
/// The _meta property is reserved by ACP to allow clients and agents to attach additional
/// metadata to their interactions. Implementations MUST NOT make assumptions about values at
/// these keys.
@@ -410,7 +410,7 @@ impl ContentChunk {
/// A change in `messageId` indicates a new message has started.
#[cfg(feature = "unstable_message_id")]
#[must_use]
- pub fn message_id(mut self, message_id: impl IntoOption) -> Self {
+ pub fn message_id(mut self, message_id: impl IntoOption) -> Self {
self.message_id = message_id.into_option();
self
}
@@ -427,6 +427,29 @@ impl ContentChunk {
}
}
+/// Unique identifier for a message within a session.
+#[cfg(feature = "unstable_message_id")]
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
+#[serde(transparent)]
+#[from(Arc, String, &'static str)]
+#[non_exhaustive]
+pub struct MessageId(pub Arc);
+
+#[cfg(feature = "unstable_message_id")]
+impl MessageId {
+ #[must_use]
+ pub fn new(id: impl Into>) -> Self {
+ Self(id.into())
+ }
+}
+
+#[cfg(feature = "unstable_message_id")]
+impl IntoOption for &str {
+ fn into_option(self) -> Option {
+ Some(MessageId::new(self))
+ }
+}
+
/// Available commands are ready or have changed
#[serde_as]
#[skip_serializing_none]
diff --git a/src/v2/client.rs b/src/v2/client.rs
index bb49bb9c..926761d6 100644
--- a/src/v2/client.rs
+++ b/src/v2/client.rs
@@ -432,16 +432,11 @@ impl Cost {
pub struct ContentChunk {
/// A single item of content
pub content: ContentBlock,
- /// **UNSTABLE**
- ///
- /// This capability is not part of the spec yet, and may be removed or changed at any point.
- ///
/// A unique identifier for the message this chunk belongs to.
///
/// All chunks belonging to the same message share the same `messageId`.
/// A change in `messageId` indicates a new message has started.
- #[cfg(feature = "unstable_message_id")]
- pub message_id: Option,
+ pub message_id: MessageId,
/// The _meta property is reserved by ACP to allow clients and agents to attach additional
/// metadata to their interactions. Implementations MUST NOT make assumptions about values at
/// these keys.
@@ -453,27 +448,21 @@ pub struct ContentChunk {
impl ContentChunk {
#[must_use]
- pub fn new(content: ContentBlock) -> Self {
+ pub fn new(content: ContentBlock, message_id: impl Into) -> Self {
Self {
content,
- #[cfg(feature = "unstable_message_id")]
- message_id: None,
+ message_id: message_id.into(),
meta: None,
}
}
- /// **UNSTABLE**
- ///
- /// This capability is not part of the spec yet, and may be removed or changed at any point.
- ///
/// A unique identifier for the message this chunk belongs to.
///
/// All chunks belonging to the same message share the same `messageId`.
/// A change in `messageId` indicates a new message has started.
- #[cfg(feature = "unstable_message_id")]
#[must_use]
- pub fn message_id(mut self, message_id: impl IntoOption) -> Self {
- self.message_id = message_id.into_option();
+ pub fn message_id(mut self, message_id: impl Into) -> Self {
+ self.message_id = message_id.into();
self
}
@@ -489,6 +478,20 @@ impl ContentChunk {
}
}
+/// Unique identifier for a message within a session.
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
+#[serde(transparent)]
+#[from(Arc, String, &'static str)]
+#[non_exhaustive]
+pub struct MessageId(pub Arc);
+
+impl MessageId {
+ #[must_use]
+ pub fn new(id: impl Into>) -> Self {
+ Self(id.into())
+ }
+}
+
/// Available commands are ready or have changed
#[serde_as]
#[skip_serializing_none]
diff --git a/src/v2/conversion.rs b/src/v2/conversion.rs
index fe384c29..010e5fe5 100644
--- a/src/v2/conversion.rs
+++ b/src/v2/conversion.rs
@@ -438,6 +438,24 @@ impl IntoV2 for crate::v1::SessionId {
}
}
+#[cfg(feature = "unstable_message_id")]
+impl IntoV1 for super::MessageId {
+ type Output = crate::v1::MessageId;
+
+ fn into_v1(self) -> Result {
+ Ok(crate::v1::MessageId(self.0.into_v1()?))
+ }
+}
+
+#[cfg(feature = "unstable_message_id")]
+impl IntoV2 for crate::v1::MessageId {
+ type Output = super::MessageId;
+
+ fn into_v2(self) -> Result {
+ Ok(super::MessageId(self.0.into_v2()?))
+ }
+}
+
#[cfg(not(feature = "unstable_plan_operations"))]
impl IntoV1 for super::PlanUpdate {
type Output = crate::v1::Plan;
@@ -1051,14 +1069,15 @@ impl IntoV1 for super::ContentChunk {
fn into_v1(self) -> Result {
let Self {
content,
- #[cfg(feature = "unstable_message_id")]
message_id,
meta,
} = self;
+ #[cfg(not(feature = "unstable_message_id"))]
+ drop(message_id);
Ok(crate::v1::ContentChunk {
content: content.into_v1()?,
#[cfg(feature = "unstable_message_id")]
- message_id: message_id.into_v1()?,
+ message_id: Some(message_id.into_v1()?),
meta: meta.into_v1()?,
})
}
@@ -1074,12 +1093,27 @@ impl IntoV2 for crate::v1::ContentChunk {
message_id,
meta,
} = self;
- Ok(super::ContentChunk {
- content: content.into_v2()?,
- #[cfg(feature = "unstable_message_id")]
- message_id: message_id.into_v2()?,
- meta: meta.into_v2()?,
- })
+ #[cfg(not(feature = "unstable_message_id"))]
+ {
+ drop((content, meta));
+ Err(ProtocolConversionError::new(
+ "v1 ContentChunk without messageId cannot be represented in v2",
+ ))
+ }
+ #[cfg(feature = "unstable_message_id")]
+ {
+ Ok(super::ContentChunk {
+ content: content.into_v2()?,
+ message_id: message_id
+ .ok_or_else(|| {
+ ProtocolConversionError::new(
+ "v1 ContentChunk without messageId cannot be represented in v2",
+ )
+ })?
+ .into_v2()?,
+ meta: meta.into_v2()?,
+ })
+ }
}
}
@@ -8781,16 +8815,19 @@ mod tests {
#[test]
fn round_trips_session_notification_for_unchanged_update_kinds() {
+ #[cfg(feature = "unstable_message_id")]
+ fn content_chunk(text: &str, message_id: &str) -> v1::ContentChunk {
+ let chunk = v1::ContentChunk::new(v1::ContentBlock::Text(v1::TextContent::new(text)));
+ chunk.message_id(message_id)
+ }
+
let cases: Vec = vec![
- v1::SessionUpdate::UserMessageChunk(v1::ContentChunk::new(v1::ContentBlock::Text(
- v1::TextContent::new("u"),
- ))),
- v1::SessionUpdate::AgentMessageChunk(v1::ContentChunk::new(v1::ContentBlock::Text(
- v1::TextContent::new("a"),
- ))),
- v1::SessionUpdate::AgentThoughtChunk(v1::ContentChunk::new(v1::ContentBlock::Text(
- v1::TextContent::new("t"),
- ))),
+ #[cfg(feature = "unstable_message_id")]
+ v1::SessionUpdate::UserMessageChunk(content_chunk("u", "msg_user")),
+ #[cfg(feature = "unstable_message_id")]
+ v1::SessionUpdate::AgentMessageChunk(content_chunk("a", "msg_agent")),
+ #[cfg(feature = "unstable_message_id")]
+ v1::SessionUpdate::AgentThoughtChunk(content_chunk("t", "msg_thought")),
v1::SessionUpdate::ToolCall(v1::ToolCall::new("tc", "title")),
v1::SessionUpdate::ToolCallUpdate(v1::ToolCallUpdate::new(
"tc",
@@ -8816,6 +8853,14 @@ mod tests {
}
}
+ #[test]
+ fn v1_content_chunk_without_message_id_does_not_convert_to_v2() {
+ assert_v1_to_v2_error(
+ v1::ContentChunk::new(v1::ContentBlock::Text(v1::TextContent::new("missing"))),
+ "v1 ContentChunk without messageId cannot be represented in v2",
+ );
+ }
+
#[test]
fn v1_plan_session_update_converts_to_v2_item_plan_update() {
let update = v1::SessionUpdate::Plan(v1::Plan::new(vec![v1::PlanEntry::new(