SDK concepts and ownership
The SDK splits configuration, conversation state, and one active model loop into explicit owners. Hosts build a runtime once, open sessions from it, and drive runs through events plus a typed final outcome.
Runtime
Construction is side-effect-free by default:
SystemPrompt::None- no tools
- no workspace
DenyAllPolicyDenyApprovals- no compactor or automatic compaction
- no environment, keychain, filesystem, network, terminal, logger, or update-check access
A custom system prompt is inserted as the first history message for a new session. Restoring a snapshot does not insert it again.
Session
A Session owns a conversation ID, provider-neutral history, a monotonic revision, compaction continuation state, runtime configuration, and explicit lifecycle state. Clones refer to the same mutable session.
One session permits only one active run or manual compaction. A second attempt returns Error::SessionBusy. Different sessions created from the same runtime may run concurrently, subject to the provider and host resources. Hosts can:
- inspect cloned history and create a snapshot
- inspect state, revision, diagnostics, and reasoning level
- reset an idle session
- replace the provider while idle and inspect provider-context omissions
- change reasoning while idle
- run an explicit compactor
History cannot be mutated in place through the public session API. Initial history is supplied through SessionOptions and validated run changes are committed by the runtime.
Run
Session::start(UserInput) creates a Run, a unique run ID, an ordered event receiver, a command channel, and a shared cancellation token. The run drives one provider/tool loop and is the host's handle for:
- reading
RunEventvalues - obtaining the final typed
RunOutcome(authoritative even if the event stream ends early) - cancelling
- steering with additional user input (
Run::steer) - retractable steering (
Run::steer_retractable/Run::retract_steering) before staged input reaches history - responding to typed host-input requests
Session::complete is a convenience path over the same run loop. It drains events and returns the final outcome. Because it has no host interaction callback, it cancels and returns InvalidHostResponse if a tool requests host input. Use Session::start for questionnaires or other interactive host work.
Tool host
ToolHost runs the same tool registry, workspace policy, approvals, and hooks without a model provider. Build it with ToolHost::builder() (or the shared builder pattern on ToolHostBuilder). Use ToolHost::invoke for a single non-interactive call, or ToolHost::start when the tool may emit progress or request host input. Dropping a ToolHostRun cancels that work. Clones of a tool host share one approval-memory session.
Hooks
Lifecycle hooks are optional. A host supplies a HookObserver and/or PreToolUseGate through RhoBuilder (and the same hooks on ToolHostBuilder). The SDK emits bounded HookEnvelope values; it does not load hooks.toml or spawn processes. See hooks.
Usage recording
Optional ProviderRequestUsageRecorder implementations receive physical provider-request facts for durable ledgers. Wire them with RhoBuilder::usage_recorder / usage_recording / usage_purpose. This is separate from model-facing UsageUpdated events on a run.
Provider turn and step
A run appends the initial user input to a private candidate history and then performs model steps. Before each step it may compact. A provider receives borrowed provider-neutral messages, tool specifications, cancellation, reasoning level, and optional provider-specific cache metadata. It does not receive the session object and cannot mutate history directly.
A step can end in final assistant content or tool calls. Tool calls are proposed and executed in model order. Successful and failed tool results are both returned to the model for a following step. The default maximum is 32 model steps and can be changed with RhoBuilder::max_steps. Reaching that budget commits the accumulated history and completes with StopReason::MaxSteps, allowing the host to distinguish a resumable runtime limit from the provider's normal StopReason::EndTurn completion.
Host responsibilities
The SDK supplies mechanics, not ambient authority. The embedding host owns:
- selecting and configuring a real provider
- acquiring, storing, rotating, and redacting credentials
- deciding which tools to register
- implementing tool behavior and calling
ToolContext::authorizebefore sensitive actions - selecting workspace and approval policy
- wiring hooks, usage recorders, and any durable ledger sinks
- rendering semantic events
- storing snapshots atomically and applying retention or encryption
- responding to host input exactly once
- draining or deliberately dropping runs
- calling shutdown and waiting for host-owned resources
- setting logging and telemetry policy
A custom provider or tool is trusted host code. The SDK cannot prevent it from opening files, spawning processes, or using the network outside ToolContext. The host or operating system must enforce sandboxing when plugin code is not fully trusted.
Diagnostics and prompt sources
Rho::diagnostics and Session::diagnostics return owned snapshots of effective provider identity, registered tools, workspace root and path scope, prompt-source metadata, event capacity, step limit, compaction threshold, reasoning level, and enabled SDK features. Diagnostics are intended to describe configuration, not contain credentials or prompt bodies.
The current core SDK supports SystemPrompt::None and SystemPrompt::Custom. Rho coding-prompt construction, AGENTS.md discovery, and skill discovery belong to explicit application or future adapter policy. A host that performs instruction discovery must scope it to the configured workspace and expose included sources in diagnostics rather than hiding global discovery.
Continue with providers, tools, hooks, and the detailed run contracts.