{
  "name": "forge-provider-session",
  "language": "rust",
  "version": "0.2.0",
  "description": "Provider session model, policy, and runtime routing for the Forge SDK",
  "manifest": "forge-rs/crates/forge-provider-session/Cargo.toml",
  "manifestSha256": "3a9da17d7636cc828e97739de461561ce01d9c9159e896501c6af8979e4f0b65",
  "status": "source-reference",
  "registryPublicationVerified": false,
  "route": "/libraries/rust/forge-provider-session",
  "features": {},
  "files": [
    {
      "path": "forge-rs/crates/forge-provider-session/src/error.rs",
      "sha256": "ac1d98ece9f023373709204a0e7515fe3719d0b2ea759c8b140e57f01c138911",
      "artifactSha256": "b573f7ffa401725b7fdbad75c3c78dd97508fc1483ffb71b13aa8d2171525393",
      "url": "/reference/source/forge-rs/crates/forge-provider-session/src/error.rs.txt",
      "declarations": [
        {
          "name": "::ProviderSessionError",
          "line": 13,
          "signature": "#[derive(Debug, Error)]\npub enum ProviderSessionError {\n    /// The requested provider is not allowed by the active session policy.\n    #[error(\n        \"provider '{provider_ref}' is not allowed by session policy '{policy_name}': {reason}\"\n    )]\n    ProviderNotAllowed {\n        /// The provider reference that was denied.\n        provider_ref: String,\n        /// The name of the policy that denied the request.\n        policy_name: String,\n        /// Human-readable explanation of why the provider was denied.\n        reason: String,\n    },\n\n    /// A session was not found for the given ID.\n    #[error(\"session '{session_id}' not found in session manager\")]\n    SessionNotFound {\n        /// The session ID that was looked up.\n        session_id: String,\n    },\n\n    /// An invalid session state transition was attempted.\n    #[error(\"invalid session transition from {from:?} to {to:?}: {reason}\")]\n    InvalidSessionTransition {\n        /// The current session state.\n        from: super::session::SessionState,\n        /// The attempted target state.\n        to: super::session::SessionState,\n        /// Human-readable explanation of why the transition is invalid.\n        reason: String,\n    },\n\n    /// A session has expired and cannot be used.\n    #[error(\"session '{session_id}' expired at {expired_at}; create a new session\")]\n    SessionExpired {\n        /// The expired session ID.\n        session_id: String,\n        /// When the session expired (ISO 8601).\n        expired_at: String,\n    },\n\n    /// A runtime provider swap was attempted but the policy forbids it.\n    #[error(\n        \"runtime provider swap from '{from_provider}' to '{to_provider}' denied by policy '{policy_name}': runtime swapping is disabled\"\n    )]\n    RuntimeSwapDenied {\n        /// The current provider.\n        from_provider: String,\n        /// The requested new provider.\n        to_provider: String,\n        /// The policy that denied the swap.\n        policy_name: String,\n    },\n\n    /// No default provider is configured in the session policy.\n    #[error(\"no default provider configured in session policy '{policy_name}'; set a default_provider in the SessionPolicy\")]\n    NoDefaultProvider {\n        /// The policy that lacks a default provider.\n        policy_name: String,\n    },\n\n    /// The session policy is invalid.\n    #[error(\"invalid session policy: {reason}\")]\n    InvalidPolicy {\n        /// What is wrong with the policy.\n        reason: String,\n    },\n\n    /// A Forge core error occurred.\n    #[error(\"forge core error: {0}\")]\n    ForgeCore(#[from] forge_core::error::ForgeError),\n}",
          "documentation": "Errors that can occur during provider session operations.\n\nEvery variant includes enough context to diagnose the issue without reading\nsource code."
        },
        {
          "name": "::ProviderSessionResult",
          "line": 87,
          "signature": "pub type ProviderSessionResult<T> = Result<T, ProviderSessionError>;",
          "documentation": "A specialized `Result` type for provider session operations."
        }
      ]
    },
    {
      "path": "forge-rs/crates/forge-provider-session/src/lib.rs",
      "sha256": "a5d505a751ad43eb2dac652fb76ce49bdf504a5086cc7768e4699fa0f4843f82",
      "artifactSha256": "42dd753916143f4f23dab992ddd8de4b0114c7e9ad3999eb35ce858b958cd4ef",
      "url": "/reference/source/forge-rs/crates/forge-provider-session/src/lib.rs.txt",
      "declarations": [
        {
          "name": "error",
          "line": 62,
          "signature": "pub mod error;",
          "documentation": ""
        },
        {
          "name": "policy",
          "line": 63,
          "signature": "pub mod policy;",
          "documentation": ""
        },
        {
          "name": "session",
          "line": 64,
          "signature": "pub mod session;",
          "documentation": ""
        },
        {
          "name": "pub use error::{ProviderSessionError, ProviderSessionResult};",
          "line": 66,
          "signature": "pub use error::{ProviderSessionError, ProviderSessionResult};",
          "documentation": ""
        },
        {
          "name": "pub use policy::{\n    PolicyDecision, PolicyEngine, ProviderType, SessionPolicy, SessionPolicyBuilder,\n    TaskRoutingRule,\n};",
          "line": 67,
          "signature": "pub use policy::{\n    PolicyDecision, PolicyEngine, ProviderType, SessionPolicy, SessionPolicyBuilder,\n    TaskRoutingRule,\n};",
          "documentation": ""
        },
        {
          "name": "pub use session::{\n    ProviderSession, SessionConfig, SessionId, SessionManager, SessionState, SwapOutcome,\n};",
          "line": 71,
          "signature": "pub use session::{\n    ProviderSession, SessionConfig, SessionId, SessionManager, SessionState, SwapOutcome,\n};",
          "documentation": ""
        }
      ]
    },
    {
      "path": "forge-rs/crates/forge-provider-session/src/policy.rs",
      "sha256": "61f4f958c119651a68c686160329ee6d579813afea93e73231c93780b0997355",
      "artifactSha256": "a7150b1fe61d018989cc2828264dc8dc06efbb2fef9d96c7c6776588587b3b7c",
      "url": "/reference/source/forge-rs/crates/forge-provider-session/src/policy.rs.txt",
      "declarations": [
        {
          "name": "::ProviderType",
          "line": 38,
          "signature": "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\npub enum ProviderType {\n    /// Direct model provider (Anthropic, OpenAI, Google, etc.).\n    DirectModel,\n    /// Local model provider (Ollama, llama.cpp, etc.).\n    Local,\n    /// Routed through a gateway (Foundry, OpenRouter, etc.).\n    Routed,\n    /// Coding subscription provider (Claude Code, Codex, etc.).\n    CodingSubscription,\n}",
          "documentation": "Classification of provider types.\n\nProvider types inform session management about the nature of the\nprovider connection and what session semantics apply.\n\n# Examples\n\n```\nuse forge_provider_session::ProviderType;\n\nlet pt = ProviderType::DirectModel;\nassert_eq!(format!(\"{pt:?}\"), \"DirectModel\");\n```"
        },
        {
          "name": "::TaskRoutingRule",
          "line": 69,
          "signature": "#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct TaskRoutingRule {\n/// If set, this rule only matches the given task mode.\n\n#[serde(default, skip_serializing_if = \"Option::is_none\")]\npub task_mode: Option<TaskMode>,\n/// If set, this rule only matches the given domain string.\n\n#[serde(default, skip_serializing_if = \"Option::is_none\")]\npub domain: Option<String>,\n/// The provider to use when this rule matches.\n\npub provider_ref: String\n}",
          "documentation": "A routing rule that maps task characteristics to a specific provider.\n\nRules are evaluated in order. The first rule whose conditions match\nthe routing context wins. Both `task_mode` and `domain` are optional;\nif both are `None`, the rule matches everything (use as fallback).\n\n# Examples\n\n```\nuse forge_provider_session::TaskRoutingRule;\nuse forge_core::routing::TaskMode;\n\nlet rule = TaskRoutingRule {\n    task_mode: Some(TaskMode::Planning),\n    domain: None,\n    provider_ref: \"anthropic:claude-opus-4\".to_string(),\n};\nassert!(rule.matches_mode(&TaskMode::Planning));\n```"
        },
        {
          "name": "::TaskRoutingRule::matches_mode",
          "line": 86,
          "signature": "pub fn matches_mode(&self, mode: &TaskMode) -> bool;",
          "documentation": "Returns `true` if this rule matches the given task mode.\n\nA rule with `task_mode: None` matches any task mode."
        },
        {
          "name": "::TaskRoutingRule::matches_domain",
          "line": 96,
          "signature": "pub fn matches_domain(&self, domain: &str) -> bool;",
          "documentation": "Returns `true` if this rule matches the given domain.\n\nA rule with `domain: None` matches any domain."
        },
        {
          "name": "::TaskRoutingRule::matches",
          "line": 106,
          "signature": "pub fn matches(&self, task_mode: Option<&TaskMode>, domain: Option<&str>) -> bool;",
          "documentation": "Returns `true` if this rule matches the given context.\n\nBoth conditions must match. `None` conditions are treated as wildcards."
        },
        {
          "name": "::SessionPolicy",
          "line": 153,
          "signature": "#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct SessionPolicy {\n\n}",
          "documentation": "Declarative session policy for provider selection and switching.\n\nA `SessionPolicy` defines which providers are available, which is the\ndefault, what task-level routing rules apply, and whether runtime\nswapping is allowed.\n\n# Examples\n\n```\nuse forge_provider_session::{SessionPolicy, TaskRoutingRule};\nuse forge_core::routing::TaskMode;\n\nlet policy = SessionPolicy::builder()\n    .name(\"engineering-team\")\n    .default_provider(\"anthropic:claude-sonnet-4-5-20250929\")\n    .allowed_provider(\"anthropic:claude-sonnet-4-5-20250929\")\n    .allowed_provider(\"anthropic:claude-opus-4\")\n    .allowed_provider(\"openai:gpt-4o\")\n    .task_route(TaskRoutingRule {\n        task_mode: Some(TaskMode::Planning),\n        domain: None,\n        provider_ref: \"anthropic:claude-opus-4\".to_string(),\n    })\n    .allow_runtime_swap(true)\n    .session_ttl_seconds(3600)\n    .build()\n    .unwrap();\n\nassert_eq!(policy.name(), \"engineering-team\");\nassert!(policy.is_provider_allowed(\"anthropic:claude-opus-4\"));\n```"
        },
        {
          "name": "::SessionPolicy::builder",
          "line": 164,
          "signature": "pub fn builder() -> SessionPolicyBuilder;",
          "documentation": "Returns a builder for constructing a `SessionPolicy`."
        },
        {
          "name": "::SessionPolicy::name",
          "line": 169,
          "signature": "pub fn name(&self) -> &str;",
          "documentation": "Returns the policy name."
        },
        {
          "name": "::SessionPolicy::default_provider",
          "line": 174,
          "signature": "pub fn default_provider(&self) -> Option<&str>;",
          "documentation": "Returns the default provider reference, if set."
        },
        {
          "name": "::SessionPolicy::is_provider_allowed",
          "line": 181,
          "signature": "pub fn is_provider_allowed(&self, provider_ref: &str) -> bool;",
          "documentation": "Returns `true` if the given provider is allowed by this policy.\n\nIf the allowed list is empty, all providers are allowed (open policy)."
        },
        {
          "name": "::SessionPolicy::allow_runtime_swap",
          "line": 189,
          "signature": "pub fn allow_runtime_swap(&self) -> bool;",
          "documentation": "Returns `true` if runtime provider swapping is allowed."
        },
        {
          "name": "::SessionPolicy::session_ttl_seconds",
          "line": 194,
          "signature": "pub fn session_ttl_seconds(&self) -> Option<u64>;",
          "documentation": "Returns the session TTL in seconds, if configured."
        },
        {
          "name": "::SessionPolicy::task_routing_rules",
          "line": 199,
          "signature": "pub fn task_routing_rules(&self) -> &[TaskRoutingRule];",
          "documentation": "Returns the task routing rules."
        },
        {
          "name": "::SessionPolicy::allowed_providers",
          "line": 204,
          "signature": "pub fn allowed_providers(&self) -> &[String];",
          "documentation": "Returns the list of allowed providers."
        },
        {
          "name": "::SessionPolicy::open",
          "line": 219,
          "signature": "pub fn open(name: impl Into<String>) -> Self;",
          "documentation": "Creates a permissive policy that allows all providers with no restrictions.\n\n# Examples\n\n```\nuse forge_provider_session::SessionPolicy;\n\nlet policy = SessionPolicy::open(\"development\");\nassert!(policy.is_provider_allowed(\"anything:goes\"));\nassert!(policy.allow_runtime_swap());\n```"
        },
        {
          "name": "::SessionPolicyBuilder",
          "line": 246,
          "signature": "#[derive(Debug, Default)]\npub struct SessionPolicyBuilder {\n\n}",
          "documentation": "Builder for [`SessionPolicy`].\n\n# Examples\n\n```\nuse forge_provider_session::SessionPolicy;\n\nlet policy = SessionPolicy::builder()\n    .name(\"my-policy\")\n    .default_provider(\"openai:gpt-4o\")\n    .allow_runtime_swap(false)\n    .build()\n    .unwrap();\n```"
        },
        {
          "name": "::SessionPolicyBuilder::new",
          "line": 257,
          "signature": "pub fn new() -> Self;",
          "documentation": "Creates a new builder with default values."
        },
        {
          "name": "::SessionPolicyBuilder::name",
          "line": 262,
          "signature": "pub fn name(mut self, name: impl Into<String>) -> Self;",
          "documentation": "Sets the policy name."
        },
        {
          "name": "::SessionPolicyBuilder::default_provider",
          "line": 268,
          "signature": "pub fn default_provider(mut self, provider_ref: impl Into<String>) -> Self;",
          "documentation": "Sets the default provider reference."
        },
        {
          "name": "::SessionPolicyBuilder::allowed_provider",
          "line": 274,
          "signature": "pub fn allowed_provider(mut self, provider_ref: impl Into<String>) -> Self;",
          "documentation": "Adds a provider to the allowed list."
        },
        {
          "name": "::SessionPolicyBuilder::task_route",
          "line": 280,
          "signature": "pub fn task_route(mut self, rule: TaskRoutingRule) -> Self;",
          "documentation": "Adds a task routing rule."
        },
        {
          "name": "::SessionPolicyBuilder::allow_runtime_swap",
          "line": 286,
          "signature": "pub fn allow_runtime_swap(mut self, allowed: bool) -> Self;",
          "documentation": "Sets whether runtime provider swapping is allowed."
        },
        {
          "name": "::SessionPolicyBuilder::session_ttl_seconds",
          "line": 292,
          "signature": "pub fn session_ttl_seconds(mut self, ttl: u64) -> Self;",
          "documentation": "Sets the session TTL in seconds."
        },
        {
          "name": "::SessionPolicyBuilder::build",
          "line": 307,
          "signature": "pub fn build(self) -> ProviderSessionResult<SessionPolicy>;",
          "documentation": "Builds the `SessionPolicy`, validating all fields.\n\n# Errors\n\nReturns `ProviderSessionError::InvalidPolicy` if:\n- The policy name is missing or empty.\n- The default provider is set but not in the allowed list (when the\n  allowed list is non-empty).\n- A task routing rule references a provider not in the allowed list\n  (when the allowed list is non-empty)."
        },
        {
          "name": "::PolicyDecision",
          "line": 372,
          "signature": "#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct PolicyDecision {\n/// The selected provider reference.\n\npub provider_ref: String,\n/// Human-readable explanation of why this provider was selected.\n\npub reason: String,\n/// Whether runtime swapping is allowed under the current policy.\n\npub runtime_swap_allowed: bool\n}",
          "documentation": "The output of a policy evaluation: which provider to use and why.\n\n# Examples\n\n```\nuse forge_provider_session::{PolicyDecision, PolicyEngine, SessionPolicy};\n\nlet policy = SessionPolicy::builder()\n    .default_provider(\"openai:gpt-4o\")\n    .build()\n    .unwrap();\nlet engine = PolicyEngine::new(policy);\nlet decision = engine.evaluate_default();\nassert_eq!(decision.provider_ref, \"openai:gpt-4o\");\nassert_eq!(decision.reason, \"default provider from policy\");\n```"
        },
        {
          "name": "::PolicyEngine",
          "line": 413,
          "signature": "pub struct PolicyEngine {\n\n}",
          "documentation": "Evaluates [`SessionPolicy`] rules against routing context.\n\nThe policy engine is the decision-maker for provider selection. It\ntakes a policy and context (task mode, domain, etc.) and produces\na [`PolicyDecision`].\n\n# Examples\n\n```\nuse forge_provider_session::{PolicyEngine, SessionPolicy, TaskRoutingRule};\nuse forge_core::routing::TaskMode;\n\nlet policy = SessionPolicy::builder()\n    .default_provider(\"openai:gpt-4o\")\n    .task_route(TaskRoutingRule {\n        task_mode: Some(TaskMode::Planning),\n        domain: None,\n        provider_ref: \"anthropic:claude-opus-4\".to_string(),\n    })\n    .build()\n    .unwrap();\n\nlet engine = PolicyEngine::new(policy);\n\n// Task-level routing overrides default\nlet decision = engine.evaluate(Some(&TaskMode::Planning), None);\nassert_eq!(decision.provider_ref, \"anthropic:claude-opus-4\");\n\n// Falls back to default for unmatched task modes\nlet decision = engine.evaluate(Some(&TaskMode::Execution), None);\nassert_eq!(decision.provider_ref, \"openai:gpt-4o\");\n```"
        },
        {
          "name": "::PolicyEngine::new",
          "line": 419,
          "signature": "pub fn new(policy: SessionPolicy) -> Self;",
          "documentation": "Creates a new policy engine with the given policy."
        },
        {
          "name": "::PolicyEngine::policy",
          "line": 424,
          "signature": "pub fn policy(&self) -> &SessionPolicy;",
          "documentation": "Returns a reference to the underlying policy."
        },
        {
          "name": "::PolicyEngine::evaluate_default",
          "line": 434,
          "signature": "pub fn evaluate_default(&self) -> PolicyDecision;",
          "documentation": "Evaluates the policy with the default context (no task mode, no domain).\n\n# Returns\n\nA `PolicyDecision` with the default provider, or a fallback decision\nif no default is configured."
        },
        {
          "name": "::PolicyEngine::evaluate",
          "line": 453,
          "signature": "pub fn evaluate(&self, task_mode: Option<&TaskMode>, domain: Option<&str>) -> PolicyDecision;",
          "documentation": "Evaluates the policy against the given task mode and domain.\n\nEvaluation order:\n1. Task routing rules (first match wins)\n2. Default provider\n3. Fallback (first allowed provider, or error sentinel)\n\n# Arguments\n\n* `task_mode` - The current task mode, if known.\n* `domain` - The current domain, if known.\n\n# Returns\n\nA `PolicyDecision` identifying the provider to use and why."
        },
        {
          "name": "::PolicyEngine::check_provider_allowed",
          "line": 505,
          "signature": "pub fn check_provider_allowed(&self, provider_ref: &str) -> ProviderSessionResult<()>;",
          "documentation": "Checks whether a specific provider is allowed by the policy.\n\n# Arguments\n\n* `provider_ref` - The provider reference to check.\n\n# Errors\n\nReturns `ProviderSessionError::ProviderNotAllowed` if the provider\nis not in the allowed list."
        },
        {
          "name": "::PolicyEngine::check_runtime_swap_allowed",
          "line": 526,
          "signature": "pub fn check_runtime_swap_allowed(\n        &self,\n        from_provider: &str,\n        to_provider: &str,\n    ) -> ProviderSessionResult<()>;",
          "documentation": "Checks whether a runtime swap is allowed by the policy.\n\n# Errors\n\nReturns `ProviderSessionError::RuntimeSwapDenied` if runtime swapping\nis disabled."
        }
      ]
    },
    {
      "path": "forge-rs/crates/forge-provider-session/src/session.rs",
      "sha256": "507109decb20f63767993e720adfa674b1081f1dfa95df911529b717efd74f55",
      "artifactSha256": "e9f883a45a033443ec0dbe3fc90c888a9ae35edc6c672df95a8bf131b2d44215",
      "url": "/reference/source/forge-rs/crates/forge-provider-session/src/session.rs.txt",
      "declarations": [
        {
          "name": "::SessionId",
          "line": 37,
          "signature": "#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub struct SessionId(String);",
          "documentation": "Unique identifier for a provider session.\n\n# Examples\n\n```\nuse forge_provider_session::SessionId;\n\nlet id = SessionId::new();\nassert!(!id.as_str().is_empty());\n```"
        },
        {
          "name": "::SessionId::new",
          "line": 41,
          "signature": "pub fn new() -> Self;",
          "documentation": "Creates a new random session ID."
        },
        {
          "name": "::SessionId::from_string",
          "line": 46,
          "signature": "pub fn from_string(id: impl Into<String>) -> Self;",
          "documentation": "Creates a session ID from an existing string."
        },
        {
          "name": "::SessionId::as_str",
          "line": 51,
          "signature": "pub fn as_str(&self) -> &str;",
          "documentation": "Returns the session ID as a string slice."
        },
        {
          "name": "::SessionState",
          "line": 81,
          "signature": "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\npub enum SessionState {\n    /// Session has been created but not yet activated.\n    Created,\n    /// Session is active and accepting requests.\n    Active,\n    /// Session is temporarily paused (e.g., agent idle).\n    Paused,\n    /// Session is mid-swap to a different provider.\n    Switching,\n    /// Session has expired due to TTL or provider timeout.\n    Expired,\n    /// Session has been cleanly closed.\n    Closed,\n}",
          "documentation": "Lifecycle state of a provider session.\n\n# Valid Transitions\n\n```text\nCreated -> Active -> Paused -> Active (resume)\n                  -> Switching -> Active (new provider)\n                  -> Expired\n                  -> Closed\nCreated -> Closed (abort before activation)\n```"
        },
        {
          "name": "::SessionState::can_transition_to",
          "line": 98,
          "signature": "pub fn can_transition_to(&self, target: SessionState) -> bool;",
          "documentation": "Returns `true` if the transition from `self` to `target` is valid."
        },
        {
          "name": "::SessionConfig",
          "line": 132,
          "signature": "#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct SessionConfig {\n/// The agent that owns this session.\n\npub agent_id: String,\n/// The initial provider to bind to.\n\npub initial_provider_ref: String,\n/// Session TTL in seconds (overrides policy TTL if set).\n\npub ttl_seconds: Option<u64>,\n/// Arbitrary metadata attached to the session.\n\npub metadata: HashMap<String, String>\n}",
          "documentation": "Configuration for creating a new provider session.\n\n# Examples\n\n```\nuse forge_provider_session::SessionConfig;\n\nlet config = SessionConfig {\n    agent_id: \"agent-123\".to_string(),\n    initial_provider_ref: \"openai:gpt-4o\".to_string(),\n    ttl_seconds: Some(3600),\n    metadata: Default::default(),\n};\nassert_eq!(config.agent_id, \"agent-123\");\n```"
        },
        {
          "name": "::ProviderSession",
          "line": 163,
          "signature": "#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ProviderSession {\n\n}",
          "documentation": "A provider session: a long-lived binding between an agent and a provider.\n\nSessions track usage, state, and support graceful provider switching.\n\n# Examples\n\n```\nuse forge_provider_session::{ProviderSession, SessionConfig, SessionState};\n\nlet config = SessionConfig {\n    agent_id: \"agent-1\".to_string(),\n    initial_provider_ref: \"openai:gpt-4o\".to_string(),\n    ttl_seconds: None,\n    metadata: Default::default(),\n};\nlet session = ProviderSession::new(config);\nassert_eq!(session.state(), SessionState::Created);\nassert_eq!(session.provider_ref(), \"openai:gpt-4o\");\n```"
        },
        {
          "name": "::ProviderSwapRecord",
          "line": 180,
          "signature": "#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ProviderSwapRecord {\n/// The provider that was replaced.\n\npub from_provider: String,\n/// The provider that replaced it.\n\npub to_provider: String,\n/// When the swap occurred.\n\npub swapped_at: DateTime<Utc>,\n/// Why the swap was initiated.\n\npub reason: String\n}",
          "documentation": "A record of a provider swap that occurred during a session."
        },
        {
          "name": "::SwapOutcome",
          "line": 206,
          "signature": "#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct SwapOutcome {\n/// The provider that was replaced.\n\npub previous_provider: String,\n/// The new active provider.\n\npub new_provider: String,\n/// Total number of swaps in this session's lifetime.\n\npub swap_count: usize\n}",
          "documentation": "The outcome of a provider swap operation.\n\n# Examples\n\n```\nuse forge_provider_session::SwapOutcome;\n\nlet outcome = SwapOutcome {\n    previous_provider: \"openai:gpt-4o\".to_string(),\n    new_provider: \"anthropic:claude-opus-4\".to_string(),\n    swap_count: 1,\n};\nassert_eq!(outcome.swap_count, 1);\n```"
        },
        {
          "name": "::ProviderSession::new",
          "line": 217,
          "signature": "pub fn new(config: SessionConfig) -> Self;",
          "documentation": "Creates a new session in the `Created` state."
        },
        {
          "name": "::ProviderSession::id",
          "line": 240,
          "signature": "pub fn id(&self) -> &SessionId;",
          "documentation": "Returns the session ID."
        },
        {
          "name": "::ProviderSession::agent_id",
          "line": 245,
          "signature": "pub fn agent_id(&self) -> &str;",
          "documentation": "Returns the agent ID that owns this session."
        },
        {
          "name": "::ProviderSession::provider_ref",
          "line": 250,
          "signature": "pub fn provider_ref(&self) -> &str;",
          "documentation": "Returns the current provider reference."
        },
        {
          "name": "::ProviderSession::state",
          "line": 255,
          "signature": "pub fn state(&self) -> SessionState;",
          "documentation": "Returns the current session state."
        },
        {
          "name": "::ProviderSession::created_at",
          "line": 260,
          "signature": "pub fn created_at(&self) -> DateTime<Utc>;",
          "documentation": "Returns when the session was created."
        },
        {
          "name": "::ProviderSession::last_active_at",
          "line": 265,
          "signature": "pub fn last_active_at(&self) -> DateTime<Utc>;",
          "documentation": "Returns when the session was last active."
        },
        {
          "name": "::ProviderSession::expires_at",
          "line": 270,
          "signature": "pub fn expires_at(&self) -> Option<DateTime<Utc>>;",
          "documentation": "Returns when the session expires, if configured."
        },
        {
          "name": "::ProviderSession::total_requests",
          "line": 275,
          "signature": "pub fn total_requests(&self) -> u64;",
          "documentation": "Returns the total number of requests in this session."
        },
        {
          "name": "::ProviderSession::total_input_tokens",
          "line": 280,
          "signature": "pub fn total_input_tokens(&self) -> u64;",
          "documentation": "Returns the total number of input tokens in this session."
        },
        {
          "name": "::ProviderSession::total_output_tokens",
          "line": 285,
          "signature": "pub fn total_output_tokens(&self) -> u64;",
          "documentation": "Returns the total number of output tokens in this session."
        },
        {
          "name": "::ProviderSession::provider_history",
          "line": 290,
          "signature": "pub fn provider_history(&self) -> &[ProviderSwapRecord];",
          "documentation": "Returns the provider swap history."
        },
        {
          "name": "::ProviderSession::is_expired",
          "line": 295,
          "signature": "pub fn is_expired(&self) -> bool;",
          "documentation": "Returns `true` if the session has expired."
        },
        {
          "name": "::ProviderSession::transition_to",
          "line": 309,
          "signature": "pub fn transition_to(&mut self, target: SessionState) -> ProviderSessionResult<()>;",
          "documentation": "Transitions the session to a new state.\n\n# Errors\n\nReturns `ProviderSessionError::InvalidSessionTransition` if the\ntransition is not valid."
        },
        {
          "name": "::ProviderSession::activate",
          "line": 331,
          "signature": "pub fn activate(&mut self) -> ProviderSessionResult<()>;",
          "documentation": "Activates the session (transitions from Created to Active).\n\n# Errors\n\nReturns an error if the session is not in the `Created` state."
        },
        {
          "name": "::ProviderSession::record_request",
          "line": 336,
          "signature": "pub fn record_request(&mut self, input_tokens: u64, output_tokens: u64);",
          "documentation": "Records a completed request against this session."
        },
        {
          "name": "::ProviderSession::swap_provider",
          "line": 356,
          "signature": "pub fn swap_provider(\n        &mut self,\n        new_provider: impl Into<String>,\n        reason: impl Into<String>,\n    ) -> ProviderSessionResult<SwapOutcome>;",
          "documentation": "Swaps the active provider mid-session.\n\nThis transitions the session through `Switching` and back to `Active`\nwith the new provider.\n\n# Arguments\n\n* `new_provider` - The provider to swap to.\n* `reason` - Why the swap is being initiated.\n\n# Errors\n\nReturns an error if the session cannot enter the `Switching` state."
        },
        {
          "name": "::ProviderSession::close",
          "line": 389,
          "signature": "pub fn close(&mut self) -> ProviderSessionResult<()>;",
          "documentation": "Closes the session cleanly.\n\n# Errors\n\nReturns an error if the session cannot transition to `Closed`."
        },
        {
          "name": "::SessionManager",
          "line": 423,
          "signature": "pub struct SessionManager {\n\n}",
          "documentation": "Manages the lifecycle of multiple provider sessions.\n\nThe session manager creates sessions using a [`PolicyEngine`] for\nprovider selection, tracks active sessions, and handles expiration.\n\n# Examples\n\n```\nuse forge_provider_session::{\n    SessionManager, SessionConfig, SessionPolicy, PolicyEngine,\n};\n\nlet policy = SessionPolicy::builder()\n    .default_provider(\"openai:gpt-4o\")\n    .allow_runtime_swap(true)\n    .build()\n    .unwrap();\nlet engine = PolicyEngine::new(policy);\nlet mut manager = SessionManager::new(engine);\n\nlet config = SessionConfig {\n    agent_id: \"agent-1\".to_string(),\n    initial_provider_ref: \"openai:gpt-4o\".to_string(),\n    ttl_seconds: None,\n    metadata: Default::default(),\n};\nlet session_id = manager.create_session(config).unwrap();\nassert!(manager.get_session(session_id.as_str()).is_some());\n```"
        },
        {
          "name": "::SessionManager::new",
          "line": 430,
          "signature": "pub fn new(engine: PolicyEngine) -> Self;",
          "documentation": "Creates a new session manager with the given policy engine."
        },
        {
          "name": "::SessionManager::engine",
          "line": 438,
          "signature": "pub fn engine(&self) -> &PolicyEngine;",
          "documentation": "Returns a reference to the underlying policy engine."
        },
        {
          "name": "::SessionManager::create_session",
          "line": 448,
          "signature": "pub fn create_session(&mut self, config: SessionConfig) -> ProviderSessionResult<SessionId>;",
          "documentation": "Creates a new session, validating the initial provider against policy.\n\n# Errors\n\nReturns `ProviderSessionError::ProviderNotAllowed` if the initial\nprovider is not allowed by the policy."
        },
        {
          "name": "::SessionManager::create_default_session",
          "line": 464,
          "signature": "pub fn create_default_session(\n        &mut self,\n        agent_id: impl Into<String>,\n    ) -> ProviderSessionResult<SessionId>;",
          "documentation": "Creates a new session using the policy engine's default provider.\n\n# Errors\n\nReturns `ProviderSessionError::NoDefaultProvider` if no default\nprovider is configured."
        },
        {
          "name": "::SessionManager::create_routed_session",
          "line": 491,
          "signature": "pub fn create_routed_session(\n        &mut self,\n        agent_id: impl Into<String>,\n        task_mode: Option<&TaskMode>,\n        domain: Option<&str>,\n    ) -> ProviderSessionResult<(SessionId, PolicyDecision)>;",
          "documentation": "Creates a session routed by task mode and domain.\n\nUses the policy engine to select the best provider for the given\ntask characteristics.\n\n# Errors\n\nReturns an error if the policy engine cannot produce a valid decision."
        },
        {
          "name": "::SessionManager::get_session",
          "line": 514,
          "signature": "pub fn get_session(&self, session_id: &str) -> Option<&ProviderSession>;",
          "documentation": "Retrieves a session by ID."
        },
        {
          "name": "::SessionManager::get_session_mut",
          "line": 519,
          "signature": "pub fn get_session_mut(&mut self, session_id: &str) -> Option<&mut ProviderSession>;",
          "documentation": "Retrieves a mutable session by ID."
        },
        {
          "name": "::SessionManager::activate_session",
          "line": 529,
          "signature": "pub fn activate_session(&mut self, session_id: &str) -> ProviderSessionResult<()>;",
          "documentation": "Activates a session (transitions from Created to Active).\n\n# Errors\n\nReturns `ProviderSessionError::SessionNotFound` if the session\ndoes not exist, or an error if the transition is invalid."
        },
        {
          "name": "::SessionManager::swap_provider",
          "line": 555,
          "signature": "pub fn swap_provider(\n        &mut self,\n        session_id: &str,\n        new_provider: &str,\n        reason: &str,\n    ) -> ProviderSessionResult<SwapOutcome>;",
          "documentation": "Swaps the provider for an active session.\n\n# Errors\n\nReturns errors if the session is not found, the swap is not allowed\nby policy, or the new provider is not in the allowed list."
        },
        {
          "name": "::SessionManager::close_session",
          "line": 587,
          "signature": "pub fn close_session(&mut self, session_id: &str) -> ProviderSessionResult<()>;",
          "documentation": "Closes a session.\n\n# Errors\n\nReturns `ProviderSessionError::SessionNotFound` if the session\ndoes not exist."
        },
        {
          "name": "::SessionManager::active_session_ids",
          "line": 597,
          "signature": "pub fn active_session_ids(&self) -> Vec<&str>;",
          "documentation": "Returns all active session IDs."
        },
        {
          "name": "::SessionManager::session_count",
          "line": 606,
          "signature": "pub fn session_count(&self) -> usize;",
          "documentation": "Returns the number of tracked sessions (all states)."
        },
        {
          "name": "::SessionManager::gc_sessions",
          "line": 611,
          "signature": "pub fn gc_sessions(&mut self) -> usize;",
          "documentation": "Removes all closed and expired sessions."
        }
      ]
    }
  ]
}
