pub struct KodaSession {
pub id: String,
pub agent: Arc<KodaAgent>,
pub db: Database,
pub provider: Box<dyn LlmProvider>,
pub mode: TrustMode,
pub cancel: CancellationToken,
pub file_tracker: FileTracker,
pub title_set: bool,
pub proxy: Option<ProxyHandle>,
pub socks5_proxy: Option<ProxyHandle>,
pub bg_agents: Arc<BgAgentRegistry>,
pub sub_agent_cache: SubAgentCache,
}Expand description
A single conversation session with its own state.
Each session has its own provider, trust mode, and cancel token.
Multiple sessions can share the same Arc<KodaAgent>.
Fields§
§id: StringUnique session identifier.
agent: Arc<KodaAgent>Shared agent configuration (tools, system prompt).
db: DatabaseDatabase handle for message persistence.
provider: Box<dyn LlmProvider>LLM provider for this session.
mode: TrustModeCurrent trust mode (Plan / Safe / Auto).
cancel: CancellationTokenCancellation token for graceful shutdown.
file_tracker: FileTrackerFile lifecycle tracker — tracks files created by Koda (#465).
title_set: boolWhether the session title has already been set (first-message guard).
proxy: Option<ProxyHandle>Per-session HTTP CONNECT proxy (Phase 3b of #934).
Spawned unconditionally in Self::new with the hardcoded
koda_sandbox::DEFAULT_DEV_ALLOWLIST — koda is config-free,
so there’s no “opt in” toggle and no user-tunable allowlist
(yet; future work: DB-backed slash command for per-project
extensions). Always-on means every Bash invocation routes
through this proxy and unknown hostnames get a 403 at the CONNECT
layer.
Option rather than bare ProxyHandle because spawn can fail
(ephemeral-port exhaustion, broken loopback, runtime shutdown).
Fail-open: on spawn failure we log + continue with None,
matching the contract of koda_sandbox::ExternalProxy::spawn.
A broken proxy must never break a session — the kernel sandbox
remains the authoritative network boundary anyway.
Held for the session’s lifetime; Drop aborts the proxy task
and closes the listener — no manual teardown needed.
socks5_proxy: Option<ProxyHandle>Per-session SOCKS5 proxy (Phase 3d.1 of #934). Sibling of
Self::proxy for raw-TCP clients (git over ssh, gRPC) that
don’t honor HTTPS_PROXY. Same fail-open contract: spawn
failure logs a warning and the field stays None. Uses the
same hostname allowlist as the HTTP proxy by construction —
see koda_sandbox::BuiltInSocks5Proxy.
bg_agents: Arc<BgAgentRegistry>Background sub-agent registry (#1022 B12).
Lives on the session, not on inference_loop, so background
agents survive across turns. The previous design constructed
the registry locally inside inference_loop; when the loop
returned (final text, error, hard-stop) the Arc dropped and
every still-pending bg task was aborted via
tokio_util::task::AbortOnDropHandle — silently discarding
any not-yet-completed result. With single-iteration responses
(InvokeAgent { background: true } followed by final text in
the same turn) this lost the bg result every time.
Owning here means: bg tasks keep running between turns, and the
next turn’s first iteration drains anything that completed
during the idle gap. Registry abort still happens at
Drop — i.e. when the session itself is dropped — which is
what users actually mean by “stop”.
Wrapped in Arc because tool dispatch needs to hand the same
registry into the recursive execute_sub_agent call (so
nested InvokeAgent { background: true } registers in the
caller-visible slot, not a fresh per-call one).
sub_agent_cache: SubAgentCacheCross-turn sub-agent result cache (#1022 B12).
Same lifetime motivation as Self::bg_agents: was previously
re-created per inference_loop invocation, which threw away
every cache entry on each turn boundary and made the cache
useless for the natural “ask, follow up, ask again” flow.
Living on the session means the second turn can hit results
computed in the first.
Invalidation still happens on every mutating tool call via
crate::tool_dispatch::execute_one_tool — generation bump,
cached entries with stale generations are treated as misses.
Cross-turn doesn’t change that contract; it just extends the
window in which a still-fresh entry can be reused.
Implementations§
Source§impl KodaSession
impl KodaSession
Sourcepub async fn new(
id: String,
agent: Arc<KodaAgent>,
db: Database,
config: &KodaConfig,
mode: TrustMode,
) -> Self
pub async fn new( id: String, agent: Arc<KodaAgent>, db: Database, config: &KodaConfig, mode: TrustMode, ) -> Self
Create a new session from an agent, config, and database.
Sourcepub async fn run_turn(
&mut self,
config: &KodaConfig,
pending_images: Option<Vec<ImageData>>,
sink: &dyn EngineSink,
cmd_rx: &mut Receiver<EngineCommand>,
) -> Result<()>
pub async fn run_turn( &mut self, config: &KodaConfig, pending_images: Option<Vec<ImageData>>, sink: &dyn EngineSink, cmd_rx: &mut Receiver<EngineCommand>, ) -> Result<()>
Run one inference turn: prompt → streaming → tool execution → response.
Emits TurnStart and TurnEnd lifecycle events. The loop-cap prompt is handled via EngineEvent::LoopCapReached / EngineCommand::LoopDecision
through the cmd_rx channel.
Sourcepub fn update_provider(&mut self, config: &KodaConfig)
pub fn update_provider(&mut self, config: &KodaConfig)
Replace the provider (e.g., after switching models or providers).
Auto Trait Implementations§
impl Freeze for KodaSession
impl !RefUnwindSafe for KodaSession
impl Send for KodaSession
impl Sync for KodaSession
impl Unpin for KodaSession
impl UnsafeUnpin for KodaSession
impl !UnwindSafe for KodaSession
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more