Runtime Model
Runtime Entry Point
odyssey-rs-runtime exposes OdysseyRuntime as the main embeddable API. The public surface is
intentionally small:
- bundle helpers such as
init,build_and_install,build_to,inspect_bundle,export_bundle, andimport_bundle - bundle discovery helpers such as
list_agents,list_models, andlist_skills - session helpers such as
create_session,list_sessions,get_session, anddelete_session - execution helpers such as
run,submit,run_session_command,execution_status,subscribe_session, andresolve_approval
Default Runtime Configuration
RuntimeConfig::default() uses the following local directories:
~/.odyssey/bundles~/.odyssey/sessions~/.odyssey/sandbox
It also defaults to:
- bind address
127.0.0.1:8472 - hub URL
http://127.0.0.1:8473 worker_count = 4queue_capacity = 128
RuntimeConfig.sandbox_mode_override is None by default. When the CLI or an embedding sets it,
that override replaces the bundle manifest sandbox mode for execution.
RuntimeConfig::load() and the odyssey-rs CLI also read ~/.odyssey/config.yml automatically
when that file exists. The config file can override runtime defaults such as:
cache_rootsession_rootsandbox_rootbind_addrhub_urlworker_countqueue_capacitydefault_modelor the shorthandmodel,model_provider, andmodel_config- bundle-scoped agent overrides under
bundles."<bundle-ref>".agents."<agent-id>"
Bundle selectors are resolved against installed bundle identity, not the current working directory.
For local installs, selectors such as hello-world@latest, hello-world@0.1.0, and
local/hello-world@latest are all valid.
Session Lifecycle
A session is created from a SessionSpec that points at an AgentRef. During creation, the
runtime resolves the bundle, picks the session model, allocates a UUID, and persists a JSON record
to disk.
Session records currently store:
- the bundle reference used to create the session
- the resolved agent id
- the chosen model provider, model name, and optional model config
- the merged session sandbox overlay
- all completed turns
Session sandbox resolution happens once at session creation with this precedence:
- bundle manifest sandbox policy
- user config
bundles."<bundle-ref>".agents."<agent-id>".sandbox - explicit
SessionSpec.sandbox RuntimeConfig.sandbox_mode_overridefor mode only
That merged session sandbox is persisted with the session, so later turns and restarts use the same mounts, env passthrough, and system tool exposure.
Host mount sources from that merged overlay are not materialized by the runtime. Both
mounts.read and mounts.write must point to existing host paths before sandbox startup.
Session model resolution uses the same pattern:
- bundle or agent manifest model
- user config
bundles."<bundle-ref>".agents."<agent-id>".model* - explicit
SessionSpec.model
Turns are appended after execution completes. The runtime stores either a simple prompt/response pair or a normalized chat history that includes tool use and tool result records.
Session files are written atomically. On startup, unreadable or corrupt session files are skipped and quarantined instead of aborting the whole runtime.
Execution Flow
For each submitted ExecutionRequest, the runtime currently does the following:
- Load the session record.
- Resolve the bundle and agent from the bundle store.
- Pick the effective sandbox mode from the runtime override or the manifest default.
- Stage the installed bundle into a sandbox cell.
- Load bundle skills and append their summary section to the base system prompt when skills exist.
- Select builtin tools based on manifest entries plus agent allow or deny filters.
- Resolve the active LLM provider from the chosen
ModelSpec. - Build memory from prior turns.
- Run the
reactexecutor. - Persist the completed turn back into the session store.
run waits for completion and returns RunOutput. submit only enqueues the request and returns
an ExecutionHandle.
When the runtime stages a bundle into a managed sandbox cell, it keeps the bundle contents under
app/ and keeps mutable runtime state in sibling directories such as data/, cache/, tmp/,
and runs/. This lets Odyssey keep the staged app tree read-only in restricted modes while still
providing writable locations for HOME, temp files, caches, and per-execution scratch data that
normal tools expect.
run_session_command is the direct operator command path for an existing session. It resolves the
session bundle, stages the bundle into the session sandbox cell, parses the command line into an
argv-style process invocation, and runs that process directly inside the session sandbox. It emits
the same ExecCommand* session events used by builtin tool-driven command execution, but it does
not route through the bundle's Bash tool wrapper and it does not append a normal turn record to
session history.
delete_session is asynchronous because it waits for in-flight work on that session to finish,
removes the persisted session file, clears any approval state, and shuts down sandbox cells for the
session.
Turn Context Overrides
The public protocol currently allows two per-turn overrides:
TurnContextOverride.cwdTurnContextOverride.model
The runtime includes the resolved sandbox mode in TurnStarted.context, but the public
ExecutionRequest does not currently support a per-turn sandbox-mode override.
Events
Session subscribers receive EventMsg values over a broadcast channel. The event payloads include:
- turn lifecycle events
- streamed assistant deltas
- streamed reasoning deltas
- tool call start, delta, and finish events
- streamed command output for tool-driven process execution
- permission requests and approval resolutions
- plan updates
- runtime errors
The HTTP server exposes the same session event stream over server-sent events.
Approvals
Tool approvals are driven by bundle sandbox tool rules:
allowlets the tool run immediatelydenyfails the tool callaskemitsPermissionRequestedand waits forresolve_approval
ApprovalDecision::AllowAlways is remembered for the rest of the current session only. It is not
persisted across runtime restarts.
Concurrency Model
submitpushes work into an in-process queueworker_countlimits how many turns can execute concurrently in one runtime process- execution within a single session is serialized by a per-session async lock
- different sessions can execute concurrently
This means the runtime is concurrent across sessions but intentionally ordered within a session.
Current Provider Support
Cloud model providers are wired up for:
- Anthropic
- Azure OpenAI
- DeepSeek
- Google or Gemini
- Groq
- MiniMax
- OpenAI
- OpenRouter
- Phind
- xAI
Local providers currently wired up for runtime execution are:
llama_cpp(aliases:llamacpp,llama-cpp) using Hugging Face-hosted GGUF models
Use model.name as Odyssey's logical model id and put the Hugging Face source details in
model.config:
default_model:
provider: llama_cpp
name: qwen3.5-9b-q4
config:
hf_repo_id: unsloth/Qwen3.5-9B-GGUF
hf_filename: Qwen3.5-9B-Q4_0.gguf
hf_revision: main
max_tokens: 1024
temperature: 0.2
n_ctx: 4096
n_threads: 8
n_threads_batch: 8
n_gpu_layers: 99
reasoning_format: auto
extra_body:
chat_template_kwargs:
enable_thinking: true
config.hf_repo_id is required. Optional llama.cpp-specific fields include hf_filename,
hf_mmproj_filename, hf_revision, model_dir, chat_template, system_prompt,
force_json_grammar, reasoning_format, extra_body, repeat_penalty,
frequency_penalty, presence_penalty, repeat_last_n, seed, n_batch, n_ubatch,
main_gpu, split_mode, use_mlock, and devices.
The first time the runtime resolves a given llama_cpp model config it may download model files
from Hugging Face and initialize the local llama.cpp backend. After that, the runtime keeps the
provider warm in-process and reuses it across turns and sessions. Private Hugging Face repos can
use the standard HUGGINGFACE_TOKEN, HF_TOKEN, or HUGGINGFACE_HUB_TOKEN environment
variables.
For build-time acceleration backends, odyssey-rs-runtime exposes cuda and meta Cargo
features, and the odyssey-rs crate forwards the same feature names. Odyssey's meta feature
maps to the upstream autoagents-llamacpp metal backend flag.