Skip to main content

Bundle Format

Bundle Files

An Odyssey bundle is defined by a small set of source files:

<project>/
odyssey.bundle.json5 # Bundle manifest
agent.yaml # Agent spec (path configurable via agent_spec)
README.md # Bundle documentation
skills/ # Skill directories referenced by manifest
resources/ # Resource files referenced by manifest
FilePurpose
odyssey.bundle.json5Runtime policy: bundle identity, executor, memory, tool list, sandbox settings, mounts, env passthrough, and resource limits
agent.yamlAgent identity, prompt, model selection, and tool permission rules
README.mdHuman-facing bundle documentation copied into the built artifact
skills/Skill directories that can be surfaced through the Skill tool
resources/Bundle-local assets copied into the built artifact when present

odyssey.bundle.json5 points to the agent spec through agent_spec, so the agent file does not have to be named agent.yaml, but the generated starter project uses that name.

Starter Template Versus Schema Defaults

There are two related but different things to keep in mind:

  • odyssey-rs init writes an opinionated starter template
  • BundleManifest also has schema defaults when some fields are omitted

Today the starter template generated by odyssey-rs init chooses these values:

FieldStarter template value
executor.idreact
memory.idsliding_window
toolsAll builtin tools are listed explicitly
sandbox.moderead_only
sandbox.permissions.network["*"]
sandbox.system_tools_modestandard
sandbox.system_tools[]
sandbox.env{}

By contrast, if you omit fields from a manifest and rely on schema defaults, the manifest layer currently fills in:

FieldSchema default
memory{ type: "prebuilt", id: "sliding_window", config: null }
skills[]
tools[]
sandbox.modeworkspace_write
sandbox.permissions.network[]
sandbox.system_tools_modeexplicit
sandbox.system_tools[]
sandbox.resources{ cpu: 1, memory_mb: 512 }

Manifest Schema

The bundle manifest (odyssey.bundle.json5) deserializes into BundleManifest. A compact manifest that relies on schema defaults can look like this:

{
id: 'my-bundle',
version: '0.1.0',
manifest_version: 'odyssey.bundle/v1',
readme: 'README.md',
agent_spec: 'agent.yaml',
executor: { type: 'prebuilt', id: 'react' },
memory: { type: 'prebuilt', id: 'sliding_window' }
}

Top-level fields

FieldTypeDescription
idstringUnique bundle identifier (required)
versionstringSemantic version (required)
manifest_versionstringBundle manifest schema identifier (required)
readmestringRelative path to the bundle README file (required)
agent_specstringRelative path to the agent YAML file (required)
executorobjectExecutor configuration (required)
memoryobjectMemory provider configuration
skillsarraySkill entries ({ name, path })
toolsarrayTool entries ({ name, source })
sandboxobjectSandbox execution policies

Executor and memory

In the current runtime:

  • executor.type must be prebuilt
  • executor.id must currently be react
  • memory.type must be prebuilt
  • memory.id must currently be sliding_window

Sandbox fields

FieldTypeDefaultDescription
modestring"workspace_write"Isolation mode: read_only, workspace_write, or danger_full_access
envobject{}Map of sandbox env var name to host env var name to read at runtime
permissions.filesystem.execstring[][]Executable paths under the staged bundle root
permissions.filesystem.mounts.readstring[][]Read-only host mount paths; absolute paths are allowed, and . resolves to the current working directory
permissions.filesystem.mounts.writestring[][]Read-write host mount paths; absolute paths are allowed, and . resolves to the current working directory
permissions.networkstring[][][] disables network; ["*"] enables unrestricted outbound network
system_tools_modestring"explicit"Host executable policy: explicit, standard, or all
system_toolsstring[][]Host executable names or paths that process-execution tools may run
resources.cpunumber1CPU limit
resources.memory_mbnumber512Memory limit in MB

Manifest example with an explicit sandbox

{
id: 'repo-auditor',
version: '0.1.0',
manifest_version: 'odyssey.bundle/v1',
readme: 'README.md',
agent_spec: 'agent.yaml',
executor: { type: 'prebuilt', id: 'react' },
memory: { type: 'prebuilt', id: 'sliding_window', config: { max_window: 100 } },
tools: [
{ name: 'Read', source: 'builtin' },
{ name: 'Glob', source: 'builtin' },
{ name: 'Grep', source: 'builtin' },
{ name: 'Bash', source: 'builtin' }
],
sandbox: {
mode: 'workspace_write',
env: {
CARGO_HOME: 'CARGO_HOME'
},
permissions: {
filesystem: {
exec: [],
mounts: {
read: ['.'],
write: []
}
},
network: []
},
system_tools_mode: 'explicit',
system_tools: ['sh', 'cargo'],
resources: { cpu: 2, memory_mb: 1024 }
}
}

