Lightspeed is a hosted agent product in progress, built around a deterministic, event-sourced engine and a Temporal-backed runtime.
The current focus is a small deterministic loop that can plan an agent session, record domain events, rebuild state by replaying a session log, and emit substrate-neutral actions for LLM/tool work.
That shape is meant to work well for agents running in durable workflow systems such as Temporal, hosted services, or other controlled runtimes where the agent should not assume direct ownership of an OS process, sandbox, or VM (like most coding agents do nowadays).
Previously this repo hosted an implementation of the Attractor runtime according to StrongDM's spec, with the goal to build a dark software factory.
We sinced moved towards building agents that orchestrate workflows directly, instead of deterministic workflow DAGs like Atrractor. But for that to work, agents have to be able to run outside sandboxes or VMs to orchestrate coding agents inside the guest-OSes.
We belive running and coordination agents at scale are best managed by durable workflow engines like Temporal or Inngest. Unfortunately there is no good agent runtimes or SDKs to build agents on such platforms. So this project is attempting to close this gap.
- Keep the engine deterministic.
engineowns the generic session log and the built-in CoreAgent domain: commands, events, state, planning, action emission, request/result helpers, and replay. - Execute side effects outside the core. LLM calls, host tools, filesystem access, process execution, MCP, human input, timers, retries, and cancellation belong in runtimes, adapters, workflow activities, or tool packages.
- Speak provider APIs natively.
openai:responses,openai:completions, andanthropic:messagesare different APIs with different context rules, tool encodings, streaming events, cache behavior, continuation semantics, and error shapes. - Parse only required reducer facts for deterministic branching. Provider-native data that the reducer does not need to branch on should remain opaque and blob-backed.
- Keep provider request vocabulary out of the deterministic core. The engine
plans a provider-neutral generation intent (model route, context snapshot,
tools, tool choice, output limit) plus opaque
ProviderParams;llm-runtimeadapters own the typed param schemas and materialize provider-native wire requests. Params are validated against the adapter schema at the admission boundary, before they enter the session log. Transport configuration (endpoints, credentials, headers) is runtime deployment config keyed byprovider_idand never enters the log. - Store the rest of the user inputs, context, files, and model respones in content addressed storage and only pass refs to that data, so that the objects traveling between deterministic workflow and effexts stays thin and minimal.
- Treat context management as a first-class agent concern. The core plans context windows, records context items, and leaves room for compaction as an explicit future operation.
- Keep the client boundary stable. CLIs, TUIs, editors, hosted gateways, and future Temporal frontends should consume
api, not reducer internals.
| Crate | Path | Purpose |
|---|---|---|
engine |
crates/engine |
Deterministic session kernel plus built-in CoreAgent: dynamic session log storage, CoreAgent command/event/state models, planning, codecs, and the substrate-neutral drive machine |
api |
crates/api |
Client-facing session/run/item API types, views, notifications, and the exported wire-contract artifacts under interop/contract/ |
api-projection |
crates/api-projection |
Shared CoreAgent-to-api projection helpers for local and workflow-backed gateways |
temporal-workflow |
crates/temporal-workflow |
Temporal workflow, signals, queries, and activity request/response DTOs |
temporal-server |
crates/temporal-server |
Hosted runtime binary and modules for the Temporal worker, HTTP/JSON-RPC gateway, and combined local/small-deployment mode |
test-support |
crates/test-support |
Fast in-process runner harness for tests/evals; not a production runtime |
tools |
crates/tools |
Optional host filesystem/process tool package |
store-fs |
crates/store-fs |
Filesystem-backed session log and content-addressed blob store adapters |
store-pg |
crates/store-pg |
PostgreSQL-backed session store, CAS catalog, MCP server catalog, and encrypted auth storage |
mcp-registry |
crates/mcp-registry |
Provider-independent remote MCP server catalog DTOs, validation, and store traits |
auth-registry |
crates/auth-registry |
Generic auth grants/secrets/providers, OAuth + GitHub App drivers, and the refreshing/minting token broker |
eval |
crates/eval |
Eval harness for agent/tool workflows |
llm-runtime |
crates/llm-runtime |
CoreAgent LLM runtime over provider-native clients |
llm-clients |
crates/llm-clients |
Provider-native OpenAI and Anthropic API clients |
cli |
crates/cli |
Command-line chat client for the API gateway |
docs/spec/holds working design notes.docs/roadmap/holds implementation plans and historical milestones.
Prerequisites:
- Rust toolchain with edition 2024 support (e.g. rustup)
- Docker with Compose for the local Postgres, MinIO, and Temporal stack
OPENAI_API_KEYfor live OpenAI-backed chat and eval runsANTHROPIC_API_KEYfor live Anthropic client tests
Easiest is to copy .env_example to .env and set provider keys there. The
hosted server worker mode registers real provider adapters and session-mounted
VFS tools; for OpenAI-backed local chat, set OPENAI_API_KEY.
Build and test:
cargo build
cargo testThe hosted path runs three pieces locally:
- Docker infra: Postgres/CAS catalog, MinIO object storage, Temporal.
temporal-server: registers the Temporal workflow/activities and exposes the public JSON-RPC API on HTTP. Its binary is still namedserver, and it can also run only the worker or only the gateway.cli: starts or resumes sessions and submits chat messages through the gateway.
From the repository root:
local/up.shThis starts Postgres on localhost:15432, MinIO on localhost:29000,
Temporal on localhost:7233, and the Temporal UI on http://localhost:8233.
Each shell that runs Lightspeed commands should load the local environment:
source local/env.shOpen a first shell:
source local/env.sh
# export OPENAI_API_KEY=... # omit this if it is already in .env
cargo run -p temporal-serverWith no subcommand, the server binary runs the gateway and Temporal worker
together in one process. The gateway listens on http://127.0.0.1:18080 by default.
Optional health check:
curl http://127.0.0.1:18080/healthFor split deployments, run the two roles separately:
cargo run -p temporal-server -- worker
cargo run -p temporal-server -- gatewayOpen another shell:
source local/env.sh
cargo run -p cli -- chat --newThat starts an interactive TUI session. LIGHTSPEED_API_URL is exported by
local/env.sh, so you do not need to pass --api-url.
For OpenAI-backed chat, the CLI sends typed session/run configuration through
the API. Use --model ... on a command, or set LIGHTSPEED_CHAT_MODEL, if you want
a specific model.
To send one message and exit:
cargo run -p cli -- chat --new "hello from the hosted path"The non-interactive command prints connected session=...; reuse that session
ID to continue the same conversation:
cargo run -p cli -- chat --session session_1
cargo run -p cli -- chat --session session_1 "continue the conversation"To get machine-readable output for a one-shot run:
cargo run -p cli -- chat --new --json "summarize this repository"To chat with a local directory mounted as a writable CAS-backed VFS workspace:
cargo run -p cli -- chat --new --mount .The CLI snapshots the directory locally, uploads missing blobs, creates a VFS
workspace from that snapshot, mounts it at /workspace, and starts the chat
session with /workspace as the working directory. Use --mount-path to pick
a different VFS mount path.
The cli package builds the lightspeed binary, so installed usage is equivalent:
lightspeed chat --newTo upload a local directory as a CAS-backed VFS snapshot:
cargo run -p cli -- vfs snapshot .The command walks the directory locally, uploads missing content-addressed blobs
through the gateway, commits the VFS manifest, and prints the resulting
snapshotRef. Use --json for a machine-readable summary.
To materialize a snapshot back to a local directory:
cargo run -p cli -- vfs materialize sha256:... ./outThe command downloads only blobs needed for files that do not already match locally, writes under the selected destination, and refuses destination symlinks that could escape that directory.
To create and mount VFS workspaces explicitly:
cargo run -p cli -- vfs workspace create sha256:...
cargo run -p cli -- vfs workspace read workspace_...
cargo run -p cli -- vfs workspace update --expected-revision 0 workspace_... sha256:...
cargo run -p cli -- vfs workspace update workspace_... sha256:...
cargo run -p cli -- vfs workspace delete workspace_...
cargo run -p cli -- vfs mount put --session session_1 --path /workspace --workspace workspace_...
cargo run -p cli -- vfs mount delete --session session_1 --path /workspace
cargo run -p cli -- vfs mount list --session session_1Snapshot mounts are read-only; workspace mounts can be read-only or read-write.
local/down.shTo reset persisted local state while keeping containers available:
local/reset.shAt a high level, an agent session works like this:
- A client starts or opens a session through
api. - The runtime admits input as a command to the CoreAgent drive machine.
- The core emits append, LLM, or tool actions without performing I/O.
- The runtime or workflow substrate fulfills those actions through stores, adapter traits, or workflow activities.
- Only committed session entries are resumed into the core state.
api-projectionprojects internal events/state into client-facing session, run, and item views.
test-support owns an inline SessionRunner harness for tests and evals only.
Durability and retry semantics belong in the Temporal hosted path, where core
actions are fulfilled through activities.
The hosted run/start path is asynchronous at the API boundary: it returns
after the workflow has accepted and started or observed the run, while clients
continue following session/events/read or refreshing session/read for tool
activity and final output. session/start accepts a product-level config block
for model, instructions, generation, context, and run defaults; instructions can
be supplied as inline text or an existing CAS blob ref. session/update applies
revision-checked patches to idle sessions without requiring clients to resubmit
the full config. run/start accepts typed per-run model, generation, and limit
overrides, and input can be supplied as inline text or an existing text CAS blob
ref. The gateway owns API-to-command conversion for run/start: it writes
inline run input to CAS or validates the supplied ref, builds
CoreAgentCommand::RequestRun, wraps the encoded core command as a workflow
admission, and signals the workflow.
Default deterministic tests:
cargo testIgnored live provider tests require API keys and may cost money:
cargo test -p llm-clients -- --ignoredinterop/contract/ holds the committed machine-readable contract of the JSON-RPC
gateway, generated from the api crate's types via schemars:
api.schema.json— draft-07 JSON Schema bundle of every wire type;methods.json— method/notification manifest with refs into the bundle;openrpc.json— OpenRPC document for docs tooling (not for codegen).
The manifest is produced by the same macro that generates the JSON-RPC
dispatcher, so it cannot drift from what the server serves. After changing
api types, regenerate and commit:
cargo run -p api --bin export-schemacargo test -p api fails while the committed artifacts are stale.
Interop packages that consume this contract live under interop/, including
the private TypeScript client in interop/ts-client/ and the messaging bridge
in interop/messaging/.
Local commands load a root .env file when present. Use
.env_example as the template for provider credentials, or set
the same variables directly in your shell.
| Variable | Purpose |
|---|---|
OPENAI_API_KEY |
OpenAI provider authentication |
ANTHROPIC_API_KEY |
Anthropic provider authentication |
OPENAI_BASE_URL |
Override OpenAI API endpoint |
ANTHROPIC_BASE_URL |
Override Anthropic API endpoint |
LIGHTSPEED_CHAT_PROVIDER |
Default chat provider ID |
LIGHTSPEED_CHAT_API_KIND |
Default chat provider API kind |
LIGHTSPEED_CHAT_MODEL |
Default chat model |
LIGHTSPEED_CHAT_REASONING_EFFORT |
Default reasoning effort (OpenAI reasoning / Anthropic thinking) |
LIGHTSPEED_CHAT_MAX_TOKENS |
Default max output token setting for chat runs |
LIGHTSPEED_API_URL |
CLI JSON-RPC gateway URL |
LIGHTSPEED_POSTGRES_URL |
PostgreSQL session/CAS database URL |
LIGHTSPEED_PG_UNIVERSE_ID |
Hosted store universe UUID |
LIGHTSPEED_TASK_QUEUE |
Temporal task queue override; defaults to lightspeed-universe-{LIGHTSPEED_PG_UNIVERSE_ID} |
LIGHTSPEED_OBJECT_STORE_ENDPOINT |
S3-compatible object store endpoint |
RUST_LOG |
Server log filter. Defaults to app/Temporal info and dependency warnings; use temporal_server=debug,temporal_workflow=debug for more detail |
LIGHTSPEED_LOG_FORMAT |
Server log format: compact (default), pretty, or json |