use std::{path::PathBuf, sync::Arc};
#[cfg(feature = "unstable_auth_methods")]
use std::collections::HashMap;
use derive_more::{Display, From};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, IntoOption, Meta,
ProtocolVersion, SessionId,
};
#[cfg(feature = "unstable_nes")]
use crate::{
AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
};
#[cfg(feature = "unstable_nes")]
use crate::nes::{
DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
NES_SUGGEST_METHOD_NAME,
};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct InitializeRequest {
pub protocol_version: ProtocolVersion,
#[serde(default)]
pub client_capabilities: ClientCapabilities,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_info: Option<Implementation>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl InitializeRequest {
#[must_use]
pub fn new(protocol_version: ProtocolVersion) -> Self {
Self {
protocol_version,
client_capabilities: ClientCapabilities::default(),
client_info: None,
meta: None,
}
}
#[must_use]
pub fn client_capabilities(mut self, client_capabilities: ClientCapabilities) -> Self {
self.client_capabilities = client_capabilities;
self
}
#[must_use]
pub fn client_info(mut self, client_info: impl IntoOption<Implementation>) -> Self {
self.client_info = client_info.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct InitializeResponse {
pub protocol_version: ProtocolVersion,
#[serde(default)]
pub agent_capabilities: AgentCapabilities,
#[serde(default)]
pub auth_methods: Vec<AuthMethod>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_info: Option<Implementation>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl InitializeResponse {
#[must_use]
pub fn new(protocol_version: ProtocolVersion) -> Self {
Self {
protocol_version,
agent_capabilities: AgentCapabilities::default(),
auth_methods: vec![],
agent_info: None,
meta: None,
}
}
#[must_use]
pub fn agent_capabilities(mut self, agent_capabilities: AgentCapabilities) -> Self {
self.agent_capabilities = agent_capabilities;
self
}
#[must_use]
pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
self.auth_methods = auth_methods;
self
}
#[must_use]
pub fn agent_info(mut self, agent_info: impl IntoOption<Implementation>) -> Self {
self.agent_info = agent_info.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Implementation {
pub name: String,
pub title: Option<String>,
pub version: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl Implementation {
#[must_use]
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
Self {
name: name.into(),
title: None,
version: version.into(),
meta: None,
}
}
#[must_use]
pub fn title(mut self, title: impl IntoOption<String>) -> Self {
self.title = title.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AuthenticateRequest {
pub method_id: AuthMethodId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl AuthenticateRequest {
#[must_use]
pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
Self {
method_id: method_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AuthenticateResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl AuthenticateResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_logout")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct LogoutRequest {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_logout")]
impl LogoutRequest {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_logout")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct LogoutResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_logout")]
impl LogoutResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_logout")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AgentAuthCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub logout: Option<LogoutCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_logout")]
impl AgentAuthCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn logout(mut self, logout: impl IntoOption<LogoutCapabilities>) -> Self {
self.logout = logout.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_logout")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub struct LogoutCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_logout")]
impl LogoutCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
#[serde(transparent)]
#[from(Arc<str>, String, &'static str)]
#[non_exhaustive]
pub struct AuthMethodId(pub Arc<str>);
impl AuthMethodId {
#[must_use]
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum AuthMethod {
#[cfg(feature = "unstable_auth_methods")]
EnvVar(AuthMethodEnvVar),
#[cfg(feature = "unstable_auth_methods")]
Terminal(AuthMethodTerminal),
#[serde(untagged)]
Agent(AuthMethodAgent),
}
impl AuthMethod {
#[must_use]
pub fn id(&self) -> &AuthMethodId {
match self {
Self::Agent(a) => &a.id,
#[cfg(feature = "unstable_auth_methods")]
Self::EnvVar(e) => &e.id,
#[cfg(feature = "unstable_auth_methods")]
Self::Terminal(t) => &t.id,
}
}
#[must_use]
pub fn name(&self) -> &str {
match self {
Self::Agent(a) => &a.name,
#[cfg(feature = "unstable_auth_methods")]
Self::EnvVar(e) => &e.name,
#[cfg(feature = "unstable_auth_methods")]
Self::Terminal(t) => &t.name,
}
}
#[must_use]
pub fn description(&self) -> Option<&str> {
match self {
Self::Agent(a) => a.description.as_deref(),
#[cfg(feature = "unstable_auth_methods")]
Self::EnvVar(e) => e.description.as_deref(),
#[cfg(feature = "unstable_auth_methods")]
Self::Terminal(t) => t.description.as_deref(),
}
}
#[must_use]
pub fn meta(&self) -> Option<&Meta> {
match self {
Self::Agent(a) => a.meta.as_ref(),
#[cfg(feature = "unstable_auth_methods")]
Self::EnvVar(e) => e.meta.as_ref(),
#[cfg(feature = "unstable_auth_methods")]
Self::Terminal(t) => t.meta.as_ref(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AuthMethodAgent {
pub id: AuthMethodId,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl AuthMethodAgent {
#[must_use]
pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
description: None,
meta: None,
}
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_auth_methods")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AuthMethodEnvVar {
pub id: AuthMethodId,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub vars: Vec<AuthEnvVar>,
#[serde(skip_serializing_if = "Option::is_none")]
pub link: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_auth_methods")]
impl AuthMethodEnvVar {
#[must_use]
pub fn new(
id: impl Into<AuthMethodId>,
name: impl Into<String>,
vars: Vec<AuthEnvVar>,
) -> Self {
Self {
id: id.into(),
name: name.into(),
description: None,
vars,
link: None,
meta: None,
}
}
#[must_use]
pub fn link(mut self, link: impl IntoOption<String>) -> Self {
self.link = link.into_option();
self
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_auth_methods")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AuthEnvVar {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default = "default_true", skip_serializing_if = "is_true")]
#[schemars(extend("default" = true))]
pub secret: bool,
#[serde(default, skip_serializing_if = "is_false")]
#[schemars(extend("default" = false))]
pub optional: bool,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_auth_methods")]
fn default_true() -> bool {
true
}
#[cfg(feature = "unstable_auth_methods")]
#[expect(clippy::trivially_copy_pass_by_ref)]
fn is_true(v: &bool) -> bool {
*v
}
#[cfg(feature = "unstable_auth_methods")]
#[expect(clippy::trivially_copy_pass_by_ref)]
fn is_false(v: &bool) -> bool {
!*v
}
#[cfg(feature = "unstable_auth_methods")]
impl AuthEnvVar {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
label: None,
secret: true,
optional: false,
meta: None,
}
}
#[must_use]
pub fn label(mut self, label: impl IntoOption<String>) -> Self {
self.label = label.into_option();
self
}
#[must_use]
pub fn secret(mut self, secret: bool) -> Self {
self.secret = secret;
self
}
#[must_use]
pub fn optional(mut self, optional: bool) -> Self {
self.optional = optional;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_auth_methods")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AuthMethodTerminal {
pub id: AuthMethodId,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub args: Vec<String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub env: HashMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_auth_methods")]
impl AuthMethodTerminal {
#[must_use]
pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
description: None,
args: Vec::new(),
env: HashMap::new(),
meta: None,
}
}
#[must_use]
pub fn args(mut self, args: Vec<String>) -> Self {
self.args = args;
self
}
#[must_use]
pub fn env(mut self, env: HashMap<String, String>) -> Self {
self.env = env;
self
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NewSessionRequest {
pub cwd: PathBuf,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_directories: Vec<PathBuf>,
pub mcp_servers: Vec<McpServer>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NewSessionRequest {
#[must_use]
pub fn new(cwd: impl Into<PathBuf>) -> Self {
Self {
cwd: cwd.into(),
#[cfg(feature = "unstable_session_additional_directories")]
additional_directories: vec![],
mcp_servers: vec![],
meta: None,
}
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
self.additional_directories = additional_directories;
self
}
#[must_use]
pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
self.mcp_servers = mcp_servers;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NewSessionResponse {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
#[cfg(feature = "unstable_session_model")]
#[serde(skip_serializing_if = "Option::is_none")]
pub models: Option<SessionModelState>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_options: Option<Vec<SessionConfigOption>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl NewSessionResponse {
#[must_use]
pub fn new(session_id: impl Into<SessionId>) -> Self {
Self {
session_id: session_id.into(),
modes: None,
#[cfg(feature = "unstable_session_model")]
models: None,
config_options: None,
meta: None,
}
}
#[must_use]
pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
self.modes = modes.into_option();
self
}
#[cfg(feature = "unstable_session_model")]
#[must_use]
pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
self.models = models.into_option();
self
}
#[must_use]
pub fn config_options(
mut self,
config_options: impl IntoOption<Vec<SessionConfigOption>>,
) -> Self {
self.config_options = config_options.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct LoadSessionRequest {
pub mcp_servers: Vec<McpServer>,
pub cwd: PathBuf,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_directories: Vec<PathBuf>,
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl LoadSessionRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
Self {
mcp_servers: vec![],
cwd: cwd.into(),
#[cfg(feature = "unstable_session_additional_directories")]
additional_directories: vec![],
session_id: session_id.into(),
meta: None,
}
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
self.additional_directories = additional_directories;
self
}
#[must_use]
pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
self.mcp_servers = mcp_servers;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct LoadSessionResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
#[cfg(feature = "unstable_session_model")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub models: Option<SessionModelState>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config_options: Option<Vec<SessionConfigOption>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl LoadSessionResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
self.modes = modes.into_option();
self
}
#[cfg(feature = "unstable_session_model")]
#[must_use]
pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
self.models = models.into_option();
self
}
#[must_use]
pub fn config_options(
mut self,
config_options: impl IntoOption<Vec<SessionConfigOption>>,
) -> Self {
self.config_options = config_options.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_fork")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ForkSessionRequest {
pub session_id: SessionId,
pub cwd: PathBuf,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_directories: Vec<PathBuf>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mcp_servers: Vec<McpServer>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_fork")]
impl ForkSessionRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
Self {
session_id: session_id.into(),
cwd: cwd.into(),
#[cfg(feature = "unstable_session_additional_directories")]
additional_directories: vec![],
mcp_servers: vec![],
meta: None,
}
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
self.additional_directories = additional_directories;
self
}
#[must_use]
pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
self.mcp_servers = mcp_servers;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_fork")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ForkSessionResponse {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
#[cfg(feature = "unstable_session_model")]
#[serde(skip_serializing_if = "Option::is_none")]
pub models: Option<SessionModelState>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_options: Option<Vec<SessionConfigOption>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_fork")]
impl ForkSessionResponse {
#[must_use]
pub fn new(session_id: impl Into<SessionId>) -> Self {
Self {
session_id: session_id.into(),
modes: None,
#[cfg(feature = "unstable_session_model")]
models: None,
config_options: None,
meta: None,
}
}
#[must_use]
pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
self.modes = modes.into_option();
self
}
#[cfg(feature = "unstable_session_model")]
#[must_use]
pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
self.models = models.into_option();
self
}
#[must_use]
pub fn config_options(
mut self,
config_options: impl IntoOption<Vec<SessionConfigOption>>,
) -> Self {
self.config_options = config_options.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_resume")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ResumeSessionRequest {
pub session_id: SessionId,
pub cwd: PathBuf,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_directories: Vec<PathBuf>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mcp_servers: Vec<McpServer>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_resume")]
impl ResumeSessionRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
Self {
session_id: session_id.into(),
cwd: cwd.into(),
#[cfg(feature = "unstable_session_additional_directories")]
additional_directories: vec![],
mcp_servers: vec![],
meta: None,
}
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
self.additional_directories = additional_directories;
self
}
#[must_use]
pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
self.mcp_servers = mcp_servers;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_resume")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ResumeSessionResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
#[cfg(feature = "unstable_session_model")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub models: Option<SessionModelState>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config_options: Option<Vec<SessionConfigOption>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_resume")]
impl ResumeSessionResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
self.modes = modes.into_option();
self
}
#[cfg(feature = "unstable_session_model")]
#[must_use]
pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
self.models = models.into_option();
self
}
#[must_use]
pub fn config_options(
mut self,
config_options: impl IntoOption<Vec<SessionConfigOption>>,
) -> Self {
self.config_options = config_options.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_close")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct CloseSessionRequest {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_close")]
impl CloseSessionRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>) -> Self {
Self {
session_id: session_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_close")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct CloseSessionResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_close")]
impl CloseSessionResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ListSessionsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<PathBuf>,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_directories: Vec<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl ListSessionsRequest {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
self.cwd = cwd.into_option();
self
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
self.additional_directories = additional_directories;
self
}
#[must_use]
pub fn cursor(mut self, cursor: impl IntoOption<String>) -> Self {
self.cursor = cursor.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ListSessionsResponse {
pub sessions: Vec<SessionInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl ListSessionsResponse {
#[must_use]
pub fn new(sessions: Vec<SessionInfo>) -> Self {
Self {
sessions,
next_cursor: None,
meta: None,
}
}
#[must_use]
pub fn next_cursor(mut self, next_cursor: impl IntoOption<String>) -> Self {
self.next_cursor = next_cursor.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionInfo {
pub session_id: SessionId,
pub cwd: PathBuf,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_directories: Vec<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionInfo {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
Self {
session_id: session_id.into(),
cwd: cwd.into(),
#[cfg(feature = "unstable_session_additional_directories")]
additional_directories: vec![],
title: None,
updated_at: None,
meta: None,
}
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
self.additional_directories = additional_directories;
self
}
#[must_use]
pub fn title(mut self, title: impl IntoOption<String>) -> Self {
self.title = title.into_option();
self
}
#[must_use]
pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
self.updated_at = updated_at.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionModeState {
pub current_mode_id: SessionModeId,
pub available_modes: Vec<SessionMode>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionModeState {
#[must_use]
pub fn new(
current_mode_id: impl Into<SessionModeId>,
available_modes: Vec<SessionMode>,
) -> Self {
Self {
current_mode_id: current_mode_id.into(),
available_modes,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionMode {
pub id: SessionModeId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionMode {
#[must_use]
pub fn new(id: impl Into<SessionModeId>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
description: None,
meta: None,
}
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
#[serde(transparent)]
#[from(Arc<str>, String, &'static str)]
#[non_exhaustive]
pub struct SessionModeId(pub Arc<str>);
impl SessionModeId {
#[must_use]
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionModeRequest {
pub session_id: SessionId,
pub mode_id: SessionModeId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SetSessionModeRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, mode_id: impl Into<SessionModeId>) -> Self {
Self {
session_id: session_id.into(),
mode_id: mode_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionModeResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SetSessionModeResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
#[serde(transparent)]
#[from(Arc<str>, String, &'static str)]
#[non_exhaustive]
pub struct SessionConfigId(pub Arc<str>);
impl SessionConfigId {
#[must_use]
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
#[serde(transparent)]
#[from(Arc<str>, String, &'static str)]
#[non_exhaustive]
pub struct SessionConfigValueId(pub Arc<str>);
impl SessionConfigValueId {
#[must_use]
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
#[serde(transparent)]
#[from(Arc<str>, String, &'static str)]
#[non_exhaustive]
pub struct SessionConfigGroupId(pub Arc<str>);
impl SessionConfigGroupId {
#[must_use]
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionConfigSelectOption {
pub value: SessionConfigValueId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionConfigSelectOption {
#[must_use]
pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
Self {
value: value.into(),
name: name.into(),
description: None,
meta: None,
}
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionConfigSelectGroup {
pub group: SessionConfigGroupId,
pub name: String,
pub options: Vec<SessionConfigSelectOption>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionConfigSelectGroup {
#[must_use]
pub fn new(
group: impl Into<SessionConfigGroupId>,
name: impl Into<String>,
options: Vec<SessionConfigSelectOption>,
) -> Self {
Self {
group: group.into(),
name: name.into(),
options,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(untagged)]
#[non_exhaustive]
pub enum SessionConfigSelectOptions {
Ungrouped(Vec<SessionConfigSelectOption>),
Grouped(Vec<SessionConfigSelectGroup>),
}
impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
fn from(options: Vec<SessionConfigSelectOption>) -> Self {
SessionConfigSelectOptions::Ungrouped(options)
}
}
impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
SessionConfigSelectOptions::Grouped(groups)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionConfigSelect {
pub current_value: SessionConfigValueId,
pub options: SessionConfigSelectOptions,
}
impl SessionConfigSelect {
#[must_use]
pub fn new(
current_value: impl Into<SessionConfigValueId>,
options: impl Into<SessionConfigSelectOptions>,
) -> Self {
Self {
current_value: current_value.into(),
options: options.into(),
}
}
}
#[cfg(feature = "unstable_boolean_config")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionConfigBoolean {
pub current_value: bool,
}
#[cfg(feature = "unstable_boolean_config")]
impl SessionConfigBoolean {
#[must_use]
pub fn new(current_value: bool) -> Self {
Self { current_value }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum SessionConfigOptionCategory {
Mode,
Model,
ThoughtLevel,
#[serde(untagged)]
Other(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
#[schemars(extend("discriminator" = {"propertyName": "type"}))]
#[non_exhaustive]
pub enum SessionConfigKind {
Select(SessionConfigSelect),
#[cfg(feature = "unstable_boolean_config")]
Boolean(SessionConfigBoolean),
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionConfigOption {
pub id: SessionConfigId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<SessionConfigOptionCategory>,
#[serde(flatten)]
pub kind: SessionConfigKind,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionConfigOption {
#[must_use]
pub fn new(
id: impl Into<SessionConfigId>,
name: impl Into<String>,
kind: SessionConfigKind,
) -> Self {
Self {
id: id.into(),
name: name.into(),
description: None,
category: None,
kind,
meta: None,
}
}
#[must_use]
pub fn select(
id: impl Into<SessionConfigId>,
name: impl Into<String>,
current_value: impl Into<SessionConfigValueId>,
options: impl Into<SessionConfigSelectOptions>,
) -> Self {
Self::new(
id,
name,
SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
)
}
#[cfg(feature = "unstable_boolean_config")]
#[must_use]
pub fn boolean(
id: impl Into<SessionConfigId>,
name: impl Into<String>,
current_value: bool,
) -> Self {
Self::new(
id,
name,
SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
)
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
self.category = category.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_boolean_config")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum SessionConfigOptionValue {
Boolean {
value: bool,
},
#[serde(untagged)]
ValueId {
value: SessionConfigValueId,
},
}
#[cfg(feature = "unstable_boolean_config")]
impl SessionConfigOptionValue {
#[must_use]
pub fn value_id(id: impl Into<SessionConfigValueId>) -> Self {
Self::ValueId { value: id.into() }
}
#[must_use]
pub fn boolean(val: bool) -> Self {
Self::Boolean { value: val }
}
#[must_use]
pub fn as_value_id(&self) -> Option<&SessionConfigValueId> {
match self {
Self::ValueId { value } => Some(value),
_ => None,
}
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Boolean { value } => Some(*value),
_ => None,
}
}
}
#[cfg(feature = "unstable_boolean_config")]
impl From<SessionConfigValueId> for SessionConfigOptionValue {
fn from(value: SessionConfigValueId) -> Self {
Self::ValueId { value }
}
}
#[cfg(feature = "unstable_boolean_config")]
impl From<bool> for SessionConfigOptionValue {
fn from(value: bool) -> Self {
Self::Boolean { value }
}
}
#[cfg(feature = "unstable_boolean_config")]
impl From<&str> for SessionConfigOptionValue {
fn from(value: &str) -> Self {
Self::ValueId {
value: SessionConfigValueId::new(value),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionConfigOptionRequest {
pub session_id: SessionId,
pub config_id: SessionConfigId,
#[cfg(feature = "unstable_boolean_config")]
#[serde(flatten)]
pub value: SessionConfigOptionValue,
#[cfg(not(feature = "unstable_boolean_config"))]
pub value: SessionConfigValueId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SetSessionConfigOptionRequest {
#[cfg(feature = "unstable_boolean_config")]
#[must_use]
pub fn new(
session_id: impl Into<SessionId>,
config_id: impl Into<SessionConfigId>,
value: impl Into<SessionConfigOptionValue>,
) -> Self {
Self {
session_id: session_id.into(),
config_id: config_id.into(),
value: value.into(),
meta: None,
}
}
#[cfg(not(feature = "unstable_boolean_config"))]
#[must_use]
pub fn new(
session_id: impl Into<SessionId>,
config_id: impl Into<SessionConfigId>,
value: impl Into<SessionConfigValueId>,
) -> Self {
Self {
session_id: session_id.into(),
config_id: config_id.into(),
value: value.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionConfigOptionResponse {
pub config_options: Vec<SessionConfigOption>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SetSessionConfigOptionResponse {
#[must_use]
pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
Self {
config_options,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum McpServer {
Http(McpServerHttp),
Sse(McpServerSse),
#[serde(untagged)]
Stdio(McpServerStdio),
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct McpServerHttp {
pub name: String,
pub url: String,
pub headers: Vec<HttpHeader>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl McpServerHttp {
#[must_use]
pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
Self {
name: name.into(),
url: url.into(),
headers: Vec::new(),
meta: None,
}
}
#[must_use]
pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
self.headers = headers;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct McpServerSse {
pub name: String,
pub url: String,
pub headers: Vec<HttpHeader>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl McpServerSse {
#[must_use]
pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
Self {
name: name.into(),
url: url.into(),
headers: Vec::new(),
meta: None,
}
}
#[must_use]
pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
self.headers = headers;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct McpServerStdio {
pub name: String,
pub command: PathBuf,
pub args: Vec<String>,
pub env: Vec<EnvVariable>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl McpServerStdio {
#[must_use]
pub fn new(name: impl Into<String>, command: impl Into<PathBuf>) -> Self {
Self {
name: name.into(),
command: command.into(),
args: Vec::new(),
env: Vec::new(),
meta: None,
}
}
#[must_use]
pub fn args(mut self, args: Vec<String>) -> Self {
self.args = args;
self
}
#[must_use]
pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
self.env = env;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct EnvVariable {
pub name: String,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl EnvVariable {
#[must_use]
pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
Self {
name: name.into(),
value: value.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct HttpHeader {
pub name: String,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl HttpHeader {
#[must_use]
pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
Self {
name: name.into(),
value: value.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PromptRequest {
pub session_id: SessionId,
#[cfg(feature = "unstable_message_id")]
#[serde(skip_serializing_if = "Option::is_none")]
pub message_id: Option<String>,
pub prompt: Vec<ContentBlock>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl PromptRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
Self {
session_id: session_id.into(),
#[cfg(feature = "unstable_message_id")]
message_id: None,
prompt,
meta: None,
}
}
#[cfg(feature = "unstable_message_id")]
#[must_use]
pub fn message_id(mut self, message_id: impl IntoOption<String>) -> Self {
self.message_id = message_id.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PromptResponse {
#[cfg(feature = "unstable_message_id")]
#[serde(skip_serializing_if = "Option::is_none")]
pub user_message_id: Option<String>,
pub stop_reason: StopReason,
#[cfg(feature = "unstable_session_usage")]
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl PromptResponse {
#[must_use]
pub fn new(stop_reason: StopReason) -> Self {
Self {
#[cfg(feature = "unstable_message_id")]
user_message_id: None,
stop_reason,
#[cfg(feature = "unstable_session_usage")]
usage: None,
meta: None,
}
}
#[cfg(feature = "unstable_message_id")]
#[must_use]
pub fn user_message_id(mut self, user_message_id: impl IntoOption<String>) -> Self {
self.user_message_id = user_message_id.into_option();
self
}
#[cfg(feature = "unstable_session_usage")]
#[must_use]
pub fn usage(mut self, usage: impl IntoOption<Usage>) -> Self {
self.usage = usage.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum StopReason {
EndTurn,
MaxTokens,
MaxTurnRequests,
Refusal,
Cancelled,
}
#[cfg(feature = "unstable_session_usage")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Usage {
pub total_tokens: u64,
pub input_tokens: u64,
pub output_tokens: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub thought_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_read_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_write_tokens: Option<u64>,
}
#[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,
}
}
#[must_use]
pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
self.thought_tokens = thought_tokens.into_option();
self
}
#[must_use]
pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
self.cached_read_tokens = cached_read_tokens.into_option();
self
}
#[must_use]
pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
self.cached_write_tokens = cached_write_tokens.into_option();
self
}
}
#[cfg(feature = "unstable_session_model")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionModelState {
pub current_model_id: ModelId,
pub available_models: Vec<ModelInfo>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_model")]
impl SessionModelState {
#[must_use]
pub fn new(current_model_id: impl Into<ModelId>, available_models: Vec<ModelInfo>) -> Self {
Self {
current_model_id: current_model_id.into(),
available_models,
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_model")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
#[serde(transparent)]
#[from(Arc<str>, String, &'static str)]
#[non_exhaustive]
pub struct ModelId(pub Arc<str>);
#[cfg(feature = "unstable_session_model")]
impl ModelId {
#[must_use]
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
}
#[cfg(feature = "unstable_session_model")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ModelInfo {
pub model_id: ModelId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_model")]
impl ModelInfo {
#[must_use]
pub fn new(model_id: impl Into<ModelId>, name: impl Into<String>) -> Self {
Self {
model_id: model_id.into(),
name: name.into(),
description: None,
meta: None,
}
}
#[must_use]
pub fn description(mut self, description: impl IntoOption<String>) -> Self {
self.description = description.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_model")]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODEL_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionModelRequest {
pub session_id: SessionId,
pub model_id: ModelId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_model")]
impl SetSessionModelRequest {
#[must_use]
pub fn new(session_id: impl Into<SessionId>, model_id: impl Into<ModelId>) -> Self {
Self {
session_id: session_id.into(),
model_id: model_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_model")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODEL_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionModelResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_model")]
impl SetSessionModelResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AgentCapabilities {
#[serde(default)]
pub load_session: bool,
#[serde(default)]
pub prompt_capabilities: PromptCapabilities,
#[serde(default)]
pub mcp_capabilities: McpCapabilities,
#[serde(default)]
pub session_capabilities: SessionCapabilities,
#[cfg(feature = "unstable_logout")]
#[serde(default)]
pub auth: AgentAuthCapabilities,
#[cfg(feature = "unstable_nes")]
#[serde(skip_serializing_if = "Option::is_none")]
pub nes: Option<NesCapabilities>,
#[cfg(feature = "unstable_nes")]
#[serde(skip_serializing_if = "Option::is_none")]
pub position_encoding: Option<PositionEncodingKind>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl AgentCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn load_session(mut self, load_session: bool) -> Self {
self.load_session = load_session;
self
}
#[must_use]
pub fn prompt_capabilities(mut self, prompt_capabilities: PromptCapabilities) -> Self {
self.prompt_capabilities = prompt_capabilities;
self
}
#[must_use]
pub fn mcp_capabilities(mut self, mcp_capabilities: McpCapabilities) -> Self {
self.mcp_capabilities = mcp_capabilities;
self
}
#[must_use]
pub fn session_capabilities(mut self, session_capabilities: SessionCapabilities) -> Self {
self.session_capabilities = session_capabilities;
self
}
#[cfg(feature = "unstable_logout")]
#[must_use]
pub fn auth(mut self, auth: AgentAuthCapabilities) -> Self {
self.auth = auth;
self
}
#[cfg(feature = "unstable_nes")]
#[must_use]
pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
self.nes = nes.into_option();
self
}
#[cfg(feature = "unstable_nes")]
#[must_use]
pub fn position_encoding(
mut self,
position_encoding: impl IntoOption<PositionEncodingKind>,
) -> Self {
self.position_encoding = position_encoding.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SessionCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub list: Option<SessionListCapabilities>,
#[cfg(feature = "unstable_session_additional_directories")]
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
#[cfg(feature = "unstable_session_fork")]
#[serde(skip_serializing_if = "Option::is_none")]
pub fork: Option<SessionForkCapabilities>,
#[cfg(feature = "unstable_session_resume")]
#[serde(skip_serializing_if = "Option::is_none")]
pub resume: Option<SessionResumeCapabilities>,
#[cfg(feature = "unstable_session_close")]
#[serde(skip_serializing_if = "Option::is_none")]
pub close: Option<SessionCloseCapabilities>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn list(mut self, list: impl IntoOption<SessionListCapabilities>) -> Self {
self.list = list.into_option();
self
}
#[cfg(feature = "unstable_session_additional_directories")]
#[must_use]
pub fn additional_directories(
mut self,
additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
) -> Self {
self.additional_directories = additional_directories.into_option();
self
}
#[cfg(feature = "unstable_session_fork")]
#[must_use]
pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
self.fork = fork.into_option();
self
}
#[cfg(feature = "unstable_session_resume")]
#[must_use]
pub fn resume(mut self, resume: impl IntoOption<SessionResumeCapabilities>) -> Self {
self.resume = resume.into_option();
self
}
#[cfg(feature = "unstable_session_close")]
#[must_use]
pub fn close(mut self, close: impl IntoOption<SessionCloseCapabilities>) -> Self {
self.close = close.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub struct SessionListCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl SessionListCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_additional_directories")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub struct SessionAdditionalDirectoriesCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_additional_directories")]
impl SessionAdditionalDirectoriesCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_fork")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub struct SessionForkCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_fork")]
impl SessionForkCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_resume")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub struct SessionResumeCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_resume")]
impl SessionResumeCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(feature = "unstable_session_close")]
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[non_exhaustive]
pub struct SessionCloseCapabilities {
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
#[cfg(feature = "unstable_session_close")]
impl SessionCloseCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PromptCapabilities {
#[serde(default)]
pub image: bool,
#[serde(default)]
pub audio: bool,
#[serde(default)]
pub embedded_context: bool,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl PromptCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn image(mut self, image: bool) -> Self {
self.image = image;
self
}
#[must_use]
pub fn audio(mut self, audio: bool) -> Self {
self.audio = audio;
self
}
#[must_use]
pub fn embedded_context(mut self, embedded_context: bool) -> Self {
self.embedded_context = embedded_context;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct McpCapabilities {
#[serde(default)]
pub http: bool,
#[serde(default)]
pub sse: bool,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl McpCapabilities {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn http(mut self, http: bool) -> Self {
self.http = http;
self
}
#[must_use]
pub fn sse(mut self, sse: bool) -> Self {
self.sse = sse;
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct AgentMethodNames {
pub initialize: &'static str,
pub authenticate: &'static str,
pub session_new: &'static str,
pub session_load: &'static str,
pub session_set_mode: &'static str,
pub session_set_config_option: &'static str,
pub session_prompt: &'static str,
pub session_cancel: &'static str,
#[cfg(feature = "unstable_session_model")]
pub session_set_model: &'static str,
pub session_list: &'static str,
#[cfg(feature = "unstable_session_fork")]
pub session_fork: &'static str,
#[cfg(feature = "unstable_session_resume")]
pub session_resume: &'static str,
#[cfg(feature = "unstable_session_close")]
pub session_close: &'static str,
#[cfg(feature = "unstable_logout")]
pub logout: &'static str,
#[cfg(feature = "unstable_nes")]
pub nes_start: &'static str,
#[cfg(feature = "unstable_nes")]
pub nes_suggest: &'static str,
#[cfg(feature = "unstable_nes")]
pub nes_accept: &'static str,
#[cfg(feature = "unstable_nes")]
pub nes_reject: &'static str,
#[cfg(feature = "unstable_nes")]
pub nes_close: &'static str,
#[cfg(feature = "unstable_nes")]
pub document_did_open: &'static str,
#[cfg(feature = "unstable_nes")]
pub document_did_change: &'static str,
#[cfg(feature = "unstable_nes")]
pub document_did_close: &'static str,
#[cfg(feature = "unstable_nes")]
pub document_did_save: &'static str,
#[cfg(feature = "unstable_nes")]
pub document_did_focus: &'static str,
}
pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
initialize: INITIALIZE_METHOD_NAME,
authenticate: AUTHENTICATE_METHOD_NAME,
session_new: SESSION_NEW_METHOD_NAME,
session_load: SESSION_LOAD_METHOD_NAME,
session_set_mode: SESSION_SET_MODE_METHOD_NAME,
session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
session_prompt: SESSION_PROMPT_METHOD_NAME,
session_cancel: SESSION_CANCEL_METHOD_NAME,
#[cfg(feature = "unstable_session_model")]
session_set_model: SESSION_SET_MODEL_METHOD_NAME,
session_list: SESSION_LIST_METHOD_NAME,
#[cfg(feature = "unstable_session_fork")]
session_fork: SESSION_FORK_METHOD_NAME,
#[cfg(feature = "unstable_session_resume")]
session_resume: SESSION_RESUME_METHOD_NAME,
#[cfg(feature = "unstable_session_close")]
session_close: SESSION_CLOSE_METHOD_NAME,
#[cfg(feature = "unstable_logout")]
logout: LOGOUT_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
nes_start: NES_START_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
nes_suggest: NES_SUGGEST_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
nes_accept: NES_ACCEPT_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
nes_reject: NES_REJECT_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
nes_close: NES_CLOSE_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
#[cfg(feature = "unstable_nes")]
document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
};
pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
#[cfg(feature = "unstable_session_model")]
pub(crate) const SESSION_SET_MODEL_METHOD_NAME: &str = "session/set_model";
pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
#[cfg(feature = "unstable_session_fork")]
pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
#[cfg(feature = "unstable_session_resume")]
pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
#[cfg(feature = "unstable_session_close")]
pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
#[cfg(feature = "unstable_logout")]
pub(crate) const LOGOUT_METHOD_NAME: &str = "logout";
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
#[schemars(inline)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum ClientRequest {
InitializeRequest(InitializeRequest),
AuthenticateRequest(AuthenticateRequest),
#[cfg(feature = "unstable_logout")]
LogoutRequest(LogoutRequest),
NewSessionRequest(NewSessionRequest),
LoadSessionRequest(LoadSessionRequest),
ListSessionsRequest(ListSessionsRequest),
#[cfg(feature = "unstable_session_fork")]
ForkSessionRequest(ForkSessionRequest),
#[cfg(feature = "unstable_session_resume")]
ResumeSessionRequest(ResumeSessionRequest),
#[cfg(feature = "unstable_session_close")]
CloseSessionRequest(CloseSessionRequest),
SetSessionModeRequest(SetSessionModeRequest),
SetSessionConfigOptionRequest(SetSessionConfigOptionRequest),
PromptRequest(PromptRequest),
#[cfg(feature = "unstable_session_model")]
SetSessionModelRequest(SetSessionModelRequest),
#[cfg(feature = "unstable_nes")]
StartNesRequest(StartNesRequest),
#[cfg(feature = "unstable_nes")]
SuggestNesRequest(SuggestNesRequest),
#[cfg(feature = "unstable_nes")]
CloseNesRequest(CloseNesRequest),
ExtMethodRequest(ExtRequest),
}
impl ClientRequest {
#[must_use]
pub fn method(&self) -> &str {
match self {
Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
Self::AuthenticateRequest(_) => AGENT_METHOD_NAMES.authenticate,
#[cfg(feature = "unstable_logout")]
Self::LogoutRequest(_) => AGENT_METHOD_NAMES.logout,
Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
Self::LoadSessionRequest(_) => AGENT_METHOD_NAMES.session_load,
Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
#[cfg(feature = "unstable_session_fork")]
Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
#[cfg(feature = "unstable_session_resume")]
Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
#[cfg(feature = "unstable_session_close")]
Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
Self::SetSessionModeRequest(_) => AGENT_METHOD_NAMES.session_set_mode,
Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
#[cfg(feature = "unstable_session_model")]
Self::SetSessionModelRequest(_) => AGENT_METHOD_NAMES.session_set_model,
#[cfg(feature = "unstable_nes")]
Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
#[cfg(feature = "unstable_nes")]
Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
#[cfg(feature = "unstable_nes")]
Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
Self::ExtMethodRequest(ext_request) => &ext_request.method,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
#[schemars(inline)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum AgentResponse {
InitializeResponse(InitializeResponse),
AuthenticateResponse(#[serde(default)] AuthenticateResponse),
#[cfg(feature = "unstable_logout")]
LogoutResponse(#[serde(default)] LogoutResponse),
NewSessionResponse(NewSessionResponse),
LoadSessionResponse(#[serde(default)] LoadSessionResponse),
ListSessionsResponse(ListSessionsResponse),
#[cfg(feature = "unstable_session_fork")]
ForkSessionResponse(ForkSessionResponse),
#[cfg(feature = "unstable_session_resume")]
ResumeSessionResponse(#[serde(default)] ResumeSessionResponse),
#[cfg(feature = "unstable_session_close")]
CloseSessionResponse(#[serde(default)] CloseSessionResponse),
SetSessionModeResponse(#[serde(default)] SetSessionModeResponse),
SetSessionConfigOptionResponse(SetSessionConfigOptionResponse),
PromptResponse(PromptResponse),
#[cfg(feature = "unstable_session_model")]
SetSessionModelResponse(#[serde(default)] SetSessionModelResponse),
#[cfg(feature = "unstable_nes")]
StartNesResponse(StartNesResponse),
#[cfg(feature = "unstable_nes")]
SuggestNesResponse(SuggestNesResponse),
#[cfg(feature = "unstable_nes")]
CloseNesResponse(#[serde(default)] CloseNesResponse),
ExtMethodResponse(ExtResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
#[schemars(inline)]
#[non_exhaustive]
pub enum ClientNotification {
CancelNotification(CancelNotification),
#[cfg(feature = "unstable_nes")]
DidOpenDocumentNotification(DidOpenDocumentNotification),
#[cfg(feature = "unstable_nes")]
DidChangeDocumentNotification(DidChangeDocumentNotification),
#[cfg(feature = "unstable_nes")]
DidCloseDocumentNotification(DidCloseDocumentNotification),
#[cfg(feature = "unstable_nes")]
DidSaveDocumentNotification(DidSaveDocumentNotification),
#[cfg(feature = "unstable_nes")]
DidFocusDocumentNotification(DidFocusDocumentNotification),
#[cfg(feature = "unstable_nes")]
AcceptNesNotification(AcceptNesNotification),
#[cfg(feature = "unstable_nes")]
RejectNesNotification(RejectNesNotification),
ExtNotification(ExtNotification),
}
impl ClientNotification {
#[must_use]
pub fn method(&self) -> &str {
match self {
Self::CancelNotification(_) => AGENT_METHOD_NAMES.session_cancel,
#[cfg(feature = "unstable_nes")]
Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
#[cfg(feature = "unstable_nes")]
Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
#[cfg(feature = "unstable_nes")]
Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
#[cfg(feature = "unstable_nes")]
Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
#[cfg(feature = "unstable_nes")]
Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
#[cfg(feature = "unstable_nes")]
Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
#[cfg(feature = "unstable_nes")]
Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
Self::ExtNotification(ext_notification) => &ext_notification.method,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct CancelNotification {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
pub meta: Option<Meta>,
}
impl CancelNotification {
#[must_use]
pub fn new(session_id: impl Into<SessionId>) -> Self {
Self {
session_id: session_id.into(),
meta: None,
}
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg(test)]
mod test_serialization {
use super::*;
use serde_json::json;
#[test]
fn test_mcp_server_stdio_serialization() {
let server = McpServer::Stdio(
McpServerStdio::new("test-server", "/usr/bin/server")
.args(vec!["--port".to_string(), "3000".to_string()])
.env(vec![EnvVariable::new("API_KEY", "secret123")]),
);
let json = serde_json::to_value(&server).unwrap();
assert_eq!(
json,
json!({
"name": "test-server",
"command": "/usr/bin/server",
"args": ["--port", "3000"],
"env": [
{
"name": "API_KEY",
"value": "secret123"
}
]
})
);
let deserialized: McpServer = serde_json::from_value(json).unwrap();
match deserialized {
McpServer::Stdio(McpServerStdio {
name,
command,
args,
env,
meta: _,
}) => {
assert_eq!(name, "test-server");
assert_eq!(command, PathBuf::from("/usr/bin/server"));
assert_eq!(args, vec!["--port", "3000"]);
assert_eq!(env.len(), 1);
assert_eq!(env[0].name, "API_KEY");
assert_eq!(env[0].value, "secret123");
}
_ => panic!("Expected Stdio variant"),
}
}
#[test]
fn test_mcp_server_http_serialization() {
let server = McpServer::Http(
McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
HttpHeader::new("Authorization", "Bearer token123"),
HttpHeader::new("Content-Type", "application/json"),
]),
);
let json = serde_json::to_value(&server).unwrap();
assert_eq!(
json,
json!({
"type": "http",
"name": "http-server",
"url": "https://api.example.com",
"headers": [
{
"name": "Authorization",
"value": "Bearer token123"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
})
);
let deserialized: McpServer = serde_json::from_value(json).unwrap();
match deserialized {
McpServer::Http(McpServerHttp {
name,
url,
headers,
meta: _,
}) => {
assert_eq!(name, "http-server");
assert_eq!(url, "https://api.example.com");
assert_eq!(headers.len(), 2);
assert_eq!(headers[0].name, "Authorization");
assert_eq!(headers[0].value, "Bearer token123");
assert_eq!(headers[1].name, "Content-Type");
assert_eq!(headers[1].value, "application/json");
}
_ => panic!("Expected Http variant"),
}
}
#[test]
fn test_mcp_server_sse_serialization() {
let server = McpServer::Sse(
McpServerSse::new("sse-server", "https://sse.example.com/events")
.headers(vec![HttpHeader::new("X-API-Key", "apikey456")]),
);
let json = serde_json::to_value(&server).unwrap();
assert_eq!(
json,
json!({
"type": "sse",
"name": "sse-server",
"url": "https://sse.example.com/events",
"headers": [
{
"name": "X-API-Key",
"value": "apikey456"
}
]
})
);
let deserialized: McpServer = serde_json::from_value(json).unwrap();
match deserialized {
McpServer::Sse(McpServerSse {
name,
url,
headers,
meta: _,
}) => {
assert_eq!(name, "sse-server");
assert_eq!(url, "https://sse.example.com/events");
assert_eq!(headers.len(), 1);
assert_eq!(headers[0].name, "X-API-Key");
assert_eq!(headers[0].value, "apikey456");
}
_ => panic!("Expected Sse variant"),
}
}
#[test]
fn test_session_config_option_category_known_variants() {
assert_eq!(
serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
json!("mode")
);
assert_eq!(
serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
json!("model")
);
assert_eq!(
serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
json!("thought_level")
);
assert_eq!(
serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
SessionConfigOptionCategory::Mode
);
assert_eq!(
serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
SessionConfigOptionCategory::Model
);
assert_eq!(
serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
SessionConfigOptionCategory::ThoughtLevel
);
}
#[test]
fn test_session_config_option_category_unknown_variants() {
let unknown: SessionConfigOptionCategory =
serde_json::from_str("\"some_future_category\"").unwrap();
assert_eq!(
unknown,
SessionConfigOptionCategory::Other("some_future_category".to_string())
);
let json = serde_json::to_value(&unknown).unwrap();
assert_eq!(json, json!("some_future_category"));
}
#[test]
fn test_session_config_option_category_custom_categories() {
let custom: SessionConfigOptionCategory =
serde_json::from_str("\"_my_custom_category\"").unwrap();
assert_eq!(
custom,
SessionConfigOptionCategory::Other("_my_custom_category".to_string())
);
let json = serde_json::to_value(&custom).unwrap();
assert_eq!(json, json!("_my_custom_category"));
let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
assert_eq!(
deserialized,
SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
);
}
#[test]
fn test_auth_method_agent_serialization() {
let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
let json = serde_json::to_value(&method).unwrap();
assert_eq!(
json,
json!({
"id": "default-auth",
"name": "Default Auth"
})
);
assert!(!json.as_object().unwrap().contains_key("description"));
assert!(!json.as_object().unwrap().contains_key("type"));
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
match deserialized {
AuthMethod::Agent(AuthMethodAgent { id, name, .. }) => {
assert_eq!(id.0.as_ref(), "default-auth");
assert_eq!(name, "Default Auth");
}
#[cfg(feature = "unstable_auth_methods")]
_ => panic!("Expected Agent variant"),
}
}
#[test]
fn test_auth_method_explicit_agent_deserialization() {
let json = json!({
"id": "agent-auth",
"name": "Agent Auth",
"type": "agent"
});
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
assert!(matches!(deserialized, AuthMethod::Agent(_)));
}
#[cfg(feature = "unstable_session_additional_directories")]
#[test]
fn test_session_additional_directories_serialization() {
assert_eq!(
serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
json!({
"cwd": "/home/user/project",
"mcpServers": []
})
);
assert_eq!(
serde_json::to_value(
NewSessionRequest::new("/home/user/project").additional_directories(vec![
PathBuf::from("/home/user/shared-lib"),
PathBuf::from("/home/user/product-docs"),
])
)
.unwrap(),
json!({
"cwd": "/home/user/project",
"additionalDirectories": [
"/home/user/shared-lib",
"/home/user/product-docs"
],
"mcpServers": []
})
);
assert_eq!(
serde_json::to_value(
ListSessionsRequest::new().additional_directories(Vec::<PathBuf>::new())
)
.unwrap(),
json!({})
);
assert_eq!(
serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
json!({
"sessionId": "sess_abc123",
"cwd": "/home/user/project"
})
);
assert_eq!(
serde_json::to_value(
SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
PathBuf::from("/home/user/shared-lib"),
PathBuf::from("/home/user/product-docs"),
])
)
.unwrap(),
json!({
"sessionId": "sess_abc123",
"cwd": "/home/user/project",
"additionalDirectories": [
"/home/user/shared-lib",
"/home/user/product-docs"
]
})
);
assert_eq!(
serde_json::from_value::<SessionInfo>(json!({
"sessionId": "sess_abc123",
"cwd": "/home/user/project"
}))
.unwrap()
.additional_directories,
Vec::<PathBuf>::new()
);
assert_eq!(
serde_json::from_value::<ListSessionsRequest>(json!({}))
.unwrap()
.additional_directories,
Vec::<PathBuf>::new()
);
assert_eq!(
serde_json::from_value::<ListSessionsRequest>(json!({
"additionalDirectories": []
}))
.unwrap()
.additional_directories,
Vec::<PathBuf>::new()
);
}
#[cfg(feature = "unstable_session_additional_directories")]
#[test]
fn test_session_additional_directories_capabilities_serialization() {
assert_eq!(
serde_json::to_value(
SessionCapabilities::new()
.additional_directories(SessionAdditionalDirectoriesCapabilities::new())
)
.unwrap(),
json!({
"additionalDirectories": {}
})
);
}
#[cfg(feature = "unstable_auth_methods")]
#[test]
fn test_auth_method_env_var_serialization() {
let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
"api-key",
"API Key",
vec![AuthEnvVar::new("API_KEY")],
));
let json = serde_json::to_value(&method).unwrap();
assert_eq!(
json,
json!({
"id": "api-key",
"name": "API Key",
"type": "env_var",
"vars": [{"name": "API_KEY"}]
})
);
assert!(!json["vars"][0].as_object().unwrap().contains_key("secret"));
assert!(
!json["vars"][0]
.as_object()
.unwrap()
.contains_key("optional")
);
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
match deserialized {
AuthMethod::EnvVar(AuthMethodEnvVar {
id,
name: method_name,
vars,
link,
..
}) => {
assert_eq!(id.0.as_ref(), "api-key");
assert_eq!(method_name, "API Key");
assert_eq!(vars.len(), 1);
assert_eq!(vars[0].name, "API_KEY");
assert!(vars[0].secret);
assert!(!vars[0].optional);
assert!(link.is_none());
}
_ => panic!("Expected EnvVar variant"),
}
}
#[cfg(feature = "unstable_auth_methods")]
#[test]
fn test_auth_method_env_var_with_link_serialization() {
let method = AuthMethod::EnvVar(
AuthMethodEnvVar::new("api-key", "API Key", vec![AuthEnvVar::new("API_KEY")])
.link("https://example.com/keys"),
);
let json = serde_json::to_value(&method).unwrap();
assert_eq!(
json,
json!({
"id": "api-key",
"name": "API Key",
"type": "env_var",
"vars": [{"name": "API_KEY"}],
"link": "https://example.com/keys"
})
);
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
match deserialized {
AuthMethod::EnvVar(AuthMethodEnvVar { link, .. }) => {
assert_eq!(link.as_deref(), Some("https://example.com/keys"));
}
_ => panic!("Expected EnvVar variant"),
}
}
#[cfg(feature = "unstable_auth_methods")]
#[test]
fn test_auth_method_env_var_multiple_vars() {
let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
"azure-openai",
"Azure OpenAI",
vec![
AuthEnvVar::new("AZURE_OPENAI_API_KEY").label("API Key"),
AuthEnvVar::new("AZURE_OPENAI_ENDPOINT")
.label("Endpoint URL")
.secret(false),
AuthEnvVar::new("AZURE_OPENAI_API_VERSION")
.label("API Version")
.secret(false)
.optional(true),
],
));
let json = serde_json::to_value(&method).unwrap();
assert_eq!(
json,
json!({
"id": "azure-openai",
"name": "Azure OpenAI",
"type": "env_var",
"vars": [
{"name": "AZURE_OPENAI_API_KEY", "label": "API Key"},
{"name": "AZURE_OPENAI_ENDPOINT", "label": "Endpoint URL", "secret": false},
{"name": "AZURE_OPENAI_API_VERSION", "label": "API Version", "secret": false, "optional": true}
]
})
);
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
match deserialized {
AuthMethod::EnvVar(AuthMethodEnvVar { vars, .. }) => {
assert_eq!(vars.len(), 3);
assert_eq!(vars[0].name, "AZURE_OPENAI_API_KEY");
assert_eq!(vars[0].label.as_deref(), Some("API Key"));
assert!(vars[0].secret);
assert!(!vars[0].optional);
assert_eq!(vars[1].name, "AZURE_OPENAI_ENDPOINT");
assert!(!vars[1].secret);
assert!(!vars[1].optional);
assert_eq!(vars[2].name, "AZURE_OPENAI_API_VERSION");
assert!(!vars[2].secret);
assert!(vars[2].optional);
}
_ => panic!("Expected EnvVar variant"),
}
}
#[cfg(feature = "unstable_auth_methods")]
#[test]
fn test_auth_method_terminal_serialization() {
let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
let json = serde_json::to_value(&method).unwrap();
assert_eq!(
json,
json!({
"id": "tui-auth",
"name": "Terminal Auth",
"type": "terminal"
})
);
assert!(!json.as_object().unwrap().contains_key("args"));
assert!(!json.as_object().unwrap().contains_key("env"));
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
match deserialized {
AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
assert!(args.is_empty());
assert!(env.is_empty());
}
_ => panic!("Expected Terminal variant"),
}
}
#[cfg(feature = "unstable_auth_methods")]
#[test]
fn test_auth_method_terminal_with_args_and_env_serialization() {
use std::collections::HashMap;
let mut env = HashMap::new();
env.insert("TERM".to_string(), "xterm-256color".to_string());
let method = AuthMethod::Terminal(
AuthMethodTerminal::new("tui-auth", "Terminal Auth")
.args(vec!["--interactive".to_string(), "--color".to_string()])
.env(env),
);
let json = serde_json::to_value(&method).unwrap();
assert_eq!(
json,
json!({
"id": "tui-auth",
"name": "Terminal Auth",
"type": "terminal",
"args": ["--interactive", "--color"],
"env": {
"TERM": "xterm-256color"
}
})
);
let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
match deserialized {
AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
assert_eq!(args, vec!["--interactive", "--color"]);
assert_eq!(env.len(), 1);
assert_eq!(env.get("TERM").unwrap(), "xterm-256color");
}
_ => panic!("Expected Terminal variant"),
}
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_id_serialize() {
let val = SessionConfigOptionValue::value_id("model-1");
let json = serde_json::to_value(&val).unwrap();
assert_eq!(json, json!({ "value": "model-1" }));
assert!(!json.as_object().unwrap().contains_key("type"));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_boolean_serialize() {
let val = SessionConfigOptionValue::boolean(true);
let json = serde_json::to_value(&val).unwrap();
assert_eq!(json, json!({ "type": "boolean", "value": true }));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_deserialize_no_type() {
let json = json!({ "value": "model-1" });
let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
assert_eq!(val, SessionConfigOptionValue::value_id("model-1"));
assert_eq!(val.as_value_id().unwrap().to_string(), "model-1");
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_deserialize_boolean() {
let json = json!({ "type": "boolean", "value": true });
let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
assert_eq!(val, SessionConfigOptionValue::boolean(true));
assert_eq!(val.as_bool(), Some(true));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_deserialize_boolean_false() {
let json = json!({ "type": "boolean", "value": false });
let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
assert_eq!(val, SessionConfigOptionValue::boolean(false));
assert_eq!(val.as_bool(), Some(false));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
let json = json!({ "type": "text", "value": "freeform input" });
let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
assert_eq!(val.as_value_id().unwrap().to_string(), "freeform input");
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_roundtrip_value_id() {
let original = SessionConfigOptionValue::value_id("option-a");
let json = serde_json::to_value(&original).unwrap();
let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
assert_eq!(original, roundtripped);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_roundtrip_boolean() {
let original = SessionConfigOptionValue::boolean(false);
let json = serde_json::to_value(&original).unwrap();
let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
assert_eq!(original, roundtripped);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_type_mismatch_boolean_with_string() {
let json = json!({ "type": "boolean", "value": "not a bool" });
let result = serde_json::from_value::<SessionConfigOptionValue>(json);
assert!(result.is_ok());
assert_eq!(
result.unwrap().as_value_id().unwrap().to_string(),
"not a bool"
);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_value_from_impls() {
let from_str: SessionConfigOptionValue = "model-1".into();
assert_eq!(from_str.as_value_id().unwrap().to_string(), "model-1");
let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
assert_eq!(from_id.as_value_id().unwrap().to_string(), "model-2");
let from_bool: SessionConfigOptionValue = true.into();
assert_eq!(from_bool.as_bool(), Some(true));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_set_session_config_option_request_value_id() {
let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
let json = serde_json::to_value(&req).unwrap();
assert_eq!(
json,
json!({
"sessionId": "sess_1",
"configId": "model",
"value": "model-1"
})
);
assert!(!json.as_object().unwrap().contains_key("type"));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_set_session_config_option_request_boolean() {
let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
let json = serde_json::to_value(&req).unwrap();
assert_eq!(
json,
json!({
"sessionId": "sess_1",
"configId": "brave_mode",
"type": "boolean",
"value": true
})
);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_set_session_config_option_request_deserialize_no_type() {
let json = json!({
"sessionId": "sess_1",
"configId": "model",
"value": "model-1"
});
let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.session_id.to_string(), "sess_1");
assert_eq!(req.config_id.to_string(), "model");
assert_eq!(req.value.as_value_id().unwrap().to_string(), "model-1");
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_set_session_config_option_request_deserialize_boolean() {
let json = json!({
"sessionId": "sess_1",
"configId": "brave_mode",
"type": "boolean",
"value": true
});
let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.value.as_bool(), Some(true));
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_set_session_config_option_request_roundtrip_value_id() {
let original = SetSessionConfigOptionRequest::new("s", "c", "v");
let json = serde_json::to_value(&original).unwrap();
let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
assert_eq!(original, roundtripped);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_set_session_config_option_request_roundtrip_boolean() {
let original = SetSessionConfigOptionRequest::new("s", "c", false);
let json = serde_json::to_value(&original).unwrap();
let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
assert_eq!(original, roundtripped);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_boolean_serialization() {
let cfg = SessionConfigBoolean::new(true);
let json = serde_json::to_value(&cfg).unwrap();
assert_eq!(json, json!({ "currentValue": true }));
let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
assert!(deserialized.current_value);
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_boolean_variant() {
let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
.description("Skip confirmation prompts");
let json = serde_json::to_value(&opt).unwrap();
assert_eq!(
json,
json!({
"id": "brave_mode",
"name": "Brave Mode",
"description": "Skip confirmation prompts",
"type": "boolean",
"currentValue": false
})
);
let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
assert_eq!(deserialized.id.to_string(), "brave_mode");
assert_eq!(deserialized.name, "Brave Mode");
match deserialized.kind {
SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
_ => panic!("Expected Boolean kind"),
}
}
#[cfg(feature = "unstable_boolean_config")]
#[test]
fn test_session_config_option_select_still_works() {
let opt = SessionConfigOption::select(
"model",
"Model",
"model-1",
vec![
SessionConfigSelectOption::new("model-1", "Model 1"),
SessionConfigSelectOption::new("model-2", "Model 2"),
],
);
let json = serde_json::to_value(&opt).unwrap();
assert_eq!(json["type"], "select");
assert_eq!(json["currentValue"], "model-1");
assert_eq!(json["options"].as_array().unwrap().len(), 2);
let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
match deserialized.kind {
SessionConfigKind::Select(ref s) => {
assert_eq!(s.current_value.to_string(), "model-1");
}
_ => panic!("Expected Select kind"),
}
}
}