Real-time index of opencode sessions
Rust 100.0%
22 1 0

Clone this repository

https://tangled.org/jauntywk.bsky.social/opencode-session-rs https://tangled.org/did:plc:zjbq26wybii5ojoypkso2mso/opencode-session-rs
git@tangled.org:jauntywk.bsky.social/opencode-session-rs git@tangled.org:did:plc:zjbq26wybii5ojoypkso2mso/opencode-session-rs

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

opencode-session-rs#

Read opencode session storage with typed Rust APIs, an indexed metadata layer, and lazy file loading.

What this crate does#

  • Detects opencode storage paths ($XDG_DATA_HOME/opencode/storage by default).
  • Builds an in-memory index of projects, sessions, messages, and parts.
  • Resolves session/message/part leaves as memory-mapped spans before hydration.
  • Lets you run workflows at different levels: index-only, reference tree, or hydrated tree.

The primary high-level entrypoint is SessionMaterializer.

Install#

[dependencies]
opencode-session = "0.1"

Quick start#

use opencode_session::{SessionId, SessionMaterializer};
use std::str::FromStr;

fn main() -> opencode_session::Result<()> {
    let materializer = SessionMaterializer::new()?;

    for project_id in materializer.project_ids() {
        let session_ids = materializer.session_ids_for_project(project_id);
        println!("project={project_id} sessions={}", session_ids.len());
    }

    let session_id = SessionId::from_str("ses_3975b29b7ffeDyjus9LjxKUoeX")?;
    let tree = materializer.load_session_tree(&session_id)?;
    println!("loaded {} messages", tree.messages.len());

    Ok(())
}

API surfaces#

1. Structural index access#

Use this when you want fast metadata queries without loading full JSON payloads.

let materializer = SessionMaterializer::new()?;

let session_meta = materializer.session_meta(&session_id);
let message_ids = materializer.message_ids_for_session(&session_id);

2. Piece-by-piece loading#

Use this when you want to deconstruct control flow and run explicit stages.

let message_ids = materializer.message_ids_for_session(&session_id);

for message_id in message_ids {
    let message = materializer.load_message(message_id)?;
    let parts = materializer.load_parts_for_message(message_id)?;
    println!("message={} parts={}", message.id(), parts.len());
}

3. Full tree materialization#

Use this when you want one call returning session + messages + parts.

let tree = materializer.load_session_tree(&session_id)?;
println!("session={} messages={}", tree.session.info.id, tree.messages.len());

4. Incremental index construction#

Use this when you want to control indexing scope (for example, one project at a time).

use opencode_session::{SessionIndex, StoragePaths};

let paths = StoragePaths::detect()?;
let mut builder = SessionIndex::builder(paths);
builder.index_project("05fc47d0307a3740bf2ac963190b7c5b029b6ab1")?;
let index = builder.finish();

println!("indexed sessions={}", index.session_count());

5. Decomposed flow execution (Bon builder)#

Use staged planning + resolve + hydrate when you want full control and zero-copy leaves.

use opencode_session::{SessionFlowOptions, SessionId, SessionMaterializer};
use std::str::FromStr;

let materializer = SessionMaterializer::new()?;
let session_id = SessionId::from_str("ses_3975b29b7ffeDyjus9LjxKUoeX")?;

let options = SessionFlowOptions::builder()
    .session_id(session_id)
    .part_limit_per_message(5)
    .build();

let plan = materializer.plan_session_tree(&options)?;
let ref_tree = materializer.resolve_session_tree(&plan.value)?;
let hydrated = materializer.hydrate_session_tree(&ref_tree.value)?;

// zero-copy leaf access before hydration
let first_message = &ref_tree.value.session.messages[0];
let raw_bytes = first_message.span.as_bytes();

println!(
    "planned_messages={} hydrated_messages={} first_message_bytes={}",
    plan.value.messages.len(),
    hydrated.value.messages.len(),
    raw_bytes.len(),
);

Core types#

Notes#

  • This crate is pre-1.0 and API shaping is active.
  • Backward compatibility between minor revisions is not guaranteed yet.
  • watch and watch-fallback features are reserved for live change tracking integration.
  • Registry internals use papaya concurrent hash maps for index and mmap caches.