diff --git a/Cargo.toml b/Cargo.toml index 592903db..0bc08b32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ unstable = [ "unstable_session_list", "unstable_session_model", "unstable_session_resume", + "unstable_session_usage", ] unstable_cancel_request = [] unstable_session_config_options = [] @@ -30,6 +31,7 @@ unstable_session_info_update = [] unstable_session_list = [] unstable_session_model = [] unstable_session_resume = [] +unstable_session_usage = [] [[bin]] name = "generate" diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index a90cfec6..440a6d99 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -612,6 +612,14 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte StopReason} required> Indicates why the agent stopped processing the turn. +Usage | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Token usage for this turn (optional). + + ### session/resume @@ -1955,6 +1963,25 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte A single item of content +## Cost + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Cost information for a session. + +**Type:** Object + +**Properties:** + + + Total cumulative cost for session. + + + ISO 4217 currency code (e.g., "USD", "EUR"). + + ## CurrentModeUpdate The current mode of the session has changed @@ -3687,6 +3714,44 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Context window and cost update for the session. + + + + + 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. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + +Cost | null} > + Cumulative session cost (optional). + + + + + Total context window size in tokens. + + - Minimum: `0` + + + + Tokens currently in context. + + - Minimum: `0` + + + + + + ## StopReason Reasons why an agent stops processing a prompt turn. @@ -4131,3 +4196,88 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte A hint to display when the input hasn't been provided yet + +## Usage + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Token usage information for a prompt turn. + +**Type:** Object + +**Properties:** + + + Total cache read tokens. + + - Minimum: `0` + + + + Total cache write tokens. + + - Minimum: `0` + + + + Total input tokens across all turns. + + - Minimum: `0` + + + + Total output tokens across all turns. + + - Minimum: `0` + + + + Total thought/reasoning tokens + + - Minimum: `0` + + + + Sum of all token types across session. + + - Minimum: `0` + + + +## UsageUpdate + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Context window and cost update for a session. + +**Type:** Object + +**Properties:** + + + 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. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + +Cost | null} > + Cumulative session cost (optional). + + + Total context window size in tokens. + + - Minimum: `0` + + + + Tokens currently in context. + + - Minimum: `0` + + diff --git a/docs/rfds/session-usage.mdx b/docs/rfds/session-usage.mdx index bf56bdfc..c8df37a1 100644 --- a/docs/rfds/session-usage.mdx +++ b/docs/rfds/session-usage.mdx @@ -92,7 +92,7 @@ Add a `usage` field to `PromptResponse` for token consumption tracking: - `total_tokens` (number, required) - Sum of all token types across session - `input_tokens` (number, required) - Total input tokens across all turns - `output_tokens` (number, required) - Total output tokens across all turns -- `thought_tokens` (number, optional) - Total thought/reasoning tokens (for o1/o3 models) +- `thought_tokens` (number, optional) - Total thought/reasoning tokens - `cached_read_tokens` (number, optional) - Total cache read tokens - `cached_write_tokens` (number, optional) - Total cache write tokens diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index c7e8534c..f1fe59d8 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -1013,6 +1013,22 @@ "required": ["content"], "type": "object" }, + "Cost": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCost information for a session.", + "properties": { + "amount": { + "description": "Total cumulative cost for session.", + "format": "double", + "type": "number" + }, + "currency": { + "description": "ISO 4217 currency code (e.g., \"USD\", \"EUR\").", + "type": "string" + } + }, + "required": ["amount", "currency"], + "type": "object" + }, "CreateTerminalRequest": { "description": "Request to create a new terminal and execute a command.", "properties": { @@ -2224,6 +2240,17 @@ } ], "description": "Indicates why the agent stopped processing the turn." + }, + "usage": { + "anyOf": [ + { + "$ref": "#/$defs/Usage" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nToken usage for this turn (optional)." } }, "required": ["stopReason"], @@ -3191,6 +3218,22 @@ }, "required": ["sessionUpdate"], "type": "object" + }, + { + "allOf": [ + { + "$ref": "#/$defs/UsageUpdate" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nContext window and cost update for the session.", + "properties": { + "sessionUpdate": { + "const": "usage_update", + "type": "string" + } + }, + "required": ["sessionUpdate"], + "type": "object" } ] }, @@ -3813,6 +3856,84 @@ "required": ["hint"], "type": "object" }, + "Usage": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nToken usage information for a prompt turn.", + "properties": { + "cachedReadTokens": { + "description": "Total cache read tokens.", + "format": "uint64", + "minimum": 0, + "type": ["integer", "null"] + }, + "cachedWriteTokens": { + "description": "Total cache write tokens.", + "format": "uint64", + "minimum": 0, + "type": ["integer", "null"] + }, + "inputTokens": { + "description": "Total input tokens across all turns.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "outputTokens": { + "description": "Total output tokens across all turns.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "thoughtTokens": { + "description": "Total thought/reasoning tokens", + "format": "uint64", + "minimum": 0, + "type": ["integer", "null"] + }, + "totalTokens": { + "description": "Sum of all token types across session.", + "format": "uint64", + "minimum": 0, + "type": "integer" + } + }, + "required": ["totalTokens", "inputTokens", "outputTokens"], + "type": "object" + }, + "UsageUpdate": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nContext window and cost update for a session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "cost": { + "anyOf": [ + { + "$ref": "#/$defs/Cost" + }, + { + "type": "null" + } + ], + "description": "Cumulative session cost (optional)." + }, + "size": { + "description": "Total context window size in tokens.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "used": { + "description": "Tokens currently in context.", + "format": "uint64", + "minimum": 0, + "type": "integer" + } + }, + "required": ["used", "size"], + "type": "object" + }, "WaitForTerminalExitRequest": { "description": "Request to wait for a terminal command to exit.", "properties": { diff --git a/src/agent.rs b/src/agent.rs index 6f5e5627..f3477d1e 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -2118,6 +2118,14 @@ impl PromptRequest { pub struct PromptResponse { /// Indicates why the agent stopped processing the turn. pub stop_reason: StopReason, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Token usage for this turn (optional). + #[cfg(feature = "unstable_session_usage")] + #[serde(skip_serializing_if = "Option::is_none")] + pub usage: 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. @@ -2132,10 +2140,24 @@ impl PromptResponse { pub fn new(stop_reason: StopReason) -> Self { Self { stop_reason, + #[cfg(feature = "unstable_session_usage")] + usage: None, meta: None, } } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Token usage for this turn. + #[cfg(feature = "unstable_session_usage")] + #[must_use] + pub fn usage(mut self, usage: impl IntoOption) -> Self { + self.usage = usage.into_option(); + self + } + /// 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. @@ -2175,6 +2197,69 @@ pub enum StopReason { Cancelled, } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Token usage information for a prompt turn. +#[cfg(feature = "unstable_session_usage")] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct Usage { + /// Sum of all token types across session. + pub total_tokens: u64, + /// Total input tokens across all turns. + pub input_tokens: u64, + /// Total output tokens across all turns. + pub output_tokens: u64, + /// Total thought/reasoning tokens + #[serde(skip_serializing_if = "Option::is_none")] + pub thought_tokens: Option, + /// Total cache read tokens. + #[serde(skip_serializing_if = "Option::is_none")] + pub cached_read_tokens: Option, + /// Total cache write tokens. + #[serde(skip_serializing_if = "Option::is_none")] + pub cached_write_tokens: Option, +} + +#[cfg(feature = "unstable_session_usage")] +impl Usage { + #[must_use] + pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self { + Self { + total_tokens, + input_tokens, + output_tokens, + thought_tokens: None, + cached_read_tokens: None, + cached_write_tokens: None, + } + } + + /// Total thought/reasoning tokens + #[must_use] + pub fn thought_tokens(mut self, thought_tokens: impl IntoOption) -> Self { + self.thought_tokens = thought_tokens.into_option(); + self + } + + /// Total cache read tokens. + #[must_use] + pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption) -> Self { + self.cached_read_tokens = cached_read_tokens.into_option(); + self + } + + /// Total cache write tokens. + #[must_use] + pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption) -> Self { + self.cached_write_tokens = cached_write_tokens.into_option(); + self + } +} + // Model /// **UNSTABLE** diff --git a/src/client.rs b/src/client.rs index 94cf5644..2a6119b3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -104,6 +104,13 @@ pub enum SessionUpdate { #[cfg(feature = "unstable_session_info_update")] /// Session metadata has been updated (title, timestamps, custom metadata) SessionInfoUpdate(SessionInfoUpdate), + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Context window and cost update for the session. + #[cfg(feature = "unstable_session_usage")] + UsageUpdate(UsageUpdate), } /// The current mode of the session has changed @@ -245,6 +252,90 @@ impl SessionInfoUpdate { } } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Context window and cost update for a session. +#[cfg(feature = "unstable_session_usage")] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct UsageUpdate { + /// Tokens currently in context. + pub used: u64, + /// Total context window size in tokens. + pub size: u64, + /// Cumulative session cost (optional). + #[serde(skip_serializing_if = "Option::is_none")] + pub cost: 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. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_session_usage")] +impl UsageUpdate { + #[must_use] + pub fn new(used: u64, size: u64) -> Self { + Self { + used, + size, + cost: None, + meta: None, + } + } + + /// Cumulative session cost (optional). + #[must_use] + pub fn cost(mut self, cost: impl IntoOption) -> Self { + self.cost = cost.into_option(); + self + } + + /// 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. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Cost information for a session. +#[cfg(feature = "unstable_session_usage")] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct Cost { + /// Total cumulative cost for session. + pub amount: f64, + /// ISO 4217 currency code (e.g., "USD", "EUR"). + pub currency: String, +} + +#[cfg(feature = "unstable_session_usage")] +impl Cost { + #[must_use] + pub fn new(amount: f64, currency: impl Into) -> Self { + Self { + amount, + currency: currency.into(), + } + } +} + /// A streamed item of content #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] #[serde(rename_all = "camelCase")]