Validation rules

  • executor.type must be prebuilt
  • memory.type must be prebuilt
  • executor.id and memory.id must be non-empty
  • readme must point to an existing file inside the project
  • agent_spec must point to an existing file inside the project
  • Each skill path must exist inside the project
  • sandbox.permissions.filesystem.exec entries must point to existing paths inside the project; absolute paths, .., and symlink escapes are rejected
  • Host mount paths in sandbox.permissions.filesystem.mounts.read and .write must be absolute, except . which resolves to the current working directory
  • sandbox.env keys and values must be valid environment variable names using ASCII letters, digits, and underscores, and must not start with a digit
  • sandbox.permissions.network only supports [] or ["*"] in v1
  • sandbox.system_tools_mode must be one of explicit, standard, or all
  • Agent tool permission entries such as Bash(cargo test:*) must parse successfully; malformed entries are rejected
  • Tool entries must use source: "builtin"

Current implementation limits

  • executor.id = "react" is the only executor implemented by the runtime
  • memory.id = "sliding_window" is the only memory provider implemented
  • Only source: "builtin" tools are supported (no custom tools in v1)
  • Network hostname allowlists are not implemented in v1; network policy is either disabled or unrestricted
  • sandbox.env reads host variables at runtime and injects them under the configured sandbox variable names; missing host variables are silently omitted
  • system_tools_mode: "explicit" only mounts declared bundle exec paths and named system_tools into confined Linux sandboxes, so complex host tools that spawn helper executables may require additional explicit entries or standard
  • system_tools_mode: "all" allows sandboxed process execution to resolve any executable path the sandbox can already see, so it should only be used for trusted workspace agents
  • v1 treats bundle manifests as trusted configuration. Mounts, system tools, env passthrough, and bundle-local exec paths are enforced as declared by the bundle author. v2 is expected to treat bundles as untrusted input and add stricter approval or policy layers around those capabilities.

Agent Spec

The agent spec YAML (agent.yaml) maps to AgentSpec. Default template:

id: my-bundle
description: my-bundle agent using Odyssey runtime
prompt: |
You are a helpful assistant Tess, Created by LiquidOS.
model:
provider: openai
name: gpt-4.1-mini
tools:
allow: []
ask: []
deny: []

Fields

FieldTypeRequiredDescription
idstringyesAgent identifier
descriptionstringnoHuman-readable description
promptstringyesSystem prompt
modelobjectyesLLM configuration
model.providerstringyesProvider name (for example openai, anthropic)
model.namestringyesModel name (for example gpt-4.1-mini)
model.configobjectnoProvider-specific configuration
tools.allowstring[]noTool permissions granted without approval; entries may be coarse like Read or granular like Bash(curl:*)
tools.askstring[]noTool permissions that require approval before execution
tools.denystring[]noTool permissions that are always denied

model can be overridden at session creation and again per-turn through TurnContextOverride.model.

Agent tool permission entries may be coarse (Read) or granular (Bash(cargo test:*)), and granular entries must use a single outer (...) matcher suffix.

Important behavior detail:

  • an empty tools.allow list does not mean "no tools"
  • the runtime first selects tools from manifest.tools or the builtin registry
  • allow, ask, and deny then refine availability and permission behavior

That means the default starter agent with empty allow, ask, and deny still has access to the tools declared by the manifest.

Build Output Layout

odyssey-rs-bundle materializes an OCI bundle layout when building directly:

<bundle-layout-root>/
agent.yaml
bundle.json
index.json
oci-layout
blobs/
resources/
skills/

When a bundle is installed into the local store, the runtime-visible files stay at install root and the OCI metadata moves under .odyssey/:

<install-root>/
agent.yaml
resources/
skills/
.odyssey/
bundle.json
index.json
oci-layout
blobs/

Build behavior that matters when authoring bundles:

  • The agent spec file is copied into the built layout as agent.yaml
  • The README file is copied into the built layout at the manifest-defined readme path
  • Each skill entry is copied into skills/<skill.name>
  • The resources/ directory, when present, is copied into the built layout as resources/
  • Host mounts are also exposed inside the staged app workspace under mount/read/... and mount/write/... so the agent can reference them with workspace-relative paths
  • Installed bundles keep OCI metadata and blobs under .odyssey/ so the install root stays focused on runtime-visible files
  • The manifest (odyssey.bundle.json5) is not included in the payload layer

Bundle References

You will see the same bundle reference forms across the CLI, HTTP APIs, and runtime:

  • hello-world or hello-world@latest resolves the latest installed local bundle
  • local/hello-world:0.1.0 resolves a specific namespaced install
  • team/demo:0.2.0 and team/demo@0.2.0 resolve namespaced references
  • team/demo@sha256:<digest> resolves by digest
  • ./dist/my-bundle resolves a bundle layout directory directly
  • .odyssey archives must be imported before use; they are not executed directly