astrid-capsule-react: the ReAct loop coordinator that owns no intelligence
ReAct loop coordinator. Stateless state machine for reasoning-and-action cycle. Part of Unicity AOS.
At a glance
- What is it?
- A stateless Rust capsule that sequences five other capsules over an IPC event bus, so the reasoning-and-action cycle can be swapped without touching anything else. Here is what the state machine covers, what it refuses to do, and where it stops being the right tool.
- Who is it for?
- Adopt astrid-capsule-react if you are already running Astrid OS and want the reasoning-and-action cycle to be one replaceable component rather than logic smeared across your prompt builder and tool router. Do not adopt it as a standalone agent framework: it is a cdylib targeting wasm32-unknown-unknown, it is not published to crates.io, and it calls no LLM on its own.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 82 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The coordination problem astrid-capsule-react exists to remove
Most agent implementations start as one file and end as five. The provider call, the system prompt assembly, the tool dispatch and the transcript write all live in the same function, and the loop that sequences them is indistinguishable from the work they do. Changing the loop means changing the file that also holds your retry logic. astrid-capsule-react takes the opposite position: the loop is the only thing in the crate.
The README states the boundary plainly. The capsule "does not call LLMs, execute tools, build prompts, or store messages. It orchestrates the capsules that do." The intended reader is someone building on Astrid OS who wants to alter how an agent reasons without forking the components that reason. That is a narrower audience than a general agent framework serves, and the design only pays off if you actually intend to swap the orchestration strategy later.
The state machine and the five capsules it drives
The cycle is written out in the README as a linear sequence:
Idle -> AwaitingIdentity -> AwaitingPromptBuild -> Streaming -> AwaitingTools -> Streaming -> ... -> IdleRead that carefully and the architecture falls out. After Idle the coordinator waits on Identity, which builds the system prompt from workspace config. Then AwaitingPromptBuild, where plugin contributions are merged into the final prompt. Then Streaming, which is the provider capsule emitting tokens. Then AwaitingTools, where the Tool Router dispatches calls and collects results. The ellipsis before Idle is the part that matters: the Streaming and AwaitingTools pair repeats for as many tool round trips as the turn requires, and only then does control return to Idle.
The five collaborators are named in the README: Session for history and turn persistence, Identity for the system prompt, Prompt Builder for plugin merging, a Provider such as Anthropic for streaming, and Tool Router for dispatch. The coordinator talks to all of them over the IPC event bus. That bus is the actual interface, which means the state machine is less a controller than a message-passing schedule. Anyone debugging a stalled turn should be looking at which of the five capsules failed to answer, not at the loop itself.
Building the capsule and reading its state keys
The README gives two commands for development. The first adds the WebAssembly target, the second builds the release artifact:
rustup target add wasm32-unknown-unknown
cargo build --target wasm32-unknown-unknown --releaseCargo.toml sets crate-type to cdylib, so the output is a dynamic library rather than a Rust library other crates link against. The same file sets publish = false, which means this is not a crate you add to a dependency list. It is a capsule loaded by the host. The release profile is tuned for size rather than speed: opt-level "z", lto = true, codegen-units = 1, strip = true and panic = "abort". That last setting matters for anyone writing panic handlers, because there is no unwinding to catch.
The persistence section of the README names three KV keys. react.turn.{session_id} holds the current turn state, react.req2sess.{request_id} correlates a request to its session, and react.call2sess.{call_id} correlates a tool call to its session. The README describes this as control flow state, not conversation history, and states that history lives in the session capsule. A first real use is therefore less about writing code and more about watching those keys while a turn runs: send a prompt, then inspect react.turn.{session_id} as it moves through the states above, and check that react.call2sess.{call_id} appears and disappears around each tool dispatch. The README does not document a command for reading the KV store, so that step depends on whatever tooling the host provides.
Where the stateless design leaves you exposed
Statelessness here is a claim about conversation history, not about the process. The capsule does keep per-turn state, and the README lists the three key patterns it writes. What the README does not describe is expiry. There is no TTL, no cleanup step and no rollback path documented for react.turn.{session_id}, react.req2sess.{request_id} or react.call2sess.{call_id}. A turn that dies mid-flight, because the provider stream drops or the Tool Router never answers, leaves its correlation keys behind unless something outside this capsule removes them. The README is silent on what that something is.
The second limitation is the one the design advertises as a feature. Because the loop contains no inference logic, it also contains no fallback logic. If the Provider capsule returns nothing, the coordinator has no second model to try. If the Tool Router times out, retry policy is not part of this state machine. Those decisions belong to the capsules being coordinated, and anyone expecting the loop to absorb provider flakiness will be disappointed. The state names also assume a single linear strategy: AwaitingIdentity then AwaitingPromptBuild then Streaming is a fixed order. The README's suggestion that you could replace this capsule with debate or MCTS orchestration implies you would be writing that ordering from scratch, not configuring this one.
How this differs from a framework like LangGraph or a plain agent loop
The obvious comparison is a graph-based agent framework, where you declare nodes and edges and the framework executes the graph in-process. Here the nodes are separate capsules communicating over an IPC event bus, and the graph is a single hardcoded path with one repeat point. The difference in approach is where the boundary sits. A graph framework keeps your orchestration and your node implementations in one binary and one language. astrid-capsule-react keeps them in separate capsules that can be rebuilt independently, at the cost of every transition being a message with serialization on both ends.
At the other extreme is the plain while loop inside an application. That is simpler and has no IPC overhead, but it couples the loop to whatever writes the prompt and dispatches the tools. The capsule's value is that replacing it changes agent behaviour "without touching any other capsule", as the README puts it. If you never intend to make that replacement, the indirection buys you nothing.
Maintenance, versioning and licence terms
The last push to the repository was on 2026-07-09, and the most recent release, v0.2.2, was tagged the same day. Two earlier releases, v0.2.0 and v0.2.1, both landed on 2026-07-02, so the visible release history is a burst of three versions inside eight days followed by a quieter period. The repository is not archived. There is no changelog in the top-level entries, so the difference between v0.2.1 and v0.2.2 is not documented anywhere in the files published with the repository.
Upgrade cost is partly structural. The crate depends on astrid-sdk version 0.7 with the derive feature, and it is built against edition 2024 with an MSRV of 1.94 per the README badge. Rust toolchain pins live in rust-toolchain.toml, so the compiler version is part of the repository rather than your environment. Because publish = false, there is no crates.io version to track and no semver contract to lean on: you take the capsule at whatever commit the host loads.
On licensing, the README badge and the LICENSE-MIT and LICENSE-APACHE files indicate dual licensing under MIT OR Apache-2.0, matching the repository metadata. The package description in Cargo.toml does not restate the licence field. If you redistribute the capsule, check both licence files rather than relying on the badge alone; this is a description of what the repository contains, not legal advice.
Editorial conclusion
Adopt astrid-capsule-react if you are already running Astrid OS and want the reasoning-and-action cycle to be one replaceable component rather than logic smeared across your prompt builder and tool router. Do not adopt it as a standalone agent framework: it is a cdylib targeting wasm32-unknown-unknown, it is not published to crates.io, and it calls no LLM on its own. Before committing, confirm that the KV keys react.turn.{session_id}, react.req2sess.{request_id} and react.call2sess.{call_id} have no TTL or cleanup path documented anywhere you can find, because that gap is the one thing in this capsule that a long-running session will eventually expose.
Frequently asked questions
Does astrid-capsule-react call the LLM itself?
No. The README states the capsule does not call LLMs, execute tools, build prompts or store messages; it coordinates the capsules that do, with a Provider capsule such as Anthropic handling the streaming.
Can I install astrid-capsule-react from crates.io?
No. Cargo.toml sets publish = false and crate-type to cdylib, so it is built as a WebAssembly capsule and loaded by the host rather than added as a dependency.
What state does astrid-capsule-react persist between turns?
The README lists three KV keys: react.turn.{session_id} for the current turn state, react.req2sess.{request_id} for request-to-session correlation, and react.call2sess.{call_id} for tool call-to-session correlation. Conversation history is stored by the session capsule instead.
Which Rust version does astrid-capsule-react need?
The README badge states an MSRV of 1.94, and Cargo.toml declares edition 2024. The repository also carries a rust-toolchain.toml file that pins the toolchain.
What licence is astrid-capsule-react released under?
The README and repository metadata give MIT OR Apache-2.0, with LICENSE-MIT and LICENSE-APACHE files at the top level. The Cargo.toml package section does not restate the licence field.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/unicity-aos-capsule-react)