Recipe: other frameworks — notes, not adapters
offshoot doesn't ship a LlamaIndex or CrewAI package, and this page isn't
one in disguise. Per the roadmap's stance: LangGraph gets a real companion
(offshoot.langgraph.ThreadForks, see
examples/langgraph-rewind/) because
its thread/checkpoint model maps onto offshoot's branch/checkpoint model
almost exactly. Claude Code hooks and the OpenAI Agents SDK get full
recipes (see claude-agent-sdk.md and
openai-agents.md) because both have a single,
official, SQLite-backed persistence knob you can point at an offshoot
checkout path with zero glue code. LlamaIndex and CrewAI don't have quite
as clean a single knob — this page is the honest, short version of what's
there and what to actually check before you build on it.
Neither section below was run against a live install as part of this
task (LlamaIndex's SQL chat store needs the aiosqlite extra and network
access this pass didn't spend on it; CrewAI's crewai package failed to
build locally in this environment — tiktoken, one of its transitive
dependencies, has no prebuilt wheel for this Python/platform combination
and needs a Rust toolchain to build from source, which isn't installed
here). Treat both as starting points to verify against the framework's
own current docs, not pinned recipes the way
openai-agents.md is (that one was run, end to end,
against a real pip install openai-agents).
LlamaIndex
LlamaIndex's chat-memory layer is pluggable storage, same shape as the
OpenAI Agents SDK's SQLiteSession in spirit: a BaseChatStore
implementation backs a ChatMemoryBuffer (or the newer Memory API), and
llama_index.core.storage.chat_store.sql.SQLAlchemyChatStore is the
SQL-backed one. Its constructor (checked against a real
pip install llama-index-core while writing this doc, though the store
itself wasn't exercised end-to-end) is:
SQLAlchemyChatStore(
table_name: str,
async_database_uri: str | None = "sqlite+aiosqlite:///:memory:",
async_engine: AsyncEngine | None = None,
db_data: list[dict] | None = None,
db_schema: str | None = None,
)
The pattern, if you go this route: point async_database_uri at an
offshoot checkout path instead of :memory: or a fixed project file —
store = SQLAlchemyChatStore(
table_name="chat_history",
async_database_uri=f"sqlite+aiosqlite:///{checkout_path}",
)
— using the same sqlite+aiosqlite:/// URI form SQLAlchemy's async SQLite
driver expects (needs pip install aiosqlite alongside llama-index-core;
not verified in this pass). From there, the same fork/checkpoint/rollback
loop openai-agents.md walks through applies unchanged
— offshoot only ever sees the resulting SQLite file, same as with
SQLiteSession. Verify the constructor and the URI form against your
installed LlamaIndex version before relying on this — LlamaIndex's
storage APIs have moved before (older versions used a different chat-store
surface entirely) and this skill pass didn't confirm read/write behavior
against a live checkout, only that the class and its constructor exist as
shown.
CrewAI
CrewAI's built-in memory (Crew(memory=True)) is backed by SQLite for its
long-term/entity memory by default, written to a fixed path under the
user's local app-data directory rather than a path you hand it directly —
check CrewAI's current memory documentation for the exact storage class
and constructor (LTMSQLiteStorage and similar names have existed in past
versions, but this pass could not verify the current shape against a real
install, for the dependency-build reason above). If CrewAI's storage class
accepts an explicit db_path-style argument in your installed version, the
same pattern applies: point it at an offshoot checkout path instead of the
framework's default location, then fork/checkpoint/rollback that file
exactly as the other recipes on this page do. If it doesn't expose a path
argument in the version you're on, the fallback is coarser but still
works: point the whole CrewAI storage directory at a path under an
offshoot checkout (most of these frameworks' local storage is "a directory
with one or more SQLite files in it," and offshoot can branch a directory
of SQLite files the same way it branches one — checkpoint/fork/rollback
operate on whatever's in the checkout at the time).
The general shape, if your framework isn't listed
- Find where the framework writes persistent SQLite state, and whether it takes a path (or URI) argument rather than hard-coding one.
- Point that argument at a path under an offshoot checkout —
offshoot session open <db>(daemon) oroffshoot checkout <db>(at rest) both print one. - Checkpoint/flush through offshoot at whatever granularity the framework naturally represents (a turn, a task, a run) — not through the framework's own API, which has no notion that offshoot exists underneath it.
- Fork before an attempt you want to be able to throw away in isolation;
promoteordestroyafterward, same as every other offshoot workflow.
No adapter package needed — the requirement is just "the framework writes its state to a SQLite file whose path you control," which most local-first agent memory/session layers do.