monoloop
Product façade for Monoloop: Connector + Interpreter + transaction-composing Loop.
Hosts should depend on this crate for plug-and-play assembly (cargo add monoloop).
Profile connectors (Grok, Cursor, …) are optional Cargo features.
License: AGPL-3.0-or-later. Commercial: https://frogfish.io
What this crate is / is not
| Is | Is not |
|---|---|
| Re-exports of the three product components + contracts | A fourth runtime component |
Thin CLI (monoloop --version / --copyright) |
A chat UI or agent framework |
| Assembly entry for hosts | Test kit (see monoloop-testkit) |
Agent assembly recipe (copy this shape)
Monoloop is assembled, not “started with a prompt.” Wire one Channel, start the
runtime, then submit push-based transactions.
1. ChannelBinding {
connector_factory, // FakeConnectorFactory OR profile helper
encoder, // profile encoder OR TestTextEncoder (smoke only)
interpreter, // DefaultInterpreterFactory (or profile-provided)
capabilities, // must include option_policy + dialects
… // id, kind, tool_mode, endpoint_ref, limits
}
2. ChannelRegistry::build(vec![binding])
3. DefaultTransactionRuntime::start(RuntimeBootstrap {
config: RuntimeConfig { enable_mcp_listener: false, ..Default::default() },
channels, tools: HostToolRegistry::empty(), executor
})
4. TransactionRuntime::submit(TransactionRequest {
channel_id, input: user_text_input(…),
events: FnEventSink(…), completion: FnCompletionCallback(…),
invocation_config, tools: vec![], …
})
5. Await the one completion callback; run-owned state is destroyed at terminal
Hosts with a live profile should call that profile’s *_channel_binding(...)
(see Optional Channel profiles). Do not hand-roll Grok/Cursor/… bindings from the
smoke sample.
Offline smoke sample (FakeConnector + test_raw dialect, no network, no testkit):
See examples/fake_echo.rs. A component-level twin lives
under monoloop-loop (same wiring, direct crate imports).
TestTextEncoder (smoke only)
TestTextEncoder is a loop-owned deterministic encoder for FakeConnector +
DialectDescriptor::test_raw(). It is not the production host path and is
not the testkit ACP/OpenAI fixture encoder family. Live Channels use the
encoder supplied by *_channel_binding.
Module map (façade paths)
| Use | From |
|---|---|
monoloop::contracts::* |
identities, ports, user_text_input, sinks |
monoloop::connector::FakeConnectorFactory |
deterministic DirectLlm transport (smoke) |
monoloop::interpreter::DefaultInterpreterFactory |
bytes → complete canonical units |
monoloop::loop_runtime::{ChannelBinding, ChannelRegistry, DefaultTransactionRuntime, HostToolRegistry, RuntimeBootstrap, RuntimeConfig, TestTextEncoder} |
composition |
Hard rules agents must not break
- Do not depend on
monoloop-testkitfrom product/host product crates. - Do not invent ambient “current session”; pass explicit IDs on the request.
- Empty tools (
HostToolRegistry::empty(),tools: vec![]) is required first qualification: complete tool requests becometool_unavailablewith zero effects. (fake_echodoes not emit tool calls; seemonoloop-looptests/empty_loop.rs.) - Set
RuntimeConfig.enable_mcp_listener: falseunless you intentionally start the MCP shell (Defaultenables it). - Events and completion are push callbacks on
TransactionRequest, not a pull API. - One in-flow transaction per
SessionKey; duplicates are rejected, never queued. - Canonical completeness ≠ authorization; never treat Interpreter output as safe to execute.
Optional Channel profiles
= { = "0.1", = ["grok"] }
# also: cursor, codex, agy, zai, claude
Each profile crate exports a *_channel_binding(...) helper. Live qualification
examples live in monoloop-testkit, not here.
Version / license helpers
use ;
println!; // e.g. 0.1.0+build-42
println!;
Normative specs: repository doc/README.md, doc/MONOLOOP.md,
doc/TRANSACTION_RUNTIME_IMPLEMENTATION.md.